Repository: TheAlgorithms/Python

Issue:
Adding the fractionated morse cipher
### Feature description

The implementation of the fractionated morse cipher has not yet been added to the ciphers directory. The Fractionated Morse cipher first converts the plaintext to morse code, then enciphers fixed size blocks of morse code back to letters. I have already implemented it in python and sent a pull request which has passed all the tests and just needs to be reviewed. 

Pull request - #9442

This would be a good addition to the repository and I hope you take a look at it! Thank you!


Discussion:

Resolving patch:
diff --git a/ciphers/fractionated_morse_cipher.py b/ciphers/fractionated_morse_cipher.py
new file mode 100644
--- /dev/null
+++ b/ciphers/fractionated_morse_cipher.py
@@ -0,0 +1,167 @@
+"""
+Python program for the Fractionated Morse Cipher.
+
+The Fractionated Morse cipher first converts the plaintext to Morse code,
+then enciphers fixed-size blocks of Morse code back to letters.
+This procedure means plaintext letters are mixed into the ciphertext letters,
+making it more secure than substitution ciphers.
+
+http://practicalcryptography.com/ciphers/fractionated-morse-cipher/
+"""
+import string
+
+MORSE_CODE_DICT = {
+    "A": ".-",
+    "B": "-...",
+    "C": "-.-.",
+    "D": "-..",
+    "E": ".",
+    "F": "..-.",
+    "G": "--.",
+    "H": "....",
+    "I": "..",
+    "J": ".---",
+    "K": "-.-",
+    "L": ".-..",
+    "M": "--",
+    "N": "-.",
+    "O": "---",
+    "P": ".--.",
+    "Q": "--.-",
+    "R": ".-.",
+    "S": "...",
+    "T": "-",
+    "U": "..-",
+    "V": "...-",
+    "W": ".--",
+    "X": "-..-",
+    "Y": "-.--",
+    "Z": "--..",
+    " ": "",
+}
+
+# Define possible trigrams of Morse code
+MORSE_COMBINATIONS = [
+    "...",
+    "..-",
+    "..x",
+    ".-.",
+    ".--",
+    ".-x",
+    ".x.",
+    ".x-",
+    ".xx",
+    "-..",
+    "-.-",
+    "-.x",
+    "--.",
+    "---",
+    "--x",
+    "-x.",
+    "-x-",
+    "-xx",
+    "x..",
+    "x.-",
+    "x.x",
+    "x-.",
+    "x--",
+    "x-x",
+    "xx.",
+    "xx-",
+    "xxx",
+]
+
+# Create a reverse dictionary for Morse code
+REVERSE_DICT = {value: key for key, value in MORSE_CODE_DICT.items()}
+
+
+def encode_to_morse(plaintext: str) -> str:
+    """Encode a plaintext message into Morse code.
+
+    Args:
+        plaintext: The plaintext message to encode.
+
+    Returns:
+        The Morse code representation of the plaintext message.
+
+    Example:
+        >>> encode_to_morse("defend the east")
+        '-..x.x..-.x.x-.x-..xx-x....x.xx.x.-x...x-'
+    """
+    return "x".join([MORSE_CODE_DICT.get(letter.upper(), "") for letter in plaintext])
+
+
+def encrypt_fractionated_morse(plaintext: str, key: str) -> str:
+    """Encrypt a plaintext message using Fractionated Morse Cipher.
+
+    Args:
+        plaintext: The plaintext message to encrypt.
+        key: The encryption key.
+
+    Returns:
+        The encrypted ciphertext.
+
+    Example:
+        >>> encrypt_fractionated_morse("defend the east","Roundtable")
+        'ESOAVVLJRSSTRX'
+
+    """
+    morse_code = encode_to_morse(plaintext)
+    key = key.upper() + string.ascii_uppercase
+    key = "".join(sorted(set(key), key=key.find))
+
+    # Ensure morse_code length is a multiple of 3
+    padding_length = 3 - (len(morse_code) % 3)
+    morse_code += "x" * padding_length
+
+    fractionated_morse_dict = {v: k for k, v in zip(key, MORSE_COMBINATIONS)}
+    fractionated_morse_dict["xxx"] = ""
+    encrypted_text = "".join(
+        [
+            fractionated_morse_dict[morse_code[i : i + 3]]
+            for i in range(0, len(morse_code), 3)
+        ]
+    )
+    return encrypted_text
+
+
+def decrypt_fractionated_morse(ciphertext: str, key: str) -> str:
+    """Decrypt a ciphertext message encrypted with Fractionated Morse Cipher.
+
+    Args:
+        ciphertext: The ciphertext message to decrypt.
+        key: The decryption key.
+
+    Returns:
+        The decrypted plaintext message.
+
+    Example:
+        >>> decrypt_fractionated_morse("ESOAVVLJRSSTRX","Roundtable")
+        'DEFEND THE EAST'
+    """
+    key = key.upper() + string.ascii_uppercase
+    key = "".join(sorted(set(key), key=key.find))
+
+    inverse_fractionated_morse_dict = dict(zip(key, MORSE_COMBINATIONS))
+    morse_code = "".join(
+        [inverse_fractionated_morse_dict.get(letter, "") for letter in ciphertext]
+    )
+    decrypted_text = "".join(
+        [REVERSE_DICT[code] for code in morse_code.split("x")]
+    ).strip()
+    return decrypted_text
+
+
+if __name__ == "__main__":
+    """
+    Example usage of Fractionated Morse Cipher.
+    """
+    plaintext = "defend the east"
+    print("Plain Text:", plaintext)
+    key = "ROUNDTABLE"
+
+    ciphertext = encrypt_fractionated_morse(plaintext, key)
+    print("Encrypted:", ciphertext)
+
+    decrypted_text = decrypt_fractionated_morse(ciphertext, key)
+    print("Decrypted:", decrypted_text)


Test patch: