Skip to content

Implemented simple keyword cipher #1589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Nov 23, 2019
Merged
90 changes: 90 additions & 0 deletions ciphers/simple_keyword_cypher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
def remove_duplicates(key: str) -> str:
"""
Removes duplicate alphabetic characters in a keyword (letter is ignored after its
first appearance).
:param key: Keyword to use
:return: String with duplicates removed
>>> remove_duplicates('Hello World!!')
'Helo Wrd'
"""

key_no_dups = ""
for ch in key:
if ch == " " or ch not in key_no_dups and ch.isalpha():
key_no_dups += ch
return key_no_dups


def create_cipher_map(key: str) -> dict:
"""
Returns a cipher map given a keyword.
:param key: keyword to use
:return: dictionary cipher map
"""
# Create alphabet list
alphabet = [chr(i + 65) for i in range(26)]
# Remove duplicate characters from key
key = remove_duplicates(key.upper())
offset = len(key)
# First fill cipher with key characters
cipher_alphabet = {alphabet[i]: char for i, char in enumerate(key)}
# Then map remaining characters in alphabet to
# the alphabet from the beginning
for i in range(len(cipher_alphabet), 26):
char = alphabet[i - offset]
# Ensure we are not mapping letters to letters previously mapped
while char in key:
offset -= 1
char = alphabet[i - offset]
cipher_alphabet[alphabet[i]] = char
return cipher_alphabet


def encipher(message: str, cipher_map: dict) -> str:
"""
Enciphers a message given a cipher map.
:param message: Message to encipher
:param cipher_map: Cipher map
:return: enciphered string
>>> encipher('Hello World!!', create_cipher_map('Goodbye!!'))
'CYJJM VMQJB!!'
"""
return "".join(cipher_map.get(ch, ch) for ch in message.upper())


def decipher(message: str, cipher_map: dict) -> str:
"""
Deciphers a message given a cipher map
:param message: Message to decipher
:param cipher_map: Dictionary mapping to use
:return: Deciphered string
>>> cipher_map = create_cipher_map('Goodbye!!')
>>> decipher(encipher('Hello World!!', cipher_map), cipher_map)
'HELLO WORLD!!'
"""
# Reverse our cipher mappings
rev_cipher_map = {v: k for k, v in cipher_map.items()}
return "".join(rev_cipher_map.get(ch, ch) for ch in message.upper())


def main():
"""
Handles I/O
:return: void
"""
message = input("Enter message to encode or decode: ").strip()
key = input("Enter keyword: ").strip()
option = input("Encipher or decipher? E/D:").strip()[0].lower()
try:
func = {"e": encipher, "d": decipher}[option]
except KeyError:
raise KeyError("invalid input option")
cipher_map = create_cipher_map(key)
print(func(message, cipher_map))


if __name__ == "__main__":
import doctest

doctest.testmod()
main()