Mercurial > public > cpp-enigma
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 } |