├── .github └── workflows │ └── build.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── adaptive_huffman_coding ├── __init__.py ├── tree.py └── utils.py ├── main.py ├── poetry.lock ├── poetry.toml ├── pylintrc ├── pyproject.toml └── tests ├── __init__.py ├── images ├── Baboon.raw ├── Lena.raw └── simple.raw └── test_adaptive_huffman_coding.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | 11 | - name: Set up Python 12 | uses: actions/setup-python@v2 13 | with: 14 | python-version: 3.8 15 | 16 | - name: Set up Poetry 17 | run: | 18 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python 19 | echo "PATH=$HOME/.poetry/bin:$PATH" >> $GITHUB_ENV 20 | - name: Install dependencies 21 | run: poetry install --no-root 22 | 23 | - name: Lint 24 | run: | 25 | poetry run python -m pylint adaptive_huffman_coding/ 26 | poetry run python -m mypy --ignore-missing-import adaptive_huffman_coding/ 27 | - name: Test 28 | run: poetry run python -m pytest -v --cov-report xml --cov=adaptive_huffman_coding tests/ 29 | - name: Upload coverage report to Codacy 30 | uses: codacy/codacy-coverage-reporter-action@master 31 | with: 32 | project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} 33 | coverage-reports: coverage.xml 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | .dmypy.json 111 | dmypy.json 112 | 113 | # vscode 114 | .vscode/* 115 | !.vscode/settings.json 116 | !.vscode/tasks.json 117 | !.vscode/launch.json 118 | !.vscode/extensions.json 119 | 120 | # project 121 | temp*.* 122 | compressed* 123 | extract* 124 | *.protojpg 125 | *.lprof -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Current File (Integrated Terminal)", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal" 13 | }, 14 | { 15 | "name": "Python: Attach", 16 | "type": "python", 17 | "request": "attach", 18 | "port": 5678, 19 | "host": "localhost" 20 | }, 21 | { 22 | "name": "Python: Module", 23 | "type": "python", 24 | "request": "launch", 25 | "module": "enter-your-module-name-here", 26 | "console": "integratedTerminal" 27 | }, 28 | { 29 | "name": "Python: Django", 30 | "type": "python", 31 | "request": "launch", 32 | "program": "${workspaceFolder}/manage.py", 33 | "console": "integratedTerminal", 34 | "args": [ 35 | "runserver", 36 | "--noreload", 37 | "--nothreading" 38 | ], 39 | "django": true 40 | }, 41 | { 42 | "name": "Python: Flask", 43 | "type": "python", 44 | "request": "launch", 45 | "module": "flask", 46 | "env": { 47 | "FLASK_APP": "app.py" 48 | }, 49 | "args": [ 50 | "run", 51 | "--no-debugger", 52 | "--no-reload" 53 | ], 54 | "jinja": true 55 | }, 56 | { 57 | "name": "Python: Current File (External Terminal)", 58 | "type": "python", 59 | "request": "launch", 60 | "program": "${file}", 61 | "console": "externalTerminal" 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "python.linting.enabled": true, 4 | "python.linting.pylintEnabled": true, 5 | "python.linting.mypyEnabled": true, 6 | "python.linting.pylintUseMinimalCheckers": false, 7 | "python.pythonPath": ".venv/bin/python" 8 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sean Wu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adaptive Huffman Coding Compression 2 | 3 | [![build](https://github.com/seanwu1105/adaptive-huffman-coding/workflows/build/badge.svg)](https://github.com/seanwu1105/adaptive-huffman-coding/actions?query=workflow%3Abuild) 4 | 5 | Adaptive Huffman coding algorithm in Python. 6 | 7 | ## Testing Files and Compressing Results 8 | 9 | | File Name | Compressed | Compressing/Extracting Time | First-Order Entropy | Compressed File Size (DPCM) | Compressing/Extracting Time (DPCM) | First-Order Entropy (DPCM) | 10 | |:-----------------------------------------------------------------------------------------:|:------------:|:---------------------------:|:-------------------:|:---------------------------:|:----------------------------------:|:--------------------------:| 11 | | `Lena.raw` ![Lena.raw Preview ](https://i.imgur.com/gLSTp61.png "Lena.raw Preview" ) | 245153 Bytes | 02:12s/01:50s | 7.447359 | 167384 Bytes | 01:50s/01:11s | 5.064970 | 12 | | `Baboon.raw` ![Baboon.raw Preview ](https://i.imgur.com/zDWHGXN.png "Baboon.raw Preview" ) | 242230 Bytes | 02:15s/01:50s | 7.357734 | 209341 Bytes | 02:15s/01:47s | 6.352999 | 13 | 14 | ## Getting Started 15 | 16 | ### Environment 17 | 18 | * Visual Studio Code 19 | * Poetry 1.0.10 or later 20 | 21 | ### Installation 22 | 23 | Clone this repositroy. 24 | 25 | ``` bash 26 | git clone https://github.com/seanwu1105/adaptive_huffman_coding 27 | ``` 28 | 29 | Open the root directory. 30 | 31 | ``` bash 32 | cd adaptive_huffman_coding 33 | ``` 34 | 35 | Install the dependencies with Poetry. 36 | 37 | ``` bash 38 | poetry install --no-root 39 | ``` 40 | 41 | ### Uses 42 | 43 | This project is fully written in Python 3. For data compression given specific alphabet size, 44 | 45 | ``` python 46 | from adaptive_huffman.ada_huffman import compress 47 | 48 | alphabet_range = (0, 255) # from 0 to 255 inclusively 49 | dpcm = False # preprocess the target data with DPCM 50 | compress(fn, 'compressed', alphabet_range=alphabet_range, dpcm=dpcm) 51 | ``` 52 | 53 | For data extraction given specific alphabet size, 54 | 55 | ``` python 56 | from adaptive_huffman.ada_huffman import extract 57 | 58 | alphabet_range = (0, 255) # from 0 to 255 inclusively 59 | dpcm = False # postprocess the target data with DPCM 60 | extract('compressed', 'extracted.raw', alphabet_range=alphabet_range, dpcm=dpcm) 61 | ``` 62 | 63 | You could find the demo in [`main.py`](/main.py), which will compress and extract the target data (`Lena.raw` and `Baboon.raw`) then show the original and after-processing images for comparison with Matplotlib. To run the demo in **Ubuntu 18.04**, 64 | 65 | ``` bash 66 | python3 main.py 67 | ``` 68 | 69 | > Note that in Windows, this project requires **Visual C++ 14.0** as it requires **bitarray** module which needs C source codes compilation. 70 | 71 | Furthermore, for simple testing (whether the file after compression and extraction is the same as the original), 72 | 73 | ``` bash 74 | pytest 75 | ``` 76 | 77 | ## Caveat 78 | 79 | * This algorithm will read **ALL** image into memory and **ALL** encoded bits would be saved as **Python String**, and thus it is possible to cause memory overflow if the input image file is too large. 80 | * [Details Report](https://is.gd/VMCLWw) 81 | 82 | ## Algorithm and Implementation 83 | 84 | ### Encoding Procedure 85 | 86 | Assume the input stream is a sequence of bytes. First, if we use DPCM as symbol set, we use the following method for differentiation. 87 | 88 | ``` python 89 | # `seq` is byte array originally. For indexing, we need to convert it into list. 90 | seq = list(seq) 91 | return ((item - seq[idx - 1]) & 0xff if idx else seq[idx] for idx, item in enumerate(seq)) 92 | ``` 93 | 94 | Second, we convert every byte in the sequence into a fixed code by the following method: 95 | 96 | Assume the source has an alphabet (`a[1]`, `a[2]`, ..., `a[m]`) of size `m`, then pick `e` and `r` such that `m = 2^e + r` and `0 <= r < 2^e`. We calculate `e` and `r` by the following Python codes. 97 | 98 | ``` python 99 | # Get the largest `e` smaller than `alphabet_size`. 100 | e = m.bit_length() - 1 101 | r = m - 2**e 102 | ``` 103 | 104 | Let `k` to be the index of a certain alphabet. If `1 <= k <= 2r`, the symbol `a[k]` is encoded as the `(e + 1)`-bit binary representation of `k - 1`. Else, `a[k]` is encoded as `e`-bit representation of `k - r - 1`. After converting byte symbols into fixed codes, we encode the whole sequence into bit sequence by the following flowchart. 105 | 106 | ![Encoding Flowchart](https://i.imgur.com/5xzPKiO.png "Encoding Flowchart") 107 | 108 | Finally, as the length of the bit sequence might not be a multiple of 8, the few remaining bits (1..7) are set to 0. We need to **add the length of remaining bits we filled to the head of output bit sequence** in order to notify the exact file size for decoding procedure. To implement this, we could simply insert 3-bit at the beginning of the output bit sequence to represent the length of fill-up remaining bits. 109 | 110 | ``` python 111 | # Get the length of remaining bits. 112 | remaining_bits_length = (bits2bytes(code.length() + 3) * 8 - (code.length() + 3)) 113 | # Insert the length information into the beginning of output sequence. 114 | for bit in '{:03b}'.format(remaining_bits_length)[::-1]: 115 | code.insert(0, False if bit == '0' else True) 116 | ``` 117 | 118 | ### Decoding Procedure 119 | 120 | First, we need to get the actual file size from the first 3-bit of the input bit sequence and remove them as well as the fill-up remaining bits appending to the sequence. 121 | 122 | ``` python 123 | # Get the actual file size and remove the information. 124 | remaining_bits_length = int(read_bit(3).to01(), 2) 125 | # Remove the remaining bits. 126 | del bit_seq[-remaining_bits_length:] 127 | ``` 128 | 129 | After that, we could decode the content of the file as the following flowchart. 130 | 131 | ![Decoding Flowchart](https://i.imgur.com/x1OSKbe.png "Decoding Flowchart") 132 | 133 | Finally, if the file is encoded with DPCM, we need to convert the content back into the real data. 134 | 135 | ``` python 136 | # `seq` is the byte sequence of decoded data. 137 | return itertools.accumulate(seq, lambda x, y: (x + y) & 0xff) 138 | ``` 139 | 140 | ### Updating Procedure 141 | 142 | The updating procedure used by both encoding and decoding procedure is the following flowchart. 143 | 144 | ![Update Flowchart](https://i.imgur.com/hmCA8jT.png "Update Flowchart") 145 | 146 | ### Data Structure 147 | 148 | We use a DAG, which has 2 children and parent pointer for every node, to implement Huffman tree. The code of a node is generated when we search for it (`Tree.search()` in class `Tree` of [`tree.py`](/adaptive_huffman/tree.py)), and thus the code for every node will not be updated when exchanging or appending. Furthermore, we use an array (`self.all_nodes` in class `AdaptiveHuffman` of [`ada_huffman.py`](/adaptive_huffman/ada_huffman.py)) to keep track of each node within the tree. By doing this, we could search a node within the tree iteratively instead of recursively traversal. 149 | -------------------------------------------------------------------------------- /adaptive_huffman_coding/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import operator 3 | import os 4 | 5 | from bitarray import bitarray, bits2bytes 6 | from progress.bar import ShadyBar 7 | 8 | from .tree import Tree, NYT, exchange 9 | from .utils import (encode_dpcm, decode_dpcm, bin_str2bool_list, 10 | bool_list2bin_str, bool_list2int, entropy) 11 | 12 | __version__ = '0.1.0' 13 | 14 | 15 | # pylint: disable=too-many-instance-attributes 16 | class AdaptiveHuffman: 17 | def __init__(self, byte_seq, alphabet_range=(0, 255), dpcm=False): 18 | """Create an adaptive huffman encoder and decoder. 19 | 20 | Args: 21 | byte_seq (bytes): The bytes sequence to encode or decode. 22 | alphabet_range (tuple or integer): The range of alphabet 23 | inclusively. 24 | """ 25 | 26 | self.byte_seq = byte_seq 27 | self.dpcm = dpcm 28 | 29 | self._bits = None # Only used in decode(). 30 | self._bits_idx = 0 # Only used in decode(). 31 | 32 | # Get the first decimal number of all alphabets 33 | self._alphabet_first_num = min(alphabet_range) 34 | alphabet_size = abs(alphabet_range[0] - alphabet_range[1]) + 1 35 | # Select an `exp` and `rem` which meet `alphabet_size = 2**exp + rem`. 36 | # Get the largest `exp` smaller than `alphabet_size`. 37 | self.exp = alphabet_size.bit_length() - 1 38 | self.rem = alphabet_size - 2**self.exp 39 | 40 | # Initialize the current node # as the maximum number of nodes with 41 | # `alphabet_size` leaves in a complete binary tree. 42 | self.current_node_num = alphabet_size * 2 - 1 43 | 44 | self.tree = Tree(0, self.current_node_num, data=NYT) 45 | self.all_nodes = [self.tree] 46 | self.nyt = self.tree # initialize the NYT reference 47 | 48 | def encode(self): 49 | """Encode the target byte sequence into compressed bit sequence by 50 | adaptive Huffman coding. 51 | 52 | Returns: 53 | bitarray: The compressed bitarray. Use `bitarray.tofile()` to save 54 | to file. 55 | """ 56 | 57 | def encode_fixed_code(dec): 58 | """Convert a decimal number into specified fixed code. 59 | 60 | Arguments: 61 | dec {int} -- The alphabet need to be converted into fixed code. 62 | 63 | Returns: 64 | list of bool -- Fixed codes. 65 | """ 66 | 67 | alphabet_idx = dec - (self._alphabet_first_num - 1) 68 | if alphabet_idx <= 2 * self.rem: 69 | fixed_str = '{:0{padding}b}'.format( 70 | alphabet_idx - 1, 71 | padding=self.exp + 1 72 | ) 73 | else: 74 | fixed_str = '{:0{padding}b}'.format( 75 | alphabet_idx - self.rem - 1, 76 | padding=self.exp 77 | ) 78 | return bin_str2bool_list(fixed_str) 79 | 80 | progressbar = ShadyBar( 81 | 'encoding', 82 | max=len(self.byte_seq), 83 | suffix='%(percent).1f%% - %(elapsed_td)ss' 84 | ) 85 | 86 | if self.dpcm: 87 | self.byte_seq = tuple(encode_dpcm(self.byte_seq)) 88 | 89 | logging.getLogger(__name__).info('entropy: %f', entropy(self.byte_seq)) 90 | 91 | code = [] 92 | for symbol in self.byte_seq: 93 | fixed_code = encode_fixed_code(symbol) 94 | result = self.tree.search(fixed_code) 95 | if result['first_appearance']: 96 | code.extend(result['code']) # send code of NYT 97 | code.extend(fixed_code) # send fixed code of symbol 98 | else: 99 | # send code which is path from root to the node of symbol 100 | code.extend(result['code']) 101 | self.update(fixed_code, result['first_appearance']) 102 | progressbar.next() 103 | 104 | # Add remaining bits length info at the beginning of the code in order 105 | # to avoid the decoder regarding the remaining bits as actual data. The 106 | # remaining bits length info require 3 bits to store the length. Note 107 | # that the first 3 bits are stored as big endian binary string. 108 | remaining_bits_length = ( 109 | bits2bytes(len(code) + 3) * 8 - (len(code) + 3) 110 | ) 111 | code = (bin_str2bool_list('{:03b}'.format(remaining_bits_length)) 112 | + code) 113 | 114 | progressbar.finish() 115 | return bitarray(code) 116 | 117 | def decode(self): 118 | """Decode the target byte sequence which is encoded by adaptive Huffman 119 | coding. 120 | 121 | Returns: 122 | list: A list of integer representing the number of decoded byte 123 | sequence. 124 | """ 125 | 126 | def read_bits(bit_count): 127 | """Read n leftmost bits and move iterator n steps. 128 | 129 | Arguments: 130 | n {int} -- The # of bits is about to read. 131 | 132 | Returns: 133 | list -- The n bits has been read. 134 | """ 135 | 136 | progressbar.next(bit_count) 137 | ret = self._bits[self._bits_idx:self._bits_idx + bit_count] 138 | self._bits_idx += bit_count 139 | return ret 140 | 141 | def decode_fixed_code(): 142 | fixed_code = read_bits(self.exp) 143 | integer = bool_list2int(fixed_code) 144 | if integer < self.rem: 145 | fixed_code += read_bits(1) 146 | integer = bool_list2int(fixed_code) 147 | else: 148 | integer += self.rem 149 | return integer + 1 + (self._alphabet_first_num - 1) 150 | 151 | # Get boolean list ([True, False, ...]) from bytes. 152 | bits = bitarray() 153 | bits.frombytes(self.byte_seq) 154 | self._bits = bits.tolist() 155 | self._bits_idx = 0 156 | 157 | progressbar = ShadyBar( 158 | 'decoding', 159 | max=len(self._bits), 160 | suffix='%(percent).1f%% - %(elapsed_td)ss' 161 | ) 162 | 163 | # Remove the remaining bits in the last of bit sequence generated by 164 | # bitarray.tofile() to fill up to complete byte size (8 bits). The 165 | # remaining bits length could be retrieved by reading the first 3 bits. 166 | # Note that the first 3 bits are stored as big endian binary string. 167 | remaining_bits_length = bool_list2int(read_bits(3)) 168 | if remaining_bits_length: 169 | del self._bits[-remaining_bits_length:] 170 | progressbar.next(remaining_bits_length) 171 | self._bits = tuple(self._bits) 172 | 173 | code = [] 174 | while self._bits_idx < len(self._bits): 175 | current_node = self.tree # go to root 176 | while current_node.left or current_node.right: 177 | bit = read_bits(1)[0] 178 | current_node = current_node.right if bit else current_node.left 179 | if current_node.data == NYT: 180 | first_appearance = True 181 | dec = decode_fixed_code() 182 | code.append(dec) 183 | else: 184 | # decode element corresponding to node 185 | first_appearance = False 186 | dec = current_node.data 187 | code.append(current_node.data) 188 | self.update(dec, first_appearance) 189 | 190 | progressbar.finish() 191 | return decode_dpcm(code) if self.dpcm else code 192 | 193 | def update(self, data, first_appearance): 194 | 195 | def find_node_data(data): 196 | for node in self.all_nodes: 197 | if node.data == data: 198 | return node 199 | raise KeyError(f'Cannot find the target node given {data}.') 200 | 201 | current_node = None 202 | while True: 203 | if first_appearance: 204 | current_node = self.nyt 205 | 206 | self.current_node_num -= 1 207 | new_external = Tree(1, self.current_node_num, data=data) 208 | current_node.right = new_external 209 | self.all_nodes.append(new_external) 210 | 211 | self.current_node_num -= 1 212 | self.nyt = Tree(0, self.current_node_num, data=NYT) 213 | current_node.left = self.nyt 214 | self.all_nodes.append(self.nyt) 215 | 216 | current_node.weight += 1 217 | current_node.data = None 218 | self.nyt = current_node.left 219 | else: 220 | if not current_node: 221 | # First time as `current_node` is None. 222 | current_node = find_node_data(data) 223 | node_max_num_in_block = max( 224 | ( 225 | n for n in self.all_nodes 226 | if n.weight == current_node.weight 227 | ), 228 | key=operator.attrgetter('num') 229 | ) 230 | if node_max_num_in_block not in (current_node, current_node.parent): 231 | exchange(current_node, node_max_num_in_block) 232 | current_node = node_max_num_in_block 233 | current_node.weight += 1 234 | if not current_node.parent: 235 | break 236 | current_node = current_node.parent 237 | first_appearance = False 238 | 239 | 240 | def compress(in_filename, out_filename, alphabet_range, dpcm): 241 | with open(in_filename, 'rb') as in_file: 242 | logging.getLogger(__name__).info('open file: "%s"', in_filename) 243 | content = in_file.read() 244 | logging.getLogger(__name__).info( 245 | 'original size: %d bytes', os.path.getsize(in_file.name) 246 | ) 247 | ada_huff = AdaptiveHuffman(content, alphabet_range, dpcm) 248 | code = ada_huff.encode() 249 | 250 | with open(out_filename, 'wb') as out_file: 251 | logging.getLogger(__name__).info('write file: "%s"', out_filename) 252 | code.tofile(out_file) 253 | logging.getLogger(__name__).info( 254 | 'compressed size: %d bytes', os.path.getsize(out_filename) 255 | ) 256 | 257 | 258 | def extract(in_filename, out_filename, alphabet_range, dpcm): 259 | with open(in_filename, 'rb') as in_file: 260 | logging.getLogger(__name__).info('open file: "%s"', in_filename) 261 | content = in_file.read() 262 | logging.getLogger(__name__).info( 263 | 'original size: %d bytes', os.path.getsize(in_file.name) 264 | ) 265 | ada_huff = AdaptiveHuffman(content, alphabet_range, dpcm) 266 | code = ada_huff.decode() 267 | 268 | with open(out_filename, 'wb') as out_file: 269 | logging.getLogger(__name__).info('write file: "%s"', out_filename) 270 | out_file.write(bytes(code)) 271 | logging.getLogger(__name__).info( 272 | 'extract size: %d bytes', os.path.getsize(out_filename) 273 | ) 274 | -------------------------------------------------------------------------------- /adaptive_huffman_coding/tree.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | NYT = 'NYT' 4 | 5 | 6 | # pylint: disable=too-many-instance-attributes 7 | class Tree: 8 | def __init__(self, weight, num, data=None): 9 | """Use a set (`nodes`) to store all nodes in order to search the same 10 | weight nodes (block) iteratively which would be faster than recursive 11 | traversal of a tree. 12 | """ 13 | self.weight = weight 14 | self.num = num 15 | self._left = None 16 | self._right = None 17 | self.parent = None 18 | self.data = data 19 | # code will not be always updated 20 | self.code = [] 21 | 22 | def __repr__(self): 23 | return "#%d(%d)%s '%s'" % (self.num, self.weight, self.data, self.code) 24 | 25 | @property 26 | def left(self): 27 | return self._left 28 | 29 | @left.setter 30 | def left(self, left): 31 | self._left = left 32 | if self._left: 33 | self._left.parent = self 34 | 35 | @property 36 | def right(self): 37 | return self._right 38 | 39 | @right.setter 40 | def right(self, right): 41 | self._right = right 42 | if self._right: 43 | self._right.parent = self 44 | 45 | def pretty(self, indent_str=' '): 46 | return ''.join(self.pretty_impl(0, indent_str)) 47 | 48 | def pretty_impl(self, level, indent_str): 49 | if not self._left and not self._right: 50 | return [indent_str * level, '%s' % self, '\n'] 51 | line = [indent_str * level, '%s' % self, '\n'] 52 | for subtree in (self._left, self._right): 53 | if isinstance(subtree, Tree): 54 | line += subtree.pretty_impl(level + 1, indent_str) 55 | return line 56 | 57 | def search(self, target): 58 | """Search a specific data according within the tree. Return the code of 59 | corresponding node if found. The code is the path from the root to the 60 | target node. If not found in the tree, return the code of NYT node. 61 | 62 | Args: 63 | target (any): The target data which needs to be found. 64 | 65 | Returns: 66 | {'first_appearance': bool, 'code': str}: An dictionary which 67 | contain the information of searching result. 68 | """ 69 | 70 | stack = collections.deque([self]) 71 | while stack: 72 | current = stack.pop() 73 | if current.data == target: 74 | return {'first_appearance': False, 'code': current.code} 75 | if current.data == NYT: 76 | nytcode = current.code 77 | if current.right: 78 | current.right.code = current.code + [1] 79 | stack.append(current.right) 80 | if current.left: 81 | current.left.code = current.code + [0] 82 | stack.append(current.left) 83 | return {'first_appearance': True, 'code': nytcode} 84 | 85 | 86 | def exchange(node1, node2): 87 | """Exchange the children, data of two nodes but keep the number, parent and 88 | weight the same. Note that this function will not change the reference of 89 | `node1` and `node2`. 90 | """ 91 | 92 | node1.left, node2.left = node2.left, node1.left 93 | node1.right, node2.right = node2.right, node1.right 94 | node1.data, node2.data = node2.data, node1.data 95 | -------------------------------------------------------------------------------- /adaptive_huffman_coding/utils.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import itertools 3 | import math 4 | 5 | from matplotlib import pyplot as plt 6 | import numpy as np 7 | 8 | 9 | def show_raw_img(original_filename, extracted_filename, size): 10 | with open(original_filename, 'rb') as img_file: 11 | original_img = np.fromfile(img_file, dtype=np.uint8) 12 | with open(extracted_filename, 'rb') as img_file: 13 | extracted_img = np.fromfile(img_file, dtype=np.uint8) 14 | original_img.shape = extracted_img.shape = size 15 | _, axarr = plt.subplots(1, 2) 16 | axarr[0].set_title('Original') 17 | axarr[0].imshow(original_img, cmap='gray') 18 | axarr[1].set_title('After Compression and Extraction') 19 | axarr[1].imshow(extracted_img, cmap='gray') 20 | plt.show() 21 | 22 | 23 | def encode_dpcm(seq): 24 | return ( 25 | (item - seq[idx - 1]) & 0xff if idx else item 26 | for idx, item in enumerate(seq) 27 | ) 28 | 29 | 30 | def decode_dpcm(seq): 31 | return itertools.accumulate(seq, lambda x, y: (x + y) & 0xff) 32 | 33 | 34 | def bin_str2bool_list(binary_string): 35 | return [c == '1' for c in binary_string] 36 | 37 | 38 | def bool_list2bin_str(boolean_list): 39 | return ''.join('1' if i else '0' for i in boolean_list) 40 | 41 | 42 | def bool_list2int(boolean_list): 43 | return sum(v << i for i, v in enumerate(reversed(boolean_list))) 44 | 45 | 46 | def entropy(byte_seq): 47 | counter = collections.Counter(byte_seq) 48 | ret = 0 49 | for count in counter.values(): 50 | prob = count / sum(counter.values()) 51 | ret += prob * math.log2(prob) 52 | return -ret 53 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from adaptive_huffman_coding import compress, extract 4 | from adaptive_huffman_coding.utils import show_raw_img 5 | 6 | 7 | logging.basicConfig(level=logging.INFO) 8 | 9 | 10 | def main(): 11 | for filename in ('tests/images/Lena.raw', 'tests/images/Baboon.raw'): 12 | alphabet_range = (0, 255) 13 | dpcm = False 14 | compress(filename, 'compressed', 15 | alphabet_range=alphabet_range, dpcm=dpcm) 16 | extract('compressed', 'extracted.raw', 17 | alphabet_range=alphabet_range, dpcm=dpcm) 18 | show_raw_img(filename, 'extracted.raw', size=(512, 512)) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "astroid" 3 | version = "2.4.2" 4 | description = "An abstract syntax tree for Python with inference support." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=3.5" 8 | 9 | [package.dependencies] 10 | lazy-object-proxy = ">=1.4.0,<1.5.0" 11 | six = ">=1.12,<2.0" 12 | wrapt = ">=1.11,<2.0" 13 | 14 | [[package]] 15 | name = "atomicwrites" 16 | version = "1.4.0" 17 | description = "Atomic file writes." 18 | category = "dev" 19 | optional = false 20 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 21 | 22 | [[package]] 23 | name = "attrs" 24 | version = "20.2.0" 25 | description = "Classes Without Boilerplate" 26 | category = "dev" 27 | optional = false 28 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 29 | 30 | [package.extras] 31 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] 32 | docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] 33 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] 34 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] 35 | 36 | [[package]] 37 | name = "autopep8" 38 | version = "1.5.4" 39 | description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" 40 | category = "dev" 41 | optional = false 42 | python-versions = "*" 43 | 44 | [package.dependencies] 45 | pycodestyle = ">=2.6.0" 46 | toml = "*" 47 | 48 | [[package]] 49 | name = "bitarray" 50 | version = "1.6.0" 51 | description = "efficient arrays of booleans -- C extension" 52 | category = "main" 53 | optional = false 54 | python-versions = "*" 55 | 56 | [[package]] 57 | name = "certifi" 58 | version = "2020.6.20" 59 | description = "Python package for providing Mozilla's CA Bundle." 60 | category = "main" 61 | optional = false 62 | python-versions = "*" 63 | 64 | [[package]] 65 | name = "colorama" 66 | version = "0.4.3" 67 | description = "Cross-platform colored terminal text." 68 | category = "dev" 69 | optional = false 70 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 71 | 72 | [[package]] 73 | name = "coverage" 74 | version = "5.2.1" 75 | description = "Code coverage measurement for Python" 76 | category = "dev" 77 | optional = false 78 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 79 | 80 | [package.extras] 81 | toml = ["toml"] 82 | 83 | [[package]] 84 | name = "cycler" 85 | version = "0.10.0" 86 | description = "Composable style cycles" 87 | category = "main" 88 | optional = false 89 | python-versions = "*" 90 | 91 | [package.dependencies] 92 | six = "*" 93 | 94 | [[package]] 95 | name = "iniconfig" 96 | version = "1.0.1" 97 | description = "iniconfig: brain-dead simple config-ini parsing" 98 | category = "dev" 99 | optional = false 100 | python-versions = "*" 101 | 102 | [[package]] 103 | name = "isort" 104 | version = "5.5.2" 105 | description = "A Python utility / library to sort Python imports." 106 | category = "dev" 107 | optional = false 108 | python-versions = ">=3.6,<4.0" 109 | 110 | [package.extras] 111 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 112 | requirements_deprecated_finder = ["pipreqs", "pip-api"] 113 | colors = ["colorama (>=0.4.3,<0.5.0)"] 114 | 115 | [[package]] 116 | name = "kiwisolver" 117 | version = "1.2.0" 118 | description = "A fast implementation of the Cassowary constraint solver" 119 | category = "main" 120 | optional = false 121 | python-versions = ">=3.6" 122 | 123 | [[package]] 124 | name = "lazy-object-proxy" 125 | version = "1.4.3" 126 | description = "A fast and thorough lazy object proxy." 127 | category = "dev" 128 | optional = false 129 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 130 | 131 | [[package]] 132 | name = "matplotlib" 133 | version = "3.3.2" 134 | description = "Python plotting package" 135 | category = "main" 136 | optional = false 137 | python-versions = ">=3.6" 138 | 139 | [package.dependencies] 140 | certifi = ">=2020.06.20" 141 | cycler = ">=0.10" 142 | kiwisolver = ">=1.0.1" 143 | numpy = ">=1.15" 144 | pillow = ">=6.2.0" 145 | pyparsing = ">=2.0.3,<2.0.4 || >2.0.4,<2.1.2 || >2.1.2,<2.1.6 || >2.1.6" 146 | python-dateutil = ">=2.1" 147 | 148 | [[package]] 149 | name = "mccabe" 150 | version = "0.6.1" 151 | description = "McCabe checker, plugin for flake8" 152 | category = "dev" 153 | optional = false 154 | python-versions = "*" 155 | 156 | [[package]] 157 | name = "mypy" 158 | version = "0.790" 159 | description = "Optional static typing for Python" 160 | category = "dev" 161 | optional = false 162 | python-versions = ">=3.5" 163 | 164 | [package.dependencies] 165 | mypy-extensions = ">=0.4.3,<0.5.0" 166 | typed-ast = ">=1.4.0,<1.5.0" 167 | typing-extensions = ">=3.7.4" 168 | 169 | [package.extras] 170 | dmypy = ["psutil (>=4.0)"] 171 | 172 | [[package]] 173 | name = "mypy-extensions" 174 | version = "0.4.3" 175 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 176 | category = "dev" 177 | optional = false 178 | python-versions = "*" 179 | 180 | [[package]] 181 | name = "numpy" 182 | version = "1.19.2" 183 | description = "NumPy is the fundamental package for array computing with Python." 184 | category = "main" 185 | optional = false 186 | python-versions = ">=3.6" 187 | 188 | [[package]] 189 | name = "packaging" 190 | version = "20.4" 191 | description = "Core utilities for Python packages" 192 | category = "dev" 193 | optional = false 194 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 195 | 196 | [package.dependencies] 197 | pyparsing = ">=2.0.2" 198 | six = "*" 199 | 200 | [[package]] 201 | name = "pillow" 202 | version = "7.2.0" 203 | description = "Python Imaging Library (Fork)" 204 | category = "main" 205 | optional = false 206 | python-versions = ">=3.5" 207 | 208 | [[package]] 209 | name = "pluggy" 210 | version = "0.13.1" 211 | description = "plugin and hook calling mechanisms for python" 212 | category = "dev" 213 | optional = false 214 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 215 | 216 | [package.extras] 217 | dev = ["pre-commit", "tox"] 218 | 219 | [[package]] 220 | name = "progress" 221 | version = "1.5" 222 | description = "Easy to use progress bars" 223 | category = "main" 224 | optional = false 225 | python-versions = "*" 226 | 227 | [[package]] 228 | name = "py" 229 | version = "1.9.0" 230 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 231 | category = "dev" 232 | optional = false 233 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 234 | 235 | [[package]] 236 | name = "pycodestyle" 237 | version = "2.6.0" 238 | description = "Python style guide checker" 239 | category = "dev" 240 | optional = false 241 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 242 | 243 | [[package]] 244 | name = "pylint" 245 | version = "2.6.0" 246 | description = "python code static checker" 247 | category = "dev" 248 | optional = false 249 | python-versions = ">=3.5.*" 250 | 251 | [package.dependencies] 252 | astroid = ">=2.4.0,<=2.5" 253 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 254 | isort = ">=4.2.5,<6" 255 | mccabe = ">=0.6,<0.7" 256 | toml = ">=0.7.1" 257 | 258 | [[package]] 259 | name = "pyparsing" 260 | version = "2.4.7" 261 | description = "Python parsing module" 262 | category = "main" 263 | optional = false 264 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 265 | 266 | [[package]] 267 | name = "pytest" 268 | version = "6.1.1" 269 | description = "pytest: simple powerful testing with Python" 270 | category = "dev" 271 | optional = false 272 | python-versions = ">=3.5" 273 | 274 | [package.dependencies] 275 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 276 | attrs = ">=17.4.0" 277 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 278 | iniconfig = "*" 279 | packaging = "*" 280 | pluggy = ">=0.12,<1.0" 281 | py = ">=1.8.2" 282 | toml = "*" 283 | 284 | [package.extras] 285 | checkqa_mypy = ["mypy (0.780)"] 286 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 287 | 288 | [[package]] 289 | name = "pytest-cov" 290 | version = "2.10.1" 291 | description = "Pytest plugin for measuring coverage." 292 | category = "dev" 293 | optional = false 294 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 295 | 296 | [package.dependencies] 297 | coverage = ">=4.4" 298 | pytest = ">=4.6" 299 | 300 | [package.extras] 301 | testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"] 302 | 303 | [[package]] 304 | name = "python-dateutil" 305 | version = "2.8.1" 306 | description = "Extensions to the standard Python datetime module" 307 | category = "main" 308 | optional = false 309 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 310 | 311 | [package.dependencies] 312 | six = ">=1.5" 313 | 314 | [[package]] 315 | name = "six" 316 | version = "1.15.0" 317 | description = "Python 2 and 3 compatibility utilities" 318 | category = "main" 319 | optional = false 320 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 321 | 322 | [[package]] 323 | name = "toml" 324 | version = "0.10.1" 325 | description = "Python Library for Tom's Obvious, Minimal Language" 326 | category = "dev" 327 | optional = false 328 | python-versions = "*" 329 | 330 | [[package]] 331 | name = "typed-ast" 332 | version = "1.4.1" 333 | description = "a fork of Python 2 and 3 ast modules with type comment support" 334 | category = "dev" 335 | optional = false 336 | python-versions = "*" 337 | 338 | [[package]] 339 | name = "typing-extensions" 340 | version = "3.7.4.3" 341 | description = "Backported and Experimental Type Hints for Python 3.5+" 342 | category = "dev" 343 | optional = false 344 | python-versions = "*" 345 | 346 | [[package]] 347 | name = "wrapt" 348 | version = "1.12.1" 349 | description = "Module for decorators, wrappers and monkey patching." 350 | category = "dev" 351 | optional = false 352 | python-versions = "*" 353 | 354 | [metadata] 355 | lock-version = "1.1" 356 | python-versions = "^3.8" 357 | content-hash = "7eeac9e29175a72bf27aebc2cb5126e404a1ca48e6388a1d200532a366479069" 358 | 359 | [metadata.files] 360 | astroid = [ 361 | {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"}, 362 | {file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"}, 363 | ] 364 | atomicwrites = [ 365 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 366 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 367 | ] 368 | attrs = [ 369 | {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"}, 370 | {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"}, 371 | ] 372 | autopep8 = [ 373 | {file = "autopep8-1.5.4.tar.gz", hash = "sha256:d21d3901cb0da6ebd1e83fc9b0dfbde8b46afc2ede4fe32fbda0c7c6118ca094"}, 374 | ] 375 | bitarray = [ 376 | {file = "bitarray-1.6.0.tar.gz", hash = "sha256:ba157ddebddc723fe021fc80595b3c70924d69ee58286b62bfca21da48edfc9d"}, 377 | ] 378 | certifi = [ 379 | {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, 380 | {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, 381 | ] 382 | colorama = [ 383 | {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, 384 | {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, 385 | ] 386 | coverage = [ 387 | {file = "coverage-5.2.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4"}, 388 | {file = "coverage-5.2.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01"}, 389 | {file = "coverage-5.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8"}, 390 | {file = "coverage-5.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59"}, 391 | {file = "coverage-5.2.1-cp27-cp27m-win32.whl", hash = "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3"}, 392 | {file = "coverage-5.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f"}, 393 | {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd"}, 394 | {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651"}, 395 | {file = "coverage-5.2.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b"}, 396 | {file = "coverage-5.2.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d"}, 397 | {file = "coverage-5.2.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3"}, 398 | {file = "coverage-5.2.1-cp35-cp35m-win32.whl", hash = "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"}, 399 | {file = "coverage-5.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962"}, 400 | {file = "coverage-5.2.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082"}, 401 | {file = "coverage-5.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716"}, 402 | {file = "coverage-5.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb"}, 403 | {file = "coverage-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d"}, 404 | {file = "coverage-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546"}, 405 | {file = "coverage-5.2.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811"}, 406 | {file = "coverage-5.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258"}, 407 | {file = "coverage-5.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034"}, 408 | {file = "coverage-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46"}, 409 | {file = "coverage-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8"}, 410 | {file = "coverage-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0"}, 411 | {file = "coverage-5.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd"}, 412 | {file = "coverage-5.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b"}, 413 | {file = "coverage-5.2.1-cp38-cp38-win32.whl", hash = "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd"}, 414 | {file = "coverage-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d"}, 415 | {file = "coverage-5.2.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3"}, 416 | {file = "coverage-5.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4"}, 417 | {file = "coverage-5.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4"}, 418 | {file = "coverage-5.2.1-cp39-cp39-win32.whl", hash = "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89"}, 419 | {file = "coverage-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b"}, 420 | {file = "coverage-5.2.1.tar.gz", hash = "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b"}, 421 | ] 422 | cycler = [ 423 | {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, 424 | {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, 425 | ] 426 | iniconfig = [ 427 | {file = "iniconfig-1.0.1-py3-none-any.whl", hash = "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437"}, 428 | {file = "iniconfig-1.0.1.tar.gz", hash = "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"}, 429 | ] 430 | isort = [ 431 | {file = "isort-5.5.2-py3-none-any.whl", hash = "sha256:ba91218eee31f1e300ecc079ef0c524cea3fc41bfbb979cbdf5fd3a889e3cfed"}, 432 | {file = "isort-5.5.2.tar.gz", hash = "sha256:171c5f365791073426b5ed3a156c2081a47f88c329161fd28228ff2da4c97ddb"}, 433 | ] 434 | kiwisolver = [ 435 | {file = "kiwisolver-1.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:443c2320520eda0a5b930b2725b26f6175ca4453c61f739fef7a5847bd262f74"}, 436 | {file = "kiwisolver-1.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:efcf3397ae1e3c3a4a0a0636542bcad5adad3b1dd3e8e629d0b6e201347176c8"}, 437 | {file = "kiwisolver-1.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fccefc0d36a38c57b7bd233a9b485e2f1eb71903ca7ad7adacad6c28a56d62d2"}, 438 | {file = "kiwisolver-1.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:be046da49fbc3aa9491cc7296db7e8d27bcf0c3d5d1a40259c10471b014e4e0c"}, 439 | {file = "kiwisolver-1.2.0-cp36-none-win32.whl", hash = "sha256:60a78858580761fe611d22127868f3dc9f98871e6fdf0a15cc4203ed9ba6179b"}, 440 | {file = "kiwisolver-1.2.0-cp36-none-win_amd64.whl", hash = "sha256:556da0a5f60f6486ec4969abbc1dd83cf9b5c2deadc8288508e55c0f5f87d29c"}, 441 | {file = "kiwisolver-1.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7cc095a4661bdd8a5742aaf7c10ea9fac142d76ff1770a0f84394038126d8fc7"}, 442 | {file = "kiwisolver-1.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c955791d80e464da3b471ab41eb65cf5a40c15ce9b001fdc5bbc241170de58ec"}, 443 | {file = "kiwisolver-1.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:603162139684ee56bcd57acc74035fceed7dd8d732f38c0959c8bd157f913fec"}, 444 | {file = "kiwisolver-1.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:63f55f490b958b6299e4e5bdac66ac988c3d11b7fafa522800359075d4fa56d1"}, 445 | {file = "kiwisolver-1.2.0-cp37-none-win32.whl", hash = "sha256:03662cbd3e6729f341a97dd2690b271e51a67a68322affab12a5b011344b973c"}, 446 | {file = "kiwisolver-1.2.0-cp37-none-win_amd64.whl", hash = "sha256:4eadb361baf3069f278b055e3bb53fa189cea2fd02cb2c353b7a99ebb4477ef1"}, 447 | {file = "kiwisolver-1.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c31bc3c8e903d60a1ea31a754c72559398d91b5929fcb329b1c3a3d3f6e72113"}, 448 | {file = "kiwisolver-1.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d52b989dc23cdaa92582ceb4af8d5bcc94d74b2c3e64cd6785558ec6a879793e"}, 449 | {file = "kiwisolver-1.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:e586b28354d7b6584d8973656a7954b1c69c93f708c0c07b77884f91640b7657"}, 450 | {file = "kiwisolver-1.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:38d05c9ecb24eee1246391820ed7137ac42a50209c203c908154782fced90e44"}, 451 | {file = "kiwisolver-1.2.0-cp38-none-win32.whl", hash = "sha256:d069ef4b20b1e6b19f790d00097a5d5d2c50871b66d10075dab78938dc2ee2cf"}, 452 | {file = "kiwisolver-1.2.0-cp38-none-win_amd64.whl", hash = "sha256:18d749f3e56c0480dccd1714230da0f328e6e4accf188dd4e6884bdd06bf02dd"}, 453 | {file = "kiwisolver-1.2.0.tar.gz", hash = "sha256:247800260cd38160c362d211dcaf4ed0f7816afb5efe56544748b21d6ad6d17f"}, 454 | ] 455 | lazy-object-proxy = [ 456 | {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, 457 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"}, 458 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win32.whl", hash = "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4"}, 459 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a"}, 460 | {file = "lazy_object_proxy-1.4.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d"}, 461 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a"}, 462 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win32.whl", hash = "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e"}, 463 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win_amd64.whl", hash = "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357"}, 464 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50"}, 465 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db"}, 466 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449"}, 467 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156"}, 468 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531"}, 469 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb"}, 470 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08"}, 471 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383"}, 472 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142"}, 473 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea"}, 474 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62"}, 475 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"}, 476 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"}, 477 | ] 478 | matplotlib = [ 479 | {file = "matplotlib-3.3.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:27f9de4784ae6fb97679556c5542cf36c0751dccb4d6407f7c62517fa2078868"}, 480 | {file = "matplotlib-3.3.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:06866c138d81a593b535d037b2727bec9b0818cadfe6a81f6ec5715b8dd38a89"}, 481 | {file = "matplotlib-3.3.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5ccecb5f78b51b885f0028b646786889f49c54883e554fca41a2a05998063f23"}, 482 | {file = "matplotlib-3.3.2-cp36-cp36m-win32.whl", hash = "sha256:69cf76d673682140f46c6cb5e073332c1f1b2853c748dc1cb04f7d00023567f7"}, 483 | {file = "matplotlib-3.3.2-cp36-cp36m-win_amd64.whl", hash = "sha256:371518c769d84af8ec9b7dcb871ac44f7a67ef126dd3a15c88c25458e6b6d205"}, 484 | {file = "matplotlib-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:793e061054662aa27acaff9201cdd510a698541c6e8659eeceb31d66c16facc6"}, 485 | {file = "matplotlib-3.3.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:16b241c3d17be786966495229714de37de04472da472277869b8d5b456a8df00"}, 486 | {file = "matplotlib-3.3.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3fb0409754b26f48045bacd6818e44e38ca9338089f8ba689e2f9344ff2847c7"}, 487 | {file = "matplotlib-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:548cfe81476dbac44db96e9c0b074b6fb333b4d1f12b1ae68dbed47e45166384"}, 488 | {file = "matplotlib-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f0268613073df055bcc6a490de733012f2cf4fe191c1adb74e41cec8add1a165"}, 489 | {file = "matplotlib-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:57be9e21073fc367237b03ecac0d9e4b8ddbe38e86ec4a316857d8d93ac9286c"}, 490 | {file = "matplotlib-3.3.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:be2f0ec62e0939a9dcfd3638c140c5a74fc929ee3fd1f31408ab8633db6e1523"}, 491 | {file = "matplotlib-3.3.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c5d0c2ae3e3ed4e9f46b7c03b40d443601012ffe8eb8dfbb2bd6b2d00509f797"}, 492 | {file = "matplotlib-3.3.2-cp38-cp38-win32.whl", hash = "sha256:a522de31e07ed7d6f954cda3fbd5ca4b8edbfc592a821a7b00291be6f843292e"}, 493 | {file = "matplotlib-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:8bc1d3284dee001f41ec98f59675f4d723683e1cc082830b440b5f081d8e0ade"}, 494 | {file = "matplotlib-3.3.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:799c421bc245a0749c1515b6dea6dc02db0a8c1f42446a0f03b3b82a60a900dc"}, 495 | {file = "matplotlib-3.3.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:2f5eefc17dc2a71318d5a3496313be5c351c0731e8c4c6182c9ac3782cfc4076"}, 496 | {file = "matplotlib-3.3.2.tar.gz", hash = "sha256:3d2edbf59367f03cd9daf42939ca06383a7d7803e3993eb5ff1bee8e8a3fbb6b"}, 497 | ] 498 | mccabe = [ 499 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 500 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 501 | ] 502 | mypy = [ 503 | {file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"}, 504 | {file = "mypy-0.790-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802"}, 505 | {file = "mypy-0.790-cp35-cp35m-win_amd64.whl", hash = "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de"}, 506 | {file = "mypy-0.790-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1"}, 507 | {file = "mypy-0.790-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc"}, 508 | {file = "mypy-0.790-cp36-cp36m-win_amd64.whl", hash = "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7"}, 509 | {file = "mypy-0.790-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"}, 510 | {file = "mypy-0.790-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178"}, 511 | {file = "mypy-0.790-cp37-cp37m-win_amd64.whl", hash = "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324"}, 512 | {file = "mypy-0.790-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01"}, 513 | {file = "mypy-0.790-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666"}, 514 | {file = "mypy-0.790-cp38-cp38-win_amd64.whl", hash = "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea"}, 515 | {file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"}, 516 | {file = "mypy-0.790.tar.gz", hash = "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975"}, 517 | ] 518 | mypy-extensions = [ 519 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 520 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 521 | ] 522 | numpy = [ 523 | {file = "numpy-1.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a"}, 524 | {file = "numpy-1.19.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd"}, 525 | {file = "numpy-1.19.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a"}, 526 | {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e"}, 527 | {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526"}, 528 | {file = "numpy-1.19.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c"}, 529 | {file = "numpy-1.19.2-cp36-cp36m-win32.whl", hash = "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d"}, 530 | {file = "numpy-1.19.2-cp36-cp36m-win_amd64.whl", hash = "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15"}, 531 | {file = "numpy-1.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4"}, 532 | {file = "numpy-1.19.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8"}, 533 | {file = "numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02"}, 534 | {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf"}, 535 | {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716"}, 536 | {file = "numpy-1.19.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45"}, 537 | {file = "numpy-1.19.2-cp37-cp37m-win32.whl", hash = "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a"}, 538 | {file = "numpy-1.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1"}, 539 | {file = "numpy-1.19.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d"}, 540 | {file = "numpy-1.19.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d"}, 541 | {file = "numpy-1.19.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9"}, 542 | {file = "numpy-1.19.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1"}, 543 | {file = "numpy-1.19.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c"}, 544 | {file = "numpy-1.19.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5"}, 545 | {file = "numpy-1.19.2-cp38-cp38-win32.whl", hash = "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b"}, 546 | {file = "numpy-1.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65"}, 547 | {file = "numpy-1.19.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8"}, 548 | {file = "numpy-1.19.2.zip", hash = "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c"}, 549 | ] 550 | packaging = [ 551 | {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, 552 | {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, 553 | ] 554 | pillow = [ 555 | {file = "Pillow-7.2.0-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae"}, 556 | {file = "Pillow-7.2.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f"}, 557 | {file = "Pillow-7.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38"}, 558 | {file = "Pillow-7.2.0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5"}, 559 | {file = "Pillow-7.2.0-cp35-cp35m-win32.whl", hash = "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad"}, 560 | {file = "Pillow-7.2.0-cp35-cp35m-win_amd64.whl", hash = "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f"}, 561 | {file = "Pillow-7.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d"}, 562 | {file = "Pillow-7.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233"}, 563 | {file = "Pillow-7.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f"}, 564 | {file = "Pillow-7.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8"}, 565 | {file = "Pillow-7.2.0-cp36-cp36m-win32.whl", hash = "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a"}, 566 | {file = "Pillow-7.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce"}, 567 | {file = "Pillow-7.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4"}, 568 | {file = "Pillow-7.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727"}, 569 | {file = "Pillow-7.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b"}, 570 | {file = "Pillow-7.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d"}, 571 | {file = "Pillow-7.2.0-cp37-cp37m-win32.whl", hash = "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63"}, 572 | {file = "Pillow-7.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1"}, 573 | {file = "Pillow-7.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6"}, 574 | {file = "Pillow-7.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9"}, 575 | {file = "Pillow-7.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41"}, 576 | {file = "Pillow-7.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8"}, 577 | {file = "Pillow-7.2.0-cp38-cp38-win32.whl", hash = "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f"}, 578 | {file = "Pillow-7.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6"}, 579 | {file = "Pillow-7.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6"}, 580 | {file = "Pillow-7.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117"}, 581 | {file = "Pillow-7.2.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d"}, 582 | {file = "Pillow-7.2.0.tar.gz", hash = "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626"}, 583 | ] 584 | pluggy = [ 585 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 586 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 587 | ] 588 | progress = [ 589 | {file = "progress-1.5.tar.gz", hash = "sha256:69ecedd1d1bbe71bf6313d88d1e6c4d2957b7f1d4f71312c211257f7dae64372"}, 590 | ] 591 | py = [ 592 | {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, 593 | {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, 594 | ] 595 | pycodestyle = [ 596 | {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, 597 | {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, 598 | ] 599 | pylint = [ 600 | {file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"}, 601 | {file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"}, 602 | ] 603 | pyparsing = [ 604 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 605 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 606 | ] 607 | pytest = [ 608 | {file = "pytest-6.1.1-py3-none-any.whl", hash = "sha256:7a8190790c17d79a11f847fba0b004ee9a8122582ebff4729a082c109e81a4c9"}, 609 | {file = "pytest-6.1.1.tar.gz", hash = "sha256:8f593023c1a0f916110285b6efd7f99db07d59546e3d8c36fc60e2ab05d3be92"}, 610 | ] 611 | pytest-cov = [ 612 | {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, 613 | {file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"}, 614 | ] 615 | python-dateutil = [ 616 | {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, 617 | {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, 618 | ] 619 | six = [ 620 | {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, 621 | {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, 622 | ] 623 | toml = [ 624 | {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, 625 | {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, 626 | ] 627 | typed-ast = [ 628 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, 629 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, 630 | {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, 631 | {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, 632 | {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, 633 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, 634 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, 635 | {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, 636 | {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, 637 | {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, 638 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, 639 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, 640 | {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, 641 | {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, 642 | {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, 643 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, 644 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, 645 | {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, 646 | {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, 647 | {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, 648 | {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"}, 649 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"}, 650 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"}, 651 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"}, 652 | {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"}, 653 | {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"}, 654 | {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, 655 | ] 656 | typing-extensions = [ 657 | {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, 658 | {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, 659 | {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, 660 | ] 661 | wrapt = [ 662 | {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, 663 | ] 664 | -------------------------------------------------------------------------------- /poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | in-project = true -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # A comma-separated list of package or module names from where C extensions may 4 | # be loaded. Extensions are loading into the active Python interpreter and may 5 | # run arbitrary code. 6 | extension-pkg-whitelist= 7 | 8 | # Specify a score threshold to be exceeded before program exits with error. 9 | fail-under=10 10 | 11 | # Add files or directories to the blacklist. They should be base names, not 12 | # paths. 13 | ignore=CVS 14 | 15 | # Add files or directories matching the regex patterns to the blacklist. The 16 | # regex matches against base names, not paths. 17 | ignore-patterns= 18 | 19 | # Python code to execute, usually for sys.path manipulation such as 20 | # pygtk.require(). 21 | #init-hook= 22 | 23 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 24 | # number of processors available to use. 25 | jobs=1 26 | 27 | # Control the amount of potential inferred values when inferring a single 28 | # object. This can help the performance when dealing with large functions or 29 | # complex, nested conditions. 30 | limit-inference-results=100 31 | 32 | # List of plugins (as comma separated values of python module names) to load, 33 | # usually to register additional checkers. 34 | load-plugins= 35 | 36 | # Pickle collected data for later comparisons. 37 | persistent=yes 38 | 39 | # When enabled, pylint would attempt to guess common misconfiguration and emit 40 | # user-friendly hints instead of false-positive error messages. 41 | suggestion-mode=yes 42 | 43 | # Allow loading of arbitrary C extensions. Extensions are imported into the 44 | # active Python interpreter and may run arbitrary code. 45 | unsafe-load-any-extension=no 46 | 47 | 48 | [MESSAGES CONTROL] 49 | 50 | # Only show warnings with the listed confidence levels. Leave empty to show 51 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 52 | confidence= 53 | 54 | # Disable the message, report, category or checker with the given id(s). You 55 | # can either give multiple identifiers separated by comma (,) or put this 56 | # option multiple times (only on the command line, not in the configuration 57 | # file where it should appear only once). You can also use "--disable=all" to 58 | # disable everything first and then reenable specific checks. For example, if 59 | # you want to run only the similarities checker, you can use "--disable=all 60 | # --enable=similarities". If you want to run only the classes checker, but have 61 | # no Warning level messages displayed, use "--disable=all --enable=classes 62 | # --disable=W". 63 | disable=print-statement, 64 | parameter-unpacking, 65 | unpacking-in-except, 66 | old-raise-syntax, 67 | backtick, 68 | long-suffix, 69 | old-ne-operator, 70 | old-octal-literal, 71 | import-star-module-level, 72 | non-ascii-bytes-literal, 73 | raw-checker-failed, 74 | bad-inline-option, 75 | locally-disabled, 76 | file-ignored, 77 | suppressed-message, 78 | useless-suppression, 79 | deprecated-pragma, 80 | use-symbolic-message-instead, 81 | apply-builtin, 82 | basestring-builtin, 83 | buffer-builtin, 84 | cmp-builtin, 85 | coerce-builtin, 86 | execfile-builtin, 87 | file-builtin, 88 | long-builtin, 89 | raw_input-builtin, 90 | reduce-builtin, 91 | standarderror-builtin, 92 | unicode-builtin, 93 | xrange-builtin, 94 | coerce-method, 95 | delslice-method, 96 | getslice-method, 97 | setslice-method, 98 | no-absolute-import, 99 | old-division, 100 | dict-iter-method, 101 | dict-view-method, 102 | next-method-called, 103 | metaclass-assignment, 104 | indexing-exception, 105 | raising-string, 106 | reload-builtin, 107 | oct-method, 108 | hex-method, 109 | nonzero-method, 110 | cmp-method, 111 | input-builtin, 112 | round-builtin, 113 | intern-builtin, 114 | unichr-builtin, 115 | map-builtin-not-iterating, 116 | zip-builtin-not-iterating, 117 | range-builtin-not-iterating, 118 | filter-builtin-not-iterating, 119 | using-cmp-argument, 120 | eq-without-hash, 121 | div-method, 122 | idiv-method, 123 | rdiv-method, 124 | exception-message-attribute, 125 | invalid-str-codec, 126 | sys-max-int, 127 | bad-python3-import, 128 | deprecated-string-function, 129 | deprecated-str-translate-call, 130 | deprecated-itertools-function, 131 | deprecated-types-field, 132 | next-method-defined, 133 | dict-items-not-iterating, 134 | dict-keys-not-iterating, 135 | dict-values-not-iterating, 136 | deprecated-operator-function, 137 | deprecated-urllib-function, 138 | xreadlines-attribute, 139 | deprecated-sys-function, 140 | exception-escape, 141 | comprehension-escape, 142 | missing-module-docstring, 143 | missing-class-docstring, 144 | missing-function-docstring 145 | 146 | # Enable the message, report, category or checker with the given id(s). You can 147 | # either give multiple identifier separated by comma (,) or put this option 148 | # multiple time (only on the command line, not in the configuration file where 149 | # it should appear only once). See also the "--disable" option for examples. 150 | enable=c-extension-no-member 151 | 152 | 153 | [REPORTS] 154 | 155 | # Python expression which should return a score less than or equal to 10. You 156 | # have access to the variables 'error', 'warning', 'refactor', and 'convention' 157 | # which contain the number of messages in each category, as well as 'statement' 158 | # which is the total number of statements analyzed. This score is used by the 159 | # global evaluation report (RP0004). 160 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 161 | 162 | # Template used to display messages. This is a python new-style format string 163 | # used to format the message information. See doc for all details. 164 | #msg-template= 165 | 166 | # Set the output format. Available formats are text, parseable, colorized, json 167 | # and msvs (visual studio). You can also give a reporter class, e.g. 168 | # mypackage.mymodule.MyReporterClass. 169 | output-format=text 170 | 171 | # Tells whether to display a full report or only the messages. 172 | reports=no 173 | 174 | # Activate the evaluation score. 175 | score=yes 176 | 177 | 178 | [REFACTORING] 179 | 180 | # Maximum number of nested blocks for function / method body 181 | max-nested-blocks=5 182 | 183 | # Complete name of functions that never returns. When checking for 184 | # inconsistent-return-statements if a never returning function is called then 185 | # it will be considered as an explicit return statement and no message will be 186 | # printed. 187 | never-returning-functions=sys.exit 188 | 189 | 190 | [LOGGING] 191 | 192 | # The type of string formatting that logging methods do. `old` means using % 193 | # formatting, `new` is for `{}` formatting. 194 | logging-format-style=old 195 | 196 | # Logging modules to check that the string format arguments are in logging 197 | # function parameter format. 198 | logging-modules=logging 199 | 200 | 201 | [BASIC] 202 | 203 | # Naming style matching correct argument names. 204 | argument-naming-style=snake_case 205 | 206 | # Regular expression matching correct argument names. Overrides argument- 207 | # naming-style. 208 | #argument-rgx= 209 | 210 | # Naming style matching correct attribute names. 211 | attr-naming-style=snake_case 212 | 213 | # Regular expression matching correct attribute names. Overrides attr-naming- 214 | # style. 215 | #attr-rgx= 216 | 217 | # Bad variable names which should always be refused, separated by a comma. 218 | bad-names=foo, 219 | bar, 220 | baz, 221 | toto, 222 | tutu, 223 | tata 224 | 225 | # Bad variable names regexes, separated by a comma. If names match any regex, 226 | # they will always be refused 227 | bad-names-rgxs= 228 | 229 | # Naming style matching correct class attribute names. 230 | class-attribute-naming-style=any 231 | 232 | # Regular expression matching correct class attribute names. Overrides class- 233 | # attribute-naming-style. 234 | #class-attribute-rgx= 235 | 236 | # Naming style matching correct class names. 237 | class-naming-style=PascalCase 238 | 239 | # Regular expression matching correct class names. Overrides class-naming- 240 | # style. 241 | #class-rgx= 242 | 243 | # Naming style matching correct constant names. 244 | const-naming-style=UPPER_CASE 245 | 246 | # Regular expression matching correct constant names. Overrides const-naming- 247 | # style. 248 | #const-rgx= 249 | 250 | # Minimum line length for functions/classes that require docstrings, shorter 251 | # ones are exempt. 252 | docstring-min-length=-1 253 | 254 | # Naming style matching correct function names. 255 | function-naming-style=snake_case 256 | 257 | # Regular expression matching correct function names. Overrides function- 258 | # naming-style. 259 | #function-rgx= 260 | 261 | # Good variable names which should always be accepted, separated by a comma. 262 | good-names=i, 263 | j, 264 | k, 265 | ex, 266 | Run, 267 | _ 268 | 269 | # Good variable names regexes, separated by a comma. If names match any regex, 270 | # they will always be accepted 271 | good-names-rgxs= 272 | 273 | # Include a hint for the correct naming format with invalid-name. 274 | include-naming-hint=no 275 | 276 | # Naming style matching correct inline iteration names. 277 | inlinevar-naming-style=any 278 | 279 | # Regular expression matching correct inline iteration names. Overrides 280 | # inlinevar-naming-style. 281 | #inlinevar-rgx= 282 | 283 | # Naming style matching correct method names. 284 | method-naming-style=snake_case 285 | 286 | # Regular expression matching correct method names. Overrides method-naming- 287 | # style. 288 | #method-rgx= 289 | 290 | # Naming style matching correct module names. 291 | module-naming-style=snake_case 292 | 293 | # Regular expression matching correct module names. Overrides module-naming- 294 | # style. 295 | #module-rgx= 296 | 297 | # Colon-delimited sets of names that determine each other's naming style when 298 | # the name regexes allow several styles. 299 | name-group= 300 | 301 | # Regular expression which should only match function or class names that do 302 | # not require a docstring. 303 | no-docstring-rgx=^_ 304 | 305 | # List of decorators that produce properties, such as abc.abstractproperty. Add 306 | # to this list to register other decorators that produce valid properties. 307 | # These decorators are taken in consideration only for invalid-name. 308 | property-classes=abc.abstractproperty 309 | 310 | # Naming style matching correct variable names. 311 | variable-naming-style=snake_case 312 | 313 | # Regular expression matching correct variable names. Overrides variable- 314 | # naming-style. 315 | #variable-rgx= 316 | 317 | 318 | [SPELLING] 319 | 320 | # Limits count of emitted suggestions for spelling mistakes. 321 | max-spelling-suggestions=4 322 | 323 | # Spelling dictionary name. Available dictionaries: none. To make it work, 324 | # install the python-enchant package. 325 | spelling-dict= 326 | 327 | # List of comma separated words that should not be checked. 328 | spelling-ignore-words= 329 | 330 | # A path to a file that contains the private dictionary; one word per line. 331 | spelling-private-dict-file= 332 | 333 | # Tells whether to store unknown words to the private dictionary (see the 334 | # --spelling-private-dict-file option) instead of raising a message. 335 | spelling-store-unknown-words=no 336 | 337 | 338 | [TYPECHECK] 339 | 340 | # List of decorators that produce context managers, such as 341 | # contextlib.contextmanager. Add to this list to register other decorators that 342 | # produce valid context managers. 343 | contextmanager-decorators=contextlib.contextmanager 344 | 345 | # List of members which are set dynamically and missed by pylint inference 346 | # system, and so shouldn't trigger E1101 when accessed. Python regular 347 | # expressions are accepted. 348 | generated-members= 349 | 350 | # Tells whether missing members accessed in mixin class should be ignored. A 351 | # mixin class is detected if its name ends with "mixin" (case insensitive). 352 | ignore-mixin-members=yes 353 | 354 | # Tells whether to warn about missing members when the owner of the attribute 355 | # is inferred to be None. 356 | ignore-none=yes 357 | 358 | # This flag controls whether pylint should warn about no-member and similar 359 | # checks whenever an opaque object is returned when inferring. The inference 360 | # can return multiple potential results while evaluating a Python object, but 361 | # some branches might not be evaluated, which results in partial inference. In 362 | # that case, it might be useful to still emit no-member and other checks for 363 | # the rest of the inferred objects. 364 | ignore-on-opaque-inference=yes 365 | 366 | # List of class names for which member attributes should not be checked (useful 367 | # for classes with dynamically set attributes). This supports the use of 368 | # qualified names. 369 | ignored-classes=optparse.Values,thread._local,_thread._local 370 | 371 | # List of module names for which member attributes should not be checked 372 | # (useful for modules/projects where namespaces are manipulated during runtime 373 | # and thus existing member attributes cannot be deduced by static analysis). It 374 | # supports qualified module names, as well as Unix pattern matching. 375 | ignored-modules= 376 | 377 | # Show a hint with possible names when a member name was not found. The aspect 378 | # of finding the hint is based on edit distance. 379 | missing-member-hint=yes 380 | 381 | # The minimum edit distance a name should have in order to be considered a 382 | # similar match for a missing member name. 383 | missing-member-hint-distance=1 384 | 385 | # The total number of similar names that should be taken in consideration when 386 | # showing a hint for a missing member. 387 | missing-member-max-choices=1 388 | 389 | # List of decorators that change the signature of a decorated function. 390 | signature-mutators= 391 | 392 | 393 | [VARIABLES] 394 | 395 | # List of additional names supposed to be defined in builtins. Remember that 396 | # you should avoid defining new builtins when possible. 397 | additional-builtins= 398 | 399 | # Tells whether unused global variables should be treated as a violation. 400 | allow-global-unused-variables=yes 401 | 402 | # List of strings which can identify a callback function by name. A callback 403 | # name must start or end with one of those strings. 404 | callbacks=cb_, 405 | _cb 406 | 407 | # A regular expression matching the name of dummy variables (i.e. expected to 408 | # not be used). 409 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 410 | 411 | # Argument names that match this expression will be ignored. Default to name 412 | # with leading underscore. 413 | ignored-argument-names=_.*|^ignored_|^unused_ 414 | 415 | # Tells whether we should check for unused import in __init__ files. 416 | init-import=no 417 | 418 | # List of qualified module names which can have objects that can redefine 419 | # builtins. 420 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 421 | 422 | 423 | [FORMAT] 424 | 425 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 426 | expected-line-ending-format= 427 | 428 | # Regexp for a line that is allowed to be longer than the limit. 429 | ignore-long-lines=^\s*(# )??$ 430 | 431 | # Number of spaces of indent required inside a hanging or continued line. 432 | indent-after-paren=4 433 | 434 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 435 | # tab). 436 | indent-string=' ' 437 | 438 | # Maximum number of characters on a single line. 439 | max-line-length=100 440 | 441 | # Maximum number of lines in a module. 442 | max-module-lines=1000 443 | 444 | # List of optional constructs for which whitespace checking is disabled. `dict- 445 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 446 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 447 | # `empty-line` allows space-only lines. 448 | no-space-check=trailing-comma, 449 | dict-separator 450 | 451 | # Allow the body of a class to be on the same line as the declaration if body 452 | # contains single statement. 453 | single-line-class-stmt=no 454 | 455 | # Allow the body of an if to be on the same line as the test if there is no 456 | # else. 457 | single-line-if-stmt=no 458 | 459 | 460 | [SIMILARITIES] 461 | 462 | # Ignore comments when computing similarities. 463 | ignore-comments=yes 464 | 465 | # Ignore docstrings when computing similarities. 466 | ignore-docstrings=yes 467 | 468 | # Ignore imports when computing similarities. 469 | ignore-imports=no 470 | 471 | # Minimum lines number of a similarity. 472 | min-similarity-lines=4 473 | 474 | 475 | [STRING] 476 | 477 | # This flag controls whether inconsistent-quotes generates a warning when the 478 | # character used as a quote delimiter is used inconsistently within a module. 479 | check-quote-consistency=no 480 | 481 | # This flag controls whether the implicit-str-concat should generate a warning 482 | # on implicit string concatenation in sequences defined over several lines. 483 | check-str-concat-over-line-jumps=no 484 | 485 | 486 | [MISCELLANEOUS] 487 | 488 | # List of note tags to take in consideration, separated by a comma. 489 | notes=FIXME, 490 | XXX, 491 | TODO 492 | 493 | # Regular expression of note tags to take in consideration. 494 | #notes-rgx= 495 | 496 | 497 | [IMPORTS] 498 | 499 | # List of modules that can be imported at any level, not just the top level 500 | # one. 501 | allow-any-import-level= 502 | 503 | # Allow wildcard imports from modules that define __all__. 504 | allow-wildcard-with-all=no 505 | 506 | # Analyse import fallback blocks. This can be used to support both Python 2 and 507 | # 3 compatible code, which means that the block might have code that exists 508 | # only in one or another interpreter, leading to false positives when analysed. 509 | analyse-fallback-blocks=no 510 | 511 | # Deprecated modules which should not be used, separated by a comma. 512 | deprecated-modules=optparse,tkinter.tix 513 | 514 | # Create a graph of external dependencies in the given file (report RP0402 must 515 | # not be disabled). 516 | ext-import-graph= 517 | 518 | # Create a graph of every (i.e. internal and external) dependencies in the 519 | # given file (report RP0402 must not be disabled). 520 | import-graph= 521 | 522 | # Create a graph of internal dependencies in the given file (report RP0402 must 523 | # not be disabled). 524 | int-import-graph= 525 | 526 | # Force import order to recognize a module as part of the standard 527 | # compatibility libraries. 528 | known-standard-library= 529 | 530 | # Force import order to recognize a module as part of a third party library. 531 | known-third-party=enchant 532 | 533 | # Couples of modules and preferred modules, separated by a comma. 534 | preferred-modules= 535 | 536 | 537 | [CLASSES] 538 | 539 | # List of method names used to declare (i.e. assign) instance attributes. 540 | defining-attr-methods=__init__, 541 | __new__, 542 | setUp, 543 | __post_init__ 544 | 545 | # List of member names, which should be excluded from the protected access 546 | # warning. 547 | exclude-protected=_asdict, 548 | _fields, 549 | _replace, 550 | _source, 551 | _make 552 | 553 | # List of valid names for the first argument in a class method. 554 | valid-classmethod-first-arg=cls 555 | 556 | # List of valid names for the first argument in a metaclass class method. 557 | valid-metaclass-classmethod-first-arg=cls 558 | 559 | 560 | [DESIGN] 561 | 562 | # Maximum number of arguments for function / method. 563 | max-args=5 564 | 565 | # Maximum number of attributes for a class (see R0902). 566 | max-attributes=7 567 | 568 | # Maximum number of boolean expressions in an if statement (see R0916). 569 | max-bool-expr=5 570 | 571 | # Maximum number of branch for function / method body. 572 | max-branches=12 573 | 574 | # Maximum number of locals for function / method body. 575 | max-locals=15 576 | 577 | # Maximum number of parents for a class (see R0901). 578 | max-parents=7 579 | 580 | # Maximum number of public methods for a class (see R0904). 581 | max-public-methods=20 582 | 583 | # Maximum number of return / yield for function / method body. 584 | max-returns=6 585 | 586 | # Maximum number of statements in function / method body. 587 | max-statements=50 588 | 589 | # Minimum number of public methods for a class (see R0903). 590 | min-public-methods=2 591 | 592 | 593 | [EXCEPTIONS] 594 | 595 | # Exceptions that will emit a warning when being caught. Defaults to 596 | # "BaseException, Exception". 597 | overgeneral-exceptions=BaseException, 598 | Exception -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "adaptive-huffman-coding" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Sean Wu "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.8" 9 | bitarray = "^1.6.0" 10 | numpy = "^1.19.2" 11 | matplotlib = "^3.3.2" 12 | progress = "^1.5" 13 | 14 | [tool.poetry.dev-dependencies] 15 | pytest = "^6.1" 16 | pylint = "^2.6.0" 17 | mypy = "^0.790" 18 | autopep8 = "^1.5.4" 19 | pytest-cov = "^2.10.1" 20 | 21 | [build-system] 22 | requires = ["poetry>=0.12"] 23 | build-backend = "poetry.masonry.api" 24 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanwu1105/adaptive-huffman-coding/5b0bd0fa1e8ef9806fbcb6092ede0eb46fbbc80b/tests/__init__.py -------------------------------------------------------------------------------- /tests/images/Baboon.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanwu1105/adaptive-huffman-coding/5b0bd0fa1e8ef9806fbcb6092ede0eb46fbbc80b/tests/images/Baboon.raw -------------------------------------------------------------------------------- /tests/images/Lena.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanwu1105/adaptive-huffman-coding/5b0bd0fa1e8ef9806fbcb6092ede0eb46fbbc80b/tests/images/Lena.raw -------------------------------------------------------------------------------- /tests/images/simple.raw: -------------------------------------------------------------------------------- 1 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 2 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 3 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 4 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 5 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 6 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 7 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 8 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 9 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 10 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 11 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 12 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 13 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 14 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 15 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 16 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 17 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 18 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 19 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 20 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 21 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 22 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 23 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 24 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 25 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 26 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 27 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 28 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 29 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 30 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 31 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 32 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 33 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 34 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 35 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 36 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 37 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 38 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 39 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 40 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 41 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 42 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 43 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 44 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 45 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 46 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 47 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 48 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 49 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 50 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 51 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 52 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 53 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 54 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 55 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 56 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 57 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 58 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 59 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 60 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 61 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 62 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 63 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 64 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 65 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 66 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 67 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 68 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 69 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 70 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 71 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 72 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 73 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 74 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 75 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 76 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 77 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 78 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 79 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 80 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 81 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 82 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 83 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 84 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 85 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 86 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 87 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 88 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 89 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 90 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 91 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 92 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 93 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 94 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 95 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 96 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 97 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 98 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 99 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 100 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 101 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 102 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 103 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 104 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 105 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 106 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 107 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 108 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 109 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 110 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 111 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 112 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 113 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 114 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 115 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 116 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 117 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 118 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 119 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 120 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 121 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 122 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 123 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 124 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 125 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 126 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 127 | qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-=qazwsxedcrfvbgtyhnujmik,ol.p;/[]'1234567890-= 128 | -------------------------------------------------------------------------------- /tests/test_adaptive_huffman_coding.py: -------------------------------------------------------------------------------- 1 | import filecmp 2 | import os 3 | import logging 4 | import unittest 5 | 6 | from adaptive_huffman_coding import __version__, compress, extract 7 | 8 | logging.basicConfig(level=logging.DEBUG) 9 | 10 | 11 | def test_version(): 12 | assert __version__ == '0.1.0' 13 | 14 | 15 | class TestCompressionAndExtraction(unittest.TestCase): 16 | in_filenames = ( 17 | 'tests/images/simple.raw', 18 | 'tests/images/Lena.raw', 19 | 'tests/images/Baboon.raw' 20 | ) 21 | compressed_filename = 'compressed' 22 | extracted_filename = 'extracted.raw' 23 | 24 | def test_compress_and_extract(self, dpcm=False): 25 | for filename in self.in_filenames: 26 | alphabet_range = (0, 255) 27 | compress(filename, self.compressed_filename, 28 | alphabet_range=alphabet_range, dpcm=dpcm) 29 | extract(self.compressed_filename, self.extracted_filename, 30 | alphabet_range=alphabet_range, dpcm=dpcm) 31 | if os.path.getsize(filename) > 10000: 32 | self.assertTrue(os.path.getsize( 33 | self.compressed_filename) < os.path.getsize(filename)) 34 | self.assertTrue(filecmp.cmp(filename, self.extracted_filename)) 35 | 36 | def test_compress_and_extract_dpcm(self): 37 | self.test_compress_and_extract(dpcm=True) 38 | --------------------------------------------------------------------------------