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