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

Module szyfrow.affine

+
+
+

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

+

The affine cipher operates one letter at a time. It converts each letter to a +number, then enciphers that number using a multiplier and a number. The result +is taken mod 26 and converted back into a letter.

+

For a multiplier m and adder a, a letter converted to number x is +enciphered as E(x) = (m x + a) mod 26. Deciphering uses the modular +inverse of m, m⁻¹, so that D(x) = m⁻¹ (x - a) mod 26.

+

If one_based is True, the conversion between letters and numbers maps +'a' → 1, 'b' → 2, … 'z'→ 26 and the mod function is adjusted to keep +numbers in this range during enciphering and deciphering. If one_based is +False, the conversion maps 'a' → 0, 'b' → 1, … 'z'→ 25 and mod behaves +normally.

+
+ +Expand source code + +
"""Enciphering and deciphering using the [affine cipher](https://en.wikipedia.org/wiki/Affine_cipher). 
+Also attempts to break messages that use an affine cipher.
+
+The affine cipher operates one letter at a time. It converts each letter to a 
+number, then enciphers that number using a multiplier and a number. The result 
+is taken mod 26 and converted back into a letter.
+
+For a multiplier _m_ and adder _a_, a letter converted to number _x_ is 
+enciphered as _E(x)_ = (_m_ _x_ + _a_) mod 26. Deciphering uses the modular 
+inverse of _m_, _m_⁻¹, so that _D(x)_ = _m_⁻¹ (_x_ - _a_) mod 26.
+
+If `one_based` is `True`, the conversion between letters and numbers maps 
+'a' → 1, 'b' → 2, … 'z'→ 26 and the `mod` function is adjusted to keep 
+numbers in this range during enciphering and deciphering. If `one_based` is 
+`False`, the conversion maps 'a' → 0, 'b' → 1, … 'z'→ 25 and `mod` behaves
+normally.
+"""
+
+from szyfrow.support.utilities import *
+from szyfrow.support.language_models import *
+
+modular_division_table = {
+    (multiplier, (multiplier * plaintext) % 26): plaintext
+    for plaintext in range(26) 
+    for multiplier in range(26)
+    }
+
+
+def affine_encipher_letter(accented_letter, multiplier=1, adder=0, one_based=True):
+    """Encipher a letter, given a multiplier and adder.
+
+    Accented version of latin letters (such as é and ö) are converted to their
+    non-accented versions before encryption.
+    
+    >>> cat(affine_encipher_letter(l, 3, 5, True) \
+            for l in string.ascii_letters)
+    'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE'
+    >>> cat(affine_encipher_letter(l, 3, 5, False) \
+            for l in string.ascii_letters)
+    'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC'
+    """
+    letter = unaccent(accented_letter)
+    if letter in string.ascii_letters:
+        letter_number = pos(letter)
+        if one_based: letter_number += 1
+        cipher_number = (letter_number * multiplier + adder) % 26
+        if one_based: cipher_number -= 1
+        if letter in string.ascii_uppercase:
+            return unpos(cipher_number).upper()
+        else:
+            return unpos(cipher_number)
+    else:
+        return letter
+
+def affine_decipher_letter(letter, multiplier=1, adder=0, one_based=True):
+    """Encipher a letter, given a multiplier and adder
+    
+    >>> cat(affine_decipher_letter(l, 3, 5, True) \
+            for l in 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE')
+    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+    >>> cat(affine_decipher_letter(l, 3, 5, False) \
+            for l in 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC')
+    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+    """
+    if letter in string.ascii_letters:
+        cipher_number = pos(letter)
+        if one_based: cipher_number += 1
+        plaintext_number = ( 
+            modular_division_table[multiplier, (cipher_number - adder) % 26]
+            )
+        if one_based: plaintext_number -= 1
+        if letter in string.ascii_uppercase:
+            return unpos(plaintext_number).upper()
+        else:
+            return unpos(plaintext_number) 
+    else:
+        return letter
+
+def affine_encipher(message, multiplier=1, adder=0, one_based=True):
+    """Encipher a message
+    
+    >>> affine_encipher('hours passed during which jerico tried every ' \
+           'trick he could think of', 15, 22, True)
+    'lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls omytd jlaxe mh'
+    """
+    enciphered = [affine_encipher_letter(l, multiplier, adder, one_based) 
+                  for l in message]
+    return cat(enciphered)
+
+def affine_decipher(message, multiplier=1, adder=0, one_based=True):
+    """Decipher a message
+    
+    >>> affine_decipher('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg ' \
+           'jfaoe ls omytd jlaxe mh', 15, 22, True)
+    'hours passed during which jerico tried every trick he could think of'
+    """
+    enciphered = [affine_decipher_letter(l, multiplier, adder, one_based) 
+                  for l in message]
+    return cat(enciphered)
+
+
+
+def affine_break(message, fitness=Pletters):
+    """Breaks an affine cipher using frequency analysis.
+
+    It tries all possible combinations of multiplier, adder, and one_based,
+    scores the fitness of the text decipherd with each combination, and returns
+    the key that produces the most fit deciphered text.
+
+    >>> affine_break('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls ' \
+          'omytd jlaxe mh jm bfmibj umis hfsul axubafkjamx. ls kffkxwsd jls ' \
+          'ofgbjmwfkiu olfmxmtmwaokttg jlsx ls kffkxwsd jlsi zg tsxwjl. jlsx ' \
+          'ls umfjsd jlsi zg hfsqysxog. ls dmmdtsd mx jls bats mh bkbsf. ls ' \
+          'bfmctsd kfmyxd jls lyj, mztanamyu xmc jm clm cku tmmeaxw kj lai ' \
+          'kxd clm ckuxj.') # doctest: +ELLIPSIS
+    ((15, 22, True), -340.601181913...)
+    """
+    sanitised_message = sanitise(message)
+    best_multiplier = 0
+    best_adder = 0
+    best_one_based = True
+    best_fit = float("-inf")
+    for one_based in [True, False]:
+        for multiplier in [x for x in range(1, 26, 2) if x != 13]:
+            for adder in range(26):
+                plaintext = affine_decipher(sanitised_message,
+                                            multiplier, adder, one_based)
+                fit = fitness(plaintext)
+                if fit > best_fit:
+                    best_fit = fit
+                    best_multiplier = multiplier
+                    best_adder = adder
+                    best_one_based = one_based
+
+    return (best_multiplier, best_adder, best_one_based), best_fit
+
+
+
+
+
+
+
+

