From 357e5fd9748895070800b7e9455c7e5d8a172a7c Mon Sep 17 00:00:00 2001 From: Jaivignesh-afk <23f1001347@ds.study.iitm.ac.in> Date: Wed, 18 Oct 2023 02:36:03 +0530 Subject: [PATCH 1/4] Added doctests to power_using_recursion.py --- lempel_ziv.py | 125 +++++++++++++++++++++++++++++++++ maths/power_using_recursion.py | 22 +++++- 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 lempel_ziv.py diff --git a/lempel_ziv.py b/lempel_ziv.py new file mode 100644 index 000000000000..ea6f33944a91 --- /dev/null +++ b/lempel_ziv.py @@ -0,0 +1,125 @@ +""" + One of the several implementations of Lempel–Ziv–Welch compression algorithm + https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch +""" + +import math +import os +import sys + + +def read_file_binary(file_path: str) -> str: + """ + Reads given file as bytes and returns them as a long string + """ + result = "" + try: + with open(file_path, "rb") as binary_file: + data = binary_file.read() + for dat in data: + curr_byte = f"{dat:08b}" + result += curr_byte + return result + except OSError: + print("File not accessible") + sys.exit() + + +def add_key_to_lexicon( + lexicon: dict[str, str], curr_string: str, index: int, last_match_id: str +) -> None: + """ + Adds new strings (curr_string + "0", curr_string + "1") to the lexicon + """ + lexicon.pop(curr_string) + lexicon[curr_string + "0"] = last_match_id + + if math.log2(index).is_integer(): + for curr_key in lexicon: + lexicon[curr_key] = "0" + lexicon[curr_key] + + lexicon[curr_string + "1"] = bin(index)[2:] + + +def compress_data(data_bits: str) -> str: + """ + Compresses given data_bits using Lempel–Ziv–Welch compression algorithm + and returns the result as a string + """ + lexicon = {"0": "0", "1": "1"} + result, curr_string = "", "" + index = len(lexicon) + + for i in range(len(data_bits)): + curr_string += data_bits[i] + if curr_string not in lexicon: + continue + + last_match_id = lexicon[curr_string] + result += last_match_id + add_key_to_lexicon(lexicon, curr_string, index, last_match_id) + index += 1 + curr_string = "" + + while curr_string != "" and curr_string not in lexicon: + curr_string += "0" + + if curr_string != "": + last_match_id = lexicon[curr_string] + result += last_match_id + + return result + + +def add_file_length(source_path: str, compressed: str) -> str: + """ + Adds given file's length in front (using Elias gamma coding) of the compressed + string + """ + file_length = os.path.getsize(source_path) + file_length_binary = bin(file_length)[2:] + length_length = len(file_length_binary) + + return "0" * (length_length - 1) + file_length_binary + compressed + + +def write_file_binary(file_path: str, to_write: str) -> None: + """ + Writes given to_write string (should only consist of 0's and 1's) as bytes in the + file + """ + byte_length = 8 + try: + with open(file_path, "wb") as opened_file: + result_byte_array = [ + to_write[i : i + byte_length] + for i in range(0, len(to_write), byte_length) + ] + + if len(result_byte_array[-1]) % byte_length == 0: + result_byte_array.append("10000000") + else: + result_byte_array[-1] += "1" + "0" * ( + byte_length - len(result_byte_array[-1]) - 1 + ) + + for elem in result_byte_array: + opened_file.write(int(elem, 2).to_bytes(1, byteorder="big")) + except OSError: + print("File not accessible") + sys.exit() + + +def compress(source_path: str, destination_path: str) -> None: + """ + Reads source file, compresses it and writes the compressed result in destination + file + """ + data_bits = read_file_binary(source_path) + compressed = compress_data(data_bits) + compressed = add_file_length(source_path, compressed) + write_file_binary(destination_path, compressed) + + +if __name__ == "__main__": + compress(sys.argv[1], sys.argv[2]) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index f82097f6d8ec..ab39041f01fd 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -15,13 +15,33 @@ def power(base: int, exponent: int) -> float: """ - power(3, 4) + >>> power(3, 4) 81 >>> power(2, 0) 1 + >>> power(0.5, 2) + 0.25 >>> all(power(base, exponent) == pow(base, exponent) ... for base in range(-10, 10) for exponent in range(10)) True + >>> power("b",'a') + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for -: 'str' and 'int' + >>> power('b',2) + Traceback (most recent call last): + ... + TypeError: can't multiply sequence by non-int of type 'str' + >>> power('b',1) + 'b' + >>> power(4,-1) + Traceback (most recent call last): + ... + RecursionError: maximum recursion depth exceeded + >>> power(4,0.5) + Traceback (most recent call last): + ... + RecursionError: maximum recursion depth exceeded """ return base * power(base, (exponent - 1)) if exponent else 1 From 045d90618776af07d07cd0cab266ace40dfb7dfa Mon Sep 17 00:00:00 2001 From: Jaivignesh-afk <23f1001347@ds.study.iitm.ac.in> Date: Wed, 18 Oct 2023 02:55:37 +0530 Subject: [PATCH 2/4] Added doctest to power_using_recursion.py --- lempel_ziv.py | 125 --------------------------------- maths/power_using_recursion.py | 16 ++--- 2 files changed, 5 insertions(+), 136 deletions(-) delete mode 100644 lempel_ziv.py diff --git a/lempel_ziv.py b/lempel_ziv.py deleted file mode 100644 index ea6f33944a91..000000000000 --- a/lempel_ziv.py +++ /dev/null @@ -1,125 +0,0 @@ -""" - One of the several implementations of Lempel–Ziv–Welch compression algorithm - https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch -""" - -import math -import os -import sys - - -def read_file_binary(file_path: str) -> str: - """ - Reads given file as bytes and returns them as a long string - """ - result = "" - try: - with open(file_path, "rb") as binary_file: - data = binary_file.read() - for dat in data: - curr_byte = f"{dat:08b}" - result += curr_byte - return result - except OSError: - print("File not accessible") - sys.exit() - - -def add_key_to_lexicon( - lexicon: dict[str, str], curr_string: str, index: int, last_match_id: str -) -> None: - """ - Adds new strings (curr_string + "0", curr_string + "1") to the lexicon - """ - lexicon.pop(curr_string) - lexicon[curr_string + "0"] = last_match_id - - if math.log2(index).is_integer(): - for curr_key in lexicon: - lexicon[curr_key] = "0" + lexicon[curr_key] - - lexicon[curr_string + "1"] = bin(index)[2:] - - -def compress_data(data_bits: str) -> str: - """ - Compresses given data_bits using Lempel–Ziv–Welch compression algorithm - and returns the result as a string - """ - lexicon = {"0": "0", "1": "1"} - result, curr_string = "", "" - index = len(lexicon) - - for i in range(len(data_bits)): - curr_string += data_bits[i] - if curr_string not in lexicon: - continue - - last_match_id = lexicon[curr_string] - result += last_match_id - add_key_to_lexicon(lexicon, curr_string, index, last_match_id) - index += 1 - curr_string = "" - - while curr_string != "" and curr_string not in lexicon: - curr_string += "0" - - if curr_string != "": - last_match_id = lexicon[curr_string] - result += last_match_id - - return result - - -def add_file_length(source_path: str, compressed: str) -> str: - """ - Adds given file's length in front (using Elias gamma coding) of the compressed - string - """ - file_length = os.path.getsize(source_path) - file_length_binary = bin(file_length)[2:] - length_length = len(file_length_binary) - - return "0" * (length_length - 1) + file_length_binary + compressed - - -def write_file_binary(file_path: str, to_write: str) -> None: - """ - Writes given to_write string (should only consist of 0's and 1's) as bytes in the - file - """ - byte_length = 8 - try: - with open(file_path, "wb") as opened_file: - result_byte_array = [ - to_write[i : i + byte_length] - for i in range(0, len(to_write), byte_length) - ] - - if len(result_byte_array[-1]) % byte_length == 0: - result_byte_array.append("10000000") - else: - result_byte_array[-1] += "1" + "0" * ( - byte_length - len(result_byte_array[-1]) - 1 - ) - - for elem in result_byte_array: - opened_file.write(int(elem, 2).to_bytes(1, byteorder="big")) - except OSError: - print("File not accessible") - sys.exit() - - -def compress(source_path: str, destination_path: str) -> None: - """ - Reads source file, compresses it and writes the compressed result in destination - file - """ - data_bits = read_file_binary(source_path) - compressed = compress_data(data_bits) - compressed = add_file_length(source_path, compressed) - write_file_binary(destination_path, compressed) - - -if __name__ == "__main__": - compress(sys.argv[1], sys.argv[2]) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index ab39041f01fd..dc19af479772 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -19,26 +19,20 @@ def power(base: int, exponent: int) -> float: 81 >>> power(2, 0) 1 - >>> power(0.5, 2) - 0.25 + >>> power('a', 1) + 'a' >>> all(power(base, exponent) == pow(base, exponent) ... for base in range(-10, 10) for exponent in range(10)) True - >>> power("b",'a') + >>> power('a','b') Traceback (most recent call last): ... TypeError: unsupported operand type(s) for -: 'str' and 'int' - >>> power('b',2) + >>> power('a',2) Traceback (most recent call last): ... TypeError: can't multiply sequence by non-int of type 'str' - >>> power('b',1) - 'b' - >>> power(4,-1) - Traceback (most recent call last): - ... - RecursionError: maximum recursion depth exceeded - >>> power(4,0.5) + >>> power(2,-1) Traceback (most recent call last): ... RecursionError: maximum recursion depth exceeded From 87b9d7511f2985de253a5b92c1ce7ffa1bd122a0 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 18 Oct 2023 00:41:03 +0200 Subject: [PATCH 3/4] Update power_using_recursion.py --- maths/power_using_recursion.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index dc19af479772..6a545b3525a7 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -21,18 +21,20 @@ def power(base: int, exponent: int) -> float: 1 >>> power('a', 1) 'a' + >>> power('a', 2) + 'aa' >>> all(power(base, exponent) == pow(base, exponent) ... for base in range(-10, 10) for exponent in range(10)) True - >>> power('a','b') + >>> power('a', 'b') Traceback (most recent call last): ... TypeError: unsupported operand type(s) for -: 'str' and 'int' - >>> power('a',2) + >>> power('a', 2) Traceback (most recent call last): ... TypeError: can't multiply sequence by non-int of type 'str' - >>> power(2,-1) + >>> power(2, -1) Traceback (most recent call last): ... RecursionError: maximum recursion depth exceeded @@ -41,6 +43,9 @@ def power(base: int, exponent: int) -> float: if __name__ == "__main__": + from doctests import testmod + + testmod() print("Raise base to the power of exponent using recursion...") base = int(input("Enter the base: ").strip()) exponent = int(input("Enter the exponent: ").strip()) From 987913bea88d3f8f9fe9ab9db63aadc85fadd383 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 18 Oct 2023 00:47:10 +0200 Subject: [PATCH 4/4] Update power_using_recursion.py --- maths/power_using_recursion.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/maths/power_using_recursion.py b/maths/power_using_recursion.py index 6a545b3525a7..e82635ba0005 100644 --- a/maths/power_using_recursion.py +++ b/maths/power_using_recursion.py @@ -19,21 +19,19 @@ def power(base: int, exponent: int) -> float: 81 >>> power(2, 0) 1 - >>> power('a', 1) - 'a' - >>> power('a', 2) - 'aa' >>> all(power(base, exponent) == pow(base, exponent) ... for base in range(-10, 10) for exponent in range(10)) True - >>> power('a', 'b') - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for -: 'str' and 'int' + >>> power('a', 1) + 'a' >>> power('a', 2) Traceback (most recent call last): ... TypeError: can't multiply sequence by non-int of type 'str' + >>> power('a', 'b') + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for -: 'str' and 'int' >>> power(2, -1) Traceback (most recent call last): ...