changeset 1:1459e74fda3f

Finished creating rotor class and factories.
author Brian Neal <bgneal@gmail.com>
date Fri, 22 Jun 2012 20:15:11 -0500
parents 74ebb2150658
children 713fa2a9ea9a
files enigma/SConscript enigma/enigma_types.h enigma/enigma_utils.h enigma/rotor.cpp enigma/rotor.h enigma/rotor_data.h enigma/rotor_factory.cpp enigma/rotor_factory.h
diffstat 8 files changed, 267 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/enigma/SConscript	Thu Jun 21 21:05:26 2012 -0500
+++ b/enigma/SConscript	Fri Jun 22 20:15:11 2012 -0500
@@ -2,6 +2,7 @@
 
 sources = Split("""
    rotor.cpp
+   rotor_factory.cpp
    """)
 
 env.StaticLibrary('enigma', sources)
--- a/enigma/enigma_types.h	Thu Jun 21 21:05:26 2012 -0500
+++ b/enigma/enigma_types.h	Fri Jun 22 20:15:11 2012 -0500
@@ -8,6 +8,7 @@
 
 #include <exception>
 #include <string>
+#include <array>
 
 namespace enigma
 {
@@ -28,6 +29,10 @@
    private:
       std::string what_arg;
    };
+
+   // Arrays of 26 items are very commonly used:
+   typedef std::array<int, 26> alpha_int_array;
+   typedef std::array<bool, 26> alpha_bool_array;
 }
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/enigma_utils.h	Fri Jun 22 20:15:11 2012 -0500
@@ -0,0 +1,22 @@
+#ifndef CPP_ENIGMA_ENIGMA_UTILS_H
+#define CPP_ENIGMA_ENIGMA_UTILS_H
+// Copyright (C) 2012 by Brian Neal.
+// This file is part of Cpp-Enigma, the Enigma Machine simulation.
+// Cpp-Enigma is released under the MIT License (see License.txt).
+//
+// enigma_utils.h - This file contains common functions used throughout Cpp-Enigma.
+
+namespace enigma
+{
+   // This version of mod acts like Python's with respect to negative dividends.
+   inline int alpha_mod(int dividend)
+   {
+      if (dividend < 0)
+      {
+         dividend += 26;
+      }
+      return dividend % 26;
+   }
+}
+
+#endif
--- a/enigma/rotor.cpp	Thu Jun 21 21:05:26 2012 -0500
+++ b/enigma/rotor.cpp	Fri Jun 22 20:15:11 2012 -0500
@@ -8,21 +8,37 @@
 #include <array>
 #include <algorithm>
 #include "rotor.h"
+#include "enigma_utils.h"
 
 using namespace enigma;
 