Functions

+
+
+def affine_break(message, fitness=<function Pletters>) +
+
+

Breaks an affine cipher using frequency analysis.

+

It tries all possible combinations of multiplier, adder, and one_based, +scores the fitness of the text decipherd with each combination, and returns +the key that produces the most fit deciphered text.

+
>>> affine_break('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls '           'omytd jlaxe mh jm bfmibj umis hfsul axubafkjamx. ls kffkxwsd jls '           'ofgbjmwfkiu olfmxmtmwaokttg jlsx ls kffkxwsd jlsi zg tsxwjl. jlsx '           'ls umfjsd jlsi zg hfsqysxog. ls dmmdtsd mx jls bats mh bkbsf. ls '           'bfmctsd kfmyxd jls lyj, mztanamyu xmc jm clm cku tmmeaxw kj lai '           'kxd clm ckuxj.') # doctest: +ELLIPSIS
+((15, 22, True), -340.601181913...)
+
+
+ +Expand source code + +
def affine_break(message, fitness=Pletters):
+    """Breaks an affine cipher using frequency analysis.
+
+    It tries all possible combinations of multiplier, adder, and one_based,
+    scores the fitness of the text decipherd with each combination, and returns
+    the key that produces the most fit deciphered text.
+
+    >>> affine_break('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls ' \
+          'omytd jlaxe mh jm bfmibj umis hfsul axubafkjamx. ls kffkxwsd jls ' \
+          'ofgbjmwfkiu olfmxmtmwaokttg jlsx ls kffkxwsd jlsi zg tsxwjl. jlsx ' \
+          'ls umfjsd jlsi zg hfsqysxog. ls dmmdtsd mx jls bats mh bkbsf. ls ' \
+          'bfmctsd kfmyxd jls lyj, mztanamyu xmc jm clm cku tmmeaxw kj lai ' \
+          'kxd clm ckuxj.') # doctest: +ELLIPSIS
+    ((15, 22, True), -340.601181913...)
+    """
+    sanitised_message = sanitise(message)
+    best_multiplier = 0
+    best_adder = 0
+    best_one_based = True
+    best_fit = float("-inf")
+    for one_based in [True, False]:
+        for multiplier in [x for x in range(1, 26, 2) if x != 13]:
+            for adder in range(26):
+                plaintext = affine_decipher(sanitised_message,
+                                            multiplier, adder, one_based)
+                fit = fitness(plaintext)
+                if fit > best_fit:
+                    best_fit = fit
+                    best_multiplier = multiplier
+                    best_adder = adder
+                    best_one_based = one_based
+
+    return (best_multiplier, best_adder, best_one_based), best_fit
+
+
+
+def affine_decipher(message, multiplier=1, adder=0, one_based=True) +
+
+

