X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=cipher.py;h=f7e3ece485cf1800a1e1d703959b10f05b217cf2;hb=08af6491b3767197b997cdaf540636a7b320e266;hp=91e64e51e6f6fc403f088dd44b20db11b5f4593a;hpb=14ac787d794684b5aceacb307d687597e464e7b4;p=cipher-tools.git diff --git a/cipher.py b/cipher.py index 91e64e5..f7e3ece 100644 --- a/cipher.py +++ b/cipher.py @@ -2,7 +2,7 @@ import string import collections import math from enum import Enum -from itertools import zip_longest, cycle, chain +from itertools import zip_longest, cycle, chain, count import numpy as np from numpy import matrix from numpy import linalg @@ -671,6 +671,111 @@ def hill_decipher(matrix, message, fillvalue='a'): return hill_encipher(inverse_matrix, message, fillvalue) +# Where each piece of text ends up in the AMSCO transpositon cipher. +# 'index' shows where the slice appears in the plaintext, with the slice +# from 'start' to 'end' +AmscoSlice = collections.namedtuple('AmscoSlice', ['index', 'start', 'end']) + +def amsco_transposition_positions(message, keyword, + fillpattern=(1, 2), + fillcolumnwise=False, + emptycolumnwise=True): + """Creates the grid for the AMSCO transposition cipher. Each element in the + grid shows the index of that slice and the start and end positions of the + plaintext that go to make it up. + + >>> amsco_transposition_positions(string.ascii_lowercase, 'freddy', \ + fillpattern=(1, 2)) # doctest: +NORMALIZE_WHITESPACE + [[AmscoSlice(index=3, start=4, end=6), + AmscoSlice(index=2, start=3, end=4), + AmscoSlice(index=0, start=0, end=1), + AmscoSlice(index=1, start=1, end=3), + AmscoSlice(index=4, start=6, end=7)], + [AmscoSlice(index=8, start=12, end=13), + AmscoSlice(index=7, start=10, end=12), + AmscoSlice(index=5, start=7, end=9), + AmscoSlice(index=6, start=9, end=10), + AmscoSlice(index=9, start=13, end=15)], + [AmscoSlice(index=13, start=19, end=21), + AmscoSlice(index=12, start=18, end=19), + AmscoSlice(index=10, start=15, end=16), + AmscoSlice(index=11, start=16, end=18), + AmscoSlice(index=14, start=21, end=22)], + [AmscoSlice(index=18, start=27, end=28), + AmscoSlice(index=17, start=25, end=27), + AmscoSlice(index=15, start=22, end=24), + AmscoSlice(index=16, start=24, end=25), + AmscoSlice(index=19, start=28, end=30)]] + """ + transpositions = transpositions_of(keyword) + fill_iterator = cycle(fillpattern) + indices = count() + message_length = len(message) + + current_position = 0 + grid = [] + while current_position < message_length: + row = [] + for _ in range(len(transpositions)): + index = next(indices) + gap = next(fill_iterator) + row += [AmscoSlice(index, current_position, current_position + gap)] + current_position += gap + grid += [row] + return [transpose(r, transpositions) for r in grid] + +def amsco_transposition_encipher(message, keyword, fillpattern=(1,2)): + """AMSCO transposition encipher. + + >>> amsco_transposition_encipher('hellothere', 'abc', fillpattern=(1, 2)) + 'hoteelhler' + >>> amsco_transposition_encipher('hellothere', 'abc', fillpattern=(2, 1)) + 'hetelhelor' + >>> amsco_transposition_encipher('hellothere', 'acb', fillpattern=(1, 2)) + 'hotelerelh' + >>> amsco_transposition_encipher('hellothere', 'acb', fillpattern=(2, 1)) + 'hetelorlhe' + >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2)) + 'hecsoisttererteipexhomen' + >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(2, 1)) + 'heetcisooestrrepeixthemn' + >>> amsco_transposition_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2)) + 'hxomeiphscerettoisenteer' + """ + grid = amsco_transposition_positions(message, keyword, fillpattern=fillpattern) + ct_as_grid = [[message[s.start:s.end] for s in r] for r in grid] + return combine_every_nth(ct_as_grid) + + +def amsco_transposition_decipher(message, keyword, fillpattern=(1,2)): + """AMSCO transposition decipher + + >>> amsco_transposition_decipher('hoteelhler', 'abc', fillpattern=(1, 2)) + 'hellothere' + >>> amsco_transposition_decipher('hetelhelor', 'abc', fillpattern=(2, 1)) + 'hellothere' + >>> amsco_transposition_decipher('hotelerelh', 'acb', fillpattern=(1, 2)) + 'hellothere' + >>> amsco_transposition_decipher('hetelorlhe', 'acb', fillpattern=(2, 1)) + 'hellothere' + >>> amsco_transposition_decipher('hecsoisttererteipexhomen', 'cipher', fillpattern=(1, 2)) + 'hereissometexttoencipher' + >>> amsco_transposition_decipher('heetcisooestrrepeixthemn', 'cipher', fillpattern=(2, 1)) + 'hereissometexttoencipher' + >>> amsco_transposition_decipher('hxomeiphscerettoisenteer', 'cipher', fillpattern=(1, 3, 2)) + 'hereissometexttoencipher' + """ + + grid = amsco_transposition_positions(message, keyword, fillpattern=fillpattern) + transposed_sections = [s for c in [l for l in zip(*grid)] for s in c] + plaintext_list = [''] * len(transposed_sections) + current_pos = 0 + for slice in transposed_sections: + plaintext_list[slice.index] = message[current_pos:current_pos-slice.start+slice.end][:len(message[slice.start:slice.end])] + current_pos += len(message[slice.start:slice.end]) + return ''.join(plaintext_list) + + class PocketEnigma(object): """A pocket enigma machine The wheel is internally represented as a 26-element list self.wheel_map,