changeset 14:eea5734a1655

Build encrypt wiring table inside switches. Add tests. Separate switch stepping into a new method in preparation for adding encrypt.
author Brian Neal <bgneal@gmail.com>
date Mon, 02 Dec 2013 20:44:26 -0600
parents 2e3d919383dd
children 672ba8b2269f
files purple/machine.py purple/switch.py purple/tests/test_switch.py
diffstat 3 files changed, 131 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/purple/machine.py	Sun Dec 01 20:30:06 2013 -0600
+++ b/purple/machine.py	Mon Dec 02 20:44:26 2013 -0600
@@ -191,62 +191,68 @@
             n = self.plugboard[c]
             if n < 6:
                 # This input goes to the sixes switch
-                x = self.sixes[n]
+                x = self.sixes.decrypt(n)
             else:
                 # This input goes to the chain of twenties switches
                 n -= 6
-                x = self.twenties[0][self.twenties[1][self.twenties[2][n]]]
+                x = self.twenties[0].decrypt(self.twenties[1].decrypt(
+                        self.twenties[2].decrypt(n)))
                 x += 6
 
             plaintext.append(self.alphabet[x])
 
-            # Now step the switches. First read the sixes and middle switch
-            # positions before stepping anything. Use these latched values in
-            # the decision processes for stepping a twenties. This is crucial!
-            sixes_pos = self.sixes.pos
-            middle_pos = self.middle_switch.pos
-
-            # Now we can step the sixes. It unconditionally steps after every
-            # letter is processed.
-            self.sixes.step()
-
-            # Only 1 twenties switch steps at a time.
-            #
-            # Normally the fast switch advances every letter.
-            #
-            # However if the sixes is at the last position (24), the middle
-            # switch steps instead.
-            #
-            # But if the sixes is at the last position (24) and the middle
-            # switch is at the last position (24), the slow switch will step.
-            # The middle switch will step on the next letter in this case.
-            #
-            # Here we reproduce figure 10 from the reference paper with offsets
-            # adjusted for 0-based indexing to illustrate the movement. In this
-            # example the fast is twenties #1, middle is #2, and slow is #3.
-            #
-            # Sixes     Twenties #1     Twenties #2     Twenties #3
-            # -----------------------------------------------------
-            #   20          0               24              4
-            #   21          1               24              4
-            #   22          2               24              4
-            #   23          3               24              4
-            # * 24          3               24              5
-            # #  0          3                0              5
-            #    1          4                0              5
-            #
-            # Here we see the fast switch stepping. However at (*) both the
-            # sixes and middle switch have reached their last positions, so the
-            # slow switch steps. At the next letter (#) the middle switch plays
-            # "catch up" and advances while the fast switch holds steady.
-
-            if sixes_pos == 24 and middle_pos == 24:
-                self.slow_switch.step()
-                self.middle_switch_catchup = True
-            elif sixes_pos == 24 or self.middle_switch_catchup:
-                self.middle_switch.step()
-                self.middle_switch_catchup = False
-            else:
-                self.fast_switch.step()
+            # Now step the switches.
+            self.step()
 
         return ''.join(plaintext)
+
+    def step(self):
+        """Step the stepping switches."""
+        # First read the sixes and middle switch
+        # positions before stepping anything. Use these latched values in
+        # the decision processes for stepping a twenties. This is crucial!
+        sixes_pos = self.sixes.pos
+        middle_pos = self.middle_switch.pos
+
+        # Now we can step the sixes. It unconditionally steps after every
+        # letter is processed.
+        self.sixes.step()
+
+        # Only 1 twenties switch steps at a time.
+        #
+        # Normally the fast switch advances every letter.
+        #
+        # However if the sixes is at the last position (24), the middle
+        # switch steps instead.
+        #
+        # But if the sixes is at the last position (24) and the middle
+        # switch is at the last position (24), the slow switch will step.
+        # The middle switch will step on the next letter in this case.
+        #
+        # Here we reproduce figure 10 from the reference paper with offsets
+        # adjusted for 0-based indexing to illustrate the movement. In this
+        # example the fast is twenties #1, middle is #2, and slow is #3.
+        #
+        # Sixes     Twenties #1     Twenties #2     Twenties #3
+        # -----------------------------------------------------
+        #   20          0               24              4
+        #   21          1               24              4
+        #   22          2               24              4
+        #   23          3               24              4
+        # * 24          3               24              5
+        # #  0          3                0              5
+        #    1          4                0              5
+        #
+        # Here we see the fast switch stepping. However at (*) both the
+        # sixes and middle switch have reached their last positions, so the
+        # slow switch steps. At the next letter (#) the middle switch plays
+        # "catch up" and advances while the fast switch holds steady.
+
+        if sixes_pos == 24 and middle_pos == 24:
+            self.slow_switch.step()
+            self.middle_switch_catchup = True
+        elif sixes_pos == 24 or self.middle_switch_catchup:
+            self.middle_switch.step()
+            self.middle_switch_catchup = False
+        else:
+            self.fast_switch.step()
--- a/purple/switch.py	Sun Dec 01 20:30:06 2013 -0600
+++ b/purple/switch.py	Mon Dec 02 20:44:26 2013 -0600
@@ -27,16 +27,19 @@
         integers representing the output contacts. The initial position of the
         switch can also be set; this defaults to 0.
 
