From a18e7cc9eb3e760d045722699d03b965f6c6cae9 Mon Sep 17 00:00:00 2001 From: Amaras Date: Thu, 6 Aug 2020 00:18:44 +0200 Subject: [PATCH 01/32] Adding flood fill in Coconut --- .../flood_fill/code/coconut/flood_fill.coco | 126 ++++++++++++++++++ contents/flood_fill/flood_fill.md | 10 ++ 2 files changed, 136 insertions(+) create mode 100644 contents/flood_fill/code/coconut/flood_fill.coco diff --git a/contents/flood_fill/code/coconut/flood_fill.coco b/contents/flood_fill/code/coconut/flood_fill.coco new file mode 100644 index 000000000..e95248a31 --- /dev/null +++ b/contents/flood_fill/code/coconut/flood_fill.coco @@ -0,0 +1,126 @@ +from collections import deque +import numpy as np + + +data Point(x, y): + def __add__(self, other is Point) = Point(self.x + other.x, self.y + other.y) + + +# This function is necessary, because negative indices wrap around the +# array in Coconut. +def inbounds(canvas_shape, location is Point) = + min(location) >= 0 and location.x < canvas_shape[0] and location.y < canvas_shape[1] + + +def colour(canvas, location is Point, old_value, new_value): + if not inbounds(canvas.shape, location): + return + + if canvas[location] != old_value: + return + else: + canvas[location] = new_value + + +def find_neighbours(canvas, location is Point, old_value, new_value): + possible_neighbours = (Point(0, 1), Point(1, 0), Point(0, -1), Point(-1, 0)) |> map$(location.__add__) + + for neighbour in possible_neighbours: + if inbounds(canvas.shape, neighbour) and canvas[neighbour] == old_value: + yield neighbour + + +def stack_fill(canvas, location is Point, old_value, new_value): + if new_value == old_value: + return + + stack = [location] + + while stack: + current_location = stack.pop() + colour(canvas, current_location, old_value, new_value) + for neighbour in find_neighbours(canvas, current_location, old_value, + new_value): + stack.append(neighbour) + + +def queue_fill(canvas, location is Point, old_value, new_value): + if new_value == old_value: + return + + queue = deque() + queue.append(location) + + colour(canvas, location, old_value, new_value) + + while queue: + current_location = queue.popleft() + for neighbour in find_neighbours(canvas, current_location, old_value, + new_value): + queue.append(neighbour) + colour(canvas, neighbour, old_value, new_value) + + +def recursive_fill(canvas, location is Point, old_value, new_value): + if new_value == old_value: + return + colour(canvas, location, old_value, new_value) + + for neighbour in find_neighbours(canvas, location, old_value, new_value): + recursive_fill(canvas, neighbour, old_value, new_value) + + +if __name__ == '__main__': + # Testing setup + from collections import namedtuple + + TestResults = namedtuple('TestResults', 'passes failures') + pass_count = failure_count = 0 + + grid = np.zeros((5, 5)) + grid[2,:] = 1 + solution_grid = np.zeros((5, 5)) + solution_grid[:3,] = 1 + + starting_location = Point(0, 0) + + + # The following is manual unit testing of the function + recursive_fill(grid, starting_location, 0, 1) + try: + assert (grid == solution_grid).all() + except AssertionError: + print('F', end='') + failure_count += 1 + else: + print('.', end='') + pass_count += 1 + + # Resetting the grid, if everything went well. + grid[:2,] = 0 + + stack_fill(grid, starting_location, 0, 1) + try: + assert (grid == solution_grid).all() + except AssertionError: + print('F', end='') + failure_count += 1 + else: + print('.', end='') + pass_count += 1 + + grid[:2,] = 0 + + queue_fill(grid, starting_location, 0, 1) + try: + assert (grid == solution_grid).all() + except AssertionError: + print('F', end='') + failure_count += 1 + else: + print('.', end='') + pass_count += 1 + + print('') + print(TestResults(pass_count, failure_count)) + diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index e53707a3c..88e3aa6ba 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -88,6 +88,8 @@ In code, this might look like this: {% method %} {% sample lang="jl" %} [import:37-55, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="coco" %} +[import:25-30, lang:"coconut"](code/coconut/flood_fill.coco) {% endmethod %} @@ -106,6 +108,8 @@ In code, it might look like this: All Julia code snippets for this chapter rely on an exterior `color!(...)` function, defined as [import:23-35, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="coco" %} +[import:64-70, lang:"coconut"](code/coconut/flood_fill.jl) {% endmethod %} The above code continues recursing through available neighbors as long as neighbors exist, and this should work so long as we are adding the correct set of neighbors. @@ -115,6 +119,8 @@ Additionally, it is possible to do the same type of traversal by managing a stac {% method %} {% sample lang="jl" %} [import:57-77, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="coco" %} +[import:33-44, lang:"coconut"](code/coconut/flood_fill.jl) {% endmethod %} This is ultimately the same method of traversal as before; however, because we are managing our own data structure, there are a few distinct differences: @@ -152,6 +158,8 @@ The code would look something like this: {% method %} {% sample lang="jl" %} [import:80-104, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="coco" %} +[import:47-61, lang:"coconut"](code/coconut/flood_fill.jl) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally. @@ -228,6 +236,8 @@ After, we will fill in the left-hand side of the array to be all ones by choosin {% method %} {% sample lang="jl" %} [import, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="coco" %} +[import, lang:"coconut"](code/coconut/flood_fill.jl) {% endmethod %} From 7d2739bdcc87c6835852079224906c144b0818e9 Mon Sep 17 00:00:00 2001 From: Amaras Date: Thu, 6 Aug 2020 00:27:04 +0200 Subject: [PATCH 02/32] Correcting copy-paste fails --- contents/flood_fill/flood_fill.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index 88e3aa6ba..4e5eae437 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -109,7 +109,7 @@ All Julia code snippets for this chapter rely on an exterior `color!(...)` funct [import:23-35, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="coco" %} -[import:64-70, lang:"coconut"](code/coconut/flood_fill.jl) +[import:64-70, lang:"coconut"](code/coconut/flood_fill.coco) {% endmethod %} The above code continues recursing through available neighbors as long as neighbors exist, and this should work so long as we are adding the correct set of neighbors. @@ -120,7 +120,7 @@ Additionally, it is possible to do the same type of traversal by managing a stac {% sample lang="jl" %} [import:57-77, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="coco" %} -[import:33-44, lang:"coconut"](code/coconut/flood_fill.jl) +[import:33-44, lang:"coconut"](code/coconut/flood_fill.coco) {% endmethod %} This is ultimately the same method of traversal as before; however, because we are managing our own data structure, there are a few distinct differences: @@ -159,7 +159,7 @@ The code would look something like this: {% sample lang="jl" %} [import:80-104, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="coco" %} -[import:47-61, lang:"coconut"](code/coconut/flood_fill.jl) +[import:47-61, lang:"coconut"](code/coconut/flood_fill.coco) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally. @@ -237,7 +237,7 @@ After, we will fill in the left-hand side of the array to be all ones by choosin {% sample lang="jl" %} [import, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="coco" %} -[import, lang:"coconut"](code/coconut/flood_fill.jl) +[import, lang:"coconut"](code/coconut/flood_fill.coco) {% endmethod %} From 5da3ae8cac46c08dd0b0375a09d824677ff2bf9b Mon Sep 17 00:00:00 2001 From: The Gathros <6323830+Gathros@users.noreply.github.com> Date: Thu, 6 Aug 2020 22:09:12 +0000 Subject: [PATCH 03/32] Flood Fill in C (#741) * Adding flood_fill.c * Updating flood_fill.md * adding C's queue_fill to flood_fill.md --- contents/flood_fill/code/c/flood_fill.c | 259 ++++++++++++++++++++++++ contents/flood_fill/flood_fill.md | 17 +- 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 contents/flood_fill/code/c/flood_fill.c diff --git a/contents/flood_fill/code/c/flood_fill.c b/contents/flood_fill/code/c/flood_fill.c new file mode 100644 index 000000000..ea7cc851c --- /dev/null +++ b/contents/flood_fill/code/c/flood_fill.c @@ -0,0 +1,259 @@ +#include +#include +#include + +struct canvas { + int max_x, max_y; + int *data; +}; + +struct point { + int x, y; +}; + +struct stack { + size_t top, capacity; + struct point *data; +}; + +struct queue { + size_t front, back, capacity; + struct point *data; +}; + +int inbounds(struct point p, struct canvas c) { + return (p.x < 0 || p.y < 0 || p.y >= c.max_y || p.x >= c.max_x) ? 0 : 1; +} + +void color(struct canvas c, struct point p, int old_val, int new_val) { + if (inbounds(p, c) && c.data[p.x + c.max_x * p.y] == old_val) { + c.data[p.x + c.max_x * p.y] = new_val; + } +} + +int find_neighbors(struct canvas c, struct point p, int old_val, int new_val, + struct point *neighbors) { + int cnt = 0; + struct point points[4] = { + {p.x, p.y + 1}, + {p.x + 1, p.y}, + {p.x, p.y - 1}, + {p.x - 1, p.y} + }; + + for (int i = 0; i < 4; ++i) { + if (inbounds(points[i], c) && + c.data[points[i].x + c.max_x * points[i].y] == old_val) { + neighbors[cnt++] = points[i]; + } + } + + return cnt; +} + +struct stack get_stack() { + struct stack stk; + + stk.data = malloc(4 * sizeof(struct point)); + stk.capacity = 4; + stk.top = 0; + + return stk; +} + +int stack_empty(struct stack stk) { + return stk.top == 0; +} + +void stack_push(struct stack *stk, struct point element) { + if (stk->top == stk->capacity) { + stk->capacity *= 2; + stk->data = realloc(stk->data, stk->capacity * sizeof(stk->data[0])); + } + + stk->data[stk->top++] = element; +} + +struct point stack_pop(struct stack *stk) { + return stk->data[--stk->top]; +} + +void free_stack(struct stack stk) { + free(stk.data); +} + +void stack_fill(struct canvas c, struct point p, int old_val, int new_val) { + if (old_val == new_val) { + return; + } + + struct stack stk = get_stack(); + stack_push(&stk, p); + + while (!stack_empty(stk)) { + struct point cur_loc = stack_pop(&stk); + if (c.data[cur_loc.x + c.max_x * cur_loc.y] == old_val) { + color(c, cur_loc, old_val, new_val); + + struct point neighbors[4]; + int cnt = find_neighbors(c, cur_loc, old_val, new_val, neighbors); + + for (int i = 0; i < cnt; ++i) { + stack_push(&stk, neighbors[i]); + } + } + } + + free_stack(stk); +} + +struct queue get_queue() { + struct queue q; + + q.data = calloc(4, sizeof(struct point)); + q.front = 0; + q.back = 0; + q.capacity = 4; + + return q; +} + +int queue_empty(struct queue q) { + return q.front == q.back; +} + +void enqueue(struct queue *q, struct point element) { + if (q->front == (q->back + 1) % q->capacity) { + size_t size = sizeof(q->data[0]); + struct point *tmp = calloc((q->capacity * 2), size); + memcpy(tmp, q->data + q->front, (q->capacity - q->front) * size); + memcpy(tmp + q->capacity - q->front, q->data, (q->front - 1) * size); + + free(q->data); + + q->data = tmp; + q->back = q->capacity - 1; + q->front = 0; + q->capacity *= 2; + } + + q->data[q->back] = element; + q->back = (q->back + 1) % q->capacity; +} + +struct point dequeue(struct queue *q) { + struct point ret = q->data[q->front]; + q->front = (q->front + 1) % q->capacity; + + return ret; +} + +void free_queue(struct queue q) { + free(q.data); +} + +void queue_fill(struct canvas c, struct point p, int old_val, int new_val) { + if (old_val == new_val) { + return; + } + + struct queue q = get_queue(sizeof(struct point *)); + enqueue(&q, p); + + while (!queue_empty(q)) { + struct point cur_loc = dequeue(&q); + if (c.data[cur_loc.x + c.max_x * cur_loc.y] == old_val) { + color(c, cur_loc, old_val, new_val); + + struct point neighbors[4]; + int cnt = find_neighbors(c, cur_loc, old_val, new_val, neighbors); + + for (int i = 0; i < cnt; ++i) { + enqueue(&q, neighbors[i]); + } + } + } + + free_queue(q); +} + +void recursive_fill(struct canvas c, struct point p, int old_val, + int new_val) { + + if (old_val == new_val) { + return; + } + + color(c, p, old_val, new_val); + + struct point neighbors[4]; + int cnt = find_neighbors(c, p, old_val, new_val, neighbors); + + for (int i = 0; i < cnt; ++i) { + recursive_fill(c, neighbors[i], old_val, new_val); + } +} + +int grid_cmp(int *a, int *b, int size) { + for (int i = 0; i < size; ++i) { + if (a[i] != b[i]) { + return 0; + } + } + + return 1; +} + +int main() { + int grid[25] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + }; + int grid1[25] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + }; + int grid2[25] = { + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + }; + int answer_grid[25] = { + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + }; + + struct canvas c = {5, 5, grid}; + struct canvas c1 = {5, 5, grid1}; + struct canvas c2 = {5, 5, grid2}; + + struct point start_loc = {0, 0}; + + int pass_cnt = 0; + + recursive_fill(c, start_loc, 0, 1); + pass_cnt += grid_cmp(grid, answer_grid, 25); + + stack_fill(c1, start_loc, 0, 1); + pass_cnt += grid_cmp(grid1, answer_grid, 25); + + queue_fill(c2, start_loc, 0, 1); + pass_cnt += grid_cmp(grid2, answer_grid, 25); + + printf("Test Summary: | Pass\tTotal\n"); + printf("Fill Methods |\t%d\t3\n", pass_cnt); + + return 0; +} + diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index e53707a3c..836f86897 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -88,6 +88,8 @@ In code, this might look like this: {% method %} {% sample lang="jl" %} [import:37-55, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="c" %} +[import:34-52, lang:"c"](code/c/flood_fill.c) {% endmethod %} @@ -102,10 +104,17 @@ In code, it might look like this: {% method %} {% sample lang="jl" %} [import:106-118, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="c" %} +[import:180-195, lang:"c"](code/c/flood_fill.c) +{% endmethod %} -All Julia code snippets for this chapter rely on an exterior `color!(...)` function, defined as +All code snippets for this chapter rely on an exterior `color` function, defined as +{% method %} +{% sample lang="jl" %} [import:23-35, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="c" %} +[import:28-32, lang:"c"](code/c/flood_fill.c) {% endmethod %} The above code continues recursing through available neighbors as long as neighbors exist, and this should work so long as we are adding the correct set of neighbors. @@ -115,6 +124,8 @@ Additionally, it is possible to do the same type of traversal by managing a stac {% method %} {% sample lang="jl" %} [import:57-77, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="c" %} +[import:85-108, lang:"c"](code/c/flood_fill.c) {% endmethod %} This is ultimately the same method of traversal as before; however, because we are managing our own data structure, there are a few distinct differences: @@ -152,6 +163,8 @@ The code would look something like this: {% method %} {% sample lang="jl" %} [import:80-104, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="c" %} +[import:155-178, lang:"c"](code/c/flood_fill.c) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally. @@ -228,6 +241,8 @@ After, we will fill in the left-hand side of the array to be all ones by choosin {% method %} {% sample lang="jl" %} [import, lang:"julia"](code/julia/flood_fill.jl) +{% sample lang="c" %} +[import, lang:"c"](code/c/flood_fill.c) {% endmethod %} From d21dacdd1096c61341fcaf80967b103919a9473b Mon Sep 17 00:00:00 2001 From: dovisutu <40313014+dovisutu@users.noreply.github.com> Date: Fri, 11 Sep 2020 07:18:00 +0800 Subject: [PATCH 04/32] Huffman Encoding Scratch Implementation (#682) * Add code and image for huffman * Fix a missing line and a few typo --- .../huffman_encoding/code/scratch/huffman.svg | 98 +++++++++++ .../huffman_encoding/code/scratch/huffman.txt | 155 ++++++++++++++++++ contents/huffman_encoding/huffman_encoding.md | 5 + 3 files changed, 258 insertions(+) create mode 100644 contents/huffman_encoding/code/scratch/huffman.svg create mode 100644 contents/huffman_encoding/code/scratch/huffman.txt diff --git a/contents/huffman_encoding/code/scratch/huffman.svg b/contents/huffman_encoding/code/scratch/huffman.svg new file mode 100644 index 000000000..5f633b8ce --- /dev/null +++ b/contents/huffman_encoding/code/scratch/huffman.svg @@ -0,0 +1,98 @@ +defineHuffmanencodesstringsetencodedto empty hereifstring=thenelsedeleteallofLetter-Node PointerdeleteallofNodes' Values ListdeleteallofOperating Nodes' ListdeleteallofNode-lhs IndexdeleteallofNode-rhs Indexdeleteallofbit listdeleteallofcodebookinitializeleaveswithstringmaketreeif0=itemrootofNode-lhs Indexthenbinarycount special care: see issue #659elserepeatlengthofLetter-Node Pointeraddplcaceholdertocodebookgeneratecodebookfromrootformatstringusingcodebook empty heredefineinitializeleaveswithstringsetito1repeatlengthofstringifLetter-Node Pointercontainsletteriofstringthensettempto1+itemitem#ofletteriofstringinLetter-Node PointerofNodes' Values Listreplaceitemitem#ofletteriofstringinLetter-Node PointerofNodes' Values Listwithtmpelseadd1toNodes' Values ListaddletteriofstringtoLetter-Node Pointer increment: if didn't exist, add itchangeiby1defineMaketreesetito1repeatlengthofNodes' Values ListadditemiofNodes' Values ListtoOperating Nodes' Listadd0toNode-lhs Index leaves don't have childrenadd0toNode-rhs Indexchangeiby1 initializesetcounttolengthofNodes' Values Listrepeatuntilcount=1setito2 find smallest 2 nodessetminimumtoitem1ofOperating Nodes' Listsetlhs-indexto1setrhs-indexto1repeatlengthofOperating Nodes' List-1ifitemiofOperating Nodes' List<minimumthensetminimumtoitemiofOperating Nodes' Listsetlhs-indextoichangeiby1setito2replaceitemlhs-indexofOperating Nodes' Listwith2147483647 Int.MaxValueset2nd minimumtoitem1ofOperating Nodes' ListrepeatlengthofOperating Nodes' List-1ifitemiofOperating Nodes' List<2ndminimumthenset2nd minimumtoitemiofOperating Nodes' Listsetrhs-indextoichangeiby1setito2replaceitemrhs-indexofOperating Nodes' Listwith2147483647 Int.MaxValuechangecountby-1addminimum+2ndminimumtoNodes' Values Listaddminimum+2ndminimumtoOperating Nodes' Listaddlhs-indextoNode-lhs Indexaddrhs-indextoNode-rhs IndexsetroottolengthofNodes' Values Listdefinegeneratecodebookfromroot depth-first searchif0=itemrootofNode-lhs Indexthensettmpto empty heresetito1repeatlengthofbit listsettmptojointmpitemiofbit listchangeiby1replaceitemrootofcodebookwithtmpelseadd1tobit list 1 for lhsgeneratecodebookfromitemrootofNode-lhs Indexdeletelengthofbit listofbit listadd0tobit list 0 for rhsgeneratecodebookfromitemrootofNode-rhs Indexdeletelengthofbit listofbit list reached a leafdefineformatstringusingcodebooksetito1repeatlengthofstringsetencodedtojoinencodeditemitem#ofletteriofstringinLetter-Node Pointerofcodebookchangeiby1definebinarycountsettmptoitem1ofNodes' Values Listrepeatuntil0=tmpaddtmpmod2tobit listsettmptotmp-tmpmod2/2setitolengthofbit listrepeatuntil0=isetencodedtojoinencodeditemiofbit listchangeiby-1defineHuffmandecodesstringsetdecodedto empty hereif1=lengthofLetter-Node Pointerthenbinaryresumeusingstring special care: see issue #659elsesetito1setindextorootrepeatlengthofstringifletteriofstring=1thensetindextoitemindexofNode-lhs IndexelsesetindextoitemindexofNode-rhs IndexifitemindexofNode-lhs Index=0thensetdecodedtojoindecodeditemindexofLetter-Node Pointersetindextorootchangeiby1definebinaryresumeusingstringsettmpto0setito1repeatlengthofstringsettmptotmp*2+letteriofstringchangeiby1repeattmpsetdecodedtojoindecodeditem1ofLetter-Node Pointerwhenclickedsetto be encodedtobibbity bobbityHuffmanencodestobeencodedsayjoinEncoded, the string looks like: encodedfor3secondsHuffmandecodesencodedsayjoinAnd decoded, the string looks like: decodedfor3secondssayjoinjoinAs opposed to the original, which is lengthoftobeencoded*8bits,for2secondssayjoinjoinThe encoded has size lengthofencoded.for1seconds \ No newline at end of file diff --git a/contents/huffman_encoding/code/scratch/huffman.txt b/contents/huffman_encoding/code/scratch/huffman.txt new file mode 100644 index 000000000..1e1a84fa6 --- /dev/null +++ b/contents/huffman_encoding/code/scratch/huffman.txt @@ -0,0 +1,155 @@ +define Huffman encodes (string) +set [encoded v] to () // empty here +if <(string) = ()> then // empty here +else + delete all of [Letter-Node Pointer v] + delete all of [Nodes' Values List v] + delete all of [Operating Nodes' List v] + delete all of [Node-lhs Index v] + delete all of [Node-rhs Index v] + delete all of [bit list v] + delete all of [codebook v] + initialize leaves with (string) + make tree + if <(0) = (item (root) of [Node-lhs Index v])> then + binary count // special care: see issue #659 + else + repeat (length of [Letter-Node Pointer v]) + add (plcaceholder) to [codebook v] + end + generate codebook from (root) + format (string) using codebook + end +end + +define initialize leaves with (string) +set [i v] to (1) +repeat (length of (string)) + if <[Letter-Node Pointer v] contains (letter (i) of (string))> then // increment: if didn't exist, add it + set [temp v] to ((1) + (item (item # of (letter (i) of (string)) in [Letter-Node Pointer v]) of [Nodes' Values List v])) + replace item (item # of (letter (i) of (string)) in [Letter-Node Pointer v]) of [Nodes' Values List v] with (tmp) + else + add (1) to [Nodes' Values List v] + add (letter (i) of (string)) to [Letter-Node Pointer v] + end + change [i v] by (1) +end + +define Make tree +set [i v] to (1) +repeat (length of [Nodes' Values List v]) // initialize + add (item (i) of [Nodes' Values List v]) to [Operating Nodes' List v] + add (0) to [Node-lhs Index v] // leaves don't have children + add (0) to [Node-rhs Index v] + change [i v] by (1) +end +set [count v] to (length of [Nodes' Values List v]) +repeat until <(count) = (1)> + set [i v] to (2) // find smallest 2 nodes + set [minimum v] to (item (1) of [Operating Nodes' List v]) + set [lhs-index v] to (1) + set [rhs-index v] to (1) + repeat ((length of [Operating Nodes' List]) - (1)) + if <(item (i) of [Operating Nodes' List v]) < (minimum)> then + set [minimum v] to (item (i) of [Operating Nodes' List v]) + set [lhs-index v] to (i) + end + change [i v] by (1) + end + set [i v] to (2) + replace item (lhs-index) of [Operating Nodes' List v] with (2147483647) // Int.MaxValue + set [2nd minimum v] to (item (1) of [Operating Nodes' List v]) + repeat ((length of [Operating Nodes' List]) - (1)) + if <(item (i) of [Operating Nodes' List v]) < (2nd minimum)> then + set [2nd minimum v] to (item (i) of [Operating Nodes' List v]) + set [rhs-index v] to (i) + end + change [i v] by (1) + end + set [i v] to (2) + replace item (rhs-index) of [Operating Nodes' List v] with (2147483647) // Int.MaxValue + change [count v] by (-1) + add ((minimum) + (2nd minimum)) to [Nodes' Values List v] + add ((minimum) + (2nd minimum)) to [Operating Nodes' List v] + add (lhs-index) to [Node-lhs Index v] + add (rhs-index) to [Node-rhs Index v] +end +set [root v] to (length of [Nodes' Values List]) + +define generate codebook from (root) // depth-first search +if <(0) = (item (root) of [Node-lhs Index v])> then // reached a leaf + set [tmp v] to () // empty here + set [i v] to (1) + repeat (length of [bit list v]) + set [tmp v] to (join (tmp) (item (i) of [bit list v])) + change [i v] by (1) + end + replace item (root) of [codebook v] with (tmp) +else + add (1) to [bit list v] // 1 for lhs + generate codebook from (item (root) of [Node-lhs Index v]) + delete (length of [bit list v]) of [bit list v] + add (0) to [bit list v] // 0 for rhs + generate codebook from (item (root) of [Node-rhs Index v]) + delete (length of [bit list v]) of [bit list v] +end + +define format (string) using codebook +set [i v] to (1) +repeat (length of (string)) + set [encoded v] to (join (encoded) (item (item # of (letter (i) of (string)) in [Letter-Node Pointer v]) of [codebook v])) + change [i v] by (1) +end + +define binary count +set [tmp v] to (item (1) of [Nodes' Values List v]) +repeat until <(0) = (tmp)> + add ((tmp) mod (2)) to [bit list v] + set [tmp v] to (((tmp) - ((tmp) mod (2))) / (2)) +end +set [i v] to (length of [bit list v]) +repeat until <(0) = (i)> + set [encoded v] to (join (encoded) (item (i) of [bit list v])) + change [i v] by (-1) +end + +define Huffman decodes (string) +set [decoded v] to () // empty here +if <(1) = (length of [Letter-Node Pointer v])> then + binary resume using (string) // special care: see issue #659 +else + set [i v] to (1) + set [index v] to (root) + repeat (length of (string)) + if <(letter (i) of (string)) = (1)> then + set [index v] to (item (index) of [Node-lhs Index v]) + else + set [index v] to (item (index) of [Node-rhs Index v]) + end + if <(item (index) of [Node-lhs Index v]) = (0)> then + set [decoded v] to (join (decoded) (item (index) of [Letter-Node Pointer v])) + set [index v] to (root) + end + change [i v] by (1) + end +end + +define binary resume using (string) +set [tmp v] to (0) +set [i v] to (1) +repeat (length of (string)) + set [tmp v] to (((tmp) * (2)) + (letter (i) of (string))) + change [i v] by (1) +end +repeat (tmp) + set [decoded v] to (join (decoded) (item (1) of [Letter-Node Pointer v])) +end + +when @greenflag clicked +set [to be encoded v] to [bibbity bobbity] +Huffman encodes (to be encoded) +say (join [Encoded, the string looks like: ] (encoded)) for (3) seconds +Huffman decodes (encoded) +say (join [And decoded, the string looks like: ] (decoded)) for (3) seconds +say (join (join [As opposed to the original, which is ] ((length of (to be encoded)) * (8))) [bits,]) for (2) seconds +say (join (join [The encoded has size ] (length of (encoded))) [.]) for (1) seconds diff --git a/contents/huffman_encoding/huffman_encoding.md b/contents/huffman_encoding/huffman_encoding.md index 885a81ab4..f51505b32 100644 --- a/contents/huffman_encoding/huffman_encoding.md +++ b/contents/huffman_encoding/huffman_encoding.md @@ -93,6 +93,11 @@ Whether you use a stack or straight-up recursion also depends on the language, b [import, lang:"asm-x64"](code/asm-x64/huffman.s) {% sample lang="scala" %} [import, lang:"scala"](code/scala/huffman_encoding.scala) +{% sample lang="scratch" %} +The code snippet was taken from this [scratch project](https://scratch.mit.edu/projects/389604255/) +

+ +

{% endmethod %}