},
{
"cell_type": "code",
- "execution_count": 733,
+ "execution_count": 1,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 734,
+ "execution_count": 2,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 735,
+ "execution_count": 3,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 736,
+ "execution_count": 4,
"metadata": {
"collapsed": false
},
" ('y', 'z')]"
]
},
- "execution_count": 736,
+ "execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 737,
+ "execution_count": 5,
"metadata": {
"collapsed": false
},
"'zyxwcabdefghijklmnopqrstuv'"
]
},
- "execution_count": 737,
+ "execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 738,
+ "execution_count": 6,
"metadata": {
"collapsed": false,
"scrolled": true
},
{
"cell_type": "code",
- "execution_count": 739,
+ "execution_count": 7,
"metadata": {
"collapsed": false,
"scrolled": true
" 24])"
]
},
- "execution_count": 739,
+ "execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 740,
+ "execution_count": 8,
"metadata": {
"collapsed": false,
"scrolled": true
" 0])"
]
},
- "execution_count": 740,
+ "execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 741,
+ "execution_count": 9,
"metadata": {
"collapsed": false
},
"'zyxwcabdefghijklmnopqrstuv'"
]
},
- "execution_count": 741,
+ "execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 742,
+ "execution_count": 10,
"metadata": {
"collapsed": false
},
"'fgehijklmnopqrstuvwxyzdcba'"
]
},
- "execution_count": 742,
+ "execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 743,
+ "execution_count": 11,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 744,
+ "execution_count": 12,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 745,
+ "execution_count": 13,
"metadata": {
"collapsed": false
},
"'zycdefghijklmnopqrstuvwxba'"
]
},
- "execution_count": 745,
+ "execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 746,
+ "execution_count": 14,
"metadata": {
"collapsed": false
},
"'zycdefghijklmnopqrstuvwxba'"
]
},
- "execution_count": 746,
+ "execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 747,
+ "execution_count": 15,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 748,
+ "execution_count": 16,
"metadata": {
"collapsed": false
},
"('zycdefghijklmnopqrstuvwxba', 'zycdefghijklmnopqrstuvwxba')"
]
},
- "execution_count": 748,
+ "execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 749,
+ "execution_count": 17,
"metadata": {
"collapsed": false
},
"('ugcdypblnzkhmisfrqoxavwtej', 'ugcdypblnzkhmisfrqoxavwtej')"
]
},
- "execution_count": 749,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 750,
+ "execution_count": 18,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 751,
+ "execution_count": 19,
"metadata": {
"collapsed": false
},
" ('v', 'w')]"
]
},
- "execution_count": 751,
+ "execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 752,
+ "execution_count": 20,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 753,
+ "execution_count": 21,
"metadata": {
"collapsed": false
},
"'yruhqsldpxngokmiebfzcwvjat'"
]
},
- "execution_count": 753,
+ "execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 754,
+ "execution_count": 22,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 755,
+ "execution_count": 23,
"metadata": {
"collapsed": false
},
"'fvpjiaoyedrzxwgctkuqsbnmhl'"
]
},
- "execution_count": 755,
+ "execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 756,
+ "execution_count": 24,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 757,
+ "execution_count": 25,
"metadata": {
"collapsed": false,
"scrolled": true
" ('z', 'j')]"
]
},
- "execution_count": 757,
+ "execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 758,
+ "execution_count": 26,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 759,
+ "execution_count": 27,
"metadata": {
"collapsed": false
},
"('ekmflgdqvzntowyhxuspaibrcj', 'uwygadfpvzbeckmthxslrinqoj')"
]
},
- "execution_count": 759,
+ "execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 760,
+ "execution_count": 28,
"metadata": {
"collapsed": false
},
"'a'"
]
},
- "execution_count": 760,
+ "execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 761,
+ "execution_count": 29,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 762,
+ "execution_count": 30,
"metadata": {
"collapsed": false
},
"('ajdksiruxblhwtmcqgznpyfvoe', 'ajpczwrlfbdkotyuqgenhxmivs')"
]
},
- "execution_count": 762,
+ "execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 763,
+ "execution_count": 31,
"metadata": {
"collapsed": false
},
"('bdfhjlcprtxvznyeiwgakmusqo', 'tagbpcsdqeufvnzhyixjwlrkom')"
]
},
- "execution_count": 763,
+ "execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 764,
+ "execution_count": 32,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 765,
+ "execution_count": 33,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 766,
+ "execution_count": 34,
"metadata": {
"collapsed": false
},
"(1, [20])"
]
},
- "execution_count": 766,
+ "execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 767,
+ "execution_count": 35,
"metadata": {
"collapsed": false
},
"(25, [24, 11])"
]
},
- "execution_count": 767,
+ "execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 768,
+ "execution_count": 36,
"metadata": {
"collapsed": false,
"scrolled": true
},
{
"cell_type": "code",
- "execution_count": 769,
+ "execution_count": 37,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 770,
+ "execution_count": 38,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 771,
+ "execution_count": 39,
"metadata": {
"collapsed": false
},
"(0, 'c', [23, 10])"
]
},
- "execution_count": 771,
+ "execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 772,
+ "execution_count": 40,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 773,
+ "execution_count": 41,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 774,
+ "execution_count": 42,
"metadata": {
"collapsed": false
},
"'u'"
]
},
- "execution_count": 774,
+ "execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 775,
+ "execution_count": 43,
"metadata": {
"collapsed": false
},
"'a'"
]
},
- "execution_count": 775,
+ "execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 776,
+ "execution_count": 44,
"metadata": {
"collapsed": false
},
"'uejobtpzwcnsrkdgvmlfaqiyxh'"
]
},
- "execution_count": 776,
+ "execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 777,
+ "execution_count": 45,
"metadata": {
"collapsed": false,
"scrolled": true
},
{
"cell_type": "code",
- "execution_count": 778,
+ "execution_count": 46,
"metadata": {
"collapsed": false,
"scrolled": true
},
{
"cell_type": "code",
- "execution_count": 779,
+ "execution_count": 47,
"metadata": {
"collapsed": false,
"scrolled": true
},
{
"cell_type": "code",
- "execution_count": 780,
+ "execution_count": 48,
"metadata": {
"collapsed": false
},
"'olpfhnvflyn'"
]
},
- "execution_count": 780,
+ "execution_count": 48,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 781,
+ "execution_count": 49,
"metadata": {
"collapsed": false
},
"'lawnjgpwjik'"
]
},
- "execution_count": 781,
+ "execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 782,
+ "execution_count": 50,
"metadata": {
"collapsed": false
},
},
{
"cell_type": "code",
- "execution_count": 783,
+ "execution_count": 51,
"metadata": {
"collapsed": false
},
"'bahxvfrpdc'"
]
},
- "execution_count": 783,
+ "execution_count": 51,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 784,
+ "execution_count": 52,
"metadata": {
"collapsed": false
},
"'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr'"
]
},
- "execution_count": 784,
+ "execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 785,
+ "execution_count": 53,
"metadata": {
"collapsed": false
},
"'c'"
]
},
- "execution_count": 785,
+ "execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 786,
+ "execution_count": 54,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 787,
+ "execution_count": 55,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 788,
+ "execution_count": 56,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 789,
+ "execution_count": 57,
"metadata": {
"collapsed": true
},
},
{
"cell_type": "code",
- "execution_count": 790,
+ "execution_count": 58,
"metadata": {
"collapsed": false
},
" 'urygzpdmxtwshqvfnbljaokice')"
]
},
- "execution_count": 790,
+ "execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 791,
+ "execution_count": 59,
"metadata": {
"collapsed": false
},
--- /dev/null
+
+# coding: utf-8
+
+# # Enigma machine
+# Specification from [Codes and Ciphers](http://www.codesandciphers.org.uk/enigma/rotorspec.htm) page.
+#
+# Example Enigma machines from [Louise Dale](http://enigma.louisedade.co.uk/enigma.html) (full simulation) and [EnigmaCo](http://enigmaco.de/enigma/enigma.html) (good animation of the wheels, but no ring settings).
+#
+# There's also the nice Enigma simulator for Android by [Franklin Heath](https://franklinheath.co.uk/2012/02/04/our-first-app-published-enigma-simulator/), available on the [Google Play store](https://play.google.com/store/apps/details?id=uk.co.franklinheath.enigmasim&hl=en_GB).
+
+
+
+import string
+import collections
+
+# Some convenience functions
+
+cat = ''.join
+
+def clean(text): return cat(l.lower() for l in text if l in string.ascii_letters)
+
+def pos(letter):
+ if letter in string.ascii_lowercase:
+ return ord(letter) - ord('a')
+ elif letter in string.ascii_uppercase:
+ return ord(letter) - ord('A')
+ else:
+ return ''
+
+def unpos(number): return chr(number % 26 + ord('a'))
+
+
+wheel_i_spec = 'ekmflgdqvzntowyhxuspaibrcj'
+wheel_ii_spec = 'ajdksiruxblhwtmcqgznpyfvoe'
+wheel_iii_spec = 'bdfhjlcprtxvznyeiwgakmusqo'
+wheel_iv_spec = 'esovpzjayquirhxlnftgkdcmwb'
+wheel_v_spec = 'vzbrgityupsdnhlxawmjqofeck'
+wheel_vi_spec = 'jpgvoumfyqbenhzrdkasxlictw'
+wheel_vii_spec = 'nzjhgrcxmyswboufaivlpekqdt'
+wheel_viii_spec = 'fkqhtlxocbjspdzramewniuygv'
+beta_wheel_spec = 'leyjvcnixwpbqmdrtakzgfuhos'
+gamma_wheel_spec = 'fsokanuerhmbtiycwlqpzxvgjd'
+
+wheel_i_pegs = ['q']
+wheel_ii_pegs = ['e']
+wheel_iii_pegs = ['v']
+wheel_iv_pegs = ['j']
+wheel_v_pegs = ['z']
+wheel_vi_pegs = ['z', 'm']
+wheel_vii_pegs = ['z', 'm']
+wheel_viii_pegs = ['z', 'm']
+
+reflector_b_spec = 'ay br cu dh eq fs gl ip jx kn mo tz vw'
+reflector_c_spec = 'af bv cp dj ei go hy kr lz mx nw tq su'
+
+
+
+class LetterTransformer(object):
+ """A generic substitution cipher, that has different transforms in the
+ forward and backward directions. It requires that the transforms for all
+ letters by provided.
+
+ >>> lt = LetterTransformer([('z', 'a')] + [(l, string.ascii_lowercase[i+1]) \
+ for i, l in enumerate(string.ascii_lowercase[:-1])], \
+ raw_transform = True)
+ >>> lt.forward_map
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0]
+ >>> lt.backward_map
+ [25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
+
+ >>> lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase)))
+ >>> lt.forward_map
+ [25, 24, 23, 22, 2, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
+ >>> lt.backward_map
+ [5, 6, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 3, 2, 1, 0]
+ >>> cat(lt.forward(l) for l in string.ascii_lowercase)
+ 'zyxwcabdefghijklmnopqrstuv'
+ >>> cat(lt.backward(l) for l in string.ascii_lowercase)
+ 'fgehijklmnopqrstuvwxyzdcba'
+ """
+ def __init__(self, specification, raw_transform=False):
+ if raw_transform:
+ transform = specification
+ else:
+ transform = self.parse_specification(specification)
+ self.validate_transform(transform)
+ self.make_transform_map(transform)
+
+ def parse_specification(self, specification):
+ return list(zip(string.ascii_lowercase, clean(specification)))
+ # return specification
+
+ def validate_transform(self, transform):
+ """A set of pairs, of from-to"""
+ if len(transform) != 26:
+ raise ValueError("Transform specification has {} pairs, requires 26".
+ format(len(transform)))
+ for p in transform:
+ if len(p) != 2:
+ raise ValueError("Not all mappings in transform "
+ "have two elements")
+ if len(set([p[0] for p in transform])) != 26:
+ raise ValueError("Transform specification must list 26 origin letters")
+ if len(set([p[1] for p in transform])) != 26:
+ raise ValueError("Transform specification must list 26 destination letters")
+
+ def make_empty_transform(self):
+ self.forward_map = [0] * 26
+ self.backward_map = [0] * 26
+
+ def make_transform_map(self, transform):
+ self.make_empty_transform()
+ for p in transform:
+ self.forward_map[pos(p[0])] = pos(p[1])
+ self.backward_map[pos(p[1])] = pos(p[0])
+ return self.forward_map, self.backward_map
+
+ def forward(self, letter):
+ if letter in string.ascii_lowercase:
+ return unpos(self.forward_map[pos(letter)])
+ else:
+ return ''
+
+ def backward(self, letter):
+ if letter in string.ascii_lowercase:
+ return unpos(self.backward_map[pos(letter)])
+ else:
+ return ''
+
+
+class Plugboard(LetterTransformer):
+ """A plugboard, a type of letter transformer where forward and backward
+ transforms are the same. If a letter isn't explicitly transformed, it is
+ kept as it is.
+
+ >>> pb = Plugboard('ua pf rq so ni ey bg hl tx zj'.upper())
+ >>> pb.forward_map
+ [20, 6, 2, 3, 24, 15, 1, 11, 13, 25, 10, 7, 12, 8, 18, 5, 17, 16, 14, 23, 0, 21, 22, 19, 4, 9]
+ >>> pb.forward_map == pb.backward_map
+ True
+ >>> cat(pb.forward(l) for l in string.ascii_lowercase)
+ 'ugcdypblnzkhmisfrqoxavwtej'
+ >>> cat(pb.backward(l) for l in string.ascii_lowercase)
+ 'ugcdypblnzkhmisfrqoxavwtej'
+ """
+ def parse_specification(self, specification):
+ return [tuple(clean(p)) for p in specification.split()]
+
+ def validate_transform(self, transform):
+ """A set of pairs, of from-to"""
+ for p in transform:
+ if len(p) != 2:
+ raise ValueError("Not all mappings in transform"
+ "have two elements")
+
+ def make_empty_transform(self):
+ self.forward_map = list(range(26))
+ self.backward_map = list(range(26))
+
+ def make_transform_map(self, transform):
+ expanded_transform = transform + [tuple(reversed(p)) for p in transform]
+ return super(Plugboard, self).make_transform_map(expanded_transform)
+
+
+
+
+class Reflector(Plugboard):
+ """A reflector is a plugboard that requires 13 transforms.
+
+ >>> reflector_b = Reflector(reflector_b_spec)
+ >>> reflector_b.forward_map == reflector_b.backward_map
+ True
+ >>> reflector_b.forward_map
+ [24, 17, 20, 7, 16, 18, 11, 3, 15, 23, 13, 6, 14, 10, 12, 8, 4, 1, 5, 25, 2, 22, 21, 9, 0, 19]
+ >>> cat(reflector_b.forward(l) for l in string.ascii_lowercase)
+ 'yruhqsldpxngokmiebfzcwvjat'
+ >>> cat(reflector_b.backward(l) for l in string.ascii_lowercase)
+ 'yruhqsldpxngokmiebfzcwvjat'
+ """
+ def validate_transform(self, transform):
+ if len(transform) != 13:
+ raise ValueError("Reflector specification has {} pairs, requires 13".
+ format(len(transform)))
+ if len(set([p[0] for p in transform] +
+ [p[1] for p in transform])) != 26:
+ raise ValueError("Reflector specification does not contain 26 letters")
+ try:
+ super(Reflector, self).validate_transform(transform)
+ except ValueError as v:
+ raise ValueError("Not all mappings in reflector have two elements")
+
+
+
+
+class SimpleWheel(LetterTransformer):
+ """A wheel is a transform that rotates.
+
+ Looking from the right, letters go in sequence a-b-c clockwise around the
+ wheel.
+
+ The position of the wheel is the number of spaces anticlockwise the wheel
+ has turned.
+
+ Letter inputs and outputs are given relative to the frame holding the wheel,
+ so if the wheel is advanced three places, an input of 'p' will enter the
+ wheel on the position under the wheel's 'q' label.
+
+ >>> rotor_1_transform = list(zip(string.ascii_lowercase, 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower()))
+ >>> wheel_1 = SimpleWheel(rotor_1_transform, raw_transform=True)
+ >>> cat(wheel_1.forward(l) for l in string.ascii_lowercase)
+ 'ekmflgdqvzntowyhxuspaibrcj'
+ >>> cat(wheel_1.backward(l) for l in string.ascii_lowercase)
+ 'uwygadfpvzbeckmthxslrinqoj'
+
+
+ >>> wheel_2 = SimpleWheel(wheel_ii_spec)
+ >>> cat(wheel_2.forward(l) for l in string.ascii_lowercase)
+ 'ajdksiruxblhwtmcqgznpyfvoe'
+ >>> cat(wheel_2.backward(l) for l in string.ascii_lowercase)
+ 'ajpczwrlfbdkotyuqgenhxmivs'
+
+ >>> wheel_3 = SimpleWheel(wheel_iii_spec)
+ >>> wheel_3.set_position('a')
+ >>> wheel_3.advance()
+ >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase)
+ 'cegikboqswuymxdhvfzjltrpna'
+ >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase)
+ 'zfaobrcpdteumygxhwivkqjnls'
+ >>> wheel_3.position
+ 1
+ >>> wheel_3.position_l
+ 'b'
+
+ >>> for _ in range(24): wheel_3.advance()
+ >>> wheel_3.position
+ 25
+ >>> wheel_3.position_l
+ 'z'
+ >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase)
+ 'pcegikmdqsuywaozfjxhblnvtr'
+ >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase)
+ 'nubhcqdterfvgwoaizjykxmslp'
+
+ >>> wheel_3.advance()
+ >>> wheel_3.position
+ 0
+ >>> wheel_3.position_l
+ 'a'
+ >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase)
+ 'bdfhjlcprtxvznyeiwgakmusqo'
+ >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase)
+ 'tagbpcsdqeufvnzhyixjwlrkom'
+ """
+ def __init__(self, transform, position='a', raw_transform=False):
+ super(SimpleWheel, self).__init__(transform, raw_transform)
+ self.set_position(position)
+
+ def __getattribute__(self,name):
+ if name=='position_l':
+ return unpos(self.position)
+ else:
+ return object.__getattribute__(self, name)
+
+ def set_position(self, position):
+ self.position = ord(position) - ord('a')
+
+ def forward(self, letter):
+ if letter in string.ascii_lowercase:
+ return unpos((self.forward_map[(pos(letter) + self.position) % 26] - self.position))
+ else:
+ return ''
+
+ def backward(self, letter):
+ if letter in string.ascii_lowercase:
+ return unpos((self.backward_map[(pos(letter) + self.position) % 26] - self.position))
+ else:
+ return ''
+
+ def advance(self):
+ self.position = (self.position + 1) % 26
+ # return self.position
+
+
+
+class Wheel(SimpleWheel):
+ """A wheel with a movable ring.
+
+ The ring holds the letters and the pegs that turn other wheels. The core
+ holds the wiring that does the transformation.
+
+ The ring position is how many steps the core is turned relative to the ring.
+ This is one-based, so a ring setting of 1 means the core and ring are
+ aligned.
+
+ The position of the wheel is the position of the core (the transforms)
+ relative to the neutral position.
+
+ The position_l is the position of the ring, or what would be observed
+ by the user of the Enigma machine.
+
+ The peg_positions are the number of advances of this wheel before it will
+ advance the next wheel.
+
+ >>> wheel_3 = Wheel(wheel_iii_spec, wheel_iii_pegs, position='b', ring_setting=1)
+ >>> wheel_3.position
+ 1
+ >>> wheel_3.peg_positions
+ [20]
+ >>> wheel_3.position_l
+ 'b'
+ >>> wheel_3.advance()
+ >>> wheel_3.position
+ 2
+ >>> wheel_3.peg_positions
+ [19]
+ >>> wheel_3.position_l
+ 'c'
+
+ >>> wheel_6 = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', ring_setting=3)
+ >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase)
+ 'xkqhwpvngzrcfoiaselbtymjdu'
+ >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase)
+ 'ptlyrmidoxbswhnfckquzgeavj'
+ >>> wheel_6.position
+ 25
+ >>> 11 in wheel_6.peg_positions
+ True
+ >>> 24 in wheel_6.peg_positions
+ True
+ >>> wheel_6.position_l
+ 'b'
+
+ >>> wheel_6.advance()
+ >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase)
+ 'jpgvoumfyqbenhzrdkasxlictw'
+ >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase)
+ 'skxqlhcnwarvgmebjptyfdzuio'
+ >>> wheel_6.position
+ 0
+ >>> 10 in wheel_6.peg_positions
+ True
+ >>> 23 in wheel_6.peg_positions
+ True
+ >>> wheel_6.position_l
+ 'c'
+
+ >>> for _ in range(22): wheel_6.advance()
+ >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase)
+ 'mgxantkzsyqjcufirldvhoewbp'
+ >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase)
+ 'dymswobuplgraevzkqifntxcjh'
+ >>> wheel_6.position
+ 22
+ >>> 1 in wheel_6.peg_positions
+ True
+ >>> 14 in wheel_6.peg_positions
+ True
+ >>> wheel_6.position_l
+ 'y'
+
+ >>> wheel_6.advance()
+ >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase)
+ 'fwzmsjyrxpibtehqkcugndvaol'
+ >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase)
+ 'xlrvnatokfqzduyjphemswbigc'
+ >>> wheel_6.position
+ 23
+ >>> 0 in wheel_6.peg_positions
+ True
+ >>> 13 in wheel_6.peg_positions
+ True
+ >>> wheel_6.position_l
+ 'z'
+
+ >>> wheel_6.advance()
+ >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase)
+ 'vylrixqwohasdgpjbtfmcuznke'
+ >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase)
+ 'kqumzsnjepyctxiogdlrvahfbw'
+ >>> wheel_6.position
+ 24
+ >>> 25 in wheel_6.peg_positions
+ True
+ >>> 12 in wheel_6.peg_positions
+ True
+ >>> wheel_6.position_l
+ 'a'
+
+ >>> wheel_6.advance()
+ >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase)
+ 'xkqhwpvngzrcfoiaselbtymjdu'
+ >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase)
+ 'ptlyrmidoxbswhnfckquzgeavj'
+ >>> wheel_6.position
+ 25
+ >>> 24 in wheel_6.peg_positions
+ True
+ >>> 11 in wheel_6.peg_positions
+ True
+ >>> wheel_6.position_l
+ 'b'
+
+ >>> wheel_6.advance()
+ >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase)
+ 'jpgvoumfyqbenhzrdkasxlictw'
+ >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase)
+ 'skxqlhcnwarvgmebjptyfdzuio'
+ >>> wheel_6.position
+ 0
+ >>> 23 in wheel_6.peg_positions
+ True
+ >>> 10 in wheel_6.peg_positions
+ True
+ >>> wheel_6.position_l
+ 'c'
+
+ """
+ def __init__(self, transform, ring_peg_letters, ring_setting=1, position='a', raw_transform=False):
+ self.ring_peg_letters = ring_peg_letters
+ self.ring_setting = ring_setting
+ super(Wheel, self).__init__(transform, position=position, raw_transform=raw_transform)
+ self.set_position(position)
+
+ def __getattribute__(self,name):
+ if name=='position_l':
+ return unpos(self.position + self.ring_setting - 1)
+ else:
+ return object.__getattribute__(self, name)
+
+ def set_position(self, position):
+ self.position = (pos(position) - self.ring_setting + 1) % 26
+ # self.position_l = position
+ self.peg_positions = [(pos(p) - pos(position)) % 26 for p in self.ring_peg_letters]
+
+ def advance(self):
+ super(Wheel, self).advance()
+ self.peg_positions = [(p - 1) % 26 for p in self.peg_positions]
+ # self.position_l = unpos(self.position + self.ring_setting - 1)
+ # return self.position
+
+
+
+
+class Enigma(object):
+ """An Enigma machine.
+
+ >>> enigma = Enigma(reflector_b_spec, \
+ wheel_i_spec, wheel_i_pegs, \
+ wheel_ii_spec, wheel_ii_pegs, \
+ wheel_iii_spec, wheel_iii_pegs, \
+ 1, 1, 1, \
+ '')
+ >>> enigma.set_wheels('a', 'a', 't')
+ >>> enigma.wheel_positions
+ (0, 0, 19)
+ >>> cat(enigma.wheel_positions_l)
+ 'aat'
+ >>> enigma.peg_positions
+ ([16], [4], [2])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'puvioztjdhxmlyeawsrgbcqknf'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 0, 20)
+ >>> cat(enigma.wheel_positions_l)
+ 'aau'
+ >>> enigma.peg_positions
+ ([16], [4], [1])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'baigpldqcowfyzjehvtsxrkumn'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 0, 21)
+ >>> cat(enigma.wheel_positions_l)
+ 'aav'
+ >>> enigma.peg_positions
+ ([16], [4], [0])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'mnvfydiwgzsoablrxpkutchqej'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 1, 22)
+ >>> cat(enigma.wheel_positions_l)
+ 'abw'
+ >>> enigma.peg_positions
+ ([16], [3], [25])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'ulfopcykswhbzvderqixanjtgm'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 1, 23)
+ >>> cat(enigma.wheel_positions_l)
+ 'abx'
+ >>> enigma.peg_positions
+ ([16], [3], [24])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'qmwftdyovursbzhxaklejicpgn'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 1, 24)
+ >>> cat(enigma.wheel_positions_l)
+ 'aby'
+ >>> enigma.peg_positions
+ ([16], [3], [23])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'oljmzxrvucybdqasngpwihtfke'
+
+
+
+
+ >>> enigma.set_wheels('a', 'd', 't')
+ >>> enigma.wheel_positions
+ (0, 3, 19)
+ >>> cat(enigma.wheel_positions_l)
+ 'adt'
+ >>> enigma.peg_positions
+ ([16], [1], [2])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'zcbpqxwsjiuonmldethrkygfva'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 3, 20)
+ >>> cat(enigma.wheel_positions_l)
+ 'adu'
+ >>> enigma.peg_positions
+ ([16], [1], [1])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'ehprawjbngotxikcsdqlzyfmvu'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 3, 21)
+ >>> cat(enigma.wheel_positions_l)
+ 'adv'
+ >>> enigma.peg_positions
+ ([16], [1], [0])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'eqzxarpihmnvjkwgbfuyslodtc'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (0, 4, 22)
+ >>> cat(enigma.wheel_positions_l)
+ 'aew'
+ >>> enigma.peg_positions
+ ([16], [0], [25])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'qedcbtpluzmhkongavwfirsyxj'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (1, 5, 23)
+ >>> cat(enigma.wheel_positions_l)
+ 'bfx'
+ >>> enigma.peg_positions
+ ([15], [25], [24])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'iwuedhsfazqxytvrkpgncoblmj'
+
+ >>> enigma.advance()
+ >>> enigma.wheel_positions
+ (1, 5, 24)
+ >>> cat(enigma.wheel_positions_l)
+ 'bfy'
+ >>> enigma.peg_positions
+ ([15], [25], [23])
+ >>> cat(enigma.lookup(l) for l in string.ascii_lowercase)
+ 'baknstqzrmcxjdvygiefwoulph'
+
+
+ >>> enigma.set_wheels('a', 'a', 'a')
+ >>> ct = enigma.encipher('testmessage')
+ >>> ct
+ 'olpfhnvflyn'
+
+ >>> enigma.set_wheels('a', 'd', 't')
+ >>> ct = enigma.encipher('testmessage')
+ >>> ct
+ 'lawnjgpwjik'
+
+
+ >>> enigma.set_wheels('b', 'd', 'q')
+ >>> ct = enigma.encipher('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
+ >>> ct
+ 'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr'
+ >>> enigma.left_wheel.position_l
+ 'c'
+ >>> enigma.middle_wheel.position_l
+ 'h'
+ >>> enigma.right_wheel.position_l
+ 'a'
+
+ # Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm
+ # Enigma simulation settings are
+ # http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJEU;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX
+ >>> enigma31 = Enigma(reflector_b_spec, \
+ wheel_i_spec, wheel_i_pegs, \
+ wheel_v_spec, wheel_v_pegs, \
+ wheel_iii_spec, wheel_iii_pegs, \
+ 6, 20, 24, \
+ 'ua pf rq so ni ey bg hl tx zj')
+
+ >>> enigma31.set_wheels('j', 'e', 'u')
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (4, 11, 24)
+ >>> cat(enigma31.wheel_positions_l)
+ 'jev'
+ >>> enigma31.peg_positions
+ ([7], [21], [0])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'mvqjlyowkdieasgzcunxrbhtfp'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (4, 12, 25)
+ >>> cat(enigma31.wheel_positions_l)
+ 'jfw'
+ >>> enigma31.peg_positions
+ ([7], [20], [25])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'sjolzuyvrbwdpxcmtiaqfhknge'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (4, 12, 0)
+ >>> cat(enigma31.wheel_positions_l)
+ 'jfx'
+ >>> enigma31.peg_positions
+ ([7], [20], [24])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'qrxedkoywufmlvgsabpzjnicht'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (4, 12, 1)
+ >>> cat(enigma31.wheel_positions_l)
+ 'jfy'
+ >>> enigma31.peg_positions
+ ([7], [20], [23])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'hpsukliagqefwvtbjxcodnmrzy'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (4, 12, 2)
+ >>> cat(enigma31.wheel_positions_l)
+ 'jfz'
+ >>> enigma31.peg_positions
+ ([7], [20], [22])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'zevnbpyqowrtxdifhkulscjmga'
+
+
+ >>> enigma31.set_wheels('i', 'd', 'z')
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 3)
+ >>> cat(enigma31.wheel_positions_l)
+ 'ida'
+ >>> enigma31.peg_positions
+ ([8], [22], [21])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'ikhpqrvcambzjondefwyxgsutl'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 4)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idb'
+ >>> enigma31.peg_positions
+ ([8], [22], [20])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'cdabskhgzwfmlqvunyexpojtri'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 5)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idc'
+ >>> enigma31.peg_positions
+ ([8], [22], [19])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'pcbwiqhgemyvjsuaftnroldzkx'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 6)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idd'
+ >>> enigma31.peg_positions
+ ([8], [22], [18])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'xcbfvdnouptmlghjzwykierasq'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 7)
+ >>> cat(enigma31.wheel_positions_l)
+ 'ide'
+ >>> enigma31.peg_positions
+ ([8], [22], [17])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'xfvglbdynuseriwqpmkzjcoaht'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 8)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idf'
+ >>> enigma31.peg_positions
+ ([8], [22], [16])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'tfpqlbouynsewjgcdxkahzmriv'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 9)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idg'
+ >>> enigma31.peg_positions
+ ([8], [22], [15])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'cjaunvlwtbygzexrspqidfhokm'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 10)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idh'
+ >>> enigma31.peg_positions
+ ([8], [22], [14])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'yltxkrqvowebzpingfucshjdam'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 11)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idi'
+ >>> enigma31.peg_positions
+ ([8], [22], [13])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'myktluzrnxceaiqsohpdfwvjbg'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 12)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idj'
+ >>> enigma31.peg_positions
+ ([8], [22], [12])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'pynjrmiugdqxfcvakewzhoslbt'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 13)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idk'
+ >>> enigma31.peg_positions
+ ([8], [22], [11])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'mwvedyplnoxhaijgrqtszcbkfu'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 14)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idl'
+ >>> enigma31.peg_positions
+ ([8], [22], [10])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'qcbrfeutvoxpnmjladzhgiykws'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 15)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idm'
+ >>> enigma31.peg_positions
+ ([8], [22], [9])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'dnoahryetsmukbcvwfjilpqzgx'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 16)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idn'
+ >>> enigma31.peg_positions
+ ([8], [22], [8])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'nidcfehgbqsovalyjzkxwmutpr'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 17)
+ >>> cat(enigma31.wheel_positions_l)
+ 'ido'
+ >>> enigma31.peg_positions
+ ([8], [22], [7])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'joifxdulcarhzpbntkwqgysevm'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 18)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idp'
+ >>> enigma31.peg_positions
+ ([8], [22], [6])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'ptnlsxvozmwdjchayuebrgkfqi'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 19)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idq'
+ >>> enigma31.peg_positions
+ ([8], [22], [5])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'slwopzqnmxybihdeguavrtcjkf'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 20)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idr'
+ >>> enigma31.peg_positions
+ ([8], [22], [4])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'hcbedwlamzogixkytsrqvufnpj'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 21)
+ >>> cat(enigma31.wheel_positions_l)
+ 'ids'
+ >>> enigma31.peg_positions
+ ([8], [22], [3])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'odxbjwzrmelkisavuhnyqpfctg'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 22)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idt'
+ >>> enigma31.peg_positions
+ ([8], [22], [2])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'udgbfeclrwnhxksvtioqapjmzy'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 23)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idu'
+ >>> enigma31.peg_positions
+ ([8], [22], [1])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'nrdczqxmowvshaiufblypkjgte'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 10, 24)
+ >>> cat(enigma31.wheel_positions_l)
+ 'idv'
+ >>> enigma31.peg_positions
+ ([8], [22], [0])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'hkifjdoacebqtzgulyvmpsxwrn'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 11, 25)
+ >>> cat(enigma31.wheel_positions_l)
+ 'iew'
+ >>> enigma31.peg_positions
+ ([8], [21], [25])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'yptzuhofqvnmlkgbixwcejsrad'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 11, 0)
+ >>> cat(enigma31.wheel_positions_l)
+ 'iex'
+ >>> enigma31.peg_positions
+ ([8], [21], [24])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'vkdcwhqfjibzsptngumoraeyxl'
+
+ >>> enigma31.advance()
+ >>> enigma31.wheel_positions
+ (3, 11, 1)
+ >>> cat(enigma31.wheel_positions_l)
+ 'iey'
+ >>> enigma31.peg_positions
+ ([8], [21], [23])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'wenpbqrouxlkychdfgzvitajms'
+
+
+ >>> enigma31.set_wheels('i', 'd', 'z')
+ >>> enigma31.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure')
+ 'gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz'
+ >>> enigma31.wheel_positions
+ (3, 12, 6)
+ >>> cat(enigma31.wheel_positions_l)
+ 'ifd'
+ >>> enigma31.peg_positions
+ ([8], [20], [18])
+ >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase)
+ 'urygzpdmxtwshqvfnbljaokice'
+
+ >>> enigma31.set_wheels('i', 'd', 'z')
+ >>> enigma31.decipher('gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz')
+ 'verylongtestmessagewithanextrabitofmessageforgoodmeasure'
+ """
+ def __init__(self, reflector_spec,
+ left_wheel_spec, left_wheel_pegs,
+ middle_wheel_spec, middle_wheel_pegs,
+ right_wheel_spec, right_wheel_pegs,
+ left_ring_setting, middle_ring_setting, right_ring_setting,
+ plugboard_setting):
+ self.reflector = Reflector(reflector_spec)
+ self.left_wheel = Wheel(left_wheel_spec, left_wheel_pegs, ring_setting=left_ring_setting)
+ self.middle_wheel = Wheel(middle_wheel_spec, middle_wheel_pegs, ring_setting=middle_ring_setting)
+ self.right_wheel = Wheel(right_wheel_spec, right_wheel_pegs, ring_setting=right_ring_setting)
+ self.plugboard = Plugboard(plugboard_setting)
+
+ def __getattribute__(self,name):
+ if name=='wheel_positions':
+ return self.left_wheel.position, self.middle_wheel.position, self.right_wheel.position
+ elif name=='wheel_positions_l':
+ return self.left_wheel.position_l, self.middle_wheel.position_l, self.right_wheel.position_l
+ elif name=='peg_positions':
+ return self.left_wheel.peg_positions, self.middle_wheel.peg_positions, self.right_wheel.peg_positions
+ else:
+ return object.__getattribute__(self, name)
+
+ def set_wheels(self, left_wheel_position, middle_wheel_position, right_wheel_position):
+ self.left_wheel.set_position(left_wheel_position)
+ self.middle_wheel.set_position(middle_wheel_position)
+ self.right_wheel.set_position(right_wheel_position)
+
+ def lookup(self, letter):
+ a = self.plugboard.forward(letter)
+ b = self.right_wheel.forward(a)
+ c = self.middle_wheel.forward(b)
+ d = self.left_wheel.forward(c)
+ e = self.reflector.forward(d)
+ f = self.left_wheel.backward(e)
+ g = self.middle_wheel.backward(f)
+ h = self.right_wheel.backward(g)
+ i = self.plugboard.backward(h)
+ return i
+
+ def advance(self):
+ advance_middle = False
+ advance_left = False
+ if 0 in self.right_wheel.peg_positions:
+ advance_middle = True
+ if 0 in self.middle_wheel.peg_positions:
+ advance_left = True
+ advance_middle = True
+ self.right_wheel.advance()
+ if advance_middle: self.middle_wheel.advance()
+ if advance_left: self.left_wheel.advance()
+
+ def encipher_letter(self, letter):
+ self.advance()
+ return self.lookup(letter)
+
+ def encipher(self, message):
+ enciphered = ''
+ for letter in clean(message):
+ enciphered += self.encipher_letter(letter)
+ return enciphered
+
+ decipher = encipher
+
+
+# for i in range(26):
+# enigma.advance()
+# print('enigma.advance()')
+# print("assert(enigma.wheel_positions == {})".format(enigma.wheel_positions))
+# print("assert(cat(enigma.wheel_positions_l) == '{}')".format(cat(enigma.wheel_positions_l)))
+# print("assert(enigma.peg_positions == {})".format(enigma.peg_positions))
+# print("assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == '{}')".format(cat(enigma.lookup(l) for l in string.ascii_lowercase)))
+# print()
+
+
+if __name__ == "__main__":
+ import doctest
+ # doctest.testmod(extraglobs={'lt': LetterTransformer(1, 'a')})
+ doctest.testmod()
+