annotate 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
rev   line source
bgneal@3 1 // Copyright (C) 2012 by Brian Neal.
bgneal@3 2 // This file is part of Cpp-Enigma, the Enigma Machine simulation.
bgneal@3 3 // Cpp-Enigma is released under the MIT License (see License.txt).
bgneal@3 4 //
bgneal@3 5 // plugboard.cpp - This is the implementation file for the plugboard class.
bgneal@3 6
bgneal@3 7 #include <algorithm>
bgneal@3 8 #include <set>
bgneal@3 9 #include <sstream>
bgneal@3 10 #include <utility>
bgneal@3 11 #include "plugboard.h"
bgneal@3 12
bgneal@3 13 using namespace enigma;
bgneal@3 14
bgneal@3 15 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 16
bgneal@3 17 namespace
bgneal@3 18 {
bgneal@3 19 // Returns a wiring map with "straight-through" mapping, where every input
bgneal@3 20 // pin 'i' is wired to the output pin 'i':
bgneal@3 21
bgneal@3 22 alpha_int_array straight_through_mapping()
bgneal@3 23 {
bgneal@3 24 alpha_int_array result;
bgneal@3 25 for (alpha_int_array::size_type i = 0; i < result.size(); ++i)
bgneal@3 26 {
bgneal@3 27 result[i] = i;
bgneal@3 28 }
bgneal@3 29 return result;
bgneal@3 30 }
bgneal@3 31 }
bgneal@3 32
bgneal@3 33 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 34
bgneal@3 35 plugboard::plugboard()
bgneal@3 36 : wiring_map(straight_through_mapping())
bgneal@3 37 {
bgneal@3 38 }
bgneal@3 39
bgneal@3 40 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 41
bgneal@3 42 plugboard::plugboard(const pair_vector& pairs)
bgneal@3 43 : wiring_map(straight_through_mapping())
bgneal@3 44 {
bgneal@3 45 construct_wiring(pairs);
bgneal@3 46 }
bgneal@3 47
bgneal@3 48 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 49
bgneal@3 50 plugboard::plugboard(const std::string& settings)
bgneal@3 51 : wiring_map(straight_through_mapping())
bgneal@3 52 {
bgneal@3 53 if (settings.empty())
bgneal@3 54 {
bgneal@3 55 return;
bgneal@3 56 }
bgneal@3 57
bgneal@3 58 pair_vector pairs;
bgneal@3 59
bgneal@3 60 // detect which syntax is being used
bgneal@3 61 if (settings.find('/') == std::string::npos)
bgneal@3 62 {
bgneal@3 63 // Assume Heer (army) syntax
bgneal@3 64
bgneal@3 65 std::istringstream iss(settings);
bgneal@3 66 std::string s;
bgneal@3 67 while (iss >> s)
bgneal@3 68 {
bgneal@3 69 if (s.size() != 2)
bgneal@3 70 {
bgneal@3 71 throw plugboard_error("invalid settings string");
bgneal@3 72 }
bgneal@3 73 const int m = std::toupper(s[0]) - 'A';
bgneal@3 74 const int n = std::toupper(s[1]) - 'A';
bgneal@3 75
bgneal@3 76 pairs.push_back(std::make_pair(m, n));
bgneal@3 77 }
bgneal@3 78 }
bgneal@3 79 else
bgneal@3 80 {
bgneal@3 81 // Assume Kriegsmarine (navy) syntax
bgneal@3 82
bgneal@3 83 std::istringstream iss(settings);
bgneal@3 84 std::string s;
bgneal@3 85 while (iss >> s)
bgneal@3 86 {
bgneal@3 87 const std::size_t x = s.find('/');
bgneal@3 88 if (x == std::string::npos || x == s.size() - 1)
bgneal@3 89 {
bgneal@3 90 throw plugboard_error("invalid settings string");
bgneal@3 91 }
bgneal@3 92
bgneal@3 93 int m;
bgneal@3 94 int n;
bgneal@3 95 std::istringstream mss(s.substr(0, x));
bgneal@3 96 std::istringstream nss(s.substr(x + 1));
bgneal@3 97
bgneal@3 98 if ((mss >> m) && (nss >> n))
bgneal@3 99 {
bgneal@3 100 pairs.push_back(std::make_pair(m - 1, n - 1));
bgneal@3 101 }
bgneal@3 102 else
bgneal@3 103 {
bgneal@3 104 throw plugboard_error("invalid settings string");
bgneal@3 105 }
bgneal@3 106 }
bgneal@3 107 }
bgneal@3 108
bgneal@3 109 construct_wiring(pairs);
bgneal@3 110 }
bgneal@3 111
bgneal@3 112 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 113
bgneal@3 114 plugboard::pair_vector plugboard::get_pairs() const
bgneal@3 115 {
bgneal@3 116 std::set<std::pair<int, int>> pair_set;
bgneal@3 117 for (int i = 0; i < 26; ++i)
bgneal@3 118 {
bgneal@3 119 const int j = wiring_map[i];
bgneal@3 120 if (i < j)
bgneal@3 121 {
bgneal@3 122 pair_set.insert(std::make_pair(i, j));
bgneal@3 123 }
bgneal@3 124 }
bgneal@3 125
bgneal@3 126 return pair_vector(pair_set.begin(), pair_set.end());
bgneal@3 127 }
bgneal@3 128
bgneal@3 129 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 130
bgneal@3 131 std::string plugboard::army_str() const
bgneal@3 132 {
bgneal@3 133 const auto pairs = get_pairs();
bgneal@3 134
bgneal@3 135 std::string s;
bgneal@3 136
bgneal@3 137 for (const auto p : pairs)
bgneal@3 138 {
bgneal@3 139 s += static_cast<char>(p.first + 'A');
bgneal@3 140 s += static_cast<char>(p.second + 'A');
bgneal@3 141 s += ' ';
bgneal@3 142 }
bgneal@3 143 s.erase(s.size() - 1); // erase trailing space
bgneal@3 144 return s;
bgneal@3 145 }
bgneal@3 146
bgneal@3 147 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 148
bgneal@3 149 std::string plugboard::navy_str() const
bgneal@3 150 {
bgneal@3 151 const auto pairs = get_pairs();
bgneal@3 152
bgneal@3 153 std::ostringstream os;
bgneal@3 154 for (const auto p : pairs)
bgneal@3 155 {
bgneal@3 156 os << (p.first + 1) << '/' << (p.second + 1) << ' ';
bgneal@3 157 }
bgneal@3 158
bgneal@3 159 std::string s(os.str());
bgneal@3 160 s.erase(s.size() - 1); // erase trailing space
bgneal@3 161 return s;
bgneal@3 162 }
bgneal@3 163
bgneal@3 164 ////////////////////////////////////////////////////////////////////////////////
bgneal@3 165
bgneal@3 166 void plugboard::construct_wiring(const pair_vector& pairs)
bgneal@3 167 {
bgneal@3 168 if (pairs.size() > max_pairs)
bgneal@3 169 {
bgneal@3 170 throw plugboard_error("Too many pairs");
bgneal@3 171 }
bgneal@3 172
bgneal@3 173 // range check the wiring & ensure a path appears at most once
bgneal@3 174 // (the double braces were added because gcc 4.6.3 emits a warning without
bgneal@3 175 // them with -std=c++0x -Wall -Wextra -pedantic)
bgneal@3 176 alpha_int_array counts = {{ 0 }};
bgneal@3 177 for (const auto& p : pairs)
bgneal@3 178 {
bgneal@3 179 if (p.first < 0 || p.second < 0 || p.first >= 26 || p.second >= 26)
bgneal@3 180 {
bgneal@3 181 throw plugboard_error("invalid wiring pair");
bgneal@3 182 }
bgneal@3 183 ++counts[p.first];
bgneal@3 184 ++counts[p.second];
bgneal@3 185 }
bgneal@3 186
bgneal@3 187 if (std::find_if(counts.begin(),
bgneal@3 188 counts.end(),
bgneal@3 189 [](int n) { return n > 1; }) != counts.end())
bgneal@3 190 {
bgneal@3 191 throw plugboard_error("duplicate connection");
bgneal@3 192 }
bgneal@3 193
bgneal@3 194 // all checks pass if we made it this far; make the connections
bgneal@3 195
bgneal@3 196 for (auto& p : pairs)
bgneal@3 197 {
bgneal@3 198 wiring_map[p.first] = p.second;
bgneal@3 199 wiring_map[p.second] = p.first;
bgneal@3 200 }
bgneal@3 201 }