X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=docs%2Fszyfrow%2Famsco.html;fp=docs%2Fszyfrow%2Famsco.html;h=ad2caf13b70bb98e7ededf4fc4056fb5f5829f1b;hb=b535d9d75e69cc395e8de28c99e38564655e5ac9;hp=0000000000000000000000000000000000000000;hpb=f19a021eabb3222709b9d513839a14c01cfdfd38;p=szyfrow.git diff --git a/docs/szyfrow/amsco.html b/docs/szyfrow/amsco.html new file mode 100644 index 0000000..ad2caf1 --- /dev/null +++ b/docs/szyfrow/amsco.html @@ -0,0 +1,765 @@ + + + + + + +szyfrow.amsco API documentation + + + + + + + + + + + +
+
+
+

Module szyfrow.amsco

+
+
+

Enciphering and deciphering using the Amsco cipher. +Also attempts to break messages that use an Amsco cipher.

+

The Amsco cipher is a column transpositoin cipher. The plaintext is laid out, +row by row, into columns. However, different numbers of letters are laid out +in each cell, typically in a 1-2 pattern.

+

It's clearer with an example. Consider we're using the keyword "perceptive", +which turns into "perctiv". The text ""It is a truth universally +acknowledged, that a single man in, possession of a good fortune, must be in +want of a wife." is laid out in seven columns like this:

+
p  e  r  c  t  i  v
+--------------------
+i  ti s  at r  ut h 
+un i  ve r  sa l  ly 
+a  ck n  ow l  ed g 
+ed t  ha t  as i  ng 
+l  em a  ni n  po s 
+se s  si o  no f  ag 
+o  od f  or t  un e 
+mu s  tb e  in w  an 
+t  of a  wi f  e
+
+

The ciphertext is read out in columns, according to the order of the keyword. +In this example, the "c" column is read first, then the "e" column, and so on. +That gives the ciphertext of "atrowtnioorewi tiicktemsodsof utledipofunwe +iunaedlseomut svenhaasiftba rsalasnnotinf hlygngsagean".

