{
  "id": "P081",
  "slug": "big-numbers",
  "key": "P081-big-numbers",
  "title": "Big numbers",
  "summary": "Whole numbers of any size kept exact: factorials with their digits and trailing zeros checked by Legendre's formula, binomial coefficients built from prime powers without dividing, powers by repeated squaring, and long division of one big number by another.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P081 - Big numbers\n\nWhole numbers of any size, kept exact. Factorials, with their digits and\ntrailing zeros checked against Legendre's formula; binomial coefficients\nbuilt from prime powers, without dividing; powers by repeated squaring;\nand long division of one big number by another.\n\n- `main.eml` - the menu, reading numbers, and the reports\n- `bignum.eml` - long division, factorials, the sieve, Legendre's formula,\n  binomial coefficients from prime powers, powers, digit sums and zeros\n\nHow each part works:\n\n- Division: EML-P has no // operator, and int(a / b) goes through a\n  float, which is exact only below 2^53. So, as in the corpus case\n  `long-division-exact`, division is long division, one decimal digit at\n  a time. The running remainder stays below the divisor, so each digit\n  takes at most 9 subtractions, however big the numbers are. The program\n  checks every answer: quotient x divisor + remainder is the number, and\n  the remainder is smaller than the divisor.\n- Factorials: as in the corpus case `factorial-exact-digits`, the zeros at\n  the end are checked by Legendre's formula. The power of a prime p in n!\n  is n/p + n/p^2 + ... rounded down. These are divisions of small\n  numbers, so the float route is exact. The power of 2 is always larger\n  than the power of 5, so the count of 5s is the count of zeros. The\n  program counts the zeros on the digits too.\n- Binomial coefficients: n choose k is the product of primes, each to its\n  power in n! less its powers in k! and (n - k)!. Only multiplication is\n  needed, so nothing is divided.\n- Powers: repeated squaring, one squaring for each bit of the exponent and\n  one more multiplication for each 1 bit: 2^1000 takes 15 multiplications.\n\nWhat is checked: menu choices 1 to 5; n from 0 to 1000 and k from 0 to n;\na base from 2 to 999,999 and an exponent from 1 to 2000; numbers to divide\nof up to 100 digits, and a divisor that is not 0. An empty answer cancels.\nA number of more than 60 digits shows its first and last 25.\n\nSessions: `sessions/basic.in` gives:\n- 50!, 1000! (2568 digits, 249 zeros) and 0!;\n- 1000 choose 500 (300 digits), 52 choose 5 (2,598,960 poker hands) and\n  10 choose 3;\n- 2^1000 and 3^4;\n- 123456789012345678901234567890 divided by 7, and 2^100 by 3^40.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- n of 1001, -5 and abc; k above n; base 1 and exponent 2001; each then\n  cancelled;\n- \"12a\" and a 101-digit number to divide, then a division by 0.\n\nBuilt on the verified corpus cases `long-division-exact` (division digit\nby digit, exact at any size) and `factorial-exact-digits` (50! checked by\nLegendre's formula rather than by itself).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P081 big numbers: exact factorials, binomial coefficients, powers and\n# division of big numbers, each checked against something that does not\n# use the number itself.\nimport bignum\n\ndef trim(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 whole(s, most):\n    # A whole number of up to `most` digits, or -1.\n    if len(s) == 0 or len(s) > most:\n        return -1\n    0 => n\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef ask(prompt, low, high):\n    while True:\n        trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \")) => a\n        if a == \"\":\n            return -1\n        whole(a, 6) => n\n        if n >= low and n <= high:\n            return n\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n\ndef shown(x):\n    # The whole number if it is short; otherwise its first and last 25\n    # digits and how many there are.\n    str(x) => t\n    if len(t) <= 60:\n        return t\n    return t[0:25] + \"...\" + t[len(t) - 25:len(t)] + \" (\" + str(len(t)) + \" digits)\"\n\ndef fact():\n    ask(\"n\", 0, 1000) => n\n    if n == -1:\n        \"Cancelled.\" ^0\n        return\n    bignum.factorial(n) => f\n    (str(n) + \"! = \" + shown(f)) ^0\n    (\"Digits: \" + str(len(str(f))) + \"; their sum: \" + str(bignum.digit_sum(f)) + \".\") ^0\n    bignum.legendre(n, 5) => z\n    bignum.zeros_at_end(f) => counted\n    (\"Zeros at the end: \" + str(counted) + \" counted, and Legendre's n/5 + n/25 + n/125 + ... gives \" + str(z) + \".\") ^0\n    if n >= 2:\n        (\"The power of 2 in it is \" + str(bignum.legendre(n, 2)) + \", more than the \" + str(z) + \" of 5, so every 5 finds a 2 to make a 10.\") ^0\n\ndef choose():\n    ask(\"n\", 0, 1000) => n\n    if n == -1:\n        \"Cancelled.\" ^0\n        return\n    ask(\"k\", 0, n) => k\n    if k == -1:\n        \"Cancelled.\" ^0\n        return\n    bignum.binomial(n, k) => b\n    (str(n) + \" choose \" + str(k) + \" = \" + shown(b[0])) ^0\n    if len(b[1]) == 0:\n        \"No prime divides it: it is 1.\" ^0\n        return\n    \"\" => line\n    0 => shown_count\n    for pe in b[1]:\n        if shown_count < 12:\n            if line != \"\":\n                line + \" x \" => line\n            if pe[1] == 1:\n                line + str(pe[0]) => line\n            else:\n                line + str(pe[0]) + \"^\" + str(pe[1]) => line\n        shown_count + 1 => shown_count\n    if shown_count > 12:\n        line + \" x ... (\" + str(shown_count) + \" primes in all)\" => line\n    (\"As primes: \" + line) ^0\n    \"Each prime's power is its power in n! less those in k! and (n - k)!, so nothing is divided.\" ^0\n\ndef pow_():\n    ask(\"base\", 2, 999999) => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return\n    ask(\"exponent\", 1, 2000) => e\n    if e == -1:\n        \"Cancelled.\" ^0\n        return\n    bignum.power(a, e) => r\n    (str(a) + \"^\" + str(e) + \" = \" + shown(r[0])) ^0\n    (\"Repeated squaring took \" + str(r[1]) + \" multiplications; one factor at a time takes \" + str(e - 1) + \".\") ^0\n\ndef ask_big(prompt):\n    while True:\n        trim(input(prompt)) => s\n        if s == \"\":\n            return -1\n        whole(s, 100) => n\n        if n >= 0:\n            return n\n        \"Type a whole number of up to 100 digits.\" ^0\n\ndef div():\n    ask_big(\"number to divide (up to 100 digits)> \") => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return\n    while True:\n        ask_big(\"divide by (up to 100 digits, not 0)> \") => b\n        if b == -1:\n            \"Cancelled.\" ^0\n            return\n        if b > 0:\n            bignum.divide(a, b) => qr\n            (\"quotient  \" + shown(qr[0])) ^0\n            (\"remainder \" + shown(qr[1])) ^0\n            if qr[0] * b + qr[1] == a and qr[1] < b:\n                \"Check: quotient x divisor + remainder is the number, and the remainder is less than the divisor.\" ^0\n            return\n        \"Dividing by 0 has no answer.\" ^0\n\n\"== Big numbers ==\" ^0\n\"Exact factorials, binomial coefficients, powers and long division - no digit lost.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) factorial  2) n choose k  3) power  4) divide  5) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        fact()\n    elif choice == \"2\":\n        choose()\n    elif choice == \"3\":\n        pow_()\n    elif choice == \"4\":\n        div()\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import bignum\n\ndef trim(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 whole(s, most):\n    if len(s) == 0 or len(s) > most:\n        return -1\n    n = 0\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef ask(prompt, low, high):\n    while True:\n        a = trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \"))\n        if a == \"\":\n            return -1\n        n = whole(a, 6)\n        if n >= low and n <= high:\n            return n\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n\ndef shown(x):\n    t = str(x)\n    if len(t) <= 60:\n        return t\n    return t[0:25] + \"...\" + t[len(t) - 25:len(t)] + \" (\" + str(len(t)) + \" digits)\"\n\ndef fact():\n    n = ask(\"n\", 0, 1000)\n    if n == -1:\n        print(\"Cancelled.\")\n        return\n    f = bignum.factorial(n)\n    print(str(n) + \"! = \" + shown(f))\n    print(\"Digits: \" + str(len(str(f))) + \"; their sum: \" + str(bignum.digit_sum(f)) + \".\")\n    z = bignum.legendre(n, 5)\n    counted = bignum.zeros_at_end(f)\n    print(\"Zeros at the end: \" + str(counted) + \" counted, and Legendre's n/5 + n/25 + n/125 + ... gives \" + str(z) + \".\")\n    if n >= 2:\n        print(\"The power of 2 in it is \" + str(bignum.legendre(n, 2)) + \", more than the \" + str(z) + \" of 5, so every 5 finds a 2 to make a 10.\")\n\ndef choose():\n    n = ask(\"n\", 0, 1000)\n    if n == -1:\n        print(\"Cancelled.\")\n        return\n    k = ask(\"k\", 0, n)\n    if k == -1:\n        print(\"Cancelled.\")\n        return\n    b = bignum.binomial(n, k)\n    print(str(n) + \" choose \" + str(k) + \" = \" + shown(b[0]))\n    if len(b[1]) == 0:\n        print(\"No prime divides it: it is 1.\")\n        return\n    line = \"\"\n    shown_count = 0\n    for pe in b[1]:\n        if shown_count < 12:\n            if line != \"\":\n                line = line + \" x \"\n            if pe[1] == 1:\n                line = line + str(pe[0])\n            else:\n                line = line + str(pe[0]) + \"^\" + str(pe[1])\n        shown_count = shown_count + 1\n    if shown_count > 12:\n        line = line + \" x ... (\" + str(shown_count) + \" primes in all)\"\n    print(\"As primes: \" + line)\n    print(\"Each prime's power is its power in n! less those in k! and (n - k)!, so nothing is divided.\")\n\ndef pow_():\n    a = ask(\"base\", 2, 999999)\n    if a == -1:\n        print(\"Cancelled.\")\n        return\n    e = ask(\"exponent\", 1, 2000)\n    if e == -1:\n        print(\"Cancelled.\")\n        return\n    r = bignum.power(a, e)\n    print(str(a) + \"^\" + str(e) + \" = \" + shown(r[0]))\n    print(\"Repeated squaring took \" + str(r[1]) + \" multiplications; one factor at a time takes \" + str(e - 1) + \".\")\n\ndef ask_big(prompt):\n    while True:\n        s = trim(input(prompt))\n        if s == \"\":\n            return -1\n        n = whole(s, 100)\n        if n >= 0:\n            return n\n        print(\"Type a whole number of up to 100 digits.\")\n\ndef div():\n    a = ask_big(\"number to divide (up to 100 digits)> \")\n    if a == -1:\n        print(\"Cancelled.\")\n        return\n    while True:\n        b = ask_big(\"divide by (up to 100 digits, not 0)> \")\n        if b == -1:\n            print(\"Cancelled.\")\n            return\n        if b > 0:\n            qr = bignum.divide(a, b)\n            print(\"quotient  \" + shown(qr[0]))\n            print(\"remainder \" + shown(qr[1]))\n            if qr[0] * b + qr[1] == a and qr[1] < b:\n                print(\"Check: quotient x divisor + remainder is the number, and the remainder is less than the divisor.\")\n            return\n        print(\"Dividing by 0 has no answer.\")\n\nprint(\"== Big numbers ==\")\nprint(\"Exact factorials, binomial coefficients, powers and long division - no digit lost.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) factorial  2) n choose k  3) power  4) divide  5) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        fact()\n    elif choice == \"2\":\n        choose()\n    elif choice == \"3\":\n        pow_()\n    elif choice == \"4\":\n        div()\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "bignum.eml",
      "eml": "# P081 big numbers - whole numbers of any size, kept exact: factorials,\n# binomial coefficients, powers, and division of one big number by another.\n# EML-P has no // operator, and int(a / b) goes through a float, which is\n# exact only below 2^53; so division here is long division, digit by digit.\n\ndef divide(a, b):\n    # The corpus case long-division-exact: [quotient, remainder] of a >= 0\n    # by b > 0, one decimal digit of a at a time. The running remainder\n    # stays below b, so each step needs at most 9 subtractions.\n    0 => q\n    0 => r\n    for ch in str(a):\n        r * 10 + int(ch) => r\n        0 => d\n        while r >= b:\n            r - b => r\n            d + 1 => d\n        q * 10 + d => q\n    return [q, r]\n\ndef factorial(n):\n    1 => out\n    for k in [2:n]:\n        out * k => out\n    return out\n\ndef primes_to(n):\n    # The primes up to n, by the sieve of Eratosthenes.\n    [True] * (n + 1) => flags\n    [] => out\n    for p in [2:n]:\n        if flags[p]:\n            out + [p] => out\n            p * p => m\n            while m <= n:\n                False => flags[m]\n                m + p => m\n    return out\n\ndef legendre(n, p):\n    # The power of the prime p in n!: n/p + n/p^2 + ... rounded down\n    # (Legendre). These divisions are of small numbers, far below 2^53, so\n    # the float route int(n / q) is exact here.\n    0 => e\n    p => q\n    while q <= n:\n        e + int(n / q) => e\n        q * p => q\n    return e\n\ndef binomial(n, k):\n    # n choose k without dividing at all: its power of each prime p is the\n    # power in n! less the powers in k! and (n - k)! (Kummer), and the\n    # number is the product of the primes to those powers. Returns\n    # [value, [[p, power], ...] for the powers above 0].\n    1 => out\n    [] => used\n    for p in primes_to(n):\n        legendre(n, p) - legendre(k, p) - legendre(n - k, p) => e\n        if e > 0:\n            used + [[p, e]] => used\n            for i in [1:e]:\n                out * p => out\n    return [out, used]\n\ndef power(a, e):\n    # a^e by repeated squaring: [value, multiplications made].\n    1 => out\n    a => b\n    0 => count\n    e => left\n    while left > 0:\n        if left % 2 == 1:\n            out * b => out\n            count + 1 => count\n        divide(left, 2)[0] => left\n        if left > 0:\n            b * b => b\n            count + 1 => count\n    return [out, count]\n\ndef digit_sum(x):\n    0 => s\n    for ch in str(x):\n        s + int(ch) => s\n    return s\n\ndef zeros_at_end(x):\n    # Counted on the digits themselves, independently of Legendre.\n    str(x) => t\n    0 => k\n    while k < len(t) - 1 and t[len(t) - 1 - k] == \"0\":\n        k + 1 => k\n    return k\n",
      "python": "def divide(a, b):\n    q = 0\n    r = 0\n    for ch in str(a):\n        r = r * 10 + int(ch)\n        d = 0\n        while r >= b:\n            r = r - b\n            d = d + 1\n        q = q * 10 + d\n    return [q, r]\n\ndef factorial(n):\n    out = 1\n    for k in range(2, n+1):\n        out = out * k\n    return out\n\ndef primes_to(n):\n    flags = [True] * (n + 1)\n    out = []\n    for p in range(2, n+1):\n        if flags[p]:\n            out = out + [p]\n            m = p * p\n            while m <= n:\n                flags[m] = False\n                m = m + p\n    return out\n\ndef legendre(n, p):\n    e = 0\n    q = p\n    while q <= n:\n        e = e + int(n / q)\n        q = q * p\n    return e\n\ndef binomial(n, k):\n    out = 1\n    used = []\n    for p in primes_to(n):\n        e = legendre(n, p) - legendre(k, p) - legendre(n - k, p)\n        if e > 0:\n            used = used + [[p, e]]\n            for i in range(1, e+1):\n                out = out * p\n    return [out, used]\n\ndef power(a, e):\n    out = 1\n    b = a\n    count = 0\n    left = e\n    while left > 0:\n        if left % 2 == 1:\n            out = out * b\n            count = count + 1\n        left = divide(left, 2)[0]\n        if left > 0:\n            b = b * b\n            count = count + 1\n    return [out, count]\n\ndef digit_sum(x):\n    s = 0\n    for ch in str(x):\n        s = s + int(ch)\n    return s\n\ndef zeros_at_end(x):\n    t = str(x)\n    k = 0\n    while k < len(t) - 1 and t[len(t) - 1 - k] == \"0\":\n        k = k + 1\n    return k\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n1001\n-5\nabc\n\n2\n5\n6\n\n3\n1\n2\n2001\n\n4\n12a\n11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\n100\n0\n\n5\n",
      "screen": "== Big numbers ==\nExact factorials, binomial coefficients, powers and long division - no digit lost.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 1\nn (0 to 1000)> 1001\nType a whole number from 0 to 1000.\nn (0 to 1000)> -5\nType a whole number from 0 to 1000.\nn (0 to 1000)> abc\nType a whole number from 0 to 1000.\nn (0 to 1000)> \nCancelled.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 2\nn (0 to 1000)> 5\nk (0 to 5)> 6\nType a whole number from 0 to 5.\nk (0 to 5)> \nCancelled.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 3\nbase (2 to 999999)> 1\nType a whole number from 2 to 999999.\nbase (2 to 999999)> 2\nexponent (1 to 2000)> 2001\nType a whole number from 1 to 2000.\nexponent (1 to 2000)> \nCancelled.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 4\nnumber to divide (up to 100 digits)> 12a\nType a whole number of up to 100 digits.\nnumber to divide (up to 100 digits)> 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\nType a whole number of up to 100 digits.\nnumber to divide (up to 100 digits)> 100\ndivide by (up to 100 digits, not 0)> 0\nDividing by 0 has no answer.\ndivide by (up to 100 digits, not 0)> \nCancelled.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n50\n1\n1000\n1\n0\n2\n1000\n500\n2\n52\n5\n2\n10\n3\n3\n2\n1000\n3\n3\n4\n4\n123456789012345678901234567890\n7\n4\n1267650600228229401496703205376\n12157665459056928801\n5\n",
      "screen": "== Big numbers ==\nExact factorials, binomial coefficients, powers and long division - no digit lost.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 1\nn (0 to 1000)> 50\n50! = 3041409320171337804361260...7641568960512000000000000 (65 digits)\nDigits: 65; their sum: 216.\nZeros at the end: 12 counted, and Legendre's n/5 + n/25 + n/125 + ... gives 12.\nThe power of 2 in it is 47, more than the 12 of 5, so every 5 finds a 2 to make a 10.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 1\nn (0 to 1000)> 1000\n1000! = 4023872600770937735437024...0000000000000000000000000 (2568 digits)\nDigits: 2568; their sum: 10539.\nZeros at the end: 249 counted, and Legendre's n/5 + n/25 + n/125 + ... gives 249.\nThe power of 2 in it is 994, more than the 249 of 5, so every 5 finds a 2 to make a 10.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 1\nn (0 to 1000)> 0\n0! = 1\nDigits: 1; their sum: 1.\nZeros at the end: 0 counted, and Legendre's n/5 + n/25 + n/125 + ... gives 0.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 2\nn (0 to 1000)> 1000\nk (0 to 1000)> 500\n1000 choose 500 = 2702882409454365695156146...8109296905863799821216320 (300 digits)\nAs primes: 2^6 x 3^4 x 5 x 13 x 17 x 23^2 x 29 x 31 x 37 x 43 x 47 x 73 x ... (116 primes in all)\nEach prime's power is its power in n! less those in k! and (n - k)!, so nothing is divided.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 2\nn (0 to 1000)> 52\nk (0 to 52)> 5\n52 choose 5 = 2598960\nAs primes: 2^4 x 3 x 5 x 7^2 x 13 x 17\nEach prime's power is its power in n! less those in k! and (n - k)!, so nothing is divided.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 2\nn (0 to 1000)> 10\nk (0 to 10)> 3\n10 choose 3 = 120\nAs primes: 2^3 x 3 x 5\nEach prime's power is its power in n! less those in k! and (n - k)!, so nothing is divided.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 3\nbase (2 to 999999)> 2\nexponent (1 to 2000)> 1000\n2^1000 = 1071508607186267320948425...1652624386837205668069376 (302 digits)\nRepeated squaring took 15 multiplications; one factor at a time takes 999.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 3\nbase (2 to 999999)> 3\nexponent (1 to 2000)> 4\n3^4 = 81\nRepeated squaring took 3 multiplications; one factor at a time takes 3.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 4\nnumber to divide (up to 100 digits)> 123456789012345678901234567890\ndivide by (up to 100 digits, not 0)> 7\nquotient  17636684144620811271604938270\nremainder 0\nCheck: quotient x divisor + remainder is the number, and the remainder is less than the divisor.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 4\nnumber to divide (up to 100 digits)> 1267650600228229401496703205376\ndivide by (up to 100 digits, not 0)> 12157665459056928801\nquotient  104267600099\nremainder 5856291598919654077\nCheck: quotient x divisor + remainder is the number, and the remainder is less than the divisor.\n\n1) factorial  2) n choose k  3) power  4) divide  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "long-division-exact",
      "caseId": "190-long-division-exact",
      "title": "Exact integer division, by hand"
    },
    {
      "slug": "factorial-exact-digits",
      "caseId": "188-factorial-exact-digits",
      "title": "50! exactly, checked four ways"
    }
  ],
  "updated": "2026-10-15"
}
