FINISHED HUFFMAN FINALLLLLLYLYLLY

This commit is contained in:
Jacob Signorovitch
2025-01-13 13:56:01 -05:00
parent d9d937c77e
commit b6ec810bb9
3 changed files with 384 additions and 371 deletions

View File

@@ -38,19 +38,16 @@ class Examples {
void init2() { void init2() {
// jacob itaru signorovitch // jacob itaru signorovitch
letters = new ArrayList<>(Arrays.asList( letters = new ArrayList<>(Arrays.asList("j", "a", "c", "o", "b", "i", "t",
"j", "a", "c", "o", "b", "i", "t", "r", "u", "s", "g", "n", "h", "v" "r", "u", "s", "g", "n", "h", "v"));
));
counts = new ArrayList<>( counts = new ArrayList<>(
Arrays.asList(1, 2, 2, 3, 1, 3, 2, 2, 1, 1, 1, 1, 1, 1) Arrays.asList(1, 2, 2, 3, 1, 3, 2, 2, 1, 1, 1, 1, 1, 1));
);
} }
void init3() { void init3() {
// hello world // hello world
letters = letters = new ArrayList<>(Arrays.asList("h", "e", "l", "o", "w", "r", "d"));
new ArrayList<>(Arrays.asList("h", "e", "l", "o", "w", "r", "d"));
counts = new ArrayList<>(Arrays.asList(1, 1, 3, 2, 1, 1, 1)); counts = new ArrayList<>(Arrays.asList(1, 1, 3, 2, 1, 1, 1));
huff = new Huffman(letters, counts); huff = new Huffman(letters, counts);
@@ -91,18 +88,14 @@ class Examples {
void testUtilInsertIntoSorted(Tester t) { void testUtilInsertIntoSorted(Tester t) {
init(); init();
Util.insertIntoSorted(c, treesSomeSorted); Util.insertIntoSorted(c, treesSomeSorted);
t.checkExpect( t.checkExpect(treesSomeSorted,
treesSomeSorted, new ArrayList<ITree>(Arrays.asList(c, e, d, f)) new ArrayList<ITree>(Arrays.asList(c, e, d, f)));
);
Util.insertIntoSorted(a, treesSomeSorted); Util.insertIntoSorted(a, treesSomeSorted);
t.checkExpect( t.checkExpect(treesSomeSorted,
treesSomeSorted, new ArrayList<ITree>(Arrays.asList(c, e, a, d, f)) new ArrayList<ITree>(Arrays.asList(c, e, a, d, f)));
);
Util.insertIntoSorted(b, treesSomeSorted); Util.insertIntoSorted(b, treesSomeSorted);
t.checkExpect( t.checkExpect(treesSomeSorted,
treesSomeSorted, new ArrayList<ITree>(Arrays.asList(c, e, a, d, f, b)));
new ArrayList<ITree>(Arrays.asList(c, e, a, d, f, b))
);
Util.insertIntoSorted(j, treesSame); Util.insertIntoSorted(j, treesSame);
t.checkExpect(treesSame, new ArrayList<ITree>(Arrays.asList(h, i, j))); t.checkExpect(treesSame, new ArrayList<ITree>(Arrays.asList(h, i, j)));
} }
@@ -110,15 +103,11 @@ class Examples {
void testUtilInsert(Tester t) { void testUtilInsert(Tester t) {
init(); init();
Util.insert(a, treesFirstTwoSorted, 2); Util.insert(a, treesFirstTwoSorted, 2);
t.checkExpect( t.checkExpect(treesFirstTwoSorted,
treesFirstTwoSorted, new ArrayList<ITree>(Arrays.asList(c, e, a, b, f)));
new ArrayList<ITree>(Arrays.asList(c, e, a, b, f))
);
Util.insert(g, treesFirstTwoSorted, 3); Util.insert(g, treesFirstTwoSorted, 3);
t.checkExpect( t.checkExpect(treesFirstTwoSorted,
treesFirstTwoSorted, new ArrayList<ITree>(Arrays.asList(c, e, a, g, b, f)));
new ArrayList<ITree>(Arrays.asList(c, e, a, g, b, f))
);
} }
void testUtilSort(Tester t) { void testUtilSort(Tester t) {
@@ -134,32 +123,23 @@ class Examples {
void testUtilEnlist(Tester t) { void testUtilEnlist(Tester t) {
String s = "Hello"; String s = "Hello";
t.checkExpect( t.checkExpect(Util.enlist(s), new ArrayList<String>(
Util.enlist(s), Arrays.asList("H", "e", "l", "l", "o")));
new ArrayList<String>(Arrays.asList("H", "e", "l", "l", "o"))
);
String s2 = "h"; String s2 = "h";
t.checkExpect( t.checkExpect(Util.enlist(s2), new ArrayList<String>(Arrays.asList("h")));
Util.enlist(s2), new ArrayList<String>(Arrays.asList("h"))
);
} }
void testHuffmanMkcode(Tester t) { void testHuffmanMkcode(Tester t) {
init(); init();
Huffman h = new Huffman( Huffman h = new Huffman(new ArrayList<String>(Arrays.asList("a", "b")),
new ArrayList<String>(Arrays.asList("a", "b")), new ArrayList<Integer>(Arrays.asList(1, 1)));
new ArrayList<Integer>(Arrays.asList(1, 1))
);
t.checkExpect( t.checkExpect(h.code, new Node(2, new Leaf(1, "a"), new Leaf(1, "b")));
h.code, new Node(2, "b", new Leaf(1, "a"), new Leaf(1, "b"))
);
Huffman h2 = new Huffman( Huffman h2 = new Huffman(
new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e")), new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e")),
new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5)) new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5)));
);
// 1: a:1, b:2, c:3, d:4, e:5 // 1: a:1, b:2, c:3, d:4, e:5
// 2: (a:1|b:2):3, c:3, d:4, e:5 // 2: (a:1|b:2):3, c:3, d:4, e:5
@@ -169,45 +149,60 @@ class Examples {
t.checkExpect( t.checkExpect(
h2.code, h2.code,
new Node( new Node(15,
15, "e", new Node(6, new Node(3, new Leaf(1, "a"), new Leaf(2, "b")),
new Node( new Leaf(3, "c")),
6, "c", new Node(9, new Leaf(4, "d"), new Leaf(5, "e"))));
new Node(3, "b", new Leaf(1, "a"), new Leaf(2, "b")),
new Leaf(3, "c")
),
new Node(9, "e", new Leaf(4, "d"), new Leaf(5, "e"))
)
);
} }
void testHuffmanEncode(Tester t) { void testHuffmanEncodeAndAlsoDecodeAndAlsoEverythingElse(Tester t) {
init3(); init3();
t.checkExpect( t.checkExpect(
huff.code, huff.code,
new Node( new Node(
10, "lrwdoeh", 10,
new Node( new Node(
4, "oeh", 4,
new Node(2, "eh", new Leaf(1, "h"), new Leaf(1, "e")),
new Leaf(2, "o")
),
new Node( new Node(
6, "lrwd", 2,
new Leaf(1, "h"), // //
new Leaf(1, "e")),
new Leaf(2, "o")),
new Node( new Node(
3, "rwd", new Leaf(1, "d"), 6,
new Node(2, "rw", new Leaf(1, "w"), new Leaf(1, "r")) new Node(
), 3,
new Leaf(3, "l") new Leaf(1, "d"), // //
) new Node(
) 2,
); new Leaf(1, "w"), // //
new Leaf(1, "r"))),
new Leaf(3, "l"))));
t.checkExpect( ArrayList<Boolean> path =
huff.encode("he"), new ArrayList<Boolean>(Arrays.asList( new ArrayList<>(Arrays.asList(false, false, false));
false, false, false, false, false, true
)) ArrayList<Boolean> path2 =
); new ArrayList<>(Arrays.asList(false, false, false, false, false, true,
true, true, true, true, false, true));
ArrayList<Boolean> path3 = new ArrayList<>(
Arrays.asList(false, false, false, false, false, true, true, true, true,
true, false, true, true, false, true, false, false, true,
true, false, true, true, true, true, true, false, false));
t.checkExpect(huff.encode("h"), path);
t.checkExpect(huff.decode(path), "h");
t.checkExpect(huff.encode("hello"), path2);
t.checkExpect(huff.decode(path2), "hello");
t.checkExpect(huff.encode("helloworld"), path3);
t.checkExpect(huff.decode(path3), "helloworld");
Huffman huff2 = new Huffman("foo bar baz");
ArrayList<Boolean> enc = huff2.encode("booz farz fazz");
t.checkExpect(huff2.decode(enc), "booz farz fazz");
} }
} }
@@ -216,12 +211,13 @@ class Huffman {
ArrayList<String> charset; // Character set. ArrayList<String> charset; // Character set.
ArrayList<Integer> freqs; // Frequencies of characters in charset. ArrayList<Integer> freqs; // Frequencies of characters in charset.
ITree code; // The tree allowing for encoding and decoding. ITree code; // The tree allowing for encoding and decoding.
ArrayList<ArrayList<Boolean>> map; // Association between the charset and the
// path to reach that char in the code.
Huffman(ArrayList<String> charset, ArrayList<Integer> freqs) { Huffman(ArrayList<String> charset, ArrayList<Integer> freqs) {
if (charset.size() != freqs.size()) if (charset.size() != freqs.size())
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Character set must match frequencies." "Character set must match frequencies.");
);
if (charset.size() < 2) if (charset.size() < 2)
throw new IllegalArgumentException("Character set too small."); throw new IllegalArgumentException("Character set too small.");
@@ -229,7 +225,44 @@ class Huffman {
this.charset = charset; this.charset = charset;
this.freqs = freqs; this.freqs = freqs;
this.map = new ArrayList<>();
// Generate the code.
this.mkcode(); this.mkcode();
// Generate map.
this.mkmap();
}
// Generates character freqs from string.
Huffman(String data) {
if (data.length() <= 1)
throw new Error("Can't create tree from that.");
ArrayList<String> charset = new ArrayList<>();
ArrayList<Integer> freqs = new ArrayList<>();
for (int i = 0; i < data.length(); i++) {
// The current character.
String ch = data.substring(i, i + 1);
if (charset.contains(ch)) {
int j = charset.indexOf(ch);
freqs.set(j, freqs.get(j) + 1);
} else {
charset.add(ch);
freqs.add(1);
}
}
this.charset = charset;
this.freqs = freqs;
this.map = new ArrayList<>();
// Generate the code.
this.mkcode();
// Generate map.
this.mkmap();
} }
// Creates the code tree. // Creates the code tree.
@@ -250,10 +283,8 @@ class Huffman {
ITree secondLowest = nodes.get(1); ITree secondLowest = nodes.get(1);
nodes.remove(firstLowest); nodes.remove(firstLowest);
nodes.remove(secondLowest); nodes.remove(secondLowest);
ITree parent = new Node( ITree parent = new Node(firstLowest.sumFreq(secondLowest), firstLowest,
firstLowest.sumFreq(secondLowest), firstLowest.getQuestion(), secondLowest);
firstLowest, secondLowest
);
// Put new tree into list. // Put new tree into list.
nodes.add(0, parent); nodes.add(0, parent);
@@ -263,34 +294,39 @@ class Huffman {
this.code = nodes.getFirst(); this.code = nodes.getFirst();
} }
// Make the character path mapping.
void mkmap() {
for (String ch : this.charset)
this.map.addLast(this.code.encode(ch));
}
// Encode a message. // Encode a message.
ArrayList<Boolean> encode(String s) { ArrayList<Boolean> encode(String s) {
System.out.println("Encoding " + s);
System.out.flush();
ArrayList<String> msg = ArrayList<String> msg =
Util.enlist(s); // The given message, as a list of characters. Util.enlist(s); // The given message, as a list of characters.
ArrayList<Boolean> enc = ArrayList<Boolean> enc = new ArrayList<>(); // The returned encoded message.
new ArrayList<>(); // The returned encoded message.
// Loop for each character in the message. // Loop for each character in the message.
for (String ch : msg) enc.addAll(this.encodeCh(ch)); for (String ch : msg)
enc.addAll(this.encodeCh(ch));
return enc; return enc;
} }
// Encode a character. // Encode a character.
ArrayList<Boolean> encodeCh(String ch) { ArrayList<Boolean> encodeCh(String ch) {
System.out.println("Encoding character " + ch);
System.out.flush();
// Throw error if character is not in charset. // Throw error if character is not in charset.
if (!this.charset.contains(ch)) if (!this.charset.contains(ch))
throw new IllegalArgumentException( throw new IllegalArgumentException("Cannot encode unknown character \"" +
"Cannot encode unknown character \"" + ch + "\"." ch + "\".");
);
return this.code.encode(ch); int i = this.charset.indexOf(ch);
return this.map.get(i);
} }
// Decode a message.
String decode(ArrayList<Boolean> enc) { return this.code.follow(enc); }
} }
// Binary tree. // Binary tree.
@@ -305,13 +341,13 @@ interface ITree {
int sumFreq(ITree t); int sumFreq(ITree t);
int sumFreq(int freq); int sumFreq(int freq);
// Return its question (the values on the right).
String getQuestion();
// Encode a character. // Encode a character.
ArrayList<Boolean> encode(String ch); ArrayList<Boolean> encode(String ch);
ArrayList<Boolean> ArrayList<Boolean> encode(String ch, ArrayList<Boolean> encodingSoFar);
encodeHelper(String ch, ArrayList<Boolean> encodingSoFar);
// Follow (decode) a path.
String follow(ArrayList<Boolean> path);
String followCh(ArrayList<Boolean> path);
} }
abstract class ATree implements ITree { abstract class ATree implements ITree {
@@ -331,42 +367,52 @@ class Node extends ATree {
// Left (false) and right (true) branches of tree. // Left (false) and right (true) branches of tree.
ITree l, r; ITree l, r;
// The 'question' the node asks to determine whether to go left or right. If Node(int freq, ITree l, ITree r) {
// the character is in the string, go right.
String question;
Node(int freq, String question, ITree l, ITree r) {
super(freq); super(freq);
this.question = question;
this.l = l; this.l = l;
this.r = r; this.r = r;
} }
public String getQuestion() { return this.question; }
public ArrayList<Boolean> encode(String ch) { public ArrayList<Boolean> encode(String ch) {
Boolean b = this.question.contains(ch); ArrayList<Boolean> ret = new ArrayList<>();
System.out.println( ret.addAll(this.l.encode(ch, new ArrayList<>(Arrays.asList(false))));
"I'm a node encoding " + ch + " and going " + (b ? "right" : "left") ret.addAll(this.r.encode(ch, new ArrayList<>(Arrays.asList(true))));
); return ret;
System.out.flush();
// If the character's in the question, go right; otherwise, go left.
return this.question.contains(ch)
? this.r.encodeHelper(ch, new ArrayList<>(Arrays.asList(true)))
: this.l.encodeHelper(ch, new ArrayList<>(Arrays.asList(false)));
} }
public ArrayList<Boolean> public ArrayList<Boolean> encode(String ch,
encodeHelper(String ch, ArrayList<Boolean> encodingSoFar) { ArrayList<Boolean> encodingSoFar) {
System.out.println("I'm a node encode helping " + ch); ArrayList<Boolean> ret = new ArrayList<>(),
System.out.flush(); left = new ArrayList<Boolean>(Arrays.asList(false)),
Boolean contains = this.question.contains(ch); right = new ArrayList<>(Arrays.asList(true));
ITree choice = contains ? this.r : this.l; left.addAll(0, encodingSoFar);
encodingSoFar.add(contains); right.addAll(0, encodingSoFar);
return choice.encodeHelper(ch, encodingSoFar); ret.addAll(this.l.encode(ch, left));
ret.addAll(this.r.encode(ch, right));
return ret;
}
public String follow(ArrayList<Boolean> path) {
String ret = "";
// Each loop is 1 character.
while (path.size() > 0) {
Boolean b = path.getFirst();
path.removeFirst();
ret += b ? this.r.followCh(path) : this.l.followCh(path);
}
return ret;
}
public String followCh(ArrayList<Boolean> path) {
if (path.size() <= 0)
throw new Error("Invalid path.");
Boolean b = path.getFirst();
path.removeFirst();
return b ? this.r.followCh(path) : this.l.followCh(path);
} }
} }
@@ -379,19 +425,24 @@ class Leaf extends ATree {
this.ch = ch; this.ch = ch;
} }
public String getQuestion() { return this.ch; }
public ArrayList<Boolean> encode(String ch) { public ArrayList<Boolean> encode(String ch) {
System.out.println("I'm a leaf encoding " + ch); throw new Error("Can't, fool.");
System.out.flush();
throw new Error("Cannot encode a Leaf.");
} }
public ArrayList<Boolean> public ArrayList<Boolean> encode(String ch,
encodeHelper(String ch, ArrayList<Boolean> encodingSoFar) { ArrayList<Boolean> encodingSoFar) {
System.out.println("I'm a leaf encode helping " + ch); if (ch.equals(this.ch))
System.out.flush();
return encodingSoFar; return encodingSoFar;
else
return new ArrayList<Boolean>();
}
public String follow(ArrayList<Boolean> path) {
throw new Error("Shouldn't call follow() on leaf.");
}
public String followCh(ArrayList<Boolean> path) { return this.ch; }
public String follow(ArrayList<Boolean> path, String soFar) {
return soFar.concat(this.ch);
} }
} }
@@ -400,7 +451,8 @@ class Util {
// Sort a list of trees from least to most frequent. // Sort a list of trees from least to most frequent.
static void sort(ArrayList<ITree> trees) { static void sort(ArrayList<ITree> trees) {
// Our work here is done. // Our work here is done.
if (trees.size() < 2) return; if (trees.size() < 2)
return;
for (int i = 1; i < trees.size(); i++) { for (int i = 1; i < trees.size(); i++) {
// Copy out the tree to insert. // Copy out the tree to insert.
@@ -419,7 +471,8 @@ class Util {
// Insert tree. // Insert tree.
insertIntoSorted(tree, sorted); insertIntoSorted(tree, sorted);
// Overwrite original trees with sorted ones. // 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++)))
;
// Insert the last tree back in. // Insert the last tree back in.
trees.add(i, sorted.getLast()); trees.add(i, sorted.getLast());
} }
@@ -437,8 +490,8 @@ class Util {
// Convert a string to a list. // Convert a string to a list.
static ArrayList<String> enlist(String s) { static ArrayList<String> enlist(String s) {
ArrayList<String> ret = new ArrayList<>(); ArrayList<String> ret = new ArrayList<>();
for (int i = 0; i < s.length(); i++) for (int i = 0; i < s.length(); ret.add(String.valueOf(s.charAt(i++))))
ret.add(String.valueOf(s.charAt(i))); ;
return ret; return ret;
} }
} }

View File

@@ -386,6 +386,7 @@ class Game extends World {
} }
public Game onKeyEvent(String key) { public Game onKeyEvent(String key) {
if (this.guessesLeft <= 0) throw new Error("Done.");
if ("123456789".contains(key)) { // User has entered a number. if ("123456789".contains(key)) { // User has entered a number.
int choice = Integer.valueOf(key); int choice = Integer.valueOf(key);
if (choice <= this.conf.options.len()) return this.addDot( if (choice <= this.conf.options.len()) return this.addDot(

View File

@@ -1,41 +0,0 @@
// Zed settings
//
// For information on how to configure Zed, see the Zed
// documentation: https://zed.dev/docs/configuring-zed
//
// To see all of Zed's default settings without changing your
// custom settings, run `zed: open default settings` from the
// command palette (cmd-shift-p / ctrl-shift-p)
{
// GENERAL SETTINGS
"outline_panel": {
"dock": "right"
},
"vim_mode": true,
// UI SETTINGS
"ui_font_size": 16,
"ui_font_family": "Iosevka Sans Ligless",
// BUFFER SETTINGS
"buffer_font_size": 16,
"buffer_font_family": "Iosevka Mono Ligless",
// BEHAVIOR SETTINGS
"languages": {
"Java": {
"formatter": {
"external": {
"command": "clang-format",
"arguments": ["--style=file", "--assume-filename={buffer_path}"]
}
},
"format_on_save": "on"
}
},
"wrap_guides": [80],
// MISC OTHER
"theme": {
"mode": "system",
"light": "Gruvbox Light",
"dark": "Gruvbox Dark"
}
}