From 5c06ea00dec691c350c8dbb7f545277513af9736 Mon Sep 17 00:00:00 2001 From: Maxim Rebguns Date: Wed, 6 May 2020 21:20:50 -0500 Subject: [PATCH 1/9] Added more flexibility to functions, decreased amount of repeating code --- ciphers/caesar_cipher.py | 101 +++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 4427a5234d70..c4f9c8bed40e 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -1,63 +1,90 @@ -def encrypt(input_string: str, key: int) -> str: - result = "" - for x in input_string: - if not x.isalpha(): - result += x - elif x.isupper(): - result += chr((ord(x) + key - 65) % 26 + 65) - elif x.islower(): - result += chr((ord(x) + key - 97) % 26 + 97) - return result +from itertools import chain + +def encrypt(input_string: str, key: int, alphabet=None) -> str: + # Set default alphabet to lower and upper case english chars + alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] -def decrypt(input_string: str, key: int) -> str: + # The final result string result = "" - for x in input_string: - if not x.isalpha(): - result += x - elif x.isupper(): - result += chr((ord(x) - key - 65) % 26 + 65) - elif x.islower(): - result += chr((ord(x) - key - 97) % 26 + 97) + + for character in input_string: + if character not in alpha: + # Append without encryption if character is not in the alphabet + result += character + else: + # Get the index of the new key and make sure it isn't too large + new_key = (alpha.index(character) + key) % len(alpha) + + # Append the encoded character to the alphabet + result += alpha[new_key] + return result -def brute_force(input_string: str) -> None: +def decrypt(input_string: str, key: int, alphabet=None) -> str: + # Turn on decode mode by making the key negative + key *= -1 + + return encrypt(input_string, key, alphabet) + + +def brute_force(input_string: str, alphabet=None) -> dict: + # Set default alphabet to lower and upper case english chars + alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] + + # The key during testing (will increase) key = 1 + + # The encoded result result = "" - while key <= 94: - for x in input_string: - indx = (ord(x) - key) % 256 - if indx < 32: - indx = indx + 95 - result = result + chr(indx) - print(f"Key: {key}\t| Message: {result}") + + # To store data on all the combinations + brute_force_data = {} + + # Cycle through each combination + while key <= len(alpha): + # Encrypt the message + result = encrypt(input_string, key, alpha) + + # Update the data + brute_force_data[key] = result + + # Reset result and increase the key result = "" key += 1 - return None + + return brute_force_data def main(): while True: - print(f'{"-" * 10}\n Menu\n{"-" * 10}') + print(f'\n{"-" * 10}\n Menu\n{"-" * 10}') print(*["1.Encrpyt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n") - choice = input("What would you like to do?: ") + + # get user input + choice = input("\nWhat would you like to do?: ").strip() + + # run functions based on what the user chose if choice not in ["1", "2", "3", "4"]: print("Invalid choice, please enter a valid choice") elif choice == "1": input_string = input("Please enter the string to be encrypted: ") - key = int(input("Please enter off-set between 0-25: ")) - if key in range(1, 95): - print(encrypt(input_string.lower(), key)) + key = int(input("Please enter off-set between 0-25: ").strip()) + + print(encrypt(input_string, key)) elif choice == "2": input_string = input("Please enter the string to be decrypted: ") - key = int(input("Please enter off-set between 1-94: ")) - if key in range(1, 95): - print(decrypt(input_string, key)) + key = int(input("Please enter off-set between 1-94: ").strip()) + + print(decrypt(input_string, key)) elif choice == "3": input_string = input("Please enter the string to be decrypted: ") - brute_force(input_string) - main() + brute_force_data = brute_force(input_string) + + for key, value in brute_force_data.items(): + print(f"Key: {key} | Message: {value}") + elif choice == "4": print("Goodbye.") break From cf3989c58312b84efa99caa01fe1260dc9d4f359 Mon Sep 17 00:00:00 2001 From: Maxim Rebguns Date: Wed, 6 May 2020 21:45:48 -0500 Subject: [PATCH 2/9] Added docstrings --- ciphers/caesar_cipher.py | 133 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index c4f9c8bed40e..92639765edad 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -2,6 +2,52 @@ def encrypt(input_string: str, key: int, alphabet=None) -> str: + """ + encrypt + ======= + Encodes a given string with the caesar cipher and returns the encoded + message + + Parameters: + ----------- + * input_string: the plain-text that needs to be encoded + * key: the number of letters to shift the message by + + Optional: + * alphabet (None): the alphabet used to encode the cipher, if not + specified, the standard english alphabet with upper and lowercase + letters is used + + Returns: + * A string containing the encoded cipher-text + + More on the caesar cipher + ========================= + The caesar cipher is named after Julius Caesar who used it when sending + secret military messages to his troops. This is a simple substitution cipher + where very character in the plain-text is shifted by a certain number known + as the "key" or "shift". + + Example: + Say we have the following message: + "Hello, captain" + + And our alphabet is made up of lower and uppercase letters: + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + + And our shift is "2" + + We can then encode the message, one letter at a time. "H" would become "J", + since "J" is two letters away, and so on. If the shift is ever two large, or + our letter is at the end of the alphabet, we just start at the beginning + ("Z" would shift to "a" then "b" and so on). + + Our final message would be "Jgnnq, ecrvckp" + + Further reading + =============== + * https://en.m.wikipedia.org/wiki/Caesar_cipher + """ # Set default alphabet to lower and upper case english chars alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] @@ -23,6 +69,53 @@ def encrypt(input_string: str, key: int, alphabet=None) -> str: def decrypt(input_string: str, key: int, alphabet=None) -> str: + """ + decrypt + ======= + Decodes a given string of cipher-text and returns the decoded plain-text + + Parameters: + ----------- + * input_string: the cipher-text that needs to be decoded + * key: the number of letters to shift the message backwards by to decode + + Optional: + * alphabet (None): the alphabet used to decode the cipher, if not + specified, the standard english alphabet with upper and lowercase + letters is used + + Returns: + * A string containing the decoded plain-text + + More on the caesar cipher + ========================= + The caesar cipher is named after Julius Caesar who used it when sending + secret military messages to his troops. This is a simple substitution cipher + where very character in the plain-text is shifted by a certain number known + as the "key" or "shift". Please keep in mind, here we will be focused on + decryption. + + Example: + Say we have the following cipher-text: + "Jgnnq, ecrvckp" + + And our alphabet is made up of lower and uppercase letters: + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + + And our shift is "2" + + To decode the message, we would do the same thing as encoding, but in + reverse. The first letter, "J" would become "H" (remember: we are decoding) + because "H" is two letters in reverse (to the left) of "J". We would + continue doing this. A letter like "a" would shift back to the end of + the alphabet, and would become "Z" or "Y" and so on. + + Our final message would be "Hello, captain" + + Further reading + =============== + * https://en.m.wikipedia.org/wiki/Caesar_cipher + """ # Turn on decode mode by making the key negative key *= -1 @@ -30,6 +123,42 @@ def decrypt(input_string: str, key: int, alphabet=None) -> str: def brute_force(input_string: str, alphabet=None) -> dict: + """ + brute_force + =========== + Returns all the possible combinations of keys and the decoded strings in the + form of a dictionary + + Parameters: + ----------- + * input_string: the cipher-text that needs to be used during brute-force + + Optional: + * alphabet: (None): the alphabet used to decode the cipher, if not + specified, the standard english alphabet with upper and lowercase + letters is used + + More about brute force + ====================== + Brute force is when a person intercepts a message or password, not knowing + the key and tries every single combination. This is easy with the caesar + cipher since there are only all the letters in the alphabet. The more + complex the cipher, the larger amount of time it will take to do brute force + + Ex: + Say we have a 5 letter alphabet (abcde), for simplicity and we intercepted the + following message: + + "dbc" + + we could then just write out every combination: + ecd... and so on, until we reach a combination that makes sense: + "cab" + + Further reading + =============== + * https://en.wikipedia.org/wiki/Brute_force + """ # Set default alphabet to lower and upper case english chars alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] @@ -44,8 +173,8 @@ def brute_force(input_string: str, alphabet=None) -> dict: # Cycle through each combination while key <= len(alpha): - # Encrypt the message - result = encrypt(input_string, key, alpha) + # Decrypt the message + result = decrypt(input_string, key, alpha) # Update the data brute_force_data[key] = result From 71cbf740496e2342d715b39d7c4c26aa0b62f75b Mon Sep 17 00:00:00 2001 From: Maxim Rebguns Date: Wed, 6 May 2020 21:47:25 -0500 Subject: [PATCH 3/9] Updated input functions --- ciphers/caesar_cipher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 92639765edad..c800d30ddac5 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -199,12 +199,12 @@ def main(): print("Invalid choice, please enter a valid choice") elif choice == "1": input_string = input("Please enter the string to be encrypted: ") - key = int(input("Please enter off-set between 0-25: ").strip()) + key = int(input("Please enter off-set between: ").strip()) print(encrypt(input_string, key)) elif choice == "2": input_string = input("Please enter the string to be decrypted: ") - key = int(input("Please enter off-set between 1-94: ").strip()) + key = int(input("Please enter off-set: ").strip()) print(decrypt(input_string, key)) elif choice == "3": From 238d8c9706fc9f295555172bae99c41b57519574 Mon Sep 17 00:00:00 2001 From: Maxim Rebguns Date: Thu, 7 May 2020 10:31:27 -0500 Subject: [PATCH 4/9] Added doctests --- ciphers/caesar_cipher.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index c800d30ddac5..316aba52f776 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -47,6 +47,17 @@ def encrypt(input_string: str, key: int, alphabet=None) -> str: Further reading =============== * https://en.m.wikipedia.org/wiki/Caesar_cipher + + Doctests + ======== + >>> encrypt('The quick brown fox jumps over the lazy dog', 8) + 'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo' + + >>> encrypt('A very large key', 8000) + 's nWjq dSjYW cWq' + + >>> encrypt('a lowercase alphabet', 5, 'abcdefghijklmnopqrstuvwxyz') + 'f qtbjwhfxj fqumfgjy' """ # Set default alphabet to lower and upper case english chars alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] @@ -115,6 +126,17 @@ def decrypt(input_string: str, key: int, alphabet=None) -> str: Further reading =============== * https://en.m.wikipedia.org/wiki/Caesar_cipher + + Doctests + ======== + >>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8) + 'The quick brown fox jumps over the lazy dog' + + >>> decrypt('s nWjq dSjYW cWq', 8000) + 'A very large key' + + >>> decrypt('f qtbjwhfxj fqumfgjy', 5, 'abcdefghijklmnopqrstuvwxyz') + 'a lowercase alphabet' """ # Turn on decode mode by making the key negative key *= -1 @@ -158,6 +180,15 @@ def brute_force(input_string: str, alphabet=None) -> dict: Further reading =============== * https://en.wikipedia.org/wiki/Brute_force + + Doctests + ======== + >>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20] + "Please don't brute force me!" + + >>> brute_force(1) + Traceback (most recent call last): + TypeError: 'int' object is not iterable """ # Set default alphabet to lower and upper case english chars alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] @@ -199,7 +230,7 @@ def main(): print("Invalid choice, please enter a valid choice") elif choice == "1": input_string = input("Please enter the string to be encrypted: ") - key = int(input("Please enter off-set between: ").strip()) + key = int(input("Please enter off-set: ").strip()) print(encrypt(input_string, key)) elif choice == "2": @@ -220,4 +251,5 @@ def main(): if __name__ == "__main__": + brute_force(1) main() From eb92fef936c31d94611fddd8374cf12b5c0653f4 Mon Sep 17 00:00:00 2001 From: Maxim Rebguns Date: Thu, 7 May 2020 10:33:04 -0500 Subject: [PATCH 5/9] removed test piece of code --- ciphers/caesar_cipher.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 316aba52f776..bc7264b1726b 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -251,5 +251,4 @@ def main(): if __name__ == "__main__": - brute_force(1) main() From b17e6ccf63641fb36aa34b5902764a53ad398ee6 Mon Sep 17 00:00:00 2001 From: Maxim Rebguns Date: Thu, 7 May 2020 19:26:02 -0500 Subject: [PATCH 6/9] black . --- .../test_digital_image_processing.py | 1 + sorts/sleepsort.py | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/digital_image_processing/test_digital_image_processing.py b/digital_image_processing/test_digital_image_processing.py index 327e2c67f50f..fe8890de9a31 100644 --- a/digital_image_processing/test_digital_image_processing.py +++ b/digital_image_processing/test_digital_image_processing.py @@ -84,6 +84,7 @@ def test_burkes(file_path: str = "digital_image_processing/image_data/lena_small burkes.process() assert burkes.output_img.any() + def test_nearest_neighbour( file_path: str = "digital_image_processing/image_data/lena_small.jpg", ): diff --git a/sorts/sleepsort.py b/sorts/sleepsort.py index 5fa688d1bbd6..014eda8b962e 100644 --- a/sorts/sleepsort.py +++ b/sorts/sleepsort.py @@ -30,18 +30,22 @@ def sleepsort(values: List[int]) -> List[int]: [1, 2, 2, 3, 4, 8, 9] """ sleepsort.result = [] + def append_to_result(x): sleepsort.result.append(x) + mx = values[0] for v in values: if mx < v: mx = v Timer(v, append_to_result, [v]).start() - sleep(mx+1) + sleep(mx + 1) return sleepsort.result - -if __name__ == '__main__': + + +if __name__ == "__main__": import doctest + doctest.testmod() x = [3, 2, 4, 7, 3, 6, 9, 1] sorted_x = sleepsort(x) From 2f92138f4a590612f96fe8f067e0aed88e766c72 Mon Sep 17 00:00:00 2001 From: Maxim Rebguns Date: Thu, 7 May 2020 19:29:19 -0500 Subject: [PATCH 7/9] Updated caesar cipher standard alphabet to fit python 3.8 --- ciphers/caesar_cipher.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index bc7264b1726b..90b9ed9aaf51 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -1,4 +1,4 @@ -from itertools import chain +from string import ascii_letters def encrypt(input_string: str, key: int, alphabet=None) -> str: @@ -60,7 +60,7 @@ def encrypt(input_string: str, key: int, alphabet=None) -> str: 'f qtbjwhfxj fqumfgjy' """ # Set default alphabet to lower and upper case english chars - alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] + alpha = alphabet or ascii_letters # The final result string result = "" @@ -191,7 +191,7 @@ def brute_force(input_string: str, alphabet=None) -> dict: TypeError: 'int' object is not iterable """ # Set default alphabet to lower and upper case english chars - alpha = alphabet or [chr(i) for i in chain(range(65, 91), range(97, 123))] + alpha = alphabet or ascii_letters # The key during testing (will increase) key = 1 From 5cec81e0c2e0bacedcafa540374cad5a2c7ac5ae Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 8 May 2020 07:26:14 +0200 Subject: [PATCH 8/9] Update and rename sleepsort.py to sleep_sort.py --- sorts/sleep_sort.py | 49 ++++++++++++++++++++++++++++++++++++++++++ sorts/sleepsort.py | 52 --------------------------------------------- 2 files changed, 49 insertions(+), 52 deletions(-) create mode 100644 sorts/sleep_sort.py delete mode 100644 sorts/sleepsort.py diff --git a/sorts/sleep_sort.py b/sorts/sleep_sort.py new file mode 100644 index 000000000000..0feda9c5e038 --- /dev/null +++ b/sorts/sleep_sort.py @@ -0,0 +1,49 @@ +""" +Sleep sort is probably the wierdest of all sorting functions with time-complexity of +O(max(input)+n) which is quite different from almost all other sorting techniques. +If the number of inputs is small then the complexity can be approximated to be +O(max(input)) which is a constant + +If the number of inputs is large, the complexity is approximately O(n). + +This function uses multithreading a kind of higher order programming and calls n +functions, each with a sleep time equal to its number. Hence each of function wakes +in sorted time. + +This function is not stable for very large values. + +https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort +""" +from threading import Timer +from time import sleep +from typing import List + + +def sleep_sort(values: List[int]) -> List[int]: + """ + Sort the list using sleepsort. + >>> sleep_sort([3, 2, 4, 7, 3, 6, 9, 1]) + [1, 2, 3, 3, 4, 6, 7, 9] + >>> sleep_sort([3, 2, 1, 9, 8, 4, 2]) + [1, 2, 2, 3, 4, 8, 9] + """ + sleep_sort.result = [] + + def append_to_result(x): + sleep_sort.result.append(x) + + mx = values[0] + for value in values: + if mx < value: + mx = value + Timer(value, append_to_result, [value]).start() + sleep(mx + 1) + return sleep_sort.result + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + + print(sleep_sort([3, 2, 4, 7, 3, 6, 9, 1])) diff --git a/sorts/sleepsort.py b/sorts/sleepsort.py deleted file mode 100644 index 014eda8b962e..000000000000 --- a/sorts/sleepsort.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Sleepsort is probably the wierdest of all sorting functions -with time-complexity of O(max(input)+n) which is -quite different from almost all other sorting techniques. -If the number of inputs is small then the complexity -can be approximated to be O(max(input)) which is a constant - -If the number of inputs is large, the complexity is -approximately O(n). - -This function uses multithreading a kind of higher order programming -and calls n functions, each with a sleep time equal to its number. -Hence each of the functions wake in sorted form. - -This function is not stable for very large values. - -https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort -""" - -from time import sleep -from threading import Timer -from typing import List - - -def sleepsort(values: List[int]) -> List[int]: - """ - Sort the list using sleepsort. - >>> sleepsort([3, 2, 4, 7, 3, 6, 9, 1]) - [1, 2, 3, 3, 4, 6, 7, 9] - >>> sleepsort([3, 2, 1, 9, 8, 4, 2]) - [1, 2, 2, 3, 4, 8, 9] - """ - sleepsort.result = [] - - def append_to_result(x): - sleepsort.result.append(x) - - mx = values[0] - for v in values: - if mx < v: - mx = v - Timer(v, append_to_result, [v]).start() - sleep(mx + 1) - return sleepsort.result - - -if __name__ == "__main__": - import doctest - - doctest.testmod() - x = [3, 2, 4, 7, 3, 6, 9, 1] - sorted_x = sleepsort(x) - print(sorted_x) From c3232b469847e86bbf48a6ec56e4f89d06c987ae Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 8 May 2020 07:29:16 +0200 Subject: [PATCH 9/9] Or 4 --- ciphers/caesar_cipher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ciphers/caesar_cipher.py b/ciphers/caesar_cipher.py index 90b9ed9aaf51..7bda519767a1 100644 --- a/ciphers/caesar_cipher.py +++ b/ciphers/caesar_cipher.py @@ -223,10 +223,10 @@ def main(): print(*["1.Encrpyt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n") # get user input - choice = input("\nWhat would you like to do?: ").strip() + choice = input("\nWhat would you like to do?: ").strip() or "4" # run functions based on what the user chose - if choice not in ["1", "2", "3", "4"]: + if choice not in ("1", "2", "3", "4"): print("Invalid choice, please enter a valid choice") elif choice == "1": input_string = input("Please enter the string to be encrypted: ")