{
  "id": "P150",
  "slug": "fixed-width-integers",
  "key": "P150-fixed-width-integers",
  "title": "Fixed-width integers",
  "summary": "A string hash, a binary-search midpoint, factorials and a division in Python's integers and in a 32-bit int as C and Java have it: where it wraps, where it turns negative, and where the two divisions part.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P150 - Fixed-width integers\n\nThe same arithmetic in Python's integers and in a 32-bit int as C and Java\nhave it: a string hash, a midpoint, factorials and a division.\n\n- `main.eml` - the menu, a text hashed one character at a time, a\n  midpoint, a table of factorials, and a division both ways\n- `width.eml` - two's complement wrapping, character codes, the hash\n  steps, division toward minus infinity and toward zero with their\n  remainders, the three midpoints, and the factorials\n\nHow each part works:\n\n- As in the corpus case `fixed-width-overflow`, Python never overflows and\n  a 32-bit int wraps, quietly, to a number of the right type. Two's\n  complement is arithmetic mod 2^32 with the top half read as negative,\n  so it is written with %, there being no bitwise operators.\n- The hash is h = h * 31 + code, as Java's String.hashCode. \"Hello world\"\n  wraps at the space and ends at -832992604, negative though every code is\n  positive. As in the corpus case `integer-division-sign`, C's remainder\n  takes the sign of the hash: its bucket of 16 is -12, an index outside the\n  table. Written ((hash % 16) + 16) % 16 it is 4, Python's bucket - because\n  16 divides 2^32, the wrap keeps the last four bits.\n- The midpoint of a binary search: (lo + hi) / 2 wraps once lo + hi passes\n  2147483647, giving a negative midpoint; lo + (hi - lo) / 2 never does.\n- Factorials: 13! is the first past 32 bits; from there the 32-bit column\n  is plausible and wrong, and some of it negative.\n- Division: Python rounds toward minus infinity and C toward zero. Both\n  keep q * b + r = a; they differ, by one in q, exactly when the signs\n  differ and the division is not exact, and then C's remainder is no slot\n  of a ring of b.\n\nWhat is checked: menu choices 1 to 5; a text of 1 to 12 letters, digits,\nspaces, dots, dashes or underscores; lo from 0 to 2147483647 and hi from\nlo; a factorial up to 1 to 20; a and b from -9999 to 9999, b not 0. An\nempty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- hello, which fits, and Hello world, which wraps;\n- the midpoint of 1500000000 and 2000000000, and of 10 and 20;\n- factorials to 20; -7 and 7 by 2, and -7 and 7 by -2.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- texts: a cancel, 14 characters and a!b;\n- midpoints: a cancel, -1, 2147483648, then lo 100 with a cancel, 50 and\n  x;\n- factorials: a cancel, 0, 21 and x;\n- divisions: a cancel, 10000, then a 7 with a cancel, 0 and x, and a lone\n  minus sign.\n\nBuilt on the verified corpus cases `fixed-width-overflow` (a hash, a\nmidpoint and a factorial in 32 bits, written with modulo) and\n`integer-division-sign` (floor and truncating division, both against\nq * b + r = a, and where they disagree).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P150 fixed-width integers: a string hash, a midpoint, factorials and a\n# division, each in Python's integers and in a 32-bit int, and where the two\n# part.\nimport width\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 number(t, most):\n    # [True, the value] for an integer of at most most digits, a - allowed\n    # in front, or [False, 0]\n    if len(t) == 0:\n        return [False, 0]\n    0 => start\n    if t[0] == \"-\":\n        1 => start\n    if len(t) == start or len(t) - start > most:\n        return [False, 0]\n    0 => v\n    for c in t[start:len(t)]:\n        if c < \"0\" or c > \"9\":\n            return [False, 0]\n        v * 10 + int(c) => v\n    if start == 1:\n        return [True, 0 - v]\n    return [True, v]\n\ndef hash_text():\n    plain(input(\"text (letters, digits, space . - _, up to 12)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    if len(t) > 12:\n        \"Type 1 to 12 letters, digits, spaces, dots, dashes or underscores.\" ^0\n        return\n    for c in t:\n        if width.code(c) < 0:\n            \"Type 1 to 12 letters, digits, spaces, dots, dashes or underscores.\" ^0\n            return\n    width.hash_steps(t) => st\n    (\"h = h * 31 + code, one character at a time:\") ^0\n    (\"  \" + pad(\"char\", 6) + lpad(\"Python\", 24) + lpad(\"32-bit\", 14)) ^0\n    False => parted\n    for x in st:\n        (\"  \" + pad(\"'\" + x[0] + \"'\", 6) + lpad(str(x[1]), 24) + lpad(str(x[2]), 14)) => line\n        if x[1] != x[2] and not parted:\n            line + \"   <- wraps here\" => line\n            True => parted\n        line ^0\n    st[len(st) - 1] => last\n    last[1] => h\n    last[2] => w\n    if h == w:\n        \"The hash fits in 32 bits: both give the same number.\" ^0\n    elif w < 0:\n        (\"The 32-bit hash is negative, \" + str(w) + \", though every code is positive.\") ^0\n    else:\n        (\"The 32-bit hash is \" + str(w) + \", a plausible number and not Python's.\") ^0\n    width.floor_mod(h, 16) => a\n    width.trunc_mod(w, 16) => b\n    width.trunc_mod(width.trunc_mod(w, 16) + 16, 16) => c\n    (\"Bucket of 16: Python hash % 16 = \" + str(a) + \"; C, with its hash, hash % 16 = \" + str(b) + \"; written ((hash % 16) + 16) % 16, \" + str(c) + \".\") ^0\n    if b < 0:\n        \"C's remainder takes the sign of the hash: a negative index, outside the table.\" ^0\n    if h != w:\n        \"The safe index matches Python's because 16 divides 2^32: the wrap keeps the last four bits.\" ^0\n\ndef midpoint():\n    plain(input(\"lo (0 to 2147483647)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    number(t, 10) => a\n    if not a[0] or a[1] < 0 or a[1] > 2147483647:\n        \"Type a whole number from 0 to 2147483647.\" ^0\n        return\n    plain(input(\"hi (lo to 2147483647)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    number(t, 10) => b\n    if not b[0] or b[1] < a[1] or b[1] > 2147483647:\n        (\"Type a whole number from \" + str(a[1]) + \" to 2147483647.\") ^0\n        return\n    width.midpoints(a[1], b[1]) => m\n    (\"  Python (lo + hi) // 2:        \" + str(m[0])) ^0\n    (\"  32-bit (lo + hi) / 2:         \" + str(m[1])) ^0\n    (\"  32-bit lo + (hi - lo) / 2:    \" + str(m[2])) ^0\n    if m[1] != m[0]:\n        (\"lo + hi is \" + str(a[1] + b[1]) + \", past 2147483647: it wraps, and the midpoint comes out \" + str(m[1]) + \". Written lo + (hi - lo) / 2, nothing overflows.\") ^0\n    else:\n        \"lo + hi fits in 32 bits: all three agree.\" ^0\n\ndef factorials():\n    plain(input(\"up to (1 to 20)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    number(t, 2) => n\n    if not n[0] or n[1] < 1 or n[1] > 20:\n        \"Type a whole number from 1 to 20.\" ^0\n        return\n    (\"  \" + lpad(\"k\", 3) + lpad(\"k! in Python\", 22) + lpad(\"in 32 bits\", 14)) ^0\n    0 => first\n    for x in width.factorials(n[1]):\n        (\"  \" + lpad(str(x[0]), 3) + lpad(str(x[1]), 22) + lpad(str(x[2]), 14)) => line\n        if x[1] != x[2]:\n            if first == 0:\n                x[0] => first\n            if x[2] < 0:\n                line + \"   negative\" => line\n            else:\n                line + \"   wrapped\" => line\n        line ^0\n    if first == 0:\n        \"Every one fits in 32 bits.\" ^0\n    else:\n        (str(first) + \"! is the first that does not fit: from there the 32-bit column is a plausible number and wrong.\") ^0\n\ndef divide():\n    plain(input(\"a (-9999 to 9999)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    number(t, 4) => a\n    if not a[0]:\n        \"Type a whole number from -9999 to 9999.\" ^0\n        return\n    plain(input(\"b (-9999 to 9999, not 0)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    number(t, 4) => b\n    if not b[0] or b[1] == 0:\n        \"Type a whole number from -9999 to 9999, not 0.\" ^0\n        return\n    a[1] => x\n    b[1] => y\n    width.floor_div(x, y) => q1\n    width.floor_mod(x, y) => r1\n    width.trunc_div(x, y) => q2\n    width.trunc_mod(x, y) => r2\n    (\"  Python, toward minus infinity: q \" + str(q1) + \", r \" + str(r1) + \"; q * b + r = \" + str(q1 * y + r1)) ^0\n    (\"  C and Java, toward zero:       q \" + str(q2) + \", r \" + str(r2) + \"; q * b + r = \" + str(q2 * y + r2)) ^0\n    if q1 == q2:\n        \"The two agree: the quotient is exact, or a and b have the same sign.\" ^0\n    else:\n        \"Both keep q * b + r = a and differ by one in q: the signs differ and the division is not exact.\" ^0\n        if y > 0:\n            (\"As an index into \" + str(y) + \" slots, Python's r is slot \" + str(r1) + \"; C's is \" + str(r2) + \", outside them.\") ^0\n\n\"== Fixed-width integers ==\" ^0\n\"Arithmetic in Python's integers and in a 32-bit int as C and Java have it.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        hash_text()\n    elif choice == \"2\":\n        midpoint()\n    elif choice == \"3\":\n        factorials()\n    elif choice == \"4\":\n        divide()\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import width\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 number(t, most):\n    if len(t) == 0:\n        return [False, 0]\n    start = 0\n    if t[0] == \"-\":\n        start = 1\n    if len(t) == start or len(t) - start > most:\n        return [False, 0]\n    v = 0\n    for c in t[start:len(t)]:\n        if c < \"0\" or c > \"9\":\n            return [False, 0]\n        v = v * 10 + int(c)\n    if start == 1:\n        return [True, 0 - v]\n    return [True, v]\n\ndef hash_text():\n    t = plain(input(\"text (letters, digits, space . - _, up to 12)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    if len(t) > 12:\n        print(\"Type 1 to 12 letters, digits, spaces, dots, dashes or underscores.\")\n        return\n    for c in t:\n        if width.code(c) < 0:\n            print(\"Type 1 to 12 letters, digits, spaces, dots, dashes or underscores.\")\n            return\n    st = width.hash_steps(t)\n    print(\"h = h * 31 + code, one character at a time:\")\n    print(\"  \" + pad(\"char\", 6) + lpad(\"Python\", 24) + lpad(\"32-bit\", 14))\n    parted = False\n    for x in st:\n        line = \"  \" + pad(\"'\" + x[0] + \"'\", 6) + lpad(str(x[1]), 24) + lpad(str(x[2]), 14)\n        if x[1] != x[2] and not parted:\n            line = line + \"   <- wraps here\"\n            parted = True\n        print(line)\n    last = st[len(st) - 1]\n    h = last[1]\n    w = last[2]\n    if h == w:\n        print(\"The hash fits in 32 bits: both give the same number.\")\n    elif w < 0:\n        print(\"The 32-bit hash is negative, \" + str(w) + \", though every code is positive.\")\n    else:\n        print(\"The 32-bit hash is \" + str(w) + \", a plausible number and not Python's.\")\n    a = width.floor_mod(h, 16)\n    b = width.trunc_mod(w, 16)\n    c = width.trunc_mod(width.trunc_mod(w, 16) + 16, 16)\n    print(\"Bucket of 16: Python hash % 16 = \" + str(a) + \"; C, with its hash, hash % 16 = \" + str(b) + \"; written ((hash % 16) + 16) % 16, \" + str(c) + \".\")\n    if b < 0:\n        print(\"C's remainder takes the sign of the hash: a negative index, outside the table.\")\n    if h != w:\n        print(\"The safe index matches Python's because 16 divides 2^32: the wrap keeps the last four bits.\")\n\ndef midpoint():\n    t = plain(input(\"lo (0 to 2147483647)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    a = number(t, 10)\n    if not a[0] or a[1] < 0 or a[1] > 2147483647:\n        print(\"Type a whole number from 0 to 2147483647.\")\n        return\n    t = plain(input(\"hi (lo to 2147483647)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    b = number(t, 10)\n    if not b[0] or b[1] < a[1] or b[1] > 2147483647:\n        print(\"Type a whole number from \" + str(a[1]) + \" to 2147483647.\")\n        return\n    m = width.midpoints(a[1], b[1])\n    print(\"  Python (lo + hi) // 2:        \" + str(m[0]))\n    print(\"  32-bit (lo + hi) / 2:         \" + str(m[1]))\n    print(\"  32-bit lo + (hi - lo) / 2:    \" + str(m[2]))\n    if m[1] != m[0]:\n        print(\"lo + hi is \" + str(a[1] + b[1]) + \", past 2147483647: it wraps, and the midpoint comes out \" + str(m[1]) + \". Written lo + (hi - lo) / 2, nothing overflows.\")\n    else:\n        print(\"lo + hi fits in 32 bits: all three agree.\")\n\ndef factorials():\n    t = plain(input(\"up to (1 to 20)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    n = number(t, 2)\n    if not n[0] or n[1] < 1 or n[1] > 20:\n        print(\"Type a whole number from 1 to 20.\")\n        return\n    print(\"  \" + lpad(\"k\", 3) + lpad(\"k! in Python\", 22) + lpad(\"in 32 bits\", 14))\n    first = 0\n    for x in width.factorials(n[1]):\n        line = \"  \" + lpad(str(x[0]), 3) + lpad(str(x[1]), 22) + lpad(str(x[2]), 14)\n        if x[1] != x[2]:\n            if first == 0:\n                first = x[0]\n            if x[2] < 0:\n                line = line + \"   negative\"\n            else:\n                line = line + \"   wrapped\"\n        print(line)\n    if first == 0:\n        print(\"Every one fits in 32 bits.\")\n    else:\n        print(str(first) + \"! is the first that does not fit: from there the 32-bit column is a plausible number and wrong.\")\n\ndef divide():\n    t = plain(input(\"a (-9999 to 9999)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    a = number(t, 4)\n    if not a[0]:\n        print(\"Type a whole number from -9999 to 9999.\")\n        return\n    t = plain(input(\"b (-9999 to 9999, not 0)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    b = number(t, 4)\n    if not b[0] or b[1] == 0:\n        print(\"Type a whole number from -9999 to 9999, not 0.\")\n        return\n    x = a[1]\n    y = b[1]\n    q1 = width.floor_div(x, y)\n    r1 = width.floor_mod(x, y)\n    q2 = width.trunc_div(x, y)\n    r2 = width.trunc_mod(x, y)\n    print(\"  Python, toward minus infinity: q \" + str(q1) + \", r \" + str(r1) + \"; q * b + r = \" + str(q1 * y + r1))\n    print(\"  C and Java, toward zero:       q \" + str(q2) + \", r \" + str(r2) + \"; q * b + r = \" + str(q2 * y + r2))\n    if q1 == q2:\n        print(\"The two agree: the quotient is exact, or a and b have the same sign.\")\n    else:\n        print(\"Both keep q * b + r = a and differ by one in q: the signs differ and the division is not exact.\")\n        if y > 0:\n            print(\"As an index into \" + str(y) + \" slots, Python's r is slot \" + str(r1) + \"; C's is \" + str(r2) + \", outside them.\")\n\nprint(\"== Fixed-width integers ==\")\nprint(\"Arithmetic in Python's integers and in a 32-bit int as C and Java have it.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        hash_text()\n    elif choice == \"2\":\n        midpoint()\n    elif choice == \"3\":\n        factorials()\n    elif choice == \"4\":\n        divide()\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "width.eml",
      "eml": "# P150 fixed-width integers - the same arithmetic in Python's integers and\n# in a 32-bit int as C and Java have it. As in the corpus case\n# fixed-width-overflow, Python never overflows and a fixed-width language\n# wraps, quietly, to a plausible number - a hash, a midpoint, a factorial -\n# and can wrap to the wrong sign. As in the corpus case\n# integer-division-sign, Python's division rounds toward minus infinity and\n# C's toward zero, and the remainder follows; the two disagree exactly on\n# the negative values that wrap backwards, a negative hash among them. Two's\n# complement is arithmetic mod 2^32 with the top half read as negative, so\n# it is written with %.\n\ndef two31():\n    return 2147483648\n\ndef two32():\n    return 4294967296\n\ndef wrap32(n):\n    n % two32() => m\n    if m >= two31():\n        return m - two32()\n    return m\n\ndef code(c):\n    # the character code of a letter, digit, space, dot, dash or underscore\n    \"0123456789\" => d\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" => up\n    \"abcdefghijklmnopqrstuvwxyz\" => low\n    0 => i\n    while i < 26:\n        if i < 10 and d[i] == c:\n            return 48 + i\n        if up[i] == c:\n            return 65 + i\n        if low[i] == c:\n            return 97 + i\n        i + 1 => i\n    if c == \" \":\n        return 32\n    if c == \"-\":\n        return 45\n    if c == \".\":\n        return 46\n    if c == \"_\":\n        return 95\n    return 0 - 1\n\ndef hash_steps(s):\n    # [character, the hash in Python, the hash in 32 bits] after each one:\n    # h = h * 31 + code, as Java's String.hashCode\n    0 => h\n    0 => w\n    [] => out\n    for c in s:\n        h * 31 + code(c) => h\n        wrap32(w * 31 + code(c)) => w\n        out + [[c, h, w]] => out\n    return out\n\ndef floor_div(a, b):\n    # Python's a // b, written without it: the truncated quotient, one less\n    # when the signs differ and it is not exact\n    trunc_div(a, b) => q\n    if q * b != a and (a < 0) != (b < 0):\n        q - 1 => q\n    return q\n\ndef trunc_div(a, b):\n    # C's a / b on ints: toward zero; exact for big values too, the float\n    # quotient corrected step by step\n    0 => neg\n    a => x\n    b => y\n    if x < 0:\n        0 - x => x\n        1 - neg => neg\n    if y < 0:\n        0 - y => y\n        1 - neg => neg\n    int(x / y) => q\n    while q * y > x:\n        q - 1 => q\n    while (q + 1) * y <= x:\n        q + 1 => q\n    if neg == 1:\n        return 0 - q\n    return q\n\ndef floor_mod(a, b):\n    return a - floor_div(a, b) * b\n\ndef trunc_mod(a, b):\n    return a - trunc_div(a, b) * b\n\ndef midpoints(lo, hi):\n    # [Python (lo + hi) // 2, 32-bit (lo + hi) / 2, 32-bit lo + (hi - lo) / 2]\n    return [floor_div(lo + hi, 2), trunc_div(wrap32(lo + hi), 2), wrap32(lo + trunc_div(wrap32(hi - lo), 2))]\n\ndef factorials(n):\n    # [k, k! in Python, k! in 32 bits] for k from 1 to n\n    1 => f\n    1 => w\n    [] => out\n    1 => k\n    while k <= n:\n        f * k => f\n        wrap32(w * k) => w\n        out + [[k, f, w]] => out\n        k + 1 => k\n    return out\n",
      "python": "def two31():\n    return 2147483648\n\ndef two32():\n    return 4294967296\n\ndef wrap32(n):\n    m = n % two32()\n    if m >= two31():\n        return m - two32()\n    return m\n\ndef code(c):\n    d = \"0123456789\"\n    up = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    low = \"abcdefghijklmnopqrstuvwxyz\"\n    i = 0\n    while i < 26:\n        if i < 10 and d[i] == c:\n            return 48 + i\n        if up[i] == c:\n            return 65 + i\n        if low[i] == c:\n            return 97 + i\n        i = i + 1\n    if c == \" \":\n        return 32\n    if c == \"-\":\n        return 45\n    if c == \".\":\n        return 46\n    if c == \"_\":\n        return 95\n    return 0 - 1\n\ndef hash_steps(s):\n    h = 0\n    w = 0\n    out = []\n    for c in s:\n        h = h * 31 + code(c)\n        w = wrap32(w * 31 + code(c))\n        out = out + [[c, h, w]]\n    return out\n\ndef floor_div(a, b):\n    q = trunc_div(a, b)\n    if q * b != a and (a < 0) != (b < 0):\n        q = q - 1\n    return q\n\ndef trunc_div(a, b):\n    neg = 0\n    x = a\n    y = b\n    if x < 0:\n        x = 0 - x\n        neg = 1 - neg\n    if y < 0:\n        y = 0 - y\n        neg = 1 - neg\n    q = int(x / y)\n    while q * y > x:\n        q = q - 1\n    while (q + 1) * y <= x:\n        q = q + 1\n    if neg == 1:\n        return 0 - q\n    return q\n\ndef floor_mod(a, b):\n    return a - floor_div(a, b) * b\n\ndef trunc_mod(a, b):\n    return a - trunc_div(a, b) * b\n\ndef midpoints(lo, hi):\n    return [floor_div(lo + hi, 2), trunc_div(wrap32(lo + hi), 2), wrap32(lo + trunc_div(wrap32(hi - lo), 2))]\n\ndef factorials(n):\n    f = 1\n    w = 1\n    out = []\n    k = 1\n    while k <= n:\n        f = f * k\n        w = wrap32(w * k)\n        out = out + [[k, f, w]]\n        k = k + 1\n    return out\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n\n1\ntoolongtext123\n1\na!b\n2\n\n2\n-1\n2\n2147483648\n2\n100\n\n2\n100\n50\n2\n100\nx\n3\n\n3\n0\n3\n21\n3\nx\n4\n\n4\n10000\n4\n7\n\n4\n7\n0\n4\n7\nx\n4\n-\n5\n",
      "screen": "== Fixed-width integers ==\nArithmetic in Python's integers and in a 32-bit int as C and Java have it.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 1\ntext (letters, digits, space . - _, up to 12)> \nCancelled.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 1\ntext (letters, digits, space . - _, up to 12)> toolongtext123\nType 1 to 12 letters, digits, spaces, dots, dashes or underscores.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 1\ntext (letters, digits, space . - _, up to 12)> a!b\nType 1 to 12 letters, digits, spaces, dots, dashes or underscores.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> \nCancelled.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> -1\nType a whole number from 0 to 2147483647.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> 2147483648\nType a whole number from 0 to 2147483647.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> 100\nhi (lo to 2147483647)> \nCancelled.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> 100\nhi (lo to 2147483647)> 50\nType a whole number from 100 to 2147483647.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> 100\nhi (lo to 2147483647)> x\nType a whole number from 100 to 2147483647.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 3\nup to (1 to 20)> \nCancelled.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 3\nup to (1 to 20)> 0\nType a whole number from 1 to 20.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 3\nup to (1 to 20)> 21\nType a whole number from 1 to 20.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 3\nup to (1 to 20)> x\nType a whole number from 1 to 20.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> \nCancelled.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> 10000\nType a whole number from -9999 to 9999.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> 7\nb (-9999 to 9999, not 0)> \nCancelled.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> 7\nb (-9999 to 9999, not 0)> 0\nType a whole number from -9999 to 9999, not 0.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> 7\nb (-9999 to 9999, not 0)> x\nType a whole number from -9999 to 9999, not 0.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> -\nType a whole number from -9999 to 9999.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\nhello\n1\nHello world\n2\n1500000000\n2000000000\n2\n10\n20\n3\n20\n4\n-7\n2\n4\n7\n2\n4\n-7\n-2\n4\n7\n-2\n5\n",
      "screen": "== Fixed-width integers ==\nArithmetic in Python's integers and in a 32-bit int as C and Java have it.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 1\ntext (letters, digits, space . - _, up to 12)> hello\nh = h * 31 + code, one character at a time:\n  char                    Python        32-bit\n  'h'                        104           104\n  'e'                       3325          3325\n  'l'                     103183        103183\n  'l'                    3198781       3198781\n  'o'                   99162322      99162322\nThe hash fits in 32 bits: both give the same number.\nBucket of 16: Python hash % 16 = 2; C, with its hash, hash % 16 = 2; written ((hash % 16) + 16) % 16, 2.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 1\ntext (letters, digits, space . - _, up to 12)> Hello world\nh = h * 31 + code, one character at a time:\n  char                    Python        32-bit\n  'H'                         72            72\n  'e'                       2333          2333\n  'l'                      72431         72431\n  'l'                    2245469       2245469\n  'o'                   69609650      69609650\n  ' '                 2157899182   -2137068114   <- wraps here\n  'w'                66894874761   -1824601975\n  'o'              2073741117702    -728086266\n  'r'             64285974648876   -1095837652\n  'l'           1992865214115264     388771264\n  'd'          61778821637573284    -832992604\nThe 32-bit hash is negative, -832992604, though every code is positive.\nBucket of 16: Python hash % 16 = 4; C, with its hash, hash % 16 = -12; written ((hash % 16) + 16) % 16, 4.\nC's remainder takes the sign of the hash: a negative index, outside the table.\nThe safe index matches Python's because 16 divides 2^32: the wrap keeps the last four bits.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> 1500000000\nhi (lo to 2147483647)> 2000000000\n  Python (lo + hi) // 2:        1750000000\n  32-bit (lo + hi) / 2:         -397483648\n  32-bit lo + (hi - lo) / 2:    1750000000\nlo + hi is 3500000000, past 2147483647: it wraps, and the midpoint comes out -397483648. Written lo + (hi - lo) / 2, nothing overflows.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 2\nlo (0 to 2147483647)> 10\nhi (lo to 2147483647)> 20\n  Python (lo + hi) // 2:        15\n  32-bit (lo + hi) / 2:         15\n  32-bit lo + (hi - lo) / 2:    15\nlo + hi fits in 32 bits: all three agree.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 3\nup to (1 to 20)> 20\n    k          k! in Python    in 32 bits\n    1                     1             1\n    2                     2             2\n    3                     6             6\n    4                    24            24\n    5                   120           120\n    6                   720           720\n    7                  5040          5040\n    8                 40320         40320\n    9                362880        362880\n   10               3628800       3628800\n   11              39916800      39916800\n   12             479001600     479001600\n   13            6227020800    1932053504   wrapped\n   14           87178291200    1278945280   wrapped\n   15         1307674368000    2004310016   wrapped\n   16        20922789888000    2004189184   wrapped\n   17       355687428096000    -288522240   negative\n   18      6402373705728000    -898433024   negative\n   19    121645100408832000     109641728   wrapped\n   20   2432902008176640000   -2102132736   negative\n13! is the first that does not fit: from there the 32-bit column is a plausible number and wrong.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> -7\nb (-9999 to 9999, not 0)> 2\n  Python, toward minus infinity: q -4, r 1; q * b + r = -7\n  C and Java, toward zero:       q -3, r -1; q * b + r = -7\nBoth keep q * b + r = a and differ by one in q: the signs differ and the division is not exact.\nAs an index into 2 slots, Python's r is slot 1; C's is -1, outside them.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> 7\nb (-9999 to 9999, not 0)> 2\n  Python, toward minus infinity: q 3, r 1; q * b + r = 7\n  C and Java, toward zero:       q 3, r 1; q * b + r = 7\nThe two agree: the quotient is exact, or a and b have the same sign.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> -7\nb (-9999 to 9999, not 0)> -2\n  Python, toward minus infinity: q 3, r -1; q * b + r = -7\n  C and Java, toward zero:       q 3, r -1; q * b + r = -7\nThe two agree: the quotient is exact, or a and b have the same sign.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 4\na (-9999 to 9999)> 7\nb (-9999 to 9999, not 0)> -2\n  Python, toward minus infinity: q -4, r -1; q * b + r = 7\n  C and Java, toward zero:       q -3, r 1; q * b + r = 7\nBoth keep q * b + r = a and differ by one in q: the signs differ and the division is not exact.\n\n1) hash a text  2) a midpoint  3) factorials  4) divide  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "fixed-width-overflow",
      "caseId": "238-fixed-width-overflow",
      "title": "Correct in Python, wrong everywhere it will be ported"
    },
    {
      "slug": "integer-division-sign",
      "caseId": "239-integer-division-sign",
      "title": "Two right answers to -7 / 2"
    }
  ],
  "updated": "2026-10-18"
}
