summaryrefslogtreecommitdiff
path: root/huffman-coding/huffman-test.scm
diff options
context:
space:
mode:
Diffstat (limited to 'huffman-coding/huffman-test.scm')
-rw-r--r--huffman-coding/huffman-test.scm179
1 files changed, 179 insertions, 0 deletions
diff --git a/huffman-coding/huffman-test.scm b/huffman-coding/huffman-test.scm
new file mode 100644
index 0000000..b30c4ca
--- /dev/null
+++ b/huffman-coding/huffman-test.scm
@@ -0,0 +1,179 @@
+;; -*- compile-command: "guile -L . huffman-test.scm"; -*-
+(use-modules (srfi srfi-64)
+ (pq)
+ (huffman))
+
+(define runner "a fast runner need never be afraid of the dark")
+(define runner-opt-key '((#\v . "111001") (#\u . "111000") (#\t . "0100") (#\s . "111011") (#\r . "100") (#\o . "111010") (#\n . "1111") (#\k . "101101") (#\i . "101100") (#\h . "101111") (#\f . "1010") (#\e . "110") (#\d . "0101") (#\b . "101110") (#\a . "011") (#\space . "00")))
+(define abcde "A_DEAD_DAD_CEDED_A_BAD_BABE_A_BEADED_ABACA_BED")
+(define abcde-opt-key '((#\_ . "01") (#\E . "110") (#\D . "00") (#\C . "1110") (#\B . "1111") (#\A . "10")))
+(define abcde-encoded "1001001101000010010000111101100011000011001111110000111111011111100110011111110100011000011011111011101001111111000")
+(define (hash-table->alist ht)
+ (hash-map->list cons ht))
+
+(define (node-pq->char-alist pq)
+ (map (lambda (ele)
+ (cons (node-char (car ele))
+ (cdr ele)))
+ (car pq)))
+
+(define (extract-tree pq)
+ (car (pq-front pq)))
+
+
+(test-begin "harness")
+
+(test-equal "hash-frequency zero chars, empty hash"
+ '()
+ (sort-rao (hash-table->alist (hash-frequency ""))))
+
+(test-equal "hash-frequency single 'c' key pair correct"
+ '((#\c . 1))
+ (sort-rao (hash-table->alist (hash-frequency "c"))))
+
+(test-equal "hash-frequency double 'c' key pair correct"
+ '((#\c . 2))
+ (sort-rao (hash-table->alist (hash-frequency "cc"))))
+
+(test-equal "hash-frequency couple letters key pair correct"
+ '((#\c . 2) (#\Y . 1))
+ (sort-rao (hash-table->alist (hash-frequency "cYc"))))
+
+(test-equal "hash-frequency couple letters key pair correct"
+ '((#\c . 2) (#\Y . 1))
+ (sort-rao (hash-table->alist (hash-frequency "cYc"))))
+
+(test-equal "hash-frequency abcde"
+ '((#\_ . 10) (#\E . 7) (#\D . 10) (#\C . 2) (#\B . 6) (#\A . 11))
+ (sort-rao (hash-table->alist (hash-frequency abcde))))
+
+(test-assert "frequencies->pq accepts hashtable and returns a pq"
+ (pq? (frequencies->pq (make-hash-table))))
+
+(test-equal "frequencies->pq single entry"
+ '((#\a . -3))
+ (node-pq->char-alist (frequencies->pq (hash-frequency "aaa"))))
+
+(test-equal "frequencies->pq double entry"
+ '((#\a . -3) (#\b . -3))
+ (node-pq->char-alist (frequencies->pq (hash-frequency "aaabbb"))))
+
+(test-equal "frequencies->pq tie natural order"
+ '((#\a . -3) (#\b . -3) (#\c . -4))
+ (node-pq->char-alist (frequencies->pq (hash-frequency "cabababccc"))))
+
+(test-equal "frequencies->pq tie reverse order"
+ '((#\a . -3) (#\b . -3) (#\c . -4))
+ (node-pq->char-alist (frequencies->pq (hash-frequency "cbababaccc"))))
+
+(test-equal "frequencies->pq abcde"
+ '((#\C . -2) (#\B . -6) (#\E . -7) (#\D . -10) (#\_ . -10) (#\A . -11))
+ (node-pq->char-alist (frequencies->pq (hash-frequency abcde))))
+
+(let* ((pq (frequencies->pq (hash-frequency "aab")))
+ (pqo (frequencies->pq (hash-frequency "aab")))
+ (lc (car (pq-front pq)))
+ (rc (car (pq-rear pq)))
+ (p (begin (combine-and-reinsert! pq)
+ (car (pq-front pq)))))
+
+ (test-assert "pq and pqo now different objects, but both pqs"
+ (and (not (equal? pq pqo))
+ (pq? pq)
+ (pq? pqo)))
+
+ (test-assert "combine-and-reinsert! left child is correct"
+ (equal? (node-left p) lc))
+
+ (test-assert "combine-and-reinsert! right child is correct"
+ (equal? (node-right p) rc))
+
+ (test-equal "combine-and-reinsert! correctly sums priority"
+ -3
+ (cdr (pq-front pq))))
+
+(let* ((pq (frequencies->pq (hash-frequency "aaabbbcccc"))))
+ (combine-and-reinsert! pq)
+
+ (test-equal "combine-and-reinsert! first elements combined"
+ #\c
+ (node-char (car (pq-front pq))))
+
+ (test-equal "combine-and-reinsert! new node inserted in correct spot"
+ -6
+ (cdr (pq-rear pq))))
+
+(let ((pq (combine-all! (frequencies->pq (hash-frequency runner)))))
+
+ (test-assert "combine-all! returns a pq"
+ (pq? pq))
+
+ (test-equal "combine-all! pq is of length one"
+ 1
+ (pq-length pq))
+
+ (test-assert "combine-all! root item is a node"
+ (node? (car (pq-front pq))))
+
+ (test-equal "combine-all! root frequency matches length of str"
+ -46
+ (cdr (pq-front pq))))
+
+(test-equal "create-encodings level-1 tree"
+ '((#\a . "0"))
+ (sort-rao (create-encodings (extract-tree
+ (combine-all!
+ (frequencies->pq
+ (hash-frequency "a")))))))
+
+;;;; note: verified manually: https://en.wikipedia.org/wiki/Huffman_coding
+(test-equal "create-encodings abcde"
+ abcde-opt-key
+ (sort-rao (create-encodings (extract-tree
+ (combine-all!
+ (frequencies->pq
+ (hash-frequency abcde)))))))
+
+(test-equal "create-encodings runner"
+ runner-opt-key
+ (sort-rao (create-encodings (extract-tree
+ (combine-all!
+ (frequencies->pq
+ (hash-frequency runner)))))))
+
+(let ((none (encode-text "")))
+
+ (test-assert "encode-text nothing empty string"
+ (string-null? (car none)))
+
+ (test-assert "encode-text nothing empty alist"
+ (null? (cdr none))))
+
+(let ((limit (encode-text abcde)))
+
+ (test-equal "encode-text abcde string"
+ abcde-encoded
+ (car limit))
+
+ (test-equal "encode-text abcde alist"
+ abcde-opt-key
+ (sort-rao (cdr limit))))
+
+(test-equal "decode single char"
+ "x"
+ (decode-text "00011" '((#\x . "00011") (#\y . "001"))))
+
+(test-equal "decode two chars"
+ "xq"
+ (decode-text "00011" '((#\x . "000") (#\y . "011") (#\q . "11"))))
+
+(test-equal "decode abcde"
+ abcde
+ (decode-text abcde-encoded abcde-opt-key))
+
+(test-error "decode abcde nonsense key"
+ #t
+ (decode-text abcde-encoded runner-opt-key))
+
+
+(test-end "harness")