+
+ +Expand source code + +
"""Enciphering and deciphering using the [Amsco cipher](http://ericbrandel.com/2016/10/09/the-amsco-cipher/). 
+Also attempts to break messages that use an Amsco cipher.
+
+The Amsco cipher is a column transpositoin cipher. The plaintext is laid out, 
+row by row, into columns. However, different numbers of letters are laid out
+in each cell, typically in a 1-2 pattern.
+
+It's clearer with an example. Consider we're using the keyword "perceptive", 
+which turns into "perctiv". The text ""It is a truth universally 
+acknowledged, that a single man in, possession of a good fortune, must be in 
+want of a wife." is laid out in seven columns like this:
+
+    p  e  r  c  t  i  v
+    --------------------
+    i  ti s  at r  ut h 
+    un i  ve r  sa l  ly 
+    a  ck n  ow l  ed g 
+    ed t  ha t  as i  ng 
+    l  em a  ni n  po s 
+    se s  si o  no f  ag 
+    o  od f  or t  un e 
+    mu s  tb e  in w  an 
+    t  of a  wi f  e
+
+The ciphertext is read out in columns, according to the order of the keyword.
+In this example, the "c" column is read first, then the "e" column, and so on.
+That gives the ciphertext of "atrowtnioorewi tiicktemsodsof utledipofunwe 
+iunaedlseomut svenhaasiftba rsalasnnotinf hlygngsagean".
+"""
+
+from enum import Enum
+import multiprocessing 
+import itertools
+
+from szyfrow.support.utilities import *
+from szyfrow.support.language_models import *
+
+__pdoc__ = {}
+
+AmscoSlice = collections.namedtuple('AmscoSlice', ['index', 'start', 'end'])
+__pdoc__['AmscoSlice'] = """Where each piece of plainatext ends up in the AMSCO 
+transpositon cipher."""
+__pdoc__['AmscoSlice.index'] = """Where the slice appears in the plaintext"""
+__pdoc__['AmscoSlice.start'] = """Where the slice starts in the plaintext"""
+__pdoc__['AmscoSlice.end'] = """Where the slice ends in the plaintext"""
+
+class AmscoFillStyle(Enum):
+    """Different methods of filling the grid.
+    * `continuous`: continue the fillpattern unbroken by row boundaries
+    * `same_each_row`: each row has the same fillpattern
+    * `reverse_each_row`: each row has the reversed fillpattern to the row above
+    """
+    continuous = 1
+    same_each_row = 2
+    reverse_each_row = 3
+
+def amsco_positions(message, keyword, 
+      fillpattern=(1, 2),
+      fillstyle=AmscoFillStyle.continuous,
+      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_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 = itertools.cycle(fillpattern)
+    indices = itertools.count()
+    message_length = len(message)
+
+    current_position = 0
+    grid = []
+    current_fillpattern = fillpattern
+    while current_position < message_length:
+        row = []
+        if fillstyle == AmscoFillStyle.same_each_row:
+            fill_iterator = itertools.cycle(fillpattern)
+        if fillstyle == AmscoFillStyle.reverse_each_row:
+            fill_iterator = itertools.cycle(current_fillpattern)
+        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]
+        if fillstyle == AmscoFillStyle.reverse_each_row:
+            current_fillpattern = list(reversed(current_fillpattern))
+    return [transpose(r, transpositions) for r in grid]
+
+def amsco_encipher(message, keyword, 
+    fillpattern=(1,2), fillstyle=AmscoFillStyle.reverse_each_row):
+    """AMSCO transposition encipher.
+
+    >>> amsco_encipher('hellothere', 'abc', fillpattern=(1, 2))
+    'hoteelhler'
+    >>> amsco_encipher('hellothere', 'abc', fillpattern=(2, 1))
+    'hetelhelor'
+    >>> amsco_encipher('hellothere', 'acb', fillpattern=(1, 2))
+    'hotelerelh'
+    >>> amsco_encipher('hellothere', 'acb', fillpattern=(2, 1))
+    'hetelorlhe'
+    >>> amsco_encipher('hereissometexttoencipher', 'encode')
+    'etecstthhomoerereenisxip'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2))
+    'hetcsoeisterereipexthomn'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous)
+    'hecsoisttererteipexhomen'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(2, 1))
+    'heecisoosttrrtepeixhemen'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2))
+    'hxtomephescieretoeisnter'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous)
+    'hxomeiphscerettoisenteer'
+    """
+    grid = amsco_positions(message, keyword, 
+        fillpattern=fillpattern, fillstyle=fillstyle)
+    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_decipher(message, keyword, 
+    fillpattern=(1,2), fillstyle=AmscoFillStyle.reverse_each_row):
+    """AMSCO transposition decipher
+
+    >>> amsco_decipher('hoteelhler', 'abc', fillpattern=(1, 2))
+    'hellothere'
+    >>> amsco_decipher('hetelhelor', 'abc', fillpattern=(2, 1))
+    'hellothere'
+    >>> amsco_decipher('hotelerelh', 'acb', fillpattern=(1, 2))
+    'hellothere'
+    >>> amsco_decipher('hetelorlhe', 'acb', fillpattern=(2, 1))
+    'hellothere'
+    >>> amsco_decipher('etecstthhomoerereenisxip', 'encode')
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hetcsoeisterereipexthomn', 'cipher', fillpattern=(1, 2))
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hecsoisttererteipexhomen', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous)
+    'hereissometexttoencipher'
+    >>> amsco_decipher('heecisoosttrrtepeixhemen', 'cipher', fillpattern=(2, 1))
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hxtomephescieretoeisnter', 'cipher', fillpattern=(1, 3, 2))
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hxomeiphscerettoisenteer', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous)
+    'hereissometexttoencipher'
+    """
+
+    grid = amsco_positions(message, keyword, 
+        fillpattern=fillpattern, fillstyle=fillstyle)
+    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 cat(plaintext_list)
+
+
+def amsco_break(message, translist=None, patterns = [(1, 2), (2, 1)],
+                                  fillstyles = [AmscoFillStyle.continuous, 
+                                                AmscoFillStyle.same_each_row, 
+                                                AmscoFillStyle.reverse_each_row],
+                                  fitness=Pbigrams, 
+                                  chunksize=500):
+    """Breaks an AMSCO transposition cipher using a dictionary and
+    n-gram frequency analysis.
+
+    If `translist` is not specified, use 
+    [`szyfrow.support.langauge_models.transpositions`](support/language_models.html#szyfrow.support.language_models.transpositions).
+
+    >>> amsco_break(amsco_encipher(sanitise( \
+            "It is a truth universally acknowledged, that a single man in \
+             possession of a good fortune, must be in want of a wife. However \
+             little known the feelings or views of such a man may be on his \
+             first entering a neighbourhood, this truth is so well fixed in \
+             the minds of the surrounding families, that he is considered the \
+             rightful property of some one or other of their daughters."), \
+        'encipher'), \
+        translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \
+                   (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \
+                   (6, 1, 0, 4, 5, 3, 2): ['keyword']}, \
+        patterns=[(1, 2)]) # doctest: +ELLIPSIS
+    (((2, 0, 5, 3, 1, 4, 6), (1, 2), <AmscoFillStyle.continuous: 1>), -709.4646722...)
+    >>> amsco_break(amsco_encipher(sanitise( \
+            "It is a truth universally acknowledged, that a single man in \
+             possession of a good fortune, must be in want of a wife. However \
+             little known the feelings or views of such a man may be on his \
+             first entering a neighbourhood, this truth is so well fixed in \
+             the minds of the surrounding families, that he is considered the \
+             rightful property of some one or other of their daughters."), \
+        'encipher', fillpattern=(2, 1)), \
+        translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \
+                   (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \
+                   (6, 1, 0, 4, 5, 3, 2): ['keyword']}, \
+        patterns=[(1, 2), (2, 1)], fitness=Ptrigrams) # doctest: +ELLIPSIS
+    (((2, 0, 5, 3, 1, 4, 6), (2, 1), <AmscoFillStyle.continuous: 1>), -997.0129085...)
+    """
+    if translist is None:
+        translist = transpositions
+    
+    with multiprocessing.Pool() as pool:
+        helper_args = [(message, trans, pattern, fillstyle, fitness)
+                       for trans in translist
+                       for pattern in patterns
+                       for fillstyle in fillstyles]
+        # Gotcha: the helper function here needs to be defined at the top level
+        #   (limitation of Pool.starmap)
+        breaks = pool.starmap(amsco_break_worker, helper_args, chunksize) 
+        return max(breaks, key=lambda k: k[1])
+
+def amsco_break_worker(message, transposition,
+        pattern, fillstyle, fitness):
+    plaintext = amsco_decipher(message, transposition,
+        fillpattern=pattern, fillstyle=fillstyle)
+    fit = fitness(sanitise(plaintext))
+    return (transposition, pattern, fillstyle), fit
+
+if __name__ == "__main__":
+    import doctest
+
+
+
+
+
+
+
+

