{
  "id": "P068",
  "slug": "prime-explorer",
  "key": "P068-prime-explorer",
  "title": "Prime explorer",
  "summary": "The primes up to 100,000 by the sieve of Eratosthenes, any number up to a trillion broken into prime factors, twin primes, every way an even number is a sum of two primes, and the gaps between primes with how often each occurs.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P068 - Prime explorer\n\nThe primes up to a limit, by the sieve of Eratosthenes. A number up to\n999,999,999,999 broken into its prime factors. The twin primes, the ways an\neven number is a sum of two primes (Goldbach's question), and the gaps\nbetween primes, with how often each gap occurs.\n\n- `main.eml` - the menu, reading numbers, and the reports\n- `primes.eml` - the sieve, the list of primes, factorisation, twins,\n  Goldbach sums and gaps\n\nHow each part works:\n\n- Sieve: as in the corpus case `sieve-of-eratosthenes`, start with every\n  number marked prime, and for each prime p cross out p x p, p x p + p,\n  and so on. Crossing out can start at p x p, because a smaller multiple of\n  p has a smaller prime factor and is already crossed out. The list of\n  primes is counted first and then filled, rather than grown one prime at\n  a time.\n- Prime factors: as in the corpus case `prime-factorization`, divide by 2\n  as often as it goes, then by 3, 5, 7 and the odd numbers after, while\n  d x d is at most what is left. Whatever is left above 1 then is prime.\n  So a 12-digit number needs at most about 500,000 trial divisions:\n  600851475143 = 71 x 839 x 1471 x 6857.\n- Twin primes are primes two apart. Goldbach sums try every p from 2 to\n  n/2 and keep those where the sieve marks both p and n - p prime.\n- Gaps: the difference between each prime and the next, counted. Up to\n  1000 the largest gap is 20, from 887 to 907.\n\nWhat is checked: menu choices 1 to 6; a limit from 2 to 100,000 for the\nprimes, 5 to 100,000 for twins and 10 to 100,000 for gaps; a number from 2\nto 999,999,999,999 to factor; an even number from 4 to 100,000 for\nGoldbach. An empty answer cancels. Long lists show their first 40 primes,\n20 twin pairs or 12 sums, and say how many more there are.\n\nSessions: `sessions/basic.in` gives:\n- the 25 primes up to 100, and the 1229 up to 10,000;\n- 600851475143, the prime 1000003, and 360 = 2^3 x 3^2 x 5;\n- the 35 twin prime pairs up to 1000;\n- the 6 ways of writing 100 as a sum of two primes;\n- the gaps up to 1000: 167 of them, the largest 20.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- limits 1, 100001 and abc, then a cancel;\n- the numbers 1 and 1,000,000,000,000 to factor, then a cancel;\n- 7 for Goldbach (odd) and 2 (too small), then a cancel;\n- 9 as a gap limit, then a cancel.\n\nBuilt on the verified corpus cases `sieve-of-eratosthenes` (crossing out\nmultiples) and `prime-factorization` (trial division with multiplicity).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P068 prime explorer: the primes up to a limit by the sieve of\n# Eratosthenes, a number broken into prime factors, twin primes, the ways\n# an even number is a sum of two primes, and the gaps between primes.\nimport primes\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 counted(k, one, many):\n    if k == 1:\n        return \"1 \" + one\n    return str(k) + \" \" + many\n\ndef number(s):\n    if s == \"\" or len(s) > 12:\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) + \")> \")) => answer\n        if answer == \"\":\n            return -1\n        number(answer) => 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 listed(xs, most):\n    # The numbers with spaces, cut after `most` with how many more.\n    \"\" => s\n    for k in [0:len(xs) - 1]:\n        if k == most:\n            return s + \" ... and \" + str(len(xs) - most) + \" more\"\n        if s != \"\":\n            s + \" \" => s\n        s + str(xs[k]) => s\n    return s\n\ndef up_to():\n    ask(\"limit\", 2, 100000) => n\n    if n == -1:\n        \"Cancelled.\" ^0\n        return\n    primes.primes_up_to(n) => ps\n    (counted(len(ps), \"prime\", \"primes\") + \" up to \" + str(n) + \": \" + listed(ps, 40)) ^0\n\ndef factor():\n    ask(\"number\", 2, 999999999999) => n\n    if n == -1:\n        \"Cancelled.\" ^0\n        return\n    primes.factors(n) => fs\n    \"\" => s\n    for f in fs:\n        if s != \"\":\n            s + \" x \" => s\n        s + str(f[0]) => s\n        if f[1] > 1:\n            s + \"^\" + str(f[1]) => s\n    if len(fs) == 1 and fs[0][1] == 1:\n        (str(n) + \" is prime.\") ^0\n    else:\n        (str(n) + \" = \" + s) ^0\n\ndef twin_primes():\n    ask(\"limit\", 5, 100000) => n\n    if n == -1:\n        \"Cancelled.\" ^0\n        return\n    primes.twins(primes.primes_up_to(n)) => ts\n    [] => shown\n    for t in ts:\n        shown + [str(t[0]) + \"-\" + str(t[1])] => shown\n    (counted(len(ts), \"twin prime pair\", \"twin prime pairs\") + \" up to \" + str(n) + \": \" + listed(shown, 20)) ^0\n\ndef goldbach():\n    while True:\n        ask(\"even number\", 4, 100000) => n\n        if n == -1:\n            \"Cancelled.\" ^0\n            return\n        if n % 2 == 0:\n            break\n        \"Goldbach's question is about even numbers.\" ^0\n    primes.goldbach(n, primes.sieve(n)) => ws\n    [] => shown\n    for w in ws:\n        shown + [str(w[0]) + \"+\" + str(w[1])] => shown\n    if len(ws) == 0:\n        (str(n) + \" is not a sum of two primes - that would answer Goldbach's question.\") ^0\n    else:\n        (str(n) + \" is a sum of two primes in \" + counted(len(ws), \"way\", \"ways\") + \": \" + listed(shown, 12)) ^0\n\ndef gap_report():\n    ask(\"limit\", 10, 100000) => n\n    if n == -1:\n        \"Cancelled.\" ^0\n        return\n    primes.primes_up_to(n) => ps\n    primes.gaps(ps) => g\n    (\"Up to \" + str(n) + \" the largest gap is \" + str(g[0]) + \", first between \" + str(g[1][0]) + \" and \" + str(g[1][1]) + \".\") ^0\n    \"gap  how often\" ^0\n    for k in [1:g[0]]:\n        if k in g[2]:\n            str(k) => a\n            while len(a) < 3:\n                \" \" + a => a\n            str(g[2][k]) => b\n            while len(b) < 6:\n                \" \" + b => b\n            (a + \"  \" + b) ^0\n\n\"== Prime explorer ==\" ^0\n\"Primes by the sieve of Eratosthenes, prime factors, twins, Goldbach sums and gaps.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        up_to()\n    elif choice == \"2\":\n        factor()\n    elif choice == \"3\":\n        twin_primes()\n    elif choice == \"4\":\n        goldbach()\n    elif choice == \"5\":\n        gap_report()\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import primes\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 counted(k, one, many):\n    if k == 1:\n        return \"1 \" + one\n    return str(k) + \" \" + many\n\ndef number(s):\n    if s == \"\" or len(s) > 12:\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        answer = trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \"))\n        if answer == \"\":\n            return -1\n        n = number(answer)\n        if n >= low and n <= high:\n            return n\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n\ndef listed(xs, most):\n    s = \"\"\n    for k in range(0, len(xs)):\n        if k == most:\n            return s + \" ... and \" + str(len(xs) - most) + \" more\"\n        if s != \"\":\n            s = s + \" \"\n        s = s + str(xs[k])\n    return s\n\ndef up_to():\n    n = ask(\"limit\", 2, 100000)\n    if n == -1:\n        print(\"Cancelled.\")\n        return\n    ps = primes.primes_up_to(n)\n    print(counted(len(ps), \"prime\", \"primes\") + \" up to \" + str(n) + \": \" + listed(ps, 40))\n\ndef factor():\n    n = ask(\"number\", 2, 999999999999)\n    if n == -1:\n        print(\"Cancelled.\")\n        return\n    fs = primes.factors(n)\n    s = \"\"\n    for f in fs:\n        if s != \"\":\n            s = s + \" x \"\n        s = s + str(f[0])\n        if f[1] > 1:\n            s = s + \"^\" + str(f[1])\n    if len(fs) == 1 and fs[0][1] == 1:\n        print(str(n) + \" is prime.\")\n    else:\n        print(str(n) + \" = \" + s)\n\ndef twin_primes():\n    n = ask(\"limit\", 5, 100000)\n    if n == -1:\n        print(\"Cancelled.\")\n        return\n    ts = primes.twins(primes.primes_up_to(n))\n    shown = []\n    for t in ts:\n        shown = shown + [str(t[0]) + \"-\" + str(t[1])]\n    print(counted(len(ts), \"twin prime pair\", \"twin prime pairs\") + \" up to \" + str(n) + \": \" + listed(shown, 20))\n\ndef goldbach():\n    while True:\n        n = ask(\"even number\", 4, 100000)\n        if n == -1:\n            print(\"Cancelled.\")\n            return\n        if n % 2 == 0:\n            break\n        print(\"Goldbach's question is about even numbers.\")\n    ws = primes.goldbach(n, primes.sieve(n))\n    shown = []\n    for w in ws:\n        shown = shown + [str(w[0]) + \"+\" + str(w[1])]\n    if len(ws) == 0:\n        print(str(n) + \" is not a sum of two primes - that would answer Goldbach's question.\")\n    else:\n        print(str(n) + \" is a sum of two primes in \" + counted(len(ws), \"way\", \"ways\") + \": \" + listed(shown, 12))\n\ndef gap_report():\n    n = ask(\"limit\", 10, 100000)\n    if n == -1:\n        print(\"Cancelled.\")\n        return\n    ps = primes.primes_up_to(n)\n    g = primes.gaps(ps)\n    print(\"Up to \" + str(n) + \" the largest gap is \" + str(g[0]) + \", first between \" + str(g[1][0]) + \" and \" + str(g[1][1]) + \".\")\n    print(\"gap  how often\")\n    for k in range(1, g[0]+1):\n        if k in g[2]:\n            a = str(k)\n            while len(a) < 3:\n                a = \" \" + a\n            b = str(g[2][k])\n            while len(b) < 6:\n                b = \" \" + b\n            print(a + \"  \" + b)\n\nprint(\"== Prime explorer ==\")\nprint(\"Primes by the sieve of Eratosthenes, prime factors, twins, Goldbach sums and gaps.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        up_to()\n    elif choice == \"2\":\n        factor()\n    elif choice == \"3\":\n        twin_primes()\n    elif choice == \"4\":\n        goldbach()\n    elif choice == \"5\":\n        gap_report()\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "primes.eml",
      "eml": "# P068 prime explorer - the sieve, factoring, and what the primes up to a\n# limit look like: twins, Goldbach's sums and the gaps between them.\n\ndef sieve(n):\n    # The corpus case sieve-of-eratosthenes: start with every number from 2\n    # to n marked prime, and for each prime p cross out p*p, p*p + p, ... -\n    # smaller multiples were crossed out by smaller primes already. Returns\n    # the list of flags, index = number.\n    [True] * (n + 1) => flags\n    False => flags[0]\n    if n >= 1:\n        False => flags[1]\n    2 => p\n    while p * p <= n:\n        if flags[p]:\n            p * p => m\n            while m <= n:\n                False => flags[m]\n                m + p => m\n        p + 1 => p\n    return flags\n\ndef primes_up_to(n):\n    # Counted first, then written into a list made at its full length -\n    # growing a list by + in a loop would copy it at every prime.\n    sieve(n) => flags\n    0 => count\n    for k in [2:n]:\n        if flags[k]:\n            count + 1 => count\n    [0] * count => out\n    0 => i\n    for k in [2:n]:\n        if flags[k]:\n            k => out[i]\n            i + 1 => i\n    return out\n\ndef quotient(a, b):\n    # a // b for a >= 0, b > 0, exact (a float would lose digits past 2^53).\n    0 => q\n    while a >= b:\n        b => m\n        1 => k\n        while m + m <= a:\n            m + m => m\n            k + k => k\n        a - m => a\n        q + k => q\n    return q\n\ndef factors(n):\n    # The corpus case prime-factorization's trial division: divide out 2,\n    # then odd numbers d while d * d <= what is left; what remains above 1 is\n    # prime. Returns [[prime, power], ...].\n    [] => out\n    n => left\n    2 => d\n    while d * d <= left:\n        0 => k\n        while left % d == 0:\n            quotient(left, d) => left\n            k + 1 => k\n        if k > 0:\n            out + [[d, k]] => out\n        if d == 2:\n            3 => d\n        else:\n            d + 2 => d\n    if left > 1:\n        out + [[left, 1]] => out\n    return out\n\ndef twins(ps):\n    # Pairs of primes two apart, like 11 and 13.\n    [] => out\n    for i in [1:len(ps) - 1]:\n        if ps[i] - ps[i - 1] == 2:\n            out + [[ps[i - 1], ps[i]]] => out\n    return out\n\ndef goldbach(n, flags):\n    # Every way to write the even number n > 2 as a sum of two primes p + q\n    # with p <= q.\n    [] => out\n    for p in [2:quotient(n, 2)]:\n        if flags[p] and flags[n - p]:\n            out + [[p, n - p]] => out\n    return out\n\ndef gaps(ps):\n    # [largest gap, the primes either side of its first appearance, how many\n    # times each gap size occurs as a dict].\n    0 => best\n    [0, 0] => where\n    {} => count\n    for i in [1:len(ps) - 1]:\n        ps[i] - ps[i - 1] => g\n        if g in count:\n            count[g] + 1 => count[g]\n        else:\n            1 => count[g]\n        if g > best:\n            g => best\n            [ps[i - 1], ps[i]] => where\n    return [best, where, count]\n",
      "python": "def sieve(n):\n    flags = [True] * (n + 1)\n    flags[0] = False\n    if n >= 1:\n        flags[1] = False\n    p = 2\n    while p * p <= n:\n        if flags[p]:\n            m = p * p\n            while m <= n:\n                flags[m] = False\n                m = m + p\n        p = p + 1\n    return flags\n\ndef primes_up_to(n):\n    flags = sieve(n)\n    count = 0\n    for k in range(2, n+1):\n        if flags[k]:\n            count = count + 1\n    out = [0] * count\n    i = 0\n    for k in range(2, n+1):\n        if flags[k]:\n            out[i] = k\n            i = i + 1\n    return out\n\ndef quotient(a, b):\n    q = 0\n    while a >= b:\n        m = b\n        k = 1\n        while m + m <= a:\n            m = m + m\n            k = k + k\n        a = a - m\n        q = q + k\n    return q\n\ndef factors(n):\n    out = []\n    left = n\n    d = 2\n    while d * d <= left:\n        k = 0\n        while left % d == 0:\n            left = quotient(left, d)\n            k = k + 1\n        if k > 0:\n            out = out + [[d, k]]\n        if d == 2:\n            d = 3\n        else:\n            d = d + 2\n    if left > 1:\n        out = out + [[left, 1]]\n    return out\n\ndef twins(ps):\n    out = []\n    for i in range(1, len(ps)):\n        if ps[i] - ps[i - 1] == 2:\n            out = out + [[ps[i - 1], ps[i]]]\n    return out\n\ndef goldbach(n, flags):\n    out = []\n    for p in range(2, quotient(n, 2)+1):\n        if flags[p] and flags[n - p]:\n            out = out + [[p, n - p]]\n    return out\n\ndef gaps(ps):\n    best = 0\n    where = [0, 0]\n    count = {}\n    for i in range(1, len(ps)):\n        g = ps[i] - ps[i - 1]\n        if g in count:\n            count[g] = count[g] + 1\n        else:\n            count[g] = 1\n        if g > best:\n            best = g\n            where = [ps[i - 1], ps[i]]\n    return [best, where, count]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n1\n100001\nabc\n\n2\n1\n1000000000000\n\n4\n7\n2\n\n5\n9\n\n6\n",
      "screen": "== Prime explorer ==\nPrimes by the sieve of Eratosthenes, prime factors, twins, Goldbach sums and gaps.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 1\nlimit (2 to 100000)> 1\nType a whole number from 2 to 100000.\nlimit (2 to 100000)> 100001\nType a whole number from 2 to 100000.\nlimit (2 to 100000)> abc\nType a whole number from 2 to 100000.\nlimit (2 to 100000)> \nCancelled.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 2\nnumber (2 to 999999999999)> 1\nType a whole number from 2 to 999999999999.\nnumber (2 to 999999999999)> 1000000000000\nType a whole number from 2 to 999999999999.\nnumber (2 to 999999999999)> \nCancelled.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 4\neven number (4 to 100000)> 7\nGoldbach's question is about even numbers.\neven number (4 to 100000)> 2\nType a whole number from 4 to 100000.\neven number (4 to 100000)> \nCancelled.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 5\nlimit (10 to 100000)> 9\nType a whole number from 10 to 100000.\nlimit (10 to 100000)> \nCancelled.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n100\n1\n10000\n2\n600851475143\n2\n1000003\n2\n360\n3\n1000\n4\n100\n5\n1000\n6\n",
      "screen": "== Prime explorer ==\nPrimes by the sieve of Eratosthenes, prime factors, twins, Goldbach sums and gaps.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 1\nlimit (2 to 100000)> 100\n25 primes up to 100: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 1\nlimit (2 to 100000)> 10000\n1229 primes up to 10000: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 ... and 1189 more\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 2\nnumber (2 to 999999999999)> 600851475143\n600851475143 = 71 x 839 x 1471 x 6857\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 2\nnumber (2 to 999999999999)> 1000003\n1000003 is prime.\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 2\nnumber (2 to 999999999999)> 360\n360 = 2^3 x 3^2 x 5\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 3\nlimit (5 to 100000)> 1000\n35 twin prime pairs up to 1000: 3-5 5-7 11-13 17-19 29-31 41-43 59-61 71-73 101-103 107-109 137-139 149-151 179-181 191-193 197-199 227-229 239-241 269-271 281-283 311-313 ... and 15 more\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 4\neven number (4 to 100000)> 100\n100 is a sum of two primes in 6 ways: 3+97 11+89 17+83 29+71 41+59 47+53\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 5\nlimit (10 to 100000)> 1000\nUp to 1000 the largest gap is 20, first between 887 and 907.\ngap  how often\n  1       1\n  2      35\n  4      40\n  6      44\n  8      15\n 10      16\n 12       7\n 14       7\n 18       1\n 20       1\n\n1) primes up to  2) prime factors  3) twin primes  4) Goldbach  5) gaps  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "sieve-of-eratosthenes",
      "caseId": "094-sieve-of-eratosthenes",
      "title": "Sieve of Eratosthenes"
    },
    {
      "slug": "prime-factorization",
      "caseId": "060-prime-factorization",
      "title": "Prime factorization"
    }
  ],
  "updated": "2026-10-14"
}
