aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpacien2018-11-30 16:38:17 +0100
committerpacien2018-11-30 16:38:17 +0100
commitb03508ea5e20370de26c6faf23bbbdd4e89ab1a9 (patch)
tree92539b9f20783f9c82dd5e74399b958a336e9971
parent4361a9e9c918bc4feff4d04e47dbe686b1d31a6c (diff)
downloadgziplike-b03508ea5e20370de26c6faf23bbbdd4e89ab1a9.tar.gz
isolate huffman tree construction
-rw-r--r--src/huffmantree.nim31
-rw-r--r--src/huffmantreebuilder.nim44
-rw-r--r--tests/thuffmantree.nim31
-rw-r--r--tests/thuffmantreebuilder.nim29
4 files changed, 78 insertions, 57 deletions
diff --git a/src/huffmantree.nim b/src/huffmantree.nim
index 44c9990..0266dfb 100644
--- a/src/huffmantree.nim
+++ b/src/huffmantree.nim
@@ -30,35 +30,21 @@ type HuffmanTreeNode*[T: SomeUnsignedInt] = ref object
30 maxChildValue: T 30 maxChildValue: T
31 of leaf: 31 of leaf:
32 value*: T 32 value*: T
33 weight: int
34 33
35proc huffmanBranch*[T](left, right: HuffmanTreeNode[T]): HuffmanTreeNode[T] = 34proc huffmanBranch*[T](left, right: HuffmanTreeNode[T]): HuffmanTreeNode[T] =
36 HuffmanTreeNode[T]( 35 HuffmanTreeNode[T](
37 kind: branch, left: left, right: right, 36 kind: branch, left: left, right: right,
38 maxChildValue: max(left.maxValue(), right.maxValue()), 37 maxChildValue: max(left.maxValue(), right.maxValue()))
39 weight: left.weight + right.weight)
40 38
41proc huffmanLeaf*[T](value: T, weight = 0): HuffmanTreeNode[T] = 39proc huffmanLeaf*[T](value: T): HuffmanTreeNode[T] =
42 HuffmanTreeNode[T](kind: leaf, value: value, weight: weight) 40 HuffmanTreeNode[T](kind: leaf, value: value)
43 41
44proc `==`*[T](a, b: HuffmanTreeNode[T]): bool = 42proc `==`*[T](a, b: HuffmanTreeNode[T]): bool =
45 if a.kind != b.kind or a.weight != b.weight: return false
46 case a.kind:
47 of branch: a.left == b.left and a.right == b.right
48 of leaf: a.value == b.value
49
50proc `~=`*[T](a, b: HuffmanTreeNode[T]): bool =
51 if a.kind != b.kind: return false 43 if a.kind != b.kind: return false
52 case a.kind: 44 case a.kind:
53 of branch: a.left ~= b.left and a.right ~= b.right 45 of branch: a.left == b.left and a.right == b.right
54 of leaf: a.value == b.value 46 of leaf: a.value == b.value
55 47
56proc `!~`*[T](a, b: HuffmanTreeNode[T]): bool =
57 not (a ~= b)
58
59proc `<`*[T](left, right: HuffmanTreeNode[T]): bool =
60 left.weight < right.weight
61
62proc maxValue*[T](node: HuffmanTreeNode[T]): T = 48proc maxValue*[T](node: HuffmanTreeNode[T]): T =
63 case node.kind: 49 case node.kind:
64 of branch: node.maxChildValue 50 of branch: node.maxChildValue
@@ -86,12 +72,3 @@ proc serialise*[T](tree: HuffmanTreeNode[T], bitWriter: BitWriter) =
86 bitWriter.writeBits(valueBitLength, node.value) 72 bitWriter.writeBits(valueBitLength, node.value)
87 bitWriter.writeBits(valueLengthFieldBitLength, valueBitLength.uint8) 73 bitWriter.writeBits(valueLengthFieldBitLength, valueBitLength.uint8)
88 writeNode(tree) 74 writeNode(tree)
89
90proc symbolQueue*[T](stats: CountTableRef[T]): HeapQueue[HuffmanTreeNode[T]] =
91 result = newHeapQueue[HuffmanTreeNode[T]]()
92 for item, count in stats.pairs: result.push(huffmanLeaf(item, count))
93
94proc buildHuffmanTree*[T: SomeUnsignedInt](stats: CountTableRef[T]): HuffmanTreeNode[T] =
95 var symbolQueue = symbolQueue(stats)
96 while symbolQueue.len > 1: symbolQueue.push(huffmanBranch(symbolQueue.pop(), symbolQueue.pop()))
97 symbolQueue[0]
diff --git a/src/huffmantreebuilder.nim b/src/huffmantreebuilder.nim
new file mode 100644
index 0000000..5b33d34
--- /dev/null
+++ b/src/huffmantreebuilder.nim
@@ -0,0 +1,44 @@
1# gzip-like LZSS compressor
2# Copyright (C) 2018 Pacien TRAN-GIRARD
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as
6# published by the Free Software Foundation, either version 3 of the
7# License, or (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17import tables, heapqueue
18import huffmantree, lzssencoder
19
20type WeighedHuffmanTreeNode[T] = ref object
21 weight: int
22 huffmanTreeNode: HuffmanTreeNode[T]
23
24proc weighedHuffmanBranch[T](left, right: WeighedHuffmanTreeNode[T]): WeighedHuffmanTreeNode[T] =
25 WeighedHuffmanTreeNode[T](
26 weight: left.weight + right.weight,
27 huffmanTreeNode: huffmanBranch(left.huffmanTreeNode, right.huffmanTreeNode))
28
29proc weighedHuffmanLeaf[T](value: T, weight: int): WeighedHuffmanTreeNode[T] =
30 WeighedHuffmanTreeNode[T](
31 weight: weight,
32 huffmanTreeNode: huffmanLeaf(value))
33
34proc `<`*[T](left, right: WeighedHuffmanTreeNode[T]): bool =
35 left.weight < right.weight
36
37proc symbolQueue[T](stats: CountTableRef[T]): HeapQueue[WeighedHuffmanTreeNode[T]] =
38 result = newHeapQueue[WeighedHuffmanTreeNode[T]]()
39 for item, count in stats.pairs: result.push(weighedHuffmanLeaf(item, count))
40
41proc buildHuffmanTree*[T: SomeUnsignedInt](stats: CountTableRef[T]): HuffmanTreeNode[T] =
42 var symbolQueue = symbolQueue(stats)
43 while symbolQueue.len > 1: symbolQueue.push(weighedHuffmanBranch(symbolQueue.pop(), symbolQueue.pop()))
44 symbolQueue[0].huffmanTreeNode
diff --git a/tests/thuffmantree.nim b/tests/thuffmantree.nim
index 705ac17..bc6a505 100644
--- a/tests/thuffmantree.nim
+++ b/tests/thuffmantree.nim
@@ -18,39 +18,20 @@ import unittest, streams, sequtils, tables, heapqueue
18import bitreader, bitwriter, huffmantree 18import bitreader, bitwriter, huffmantree
19 19
20suite "huffmantree": 20suite "huffmantree":
21 let stats = newCountTable(concat(repeat(1'u, 3), repeat(2'u, 1), repeat(3'u, 2)))
22 let tree = huffmanBranch( 21 let tree = huffmanBranch(
23 huffmanLeaf(1'u), 22 huffmanLeaf(1'u),
24 huffmanBranch( 23 huffmanBranch(
25 huffmanLeaf(2'u), 24 huffmanLeaf(2'u),
26 huffmanLeaf(3'u))) 25 huffmanLeaf(3'u)))
27 26
28 test "equivalence":
29 check huffmanLeaf(12'u) ~= huffmanLeaf(12'u)
30 check huffmanLeaf(12'u) ~= huffmanLeaf(12'u, 2)
31 check huffmanLeaf(12'u) !~ huffmanLeaf(21'u)
32 check huffmanLeaf(12'u) !~ huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(12'u))
33 check huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u)) ~= huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u))
34 check huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u)) !~ huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(1'u))
35 check huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 1)) ~= huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 2))
36 check huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 1)) !~ huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(12'u, 2))
37
38 test "equality": 27 test "equality":
39 check huffmanLeaf(12'u) == huffmanLeaf(12'u) 28 check huffmanLeaf(12'u) == huffmanLeaf(12'u)
40 check huffmanLeaf(12'u) != huffmanLeaf(21'u) 29 check huffmanLeaf(12'u) != huffmanLeaf(21'u)
41 check huffmanLeaf(12'u) != huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(12'u)) 30 check huffmanLeaf(12'u) != huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(12'u))
42 check huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u)) == huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u)) 31 check huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u)) == huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u))
43 check huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u)) != huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(1'u)) 32 check huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(21'u)) != huffmanBranch(huffmanLeaf(12'u), huffmanLeaf(1'u))
44 check huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 1)) == huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 1))
45 check huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 1)) != huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 2))
46 check tree == tree 33 check tree == tree
47 34
48 test "weight comparison":
49 check huffmanLeaf(12'u, 1) < huffmanLeaf(12'u, 2)
50 check huffmanLeaf(12'u, 2) > huffmanLeaf(12'u, 1)
51 check huffmanLeaf(12'u, 1) < huffmanLeaf(12'u, 1) == false
52 check huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 1)) < huffmanBranch(huffmanLeaf(12'u, 1), huffmanLeaf(21'u, 2))
53
54 test "maxValue": 35 test "maxValue":
55 check tree.maxValue() == 3 36 check tree.maxValue() == 3
56 37
@@ -71,7 +52,7 @@ suite "huffmantree":
71 52
72 stream.setPosition(0) 53 stream.setPosition(0)
73 let bitReader = stream.bitReader() 54 let bitReader = stream.bitReader()
74 check huffmantree.deserialise(bitReader, uint) ~= tree 55 check huffmantree.deserialise(bitReader, uint) == tree
75 56
76 test "serialise": 57 test "serialise":
77 let stream = newStringStream() 58 let stream = newStringStream()
@@ -91,13 +72,3 @@ suite "huffmantree":
91 check bitReader.readBits(2, uint8) == 2 72 check bitReader.readBits(2, uint8) == 2
92 check bitReader.readBool() == true # 3 leaf 73 check bitReader.readBool() == true # 3 leaf
93 check bitReader.readBits(2, uint8) == 3 74 check bitReader.readBits(2, uint8) == 3
94
95 test "symbolQueue":
96 var symbolQueue = symbolQueue(stats)
97 check symbolQueue.len == 3
98 check symbolQueue.pop() == huffmanLeaf(2'u, 1)
99 check symbolQueue.pop() == huffmanLeaf(3'u, 2)
100 check symbolQueue.pop() == huffmanLeaf(1'u, 3)
101
102 test "buildHuffmanTree":
103 check buildHuffmanTree(stats) ~= tree
diff --git a/tests/thuffmantreebuilder.nim b/tests/thuffmantreebuilder.nim
new file mode 100644
index 0000000..f782b37
--- /dev/null
+++ b/tests/thuffmantreebuilder.nim
@@ -0,0 +1,29 @@
1# gzip-like LZSS compressor
2# Copyright (C) 2018 Pacien TRAN-GIRARD
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as
6# published by the Free Software Foundation, either version 3 of the
7# License, or (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17import unittest, tables, sequtils
18import huffmantree, huffmantreebuilder
19
20suite "huffmantreebuilder":
21 let stats = newCountTable(concat(repeat(1'u, 3), repeat(2'u, 1), repeat(3'u, 2)))
22 let tree = huffmanBranch(
23 huffmanLeaf(1'u),
24 huffmanBranch(
25 huffmanLeaf(2'u),
26 huffmanLeaf(3'u)))
27
28 test "buildHuffmanTree":
29 check buildHuffmanTree(stats) == tree