From 0717b885d132b1c75370f903cccb98723fa016be Mon Sep 17 00:00:00 2001 From: Keyboard-1 <142900182+Keyboard-1@users.noreply.github.com> Date: Sun, 15 Oct 2023 12:56:24 +0530 Subject: [PATCH 1/5] Renamed trafid_cipher.py to trifid_cipher.py and added doctests --- DIRECTORY.md | 2 +- .../{trafid_cipher.py => trifid_cipher.py} | 332 +++++++++++------- 2 files changed, 198 insertions(+), 136 deletions(-) rename ciphers/{trafid_cipher.py => trifid_cipher.py} (59%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2c6000c94ed4..322289cebd7e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -119,7 +119,7 @@ * [Shuffled Shift Cipher](ciphers/shuffled_shift_cipher.py) * [Simple Keyword Cypher](ciphers/simple_keyword_cypher.py) * [Simple Substitution Cipher](ciphers/simple_substitution_cipher.py) - * [Trafid Cipher](ciphers/trafid_cipher.py) + * [Trifid Cipher](ciphers/trifid_cipher.py) * [Transposition Cipher](ciphers/transposition_cipher.py) * [Transposition Cipher Encrypt Decrypt File](ciphers/transposition_cipher_encrypt_decrypt_file.py) * [Vigenere Cipher](ciphers/vigenere_cipher.py) diff --git a/ciphers/trafid_cipher.py b/ciphers/trifid_cipher.py similarity index 59% rename from ciphers/trafid_cipher.py rename to ciphers/trifid_cipher.py index 8aa2263ca5ac..a200bbc4ee6d 100644 --- a/ciphers/trafid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -1,135 +1,197 @@ -# https://en.wikipedia.org/wiki/Trifid_cipher -from __future__ import annotations - - -def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: - one, two, three = "", "", "" - tmp = [] - - for character in message_part: - tmp.append(character_to_number[character]) - - for each in tmp: - one += each[0] - two += each[1] - three += each[2] - - return one + two + three - - -def __decrypt_part( - message_part: str, character_to_number: dict[str, str] -) -> tuple[str, str, str]: - tmp, this_part = "", "" - result = [] - - for character in message_part: - this_part += character_to_number[character] - - for digit in this_part: - tmp += digit - if len(tmp) == len(message_part): - result.append(tmp) - tmp = "" - - return result[0], result[1], result[2] - - -def __prepare( - message: str, alphabet: str -) -> tuple[str, str, dict[str, str], dict[str, str]]: - # Validate message and alphabet, set to upper and remove spaces - alphabet = alphabet.replace(" ", "").upper() - message = message.replace(" ", "").upper() - - # Check length and characters - if len(alphabet) != 27: - raise KeyError("Length of alphabet has to be 27.") - for each in message: - if each not in alphabet: - raise ValueError("Each message character has to be included in alphabet!") - - # Generate dictionares - numbers = ( - "111", - "112", - "113", - "121", - "122", - "123", - "131", - "132", - "133", - "211", - "212", - "213", - "221", - "222", - "223", - "231", - "232", - "233", - "311", - "312", - "313", - "321", - "322", - "323", - "331", - "332", - "333", - ) - character_to_number = {} - number_to_character = {} - for letter, number in zip(alphabet, numbers): - character_to_number[letter] = number - number_to_character[number] = letter - - return message, alphabet, character_to_number, number_to_character - - -def encrypt_message( - message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 -) -> str: - message, alphabet, character_to_number, number_to_character = __prepare( - message, alphabet - ) - encrypted, encrypted_numeric = "", "" - - for i in range(0, len(message) + 1, period): - encrypted_numeric += __encrypt_part( - message[i : i + period], character_to_number - ) - - for i in range(0, len(encrypted_numeric), 3): - encrypted += number_to_character[encrypted_numeric[i : i + 3]] - - return encrypted - - -def decrypt_message( - message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 -) -> str: - message, alphabet, character_to_number, number_to_character = __prepare( - message, alphabet - ) - decrypted_numeric = [] - decrypted = "" - - for i in range(0, len(message) + 1, period): - a, b, c = __decrypt_part(message[i : i + period], character_to_number) - - for j in range(len(a)): - decrypted_numeric.append(a[j] + b[j] + c[j]) - - for each in decrypted_numeric: - decrypted += number_to_character[each] - - return decrypted - - -if __name__ == "__main__": - msg = "DEFEND THE EAST WALL OF THE CASTLE." - encrypted = encrypt_message(msg, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ") - decrypted = decrypt_message(encrypted, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ") - print(f"Encrypted: {encrypted}\nDecrypted: {decrypted}") +from __future__ import annotations + +""" +The trifid cipher is a classical cipher invented by Félix Delastelle in 1902 + +Wikipedia : https://en.wikipedia.org/wiki/Trifid_cipher + +It takes in input the plaintext and the key and encrypts it +For decryption, the same key used in the encryption is required + +Syntax : encrypt_message(plaintext, key) + decrypt_message(encrypted, key) +""" + +def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: + one, two, three = "", "", "" + tmp = [] + + for character in message_part: + tmp.append(character_to_number[character]) + + for each in tmp: + one += each[0] + two += each[1] + three += each[2] + + return one + two + three + + +def __decrypt_part( + message_part: str, character_to_number: dict[str, str] +) -> tuple[str, str, str]: + tmp, this_part = "", "" + result = [] + + for character in message_part: + this_part += character_to_number[character] + + for digit in this_part: + tmp += digit + if len(tmp) == len(message_part): + result.append(tmp) + tmp = "" + + return result[0], result[1], result[2] + + +def __prepare( + message: str, alphabet: str +) -> tuple[str, str, dict[str, str], dict[str, str]]: + + """__prepare validates the entered message and alphabet to check if + it satisfies all the given requirements: + + 1) message must not create numbers lik 1,2,3 etc + 2) Key must have all the alphabets the message has + 3) Length of the key has to be 27 + + >>> __prepare("hello", "ABCDEHG") + Traceback (most recent call last): + ... + KeyError: 'Length of alphabet has to be 27.' + + >>> __prepare("hello", "AABCDEFGGIJKLMNOPQRSTUVWXYZ") + Traceback (most recent call last): + ... + raise ValueError("Each message character has to be included in alphabet!") + ValueError: Each message character has to be included in alphabet! + """ + # Validate message and alphabet, set to upper and remove spaces + alphabet = alphabet.replace(" ", "").upper() + message = message.replace(" ", "").upper() + + # Check length and characters + if len(alphabet) != 27: + raise KeyError("Length of alphabet has to be 27.") + for each in message: + if each not in alphabet: + raise ValueError("Each message character has to be included in alphabet!") + + # Generate dictionares + numbers = ( + "111", + "112", + "113", + "121", + "122", + "123", + "131", + "132", + "133", + "211", + "212", + "213", + "221", + "222", + "223", + "231", + "232", + "233", + "311", + "312", + "313", + "321", + "322", + "323", + "331", + "332", + "333", + ) + character_to_number = {} + number_to_character = {} + for letter, number in zip(alphabet, numbers): + character_to_number[letter] = number + number_to_character[number] = letter + + return message, alphabet, character_to_number, number_to_character + + +def encrypt_message( + message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 +) -> str: + """ + encrypt_message(message, alphabet, period) encrypts the message using the alphabet + and period + + 1) If not provided, default alphabet is taken as "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + 2) If not provided, default period is taken as 5 + 3) message is a compulsory argument + + >>> print(encrypt_message("hello world")) + BOJN.WKPOY + + >>> print(encrypt_message("how are you", "BAECDHFGIJKLMNOPRQTSUVXWZY.")) + HDM.XGULQ + + >>> print(encrypt_message("all aboard the train", "BAECDHFGIJKLMNOPRQTSUVXWZY.", 4)) + DBAYCKFXFCKIVEFON + """ + message, alphabet, character_to_number, number_to_character = __prepare( + message, alphabet + ) + encrypted, encrypted_numeric = "", "" + + for i in range(0, len(message) + 1, period): + encrypted_numeric += __encrypt_part( + message[i : i + period], character_to_number + ) + + for i in range(0, len(encrypted_numeric), 3): + encrypted += number_to_character[encrypted_numeric[i : i + 3]] + + return encrypted + + +def decrypt_message( + message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 +) -> str: + """ + decrypt_message(message, alphabet, period) decrypts the message using the alphabet + and period + + 1) If not provided, default alphabet is taken as "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + 2) If not provided, default period is taken as 5 + 3) message is a compulsory argument + + >>> print(decrypt_message("BOJN.IYSS")) + HELLOGUYS + + >>> print(decrypt_message("KHQDYRSTJWPYTEAOC", "QWERTYUIOPASDFGHJKLZXCVBNM.")) + ALLABOARDTHETRAIN + + >>> print(decrypt_message("DBAYCKFXFCKIVEFON", "BAECDHFGIJKLMNOPRQTSUVXWZY.", 4)) + ALLABOARDTHETRAIN + """ + message, alphabet, character_to_number, number_to_character = __prepare( + message, alphabet + ) + decrypted_numeric = [] + decrypted = "" + + for i in range(0, len(message) + 1, period): + a, b, c = __decrypt_part(message[i : i + period], character_to_number) + + for j in range(len(a)): + decrypted_numeric.append(a[j] + b[j] + c[j]) + + for each in decrypted_numeric: + decrypted += number_to_character[each] + + return decrypted + + +if __name__ == "__main__": + import doctest + + doctest.testmod() \ No newline at end of file From 04f85ca63dc9f78cb8a6efcd95cd33eac804be50 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 15 Oct 2023 07:30:13 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ciphers/trifid_cipher.py | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index a200bbc4ee6d..1f3d209793e8 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -12,6 +12,7 @@ decrypt_message(encrypted, key) """ + def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: one, two, three = "", "", "" tmp = [] @@ -48,25 +49,24 @@ def __decrypt_part( def __prepare( message: str, alphabet: str ) -> tuple[str, str, dict[str, str], dict[str, str]]: - """__prepare validates the entered message and alphabet to check if - it satisfies all the given requirements: - - 1) message must not create numbers lik 1,2,3 etc - 2) Key must have all the alphabets the message has - 3) Length of the key has to be 27 - - >>> __prepare("hello", "ABCDEHG") - Traceback (most recent call last): - ... - KeyError: 'Length of alphabet has to be 27.' - - >>> __prepare("hello", "AABCDEFGGIJKLMNOPQRSTUVWXYZ") - Traceback (most recent call last): - ... - raise ValueError("Each message character has to be included in alphabet!") - ValueError: Each message character has to be included in alphabet! - """ + it satisfies all the given requirements: + + 1) message must not create numbers lik 1,2,3 etc + 2) Key must have all the alphabets the message has + 3) Length of the key has to be 27 + + >>> __prepare("hello", "ABCDEHG") + Traceback (most recent call last): + ... + KeyError: 'Length of alphabet has to be 27.' + + >>> __prepare("hello", "AABCDEFGGIJKLMNOPQRSTUVWXYZ") + Traceback (most recent call last): + ... + raise ValueError("Each message character has to be included in alphabet!") + ValueError: Each message character has to be included in alphabet! + """ # Validate message and alphabet, set to upper and remove spaces alphabet = alphabet.replace(" ", "").upper() message = message.replace(" ", "").upper() @@ -127,7 +127,7 @@ def encrypt_message( 1) If not provided, default alphabet is taken as "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 2) If not provided, default period is taken as 5 3) message is a compulsory argument - + >>> print(encrypt_message("hello world")) BOJN.WKPOY @@ -163,7 +163,7 @@ def decrypt_message( 1) If not provided, default alphabet is taken as "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 2) If not provided, default period is taken as 5 3) message is a compulsory argument - + >>> print(decrypt_message("BOJN.IYSS")) HELLOGUYS @@ -194,4 +194,4 @@ def decrypt_message( if __name__ == "__main__": import doctest - doctest.testmod() \ No newline at end of file + doctest.testmod() From 7f15173c896a435dfaf6bf64bc59e09f32536091 Mon Sep 17 00:00:00 2001 From: Keyboard-1 <142900182+Keyboard-1@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:01:59 +0530 Subject: [PATCH 3/5] fixed gramatical error --- ciphers/trifid_cipher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index a200bbc4ee6d..c274ee9c4e57 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -52,7 +52,7 @@ def __prepare( """__prepare validates the entered message and alphabet to check if it satisfies all the given requirements: - 1) message must not create numbers lik 1,2,3 etc + 1) message must not create numbers like 1,2,3 etc 2) Key must have all the alphabets the message has 3) Length of the key has to be 27 From 34e5fe8d8a2feb1b172bc2ec1f46c57507270170 Mon Sep 17 00:00:00 2001 From: Keyboard-1 <142900182+Keyboard-1@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:17:46 +0530 Subject: [PATCH 4/5] Fixed gramatical errors in doctests --- ciphers/trifid_cipher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index c274ee9c4e57..6e4d6b19c028 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -52,7 +52,7 @@ def __prepare( """__prepare validates the entered message and alphabet to check if it satisfies all the given requirements: - 1) message must not create numbers like 1,2,3 etc + 1) message must not contain numbers like 1,2,3 etc 2) Key must have all the alphabets the message has 3) Length of the key has to be 27 From 45b21819fbe869e05130d21091b224a0f269fc29 Mon Sep 17 00:00:00 2001 From: Keyboard-1 <142900182+Keyboard-1@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:36:13 +0530 Subject: [PATCH 5/5] Added definiton and source of APR in ABOUT.md of financial --- financial/ABOUT.md | 1 + 1 file changed, 1 insertion(+) diff --git a/financial/ABOUT.md b/financial/ABOUT.md index f6b0647f8201..3409e32ea04a 100644 --- a/financial/ABOUT.md +++ b/financial/ABOUT.md @@ -2,3 +2,4 @@ * Compound Interest: "Compound interest is calculated by multiplying the initial principal amount by one plus the annual interest rate raised to the number of compound periods minus one." [Compound Interest](https://www.investopedia.com/) * Simple Interest: "Simple interest paid or received over a certain period is a fixed percentage of the principal amount that was borrowed or lent. " [Simple Interest](https://www.investopedia.com/) +* Annual Percentage Rate: "Annual percentage rate (APR) refers to the yearly interest generated by a sum that's charged to borrowers or paid to investors. APR is expressed as a percentage that represents the actual yearly cost of funds over the term of a loan or income earned on an investment. " [Annual Percentage Rate]("https://www.investopedia.com/terms/a/apr.asp") \ No newline at end of file