{
  "id": "P091",
  "slug": "huffman-coding",
  "key": "P091-huffman-coding",
  "title": "Huffman coding",
  "summary": "A text's character counts, the code tree built with a binary heap by always joining the two lightest pieces, the codes it gives and how the tree was built, the text encoded and decoded back, and the bits saved against fixed-width codes.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P091 - Huffman coding\n\nShort codes for common characters. The program counts a text's\ncharacters, builds the code tree, and shows the codes and how the tree was\nbuilt. It encodes the text and decodes it back, and compares the bits with\nfixed-width codes. It starts with \"this is an example of a huffman tree\",\nwhich takes 135 bits.\n\n- `main.eml` - the menu, the code table, the building steps, and encoding\n  and decoding\n- `huffman.eml` - character counts, the binary heap, building the tree,\n  the codes, encoding and decoding\n\nHow each part works:\n\n- Counts, as in the corpus case `char-frequency-table`: each character\n  once, with how many times it occurs.\n- The tree: every character starts as a piece weighing its count. The two\n  lightest pieces are joined into one, again and again, until one piece -\n  the whole tree - is left. The pieces wait in a binary heap, as in the\n  corpus case `binary-heap`, so the lightest is always on top. On a tie\n  the older piece goes first, so the same text always gives the same tree.\n- Codes: 0 for each step to the left, 1 to the right, from the root down.\n  Frequent characters end up near the root, with short codes. No code is\n  the start of another, so a string of bits splits into characters only\n  one way. A text of a single repeated character gets the code 0.\n- Huffman's code is the shortest of all codes that write each character as\n  its own run of bits: 135 bits here, against 144 for a 4-bit fixed code\n  and 288 for 8 bits each.\n\nWhat is checked: menu choices 1 to 6; a text of 2 to 200 characters; bits\nof 0 and 1 only, ending at the end of a code. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the codes and the building steps for the starting text, and its 135\n  bits decoded back;\n- \"0110\" decoded as m and \"10010100001\" as \"the\";\n- \"mississippi\": 21 bits, against 22 at 2 bits each.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- the bits \"0102\" and \"1\" (it stops inside a code), then a cancel;\n- the text \"a\", too short; then \"aaaa\", whose one character gets the code\n  0, so \"001\" cannot be decoded and \"000\" is \"aaa\".\n\nBuilt on the verified corpus cases `char-frequency-table` (counting each\ncharacter) and `binary-heap` (the lightest piece always on top).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P091 Huffman coding: a text's character counts and their codes, how the\n# tree was built, the text encoded and decoded back, and how many bits it\n# saves against a fixed-width code.\nimport huffman\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 lpad(s, n):\n    while len(s) < n:\n        \" \" + s => s\n    return s\n\ndef pad(s, n):\n    while len(s) < n:\n        s + \" \" => s\n    return s\n\ndef shown(c):\n    if c == \" \":\n        return \"space\"\n    return \"'\" + c + \"'\"\n\ndef label(tree, node):\n    if tree[2][node] == -1:\n        return shown(tree[1][node]) + \" (\" + str(tree[0][node]) + \")\"\n    return \"[\" + str(tree[0][node]) + \"]\"\n\ndef ratio(a, b, places):\n    # a / b to `places` decimals, cut off.\n    0 => w\n    a => r\n    while r >= b:\n        r - b => r\n        w + 1 => w\n    str(w) + \".\" => s\n    for i in [1:places]:\n        r * 10 => r\n        0 => d\n        while r >= b:\n            r - b => r\n            d + 1 => d\n        s + str(d) => s\n    return s\n\ndef analyse(text):\n    huffman.counts(text) => cn\n    huffman.build(cn[0], cn[1]) => tree\n    huffman.codes(tree) => table\n    return [cn, tree, table]\n\ndef show_codes(text, a):\n    a[0] => cn\n    a[2] => table\n    \"  char    count  code\" ^0\n    0 => total\n    for i in [0:len(cn[0]) - 1]:\n        cn[0][i] => c\n        (\"  \" + pad(shown(c), 7) + lpad(str(cn[1][i]), 5) + \"  \" + table[c]) ^0\n        total + cn[1][i] * len(table[c]) => total\n    1 => width\n    2 => cap\n    while cap < len(cn[0]):\n        cap * 2 => cap\n        width + 1 => width\n    (str(len(text)) + \" characters, \" + str(len(cn[0])) + \" different: Huffman needs \" + str(total) + \" bits, \" + ratio(total, len(text), 3) + \" a character.\") ^0\n    \"bits\" => unit\n    if width == 1:\n        \"bit\" => unit\n    (\"A fixed code of \" + str(width) + \" \" + unit + \" each needs \" + str(width * len(text)) + \"; eight bits each, \" + str(8 * len(text)) + \".\") ^0\n    \"No other code that writes each character as its own run of bits needs fewer: Huffman's is the shortest.\" ^0\n\ndef steps(a):\n    a[1] => tree\n    for st in tree[5]:\n        (\"  join \" + label(tree, st[0]) + \" and \" + label(tree, st[1]) + \" into [\" + str(tree[0][st[2]]) + \"]\") ^0\n    \"Each time, the two lightest pieces are joined (on a tie, the older first); the last piece is the whole tree.\" ^0\n\ndef round_trip(text, a):\n    huffman.encode(text, a[2]) => bits\n    if len(bits) <= 96:\n        (\"bits: \" + bits) ^0\n    else:\n        (\"bits: \" + bits[0:96] + \"... (\" + str(len(bits)) + \" in all)\") ^0\n    huffman.decode(bits, a[1]) => back\n    if back[0] == text:\n        (\"decoded: \" + back[0]) ^0\n        \"The same text: no code is the start of another, so the bits split only one way.\" ^0\n\ndef read_bits(a):\n    plain(input(\"bits to decode (0 and 1)> \")) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return\n    huffman.decode(s, a[1]) => back\n    if back[1] == \"\":\n        (\"decoded: \" + back[0]) ^0\n    else:\n        (\"Cannot decode: \" + back[1] + \".\") ^0\n\ndef new_text(text):\n    input(\"text (2 to 200 characters)> \") => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return text\n    if len(s) < 2 or len(s) > 200:\n        \"A text is 2 to 200 characters.\" ^0\n        return text\n    (\"A text of \" + str(len(s)) + \" characters.\") ^0\n    return s\n\n\"this is an example of a huffman tree\" => text\nanalyse(text) => a\n\n\"== Huffman coding ==\" ^0\n\"Short codes for common characters: the tree, the codes, and the bits saved.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show_codes(text, a)\n    elif choice == \"2\":\n        steps(a)\n    elif choice == \"3\":\n        round_trip(text, a)\n    elif choice == \"4\":\n        read_bits(a)\n    elif choice == \"5\":\n        new_text(text) => text\n        analyse(text) => a\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import huffman\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 lpad(s, n):\n    while len(s) < n:\n        s = \" \" + s\n    return s\n\ndef pad(s, n):\n    while len(s) < n:\n        s = s + \" \"\n    return s\n\ndef shown(c):\n    if c == \" \":\n        return \"space\"\n    return \"'\" + c + \"'\"\n\ndef label(tree, node):\n    if tree[2][node] == -1:\n        return shown(tree[1][node]) + \" (\" + str(tree[0][node]) + \")\"\n    return \"[\" + str(tree[0][node]) + \"]\"\n\ndef ratio(a, b, places):\n    w = 0\n    r = a\n    while r >= b:\n        r = r - b\n        w = w + 1\n    s = str(w) + \".\"\n    for i in range(1, places+1):\n        r = r * 10\n        d = 0\n        while r >= b:\n            r = r - b\n            d = d + 1\n        s = s + str(d)\n    return s\n\ndef analyse(text):\n    cn = huffman.counts(text)\n    tree = huffman.build(cn[0], cn[1])\n    table = huffman.codes(tree)\n    return [cn, tree, table]\n\ndef show_codes(text, a):\n    cn = a[0]\n    table = a[2]\n    print(\"  char    count  code\")\n    total = 0\n    for i in range(0, len(cn[0])):\n        c = cn[0][i]\n        print(\"  \" + pad(shown(c), 7) + lpad(str(cn[1][i]), 5) + \"  \" + table[c])\n        total = total + cn[1][i] * len(table[c])\n    width = 1\n    cap = 2\n    while cap < len(cn[0]):\n        cap = cap * 2\n        width = width + 1\n    print(str(len(text)) + \" characters, \" + str(len(cn[0])) + \" different: Huffman needs \" + str(total) + \" bits, \" + ratio(total, len(text), 3) + \" a character.\")\n    unit = \"bits\"\n    if width == 1:\n        unit = \"bit\"\n    print(\"A fixed code of \" + str(width) + \" \" + unit + \" each needs \" + str(width * len(text)) + \"; eight bits each, \" + str(8 * len(text)) + \".\")\n    print(\"No other code that writes each character as its own run of bits needs fewer: Huffman's is the shortest.\")\n\ndef steps(a):\n    tree = a[1]\n    for st in tree[5]:\n        print(\"  join \" + label(tree, st[0]) + \" and \" + label(tree, st[1]) + \" into [\" + str(tree[0][st[2]]) + \"]\")\n    print(\"Each time, the two lightest pieces are joined (on a tie, the older first); the last piece is the whole tree.\")\n\ndef round_trip(text, a):\n    bits = huffman.encode(text, a[2])\n    if len(bits) <= 96:\n        print(\"bits: \" + bits)\n    else:\n        print(\"bits: \" + bits[0:96] + \"... (\" + str(len(bits)) + \" in all)\")\n    back = huffman.decode(bits, a[1])\n    if back[0] == text:\n        print(\"decoded: \" + back[0])\n        print(\"The same text: no code is the start of another, so the bits split only one way.\")\n\ndef read_bits(a):\n    s = plain(input(\"bits to decode (0 and 1)> \"))\n    if s == \"\":\n        print(\"Cancelled.\")\n        return\n    back = huffman.decode(s, a[1])\n    if back[1] == \"\":\n        print(\"decoded: \" + back[0])\n    else:\n        print(\"Cannot decode: \" + back[1] + \".\")\n\ndef new_text(text):\n    s = input(\"text (2 to 200 characters)> \")\n    if s == \"\":\n        print(\"Cancelled.\")\n        return text\n    if len(s) < 2 or len(s) > 200:\n        print(\"A text is 2 to 200 characters.\")\n        return text\n    print(\"A text of \" + str(len(s)) + \" characters.\")\n    return s\n\ntext = \"this is an example of a huffman tree\"\na = analyse(text)\nprint(\"== Huffman coding ==\")\nprint(\"Short codes for common characters: the tree, the codes, and the bits saved.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show_codes(text, a)\n    elif choice == \"2\":\n        steps(a)\n    elif choice == \"3\":\n        round_trip(text, a)\n    elif choice == \"4\":\n        read_bits(a)\n    elif choice == \"5\":\n        text = new_text(text)\n        a = analyse(text)\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "huffman.eml",
      "eml": "# P091 Huffman coding - a text's character counts, the code tree built by\n# always joining the two lightest pieces, the codes it gives, and the text\n# encoded and decoded. The tree lives in lists, one entry per node: its\n# weight, its character (\"\" for a joint), and its two children (-1 for a\n# leaf). Ties go to the older node, so the same text always gives the same\n# tree.\n\ndef counts(text):\n    # The corpus case char-frequency-table: [the characters, sorted],\n    # [their counts].\n    {} => seen\n    [] => chars\n    for c in text:\n        if c in seen:\n            seen[c] + 1 => seen[c]\n        else:\n            1 => seen[c]\n            chars + [c] => chars\n    # sort the characters, so the tree does not depend on the text's order\n    for i in [1:len(chars) - 1]:\n        chars[i] => x\n        i - 1 => j\n        while j >= 0 and chars[j] > x:\n            chars[j] => chars[j + 1]\n            j - 1 => j\n        x => chars[j + 1]\n    [0] * len(chars) => ns\n    for i in [0:len(chars) - 1]:\n        seen[chars[i]] => ns[i]\n    return [chars, ns]\n\ndef lighter(w, a, b):\n    # Node a comes before node b: smaller weight, or the same weight and\n    # made earlier.\n    return w[a] < w[b] or (w[a] == w[b] and a < b)\n\ndef push(heap, w, x):\n    # The corpus case binary-heap: add at the end, then swap upwards while\n    # the parent is heavier.\n    heap + [x] => heap\n    len(heap) - 1 => i\n    while i > 0:\n        int((i - 1) / 2) => p\n        if lighter(w, heap[i], heap[p]):\n            heap[p] => t\n            heap[i] => heap[p]\n            t => heap[i]\n            p => i\n        else:\n            0 => i\n    return heap\n\ndef pop(heap, w):\n    # Take the top, move the last to the top, swap downwards. [top, heap].\n    heap[0] => top\n    heap[len(heap) - 1] => last\n    heap[0:len(heap) - 1] => heap\n    if len(heap) > 0:\n        last => heap[0]\n        0 => i\n        True => going\n        while going:\n            2 * i + 1 => l\n            l + 1 => r\n            i => s\n            if l < len(heap) and lighter(w, heap[l], heap[s]):\n                l => s\n            if r < len(heap) and lighter(w, heap[r], heap[s]):\n                r => s\n            if s == i:\n                False => going\n            else:\n                heap[s] => t\n                heap[i] => heap[s]\n                t => heap[i]\n                s => i\n    return [top, heap]\n\ndef build(chars, ns):\n    # [weights, characters, lefts, rights, root, steps]: join the two\n    # lightest until one is left; each step is [a, b, new node].\n    [] => w\n    [] => ch\n    [] => left\n    [] => right\n    [] => heap\n    for i in [0:len(chars) - 1]:\n        w + [ns[i]] => w\n        ch + [chars[i]] => ch\n        left + [-1] => left\n        right + [-1] => right\n        push(heap, w, i) => heap\n    [] => steps\n    while len(heap) > 1:\n        pop(heap, w) => r\n        r[0] => a\n        r[1] => heap\n        pop(heap, w) => r\n        r[0] => b\n        r[1] => heap\n        w + [w[a] + w[b]] => w\n        ch + [\"\"] => ch\n        left + [a] => left\n        right + [b] => right\n        len(w) - 1 => node\n        steps + [[a, b, node]] => steps\n        push(heap, w, node) => heap\n    return [w, ch, left, right, heap[0], steps]\n\ndef codes(tree):\n    # Each character's code: 0 for the left branch, 1 for the right, from\n    # the root down. A tree of one character gives it the code \"0\".\n    tree[1] => ch\n    tree[2] => left\n    tree[3] => right\n    {} => out\n    if left[tree[4]] == -1:\n        \"0\" => out[ch[tree[4]]]\n        return out\n    [[tree[4], \"\"]] => todo\n    while len(todo) > 0:\n        todo[len(todo) - 1] => top\n        todo[0:len(todo) - 1] => todo\n        top[0] => node\n        if left[node] == -1:\n            top[1] => out[ch[node]]\n        else:\n            todo + [[right[node], top[1] + \"1\"], [left[node], top[1] + \"0\"]] => todo\n    return out\n\ndef encode(text, table):\n    \"\" => bits\n    for c in text:\n        bits + table[c] => bits\n    return bits\n\ndef decode(bits, tree):\n    # Walk from the root, left on 0 and right on 1, and start again at each\n    # leaf. [text, \"\"] or [\"\", why].\n    tree[1] => ch\n    tree[2] => left\n    tree[3] => right\n    tree[4] => root\n    \"\" => out\n    if left[root] == -1:\n        for b in bits:\n            if b != \"0\":\n                return [\"\", \"with one character every bit is 0\"]\n            out + ch[root] => out\n        return [out, \"\"]\n    root => node\n    for b in bits:\n        if b == \"0\":\n            left[node] => node\n        elif b == \"1\":\n            right[node] => node\n        else:\n            return [\"\", \"bits are 0 or 1\"]\n        if left[node] == -1:\n            out + ch[node] => out\n            root => node\n    if node != root:\n        return [\"\", \"the bits stop in the middle of a character's code\"]\n    return [out, \"\"]\n",
      "python": "def counts(text):\n    seen = {}\n    chars = []\n    for c in text:\n        if c in seen:\n            seen[c] = seen[c] + 1\n        else:\n            seen[c] = 1\n            chars = chars + [c]\n    for i in range(1, len(chars)):\n        x = chars[i]\n        j = i - 1\n        while j >= 0 and chars[j] > x:\n            chars[j + 1] = chars[j]\n            j = j - 1\n        chars[j + 1] = x\n    ns = [0] * len(chars)\n    for i in range(0, len(chars)):\n        ns[i] = seen[chars[i]]\n    return [chars, ns]\n\ndef lighter(w, a, b):\n    return w[a] < w[b] or w[a] == w[b] and a < b\n\ndef push(heap, w, x):\n    heap = heap + [x]\n    i = len(heap) - 1\n    while i > 0:\n        p = int((i - 1) / 2)\n        if lighter(w, heap[i], heap[p]):\n            t = heap[p]\n            heap[p] = heap[i]\n            heap[i] = t\n            i = p\n        else:\n            i = 0\n    return heap\n\ndef pop(heap, w):\n    top = heap[0]\n    last = heap[len(heap) - 1]\n    heap = heap[0:len(heap) - 1]\n    if len(heap) > 0:\n        heap[0] = last\n        i = 0\n        going = True\n        while going:\n            l = 2 * i + 1\n            r = l + 1\n            s = i\n            if l < len(heap) and lighter(w, heap[l], heap[s]):\n                s = l\n            if r < len(heap) and lighter(w, heap[r], heap[s]):\n                s = r\n            if s == i:\n                going = False\n            else:\n                t = heap[s]\n                heap[s] = heap[i]\n                heap[i] = t\n                i = s\n    return [top, heap]\n\ndef build(chars, ns):\n    w = []\n    ch = []\n    left = []\n    right = []\n    heap = []\n    for i in range(0, len(chars)):\n        w = w + [ns[i]]\n        ch = ch + [chars[i]]\n        left = left + [-1]\n        right = right + [-1]\n        heap = push(heap, w, i)\n    steps = []\n    while len(heap) > 1:\n        r = pop(heap, w)\n        a = r[0]\n        heap = r[1]\n        r = pop(heap, w)\n        b = r[0]\n        heap = r[1]\n        w = w + [w[a] + w[b]]\n        ch = ch + [\"\"]\n        left = left + [a]\n        right = right + [b]\n        node = len(w) - 1\n        steps = steps + [[a, b, node]]\n        heap = push(heap, w, node)\n    return [w, ch, left, right, heap[0], steps]\n\ndef codes(tree):\n    ch = tree[1]\n    left = tree[2]\n    right = tree[3]\n    out = {}\n    if left[tree[4]] == -1:\n        out[ch[tree[4]]] = \"0\"\n        return out\n    todo = [[tree[4], \"\"]]\n    while len(todo) > 0:\n        top = todo[len(todo) - 1]\n        todo = todo[0:len(todo) - 1]\n        node = top[0]\n        if left[node] == -1:\n            out[ch[node]] = top[1]\n        else:\n            todo = todo + [[right[node], top[1] + \"1\"], [left[node], top[1] + \"0\"]]\n    return out\n\ndef encode(text, table):\n    bits = \"\"\n    for c in text:\n        bits = bits + table[c]\n    return bits\n\ndef decode(bits, tree):\n    ch = tree[1]\n    left = tree[2]\n    right = tree[3]\n    root = tree[4]\n    out = \"\"\n    if left[root] == -1:\n        for b in bits:\n            if b != \"0\":\n                return [\"\", \"with one character every bit is 0\"]\n            out = out + ch[root]\n        return [out, \"\"]\n    node = root\n    for b in bits:\n        if b == \"0\":\n            node = left[node]\n        elif b == \"1\":\n            node = right[node]\n        else:\n            return [\"\", \"bits are 0 or 1\"]\n        if left[node] == -1:\n            out = out + ch[node]\n            node = root\n    if node != root:\n        return [\"\", \"the bits stop in the middle of a character's code\"]\n    return [out, \"\"]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n4\n0102\n4\n1\n4\n\n5\na\n5\naaaa\n1\n4\n001\n4\n000\n6\n",
      "screen": "== Huffman coding ==\nShort codes for common characters: the tree, the codes, and the bits saved.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 4\nbits to decode (0 and 1)> 0102\nCannot decode: bits are 0 or 1.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 4\nbits to decode (0 and 1)> 1\nCannot decode: the bits stop in the middle of a character's code.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 4\nbits to decode (0 and 1)> \nCancelled.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 5\ntext (2 to 200 characters)> a\nA text is 2 to 200 characters.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 5\ntext (2 to 200 characters)> aaaa\nA text of 4 characters.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 1\n  char    count  code\n  'a'        4  0\n4 characters, 1 different: Huffman needs 4 bits, 1.000 a character.\nA fixed code of 1 bit each needs 4; eight bits each, 32.\nNo other code that writes each character as its own run of bits needs fewer: Huffman's is the shortest.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 4\nbits to decode (0 and 1)> 001\nCannot decode: with one character every bit is 0.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 4\nbits to decode (0 and 1)> 000\ndecoded: aaa\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n4\n0110\n4\n10010100001\n5\nmississippi\n1\n3\n6\n",
      "screen": "== Huffman coding ==\nShort codes for common characters: the tree, the codes, and the bits saved.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 1\n  char    count  code\n  space      7  111\n  'a'        4  000\n  'e'        4  001\n  'f'        3  1101\n  'h'        2  0100\n  'i'        2  0101\n  'l'        1  10100\n  'm'        2  0110\n  'n'        2  0111\n  'o'        1  10101\n  'p'        1  10110\n  'r'        1  10111\n  's'        2  1000\n  't'        2  1001\n  'u'        1  11000\n  'x'        1  11001\n36 characters, 16 different: Huffman needs 135 bits, 3.750 a character.\nA fixed code of 4 bits each needs 144; eight bits each, 288.\nNo other code that writes each character as its own run of bits needs fewer: Huffman's is the shortest.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 2\n  join 'l' (1) and 'o' (1) into [2]\n  join 'p' (1) and 'r' (1) into [2]\n  join 'u' (1) and 'x' (1) into [2]\n  join 'h' (2) and 'i' (2) into [4]\n  join 'm' (2) and 'n' (2) into [4]\n  join 's' (2) and 't' (2) into [4]\n  join [2] and [2] into [4]\n  join [2] and 'f' (3) into [5]\n  join 'a' (4) and 'e' (4) into [8]\n  join [4] and [4] into [8]\n  join [4] and [4] into [8]\n  join [5] and space (7) into [12]\n  join [8] and [8] into [16]\n  join [8] and [12] into [20]\n  join [16] and [20] into [36]\nEach time, the two lightest pieces are joined (on a tie, the older first); the last piece is the whole tree.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 3\nbits: 100101000101100011101011000111000011111100111001000011010110101000011111010111011110001110100110... (135 in all)\ndecoded: this is an example of a huffman tree\nThe same text: no code is the start of another, so the bits split only one way.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 4\nbits to decode (0 and 1)> 0110\ndecoded: m\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 4\nbits to decode (0 and 1)> 10010100001\ndecoded: the\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 5\ntext (2 to 200 characters)> mississippi\nA text of 11 characters.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 1\n  char    count  code\n  'i'        4  11\n  'm'        1  100\n  'p'        2  101\n  's'        4  0\n11 characters, 4 different: Huffman needs 21 bits, 1.909 a character.\nA fixed code of 2 bits each needs 22; eight bits each, 88.\nNo other code that writes each character as its own run of bits needs fewer: Huffman's is the shortest.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 3\nbits: 100110011001110110111\ndecoded: mississippi\nThe same text: no code is the start of another, so the bits split only one way.\n\n1) codes  2) how the tree was built  3) encode and decode  4) decode bits  5) new text  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "char-frequency-table",
      "caseId": "157-char-frequency-table",
      "title": "Counting with a dict, in first-appearance order"
    },
    {
      "slug": "binary-heap",
      "caseId": "096-binary-heap",
      "title": "Binary heap (min-heap priority queue)"
    }
  ],
  "updated": "2026-10-15"
}
