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