From ef5bb0e781ac648e916b56365c23e1664b05ba20 Mon Sep 17 00:00:00 2001 From: Jacob Signorovitch Date: Tue, 7 Jan 2025 19:20:47 -0500 Subject: [PATCH] More huffman. --- huffman_coding/src/huffman_coding/Main.java | 220 +++++++++++++++----- 1 file changed, 171 insertions(+), 49 deletions(-) diff --git a/huffman_coding/src/huffman_coding/Main.java b/huffman_coding/src/huffman_coding/Main.java index 812310c..9c507cf 100644 --- a/huffman_coding/src/huffman_coding/Main.java +++ b/huffman_coding/src/huffman_coding/Main.java @@ -6,19 +6,11 @@ import tester.Tester; class Examples { - ITree a; - ITree b; - ITree c; - ITree d; - ITree e; - ITree f; - ITree g, h, i, j; + ITree a, b, c, d, e, f, g, h, i, j; - ArrayList trees; - ArrayList treesSomeSorted; - ArrayList treesFirstTwoSorted; - ArrayList treesSame; - ArrayList treesSorted; + ArrayList< + ITree + > trees, treesSomeSorted, treesFirstTwoSorted, treesSame, treesSorted; void init() { a = new Leaf(12, "a"); @@ -43,16 +35,19 @@ class Examples { init(); Util.insertIntoSorted(c, treesSomeSorted); t.checkExpect( - treesSomeSorted, - new ArrayList(Arrays.asList(c, e, d, f))); + treesSomeSorted, + new ArrayList(Arrays.asList(c, e, d, f)) + ); Util.insertIntoSorted(a, treesSomeSorted); t.checkExpect( - treesSomeSorted, - new ArrayList(Arrays.asList(c, e, a, d, f))); + treesSomeSorted, + new ArrayList(Arrays.asList(c, e, a, d, f)) + ); Util.insertIntoSorted(b, treesSomeSorted); t.checkExpect( - treesSomeSorted, - new ArrayList(Arrays.asList(c, e, a, d, f, b))); + treesSomeSorted, + new ArrayList(Arrays.asList(c, e, a, d, f, b)) + ); Util.insertIntoSorted(j, treesSame); t.checkExpect(treesSame, new ArrayList(Arrays.asList(h, i, j))); } @@ -61,12 +56,14 @@ class Examples { init(); Util.insert(a, treesFirstTwoSorted, 2); t.checkExpect( - treesFirstTwoSorted, - new ArrayList(Arrays.asList(c, e, a, b, f))); + treesFirstTwoSorted, + new ArrayList(Arrays.asList(c, e, a, b, f)) + ); Util.insert(g, treesFirstTwoSorted, 3); t.checkExpect( - treesFirstTwoSorted, - new ArrayList(Arrays.asList(c, e, a, g, b, f))); + treesFirstTwoSorted, + new ArrayList(Arrays.asList(c, e, a, g, b, f)) + ); } void testUtilSort(Tester t) { @@ -79,37 +76,126 @@ class Examples { Util.sort(trees); t.checkExpect(trees, treesSorted); } + + void testUtilEnlist(Tester t) { + String s = "Hello"; + t.checkExpect( + Util.enlist(s), + new ArrayList(Arrays.asList("H", "e", "l", "l", "o")) + ); + + String s2 = "h"; + t.checkExpect( + Util.enlist(s2), + new ArrayList(Arrays.asList("h")) + ); + } + + void testHuffmanMkcode(Tester t) { + init(); + Huffman h = new Huffman( + new ArrayList(Arrays.asList("a", "b")), + new ArrayList(Arrays.asList(1, 1)) + ); + + t.checkExpect(h.code, new Node(2, new Leaf(1, "a"), new Leaf(1, "b"))); + + Huffman h2 = new Huffman( + new ArrayList(Arrays.asList("a", "b", "c", "d", "e")), + new ArrayList(Arrays.asList(1, 2, 3, 4, 5)) + ); + + // 1: a:1, b:2, c:3, d:4, e:5 + // 2: (a:1|b:2):3, c:3, d:4, e:5 + // 3: d:4, e:5, ((a:1|b:2):3|c:3):6 + // 4: ((a:1|b:2):3|c:3):6, (d:4|e:5):9 + // 5: (((a:1|b:2):3|:3):6|(d:4|e:5):9):15 + + t.checkExpect( + h2.code, + new Node( + 15, + new Node( + 6, + new Node(3, new Leaf(1, "a"), new Leaf(2, "b")), + new Leaf(3, "c") + ), + new Node(9, new Leaf(4, "d"), new Leaf(5, "e")) + ) + ); + } } class Huffman { ArrayList charset; // Character set. ArrayList freqs; // Frequencies of characters in charset. + ITree code; // The tree allowing for encoding and decoding. Huffman(ArrayList charset, ArrayList freqs) { - if (charset.size() != freqs.size()) - throw new IllegalArgumentException( - "Character set must match frequencies."); + if (charset.size() != freqs.size()) throw new IllegalArgumentException( + "Character set must match frequencies." + ); - if (charset.size() < 2) - throw new IllegalArgumentException( - "Character set too small."); + if (charset.size() < 2) throw new IllegalArgumentException( + "Character set too small." + ); this.charset = charset; this.freqs = freqs; + + this.mkcode(); } - /* - * // Encode a message. - * ArrayList encode(String s) { - * for - * } - * - * // Decode a message. - * String decode(ArrayList c) { - * - * } - */ + // Creates the code tree. + void mkcode() { + ArrayList nodes = new ArrayList<>(); + + // Copy all chars over as a tree leaves. + for (int i = 0; i < this.charset.size(); i++) nodes.add( + new Leaf(this.freqs.get(i), this.charset.get(i)) + ); + + // Combine until all under a single tree. + while (nodes.size() > 1) { + // Sort the nodes. + Util.sort(nodes); + + // Move the 2 lowest into their own tree. + ITree firstLowest = nodes.get(0); + ITree secondLowest = nodes.get(1); + nodes.remove(firstLowest); + nodes.remove(secondLowest); + ITree parent = new Node( + firstLowest.sumFreq(secondLowest), + firstLowest, + secondLowest + ); + + // Put new tree into list. + nodes.add(0, parent); + } + + // Set the code to the first (and hopefully only) tree. + this.code = nodes.getFirst(); + } + + // Encode a message. + ArrayList encode(String s) { + ArrayList msg = Util.enlist(s); // The given message, as a list of characters. + ArrayList enc = new ArrayList<>(); // The returned encoded message. + + // Loop for each character in the message. + for (String ch : msg) { + // Throw error if character is not in charset. + if (!this.charset.contains(ch)) throw new IllegalArgumentException( + "Cannot encode unknown character \"" + ch + "\"." + ); + // Go through the + } + + return enc; + } } // Binary tree. @@ -119,6 +205,13 @@ interface ITree { // Is this tree less than the given frequency? boolean lessThan(int freq); + + // Sum the frequency with another tree. + int sumFreq(ITree t); + int sumFreq(int freq); + + // Given the character, choose whether to go left (0) or right (1) or stop (-1). + Int getNext(String ch); } abstract class ATree implements ITree { @@ -136,18 +229,39 @@ abstract class ATree implements ITree { public boolean lessThan(int freq) { return this.freq < freq; } + + public int sumFreq(ITree t) { + return t.sumFreq(this.freq); + } + + public int sumFreq(int freq) { + return this.freq + freq; + } } class Node extends ATree { - // Left and right branches of tree. + // Left (false) and right (true) branches of tree. ITree l, r; + // The 'question' the node asks to determine whether to go left or right. If + // the character is in the string, go right. + String question; + Node(int freq, ITree l, ITree r) { super(freq); this.l = l; this.r = r; } + + // Ask its question on a character. + Boolean ask(String ch) { + return this.question.contains(ch); + } + + Integer getNext(String ch) { + return this.ask(ch) ? + } } class Leaf extends ATree { @@ -165,8 +279,7 @@ class Util { // Sort a list of trees from least to most frequent. static void sort(ArrayList trees) { // Our work here is done. - if (trees.size() < 2) - return; + if (trees.size() < 2) return; for (int i = 1; i < trees.size(); i++) { // Copy out the tree to insert. @@ -185,18 +298,27 @@ class Util { // Insert tree. insertIntoSorted(tree, sorted); // Overwrite original trees with sorted ones. - for (int j = 0; j < i; trees.set(j, sorted.get(j++))) - ; + for (int j = 0; j < i; trees.set(j, sorted.get(j++))); trees.add(i, sorted.getLast()); } // Insert a Tree in an already fully sorted ArrayList of Trees. static void insertIntoSorted(ITree tree, ArrayList sortedTrees) { - for (int i = 0; i < sortedTrees.size(); i++) - if (sortedTrees.get(i).lessThan(tree)) { - sortedTrees.add(i, tree);// - return; - } + for (int i = 0; i < sortedTrees.size(); i++) if ( + sortedTrees.get(i).lessThan(tree) + ) { + sortedTrees.add(i, tree); // + return; + } sortedTrees.addLast(tree); // This tree is bigger than all the others. } + + // Convert a string to a list. + static ArrayList enlist(String s) { + ArrayList ret = new ArrayList<>(); + for (int i = 0; i < s.length(); i++) ret.add( + String.valueOf(s.charAt(i)) // Why why why can't we just use a list of chars ;_; + ); + return ret; + } }