This commit is contained in:
nik
2026-05-20 16:20:45 -07:00
commit 007710b5a8
91 changed files with 450401 additions and 0 deletions

100
p3/BitInputStream.java Normal file
View File

@@ -0,0 +1,100 @@
// The BitOutputStream and BitInputStream classes provide the ability to
// write and read individual bits to a file in a compact form. One major
// limitation of this approach is that the resulting file will always have
// a number of bits that is a multiple of 8. In effect, whatever bits are
// output to the file are padded at the end with 0's to make the total
// number of bits a multiple of 8.
//
// BitInputStream has the following public methods:
// public BitInputStream(String file)
// opens an input stream with the given file name
// public int nextBit()
// reads the next bit from input (throws exception if at end of file)
// public boolean hasNextBit()
// returns true if there's another bit in the input stream to be read
// public void close()
// closes the input
import java.io.*;
import java.util.*;
@SuppressWarnings("deprecation")
public class BitInputStream {
private FileInputStream input;
private int currentByte; // current set of bits (buffer)
private int nextByte; // next set of bits (buffer)
private int numBits; // how many bits from buffer have been used
private int remainingAtEnd; // how many bits will be remaining at the end
// after we're done
private static final int BYTE_SIZE = 8; // bits per byte
// pre : given file name is legal
// post: creates a BitInputStream reading input from the file
public BitInputStream(String file) {
try {
this.input = new FileInputStream(file);
// Read in the number of remaining bits at the end
this.remainingAtEnd = this.input.read();
// Set up the nextByte field.
this.nextByte = this.input.read();
} catch (IOException ex) {
throw new RuntimeException(ex.toString());
}
this.nextByte();
}
public boolean hasNextBit() {
boolean atEnd = this.currentByte == -1;
boolean onlyRemaining = this.nextByte == -1
&& BYTE_SIZE - this.numBits == this.remainingAtEnd;
return !atEnd && !onlyRemaining;
}
// post: reads next bit from input
// throws NoSuchElementException if there is no bit to return
public int nextBit() {
// if at eof, throw exception
if (!this.hasNextBit()) {
throw new NoSuchElementException();
}
int result = this.currentByte % 2;
this.currentByte /= 2;
this.numBits++;
if (this.numBits == BYTE_SIZE) {
this.nextByte();
}
return result;
}
// post: refreshes the internal buffer with the next BYTE_SIZE bits
private void nextByte() {
this.currentByte = this.nextByte;
if (this.currentByte != -1) {
try {
this.nextByte = this.input.read();
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
this.numBits = 0;
}
// post: input is closed
public void close() {
try {
this.input.close();
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
// included to ensure that the stream is closed
protected void finalize() {
this.close();
}
}

74
p3/BitOutputStream.java Normal file
View File

@@ -0,0 +1,74 @@
// The BitOutputStream and BitInputStream classes provide the ability to
// write and read individual bits to a file in a compact form. One major
// limitation of this approach is that the resulting file will always have
// a number of bits that is a multiple of 8. In effect, whatever bits are
// output to the file are padded at the end with 0's to make the total
// number of bits a multiple of 8.
import java.io.*;
import java.util.*;
@SuppressWarnings("deprecation")
public class BitOutputStream {
private PrintStream output;
private List<Integer> buffer;
private int currentByte; // a buffer used to build up next set of digits
private int numBits; // how many digits are currently in the buffer
private boolean debug; // set to true to write ASCII 0s and 1s rather than
// bits
private static final int BYTE_SIZE = 8; // digits per byte
// Creates a BitOutputStream sending output to the given stream. If debug
// is set to true, bits are printed as ASCII 0s and 1s.
public BitOutputStream(PrintStream output, boolean debug) {
this.buffer = new ArrayList<Integer>();
this.output = output;
this.debug = debug;
}
// Writes given bit to output
public void write(int bit) {
if (this.debug) {
System.out.print(bit);
}
if (bit < 0 || bit > 1) {
throw new IllegalArgumentException("Illegal bit: " + bit);
}
this.currentByte += bit << this.numBits;
this.numBits++;
if (this.numBits == BYTE_SIZE) {
this.buffer.add(this.currentByte);
this.numBits = 0;
this.currentByte = 0;
}
}
// post: output is closed
public void close() {
int remaining = BYTE_SIZE - this.numBits;
if (remaining == 8) {
remaining = 0;
}
/* Flush the last byte (if there is one) */
if (remaining > 0) {
this.buffer.add(this.currentByte);
}
/* Now that we've received all the output, prepend it with the number
* of missing bits from the end.
*/
this.output.write(remaining);
for (int b : this.buffer) {
this.output.write(b);
}
this.output.close();
}
// included to ensure that the stream is closed
protected void finalize() {
this.close();
}
}

117
p3/HuffmanCode.java Normal file
View File

@@ -0,0 +1,117 @@
import java.util.*;
import java.io.*;
public class HuffmanCode {
public HuffmanNode root;
// Constructor that builds the Huffman tree from an array of frequencies
public HuffmanCode(int[] frequencies) {
Queue<HuffmanNode> nodes = new PriorityQueue<>();
for (int i = 0; i < frequencies.length; i++) {
if (frequencies[i] > 0) {
nodes.add(new HuffmanNode((char) i, frequencies[i]));
}
}
while (nodes.size() > 1) {
HuffmanNode left = nodes.remove();
HuffmanNode right = nodes.remove();
HuffmanNode branch = new HuffmanNode('\0', left.freq + right.freq, left, right);
nodes.add(branch);
}
this.root = nodes.isEmpty() ? null : nodes.remove();
}
// Constructor that reads the Huffman tree from a Scanner
public HuffmanCode(Scanner input) {
this.root = new HuffmanNode('\0', 0); // Initialize root with a dummy node
while (input.hasNextLine()) {
int character = Integer.parseInt(input.nextLine());
String path = input.nextLine();
addNode(character, path);
}
}
// Adds a node to the Huffman tree based on the given path
private void addNode(int character, String path) {
HuffmanNode current = root;
for (int i = 0; i < path.length(); i++) {
char direction = path.charAt(i);
if (direction == '0') {
if (current.left == null) {
if (i == path.length() - 1) {
current.left = new HuffmanNode((char) character, 1); // Final node
} else {
current.left = new HuffmanNode('\0', 0); // Create a dummy node if it doesn't exist
}
}
current = current.left;
} else if (direction == '1') {
if (current.right == null) {
if (i == path.length() - 1) {
current.right = new HuffmanNode((char) character, 1); // Final node
} else {
current.right = new HuffmanNode('\0', 0); // Create a dummy node if it doesn't exist
}
}
current = current.right;
}
}
}
// Saves the Huffman tree to the given output stream in standard format
public void save(PrintStream output) {
save(output, this.root, "");
}
private void save(PrintStream output, HuffmanNode head, String path) {
if (head != null) {
if (head.data != '\0') {
output.println((int) head.data);
output.println(path);
}
save(output, head.left, path + "0");
save(output, head.right, path + "1");
}
}
// Translates bits from the input stream to characters and writes them to the output stream
public void translate(BitInputStream input, PrintStream output) {
HuffmanNode current = root;
while (input.hasNextBit()) {
int bit = input.nextBit();
if (bit == 0) {
current = current.left;
} else {
current = current.right;
}
if (current.left == null && current.right == null) { // Leaf node
output.print(current.data);
current = root; // Restart for next character
}
}
}
// Inner class representing a node in the Huffman tree
private static class HuffmanNode implements Comparable<HuffmanNode> {
public char data;
public int freq;
public HuffmanNode left;
public HuffmanNode right;
public HuffmanNode(char data, int frequency) {
this(data, frequency, null, null);
}
public HuffmanNode(char data, int frequency, HuffmanNode left, HuffmanNode right) {
this.data = data;
this.freq = frequency;
this.left = left;
this.right = right;
}
public int compareTo(HuffmanNode other) {
return Integer.compare(this.freq, other.freq);
}
}
}

80
p3/HuffmanCode.java.bak Normal file
View File

@@ -0,0 +1,80 @@
import java.util.*;
import java.io.*;
public class HuffmanCode {
public HuffmanNode root;
public HuffmanCode(int[] frequencies) {
Queue<HuffmanNode> nodes = new PriorityQueue<>();
for (int i=0;i<frequencies.length;i++) {
nodes.add(new HuffmanNode((char) i, frequencies[i]));
}
while (nodes.size()>1) {
HuffmanNode left = nodes.remove();
HuffmanNode right = nodes.remove();
HuffmanNode branch = new HuffmanNode(null, left.freq + right.freq, left, right);
nodes.add(branch);
}
this.root = nodes.remove();
}
public HuffmanCode(Scanner input) {
assembleTree(input, new HuffmanNode(null, null));
}
private assembleTree(Scanner input, HuffmanNode head) {
if (input.hasNextLine()) {
char character = (char) input.nextLine();
}
if (input.hasNextLine()) {
String loc = input.nextLine();
}
if (head == null) {
head = new HuffmanNode(null, null);
}
}
public void save(PrintStream output) {
save(output, this.root)
}
private void save(PrintStream output, HuffmanNode head) {
output.println((int) head.data);
output.println() ;
save(head.left);
save(head.right);
}
public void translate(BitInputStream input, PrintStream output) {
}
private static class HuffmanNode implements Comparable<HuffmanNode> {
public final char data;
public final int freq;
public HuffmanNode left;
public HuffmanNode right;
public HuffmanNode(char data, int frequency) {
this(data, frequency, null, null);
}
public HuffmanNode(char data, int frequency, HuffmanNode left, HuffmanNode right) {
this.data = data;
this.freq = freq;
this.left = left;
this.right = right;
}
public int compareTo(HuffmanNode other) {
if (this.freq == other.freq) {
return 0;
}
else if (this.freq < other.freq) {
return -1;
}
return 1;
}
}
}

209
p3/HuffmanCompressor.java Normal file
View File

@@ -0,0 +1,209 @@
import java.io.*;
import java.util.*;
public class HuffmanCompressor {
private String filename;
public static final int CHAR_MAX = 255; // max char value
public static final int MAKE_CODE = 1;
public static final int COMPRESS = 2;
public static final int DECOMPRESS = 3;
public static final int ROUND_TRIP = 4;
public HuffmanCompressor(String filename) {
if (!filename.endsWith(".txt")) {
throw new IllegalArgumentException(
"This compressor only works on text files!");
}
this.filename = filename.split(".txt")[0];
}
public void makeCode() throws IOException {
System.out.println("I am about to make the Huffman code for "
+ filename + ".txt...");
FileInputStream input = new FileInputStream(filename + ".txt");
int[] count = new int[CHAR_MAX];
int n = input.read();
while (n != -1) {
int prev = n;
count[n]++;
n = input.read();
// Remove added newline by ed
if (n == -1 && prev == 10) {
count[prev]--;
}
}
// Build the code; open the output file; save the code
System.out.println("\tI built up a frequency table of the " +
"characters in your file.");
System.out.println("\tNow, I'm going to call your HuffmanCode(int[]) " +
"constructor using that frequency table.");
HuffmanCode t = new HuffmanCode(count);
System.out.println("\tOkay! Now, I am going to save the code (using " +
"your save method) to the file " + filename +
".code!");
PrintStream output = new PrintStream(new File(filename + ".code"));
t.save(output);
System.out.println("...I am done making the Huffman Code!");
}
public void compress(boolean debug) throws IOException {
System.out.println("I am about to attempt to COMPRESS "
+ filename + ".txt:");
/* We must make the code before we can compress... */
this.makeCode();
System.out.println("\tNow that I have the Huffman Code, I am going " +
"to use the huffman code file created by");
System.out.println("\tyour save() method to compress the contents " +
"into " + filename + ".short!");
String[] codes = new String[CHAR_MAX];
Scanner codeInput = new Scanner(new File(filename + ".code"));
while (codeInput.hasNextLine()) {
int n = Integer.parseInt(codeInput.nextLine());
codes[n] = codeInput.nextLine();
}
// Open file to be compressed; open output file
FileInputStream input = new FileInputStream(this.filename + ".txt");
BitOutputStream output = new BitOutputStream(
new PrintStream(this.filename + ".short"), debug);
// Print out the debug binary to a .debug file
FileOutputStream debugOut = null;
if (debug) {
debugOut = new FileOutputStream(this.filename + ".debug");
}
// Do the compression
int n = input.read();
while (n != -1) {
if (codes[n] == null) {
// check if this is actually an error, or an ed added newline
if (!(n == 10 && input.read() == -1)) {
System.out.println("Your code file has no code for " + n +
" (the character '" + (char) n + "')");
System.out.println("exiting...");
System.exit(1);
}
n = -1;
} else {
for (int i = 0; i < codes[n].length(); i++) {
output.write(codes[n].charAt(i) - '0');
// Write to the .debug file
if (debugOut != null) {
debugOut.write(codes[n].charAt(i));
}
}
n = input.read();
}
}
input.close();
output.close();
System.out.println("...I am done compressing the file");
}
public void decompress(boolean printToConsole) throws IOException {
System.out.println("I am about to attempt to DECOMPRESS " +
filename + ".short:");
System.out.println("\tTo do this, I must first read in the huffman " +
"code used to compress the file.");
System.out.println("\tI will use your HuffmanCode(Scanner) " +
"constructor!");
// Open code file and construct tree
Scanner codeInput = new Scanner(new File(this.filename + ".code"));
HuffmanCode t = new HuffmanCode(codeInput);
// Open compressed file; open output
BitInputStream input = new BitInputStream(this.filename + ".short");
PrintStream output = System.out;
if (!printToConsole) {
System.out.println("\tNow, I will decompress the file using your " +
"translate() method and save");
System.out.println("\tthe output into " + this.filename + ".new");
output = new PrintStream(new File(this.filename + ".new"));
}
else {
System.out.println("\tNow, I will decompress the file using your " +
"translate() method and display it on the console");
}
// Decompress the file
t.translate(input, output);
output.close();
System.out.println("...I am done decompressing the file");
}
public static void main(String[] args) throws IOException {
Scanner console = new Scanner(System.in);
System.out.println("Welcome to the CSE 123 Huffman Compressor!");
System.out.println();
String filename;
do {
System.out.print("Which file would you like to work with (it " +
"must be a txt file)? ");
filename = console.nextLine().trim();
} while (!filename.endsWith(".txt"));
System.out.println();
int choice = -1;
while (choice == -1) {
System.out.println("Would you like to:");
System.out.println("\t(1) make a huffman code,");
System.out.println("\t(2) compress a file,");
System.out.println("\t(3) decompress a file, or");
System.out.println(
"\t(4) do a compression followed by a decompression");
System.out.print("1-4? ");
String choiceStr = console.nextLine().trim();
try {
choice = Integer.parseInt(choiceStr);
if (choice < 1 || choice > 4) {
choice = -1;
}
} catch(NumberFormatException e) {
/* Don't change choice; so, the loop will repeat. */
}
}
boolean debug = false;
if (choice == COMPRESS || choice == ROUND_TRIP) {
debug = prompt(
console,
"Would you like to debug the compressed file (y/n)? ");
}
boolean toConsole = false;
if (choice == DECOMPRESS || choice == ROUND_TRIP) {
toConsole = !prompt(
console,
"Would you like to print the result to a file (y/n)? ");
}
HuffmanCompressor huffman = new HuffmanCompressor(filename);
switch (choice) {
case 1: huffman.makeCode(); break;
case 2: huffman.compress(debug); break;
case 3: huffman.decompress(toConsole); break;
case 4:
huffman.compress(debug);
huffman.decompress(toConsole);
break;
}
}
public static boolean prompt(Scanner console, String message) {
System.out.print(message);
return console.nextLine().trim().toLowerCase().startsWith("y");
}
}

140
p3/hamlet.code Normal file
View File

@@ -0,0 +1,140 @@
91
0000000000
74
0000000001000
48
0000000001001000
52
0000000001001001
54
0000000001001010
34
00000000010010110
38
00000000010010111
49
00000000010011
58
000000000101
86
000000000110
85
000000000111
69
000000001
87
00000001
76
000000100
71
000000101
77
000000110
83
000000111
103
000001
117
00001
10
00010
107
0001100
66
000110100
89
0001101010
68
0001101011
45
000110110
82
0001101110
122
00011011110
41
000110111110
40
000110111111
118
0001110
39
0001111
97
0010
100
00110
99
001110
102
001111
111
0100
116
0101
119
011000
65
01100100
67
0110010100
120
0110010101
78
0110010110
75
0110010111
33
011001100
79
011001101
84
01100111
108
01101
44
011100
121
011101
46
011110
98
0111110
70
0111111000
106
01111110010
113
01111110011
59
011111101
73
01111111
32
10
101
1100
112
1101000
72
11010010
63
110100110
81
11010011100
93
11010011101
80
1101001111
109
110101
114
11011
105
11100
104
11101
110
11110
115
11111

BIN
p3/hamlet.short Normal file

Binary file not shown.

4463
p3/hamlet.txt Normal file

File diff suppressed because it is too large Load Diff

40
p3/short.code Normal file
View File

@@ -0,0 +1,40 @@
105
0000
99
0001
119
001000
10
0010010
109
0010011
102
00101
108
0011
110
0100
114
0101
97
0110
104
01110
112
01111
101
100
115
1010
116
1011
111
1100
100
11010
117
110110
121
110111
32
111

2
p3/short.txt Normal file
View File

@@ -0,0 +1,2 @@
this is a short input file composed entirely of spaces and lowercase letters
and end of line characters to help you test your code

82
p3/short256.code Normal file
View File

@@ -0,0 +1,82 @@
110
00000
109
0000100
237
00001010
119
00001011
249
000011
105
00010
227
00011
99
00100
108
00101
236
00110
238
00111
233
01000
97
01001
114
01010
242
01011
229
0110
101
0111
225
10000
104
100010
112
100011
240
100100
247
10010100
127
10010101
10
10010110
13
10010111
232
100110
138
1001110
117
1001111
115
10100
244
10101
100
101100
228
101101
116
10111
243
11000
239
11001
111
11010
121
1101100
230
1101101
102
1101110
245
1101111
32
111

BIN
p3/short256.short Normal file

Binary file not shown.

2
p3/short256.txt Normal file
View File

@@ -0,0 +1,2 @@
ôèéó éó á óèïòô éîðõô æéìå ãïíðïóåä åîôéòåìù ïæ óðáãåó áîä ìï÷åòãáóå ìåôôåòóŠáîä åîä ïæ ìéîå ãèáòáãôåòó ôï èåìð ùïõ ôåóô ùïõò ãïäåŠthis is a short input file composed entirely of spaces and lowercase letters
and end of line characters to help you test your code

View File

@@ -0,0 +1,8 @@
98
0
99
100
32
101
97
11

View File

@@ -0,0 +1 @@
aba ab cabbb

8
p3/taxi.code Normal file
View File

@@ -0,0 +1,8 @@
97
0
98
10
99
110
32
111

1
p3/taxi.short Normal file
View File

@@ -0,0 +1 @@
<06>>

1
p3/taxi.txt Normal file
View File

@@ -0,0 +1 @@
ab ab cab

6
p3/tiny.code Normal file
View File

@@ -0,0 +1,6 @@
97
0
99
10
98
11

BIN
p3/tiny.short Normal file

Binary file not shown.

1
p3/tiny.txt Normal file
View File

@@ -0,0 +1 @@
cbbaaa