def _traverse_post_order(tree: HuffmanTree, byte_list: List[int] = None) \ -> List: """ Traverses a <tree> in post order and appends 1 if it is a leaf, 0 if it is a node, and other specifications. """ if not tree.is_leaf(): if byte_list is None: byte_list = [] byte_list = _traverse_post_order(tree.left, byte_list) byte_list = _traverse_post_order(tree.right, byte_list) if not tree.left.is_leaf(): byte_list.append(1) byte_list.append(tree.left.number) if tree.left.is_leaf(): byte_list.append(0) byte_list.append(tree.left.symbol) if not tree.right.is_leaf(): byte_list.append(1) byte_list.append(tree.right.number) if tree.right.is_leaf(): byte_list.append(0) byte_list.append(tree.right.symbol) return byte_list
def tree_to_bytes(tree: HuffmanTree) -> bytes: """ Return a bytes representation of the Huffman tree <tree>. The representation should be based on the postorder traversal of the tree's internal nodes, starting from 0. Precondition: <tree> has its nodes numbered. >>> tree = HuffmanTree(None, HuffmanTree(3, None, None), \ HuffmanTree(2, None, None)) >>> number_nodes(tree) >>> list(tree_to_bytes(tree)) [0, 3, 0, 2] >>> left = HuffmanTree(None, HuffmanTree(3, None, None), \ HuffmanTree(2, None, None)) >>> right = HuffmanTree(5) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> list(tree_to_bytes(tree)) [0, 3, 0, 2, 1, 0, 0, 5] >>> tree = build_huffman_tree(build_frequency_dict(b"helloworld")) >>> number_nodes(tree) >>> list(tree_to_bytes(tree)) #doctest: +NORMALIZE_WHITESPACE [0, 104, 0, 101, 0, 119, 0, 114, 1, 0, 1, 1, 0, 100, 0, 111, 0, 108,\ 1, 3, 1, 2, 1, 4] >>> tree = HuffmanTree(None, HuffmanTree(None, HuffmanTree(10, None, None),\ HuffmanTree(12, None, None)), \ HuffmanTree(None, HuffmanTree(5, None, None), HuffmanTree(7, None, None))) >>> number_nodes(tree) >>> list(tree_to_bytes(tree)) [0, 10, 0, 12, 0, 5, 0, 7, 1, 0, 1, 1] """ if tree.is_leaf() and tree.symbol is None: return bytes([]) else: return bytes(_traverse_post_order(tree))
def get_codes(tree: HuffmanTree) -> Dict[int, str]: """ Return a dictionary which maps symbols from the Huffman tree <tree> to codes. >>> tree = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> d = get_codes(tree) >>> d == {3: "0", 2: "1"} True >>> tree = HuffmanTree(None, None, None) >>> d = get_codes(tree) >>> d == {} True >>> left = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> right = HuffmanTree(9) >>> tree = HuffmanTree(None, left, right) >>> d_test = get_codes(tree) >>> d_test == {3: "00", 2: "01", 9: "1"} True >>> left_ext = HuffmanTree(None, HuffmanTree(2), HuffmanTree(3)) >>> left = HuffmanTree(None, HuffmanTree(1), left_ext) >>> right = HuffmanTree(None, HuffmanTree(9), HuffmanTree(10)) >>> tree = HuffmanTree(None, left, right) >>> d = get_codes(tree) >>> d == {1: '00', 2: '010', 3: '011', 9: '10', 10: '11'} True >>> tree = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> d_text = get_codes(tree) >>> d_text {3: '0', 2: '1'} """ # Edge Case if tree is None or (tree.symbol is None and tree.is_leaf()): return {} else: return _get_codes_helper(tree, "")
def _number_nodes_helper(tree: HuffmanTree, number: int = 0) -> int: """ A helper function that uses a <tree> to <number> the internal nodes. """ if tree.is_leaf(): return number - 1 else: number = _number_nodes_helper(tree.left, number) + 1 number = _number_nodes_helper(tree.right, number) + 1 tree.number = number return number
def number_nodes(tree: HuffmanTree) -> None: """ Number internal nodes in <tree> according to postorder traversal. The numbering starts at 0. >>> left = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> right = HuffmanTree(None, HuffmanTree(9), HuffmanTree(10)) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> tree.left.number 0 >>> tree.right.number 1 >>> tree.number 2 >>> right_ext = HuffmanTree(None, HuffmanTree(2), HuffmanTree(3)) >>> left = HuffmanTree(None, HuffmanTree(1), right_ext) >>> right = HuffmanTree(None, HuffmanTree(9), HuffmanTree(10)) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> tree.left.right.number 0 >>> tree.left.number 1 >>> tree.right.number 2 >>> tree.number 3 >>> left_ext = HuffmanTree(None, HuffmanTree(2), HuffmanTree(3)) >>> left = HuffmanTree(None, left_ext, HuffmanTree(1)) >>> right = HuffmanTree(None, HuffmanTree(9), HuffmanTree(10)) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> tree.left.left.number 0 >>> tree.left.number 1 >>> tree.right.number 2 >>> tree.number 3 >>> tree = HuffmanTree(None) >>> number_nodes(tree) >>> tree.number """ if tree.symbol is None and tree.is_leaf(): return None else: _number_nodes_helper(tree, 0) return None
def get_codes(tree: HuffmanTree) -> Dict[int, str]: """ Return a dictionary which maps symbols from the Huffman tree <tree> to codes. >>> tree = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> d = get_codes(tree) >>> d == {3: "0", 2: "1"} True """ final = {} if tree.is_leaf(): return {tree.symbol: '0'} else: _traverse_huff(tree, "", final) return final
def _recursive_numbering(tree: HuffmanTree, cur_num: list): """ Algorithm that recursively builds and mutates the huff_map """ if tree.is_leaf(): # dont number leaves return elif not tree: # blank tree, graceful termiantion return else: _recursive_numbering(tree.left, cur_num) # then right _recursive_numbering(tree.right, cur_num) # finally root tree.number = cur_num[0] cur_num[0] += 1
def _get_codes_helper(tree: HuffmanTree, code: str, symbol_dict: Any = None) -> Dict[int, str]: """ A helper function for <get_codes> that returns a dictionary which maps symbols from the Huffman tree <tree> to codes <code>, and outputs a dictionary <symbol_dict>. """ if tree.is_leaf(): symbol_dict[tree.symbol] = code return symbol_dict else: if symbol_dict is None: symbol_dict = {} symbol_dict = _get_codes_helper(tree.left, code + "0", symbol_dict) symbol_dict = _get_codes_helper(tree.right, code + "1", symbol_dict) return symbol_dict
def _find_height(tree: HuffmanTree, count: List = None) -> int: """ Returns the height <count> of a <tree>. >>> tree = HuffmanTree(None, HuffmanTree(None, \ HuffmanTree(None, HuffmanTree(104, None, None), \ HuffmanTree(101, None, None)), HuffmanTree(None, \ HuffmanTree(119, None, None), HuffmanTree(114, None, None))), \ HuffmanTree(None, HuffmanTree(108, None, None), \ HuffmanTree(None, HuffmanTree(100, None, None), \ HuffmanTree(111, None, None)))) >>> print(len(_find_height(tree))) 6 """ if not tree.is_leaf(): if count is None: count = [] _find_height(tree.left, count) _find_height(tree.right, count) count.append(tree.symbol) return count
def tree_to_bytes(tree: HuffmanTree) -> bytes: """ Return a bytes representation of the Huffman tree <tree>. The representation should be based on the postorder traversal of the tree's internal nodes, starting from 0. Precondition: <tree> has its nodes numbered. >>> tree = HuffmanTree(None, HuffmanTree(3, None, None), \ HuffmanTree(2, None, None)) >>> number_nodes(tree) >>> list(tree_to_bytes(tree)) [0, 3, 0, 2] >>> left = HuffmanTree(None, HuffmanTree(3, None, None), \ HuffmanTree(2, None, None)) >>> right = HuffmanTree(5) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> list(tree_to_bytes(tree)) [0, 3, 0, 2, 1, 0, 0, 5] >>> tree = build_huffman_tree(build_frequency_dict(b"helloworld")) >>> number_nodes(tree) >>> list(tree_to_bytes(tree))\ #doctest: +NORMALIZE_WHITESPACE [0, 104, 0, 101, 0, 119, 0, 114, 1, 0, 1, 1, 0, 100, 0, 111, 0, 108,\ 1, 3, 1, 2, 1, 4] """ final = [] if tree.is_leaf(): return bytes([0, 0, 0, 0]) else: # user recursive function to post_travers _recursive_tree_to_bytes(tree, final) return bytes(final)
def number_nodes(tree: HuffmanTree) -> None: """ Number internal nodes in <tree> according to postorder traversal. The numbering starts at 0. >>> left = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> right = HuffmanTree(None, HuffmanTree(9), HuffmanTree(10)) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> tree.left.number 0 >>> tree.right.number 1 >>> tree.number 2 """ # cant be recursive because different in/out\ # use list so we can mutate value if tree.is_leaf(): # leafs arent given a number tree.number = None else: _recursive_numbering(tree, [0])