From 9d5546987351d17b296f86ae9a45e169220ecae2 Mon Sep 17 00:00:00 2001 From: Ope Date: Thu, 19 Oct 2023 21:39:50 +0100 Subject: [PATCH 1/2] Added docstrings,doctests and fixed a bug --- ciphers/trifid_cipher.py | 90 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index 8aa2263ca5ac..98ccc1e5e320 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -1,8 +1,23 @@ -# https://en.wikipedia.org/wiki/Trifid_cipher +""" + The trifid cipher uses a table to fractionate each plaintext letter into a trigram,mixes the constituents of the trigrams, and then applies the table in reverse to turn these mixed trigrams into ciphertext letters. + https://en.wikipedia.org/wiki/Trifid_cipher +""" + from __future__ import annotations def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: + """ + Arranges the triagram value of each letter of 'message_part' vertically and joins them horizontally + + >>> __encrypt_part('ASK',{'A': '111', 'B': '112', 'C': '113', 'D': '121', 'E': '122', 'F': '123', 'G': '131', 'H': '132', 'I': '133', 'J': '211', 'K': '212', 'L': '213', 'M': '221', 'N': '222', 'O': '223', 'P': '231', 'Q': '232', 'R': '233', 'S': '311', 'T': '312', 'U': '313', 'V': '321', 'W': '322', 'X': '323', 'Y': '331', 'Z': '332', '+': '333'}) + '132111112' + + 1 3 2 + 1 1 1 + 1 1 2 + + """ one, two, three = "", "", "" tmp = [] @@ -20,6 +35,12 @@ def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> st def __decrypt_part( message_part: str, character_to_number: dict[str, str] ) -> tuple[str, str, str]: + """ + Converts each letter of the input string into there respective trigram values, joins them and splits them into three equal groups of strings. Then returns the group of strings . + + >>> __decrypt_part('ABCDE',{'A': '111', 'B': '112', 'C': '113', 'D': '121', 'E': '122', 'F': '123', 'G': '131', 'H': '132', 'I': '133', 'J': '211', 'K': '212', 'L': '213', 'M': '221', 'N': '222', 'O': '223', 'P': '231', 'Q': '232', 'R': '233', 'S': '311', 'T': '312', 'U': '313', 'V': '321', 'W': '322', 'X': '323', 'Y': '331', 'Z': '332', '+': '333'}) + ('11111', '21131', '21122') + """ tmp, this_part = "", "" result = [] @@ -38,6 +59,32 @@ def __decrypt_part( def __prepare( message: str, alphabet: str ) -> tuple[str, str, dict[str, str], dict[str, str]]: + """ + A helper function that generates the triagrams and assigns each letter of the alphabet to its corresponding triagram and stores this in a dictionary ("character_to_number" and "number_to_character") after confirming if the alphabet's length is 27. + + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxYZ+') + ('IAMABOY', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ+', {'A': '111', 'B': '112', 'C': '113', 'D': '121', 'E': '122', 'F': '123', 'G': '131', 'H': '132', 'I': '133', 'J': '211', 'K': '212', 'L': '213', 'M': '221', 'N': '222', 'O': '223', 'P': '231', 'Q': '232', 'R': '233', 'S': '311', 'T': '312', 'U': '313', 'V': '321', 'W': '322', 'X': '323', 'Y': '331', 'Z': '332', '+': '333'}, {'111': 'A', '112': 'B', '113': 'C', '121': 'D', '122': 'E', '123': 'F', '131': 'G', '132': 'H', '133': 'I', '211': 'J', '212': 'K', '213': 'L', '221': 'M', '222': 'N', '223': 'O', '231': 'P', '232': 'Q', '233': 'R', '311': 'S', '312': 'T', '313': 'U', '321': 'V', '322': 'W', '323': 'X', '331': 'Y', '332': 'Z', '333': '+'}) + + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVw') + Traceback (most recent call last): + ... + KeyError: 'Length of alphabet has to be 27.' + + >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxyzzwwtyyujjgfd') + Traceback (most recent call last): + ... + KeyError: 'Length of alphabet has to be 27.' + + >>> __prepare('am i a boy?','abCdeFghijkLmnopqrStuVwxYZ+') + Traceback (most recent call last): + ... + ValueError: Each message character has to be included in alphabet! + + >>> __prepare(500,'abCdeFghijkLmnopqrStuVwxYZ+') + Traceback (most recent call last): + ... + AttributeError: 'int' object has no attribute 'replace' + """ # Validate message and alphabet, set to upper and remove spaces alphabet = alphabet.replace(" ", "").upper() message = message.replace(" ", "").upper() @@ -91,6 +138,19 @@ def __prepare( def encrypt_message( message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 ) -> str: + """ + Encrypts a message using the trifid_cipher. Any punctuatuions that would be used should be added to the alphabet.'message' is the message you want to encrypt, 'alphabet' are the characters you want to use for the cipher, 'period' is the number of characters you want in a group whilst encrypting. + + >>> encrypt_message('I am a boy') + 'BCDGBQY' + + >>> encrypt_message(' ') + '' + + >>> encrypt_message(' aide toi le c iel ta id era ','FELIXMARDSTBCGHJKNOPQUVWYZ+',5) + 'FMJFVOISSUFTFPUFEQQC' + + """ message, alphabet, character_to_number, number_to_character = __prepare( message, alphabet ) @@ -110,18 +170,28 @@ def encrypt_message( def decrypt_message( message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5 ) -> str: + """ + Decrypts a trifid_cipher encrypted message .'message' is the message you want to decrypt, 'alphabet' are the characters used for the cipher, 'period' is the number of characters used in grouping when it was encrypted. + + >>> decrypt_message('BCDGBQY') + 'IAMABOY' + >>> decrypt_message('FMJFVOISSUFTFPUFEQQC','FELIXMARDSTBCGHJKNOPQUVWYZ+',5) + 'AIDETOILECIELTAIDERA' + + """ message, alphabet, character_to_number, number_to_character = __prepare( message, alphabet ) + decrypted_numeric = [] decrypted = "" - - for i in range(0, len(message) + 1, period): + + for i in range(0, len(message) , 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] @@ -129,7 +199,9 @@ def decrypt_message( 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}") + import doctest + doctest.testmod() + # 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 7fea1eeaf4ad4d1bce8c807d3a5d949bc53f1d05 Mon Sep 17 00:00:00 2001 From: Ope Date: Thu, 19 Oct 2023 21:52:42 +0100 Subject: [PATCH 2/2] Added docstrings,doctests and fixed a bug --- ciphers/trifid_cipher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ciphers/trifid_cipher.py b/ciphers/trifid_cipher.py index 98ccc1e5e320..dac47a8fc75b 100644 --- a/ciphers/trifid_cipher.py +++ b/ciphers/trifid_cipher.py @@ -201,7 +201,7 @@ def decrypt_message( if __name__ == "__main__": import doctest doctest.testmod() - # 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}") + 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}")