import string
import collections
-# import norms
import logging
-# import math
-from itertools import zip_longest, repeat, cycle
-# from segment import segment
-# from multiprocessing import Pool
-
+import math
+from itertools import zip_longest, cycle, chain
+from language_models import *
logger = logging.getLogger(__name__)
logger.addHandler(logging.FileHandler('cipher.log'))
#logger.setLevel(logging.DEBUG)
-modular_division_table = [[0]*26 for x in range(26)]
+modular_division_table = [[0]*26 for _ in range(26)]
for a in range(26):
for b in range(26):
c = (a * b) % 26
modular_division_table[b][c] = a
-def letters(text):
- """Remove all non-alphabetic characters from a text
- >>> letters('The Quick')
- 'TheQuick'
- >>> letters('The Quick BROWN fox jumped! over... the (9lazy) DOG')
- 'TheQuickBROWNfoxjumpedoverthelazyDOG'
- """
- return ''.join([c for c in text if c in string.ascii_letters])
-
-def sanitise(text):
- """Remove all non-alphabetic characters and convert the text to lowercase
-
- >>> sanitise('The Quick')
- 'thequick'
- >>> sanitise('The Quick BROWN fox jumped! over... the (9lazy) DOG')
- 'thequickbrownfoxjumpedoverthelazydog'
- """
- # sanitised = [c.lower() for c in text if c in string.ascii_letters]
- # return ''.join(sanitised)
- return letters(text).lower()
def ngrams(text, n):
"""Returns all n-grams of a text
return ''.join([''.join(l)
for l in zip_longest(*split_text, fillvalue='')])
+def chunks(text, n, fillvalue=None):
+ """Split a text into chunks of n characters
+
+ >>> chunks('abcdefghi', 3)
+ ['abc', 'def', 'ghi']
+ >>> chunks('abcdefghi', 4)
+ ['abcd', 'efgh', 'i']
+ >>> chunks('abcdefghi', 4, fillvalue='!')
+ ['abcd', 'efgh', 'i!!!']
+ """
+ if fillvalue:
+ padding = fillvalue[0] * (n - len(text) % n)
+ else:
+ padding = ''
+ return [(text+padding)[i:i+n] for i in range(0, len(text), n)]
+
def transpose(items, transposition):
"""Moves items around according to the given transposition
>>> transpose([10,11,12,13,14,15], (3,2,4,1,5,0))
[13, 12, 14, 11, 15, 10]
"""
- transposed = list(repeat('', len(transposition)))
+ transposed = [''] * len(transposition)
for p, t in enumerate(transposition):
transposed[p] = items[t]
return transposed
>>> untranspose([13, 12, 14, 11, 15, 10], [3,2,4,1,5,0])
[10, 11, 12, 13, 14, 15]
"""
- transposed = list(repeat('', len(transposition)))
+ transposed = [''] * len(transposition)
for p, t in enumerate(transposition):
transposed[t] = items[p]
return transposed
-
-
def deduplicate(text):
return list(collections.OrderedDict.fromkeys(text))
-
def caesar_encipher_letter(letter, shift):
"""Encipher a letter, given a shift amount
alphabet_start = ord('a')
cipher_number = ord(letter) - alphabet_start
if one_based: cipher_number += 1
- plaintext_number = ( modular_division_table[multiplier]
- [(cipher_number - adder) % 26] )
+ plaintext_number = (
+ modular_division_table[multiplier]
+ [(cipher_number - adder) % 26] )
if one_based: plaintext_number -= 1
return chr(plaintext_number % 26 + alphabet_start)
else:
cipher_translation = ''.maketrans(cipher_alphabet, string.ascii_lowercase)
return message.lower().translate(cipher_translation)
-def scytale_encipher(message, rows):
- """Enciphers using the scytale transposition cipher.
- Message is padded with spaces to allow all rows to be the same length.
- >>> scytale_encipher('thequickbrownfox', 3)
- 'tcnhkfeboqrxuo iw '
- >>> scytale_encipher('thequickbrownfox', 4)
- 'tubnhirfecooqkwx'
- >>> scytale_encipher('thequickbrownfox', 5)
- 'tubn hirf ecoo qkwx '
- >>> scytale_encipher('thequickbrownfox', 6)
- 'tqcrnxhukof eibwo '
- >>> scytale_encipher('thequickbrownfox', 7)
- 'tqcrnx hukof eibwo '
+def vigenere_encipher(message, keyword):
+ """Vigenere encipher
+
+ >>> vigenere_encipher('hello', 'abc')
+ 'hfnlp'
"""
- if len(message) % rows != 0:
- message += ' '*(rows - len(message) % rows)
- row_length = round(len(message) / rows)
- slices = [message[i:i+row_length]
- for i in range(0, len(message), row_length)]
- return ''.join([''.join(r) for r in zip_longest(*slices, fillvalue='')])
+ shifts = [ord(l) - ord('a') for l in sanitise(keyword)]
+ pairs = zip(message, cycle(shifts))
+ return ''.join([caesar_encipher_letter(l, k) for l, k in pairs])
-def scytale_decipher(message, rows):
- """Deciphers using the scytale transposition cipher.
- Assumes the message is padded so that all rows are the same length.
-
- >>> scytale_decipher('tcnhkfeboqrxuo iw ', 3)
- 'thequickbrownfox '
- >>> scytale_decipher('tubnhirfecooqkwx', 4)
- 'thequickbrownfox'
- >>> scytale_decipher('tubn hirf ecoo qkwx ', 5)
- 'thequickbrownfox '
- >>> scytale_decipher('tqcrnxhukof eibwo ', 6)
- 'thequickbrownfox '
- >>> scytale_decipher('tqcrnx hukof eibwo ', 7)
- 'thequickbrownfox '
+def vigenere_decipher(message, keyword):
+ """Vigenere decipher
+
+ >>> vigenere_decipher('hfnlp', 'abc')
+ 'hello'
"""
- cols = round(len(message) / rows)
- columns = [message[i:i+rows] for i in range(0, cols * rows, rows)]
- return ''.join([''.join(c) for c in zip_longest(*columns, fillvalue='')])
+ shifts = [ord(l) - ord('a') for l in sanitise(keyword)]
+ pairs = zip(message, cycle(shifts))
+ return ''.join([caesar_decipher_letter(l, k) for l, k in pairs])
+
+beaufort_encipher=vigenere_decipher
+beaufort_decipher=vigenere_encipher
def transpositions_of(keyword):
transpositions = tuple(key.index(l) for l in sorted(key))
return transpositions
-def column_transposition_encipher(message, keyword, fillvalue=' '):
+def pad(message_len, group_len, fillvalue):
+ padding_length = group_len - message_len % group_len
+ if padding_length == group_len: padding_length = 0
+ padding = ''
+ for i in range(padding_length):
+ if callable(fillvalue):
+ padding += fillvalue()
+ else:
+ padding += fillvalue
+ return padding
+
+def column_transposition_encipher(message, keyword, fillvalue=' ',
+ fillcolumnwise=False,
+ emptycolumnwise=False):
"""Enciphers using the column transposition cipher.
Message is padded to allow all rows to be the same length.
- >>> column_transposition_encipher('hellothere', 'clever')
+ >>> column_transposition_encipher('hellothere', 'abcdef', fillcolumnwise=True)
+ 'hlohr eltee '
+ >>> column_transposition_encipher('hellothere', 'abcdef', fillcolumnwise=True, emptycolumnwise=True)
+ 'hellothere '
+ >>> column_transposition_encipher('hellothere', 'abcdef')
+ 'hellothere '
+ >>> column_transposition_encipher('hellothere', 'abcde')
+ 'hellothere'
+ >>> column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=True, emptycolumnwise=True)
+ 'hellothere'
+ >>> column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=True, emptycolumnwise=False)
+ 'hlohreltee'
+ >>> column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=False, emptycolumnwise=True)
+ 'htehlelroe'
+ >>> column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=False, emptycolumnwise=False)
+ 'hellothere'
+ >>> column_transposition_encipher('hellothere', 'clever', fillcolumnwise=True, emptycolumnwise=True)
+ 'heotllrehe'
+ >>> column_transposition_encipher('hellothere', 'clever', fillcolumnwise=True, emptycolumnwise=False)
+ 'holrhetlee'
+ >>> column_transposition_encipher('hellothere', 'clever', fillcolumnwise=False, emptycolumnwise=True)
+ 'htleehoelr'
+ >>> column_transposition_encipher('hellothere', 'clever', fillcolumnwise=False, emptycolumnwise=False)
'hleolteher'
+ >>> column_transposition_encipher('hellothere', 'cleverly')
+ 'hleolthre e '
>>> column_transposition_encipher('hellothere', 'cleverly', fillvalue='!')
'hleolthre!e!'
+ >>> column_transposition_encipher('hellothere', 'cleverly', fillvalue=lambda: '*')
+ 'hleolthre*e*'
"""
- return column_transposition_worker(message, keyword, encipher=True,
- fillvalue=fillvalue)
+ transpositions = transpositions_of(keyword)
+ message += pad(len(message), len(transpositions), fillvalue)
+ if fillcolumnwise:
+ rows = every_nth(message, len(message) // len(transpositions))
+ else:
+ rows = chunks(message, len(transpositions))
+ transposed = [transpose(r, transpositions) for r in rows]
+ if emptycolumnwise:
+ return combine_every_nth(transposed)
+ else:
+ return ''.join(chain(*transposed))
-def column_transposition_decipher(message, keyword, fillvalue=' '):
+def column_transposition_decipher(message, keyword, fillvalue=' ',
+ fillcolumnwise=False,
+ emptycolumnwise=False):
"""Deciphers using the column transposition cipher.
Message is padded to allow all rows to be the same length.
- >>> column_transposition_decipher('hleolteher', 'clever')
+ >>> column_transposition_decipher('hellothere', 'abcde', fillcolumnwise=True, emptycolumnwise=True)
'hellothere'
- >>> column_transposition_decipher('hleolthre!e!', 'cleverly', fillvalue='?')
- 'hellothere!!'
- """
- return column_transposition_worker(message, keyword, encipher=False,
- fillvalue=fillvalue)
-
-def column_transposition_worker(message, keyword,
- encipher=True, fillvalue=' '):
- """Does the actual work of the column transposition cipher.
- Message is padded with spaces to allow all rows to be the same length.
-
- >>> column_transposition_worker('hellothere', 'clever')
- 'hleolteher'
- >>> column_transposition_worker('hellothere', 'clever', encipher=True)
- 'hleolteher'
- >>> column_transposition_worker('hleolteher', 'clever', encipher=False)
+ >>> column_transposition_decipher('hlohreltee', 'abcde', fillcolumnwise=True, emptycolumnwise=False)
+ 'hellothere'
+ >>> column_transposition_decipher('htehlelroe', 'abcde', fillcolumnwise=False, emptycolumnwise=True)
+ 'hellothere'
+ >>> column_transposition_decipher('hellothere', 'abcde', fillcolumnwise=False, emptycolumnwise=False)
+ 'hellothere'
+ >>> column_transposition_decipher('heotllrehe', 'clever', fillcolumnwise=True, emptycolumnwise=True)
+ 'hellothere'
+ >>> column_transposition_decipher('holrhetlee', 'clever', fillcolumnwise=True, emptycolumnwise=False)
+ 'hellothere'
+ >>> column_transposition_decipher('htleehoelr', 'clever', fillcolumnwise=False, emptycolumnwise=True)
+ 'hellothere'
+ >>> column_transposition_decipher('hleolteher', 'clever', fillcolumnwise=False, emptycolumnwise=False)
'hellothere'
"""
transpositions = transpositions_of(keyword)
- columns = every_nth(message, len(transpositions), fillvalue=fillvalue)
- if encipher:
- transposed_columns = transpose(columns, transpositions)
+ message += pad(len(message), len(transpositions), '*')
+ if emptycolumnwise:
+ rows = every_nth(message, len(message) // len(transpositions))
else:
- transposed_columns = untranspose(columns, transpositions)
- return combine_every_nth(transposed_columns)
+ rows = chunks(message, len(transpositions))
+ untransposed = [untranspose(r, transpositions) for r in rows]
+ if fillcolumnwise:
+ return combine_every_nth(untransposed)
+ else:
+ return ''.join(chain(*untransposed))
-def vigenere_encipher(message, keyword):
- """Vigenere encipher
+def scytale_encipher(message, rows, fillvalue=' '):
+ """Enciphers using the scytale transposition cipher.
+ Message is padded with spaces to allow all rows to be the same length.
- >>> vigenere_encipher('hello', 'abc')
- 'hfnlp'
+ >>> scytale_encipher('thequickbrownfox', 3)
+ 'tcnhkfeboqrxuo iw '
+ >>> scytale_encipher('thequickbrownfox', 4)
+ 'tubnhirfecooqkwx'
+ >>> scytale_encipher('thequickbrownfox', 5)
+ 'tubnhirfecooqkwx'
+ >>> scytale_encipher('thequickbrownfox', 6)
+ 'tqcrnxhukof eibwo '
+ >>> scytale_encipher('thequickbrownfox', 7)
+ 'tqcrnxhukof eibwo '
"""
- shifts = [ord(l) - ord('a') for l in sanitise(keyword)]
- pairs = zip(message, cycle(shifts))
- return ''.join([caesar_encipher_letter(l, k) for l, k in pairs])
-
-def vigenere_decipher(message, keyword):
- """Vigenere decipher
+ transpositions = [i for i in range(math.ceil(len(message) / rows))]
+ return column_transposition_encipher(message, transpositions,
+ fillcolumnwise=False, emptycolumnwise=True)
- >>> vigenere_decipher('hfnlp', 'abc')
- 'hello'
+def scytale_decipher(message, rows):
+ """Deciphers using the scytale transposition cipher.
+ Assumes the message is padded so that all rows are the same length.
+
+ >>> scytale_decipher('tcnhkfeboqrxuo iw ', 3)
+ 'thequickbrownfox '
+ >>> scytale_decipher('tubnhirfecooqkwx', 4)
+ 'thequickbrownfox'
+ >>> scytale_decipher('tubnhirfecooqkwx', 5)
+ 'thequickbrownfox'
+ >>> scytale_decipher('tqcrnxhukof eibwo ', 6)
+ 'thequickbrownfox '
+ >>> scytale_decipher('tqcrnxhukof eibwo ', 7)
+ 'thequickbrownfox '
"""
- shifts = [ord(l) - ord('a') for l in sanitise(keyword)]
- pairs = zip(message, cycle(shifts))
- return ''.join([caesar_decipher_letter(l, k) for l, k in pairs])
-
+ transpositions = [i for i in range(math.ceil(len(message) / rows))]
+ return column_transposition_decipher(message, transpositions,
+ fillcolumnwise=False, emptycolumnwise=True)
if __name__ == "__main__":