diff enigma/plugboard.cpp @ 3:f4e25e6b76c3

Created plugboard class and tests.
author Brian Neal <bgneal@gmail.com>
date Sat, 23 Jun 2012 23:28:17 -0500
parents
children 3370383116db
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/plugboard.cpp	Sat Jun 23 23:28:17 2012 -0500
@@ -0,0 +1,201 @@
+// 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).
+//
+// plugboard.cpp - This is the implementation file for the plugboard class.
+
+#include <algorithm>
+#include <set>
+#include <sstream>
+#include <utility>
+#include "plugboard.h"
+
+using namespace enigma;
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+   // Returns a wiring map with "straight-through" mapping, where every input
+   // pin 'i' is wired to the output pin 'i':
+
+   alpha_int_array straight_through_mapping()
+   {
+      alpha_int_array result;
+      for (alpha_int_array::size_type i = 0; i < result.size(); ++i)
+      {
+         result[i] = i;
+      }
+      return result;
+   }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+plugboard::plugboard()
+ : wiring_map(straight_through_mapping())
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+plugboard::plugboard(const pair_vector& pairs)
+ : wiring_map(straight_through_mapping())
+{
+   construct_wiring(pairs);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+plugboard::plugboard(const std::string& settings)
+ : wiring_map(straight_through_mapping())
+{
+   if (settings.empty())
+   {
+      return;
+   }
+
+   pair_vector pairs;
+
+   // detect which syntax is being used
+   if (settings.find('/') == std::string::npos)
+   {
+      // Assume Heer (army) syntax
+
+      std::istringstream iss(settings);
+      std::string s;
+      while (iss >> s)
+      {
+         if (s.size() != 2)
+         {
+            throw plugboard_error("invalid settings string");
+         }
+         const int m = std::toupper(s[0]) - 'A';
+         const int n = std::toupper(s[1]) - 'A';
+
+         pairs.push_back(std::make_pair(m, n));
+      }
+   }
+   else
+   {
+      // Assume Kriegsmarine (navy) syntax
+
+      std::istringstream iss(settings);
+      std::string s;
+      while (iss >> s)
+      {
+         const std::size_t x = s.find('/');
+         if (x == std::string::npos || x == s.size() - 1)
+         {
+            throw plugboard_error("invalid settings string");
+         }
+
+         int m;
+         int n;
+         std::istringstream mss(s.substr(0, x));
+         std::istringstream nss(s.substr(x + 1));
+
+         if ((mss >> m) && (nss >> n))
+         {
+            pairs.push_back(std::make_pair(m - 1, n - 1));
+         }
+         else
+         {
+            throw plugboard_error("invalid settings string");
+         }
+      }
+   }
+
+   construct_wiring(pairs);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+plugboard::pair_vector plugboard::get_pairs() const
+{
+   std::set<std::pair<int, int>> pair_set;
+   for (int i = 0; i < 26; ++i)
+   {
+      const int j = wiring_map[i];
+      if (i < j)
+      {
+         pair_set.insert(std::make_pair(i, j));
+      }
+   }
+
+   return pair_vector(pair_set.begin(), pair_set.end());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+std::string plugboard::army_str() const
+{
+   const auto pairs = get_pairs();
+
+   std::string s;
+
+   for (const auto p : pairs)
+   {
+      s += static_cast<char>(p.first + 'A');
+      s += static_cast<char>(p.second + 'A');
+      s += ' ';
+   }
+   s.erase(s.size() - 1);     // erase trailing space
+   return s;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+std::string plugboard::navy_str() const
+{
+   const auto pairs = get_pairs();
+
+   std::ostringstream os;
+   for (const auto p : pairs)
+   {
+      os << (p.first + 1) << '/' << (p.second + 1) << ' ';
+   }
+
+   std::string s(os.str());
+   s.erase(s.size() - 1);     // erase trailing space
+   return s;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void plugboard::construct_wiring(const pair_vector& pairs)
+{
+   if (pairs.size() > max_pairs)
+   {
+      throw plugboard_error("Too many pairs");
+   }
+
+   // range check the wiring & ensure a path appears at most once
+   // (the double braces were added because gcc 4.6.3 emits a warning without
+   // them with -std=c++0x -Wall -Wextra -pedantic)
+   alpha_int_array counts = {{ 0 }};
+   for (const auto& p : pairs)
+   {
+      if (p.first < 0 || p.second < 0 || p.first >= 26 || p.second >= 26)
+      {
+         throw plugboard_error("invalid wiring pair");
+      }
+      ++counts[p.first];
+      ++counts[p.second];
+   }
+
+   if (std::find_if(counts.begin(),
+                    counts.end(),
+                    [](int n) { return n > 1; }) != counts.end())
+   {
+      throw plugboard_error("duplicate connection");
+   }
+
+   // all checks pass if we made it this far; make the connections
+
+   for (auto& p : pairs)
+   {
+      wiring_map[p.first] = p.second;
+      wiring_map[p.second] = p.first;
+   }
+}