{
  "id": "P148",
  "slug": "price-table",
  "key": "P148-price-table",
  "title": "Price table",
  "summary": "A parcel rate card keyed on the weights as typed and on whole grams: computed weights that find no entry, and 1, 1.0 and True landing on one key so a misaligned checkbox sets the 1 kg price.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P148 - Price table\n\nA parcel rate card looked up by weight, keyed two ways: on the weights as\nthey were typed, and on whole grams.\n\n- `main.eml` - the menu, the rate card, the two tables built from it, a\n  parcel priced both ways, and every parcel of two items\n- `rates.eml` - the card, the default price, the kind of each weight cell,\n  grams rounded half up, the two tables, and the two lookups\n\nHow each part works:\n\n- The card has a row for every 0.1 kg up to 2 kg, the whole kilos typed as\n  1 and 2, then two more rows: the 1 kg price updated, typed 1.0, and a\n  True that a misaligned checkbox column put in the weight cell.\n- As in the corpus case `dict-key-identity-collapse`, 1, 1.0 and True are\n  one key: all three rows write it, the key stays the int 1 it was first\n  written as, and the last row wins - 1 kg costs 0.00. Keyed on grams, the\n  True is refused as not a number and 1000 g costs 9.00, the update.\n- As in the corpus case `float-key-instability`, a parcel's weight arrives\n  by adding its items: 0.1 + 0.2 is 0.30000000000000004, which the card\n  keyed as typed has no entry for, so the default - the 2 kg rate - is\n  charged. Keyed on grams, rounded once where each weight comes in, every\n  weight on the card is found.\n- Of the 45 parcels of two items from 0.1 to 0.9 kg, keyed as typed 8 find\n  no entry and 5 find the 1 kg price the checkbox wrote; the weight in all\n  is 45.000 kg both ways, to the gram - a check on the total passes while 13\n  prices are wrong.\n\nWhat is checked: menu choices 1 to 5; 1 to 6 weights from 0.001 to 2 kg,\neach up to 5 characters, and a parcel of at most 2 kg. An empty answer\ncancels. A weight that is not on the card is not found keyed either way,\nand the default is charged.\n\nSessions: `sessions/basic.in` gives:\n- the rate card and the two tables;\n- parcels of 0.1 and 0.2, of 0.4 and 0.6, of 0.3 alone, and of 0.7, 0.1\n  and 0.2; every two-item parcel.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- weights: a cancel, abc, 0.1.2, 0, 2.5, seven weights and 123456; a parcel\n  of 3 kg;\n- a parcel of 1 and 1, the 2 kg rate both ways.\n\nBuilt on the verified corpus cases `float-key-instability` (a table keyed\non decimal amounts, looked up by computed sums, against integer cents) and\n`dict-key-identity-collapse` (1, 1.0 and True as one key, the first key\nkept and the last value).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P148 price table: a parcel rate card, the two tables built from it - keyed\n# on the weights as typed and on whole grams - a parcel priced both ways,\n# and every parcel of two items.\nimport rates\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef money(c):\n    c % 100 => p\n    if p < 10:\n        return str(int((c - p) / 100)) + \".0\" + str(p)\n    return str(int((c - p) / 100)) + \".\" + str(p)\n\ndef kg(g):\n    # whole grams as kg with three places\n    g % 1000 => r\n    if r < 10:\n        return str(int((g - r) / 1000)) + \".00\" + str(r)\n    if r < 100:\n        return str(int((g - r) / 1000)) + \".0\" + str(r)\n    return str(int((g - r) / 1000)) + \".\" + str(r)\n\ndef show_card():\n    \"The rate card, row by row, the weight as it was entered:\" ^0\n    1 => k\n    for r in rates.card():\n        (\"  \" + lpad(str(k), 2) + \"  \" + pad(str(r[0]) + \" (\" + rates.kind(r[0]) + \")\", 16) + lpad(money(r[1]), 6)) ^0\n        k + 1 => k\n\ndef show_tables():\n    rates.float_table() => ft\n    rates.gram_table() => gt\n    len(rates.card()) => n\n    (\"Keyed on the weights as typed: \" + str(len(ft)) + \" entries from \" + str(n) + \" rows.\") ^0\n    \"\" => who\n    1 => k\n    for r in rates.card():\n        if r[0] == 1:\n            if who == \"\":\n                str(k) + \" (\" + str(r[0]) + \", \" + rates.kind(r[0]) + \")\" => who\n            else:\n                who + \", \" + str(k) + \" (\" + str(r[0]) + \", \" + rates.kind(r[0]) + \")\" => who\n        k + 1 => k\n    (\"  1, 1.0 and True are one key; rows \" + who + \" all wrote it, and the last one wins: 1 kg costs \" + money(ft[1]) + \".\") ^0\n    (\"Keyed on grams, the True refused as not a number: \" + str(len(gt)) + \" entries; 1000 g costs \" + money(gt[1000]) + \", the update typed 1.0.\") ^0\n\ndef parse(t):\n    # the weights in kg, or [] when one is not a weight from 0.001 to 2\n    [] => out\n    \"\" => cur\n    for c in t + \" \":\n        if c == \" \":\n            if cur != \"\":\n                0 => dots\n                for d in cur:\n                    if d == \".\":\n                        dots + 1 => dots\n                    elif d < \"0\" or d > \"9\":\n                        return []\n                if dots > 1 or cur == \".\" or len(cur) > 5:\n                    return []\n                float(cur) => w\n                if w < 0.001 or w > 2:\n                    return []\n                out + [w] => out\n                \"\" => cur\n        else:\n            cur + c => cur\n    return out\n\ndef price(ws):\n    0 => total\n    0 => g\n    for w in ws:\n        total + w => total\n        g + rates.grams(w) => g\n    rates.by_float(rates.float_table(), total) => pf\n    rates.by_grams(rates.gram_table(), g) => pg\n    (\"Added as floats the weight is \" + str(total) + \" kg; added as grams, \" + str(g) + \" g.\") ^0\n    if pf < 0:\n        (\"  keyed as typed: no entry for \" + str(total) + \", so the default \" + money(rates.default_price()) + \" is charged\") ^0\n        rates.default_price() => pf\n    else:\n        (\"  keyed as typed: \" + money(pf)) ^0\n    if pg < 0:\n        (\"  keyed on grams: no entry for \" + str(g) + \" g, so the default \" + money(rates.default_price()) + \" is charged\") ^0\n        rates.default_price() => pg\n    else:\n        (\"  keyed on grams: \" + money(pg)) ^0\n    return [pf, pg]\n\ndef one_parcel():\n    plain(input(\"item weights in kg (e.g. 0.1 0.2)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    parse(t) => ws\n    if len(ws) == 0 or len(ws) > 6:\n        \"Type 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\" ^0\n        return\n    0 => g\n    for w in ws:\n        g + rates.grams(w) => g\n    if g > 2000:\n        \"The parcel weighs over 2 kg; the card goes up to 2.\" ^0\n        return\n    price(ws) => p\n    if p[0] != p[1]:\n        (\"The two tables charge \" + money(p[0]) + \" and \" + money(p[1]) + \" for the same parcel.\") ^0\n\ndef pairs():\n    rates.float_table() => ft\n    rates.gram_table() => gt\n    rates.items() => its\n    0 => n\n    0 => miss\n    0 => other\n    0 => fsum\n    0 => gsum\n    0 => cf\n    0 => cg\n    \"\" => ex\n    0 => i\n    while i < len(its):\n        i => j\n        while j < len(its):\n            its[i] + its[j] => total\n            rates.grams(its[i]) + rates.grams(its[j]) => g\n            n + 1 => n\n            fsum + total => fsum\n            gsum + g => gsum\n            rates.by_float(ft, total) => pf\n            rates.by_grams(gt, g) => pg\n            if pf < 0:\n                miss + 1 => miss\n                rates.default_price() => pf\n                if ex == \"\":\n                    str(its[i]) + \" + \" + str(its[j]) + \" = \" + str(total) => ex\n            elif pf != pg:\n                other + 1 => other\n            cf + pf => cf\n            cg + pg => cg\n            j + 1 => j\n        i + 1 => i\n    (\"Every parcel of two of the items 0.1 to 0.9 kg: \" + str(n) + \" parcels.\") ^0\n    (\"  keyed as typed: \" + str(miss) + \" find no entry and pay the default, as \" + ex + \"; \" + str(other) + \" find the 1 kg entry the checkbox wrote\") ^0\n    (\"  keyed on grams: every one finds its entry\") ^0\n    (\"  charged in all: \" + money(cf) + \" keyed as typed, \" + money(cg) + \" keyed on grams\") ^0\n    if rates.grams(fsum) == gsum:\n        (\"  the weight in all is \" + kg(gsum) + \" kg both ways, to the gram: a check on the total passes while \" + str(miss + other) + \" of the \" + str(n) + \" prices are wrong\") ^0\n    else:\n        (\"  the weight in all: \" + str(fsum) + \" kg added as floats, \" + kg(gsum) + \" kg as grams\") ^0\n\n\"== Price table ==\" ^0\n\"A parcel rate card looked up by weight: keyed on the weights as typed, and on whole grams.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show_card()\n    elif choice == \"2\":\n        show_tables()\n    elif choice == \"3\":\n        one_parcel()\n    elif choice == \"4\":\n        pairs()\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import rates\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef money(c):\n    p = c % 100\n    if p < 10:\n        return str(int((c - p) / 100)) + \".0\" + str(p)\n    return str(int((c - p) / 100)) + \".\" + str(p)\n\ndef kg(g):\n    r = g % 1000\n    if r < 10:\n        return str(int((g - r) / 1000)) + \".00\" + str(r)\n    if r < 100:\n        return str(int((g - r) / 1000)) + \".0\" + str(r)\n    return str(int((g - r) / 1000)) + \".\" + str(r)\n\ndef show_card():\n    print(\"The rate card, row by row, the weight as it was entered:\")\n    k = 1\n    for r in rates.card():\n        print(\"  \" + lpad(str(k), 2) + \"  \" + pad(str(r[0]) + \" (\" + rates.kind(r[0]) + \")\", 16) + lpad(money(r[1]), 6))\n        k = k + 1\n\ndef show_tables():\n    ft = rates.float_table()\n    gt = rates.gram_table()\n    n = len(rates.card())\n    print(\"Keyed on the weights as typed: \" + str(len(ft)) + \" entries from \" + str(n) + \" rows.\")\n    who = \"\"\n    k = 1\n    for r in rates.card():\n        if r[0] == 1:\n            if who == \"\":\n                who = str(k) + \" (\" + str(r[0]) + \", \" + rates.kind(r[0]) + \")\"\n            else:\n                who = who + \", \" + str(k) + \" (\" + str(r[0]) + \", \" + rates.kind(r[0]) + \")\"\n        k = k + 1\n    print(\"  1, 1.0 and True are one key; rows \" + who + \" all wrote it, and the last one wins: 1 kg costs \" + money(ft[1]) + \".\")\n    print(\"Keyed on grams, the True refused as not a number: \" + str(len(gt)) + \" entries; 1000 g costs \" + money(gt[1000]) + \", the update typed 1.0.\")\n\ndef parse(t):\n    out = []\n    cur = \"\"\n    for c in t + \" \":\n        if c == \" \":\n            if cur != \"\":\n                dots = 0\n                for d in cur:\n                    if d == \".\":\n                        dots = dots + 1\n                    elif d < \"0\" or d > \"9\":\n                        return []\n                if dots > 1 or cur == \".\" or len(cur) > 5:\n                    return []\n                w = float(cur)\n                if w < 0.001 or w > 2:\n                    return []\n                out = out + [w]\n                cur = \"\"\n        else:\n            cur = cur + c\n    return out\n\ndef price(ws):\n    total = 0\n    g = 0\n    for w in ws:\n        total = total + w\n        g = g + rates.grams(w)\n    pf = rates.by_float(rates.float_table(), total)\n    pg = rates.by_grams(rates.gram_table(), g)\n    print(\"Added as floats the weight is \" + str(total) + \" kg; added as grams, \" + str(g) + \" g.\")\n    if pf < 0:\n        print(\"  keyed as typed: no entry for \" + str(total) + \", so the default \" + money(rates.default_price()) + \" is charged\")\n        pf = rates.default_price()\n    else:\n        print(\"  keyed as typed: \" + money(pf))\n    if pg < 0:\n        print(\"  keyed on grams: no entry for \" + str(g) + \" g, so the default \" + money(rates.default_price()) + \" is charged\")\n        pg = rates.default_price()\n    else:\n        print(\"  keyed on grams: \" + money(pg))\n    return [pf, pg]\n\ndef one_parcel():\n    t = plain(input(\"item weights in kg (e.g. 0.1 0.2)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    ws = parse(t)\n    if len(ws) == 0 or len(ws) > 6:\n        print(\"Type 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\")\n        return\n    g = 0\n    for w in ws:\n        g = g + rates.grams(w)\n    if g > 2000:\n        print(\"The parcel weighs over 2 kg; the card goes up to 2.\")\n        return\n    p = price(ws)\n    if p[0] != p[1]:\n        print(\"The two tables charge \" + money(p[0]) + \" and \" + money(p[1]) + \" for the same parcel.\")\n\ndef pairs():\n    ft = rates.float_table()\n    gt = rates.gram_table()\n    its = rates.items()\n    n = 0\n    miss = 0\n    other = 0\n    fsum = 0\n    gsum = 0\n    cf = 0\n    cg = 0\n    ex = \"\"\n    i = 0\n    while i < len(its):\n        j = i\n        while j < len(its):\n            total = its[i] + its[j]\n            g = rates.grams(its[i]) + rates.grams(its[j])\n            n = n + 1\n            fsum = fsum + total\n            gsum = gsum + g\n            pf = rates.by_float(ft, total)\n            pg = rates.by_grams(gt, g)\n            if pf < 0:\n                miss = miss + 1\n                pf = rates.default_price()\n                if ex == \"\":\n                    ex = str(its[i]) + \" + \" + str(its[j]) + \" = \" + str(total)\n            elif pf != pg:\n                other = other + 1\n            cf = cf + pf\n            cg = cg + pg\n            j = j + 1\n        i = i + 1\n    print(\"Every parcel of two of the items 0.1 to 0.9 kg: \" + str(n) + \" parcels.\")\n    print(\"  keyed as typed: \" + str(miss) + \" find no entry and pay the default, as \" + ex + \"; \" + str(other) + \" find the 1 kg entry the checkbox wrote\")\n    print(\"  keyed on grams: every one finds its entry\")\n    print(\"  charged in all: \" + money(cf) + \" keyed as typed, \" + money(cg) + \" keyed on grams\")\n    if rates.grams(fsum) == gsum:\n        print(\"  the weight in all is \" + kg(gsum) + \" kg both ways, to the gram: a check on the total passes while \" + str(miss + other) + \" of the \" + str(n) + \" prices are wrong\")\n    else:\n        print(\"  the weight in all: \" + str(fsum) + \" kg added as floats, \" + kg(gsum) + \" kg as grams\")\n\nprint(\"== Price table ==\")\nprint(\"A parcel rate card looked up by weight: keyed on the weights as typed, and on whole grams.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show_card()\n    elif choice == \"2\":\n        show_tables()\n    elif choice == \"3\":\n        one_parcel()\n    elif choice == \"4\":\n        pairs()\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "rates.eml",
      "eml": "# P148 price table - a parcel rate card looked up by weight, keyed two\n# ways. As in the corpus case float-key-instability, a table keyed on the\n# weights a person typed has no entry for a weight that arrived by adding,\n# 0.1 + 0.2 being 0.30000000000000004; keyed on whole grams, rounded once\n# where a weight comes in, it finds every weight the card has. As in the\n# corpus case dict-key-identity-collapse, 1, 1.0 and True are one key: the\n# row for 1 kg, its later update typed 1.0, and a True a misaligned\n# checkbox column put in the weight cell all land on it, and the last one\n# written is the price.\n\ndef card():\n    # [weight in kg as typed, price in cents], in the order the rows come\n    [] => out\n    1 => k\n    while k <= 20:\n        if k == 10:\n            out + [[1, 300 + 50 * (k - 1)]] => out\n        elif k == 20:\n            out + [[2, 300 + 50 * (k - 1)]] => out\n        else:\n            out + [[k / 10, 300 + 50 * (k - 1)]] => out\n        k + 1 => k\n    return out + [[1.0, 900], [True, 0]]\n\ndef default_price():\n    # charged when a weight has no entry: the 2 kg rate\n    return 1250\n\ndef kind(w):\n    str(w) => s\n    if s == \"True\" or s == \"False\":\n        return \"bool\"\n    for c in s:\n        if c == \".\":\n            return \"float\"\n    return \"int\"\n\ndef grams(w):\n    # a weight in kg to whole grams, rounded half up, once\n    return int(w * 1000 + 0.5)\n\ndef float_table():\n    {} => t\n    for r in card():\n        r[1] => t[r[0]]\n    return t\n\ndef gram_table():\n    # the same rows keyed on grams; a cell that is not a number is refused\n    {} => t\n    for r in card():\n        if kind(r[0]) != \"bool\":\n            r[1] => t[grams(r[0])]\n    return t\n\ndef by_float(t, total):\n    if total in t:\n        return t[total]\n    return 0 - 1\n\ndef by_grams(t, g):\n    if g in t:\n        return t[g]\n    return 0 - 1\n\ndef items():\n    return [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]\n",
      "python": "def card():\n    out = []\n    k = 1\n    while k <= 20:\n        if k == 10:\n            out = out + [[1, 300 + 50 * (k - 1)]]\n        elif k == 20:\n            out = out + [[2, 300 + 50 * (k - 1)]]\n        else:\n            out = out + [[k / 10, 300 + 50 * (k - 1)]]\n        k = k + 1\n    return out + [[1.0, 900], [True, 0]]\n\ndef default_price():\n    return 1250\n\ndef kind(w):\n    s = str(w)\n    if s == \"True\" or s == \"False\":\n        return \"bool\"\n    for c in s:\n        if c == \".\":\n            return \"float\"\n    return \"int\"\n\ndef grams(w):\n    return int(w * 1000 + 0.5)\n\ndef float_table():\n    t = {}\n    for r in card():\n        t[r[0]] = r[1]\n    return t\n\ndef gram_table():\n    t = {}\n    for r in card():\n        if kind(r[0]) != \"bool\":\n            t[grams(r[0])] = r[1]\n    return t\n\ndef by_float(t, total):\n    if total in t:\n        return t[total]\n    return 0 - 1\n\ndef by_grams(t, g):\n    if g in t:\n        return t[g]\n    return 0 - 1\n\ndef items():\n    return [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n3\n\n3\nabc\n3\n0.1.2\n3\n0\n3\n2.5\n3\n1 1 1\n3\n0.1 0.1 0.1 0.1 0.1 0.1 0.1\n3\n123456\n3\n1 1\n5\n",
      "screen": "== Price table ==\nA parcel rate card looked up by weight: keyed on the weights as typed, and on whole grams.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> \nCancelled.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> abc\nType 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 0.1.2\nType 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 0\nType 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 2.5\nType 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 1 1 1\nThe parcel weighs over 2 kg; the card goes up to 2.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 0.1 0.1 0.1 0.1 0.1 0.1 0.1\nType 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 123456\nType 1 to 6 weights from 0.001 to 2 kg, separated by spaces.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 1 1\nAdded as floats the weight is 2.0 kg; added as grams, 2000 g.\n  keyed as typed: 12.50\n  keyed on grams: 12.50\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n0.1 0.2\n3\n0.4 0.6\n3\n0.3\n3\n0.7 0.1 0.2\n4\n5\n",
      "screen": "== Price table ==\nA parcel rate card looked up by weight: keyed on the weights as typed, and on whole grams.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 1\nThe rate card, row by row, the weight as it was entered:\n   1  0.1 (float)       3.00\n   2  0.2 (float)       3.50\n   3  0.3 (float)       4.00\n   4  0.4 (float)       4.50\n   5  0.5 (float)       5.00\n   6  0.6 (float)       5.50\n   7  0.7 (float)       6.00\n   8  0.8 (float)       6.50\n   9  0.9 (float)       7.00\n  10  1 (int)           7.50\n  11  1.1 (float)       8.00\n  12  1.2 (float)       8.50\n  13  1.3 (float)       9.00\n  14  1.4 (float)       9.50\n  15  1.5 (float)      10.00\n  16  1.6 (float)      10.50\n  17  1.7 (float)      11.00\n  18  1.8 (float)      11.50\n  19  1.9 (float)      12.00\n  20  2 (int)          12.50\n  21  1.0 (float)       9.00\n  22  True (bool)       0.00\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 2\nKeyed on the weights as typed: 20 entries from 22 rows.\n  1, 1.0 and True are one key; rows 10 (1, int), 21 (1.0, float), 22 (True, bool) all wrote it, and the last one wins: 1 kg costs 0.00.\nKeyed on grams, the True refused as not a number: 20 entries; 1000 g costs 9.00, the update typed 1.0.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 0.1 0.2\nAdded as floats the weight is 0.30000000000000004 kg; added as grams, 300 g.\n  keyed as typed: no entry for 0.30000000000000004, so the default 12.50 is charged\n  keyed on grams: 4.00\nThe two tables charge 12.50 and 4.00 for the same parcel.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 0.4 0.6\nAdded as floats the weight is 1.0 kg; added as grams, 1000 g.\n  keyed as typed: 0.00\n  keyed on grams: 9.00\nThe two tables charge 0.00 and 9.00 for the same parcel.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 0.3\nAdded as floats the weight is 0.3 kg; added as grams, 300 g.\n  keyed as typed: 4.00\n  keyed on grams: 4.00\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 3\nitem weights in kg (e.g. 0.1 0.2)> 0.7 0.1 0.2\nAdded as floats the weight is 1.0 kg; added as grams, 1000 g.\n  keyed as typed: 0.00\n  keyed on grams: 9.00\nThe two tables charge 0.00 and 9.00 for the same parcel.\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 4\nEvery parcel of two of the items 0.1 to 0.9 kg: 45 parcels.\n  keyed as typed: 8 find no entry and pay the default, as 0.1 + 0.2 = 0.30000000000000004; 5 find the 1 kg entry the checkbox wrote\n  keyed on grams: every one finds its entry\n  charged in all: 341.50 keyed as typed, 345.00 keyed on grams\n  the weight in all is 45.000 kg both ways, to the gram: a check on the total passes while 13 of the 45 prices are wrong\n\n1) rate card  2) the two tables  3) price a parcel  4) every two-item parcel  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "float-key-instability",
      "caseId": "221-float-key-instability",
      "title": "Three wrong premises about floating point"
    },
    {
      "slug": "dict-key-identity-collapse",
      "caseId": "201-dict-key-identity-collapse",
      "title": "Three keys that are one key"
    }
  ],
  "updated": "2026-10-18"
}