Functions

+
+
+def amsco_break(message, translist=None, patterns=[(1, 2), (2, 1)], fillstyles=[<AmscoFillStyle.continuous: 1>, <AmscoFillStyle.same_each_row: 2>, <AmscoFillStyle.reverse_each_row: 3>], fitness=<function Pbigrams>, chunksize=500) +
+
+

Breaks an AMSCO transposition cipher using a dictionary and +n-gram frequency analysis.

+

If translist is not specified, use +szyfrow.support.langauge_models.transpositions.

+
>>> amsco_break(amsco_encipher(sanitise(             "It is a truth universally acknowledged, that a single man in              possession of a good fortune, must be in want of a wife. However              little known the feelings or views of such a man may be on his              first entering a neighbourhood, this truth is so well fixed in              the minds of the surrounding families, that he is considered the              rightful property of some one or other of their daughters."),         'encipher'),         translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'],                    (5, 0, 6, 1, 3, 4, 2): ['fourteen'],                    (6, 1, 0, 4, 5, 3, 2): ['keyword']},         patterns=[(1, 2)]) # doctest: +ELLIPSIS
+(((2, 0, 5, 3, 1, 4, 6), (1, 2), <AmscoFillStyle.continuous: 1>), -709.4646722...)
+>>> amsco_break(amsco_encipher(sanitise(             "It is a truth universally acknowledged, that a single man in              possession of a good fortune, must be in want of a wife. However              little known the feelings or views of such a man may be on his              first entering a neighbourhood, this truth is so well fixed in              the minds of the surrounding families, that he is considered the              rightful property of some one or other of their daughters."),         'encipher', fillpattern=(2, 1)),         translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'],                    (5, 0, 6, 1, 3, 4, 2): ['fourteen'],                    (6, 1, 0, 4, 5, 3, 2): ['keyword']},         patterns=[(1, 2), (2, 1)], fitness=Ptrigrams) # doctest: +ELLIPSIS
+(((2, 0, 5, 3, 1, 4, 6), (2, 1), <AmscoFillStyle.continuous: 1>), -997.0129085...)
+
+
+ +Expand source code + +
def amsco_break(message, translist=None, patterns = [(1, 2), (2, 1)],
+                                  fillstyles = [AmscoFillStyle.continuous, 
+                                                AmscoFillStyle.same_each_row, 
+                                                AmscoFillStyle.reverse_each_row],
+                                  fitness=Pbigrams, 
+                                  chunksize=500):
+    """Breaks an AMSCO transposition cipher using a dictionary and
+    n-gram frequency analysis.
+
+    If `translist` is not specified, use 
+    [`szyfrow.support.langauge_models.transpositions`](support/language_models.html#szyfrow.support.language_models.transpositions).
+
+    >>> amsco_break(amsco_encipher(sanitise( \
+            "It is a truth universally acknowledged, that a single man in \
+             possession of a good fortune, must be in want of a wife. However \
+             little known the feelings or views of such a man may be on his \
+             first entering a neighbourhood, this truth is so well fixed in \
+             the minds of the surrounding families, that he is considered the \
+             rightful property of some one or other of their daughters."), \
+        'encipher'), \
+        translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \
+                   (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \
+                   (6, 1, 0, 4, 5, 3, 2): ['keyword']}, \
+        patterns=[(1, 2)]) # doctest: +ELLIPSIS
+    (((2, 0, 5, 3, 1, 4, 6), (1, 2), <AmscoFillStyle.continuous: 1>), -709.4646722...)
+    >>> amsco_break(amsco_encipher(sanitise( \
+            "It is a truth universally acknowledged, that a single man in \
+             possession of a good fortune, must be in want of a wife. However \
+             little known the feelings or views of such a man may be on his \
+             first entering a neighbourhood, this truth is so well fixed in \
+             the minds of the surrounding families, that he is considered the \
+             rightful property of some one or other of their daughters."), \
+        'encipher', fillpattern=(2, 1)), \
+        translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], \
+                   (5, 0, 6, 1, 3, 4, 2): ['fourteen'], \
+                   (6, 1, 0, 4, 5, 3, 2): ['keyword']}, \
+        patterns=[(1, 2), (2, 1)], fitness=Ptrigrams) # doctest: +ELLIPSIS
+    (((2, 0, 5, 3, 1, 4, 6), (2, 1), <AmscoFillStyle.continuous: 1>), -997.0129085...)
+    """
+    if translist is None:
+        translist = transpositions
+    
+    with multiprocessing.Pool() as pool:
+        helper_args = [(message, trans, pattern, fillstyle, fitness)
+                       for trans in translist
+                       for pattern in patterns
+                       for fillstyle in fillstyles]
+        # Gotcha: the helper function here needs to be defined at the top level
+        #   (limitation of Pool.starmap)
+        breaks = pool.starmap(amsco_break_worker, helper_args, chunksize) 
+        return max(breaks, key=lambda k: k[1])
+
+
+
+def amsco_break_worker(message, transposition, pattern, fillstyle, fitness) +
+
+
+
+ +Expand source code + +
def amsco_break_worker(message, transposition,
+        pattern, fillstyle, fitness):
+    plaintext = amsco_decipher(message, transposition,
+        fillpattern=pattern, fillstyle=fillstyle)
+    fit = fitness(sanitise(plaintext))
+    return (transposition, pattern, fillstyle), fit
+
+
+
+def amsco_decipher(message, keyword, fillpattern=(1, 2), fillstyle=AmscoFillStyle.reverse_each_row) +
+
+

AMSCO transposition decipher

+
>>> amsco_decipher('hoteelhler', 'abc', fillpattern=(1, 2))
+'hellothere'
+>>> amsco_decipher('hetelhelor', 'abc', fillpattern=(2, 1))
+'hellothere'
+>>> amsco_decipher('hotelerelh', 'acb', fillpattern=(1, 2))
+'hellothere'
+>>> amsco_decipher('hetelorlhe', 'acb', fillpattern=(2, 1))
+'hellothere'
+>>> amsco_decipher('etecstthhomoerereenisxip', 'encode')
+'hereissometexttoencipher'
+>>> amsco_decipher('hetcsoeisterereipexthomn', 'cipher', fillpattern=(1, 2))
+'hereissometexttoencipher'
+>>> amsco_decipher('hecsoisttererteipexhomen', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous)
+'hereissometexttoencipher'
+>>> amsco_decipher('heecisoosttrrtepeixhemen', 'cipher', fillpattern=(2, 1))
+'hereissometexttoencipher'
+>>> amsco_decipher('hxtomephescieretoeisnter', 'cipher', fillpattern=(1, 3, 2))
+'hereissometexttoencipher'
+>>> amsco_decipher('hxomeiphscerettoisenteer', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous)
+'hereissometexttoencipher'
+
+
+ +Expand source code + +
def amsco_decipher(message, keyword, 
+    fillpattern=(1,2), fillstyle=AmscoFillStyle.reverse_each_row):
+    """AMSCO transposition decipher
+
+    >>> amsco_decipher('hoteelhler', 'abc', fillpattern=(1, 2))
+    'hellothere'
+    >>> amsco_decipher('hetelhelor', 'abc', fillpattern=(2, 1))
+    'hellothere'
+    >>> amsco_decipher('hotelerelh', 'acb', fillpattern=(1, 2))
+    'hellothere'
+    >>> amsco_decipher('hetelorlhe', 'acb', fillpattern=(2, 1))
+    'hellothere'
+    >>> amsco_decipher('etecstthhomoerereenisxip', 'encode')
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hetcsoeisterereipexthomn', 'cipher', fillpattern=(1, 2))
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hecsoisttererteipexhomen', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous)
+    'hereissometexttoencipher'
+    >>> amsco_decipher('heecisoosttrrtepeixhemen', 'cipher', fillpattern=(2, 1))
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hxtomephescieretoeisnter', 'cipher', fillpattern=(1, 3, 2))
+    'hereissometexttoencipher'
+    >>> amsco_decipher('hxomeiphscerettoisenteer', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous)
+    'hereissometexttoencipher'
+    """
+
+    grid = amsco_positions(message, keyword, 
+        fillpattern=fillpattern, fillstyle=fillstyle)
+    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 cat(plaintext_list)
+
+
+
+def amsco_encipher(message, keyword, fillpattern=(1, 2), fillstyle=AmscoFillStyle.reverse_each_row) +
+
+

AMSCO transposition encipher.

+
>>> amsco_encipher('hellothere', 'abc', fillpattern=(1, 2))
+'hoteelhler'
+>>> amsco_encipher('hellothere', 'abc', fillpattern=(2, 1))
+'hetelhelor'
+>>> amsco_encipher('hellothere', 'acb', fillpattern=(1, 2))
+'hotelerelh'
+>>> amsco_encipher('hellothere', 'acb', fillpattern=(2, 1))
+'hetelorlhe'
+>>> amsco_encipher('hereissometexttoencipher', 'encode')
+'etecstthhomoerereenisxip'
+>>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2))
+'hetcsoeisterereipexthomn'
+>>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous)
+'hecsoisttererteipexhomen'
+>>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(2, 1))
+'heecisoosttrrtepeixhemen'
+>>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2))
+'hxtomephescieretoeisnter'
+>>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous)
+'hxomeiphscerettoisenteer'
+
+
+ +Expand source code + +
def amsco_encipher(message, keyword, 
+    fillpattern=(1,2), fillstyle=AmscoFillStyle.reverse_each_row):
+    """AMSCO transposition encipher.
+
+    >>> amsco_encipher('hellothere', 'abc', fillpattern=(1, 2))
+    'hoteelhler'
+    >>> amsco_encipher('hellothere', 'abc', fillpattern=(2, 1))
+    'hetelhelor'
+    >>> amsco_encipher('hellothere', 'acb', fillpattern=(1, 2))
+    'hotelerelh'
+    >>> amsco_encipher('hellothere', 'acb', fillpattern=(2, 1))
+    'hetelorlhe'
+    >>> amsco_encipher('hereissometexttoencipher', 'encode')
+    'etecstthhomoerereenisxip'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2))
+    'hetcsoeisterereipexthomn'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous)
+    'hecsoisttererteipexhomen'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(2, 1))
+    'heecisoosttrrtepeixhemen'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2))
+    'hxtomephescieretoeisnter'
+    >>> amsco_encipher('hereissometexttoencipher', 'cipher', fillpattern=(1, 3, 2), fillstyle=AmscoFillStyle.continuous)
+    'hxomeiphscerettoisenteer'
+    """
+    grid = amsco_positions(message, keyword, 
+        fillpattern=fillpattern, fillstyle=fillstyle)
+    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_positions(message, keyword, fillpattern=(1, 2), fillstyle=AmscoFillStyle.continuous, 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_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)]]
+
+
+ +Expand source code + +
def amsco_positions(message, keyword, 
+      fillpattern=(1, 2),
+      fillstyle=AmscoFillStyle.continuous,
+      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_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 = itertools.cycle(fillpattern)
+    indices = itertools.count()
+    message_length = len(message)
+
+    current_position = 0
+    grid = []
+    current_fillpattern = fillpattern
+    while current_position < message_length:
+        row = []
+        if fillstyle == AmscoFillStyle.same_each_row:
+            fill_iterator = itertools.cycle(fillpattern)
+        if fillstyle == AmscoFillStyle.reverse_each_row:
+            fill_iterator = itertools.cycle(current_fillpattern)
+        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]
+        if fillstyle == AmscoFillStyle.reverse_each_row:
+            current_fillpattern = list(reversed(current_fillpattern))
+    return [transpose(r, transpositions) for r in grid]
+
+
+
+def cat(iterable, /) +
+
+

