{
  "id": "P028",
  "slug": "spelling-suggester",
  "key": "P028-spelling-suggester",
  "title": "Spelling suggester",
  "summary": "Checks a line of text against a list of 352 common English words and suggests, for each word it does not know, the closest words by edit distance - insertions, deletions, substitutions and swaps of two neighbouring letters - the more common word first on a tie; suggests for a single word, and adds new words for the session.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P028 - Spelling suggester\n\nChecks a line of text against a list of 352 common English words. For each\nword it does not know, it suggests the closest words in the list by edit\ndistance. It also suggests for a single word, and new words can be added for\nthe session.\n\n- `main.eml` - the menu, the questions and their checks, and what the screen\n  shows\n- `spelling.eml` - the edit distance, the words of a text, and suggestions\n- `words.eml` - the word list, most common words first\n\nHow each part works:\n\n- The edit distance is the fewest one-letter insertions, deletions and\n  substitutions, and swaps of two neighbouring letters, that turn one word\n  into another. It is the Levenshtein table of the corpus case\n  `edit-distance` with one more way into a cell, for a swap: `teh` is one\n  edit from `the`, not two. (This \"optimal string alignment\" distance never\n  edits a stretch twice, so `ca` is three edits from `abc`.)\n- Only distances that matter are worked out in full: a word whose length\n  differs by more than the limit is skipped at once.\n- Suggestions are the words within 2 edits when checking a line (at most 3)\n  and within 3 edits for a single word (at most 5), nearest first. Two words\n  equally near go in the order of the list, which runs from the most common\n  word down, so `teh` suggests `the` before `be` and `to`.\n- A word is a run of letters and apostrophes in any case; apostrophes at\n  either end are dropped, so `don't` and `it's` are single words and\n  `'quoted'` is `quoted`. A word that appears several times is reported once,\n  with how many times.\n- Added words go to the end of the list, so they rank after the built-in\n  words they tie with; they last until the program ends.\n\nWhat is checked: a line is at most 200 characters; a single word is letters\nwith apostrophes only inside, at most 20 characters; a word already in the\nlist is not added again. An empty answer cancels.\n\nSessions: `sessions/basic.in` checks a line with `teh` twice, `quikc`,\n`jumpd` and `dogg`, and a line with `freind`, `langauge` and `becuase`,\nsuggests for `wrld`, finds `The` in the list, adds `receive` and `Tomorrow`,\nsuggests for `recieve` (now `receive` first), checks a line that is all known\nwords, and lists the added words; `sessions/bad-input.in` types menu choices\nthat are not 1 to 5, an empty line, 201 characters, a line with no words,\ntwo words, `x1` and `a-b` as one word, `it's`, a word with nothing near,\n`the` to add again, `'quote'` and 21 letters, adds `Don't`, and checks a line\nwith nothing near and a line with apostrophes.\n\nBuilt on the verified corpus case `edit-distance` (the Levenshtein distance\nas a bottom-up table).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P028 spelling suggester: check a line of text against a word list, suggest\n# the closest words by edit distance, and add new words for this session.\nimport words\nimport spelling\n\n200 => longest_line\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 listed(items):\n    \"\" => out\n    0 => i\n    while i < len(items):\n        if i > 0 and i == len(items) - 1:\n            out + \" and \" => out\n        elif i > 0:\n            out + \", \" => out\n        out + items[i] => out\n        i + 1 => i\n    return out\n\ndef plural(n, word):\n    if n == 1:\n        return \"1 \" + word\n    return str(n) + \" \" + word + \"s\"\n\ndef one_word(s):\n    # The word in lower case if s is one word of letters (an apostrophe inside\n    # is fine), at most 20 characters; \"\" otherwise.\n    trim(s) => s\n    if s == \"\" or len(s) > 20:\n        return \"\"\n    spelling.words_of(s) => ws\n    if len(ws) != 1 or len(ws[0]) != len(s):\n        return \"\"\n    return ws[0]\n\ndef ask_word():\n    # A word, or \"\" when the answer is empty, which cancels.\n    while True:\n        input(\"word> \") => answer\n        if trim(answer) == \"\":\n            return \"\"\n        one_word(answer) => w\n        if w != \"\":\n            return w\n        \"Type one word of letters (an apostrophe inside is fine), at most 20.\" ^0\n\ndef check(text, known, vocabulary):\n    spelling.words_of(text) => ws\n    if len(ws) == 0:\n        \"No words in that line.\" ^0\n        return\n    [] => unknown\n    {} => times\n    for w in ws:\n        if not (w in known):\n            if w in times:\n                times[w] + 1 => times[w]\n            else:\n                1 => times[w]\n                unknown + [w] => unknown\n    if len(unknown) == 0:\n        (\"All \" + plural(len(ws), \"word\") + \" are in the word list.\") ^0\n        return\n    (plural(len(ws), \"word\") + \", \" + str(len(unknown)) + \" not in the word list:\") ^0\n    for w in unknown:\n        \"  \" + w => line\n        if times[w] > 1:\n            line + \" (\" + str(times[w]) + \" times)\" => line\n        spelling.suggestions(w, vocabulary, 2, 3) => found\n        if len(found) == 0:\n            (line + \" -> nothing within 2 edits\") ^0\n        else:\n            \"\" => names\n            for f in found:\n                if names != \"\":\n                    names + \", \" => names\n                names + f[0] => names\n            (line + \" -> \" + names) ^0\n\ndef suggest(w, known, vocabulary):\n    if w in known:\n        (\"'\" + w + \"' is in the word list.\") ^0\n        return\n    spelling.suggestions(w, vocabulary, 3, 5) => found\n    if len(found) == 0:\n        (\"Nothing in the word list is within 3 edits of '\" + w + \"'.\") ^0\n        return\n    (\"Closest to '\" + w + \"':\") ^0\n    for f in found:\n        (\"  \" + f[0] + \" - \" + plural(f[1], \"edit\")) ^0\n\n{} => known\n[] => vocabulary\nfor w in words.common:\n    True => known[w]\n    vocabulary + [w] => vocabulary\n[] => added\n\nTrue => running\nwhile running:\n    \"\" ^0\n    \"== Spelling suggester ==\" ^0\n    \"1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        \"\" => text\n        True => asking\n        while asking:\n            input(\"text> \") => text\n            if len(text) > longest_line:\n                (\"That line has \" + str(len(text)) + \" characters; at most \" + str(longest_line) + \". Type it again.\") ^0\n            else:\n                False => asking\n        if trim(text) == \"\":\n            \"Cancelled.\" ^0\n        else:\n            check(text, known, vocabulary)\n    elif choice == \"2\":\n        ask_word() => w\n        if w == \"\":\n            \"Cancelled.\" ^0\n        else:\n            suggest(w, known, vocabulary)\n    elif choice == \"3\":\n        ask_word() => w\n        if w == \"\":\n            \"Cancelled.\" ^0\n        elif w in known:\n            (\"'\" + w + \"' is already in the word list.\") ^0\n        else:\n            True => known[w]\n            vocabulary + [w] => vocabulary\n            added + [w] => added\n            (\"Added '\" + w + \"'; the word list now has \" + str(len(vocabulary)) + \" words.\") ^0\n    elif choice == \"4\":\n        if len(added) == 0:\n            \"No words added yet.\" ^0\n        else:\n            (\"Added this session: \" + listed(added) + \".\") ^0\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import words\nimport spelling\nlongest_line = 200\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 listed(items):\n    out = \"\"\n    i = 0\n    while i < len(items):\n        if i > 0 and i == len(items) - 1:\n            out = out + \" and \"\n        elif i > 0:\n            out = out + \", \"\n        out = out + items[i]\n        i = i + 1\n    return out\n\ndef plural(n, word):\n    if n == 1:\n        return \"1 \" + word\n    return str(n) + \" \" + word + \"s\"\n\ndef one_word(s):\n    s = trim(s)\n    if s == \"\" or len(s) > 20:\n        return \"\"\n    ws = spelling.words_of(s)\n    if len(ws) != 1 or len(ws[0]) != len(s):\n        return \"\"\n    return ws[0]\n\ndef ask_word():\n    while True:\n        answer = input(\"word> \")\n        if trim(answer) == \"\":\n            return \"\"\n        w = one_word(answer)\n        if w != \"\":\n            return w\n        print(\"Type one word of letters (an apostrophe inside is fine), at most 20.\")\n\ndef check(text, known, vocabulary):\n    ws = spelling.words_of(text)\n    if len(ws) == 0:\n        print(\"No words in that line.\")\n        return\n    unknown = []\n    times = {}\n    for w in ws:\n        if not w in known:\n            if w in times:\n                times[w] = times[w] + 1\n            else:\n                times[w] = 1\n                unknown = unknown + [w]\n    if len(unknown) == 0:\n        print(\"All \" + plural(len(ws), \"word\") + \" are in the word list.\")\n        return\n    print(plural(len(ws), \"word\") + \", \" + str(len(unknown)) + \" not in the word list:\")\n    for w in unknown:\n        line = \"  \" + w\n        if times[w] > 1:\n            line = line + \" (\" + str(times[w]) + \" times)\"\n        found = spelling.suggestions(w, vocabulary, 2, 3)\n        if len(found) == 0:\n            print(line + \" -> nothing within 2 edits\")\n        else:\n            names = \"\"\n            for f in found:\n                if names != \"\":\n                    names = names + \", \"\n                names = names + f[0]\n            print(line + \" -> \" + names)\n\ndef suggest(w, known, vocabulary):\n    if w in known:\n        print(\"'\" + w + \"' is in the word list.\")\n        return\n    found = spelling.suggestions(w, vocabulary, 3, 5)\n    if len(found) == 0:\n        print(\"Nothing in the word list is within 3 edits of '\" + w + \"'.\")\n        return\n    print(\"Closest to '\" + w + \"':\")\n    for f in found:\n        print(\"  \" + f[0] + \" - \" + plural(f[1], \"edit\"))\n\nknown = {}\nvocabulary = []\nfor w in words.common:\n    known[w] = True\n    vocabulary = vocabulary + [w]\nadded = []\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"== Spelling suggester ==\")\n    print(\"1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        text = \"\"\n        asking = True\n        while asking:\n            text = input(\"text> \")\n            if len(text) > longest_line:\n                print(\"That line has \" + str(len(text)) + \" characters; at most \" + str(longest_line) + \". Type it again.\")\n            else:\n                asking = False\n        if trim(text) == \"\":\n            print(\"Cancelled.\")\n        else:\n            check(text, known, vocabulary)\n    elif choice == \"2\":\n        w = ask_word()\n        if w == \"\":\n            print(\"Cancelled.\")\n        else:\n            suggest(w, known, vocabulary)\n    elif choice == \"3\":\n        w = ask_word()\n        if w == \"\":\n            print(\"Cancelled.\")\n        elif w in known:\n            print(\"'\" + w + \"' is already in the word list.\")\n        else:\n            known[w] = True\n            vocabulary = vocabulary + [w]\n            added = added + [w]\n            print(\"Added '\" + w + \"'; the word list now has \" + str(len(vocabulary)) + \" words.\")\n    elif choice == \"4\":\n        if len(added) == 0:\n            print(\"No words added yet.\")\n        else:\n            print(\"Added this session: \" + listed(added) + \".\")\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "spelling.eml",
      "eml": "# P028 spelling suggester - edit distance, the words of a text, and\n# suggestions.\n\n\"abcdefghijklmnopqrstuvwxyz\" => small\n\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" => capital\n\ndef distance(a, b, limit):\n    # The fewest one-letter insertions, deletions and substitutions, and swaps\n    # of two neighbouring letters, that turn a into b - the Levenshtein table\n    # of the corpus case edit-distance with one more way into a cell (the\n    # \"optimal string alignment\" distance). Anything over limit comes back as\n    # limit + 1, at once when the lengths alone differ by more.\n    len(a) => n\n    len(b) => m\n    if n - m > limit or m - n > limit:\n        return limit + 1\n    [] => t\n    for i in [0:n]:\n        [0] * (m + 1) => row\n        i => row[0]\n        t + [row] => t\n    for j in [0:m]:\n        j => t[0][j]\n    for i in [1:n]:\n        for j in [1:m]:\n            1 => cost\n            if a[i - 1] == b[j - 1]:\n                0 => cost\n            t[i - 1][j] + 1 => best\n            if t[i][j - 1] + 1 < best:\n                t[i][j - 1] + 1 => best\n            if t[i - 1][j - 1] + cost < best:\n                t[i - 1][j - 1] + cost => best\n            if i > 1 and j > 1 and a[i - 1] == b[j - 2] and a[i - 2] == b[j - 1]:\n                if t[i - 2][j - 2] + 1 < best:\n                    t[i - 2][j - 2] + 1 => best\n            best => t[i][j]\n    if t[n][m] > limit:\n        return limit + 1\n    return t[n][m]\n\ndef lower_letter(c):\n    # c in lower case if it is a letter, \"'\" for an apostrophe, else \"\".\n    if c == \"'\":\n        return c\n    0 => k\n    while k < 26:\n        if small[k] == c or capital[k] == c:\n            return small[k]\n        k + 1 => k\n    return \"\"\n\ndef without_apostrophes_around(w):\n    0 => i\n    len(w) => j\n    while i < j and w[i] == \"'\":\n        i + 1 => i\n    while j > i and w[j - 1] == \"'\":\n        j - 1 => j\n    return w[i:j]\n\ndef words_of(text):\n    # The words of a text in lower case: runs of letters and apostrophes, with\n    # apostrophes at either end dropped, so \"don't\" stays one word.\n    [] => out\n    \"\" => w\n    for c in text + \" \":\n        lower_letter(c) => l\n        if l != \"\":\n            w + l => w\n        else:\n            without_apostrophes_around(w) => s\n            if s != \"\":\n                out + [s] => out\n            \"\" => w\n    return out\n\ndef suggestions(word, vocabulary, limit, most):\n    # Up to `most` words of the vocabulary within `limit` edits of word, as\n    # [word, edits] pairs, nearest first; between equally near words the one\n    # earlier in the vocabulary (the more common one) comes first.\n    [] => found\n    for w in vocabulary:\n        distance(word, w, limit) => d\n        if d <= limit:\n            len(found) => j\n            while j > 0 and found[j - 1][1] > d:\n                j - 1 => j\n            found[0:j] + [[w, d]] + found[j:len(found)] => found\n            if len(found) > most:\n                found[0:most] => found\n    return found\n",
      "python": "small = \"abcdefghijklmnopqrstuvwxyz\"\ncapital = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\ndef distance(a, b, limit):\n    n = len(a)\n    m = len(b)\n    if n - m > limit or m - n > limit:\n        return limit + 1\n    t = []\n    for i in range(0, n+1):\n        row = [0] * (m + 1)\n        row[0] = i\n        t = t + [row]\n    for j in range(0, m+1):\n        t[0][j] = j\n    for i in range(1, n+1):\n        for j in range(1, m+1):\n            cost = 1\n            if a[i - 1] == b[j - 1]:\n                cost = 0\n            best = t[i - 1][j] + 1\n            if t[i][j - 1] + 1 < best:\n                best = t[i][j - 1] + 1\n            if t[i - 1][j - 1] + cost < best:\n                best = t[i - 1][j - 1] + cost\n            if i > 1 and j > 1 and a[i - 1] == b[j - 2] and a[i - 2] == b[j - 1]:\n                if t[i - 2][j - 2] + 1 < best:\n                    best = t[i - 2][j - 2] + 1\n            t[i][j] = best\n    if t[n][m] > limit:\n        return limit + 1\n    return t[n][m]\n\ndef lower_letter(c):\n    if c == \"'\":\n        return c\n    k = 0\n    while k < 26:\n        if small[k] == c or capital[k] == c:\n            return small[k]\n        k = k + 1\n    return \"\"\n\ndef without_apostrophes_around(w):\n    i = 0\n    j = len(w)\n    while i < j and w[i] == \"'\":\n        i = i + 1\n    while j > i and w[j - 1] == \"'\":\n        j = j - 1\n    return w[i:j]\n\ndef words_of(text):\n    out = []\n    w = \"\"\n    for c in text + \" \":\n        l = lower_letter(c)\n        if l != \"\":\n            w = w + l\n        else:\n            s = without_apostrophes_around(w)\n            if s != \"\":\n                out = out + [s]\n            w = \"\"\n    return out\n\ndef suggestions(word, vocabulary, limit, most):\n    found = []\n    for w in vocabulary:\n        d = distance(word, w, limit)\n        if d <= limit:\n            j = len(found)\n            while j > 0 and found[j - 1][1] > d:\n                j = j - 1\n            found = found[0:j] + [[w, d]] + found[j:len(found)]\n            if len(found) > most:\n                found = found[0:most]\n    return found\n"
    },
    {
      "name": "words.eml",
      "eml": "# P028 spelling suggester - the built-in word list: 352 common English words,\n# most common first. When two suggestions are the same number of edits away,\n# the one that comes first here comes first.\n\n[\n    \"the\", \"be\", \"to\", \"of\", \"and\", \"a\", \"in\", \"that\", \"have\", \"i\", \"it\",\n    \"for\", \"not\", \"on\", \"with\", \"he\", \"as\", \"you\", \"do\", \"at\", \"this\", \"but\",\n    \"his\", \"by\", \"from\", \"they\", \"we\", \"say\", \"her\", \"she\", \"or\", \"an\",\n    \"will\", \"my\", \"one\", \"all\", \"would\", \"there\", \"their\", \"what\", \"so\",\n    \"up\", \"out\", \"if\", \"about\", \"who\", \"get\", \"which\", \"go\", \"me\", \"when\",\n    \"make\", \"can\", \"like\", \"time\", \"no\", \"just\", \"him\", \"know\", \"take\",\n    \"people\", \"into\", \"year\", \"your\", \"good\", \"some\", \"could\", \"them\", \"see\",\n    \"other\", \"than\", \"then\", \"now\", \"look\", \"only\", \"come\", \"its\", \"over\",\n    \"think\", \"also\", \"back\", \"after\", \"use\", \"two\", \"how\", \"our\", \"work\",\n    \"first\", \"well\", \"way\", \"even\", \"new\", \"want\", \"because\", \"any\", \"these\",\n    \"give\", \"day\", \"most\", \"us\", \"is\", \"was\", \"are\", \"been\", \"has\", \"had\",\n    \"were\", \"said\", \"did\", \"made\", \"went\", \"came\", \"saw\", \"got\", \"gave\",\n    \"took\", \"found\", \"told\", \"thought\", \"called\", \"asked\", \"going\", \"being\",\n    \"does\", \"done\", \"man\", \"men\", \"woman\", \"child\", \"children\", \"world\",\n    \"life\", \"hand\", \"part\", \"place\", \"case\", \"week\", \"point\", \"number\",\n    \"home\", \"water\", \"room\", \"mother\", \"father\", \"night\", \"thing\", \"things\",\n    \"name\", \"house\", \"book\", \"word\", \"words\", \"story\", \"school\", \"family\",\n    \"friend\", \"city\", \"country\", \"money\", \"eye\", \"eyes\", \"head\", \"face\",\n    \"door\", \"line\", \"end\", \"side\", \"kind\", \"group\", \"problem\", \"question\",\n    \"idea\", \"fact\", \"light\", \"song\", \"game\", \"paper\", \"picture\", \"letter\",\n    \"language\", \"sentence\", \"spelling\", \"great\", \"little\", \"old\", \"big\",\n    \"high\", \"different\", \"small\", \"large\", \"next\", \"early\", \"young\",\n    \"important\", \"few\", \"public\", \"bad\", \"same\", \"able\", \"last\", \"long\",\n    \"best\", \"better\", \"right\", \"left\", \"full\", \"fast\", \"quick\", \"quickly\",\n    \"brown\", \"lazy\", \"black\", \"white\", \"red\", \"green\", \"blue\", \"cold\", \"hot\",\n    \"hard\", \"easy\", \"real\", \"sure\", \"free\", \"find\", \"tell\", \"ask\", \"seem\",\n    \"feel\", \"try\", \"leave\", \"call\", \"keep\", \"let\", \"begin\", \"help\", \"talk\",\n    \"turn\", \"start\", \"show\", \"hear\", \"play\", \"run\", \"move\", \"live\",\n    \"believe\", \"hold\", \"bring\", \"happen\", \"write\", \"sit\", \"stand\", \"lose\",\n    \"pay\", \"meet\", \"include\", \"continue\", \"set\", \"learn\", \"change\", \"lead\",\n    \"understand\", \"watch\", \"follow\", \"stop\", \"create\", \"speak\", \"read\",\n    \"spend\", \"grow\", \"open\", \"walk\", \"win\", \"offer\", \"remember\", \"love\",\n    \"consider\", \"appear\", \"buy\", \"wait\", \"serve\", \"die\", \"send\", \"expect\",\n    \"build\", \"stay\", \"fall\", \"cut\", \"reach\", \"kill\", \"remain\", \"jump\",\n    \"jumped\", \"spell\", \"draw\", \"carry\", \"eat\", \"here\", \"where\", \"why\",\n    \"each\", \"between\", \"under\", \"never\", \"again\", \"always\", \"often\", \"still\",\n    \"once\", \"soon\", \"together\", \"without\", \"until\", \"while\", \"through\",\n    \"around\", \"below\", \"above\", \"against\", \"away\", \"down\", \"off\", \"too\",\n    \"very\", \"really\", \"almost\", \"enough\", \"every\", \"something\", \"sometimes\",\n    \"another\", \"such\", \"both\", \"own\", \"later\", \"dog\", \"fox\", \"cat\", \"bird\",\n    \"tree\", \"river\", \"sea\", \"earth\", \"fire\", \"food\", \"car\", \"street\",\n    \"state\", \"mile\", \"animal\", \"plant\", \"page\", \"list\"\n] => common\n",
      "python": "common = [\"the\", \"be\", \"to\", \"of\", \"and\", \"a\", \"in\", \"that\", \"have\", \"i\", \"it\", \"for\", \"not\", \"on\", \"with\", \"he\", \"as\", \"you\", \"do\", \"at\", \"this\", \"but\", \"his\", \"by\", \"from\", \"they\", \"we\", \"say\", \"her\", \"she\", \"or\", \"an\", \"will\", \"my\", \"one\", \"all\", \"would\", \"there\", \"their\", \"what\", \"so\", \"up\", \"out\", \"if\", \"about\", \"who\", \"get\", \"which\", \"go\", \"me\", \"when\", \"make\", \"can\", \"like\", \"time\", \"no\", \"just\", \"him\", \"know\", \"take\", \"people\", \"into\", \"year\", \"your\", \"good\", \"some\", \"could\", \"them\", \"see\", \"other\", \"than\", \"then\", \"now\", \"look\", \"only\", \"come\", \"its\", \"over\", \"think\", \"also\", \"back\", \"after\", \"use\", \"two\", \"how\", \"our\", \"work\", \"first\", \"well\", \"way\", \"even\", \"new\", \"want\", \"because\", \"any\", \"these\", \"give\", \"day\", \"most\", \"us\", \"is\", \"was\", \"are\", \"been\", \"has\", \"had\", \"were\", \"said\", \"did\", \"made\", \"went\", \"came\", \"saw\", \"got\", \"gave\", \"took\", \"found\", \"told\", \"thought\", \"called\", \"asked\", \"going\", \"being\", \"does\", \"done\", \"man\", \"men\", \"woman\", \"child\", \"children\", \"world\", \"life\", \"hand\", \"part\", \"place\", \"case\", \"week\", \"point\", \"number\", \"home\", \"water\", \"room\", \"mother\", \"father\", \"night\", \"thing\", \"things\", \"name\", \"house\", \"book\", \"word\", \"words\", \"story\", \"school\", \"family\", \"friend\", \"city\", \"country\", \"money\", \"eye\", \"eyes\", \"head\", \"face\", \"door\", \"line\", \"end\", \"side\", \"kind\", \"group\", \"problem\", \"question\", \"idea\", \"fact\", \"light\", \"song\", \"game\", \"paper\", \"picture\", \"letter\", \"language\", \"sentence\", \"spelling\", \"great\", \"little\", \"old\", \"big\", \"high\", \"different\", \"small\", \"large\", \"next\", \"early\", \"young\", \"important\", \"few\", \"public\", \"bad\", \"same\", \"able\", \"last\", \"long\", \"best\", \"better\", \"right\", \"left\", \"full\", \"fast\", \"quick\", \"quickly\", \"brown\", \"lazy\", \"black\", \"white\", \"red\", \"green\", \"blue\", \"cold\", \"hot\", \"hard\", \"easy\", \"real\", \"sure\", \"free\", \"find\", \"tell\", \"ask\", \"seem\", \"feel\", \"try\", \"leave\", \"call\", \"keep\", \"let\", \"begin\", \"help\", \"talk\", \"turn\", \"start\", \"show\", \"hear\", \"play\", \"run\", \"move\", \"live\", \"believe\", \"hold\", \"bring\", \"happen\", \"write\", \"sit\", \"stand\", \"lose\", \"pay\", \"meet\", \"include\", \"continue\", \"set\", \"learn\", \"change\", \"lead\", \"understand\", \"watch\", \"follow\", \"stop\", \"create\", \"speak\", \"read\", \"spend\", \"grow\", \"open\", \"walk\", \"win\", \"offer\", \"remember\", \"love\", \"consider\", \"appear\", \"buy\", \"wait\", \"serve\", \"die\", \"send\", \"expect\", \"build\", \"stay\", \"fall\", \"cut\", \"reach\", \"kill\", \"remain\", \"jump\", \"jumped\", \"spell\", \"draw\", \"carry\", \"eat\", \"here\", \"where\", \"why\", \"each\", \"between\", \"under\", \"never\", \"again\", \"always\", \"often\", \"still\", \"once\", \"soon\", \"together\", \"without\", \"until\", \"while\", \"through\", \"around\", \"below\", \"above\", \"against\", \"away\", \"down\", \"off\", \"too\", \"very\", \"really\", \"almost\", \"enough\", \"every\", \"something\", \"sometimes\", \"another\", \"such\", \"both\", \"own\", \"later\", \"dog\", \"fox\", \"cat\", \"bird\", \"tree\", \"river\", \"sea\", \"earth\", \"fire\", \"food\", \"car\", \"street\", \"state\", \"mile\", \"animal\", \"plant\", \"page\", \"list\"]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nabc\n6\n1\n\n1\nword word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word x\n123 + 456 = 579 !!!\n2\ntwo words\nx1\na-b\n\n2\nit's\n2\nxyzzyq\n4\n3\nthe\n3\n'quote'\nabcdefghijklmnopqrstu\nDon't\n3\n\n1\nzzzz qqq\n1\n'Don't' panic, it's fine.\n4\n5\n",
      "screen": "\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> abc\nPick a number from 1 to 5.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 6\nPick a number from 1 to 5.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 1\ntext> \nCancelled.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 1\ntext> word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word x\nThat line has 201 characters; at most 200. Type it again.\ntext> 123 + 456 = 579 !!!\nNo words in that line.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 2\nword> two words\nType one word of letters (an apostrophe inside is fine), at most 20.\nword> x1\nType one word of letters (an apostrophe inside is fine), at most 20.\nword> a-b\nType one word of letters (an apostrophe inside is fine), at most 20.\nword> \nCancelled.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 2\nword> it's\nClosest to 'it's':\n  its - 1 edit\n  it - 2 edits\n  is - 2 edits\n  the - 3 edits\n  to - 3 edits\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 2\nword> xyzzyq\nNothing in the word list is within 3 edits of 'xyzzyq'.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 4\nNo words added yet.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 3\nword> the\n'the' is already in the word list.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 3\nword> 'quote'\nType one word of letters (an apostrophe inside is fine), at most 20.\nword> abcdefghijklmnopqrstu\nType one word of letters (an apostrophe inside is fine), at most 20.\nword> Don't\nAdded 'don't'; the word list now has 353 words.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 3\nword> \nCancelled.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 1\ntext> zzzz qqq\n2 words, 2 not in the word list:\n  zzzz -> nothing within 2 edits\n  qqq -> nothing within 2 edits\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 1\ntext> 'Don't' panic, it's fine.\n4 words, 3 not in the word list:\n  panic -> nothing within 2 edits\n  it's -> its, it, is\n  fine -> line, find, fire\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 4\nAdded this session: don't.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\nTeh quikc brown fox jumpd over teh lazy dogg.\n1\nMy freind found the langauge hard becuase of spelling.\n2\nwrld\n2\nThe\n3\nreceive\n3\nTomorrow\n2\nrecieve\n1\nI will receive the book tomorrow.\n4\n5\n",
      "screen": "\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 1\ntext> Teh quikc brown fox jumpd over teh lazy dogg.\n9 words, 4 not in the word list:\n  teh (2 times) -> the, be, to\n  quikc -> quick\n  jumpd -> jump, jumped\n  dogg -> dog, do, does\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 1\ntext> My freind found the langauge hard becuase of spelling.\n9 words, 3 not in the word list:\n  freind -> friend, find\n  langauge -> language\n  becuase -> because\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 2\nword> wrld\nClosest to 'wrld':\n  world - 1 edit\n  will - 2 edits\n  would - 2 edits\n  well - 2 edits\n  told - 2 edits\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 2\nword> The\n'the' is in the word list.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 3\nword> receive\nAdded 'receive'; the word list now has 353 words.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 3\nword> Tomorrow\nAdded 'tomorrow'; the word list now has 354 words.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 2\nword> recieve\nClosest to 'recieve':\n  receive - 1 edit\n  believe - 2 edits\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 1\ntext> I will receive the book tomorrow.\nAll 6 words are in the word list.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 4\nAdded this session: receive and tomorrow.\n\n== Spelling suggester ==\n1) check a line  2) suggest for a word  3) add a word  4) added words  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "edit-distance",
      "caseId": "083-edit-distance",
      "title": "Edit distance (Levenshtein)"
    }
  ],
  "updated": "2026-10-06"
}