-        The wiring lists are assumed to be 0-based.
+        The wiring lists are assumed to be 0-based and represent the decrypt
+        path through the switch. A reciprocal encrypt wiring map will be
+        constructed from this argument internally.
 
         """
-        self.wiring = wiring
+        self.dec_wiring = wiring
         self.num_positions = len(wiring)
         self.num_levels = len(wiring[0])
 
         if not all(self.num_levels == len(level) for level in wiring):
             raise SteppingSwitchError("Ragged wiring table")
 
+        self._build_encrypt_wiring()
         self.set_pos(init_pos)
 
     def set_pos(self, pos):
@@ -57,13 +60,33 @@
         self.pos = (self.pos + 1) % self.num_positions
         return self.pos
 
-    def __getitem__(self, level):
+    def decrypt(self, level):
         """This method is how to determine the output signal from the stepping
-        switch. The key parameter 'level' is the integer input level for the
-        incoming signal. The integer outgoing contact level is returned.
+        switch for the decrypt path. The parameter 'level' is the integer input
+        level for the incoming signal. The integer outgoing contact level is
+        returned.
 
         """
-        return self.wiring[self.pos][level]
+        return self.dec_wiring[self.pos][level]
+
+    def encrypt(self, level):
+        """This method is how to determine the output signal from the stepping
+        switch for the encrypt path. The parameter 'level' is the integer input
+        level for the incoming signal. The integer outgoing contact level is
+        returned.
+
+        """
+        return self.enc_wiring[self.pos][level]
+
+    def _build_encrypt_wiring(self):
+        """This method builds an encrypt wiring table from the decrypt wiring
+        table member.
+
+        """
+        self.enc_wiring = []
+        for level in self.dec_wiring:
+            self.enc_wiring.append(
+                [level.index(n) for n in range(self.num_levels)])
 
 
 def create_switch(switch_type, init_pos=0):
--- a/purple/tests/test_switch.py	Sun Dec 01 20:30:06 2013 -0600
+++ b/purple/tests/test_switch.py	Mon Dec 02 20:44:26 2013 -0600
@@ -50,32 +50,59 @@
             new_pos = switch.step()
             self.assertEqual((pos + 1) % 25, new_pos)
 
-    def test_output(self):
+    def test_decrypt(self):
 
         switch = create_switch(purple.switch.SIXES)
         switch.set_pos(0)
-        self.assertEqual(switch[0], 1)
-        self.assertEqual(switch[1], 0)
-        self.assertEqual(switch[2], 2)
-        self.assertEqual(switch[3], 4)
-        self.assertEqual(switch[4], 3)
-        self.assertEqual(switch[5], 5)
+        self.assertEqual(switch.decrypt(0), 1)
+        self.assertEqual(switch.decrypt(1), 0)
+        self.assertEqual(switch.decrypt(2), 2)
+        self.assertEqual(switch.decrypt(3), 4)
+        self.assertEqual(switch.decrypt(4), 3)
+        self.assertEqual(switch.decrypt(5), 5)
 
         switch.step()
-        self.assertEqual(switch[0], 5)
-        self.assertEqual(switch[1], 2)
-        self.assertEqual(switch[2], 4)
-        self.assertEqual(switch[3], 1)
-        self.assertEqual(switch[4], 0)
-        self.assertEqual(switch[5], 3)
+        self.assertEqual(switch.decrypt(0), 5)
+        self.assertEqual(switch.decrypt(1), 2)
+        self.assertEqual(switch.decrypt(2), 4)
+        self.assertEqual(switch.decrypt(3), 1)
+        self.assertEqual(switch.decrypt(4), 0)
+        self.assertEqual(switch.decrypt(5), 3)
 
         switch.set_pos(24)
-        self.assertEqual(switch[0], 4)
-        self.assertEqual(switch[1], 1)
-        self.assertEqual(switch[2], 3)
-        self.assertEqual(switch[3], 2)
-        self.assertEqual(switch[4], 5)
-        self.assertEqual(switch[5], 0)
+        self.assertEqual(switch.decrypt(0), 4)
+        self.assertEqual(switch.decrypt(1), 1)
+        self.assertEqual(switch.decrypt(2), 3)
+        self.assertEqual(switch.decrypt(3), 2)
+        self.assertEqual(switch.decrypt(4), 5)
+        self.assertEqual(switch.decrypt(5), 0)
+
+    def test_encrypt(self):
+
+        switch = create_switch(purple.switch.SIXES)
+        switch.set_pos(0)
+        self.assertEqual(switch.encrypt(0), 1)
+        self.assertEqual(switch.encrypt(1), 0)
+        self.assertEqual(switch.encrypt(2), 2)
+        self.assertEqual(switch.encrypt(3), 4)
+        self.assertEqual(switch.encrypt(4), 3)
+        self.assertEqual(switch.encrypt(5), 5)
+
+        switch.step()
+        self.assertEqual(switch.encrypt(0), 4)
+        self.assertEqual(switch.encrypt(1), 3)
+        self.assertEqual(switch.encrypt(2), 1)
+        self.assertEqual(switch.encrypt(3), 5)
+        self.assertEqual(switch.encrypt(4), 2)
+        self.assertEqual(switch.encrypt(5), 0)
+
+        switch.set_pos(24)
+        self.assertEqual(switch.encrypt(0), 5)
+        self.assertEqual(switch.encrypt(1), 1)
+        self.assertEqual(switch.encrypt(2), 3)
+        self.assertEqual(switch.encrypt(3), 2)
+        self.assertEqual(switch.encrypt(4), 0)
+        self.assertEqual(switch.encrypt(5), 4)
 
     def test_bad_create_switch(self):