0f33ac517070aa6eb892751b2c61bf24ac63f5b9
[szyfrow.git] / cadenus.py
1 from itertools import chain
2 import multiprocessing
3 from szyfrow.support.utilities import *
4 from szyfrow.support.language_models import *
5 from szyfrow.column_transposition import transpositions_of
6
7
8 def make_cadenus_keycolumn(doubled_letters = 'vw', start='a', reverse=False):
9 """Makes the key column for a Cadenus cipher (the column down between the
10 rows of letters)
11
12 >>> make_cadenus_keycolumn()['a']
13 0
14 >>> make_cadenus_keycolumn()['b']
15 1
16 >>> make_cadenus_keycolumn()['c']
17 2
18 >>> make_cadenus_keycolumn()['v']
19 21
20 >>> make_cadenus_keycolumn()['w']
21 21
22 >>> make_cadenus_keycolumn()['z']
23 24
24 >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['a']
25 1
26 >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['b']
27 0
28 >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['c']
29 24
30 >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['i']
31 18
32 >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['j']
33 18
34 >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['v']
35 6
36 >>> make_cadenus_keycolumn(doubled_letters='ij', start='b', reverse=True)['z']
37 2
38 """
39 index_to_remove = string.ascii_lowercase.find(doubled_letters[0])
40 short_alphabet = string.ascii_lowercase[:index_to_remove] + string.ascii_lowercase[index_to_remove+1:]
41 if reverse:
42 short_alphabet = cat(reversed(short_alphabet))
43 start_pos = short_alphabet.find(start)
44 rotated_alphabet = short_alphabet[start_pos:] + short_alphabet[:start_pos]
45 keycolumn = {l: i for i, l in enumerate(rotated_alphabet)}
46 keycolumn[doubled_letters[0]] = keycolumn[doubled_letters[1]]
47 return keycolumn
48
49 def cadenus_encipher(message, keyword, keycolumn, fillvalue='a'):
50 """Encipher with the Cadenus cipher
51
52 >>> cadenus_encipher(sanitise('Whoever has made a voyage up the Hudson ' \
53 'must remember the Kaatskill mountains. ' \
54 'They are a dismembered branch of the great'), \
55 'wink', \
56 make_cadenus_keycolumn(doubled_letters='vw', start='a', reverse=True))
57 'antodeleeeuhrsidrbhmhdrrhnimefmthgeaetakseomehetyaasuvoyegrastmmuuaeenabbtpchehtarorikswosmvaleatned'
58 >>> cadenus_encipher(sanitise('a severe limitation on the usefulness of ' \
59 'the cadenus is that every message must be ' \
60 'a multiple of twenty-five letters long'), \
61 'easy', \
62 make_cadenus_keycolumn(doubled_letters='vw', start='a', reverse=True))
63 'systretomtattlusoatleeesfiyheasdfnmschbhneuvsnpmtofarenuseieeieltarlmentieetogevesitfaisltngeeuvowul'
64 """
65 rows = chunks(message, len(message) // 25, fillvalue=fillvalue)
66 columns = zip(*rows)
67 rotated_columns = [col[start:] + col[:start] for start, col in zip([keycolumn[l] for l in keyword], columns)]
68 rotated_rows = zip(*rotated_columns)
69 transpositions = transpositions_of(keyword)
70 transposed = [transpose(r, transpositions) for r in rotated_rows]
71 return cat(chain(*transposed))
72
73 def cadenus_decipher(message, keyword, keycolumn, fillvalue='a'):
74 """
75 >>> cadenus_decipher('antodeleeeuhrsidrbhmhdrrhnimefmthgeaetakseomehetyaa' \
76 'suvoyegrastmmuuaeenabbtpchehtarorikswosmvaleatned', \
77 'wink', \
78 make_cadenus_keycolumn(reverse=True))
79 'whoeverhasmadeavoyageupthehudsonmustrememberthekaatskillmountainstheyareadismemberedbranchofthegreat'
80 >>> cadenus_decipher('systretomtattlusoatleeesfiyheasdfnmschbhneuvsnpmtof' \
81 'arenuseieeieltarlmentieetogevesitfaisltngeeuvowul', \
82 'easy', \
83 make_cadenus_keycolumn(reverse=True))
84 'aseverelimitationontheusefulnessofthecadenusisthateverymessagemustbeamultipleoftwentyfiveletterslong'
85 """
86 rows = chunks(message, len(message) // 25, fillvalue=fillvalue)
87 transpositions = transpositions_of(keyword)
88 untransposed_rows = [untranspose(r, transpositions) for r in rows]
89 columns = zip(*untransposed_rows)
90 rotated_columns = [col[-start:] + col[:-start] for start, col in zip([keycolumn[l] for l in keyword], columns)]
91 rotated_rows = zip(*rotated_columns)
92 # return rotated_columns
93 return cat(chain(*rotated_rows))
94
95
96 def cadenus_break(message, words=keywords,
97 doubled_letters='vw', fitness=Pbigrams):
98 c = make_cadenus_keycolumn(reverse=True)
99 valid_words = [w for w in words
100 if max(transpositions_of(w)) <= len(c)]
101 with multiprocessing.Pool() as pool:
102 results = pool.starmap(cadenus_break_worker,
103 [(message, w,
104 make_cadenus_keycolumn(doubled_letters=doubled_letters,
105 start=s, reverse=r),
106 fitness)
107 for w in words
108 for s in string.ascii_lowercase
109 for r in [True, False]
110 if max(transpositions_of(w)) <= len(
111 make_cadenus_keycolumn(
112 doubled_letters=doubled_letters, start=s, reverse=r))
113 ])
114 # return list(results)
115 return max(results, key=lambda k: k[1])
116
117 def cadenus_break_worker(message, keyword, keycolumn, fitness):
118 message_chunks = chunks(message, 175)
119 plaintext = ''.join(cadenus_decipher(c, keyword, keycolumn) for c in message_chunks)
120 fit = fitness(plaintext)
121 return (keyword, keycolumn), fit
122
123 if __name__ == "__main__":
124 import doctest