import string
import collections
import math
+from enum import Enum
from itertools import zip_longest, cycle, chain
from language_models import *
return ''.join(enciphered)
-def keyword_cipher_alphabet_of(keyword, wrap_alphabet=0):
+class Keyword_wrap_alphabet(Enum):
+ from_a = 0
+ from_last = 1
+ from_largest = 2
+
+
+def keyword_cipher_alphabet_of(keyword, wrap_alphabet=Keyword_wrap_alphabet.from_a):
"""Find the cipher alphabet given a keyword.
wrap_alphabet controls how the rest of the alphabet is added
after the keyword.
- 0 : from 'a'
- 1 : from the last letter in the sanitised keyword
- 2 : from the largest letter in the sanitised keyword
>>> keyword_cipher_alphabet_of('bayes')
'bayescdfghijklmnopqrtuvwxz'
- >>> keyword_cipher_alphabet_of('bayes', 0)
+ >>> keyword_cipher_alphabet_of('bayes', Keyword_wrap_alphabet.from_a)
'bayescdfghijklmnopqrtuvwxz'
- >>> keyword_cipher_alphabet_of('bayes', 1)
+ >>> keyword_cipher_alphabet_of('bayes', Keyword_wrap_alphabet.from_last)
'bayestuvwxzcdfghijklmnopqr'
- >>> keyword_cipher_alphabet_of('bayes', 2)
+ >>> keyword_cipher_alphabet_of('bayes', Keyword_wrap_alphabet.from_largest)
'bayeszcdfghijklmnopqrtuvwx'
"""
- if wrap_alphabet == 0:
+ if wrap_alphabet == Keyword_wrap_alphabet.from_a:
cipher_alphabet = ''.join(deduplicate(sanitise(keyword) +
string.ascii_lowercase))
else:
- if wrap_alphabet == 1:
+ if wrap_alphabet == Keyword_wrap_alphabet.from_last:
last_keyword_letter = deduplicate(sanitise(keyword))[-1]
else:
last_keyword_letter = sorted(sanitise(keyword))[-1]
return cipher_alphabet
-def keyword_encipher(message, keyword, wrap_alphabet=0):
+def keyword_encipher(message, keyword, wrap_alphabet=Keyword_wrap_alphabet.from_a):
"""Enciphers a message with a keyword substitution cipher.
wrap_alphabet controls how the rest of the alphabet is added
after the keyword.
>>> keyword_encipher('test message', 'bayes')
'rsqr ksqqbds'
- >>> keyword_encipher('test message', 'bayes', 0)
+ >>> keyword_encipher('test message', 'bayes', Keyword_wrap_alphabet.from_a)
'rsqr ksqqbds'
- >>> keyword_encipher('test message', 'bayes', 1)
+ >>> keyword_encipher('test message', 'bayes', Keyword_wrap_alphabet.from_last)
'lskl dskkbus'
- >>> keyword_encipher('test message', 'bayes', 2)
+ >>> keyword_encipher('test message', 'bayes', Keyword_wrap_alphabet.from_largest)
'qspq jsppbcs'
"""
cipher_alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet)
cipher_translation = ''.maketrans(string.ascii_lowercase, cipher_alphabet)
return unaccent(message).lower().translate(cipher_translation)
-def keyword_decipher(message, keyword, wrap_alphabet=0):
+def keyword_decipher(message, keyword, wrap_alphabet=Keyword_wrap_alphabet.from_a):
"""Deciphers a message with a keyword substitution cipher.
wrap_alphabet controls how the rest of the alphabet is added
after the keyword.
>>> keyword_decipher('rsqr ksqqbds', 'bayes')
'test message'
- >>> keyword_decipher('rsqr ksqqbds', 'bayes', 0)
+ >>> keyword_decipher('rsqr ksqqbds', 'bayes', Keyword_wrap_alphabet.from_a)
'test message'
- >>> keyword_decipher('lskl dskkbus', 'bayes', 1)
+ >>> keyword_decipher('lskl dskkbus', 'bayes', Keyword_wrap_alphabet.from_last)
'test message'
- >>> keyword_decipher('qspq jsppbcs', 'bayes', 2)
+ >>> keyword_decipher('qspq jsppbcs', 'bayes', Keyword_wrap_alphabet.from_largest)
'test message'
"""
cipher_alphabet = keyword_cipher_alphabet_of(keyword, wrap_alphabet)
frequency analysis
>>> keyword_break(keyword_encipher('this is a test message for the ' \
- 'keyword decipherment', 'elephant', 1), \
+ 'keyword decipherment', 'elephant', Keyword_wrap_alphabet.from_last), \
wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS
- (('elephant', 1), -52.834575011...)
+ (('elephant', <Keyword_wrap_alphabet.from_last: 1>), -52.834575011...)
"""
best_keyword = ''
best_wrap_alphabet = True
best_fit = float("-inf")
- for wrap_alphabet in range(3):
+ for wrap_alphabet in Keyword_wrap_alphabet:
for keyword in wordlist:
plaintext = keyword_decipher(message, keyword, wrap_alphabet)
fit = fitness(plaintext)
frequency analysis
>>> keyword_break_mp(keyword_encipher('this is a test message for the ' \
- 'keyword decipherment', 'elephant', 1), \
+ 'keyword decipherment', 'elephant', Keyword_wrap_alphabet.from_last), \
wordlist=['cat', 'elephant', 'kangaroo']) # doctest: +ELLIPSIS
- (('elephant', 1), -52.834575011...)
+ (('elephant', <Keyword_wrap_alphabet.from_last: 1>), -52.834575011...)
"""
with Pool() as pool:
helper_args = [(message, word, wrap, fitness)
- for word in wordlist for wrap in range(3)]
+ for word in wordlist
+ for wrap in Keyword_wrap_alphabet]
# Gotcha: the helper function here needs to be defined at the top level
# (limitation of Pool.starmap)
breaks = pool.starmap(keyword_break_worker, helper_args, chunksize)