a5647be20e3b30abd5573ac087eac7da1f1f5d2f
1 from support
.utilities
import *
2 from support
.language_models
import *
3 from logger
import logger
6 modular_division_table
= {
7 (multiplier
, (multiplier
* plaintext
) % 26): plaintext
8 for plaintext
in range(26)
9 for multiplier
in range(26)
13 def affine_encipher_letter(accented_letter
, multiplier
=1, adder
=0, one_based
=True):
14 """Encipher a letter, given a multiplier and adder
16 >>> cat(affine_encipher_letter(l, 3, 5, True) \
17 for l in string.ascii_letters)
18 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE'
19 >>> cat(affine_encipher_letter(l, 3, 5, False) \
20 for l in string.ascii_letters)
21 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC'
23 letter
= unaccent(accented_letter
)
24 if letter
in string
.ascii_letters
:
25 letter_number
= pos(letter
)
26 if one_based
: letter_number
+= 1
27 cipher_number
= (letter_number
* multiplier
+ adder
) % 26
28 if one_based
: cipher_number
-= 1
29 if letter
in string
.ascii_uppercase
:
30 return unpos(cipher_number
).upper()
32 return unpos(cipher_number
)
36 def affine_decipher_letter(letter
, multiplier
=1, adder
=0, one_based
=True):
37 """Encipher a letter, given a multiplier and adder
39 >>> cat(affine_decipher_letter(l, 3, 5, True) \
40 for l in 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE')
41 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
42 >>> cat(affine_decipher_letter(l, 3, 5, False) \
43 for l in 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC')
44 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
46 if letter
in string
.ascii_letters
:
47 cipher_number
= pos(letter
)
48 if one_based
: cipher_number
+= 1
49 # plaintext_number = (
50 # modular_division_table[multiplier]
51 # [(cipher_number - adder) % 26])
53 modular_division_table
[multiplier
, (cipher_number
- adder
) % 26]
55 if one_based
: plaintext_number
-= 1
56 if letter
in string
.ascii_uppercase
:
57 return unpos(plaintext_number
).upper()
59 return unpos(plaintext_number
)
63 def affine_encipher(message
, multiplier
=1, adder
=0, one_based
=True):
66 >>> affine_encipher('hours passed during which jerico tried every ' \
67 'trick he could think of', 15, 22, True)
68 'lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls omytd jlaxe mh'
70 enciphered
= [affine_encipher_letter(l
, multiplier
, adder
, one_based
)
72 return cat(enciphered
)
74 def affine_decipher(message
, multiplier
=1, adder
=0, one_based
=True):
77 >>> affine_decipher('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg ' \
78 'jfaoe ls omytd jlaxe mh', 15, 22, True)
79 'hours passed during which jerico tried every trick he could think of'
81 enciphered
= [affine_decipher_letter(l
, multiplier
, adder
, one_based
)
83 return cat(enciphered
)
87 def affine_break(message
, fitness
=Pletters
):
88 """Breaks an affine cipher using frequency analysis
90 >>> affine_break('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls ' \
91 'omytd jlaxe mh jm bfmibj umis hfsul axubafkjamx. ls kffkxwsd jls ' \
92 'ofgbjmwfkiu olfmxmtmwaokttg jlsx ls kffkxwsd jlsi zg tsxwjl. jlsx ' \
93 'ls umfjsd jlsi zg hfsqysxog. ls dmmdtsd mx jls bats mh bkbsf. ls ' \
94 'bfmctsd kfmyxd jls lyj, mztanamyu xmc jm clm cku tmmeaxw kj lai ' \
95 'kxd clm ckuxj.') # doctest: +ELLIPSIS
96 ((15, 22, True), -340.601181913...)
98 sanitised_message
= sanitise(message
)
101 best_one_based
= True
102 best_fit
= float("-inf")
103 for one_based
in [True, False]:
104 for multiplier
in [x
for x
in range(1, 26, 2) if x
!= 13]:
105 for adder
in range(26):
106 plaintext
= affine_decipher(sanitised_message
,
107 multiplier
, adder
, one_based
)
108 fit
= fitness(plaintext
)
109 logger
.debug('Affine break attempt using key {0}x+{1} ({2}) '
110 'gives fit of {3} and decrypt starting: {4}'.
111 format(multiplier
, adder
, one_based
, fit
,
115 best_multiplier
= multiplier
117 best_one_based
= one_based
118 logger
.info('Affine break best fit with key {0}x+{1} ({2}) gives fit of '
119 '{3} and decrypt starting: {4}'.format(
120 best_multiplier
, best_adder
, best_one_based
, best_fit
,
121 affine_decipher(sanitised_message
, best_multiplier
,
122 best_adder
, best_one_based
)[:50]))
123 return (best_multiplier
, best_adder
, best_one_based
), best_fit