diff --git a/src/main/java/cz/it4i/qcmp/cli/functions/DebugFunction.java b/src/main/java/cz/it4i/qcmp/cli/functions/DebugFunction.java index b2a325e6395ce7004d5d7274eea951870376fc73..ca4dcf7f96406227f58c3a41f7d924d0997be12c 100644 --- a/src/main/java/cz/it4i/qcmp/cli/functions/DebugFunction.java +++ b/src/main/java/cz/it4i/qcmp/cli/functions/DebugFunction.java @@ -4,8 +4,8 @@ import cz.it4i.qcmp.cache.QuantizationCacheManager; import cz.it4i.qcmp.cache.VQCacheFile; import cz.it4i.qcmp.cli.CompressionOptionsCLIParser; import cz.it4i.qcmp.cli.CustomFunctionBase; -import cz.it4i.qcmp.huffman.Huffman; import cz.it4i.qcmp.huffman.HuffmanNode; +import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InBitStream; import cz.it4i.qcmp.io.OutBitStream; import cz.it4i.qcmp.quantization.vector.VQCodebook; @@ -36,14 +36,14 @@ public class DebugFunction extends CustomFunctionBase { symbols[i] = i; } - final Huffman huffman = new Huffman(symbols, codebook.getVectorFrequencies()); + final HuffmanTreeBuilder huffman = new HuffmanTreeBuilder(symbols, codebook.getVectorFrequencies()); huffman.buildHuffmanTree(); final int bitsPerSymbol = (int) Utils.log2(codebook.getCodebookSize()); try (final OutBitStream bitStream = new OutBitStream(new FileOutputStream("D:\\tmp\\huffman_tree.data", false), bitsPerSymbol, 64)) { - huffman.saveHuffmanTree(bitStream); + huffman.getRoot().writeToBinaryStream(bitStream); } catch (final IOException e) { e.printStackTrace(); } @@ -51,7 +51,7 @@ public class DebugFunction extends CustomFunctionBase { HuffmanNode readRoot = null; try (final InBitStream inBitStream = new InBitStream(new FileInputStream("D:\\tmp\\huffman_tree.data"), bitsPerSymbol, 256)) { - readRoot = Huffman.readHuffmanTree(inBitStream); + readRoot = HuffmanNode.readFromStream(inBitStream); } catch (final IOException ex) { ex.printStackTrace(); } diff --git a/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java b/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java index 22b328d64310d8764bcc1b61f1cfa15909aecc02..82d9453091fcfd1024a1f3413ee5e6fd7811662f 100644 --- a/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java +++ b/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java @@ -3,7 +3,7 @@ package cz.it4i.qcmp.compression; import cz.it4i.qcmp.compression.exception.ImageCompressionException; import cz.it4i.qcmp.compression.listeners.IProgressListener; import cz.it4i.qcmp.compression.listeners.IStatusListener; -import cz.it4i.qcmp.huffman.Huffman; +import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InputData; import cz.it4i.qcmp.io.OutBitStream; @@ -100,8 +100,8 @@ public abstract class CompressorDecompressorBase { return symbols; } - protected Huffman createHuffmanCoder(final int[] symbols, final long[] frequencies) { - final Huffman huffman = new Huffman(symbols, frequencies); + protected HuffmanTreeBuilder createHuffmanCoder(final int[] symbols, final long[] frequencies) { + final HuffmanTreeBuilder huffman = new HuffmanTreeBuilder(symbols, frequencies); huffman.buildHuffmanTree(); return huffman; } @@ -157,7 +157,7 @@ public abstract class CompressorDecompressorBase { * @throws ImageCompressionException when fails to write to compress stream. */ protected long writeHuffmanEncodedIndices(final DataOutputStream compressStream, - final Huffman huffman, + final HuffmanTreeBuilder huffman, final int[] indices) throws ImageCompressionException { try (final OutBitStream outBitStream = new OutBitStream(compressStream, options.getBitsPerCodebookIndex(), 2048)) { for (final int index : indices) { diff --git a/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java b/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java index ec32d2c0127f9a9564b42ce2c9456c0aecd7ed2a..1e7c1ec7b2ac5a96f07c3280a28f109c8ec0f34e 100644 --- a/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java +++ b/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java @@ -5,7 +5,7 @@ import cz.it4i.qcmp.cache.ICacheFile; import cz.it4i.qcmp.cache.QuantizationCacheManager; import cz.it4i.qcmp.cache.SQCacheFile; import cz.it4i.qcmp.compression.exception.ImageCompressionException; -import cz.it4i.qcmp.huffman.Huffman; +import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InputData; import cz.it4i.qcmp.io.loader.IPlaneLoader; import cz.it4i.qcmp.io.loader.PlaneLoaderFactory; @@ -20,7 +20,7 @@ import java.io.IOException; public class SQImageCompressor extends CompressorDecompressorBase implements IImageCompressor { private ScalarQuantizer cachedQuantizer; - private Huffman cachedHuffman; + private HuffmanTreeBuilder cachedHuffman; public SQImageCompressor(final CompressionOptions options) { super(options); @@ -116,7 +116,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm } ScalarQuantizer quantizer = null; - Huffman huffman = null; + HuffmanTreeBuilder huffman = null; final int[] huffmanSymbols = createHuffmanSymbols(getCodebookSize()); if (options.getCodebookType() == CompressionOptions.CodebookType.Global) { reportStatusToListeners("Loading codebook from cache file."); @@ -174,7 +174,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm quantizer = trainScalarQuantizerFromData(planeData); writeCodebookToOutputStream(quantizer, compressStream); - huffman = new Huffman(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies()); + huffman = new HuffmanTreeBuilder(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies()); huffman.buildHuffmanTree(); } diff --git a/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java b/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java index 2cdfda862681de250c7cc022ef34def6c925a443..e72bfbb2baa95887536e68918ca3633bb681169c 100644 --- a/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java +++ b/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java @@ -4,8 +4,8 @@ import cz.it4i.qcmp.cache.ICacheFile; import cz.it4i.qcmp.cache.SQCacheFile; import cz.it4i.qcmp.compression.exception.ImageDecompressionException; import cz.it4i.qcmp.fileformat.QCMPFileHeader; -import cz.it4i.qcmp.huffman.Huffman; import cz.it4i.qcmp.huffman.HuffmanNode; +import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InBitStream; import cz.it4i.qcmp.quantization.scalar.SQCodebook; import cz.it4i.qcmp.utilities.Stopwatch; @@ -17,7 +17,7 @@ import java.io.IOException; public class SQImageDecompressor extends CompressorDecompressorBase implements IImageDecompressor { private SQCodebook cachedCodebook = null; - private Huffman cachedHuffman = null; + private HuffmanTreeBuilder cachedHuffman = null; public SQImageDecompressor(final CompressionOptions options) { super(options); @@ -52,7 +52,7 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I final int planePixelCount = header.getImageSizeX() * header.getImageSizeY(); SQCodebook codebook = null; - Huffman huffman = null; + HuffmanTreeBuilder huffman = null; if (!header.isCodebookPerPlane()) { // There is only one codebook. reportStatusToListeners("Loading single codebook and huffman coder."); @@ -130,7 +130,7 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I final int planePixelCount = header.getImageSizeX() * header.getImageSizeY(); SQCodebook codebook = null; - Huffman huffman = null; + HuffmanTreeBuilder huffman = null; if (!header.isCodebookPerPlane()) { // There is only one codebook. codebook = readScalarQuantizationValues(compressedStream, codebookSize); diff --git a/src/main/java/cz/it4i/qcmp/compression/VQImageCompressor.java b/src/main/java/cz/it4i/qcmp/compression/VQImageCompressor.java index c64163aa97dc4d7e230aaa267ada320ecec5f035..90d3cfd7d2f1ebe8f3822ee47034e34e175bbac1 100644 --- a/src/main/java/cz/it4i/qcmp/compression/VQImageCompressor.java +++ b/src/main/java/cz/it4i/qcmp/compression/VQImageCompressor.java @@ -6,7 +6,7 @@ import cz.it4i.qcmp.cache.VQCacheFile; import cz.it4i.qcmp.compression.exception.ImageCompressionException; import cz.it4i.qcmp.data.Range; import cz.it4i.qcmp.fileformat.QuantizationType; -import cz.it4i.qcmp.huffman.Huffman; +import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InputData; import cz.it4i.qcmp.io.loader.IPlaneLoader; import cz.it4i.qcmp.io.loader.PlaneLoaderFactory; @@ -24,7 +24,7 @@ import java.io.IOException; public class VQImageCompressor extends CompressorDecompressorBase implements IImageCompressor { private VectorQuantizer cachedQuantizer = null; - private Huffman cachedHuffman = null; + private HuffmanTreeBuilder cachedHuffman = null; private boolean useKdTree = false; @@ -158,7 +158,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm } VectorQuantizer quantizer = cachedQuantizer; - Huffman huffman = cachedHuffman; + HuffmanTreeBuilder huffman = cachedHuffman; assert (!streamMode || ((quantizer != null) && (huffman != null))); if (!streamMode) { @@ -283,7 +283,8 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm final long[] voxelLayersSizes = new long[voxelLayerCount]; final VectorQuantizer quantizer = (cachedQuantizer != null) ? cachedQuantizer : loadQuantizerFromCache(); - final Huffman huffman = (cachedHuffman != null) ? cachedHuffman : createHuffmanCoder(huffmanSymbols, quantizer.getFrequencies()); + final HuffmanTreeBuilder huffman = (cachedHuffman != null) ? cachedHuffman : createHuffmanCoder(huffmanSymbols, + quantizer.getFrequencies()); if (!streamMode) writeQuantizerToCompressStream(quantizer, compressStream); diff --git a/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java b/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java index 6be7248fdd550eee8f00f3626195c6149ec389e1..1da6828e3e2883ba57f761b676ea365929a005f4 100644 --- a/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java +++ b/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java @@ -6,8 +6,8 @@ import cz.it4i.qcmp.compression.exception.ImageDecompressionException; import cz.it4i.qcmp.data.*; import cz.it4i.qcmp.fileformat.QCMPFileHeader; import cz.it4i.qcmp.fileformat.QuantizationType; -import cz.it4i.qcmp.huffman.Huffman; import cz.it4i.qcmp.huffman.HuffmanNode; +import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InBitStream; import cz.it4i.qcmp.quantization.vector.VQCodebook; import cz.it4i.qcmp.utilities.Stopwatch; @@ -20,7 +20,7 @@ import java.io.IOException; public class VQImageDecompressor extends CompressorDecompressorBase implements IImageDecompressor { private VQCodebook cachedCodebook = null; - private Huffman cachedHuffman = null; + private HuffmanTreeBuilder cachedHuffman = null; private interface DecompressCallback { void process(final Block imageBlock, final int planeIndex) throws ImageDecompressionException; @@ -90,7 +90,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I } return reconstructedChunk; } - + @Override public void decompress(final DataInputStream compressedStream, final DataOutputStream decompressStream, @@ -122,7 +122,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I VQCodebook codebook = null; - Huffman huffman = null; + HuffmanTreeBuilder huffman = null; if (!header.isCodebookPerPlane()) { // There is only one codebook. codebook = readCodebook(compressedStream, codebookSize, vectorSize); @@ -245,7 +245,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I final VQCodebook codebook = readCodebook(compressedStream, codebookSize, vectorSize); - final Huffman huffman = createHuffmanCoder(huffmanSymbols, codebook.getVectorFrequencies()); + final HuffmanTreeBuilder huffman = createHuffmanCoder(huffmanSymbols, codebook.getVectorFrequencies()); final int voxelLayerCount = VQImageCompressor.calculateVoxelLayerCount(header.getImageSizeZ(), header.getVectorSizeZ()); final Stopwatch stopwatch = new Stopwatch(); @@ -379,7 +379,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I }); } - private int decodeHuffmanSymbol(final Huffman huffman, final InBitStream inBitStream) throws IOException { + private int decodeHuffmanSymbol(final HuffmanTreeBuilder huffman, final InBitStream inBitStream) throws IOException { HuffmanNode currentHuffmanNode = huffman.getRoot(); while (!currentHuffmanNode.isLeaf()) { currentHuffmanNode = currentHuffmanNode.traverse(inBitStream.readBit()); diff --git a/src/main/java/cz/it4i/qcmp/huffman/HuffmanNode.java b/src/main/java/cz/it4i/qcmp/huffman/HuffmanNode.java index f81e5d3a7c944eb06e34f96f92559c91b14fdede..4d453675f2dc69c591954f38f5ed8acd79c395e6 100644 --- a/src/main/java/cz/it4i/qcmp/huffman/HuffmanNode.java +++ b/src/main/java/cz/it4i/qcmp/huffman/HuffmanNode.java @@ -1,36 +1,38 @@ package cz.it4i.qcmp.huffman; +import cz.it4i.qcmp.io.InBitStream; +import cz.it4i.qcmp.io.OutBitStream; import org.jetbrains.annotations.NotNull; +import java.io.IOException; + public class HuffmanNode implements Comparable<HuffmanNode> { private int symbol = -1; - private long symbolFrequency = -1; private int bit = -1; private boolean leaf = false; private double probability = 0.0; - final HuffmanNode subNodeA; - final HuffmanNode subNodeB; + final HuffmanNode rightChild; + final HuffmanNode leftChild; - public HuffmanNode(final int symbol, final double probability, final long frequency) { + public HuffmanNode(final int symbol, final double probability) { this.symbol = symbol; this.probability = probability; - this.symbolFrequency = frequency; - subNodeA = null; - subNodeB = null; + rightChild = null; + leftChild = null; this.leaf = true; } - private HuffmanNode(final HuffmanNode parentA, final HuffmanNode parentB) { - subNodeA = parentA; - subNodeB = parentB; + private HuffmanNode(final HuffmanNode rightChild, final HuffmanNode leftChild) { + this.rightChild = rightChild; + this.leftChild = leftChild; } - public static HuffmanNode constructWithSymbol(final HuffmanNode parentA, final HuffmanNode parentB, final int symbol) { - final HuffmanNode node = new HuffmanNode(parentA, parentB); + public static HuffmanNode constructWithSymbol(final HuffmanNode rightChild, final HuffmanNode leftChild, final int symbol) { + final HuffmanNode node = new HuffmanNode(rightChild, leftChild); node.symbol = symbol; - node.leaf = (parentA == null && parentB == null); + node.leaf = (rightChild == null && leftChild == null); return node; } @@ -41,10 +43,10 @@ public class HuffmanNode implements Comparable<HuffmanNode> { } public HuffmanNode traverse(final boolean queryBit) { - if (subNodeA != null && subNodeA.bit == (queryBit ? 1 : 0)) - return subNodeA; - if (subNodeB != null && subNodeB.bit == (queryBit ? 1 : 0)) - return subNodeB; + if (rightChild != null && rightChild.bit == (queryBit ? 1 : 0)) + return rightChild; + if (leftChild != null && leftChild.bit == (queryBit ? 1 : 0)) + return leftChild; assert (false) : "Corrupted huffman tree"; return null; @@ -63,10 +65,6 @@ public class HuffmanNode implements Comparable<HuffmanNode> { return symbol; } - public long getSymbolFrequency() { - return symbolFrequency; - } - public int getBit() { return bit; } @@ -79,39 +77,104 @@ public class HuffmanNode implements Comparable<HuffmanNode> { return probability; } - public HuffmanNode getSubNodeA() { - return subNodeA; + public HuffmanNode getRightChild() { + return rightChild; } - public HuffmanNode getSubNodeB() { - return subNodeB; + public HuffmanNode getLeftChild() { + return leftChild; } - private static boolean treeNodeEquality(final HuffmanNode A, final HuffmanNode B) { - if (A.leaf) { - if (!B.leaf) { + /** + * Check if two huffman nodes are value equal and if their subtrees are also equal. + * + * @param nodeA First huffman node. + * @param nodeB Second huffman code. + * @return True if nodes and their subtrees are equal. + */ + private static boolean treeNodeEquality(final HuffmanNode nodeA, final HuffmanNode nodeB) { + if (nodeA.leaf) { + if (!nodeB.leaf) { return false; } - return A.symbol == B.symbol; + return nodeA.symbol == nodeB.symbol; } else { - if (B.leaf) { + if (nodeB.leaf) { return false; } - if (A.bit != B.bit) + if (nodeA.bit != nodeB.bit) return false; - if ((A.subNodeA != null && B.subNodeA == null) || (A.subNodeA == null && B.subNodeA != null)) + if ((nodeA.rightChild != null && nodeB.rightChild == null) || (nodeA.rightChild == null && nodeB.rightChild != null)) return false; - if ((A.subNodeB != null && B.subNodeB == null) || (A.subNodeB == null && B.subNodeB != null)) + if ((nodeA.leftChild != null && nodeB.leftChild == null) || (nodeA.leftChild == null && nodeB.leftChild != null)) return false; - final boolean subTreeAResult = treeNodeEquality(A.subNodeA, B.subNodeA); - final boolean subTreeBResult = treeNodeEquality(A.subNodeB, B.subNodeB); + + assert (nodeA.rightChild != null) : "Current node is not leaf and right child must be set."; + assert (nodeA.leftChild != null) : "Current node is not leaf and left child must be set."; + + final boolean subTreeAResult = treeNodeEquality(nodeA.rightChild, nodeB.rightChild); + final boolean subTreeBResult = treeNodeEquality(nodeA.leftChild, nodeB.leftChild); return (subTreeAResult && subTreeBResult); } } - public boolean treeEqual(final HuffmanNode opposite) { - return treeNodeEquality(this, opposite); + /** + * Check if tree starting from this node is equal to one starting from otherRoot. + * + * @param otherRoot Other tree root node. + * @return True if both trees are value equal. + */ + public boolean treeEqual(final HuffmanNode otherRoot) { + return treeNodeEquality(this, otherRoot); + } + + /** + * Save current node and its children to binary stream. + * + * @param node Node to write to stream. + * @param bitStream Binary output stream. + * @throws IOException when fails to write to stream. + */ + private void writeToBinaryStreamImpl(final HuffmanNode node, final OutBitStream bitStream) throws IOException { + if (node.isLeaf()) { + bitStream.writeBit(1); + bitStream.write(node.getSymbol()); + } else { + bitStream.writeBit(0); + writeToBinaryStreamImpl(node.getRightChild(), bitStream); + writeToBinaryStreamImpl(node.getLeftChild(), bitStream); + } + } + + /** + * Save huffman tree from this node to the binary stream. + * + * @param bitStream Binary output stream. + * @throws IOException when fails to write to stream. + */ + public void writeToBinaryStream(final OutBitStream bitStream) throws IOException { + writeToBinaryStreamImpl(this, bitStream); + } + + /** + * Read huffman tree from the binary stream. + * + * @param bitStream Binary input stream. + * @return Root of the huffman tree. + * @throws IOException when fails to read from stream. + */ + public static HuffmanNode readFromStream(final InBitStream bitStream) throws IOException { + if (bitStream.readBit()) // Leaf + { + return HuffmanNode.constructWithSymbol(null, null, bitStream.readValue()); + } else { + final HuffmanNode rightChild = readFromStream(bitStream); + rightChild.setBit(1); + final HuffmanNode leftChild = readFromStream(bitStream); + leftChild.setBit(0); + return HuffmanNode.constructWithSymbol(rightChild, leftChild, -1); + } } } \ No newline at end of file diff --git a/src/main/java/cz/it4i/qcmp/huffman/Huffman.java b/src/main/java/cz/it4i/qcmp/huffman/HuffmanTreeBuilder.java similarity index 67% rename from src/main/java/cz/it4i/qcmp/huffman/Huffman.java rename to src/main/java/cz/it4i/qcmp/huffman/HuffmanTreeBuilder.java index 4601e9725e484484ed5c460217f5938f1b309a7d..5daf0ae9782064ac07789aff49751d841f318535 100644 --- a/src/main/java/cz/it4i/qcmp/huffman/Huffman.java +++ b/src/main/java/cz/it4i/qcmp/huffman/HuffmanTreeBuilder.java @@ -1,19 +1,15 @@ package cz.it4i.qcmp.huffman; -import cz.it4i.qcmp.io.InBitStream; -import cz.it4i.qcmp.io.OutBitStream; - -import java.io.IOException; import java.util.*; -public class Huffman { +public class HuffmanTreeBuilder { private HuffmanNode root = null; private HashMap<Integer, boolean[]> symbolCodes; private HashMap<Integer, Double> symbolProbabilityMap; private final int[] symbols; private final long[] symbolFrequencies; - public Huffman(final int[] symbols, final long[] symbolFrequencies) { + public HuffmanTreeBuilder(final int[] symbols, final long[] symbolFrequencies) { assert (symbols.length == symbolFrequencies.length) : "Array lengths mismatch"; this.symbols = symbols; this.symbolFrequencies = symbolFrequencies; @@ -57,14 +53,14 @@ public class Huffman { currentCode.add(bit == 1); } - if (currentNode.subNodeA != null) { + if (currentNode.rightChild != null) { final ArrayList<Boolean> codeCopy = new ArrayList<Boolean>(currentCode); - traverseSymbolCodes(currentNode.subNodeA, codeCopy); + traverseSymbolCodes(currentNode.rightChild, codeCopy); inLeaf = false; } - if (currentNode.subNodeB != null) { + if (currentNode.leftChild != null) { final ArrayList<Boolean> codeCopy = new ArrayList<Boolean>(currentCode); - traverseSymbolCodes(currentNode.subNodeB, codeCopy); + traverseSymbolCodes(currentNode.leftChild, codeCopy); inLeaf = false; } @@ -93,7 +89,7 @@ public class Huffman { for (int sIndex = 0; sIndex < symbols.length; sIndex++) { final double symbolProbability = (double) symbolFrequencies[sIndex] / totalFrequency; symbolProbabilityMap.put(symbols[sIndex], symbolProbability); - queue.add(new HuffmanNode(symbols[sIndex], symbolProbability, symbolFrequencies[sIndex])); + queue.add(new HuffmanNode(symbols[sIndex], symbolProbability)); } return queue; @@ -115,12 +111,7 @@ public class Huffman { private HashMap<Integer, Double> createSortedHashMap(final HashMap<Integer, Double> map) { final List<Map.Entry<Integer, Double>> list = new LinkedList<Map.Entry<Integer, Double>>(map.entrySet()); //Custom Comparator - list.sort(new Comparator<Map.Entry<Integer, Double>>() { - @Override - public int compare(final Map.Entry<Integer, Double> t0, final Map.Entry<Integer, Double> t1) { - return -(t0.getValue().compareTo(t1.getValue())); - } - }); + list.sort((t0, t1) -> (-(t0.getValue().compareTo(t1.getValue())))); //copying the sorted list in HashMap to preserve the iteration order final HashMap<Integer, Double> sortedHashMap = new LinkedHashMap<Integer, Double>(); for (final Map.Entry<Integer, Double> entry : list) { @@ -128,38 +119,4 @@ public class Huffman { } return sortedHashMap; } - - private void encodeHuffmanNode(final HuffmanNode node, final OutBitStream bitStream) throws IOException { - if (node.isLeaf()) { - bitStream.writeBit(1); - bitStream.write(node.getSymbol()); - } else { - bitStream.writeBit(0); - encodeHuffmanNode(node.getSubNodeA(), bitStream); - encodeHuffmanNode(node.getSubNodeB(), bitStream); - } - } - - private static HuffmanNode decodeHuffmanNode(final InBitStream bitStream) throws IOException { - if (bitStream.readBit()) // Leaf - { - return HuffmanNode.constructWithSymbol(null, null, bitStream.readValue()); - } else { - final HuffmanNode nodeA = decodeHuffmanNode(bitStream); - nodeA.setBit(1); - final HuffmanNode nodeB = decodeHuffmanNode(bitStream); - nodeB.setBit(0); - return HuffmanNode.constructWithSymbol(nodeA, nodeB, -1); - } - } - - - public void saveHuffmanTree(final OutBitStream bitStream) throws IOException { - assert root != null : "The tree is not build."; - encodeHuffmanNode(root, bitStream); - } - - public static HuffmanNode readHuffmanTree(final InBitStream bitStream) throws IOException { - return decodeHuffmanNode(bitStream); - } }