Done challenge 6b, added some text formatting tools
[cipher-tools.git] / cipherbreak.py
index a2eecad9cb34f3bb596e2e38e22ed60c1ccca840..e3c1e5f18a9d1a1dc8fcdda43346bf39828741ee 100644 (file)
@@ -40,6 +40,18 @@ from language_models import *
 # timeit.timeit('keyword_break(c5a)', setup='gc.enable() ; from __main__ import c5a ; from cipher import keyword_break', number=1)
 # timeit.repeat('keyword_break_mp(c5a, chunksize=500)', setup='gc.enable() ; from __main__ import c5a ; from cipher import keyword_break_mp', repeat=5, number=1)
 
+
+def index_of_coincidence(text):
+    stext = sanitise(text)
+    counts = collections.Counter(stext)
+    denom = len(stext) * (len(text) - 1) / 26
+    return (
+        sum(max(counts[l] * counts[l] - 1, 0) for l in string.ascii_lowercase)
+        /
+        denom
+    )
+
+
 transpositions = collections.defaultdict(list)
 for word in keywords:
     transpositions[transpositions_of(word)] += [word]
@@ -301,7 +313,7 @@ def vigenere_frequency_break(message, max_key_length=20, fitness=Pletters):
     """
     def worker(message, key_length, fitness):
         splits = every_nth(sanitised_message, key_length)
-        key = cat([chr(caesar_break(s)[0] + ord('a')) for s in splits])
+        key = cat([unpos(caesar_break(s)[0]) for s in splits])
         plaintext = vigenere_decipher(message, key)
         fit = fitness(plaintext)
         return key, fit
@@ -376,8 +388,7 @@ def beaufort_variant_frequency_break(message, max_key_length=20, fitness=Pletter
     """
     def worker(message, key_length, fitness):
         splits = every_nth(sanitised_message, key_length)
-        key = cat([chr(-caesar_break(s)[0] % 26 + ord('a'))
-                       for s in splits])
+        key = cat([unpos(-caesar_break(s)[0]) for s in splits])
         plaintext = beaufort_variant_decipher(message, key)
         fit = fitness(plaintext)
         return key, fit