All tests now running and passing
[cipher-tools.git] / cipher / cadenus.py
1 from itertools import chain
2 from support.utilities import *
3 from support.language_models import *
4 from cipher.column_transposition import transpositions_of
5
6 from logger import logger
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 if __name__ == "__main__":
96 import doctest