Done challenge 8
[cipher-tools.git] / cipher.py
index b1413691fb025aaf6a450bde0efa00d9163b5fee..e318aa46f8776fe24d9293e7d2b91def53d72663 100644 (file)
--- a/cipher.py
+++ b/cipher.py
@@ -13,6 +13,7 @@ import pprint
 ## Utility functions
 cat = ''.join
 wcat = ' '.join
+lcat = '\n'.join
 
 def pos(letter): 
     if letter in string.ascii_lowercase:
@@ -381,7 +382,7 @@ def vigenere_encipher(message, keyword):
     >>> vigenere_encipher('hello', 'abc')
     'hfnlp'
     """
-    shifts = [ord(l) - ord('a') for l in sanitise(keyword)]
+    shifts = [pos(l) for l in sanitise(keyword)]
     pairs = zip(message, cycle(shifts))
     return cat([caesar_encipher_letter(l, k) for l, k in pairs])
 
@@ -391,7 +392,7 @@ def vigenere_decipher(message, keyword):
     >>> vigenere_decipher('hfnlp', 'abc')
     'hello'
     """
-    shifts = [ord(l) - ord('a') for l in sanitise(keyword)]
+    shifts = [pos(l) for l in sanitise(keyword)]
     pairs = zip(message, cycle(shifts))
     return cat([caesar_decipher_letter(l, k) for l, k in pairs])
 
@@ -903,12 +904,12 @@ def hill_encipher(matrix, message_letters, fillvalue='a'):
         padding = fillvalue[0] * (n - len(sanitised_message) % n)
     else:
         padding = ''
-    message = [ord(c) - ord('a') for c in sanitised_message + padding]
+    message = [pos(c) for c in sanitised_message + padding]
     message_chunks = [message[i:i+n] for i in range(0, len(message), n)]
     # message_chunks = chunks(message, len(matrix), fillvalue=None)
     enciphered_chunks = [((matrix * np.matrix(c).T).T).tolist()[0] 
             for c in message_chunks]
-    return cat([chr(int(round(l)) % 26 + ord('a')) 
+    return cat([unpos(round(l))
             for l in sum(enciphered_chunks, [])])
 
 def hill_decipher(matrix, message, fillvalue='a'):
@@ -1136,6 +1137,32 @@ def bifid_decipher(message, keyword, wrap_alphabet=KeywordWrapAlphabet.from_a,
 
     return cat(r_grid[p] for p in pairs1) 
 
+
+def autokey_encipher(message, keyword):
+    """Encipher with the autokey cipher
+
+    >>> autokey_encipher('meetatthefountain', 'kilt')
+    'wmpmmxxaeyhbryoca'
+    """
+    shifts = [pos(l) for l in keyword + message]
+    pairs = zip(message, shifts)
+    return cat([caesar_encipher_letter(l, k) for l, k in pairs])
+
+def autokey_decipher(ciphertext, keyword):
+    """Decipher with the autokey cipher
+
+    >>> autokey_decipher('wmpmmxxaeyhbryoca', 'kilt')
+    'meetatthefountain'
+    """
+    plaintext = []
+    keys = list(keyword)
+    for c in ciphertext:
+        plaintext_letter = caesar_decipher_letter(c, pos(keys[0]))
+        plaintext += [plaintext_letter]
+        keys = keys[1:] + [plaintext_letter]
+    return cat(plaintext)
+
+
 class PocketEnigma(object):
     """A pocket enigma machine
     The wheel is internally represented as a 26-element list self.wheel_map, 
@@ -1170,7 +1197,7 @@ class PocketEnigma(object):
             self.validate_wheel_spec(wheel)
             self.make_wheel_map(wheel)
         if position in string.ascii_lowercase:
-            self.position = ord(position) - ord('a')
+            self.position = pos(position)
         else:
             self.position = position
 
@@ -1184,8 +1211,8 @@ class PocketEnigma(object):
         self.validate_wheel_spec(wheel_spec)
         self.wheel_map = [0] * 26
         for p in wheel_spec:
-            self.wheel_map[ord(p[0]) - ord('a')] = ord(p[1]) - ord('a')
-            self.wheel_map[ord(p[1]) - ord('a')] = ord(p[0]) - ord('a')
+            self.wheel_map[pos(p[0])] = pos(p[1])
+            self.wheel_map[pos(p[1])] = pos(p[0])
         return self.wheel_map
 
     def validate_wheel_spec(self, wheel_spec):
@@ -1240,10 +1267,9 @@ class PocketEnigma(object):
         ''
         """
         if letter in string.ascii_lowercase:
-            return chr(
-                (self.wheel_map[(ord(letter) - ord('a') - self.position) % 26] + 
-                    self.position) % 26 + 
-                ord('a'))
+            return unpos(
+                (self.wheel_map[(pos(letter) - self.position) % 26] + 
+                    self.position))
         else:
             return ''
 
@@ -1291,7 +1317,7 @@ class PocketEnigma(object):
         >>> pe.set_position('z')
         25
         """
-        self.position = ord(position) - ord('a')
+        self.position = pos(position)
         return self.position