Concatenate any number of strings.

+

The string whose method is called is inserted in between each given string. +The result is returned as a new string.

+

Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'

+
+
+def lcat(iterable, /) +
+
+

Concatenate any number of strings.

+

The string whose method is called is inserted in between each given string. +The result is returned as a new string.

+

Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'

+
+
+def wcat(iterable, /) +
+
+

Concatenate any number of strings.

+

The string whose method is called is inserted in between each given string. +The result is returned as a new string.

+

Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'

+
+
+
+
+

Classes

+
+
+class AmscoFillStyle +(value, names=None, *, module=None, qualname=None, type=None, start=1) +
+
+

Different methods of filling the grid. +* continuous: continue the fillpattern unbroken by row boundaries +* same_each_row: each row has the same fillpattern +* reverse_each_row: each row has the reversed fillpattern to the row above

+
+ +Expand source code + +
class AmscoFillStyle(Enum):
+    """Different methods of filling the grid.
+    * `continuous`: continue the fillpattern unbroken by row boundaries
+    * `same_each_row`: each row has the same fillpattern
+    * `reverse_each_row`: each row has the reversed fillpattern to the row above
+    """
+    continuous = 1
+    same_each_row = 2
+    reverse_each_row = 3
+
+

Ancestors

+
    +
  • enum.Enum
  • +
+

Class variables

+
+
var continuous
+
+
+
+
var reverse_each_row
+
+
+
+
var same_each_row
+
+
+
+
+
+
+class AmscoSlice +(index, start, end) +
+
+

Where each piece of plainatext ends up in the AMSCO +transpositon cipher.

+

Ancestors

+
    +
  • builtins.tuple
  • +
+

Instance variables

+
+
var end
+
+

Where the slice ends in the plaintext

+
+
var index
+
+

Where the slice appears in the plaintext

+
+
var start
+
+

Where the slice starts in the plaintext

+
+
+
+
+
+
+ +
+ + + \ No newline at end of file