+////////////////////////////////////////////////////////////////////////////////
+
 namespace
 {
    const char* const ucase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-   const std::set<char> ucase_set(ucase, ucase + 26);
+
+   // Turned into a function to avoid translation unit initialization order problems.
+   const std::set<char>& get_ucase_set()
+   {
+      static const std::set<char> the_set(ucase, ucase + 26);
+      return the_set;
+   }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 rotor::rotor(const char* name, const char* wiring, int ring_setting, const char* stepping)
- : rotor_name(name),
-   wiring_str(wiring),
-   ring_setting(ring_setting),
-   pos(0),
-   rotations(0)
+ : rotor_name{name},
+   wiring_str{wiring},
+   ring_setting{ring_setting},
+   pos{0},
+   display_val{'A'},
+   entry_map(),
+   exit_map(),
+   display_map(),
+   pos_map(),
+   step_map()
 {
    // check wiring length
    if (wiring_str.size() != 26)
@@ -32,12 +48,13 @@
 
    // ensure wiring contains only uppercase letters & every char must appear
    // exactly once:
+   const std::set<char>& ucase_set(get_ucase_set());
 
-   std::array<int, 26> letter_counts {{ 0 }};
+   // g++ 4.6.3 warns about missing braces unless we double them up, below:
+   std::array<int, 26> letter_counts = {{ 0 }};
    for (int i = 0; i < 26; ++i)
    {
-      const char c(wiring_str[i]);
-
+      const char c{wiring_str[i]};
       if (ucase_set.find(c) == ucase_set.end())
       {
          throw rotor_error("invalid wiring");
@@ -51,4 +68,58 @@
    {
       throw rotor_error("invalid wiring; duplicate letter");
    }
+
+   if (ring_setting < 0 || ring_setting >= 26)
+   {
+      throw rotor_error("invalid ring setting");
+   }
+   set_ring_setting(ring_setting);
+
+   // Initialize our two arrays that describe the internal wiring. Arrays are used
+   // to do fast lookup from both entry (from the right) and exit (from the
+   // left).
+
+   for (int i = 0; i < 26; ++i)
+   {
+      const int v = wiring_str[i] - 'A';
+      entry_map[i] = v;
+      exit_map[v] = i;
+   }
+
+   // Build a lookup table that tells us when the pawls are allowed to step.
+   // The index to this array is the current display letter [A-Z] - 'A'.
+
+   if (stepping != nullptr)
+   {
+      for (char c = *stepping; *stepping != '\0'; ++stepping)
+      {
+         if (ucase_set.find(c) != ucase_set.end())
+         {
+            step_map[c - 'A'] = true;
+         }
+         else
+         {
+            throw rotor_error("invalid stepping");
+         }
+      }
+   }
+
+   // set initial position
+   set_display('A');
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+void rotor::set_ring_setting(int n)
+{
+   ring_setting = n;
+
+   // Build a mapping from window display values to positions
+   // and a reverse mapping of position to display value:
+   for (int i = 0; i < 26; ++i)
+   {
+      const int n = alpha_mod(i - ring_setting);
+      display_map[i] = n;
+      pos_map[n] = i + 'A';
+   }
+}
--- a/enigma/rotor.h	Thu Jun 21 21:05:26 2012 -0500
+++ b/enigma/rotor.h	Fri Jun 22 20:15:11 2012 -0500
@@ -8,6 +8,7 @@
 
 #include <string>
 #include "enigma_types.h"
+#include "enigma_utils.h"
 
 
 namespace enigma
@@ -93,39 +94,82 @@
       // simply for simulation convenience.
       // display.
 
-      rotor(const char* name, const char* wiring, int ring_setting = 0, const char* stepping = 0);
+      rotor(const char* name, const char* wiring, int ring_setting = 0, const char* stepping = nullptr);
 
       // Returns the rotor name:
       const std::string& name() const { return rotor_name; }
 
-      // Spin the rotor such that the string val appears in the operator window:
-      void set_display(const char* val);
+      // Spin the rotor such that the char val appears in the operator window:
+      void set_display(char val)
+      {
+         pos = display_map[val - 'A'];
+         display_val = val;
+      }
 
       // Returns what is currently being displayed in the operator window:
-      std::string get_display() const;
+      char get_display() const { return display_val; }
+
+
+      // sets the ring setting to n, where n [0-25]:
+      void set_ring_setting(int n);
+
+      // get the current ring setting:
+      int get_ring_setting() const { return ring_setting; }
 
       // Simulate a signal entering the rotor from the right at a given pin:
       // n must be an integer between 0 and 25.
       // Returns the contact number of the output signal (0-25).
-      int signal_in(int n) const;
+      int signal_in(int n) const
+      {
+         // determine what pin we have at that position due to rotation
+         const int pin = (n + pos) % 26;
+
+         // run it through the internal wiring
+         const int contact = entry_map[pin];
+
+         // turn back into a position due to rotation
+         return alpha_mod(contact - pos);
+      }
 
       // Simulate a signal entering the rotor from the left at a given contact position n.
       // n must be an integer between 0 and 25.
       // Returns the pin number of the output signal (0-25).
-      int signal_out(int n) const;
+      int signal_out(int n) const
+      {
+         // determine what contact we have at that position due to rotation
+         const int contact = (n + pos) % 26;
+
+         // run it through the internal wiring
+         const int pin = exit_map[contact];
+
+         // turn back into a position due to rotation
+         return alpha_mod(pin - pos);
+      }
 
       // Return true if this rotor has a notch in the stepping position and false otherwise:
-      bool notch_over_pawl() const;
+      bool notch_over_pawl() const
+      {
+         return step_map[display_val - 'A'];
+      }
 
       // Rotate the rotor forward one step:
-      void rotate();
+      void rotate()
+      {
+         pos = (pos + 1) % 26;
+         display_val = pos_map[pos];
+      }
 
    private:
       std::string rotor_name;
       std::string wiring_str;
       int ring_setting;
       int pos;
-      int rotations;
+      char display_val;
+      alpha_int_array entry_map;
+      alpha_int_array exit_map;
+      alpha_int_array display_map;
+      alpha_int_array pos_map;
+      alpha_bool_array step_map;
    };
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/rotor_data.h	Fri Jun 22 20:15:11 2012 -0500
@@ -0,0 +1,47 @@
+#ifndef CPP_ENIGMA_ROTOR_DATA_H
+#define CPP_ENIGMA_ROTOR_DATA_H
+// Copyright (C) 2012 by Brian Neal.
+// This file is part of Cpp-Enigma, the Enigma Machine simulation.
+// Cpp-Enigma is released under the MIT License (see License.txt).
+//
+// rotor_data.h - This file contains standard rotor & reflector data.
+
+#include <map>
+#include <string>
+
+namespace enigma
+{
+   struct rotor_data
+   {
+      const char* wiring;
+      const char* stepping;
+   };
+
+   typedef std::map<std::string, rotor_data> rotor_data_map;
+
+   const rotor_data_map simulated_rotors
+   {
+      { "I",      { "EKMFLGDQVZNTOWYHXUSPAIBRCJ", "Q" } },
+      { "II",     { "AJDKSIRUXBLHWTMCQGZNPYFVOE", "E" } },
+      { "III",    { "BDFHJLCPRTXVZNYEIWGAKMUSQO", "V" } },
+      { "IV",     { "ESOVPZJAYQUIRHXLNFTGKDCMWB", "J" } },
+      { "V",      { "VZBRGITYUPSDNHLXAWMJQOFECK", "Z" } },
+      { "VI",     { "JPGVOUMFYQBENHZRDKASXLICTW", "ZM"} },
+      { "VII",    { "NZJHGRCXMYSWBOUFAIVLPEKQDT", "ZM"} },
+      { "VIII",   { "FKQHTLXOCBJSPDZRAMEWNIUYGV", "ZM" } },
+      { "Beta",   { "LEYJVCNIXWPBQMDRTAKZGFUHOS", nullptr } },
+      { "Gamma",  { "FSOKANUERHMBTIYCWLQPZXVGJD", nullptr } },
+    };
+
+   typedef std::map<std::string, const char*> reflector_data_map;
+
+   const reflector_data_map simulated_reflectors
+   {
+      { "B",      "YRUHQSLDPXNGOKMIEBFZCWVJAT" },
+      { "C",      "FVPJIAOYEDRZXWGCTKUQSBNMHL" },
+      { "B-Thin", "ENKQAUYWJICOPBLMDXZVFTHRGS" },
+      { "C-Thin", "RDOBJNTKVEHMLFCWZAXGYIPSUQ" },
+   };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/rotor_factory.cpp	Fri Jun 22 20:15:11 2012 -0500
@@ -0,0 +1,37 @@
+// Copyright (C) 2012 by Brian Neal.
+// This file is part of Cpp-Enigma, the Enigma Machine simulation.
+// Cpp-Enigma is released under the MIT License (see License.txt).
+//
+// rotor_factory.cpp - Implementation file for the rotor & reflector factory functions.
+
+#include <string>
+#include "rotor_factory.h"
+#include "rotor_data.h"
+#include "rotor.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+std::unique_ptr<enigma::rotor> enigma::create_rotor(const char* name, int ring_setting)
+{
+   auto iter = simulated_rotors.find(name);
+   if (iter == simulated_rotors.end())
+   {
+      throw rotor_error("unknown rotor type: " + std::string(name));
+   }
+
+   const rotor_data& rd(iter->second);
+   return std::unique_ptr<rotor>{new rotor{name, rd.wiring, ring_setting, rd.stepping}};
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+std::unique_ptr<enigma::rotor> enigma::create_reflector(const char* name)
+{
+   auto iter = simulated_reflectors.find(name);
+   if (iter == simulated_reflectors.end())
+   {
+      throw rotor_error("unknown reflector type: " + std::string(name));
+   }
+
+   return std::unique_ptr<rotor>{new rotor{name, iter->second}};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/rotor_factory.h	Fri Jun 22 20:15:11 2012 -0500
@@ -0,0 +1,22 @@
+#ifndef CPP_ENIGMA_ROTOR_FACTORY_H
+#define CPP_ENIGMA_ROTOR_FACTORY_H
+// Copyright (C) 2012 by Brian Neal.
+// This file is part of Cpp-Enigma, the Enigma Machine simulation.
+// Cpp-Enigma is released under the MIT License (see License.txt).
+//
+// rotor_factory.h - simulated rotor & reflector factory functions
+
+#include <memory>
+
+namespace enigma
+{
+   class rotor;
+
+   // Create a historical rotor with the given ring setting and return it:
+   std::unique_ptr<rotor> create_rotor(const char* name, int ring_setting = 0);
+
+   // Create a historical reflector and return it:
+   std::unique_ptr<rotor> create_reflector(const char* name);
+}
+
+#endif