Decipher a message

+
>>> affine_decipher('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg '            'jfaoe ls omytd jlaxe mh', 15, 22, True)
+'hours passed during which jerico tried every trick he could think of'
+
+
+ +Expand source code + +
def affine_decipher(message, multiplier=1, adder=0, one_based=True):
+    """Decipher a message
+    
+    >>> affine_decipher('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg ' \
+           'jfaoe ls omytd jlaxe mh', 15, 22, True)
+    'hours passed during which jerico tried every trick he could think of'
+    """
+    enciphered = [affine_decipher_letter(l, multiplier, adder, one_based) 
+                  for l in message]
+    return cat(enciphered)
+
+
+
+def affine_decipher_letter(letter, multiplier=1, adder=0, one_based=True) +
+
+

Encipher a letter, given a multiplier and adder

+
>>> cat(affine_decipher_letter(l, 3, 5, True)             for l in 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE')
+'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+>>> cat(affine_decipher_letter(l, 3, 5, False)             for l in 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC')
+'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+
+
+ +Expand source code + +
def affine_decipher_letter(letter, multiplier=1, adder=0, one_based=True):
+    """Encipher a letter, given a multiplier and adder
+    
+    >>> cat(affine_decipher_letter(l, 3, 5, True) \
+            for l in 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE')
+    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+    >>> cat(affine_decipher_letter(l, 3, 5, False) \
+            for l in 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC')
+    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+    """
+    if letter in string.ascii_letters:
+        cipher_number = pos(letter)
+        if one_based: cipher_number += 1
+        plaintext_number = ( 
+            modular_division_table[multiplier, (cipher_number - adder) % 26]
+            )
+        if one_based: plaintext_number -= 1
+        if letter in string.ascii_uppercase:
+            return unpos(plaintext_number).upper()
+        else:
+            return unpos(plaintext_number) 
+    else:
+        return letter
+
+
+
+def affine_encipher(message, multiplier=1, adder=0, one_based=True) +
+
+

Encipher a message

+
>>> affine_encipher('hours passed during which jerico tried every '            'trick he could think of', 15, 22, True)
+'lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls omytd jlaxe mh'
+
+
+ +Expand source code + +
def affine_encipher(message, multiplier=1, adder=0, one_based=True):
+    """Encipher a message
+    
+    >>> affine_encipher('hours passed during which jerico tried every ' \
+           'trick he could think of', 15, 22, True)
+    'lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls omytd jlaxe mh'
+    """
+    enciphered = [affine_encipher_letter(l, multiplier, adder, one_based) 
+                  for l in message]
+    return cat(enciphered)
+
+
+
+def affine_encipher_letter(accented_letter, multiplier=1, adder=0, one_based=True) +
+
+

Encipher a letter, given a multiplier and adder.

+

Accented version of latin letters (such as é and ö) are converted to their +non-accented versions before encryption.

+
>>> cat(affine_encipher_letter(l, 3, 5, True)             for l in string.ascii_letters)
+'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE'
+>>> cat(affine_encipher_letter(l, 3, 5, False)             for l in string.ascii_letters)
+'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC'
+
+
+ +Expand source code + +
def affine_encipher_letter(accented_letter, multiplier=1, adder=0, one_based=True):
+    """Encipher a letter, given a multiplier and adder.
+
+    Accented version of latin letters (such as é and ö) are converted to their
+    non-accented versions before encryption.
+    
+    >>> cat(affine_encipher_letter(l, 3, 5, True) \
+            for l in string.ascii_letters)
+    'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE'
+    >>> cat(affine_encipher_letter(l, 3, 5, False) \
+            for l in string.ascii_letters)
+    'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC'
+    """
+    letter = unaccent(accented_letter)
+    if letter in string.ascii_letters:
+        letter_number = pos(letter)
+        if one_based: letter_number += 1
+        cipher_number = (letter_number * multiplier + adder) % 26
+        if one_based: cipher_number -= 1
+        if letter in string.ascii_uppercase:
+            return unpos(cipher_number).upper()
+        else:
+            return unpos(cipher_number)
+    else:
+        return letter
+
+
+
+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'

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