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 }
|