home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / BIPL.ZIP / PROCS.ZIP / HUFFSTUF.ICN < prev    next >
Encoding:
Text File  |  1992-09-28  |  9.9 KB  |  379 lines

  1. ############################################################################
  2. #
  3. #    File:     huffstuf.icn
  4. #
  5. #    Subject:  Procedures for huffman coding
  6. #
  7. #    Author:   Richard L. Goerwitz
  8. #
  9. #    Date:     July 20, 1992
  10. #
  11. ###########################################################################
  12. #
  13. #    Version:  1.2
  14. #
  15. ###########################################################################
  16. #  
  17. #  An odd assortment of tools that lets me compress text using an
  18. #  Iconish version of a generic Huffman algorithm.
  19. #
  20. ############################################################################
  21. #
  22. #  Links: codeobj outbits inbits
  23. #
  24. #  See also: hufftab.icn, press.icn
  25. #
  26. ############################################################################
  27.  
  28. # From the IPL.
  29. link codeobj
  30.  
  31. # Necessary records.
  32. record nodE(l,r,n)
  33. record _ND(l,r)
  34. record leaF(c,n)
  35. record huffcode(c,i,len)
  36.  
  37. # For debugging purposes.
  38. # link ximage
  39.  
  40. # Count of chars in input file.
  41. global count_of_all_chars
  42.  
  43.  
  44. procedure main(a)
  45.  
  46.     local direction, usage, size, char_tbl, heap, tree, h_tbl
  47.     usage := "huffcode -i|o filename1"
  48.  
  49.     direction := pop(a) | stop(usage)
  50.     direction ?:= { ="-"; tab(any('oi')) } | stop(usage)
  51.     *a = 1 | stop(usage)
  52.  
  53.     intext := open(a[1]) | quitprog("huffcode", "can't open "||a[1], 1)
  54.     size   := 80
  55.  
  56.     if direction == "o" then {
  57.  
  58.     char_tbl := table()
  59.     while count_chars_in_s(reads(intext), char_tbl)
  60.     heap     := initialize_heap(char_tbl)
  61.     tree     := heap_2_tree(heap)
  62.     h_tbl    := hash_codes(tree)
  63.  
  64.     put_tree(&output, tree)
  65.     seek(intext, 1)
  66.     every writes(&output, encode_string(|reads(intext, size), h_tbl))
  67.  
  68.     }
  69.     else {
  70.     tree := get_tree(intext)
  71.     every writes(&output, decode_rest_of_file(intext, size, tree))
  72.     }
  73.  
  74. end
  75.  
  76.  
  77. procedure count_chars_in_s(s, char_tbl)
  78.  
  79.     #
  80.     # Count chars in s, placing stats in char_tbl (keys = chars in
  81.     # s, values = leaF records, with the counts for each chr in s
  82.     # contained in char_tbl[chr].n).
  83.     #
  84.     local chr
  85.     initial {
  86.     /char_tbl &
  87.         quitprog("count_chars_in_s", "need 2 args - 1 string, 2 table", 9)
  88.     *char_tbl ~= 0 &
  89.         quitprog("count_chars_in_s","start me with an empty table",8)
  90.     count_of_all_chars := 0
  91.     }
  92.  
  93.     # Reset character count on no-arg invocation.
  94.     /s & /char_tbl & {
  95.     count_of_all_chars := 0
  96.     return
  97.     }
  98.  
  99.     # Insert counts for characters into char_tbl.  Note that we don't
  100.     # just put them into the table as-is.  Rather, we put them into
  101.     # a record which contains the character associated with the count.
  102.     # These records are later used by the Huffman encoding algorithm.
  103.     s ? {
  104.     while chr := move(1) do {
  105.         count_of_all_chars +:= 1
  106.         /char_tbl[chr]   := leaF(chr,0)
  107.         char_tbl[chr].n +:= 1
  108.     }
  109.     }
  110.     return *char_tbl        # for lack of anything better
  111.  
  112. end
  113.  
  114.  
  115. procedure initialize_heap(char_tbl)
  116.  
  117.     #
  118.     # Create heap data structure out of the table filled out by
  119.     # successive calls to count_chars_in_s(s,t).  The heap is just a
  120.     # list.  Naturally, it's size can be obtained via *heap.
  121.     #
  122.     local heap
  123.  
  124.     heap := list()
  125.     every push(heap, !char_tbl) do
  126.     reshuffle_heap(heap, 1)
  127.     return heap
  128.  
  129. end
  130.  
  131.  
  132. procedure reshuffle_heap(h, k)
  133.  
  134.     #
  135.     # Based loosely on Sedgewick (2nd. ed., 1988), p. 160.  Take k-th
  136.     # node on the heap, and walk down the heap, switching this node
  137.     # along the way with the child whose value is the least AND whose
  138.     # value is less than this node's.  Stop when you find no children
  139.     # whose value is less than that of the original node.  Elements on
  140.     # heap are records of type leaF, with the values contained in the
  141.     # "n" field.
  142.     #
  143.     local j
  144.  
  145.     # While we haven't spilled off the end of the heap (the size of the
  146.     # heap is *h; *h / 2 is the biggest k we need to look at)...
  147.     while k <= (*h / 2) do {
  148.  
  149.     # ...double k, assign the result to j.
  150.     j := k+k
  151.  
  152.     # If we aren't at the end of the heap...
  153.     if j < *h then {
  154.         # ...check to see which of h[k]'s children is the smallest,
  155.         # and make j point to it.
  156.         if h[j].n > h[j+1].n then
  157.         # h[j] :=: h[j+1]
  158.         j +:= 1
  159.     }
  160.  
  161.     # If the current parent (h[k]) has a value less than those of its
  162.     # children, then break; we're done.
  163.     if h[k].n <= h[j].n then break
  164.  
  165.     # Otherwise, switch the parent for the child, and loop around
  166.         # again, with k (the pointer to the parent) now pointing to the
  167.     # new offset of the element we have been working on.
  168.     h[k] :=: h[j]
  169.     k := j
  170.  
  171.     }
  172.  
  173.     return k
  174.     
  175. end
  176.  
  177.  
  178. procedure heap_2_tree(h)
  179.  
  180.     #
  181.     # Construct the Huffman tree out of heap h.  Find the smallest
  182.     # element, pop it off the heap, then reshuffle the heap.  After
  183.     # reshuffling, replace the top record on the stack with a nodE()
  184.     # record whose n field equal to the sum of the n fields for the
  185.     # element popped off the stack originally, and the one that is
  186.     # now about to be replaced.  Link the new nodE record to the 2
  187.     # elements on the heap it is now replacing.  Reshuffle the heap
  188.     # again, then repeat.  You're done when the size of the heap is
  189.     # 1.  That one element remaining (h[1]) is your Huffman tree.
  190.     #
  191.     # Based loosely on Sedgewick (2nd ed., 1988), p. 328-9.
  192.     #
  193.     local frst, scnd, count
  194.  
  195.     until *h = 1 do {
  196.  
  197.     h[1] :=: h[*h]        # Reverse first and last elements.
  198.     frst := pull(h)        # Pop last elem off & save it.
  199.     reshuffle_heap(h, 1)    # Resettle the heap.
  200.     scnd := !h        # Save (but don't clobber) top element.
  201.  
  202.     count := frst.n + scnd.n
  203.     frst := { if *frst = 2 then frst.c else _ND(frst.l, frst.r) }
  204.     scnd := { if *scnd = 2 then scnd.c else _ND(scnd.l, scnd.r) }
  205.  
  206.     h[1] := nodE(frst, scnd, count) # Create new nodE().
  207.     reshuffle_heap(h, 1)    # Resettle once again.
  208.     }
  209.  
  210.     # H is no longer a stack.  It's single element - the root of a
  211.     # Huffman tree made up of nodE()s and leaF()s.  Put the l and r
  212.     # fields of that element into an _ND record, and return the new
  213.     # record.
  214.     return _ND(h[1].l, h[1].r)
  215.  
  216. end
  217.  
  218.  
  219. procedure hash_codes(tr)
  220.  
  221.     #
  222.     # Hash Huffman codes.  Tr (arg 1) is a Huffman tree created by
  223.     # heap_2_tree(heap).  Output is a table, with the keys
  224.     # representing characters, and the values being records of type
  225.     # huffcode(i,len), where i is the Huffcode (an integer) and len is
  226.     # the number of bits it occupies.
  227.     #
  228.     local code
  229.  
  230.     huff_tbl := table()
  231.     every code := collect_bits(tr) do
  232.     insert(huff_tbl, code.c, code)
  233.     return huff_tbl
  234.  
  235. end
  236.     
  237.  
  238. procedure collect_bits(tr, i, len)
  239.  
  240.     #
  241.     # Decompose Huffman tree tr into huffcode() records which contain
  242.     # 3 fields:  c (the character encoded), i (its integer code),
  243.     # and len (the number of bytes the integer code occupies).  Sus-
  244.     # pend one such record for each character encoded in tree tr.
  245.     #
  246.  
  247.     if type(tr) == "string" then
  248.     return huffcode(tr, i, len)
  249.     else {
  250.     (/len := 1) | (len +:= 1)
  251.     (/i   := 0) | (i   *:= 2)
  252.     suspend collect_bits(tr.l, i, len)
  253.     i   +:= 1
  254.     suspend collect_bits(tr.r, i, len)
  255.     }
  256.  
  257. end
  258.  
  259.  
  260. procedure put_tree(f, tr)
  261.  
  262.     #
  263.     # Writes Huffman tree tr to file f.  Uses first two bits to store
  264.     # the size of the tree.
  265.     #
  266.     local stringized_tr
  267.     # global count_of_all_chars
  268.  
  269.     /f | /tr & quitprog("put_tree","I need two nonnull arguments",7)
  270.  
  271.     stringized_tr := encode(tr)
  272.     every writes(f, outbits(*stringized_tr, 16))     # use two bytes
  273.     outbits()                         # just in case
  274.     writes(f, stringized_tr)
  275.     # How many characters are there in the input file?
  276.     every writes(f, outbits(count_of_all_chars, 32))
  277.     outbits()
  278.  
  279. end
  280.  
  281.  
  282. procedure get_tree(f)
  283.  
  284.     #
  285.     # Reads in Huffman tree from file f, sets pointer to the first
  286.     # encoded bit (as opposed to the bits which form the tree des-
  287.     # cription) in file f.
  288.     #
  289.     local stringized_tr_size, tr
  290.     # global count_of_all_chars
  291.  
  292.     stringized_tr_size := inbits(f, 16)
  293.     tr := decode(reads(f, stringized_tr_size)) |
  294.     quitprog("get_tree", "can't decode tree", 6)
  295.     count_of_all_chars := inbits(f, 32) |
  296.     quitprog("get_tree", "garbled input file", 10)
  297.     return tr
  298.  
  299. end
  300.  
  301.  
  302. procedure encode_string(s, huffman_table)
  303.  
  304.     #
  305.     # Encode string s using the codes in huffman_table (created by
  306.     # hash_codes, which in turns uses the Huffman tree created by
  307.     # heap_2_tree).
  308.     #
  309.     # Make sure you are using reads() and not read, unless you don't
  310.     # want to preserve newlines.
  311.     #
  312.     local s2, chr, hcode    # hcode stores huffcode records
  313.     static chars_written
  314.     initial chars_written := 0
  315.  
  316.     s2 := ""
  317.     s ? {
  318.     while chr := move(1) do {
  319.         chars_written +:= 1
  320.         hcode := \huffman_table[chr] |
  321.         quitprog("encode_string", "unexpected char, "||image(chr), 11)
  322.         every s2 ||:= outbits(hcode.i, hcode.len)
  323.     }
  324.     # If at end of output stream, then flush outbits buffer.
  325.     if chars_written = count_of_all_chars then {
  326.         chars_written := 0
  327.         s2 ||:= outbits()
  328.     } else {
  329.         if chars_written > count_of_all_chars then {
  330.         chars_written := 0
  331.         quitprog("encode_string", "you're trying to write _
  332.             more chars than you originally tabulated", 12)
  333.         }
  334.     }
  335.     }
  336.     return s2
  337.  
  338. end
  339.  
  340.  
  341. procedure decode_rest_of_file(f, size, huffman_tree)
  342.  
  343.     local s2, line, E, chr, bit
  344.     static chars_decoded
  345.     initial chars_decoded := 0
  346.  
  347.     E := huffman_tree
  348.     while line := reads(f, size) do {
  349.     line ? {
  350.         s2 := ""
  351.         while chr := move(1) do {
  352.         every bit := iand(1, ishift(ord(chr), -7 to 0)) do {
  353.             E := { if bit = 0 then E.l else E.r }
  354.             if s2 ||:= string(E) then {
  355.             chars_decoded +:= 1
  356.             if chars_decoded = count_of_all_chars then {
  357.                 chars_decoded := 0
  358.                 break { break break }
  359.             }
  360.             else E := huffman_tree
  361.             }
  362.         }
  363.         }
  364.         suspend s2
  365.     }
  366.     }
  367.     suspend s2
  368.  
  369. end
  370.  
  371.  
  372. procedure quitprog(p, m, c)
  373.  
  374.     /m := "program error"
  375.     write(&errout, p, ":  ", m)
  376.     exit(\c | 1)
  377.  
  378. end
  379.