bgneal@3: // Copyright (C) 2012 by Brian Neal. bgneal@3: // This file is part of Cpp-Enigma, the Enigma Machine simulation. bgneal@3: // Cpp-Enigma is released under the MIT License (see License.txt). bgneal@3: // bgneal@3: // plugboard.cpp - This is the implementation file for the plugboard class. bgneal@3: bgneal@3: #include bgneal@3: #include bgneal@3: #include bgneal@3: #include bgneal@3: #include "plugboard.h" bgneal@3: bgneal@3: using namespace enigma; bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: namespace bgneal@3: { bgneal@3: // Returns a wiring map with "straight-through" mapping, where every input bgneal@3: // pin 'i' is wired to the output pin 'i': bgneal@3: bgneal@3: alpha_int_array straight_through_mapping() bgneal@3: { bgneal@3: alpha_int_array result; bgneal@3: for (alpha_int_array::size_type i = 0; i < result.size(); ++i) bgneal@3: { bgneal@3: result[i] = i; bgneal@3: } bgneal@3: return result; bgneal@3: } bgneal@3: } bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: plugboard::plugboard() bgneal@3: : wiring_map(straight_through_mapping()) bgneal@3: { bgneal@3: } bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: plugboard::plugboard(const pair_vector& pairs) bgneal@3: : wiring_map(straight_through_mapping()) bgneal@3: { bgneal@3: construct_wiring(pairs); bgneal@3: } bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: plugboard::plugboard(const std::string& settings) bgneal@3: : wiring_map(straight_through_mapping()) bgneal@3: { bgneal@3: if (settings.empty()) bgneal@3: { bgneal@3: return; bgneal@3: } bgneal@3: bgneal@3: pair_vector pairs; bgneal@3: bgneal@3: // detect which syntax is being used bgneal@3: if (settings.find('/') == std::string::npos) bgneal@3: { bgneal@3: // Assume Heer (army) syntax bgneal@3: bgneal@3: std::istringstream iss(settings); bgneal@3: std::string s; bgneal@3: while (iss >> s) bgneal@3: { bgneal@3: if (s.size() != 2) bgneal@3: { bgneal@3: throw plugboard_error("invalid settings string"); bgneal@3: } bgneal@3: const int m = std::toupper(s[0]) - 'A'; bgneal@3: const int n = std::toupper(s[1]) - 'A'; bgneal@3: bgneal@3: pairs.push_back(std::make_pair(m, n)); bgneal@3: } bgneal@3: } bgneal@3: else bgneal@3: { bgneal@3: // Assume Kriegsmarine (navy) syntax bgneal@3: bgneal@3: std::istringstream iss(settings); bgneal@3: std::string s; bgneal@3: while (iss >> s) bgneal@3: { bgneal@3: const std::size_t x = s.find('/'); bgneal@3: if (x == std::string::npos || x == s.size() - 1) bgneal@3: { bgneal@3: throw plugboard_error("invalid settings string"); bgneal@3: } bgneal@3: bgneal@3: int m; bgneal@3: int n; bgneal@3: std::istringstream mss(s.substr(0, x)); bgneal@3: std::istringstream nss(s.substr(x + 1)); bgneal@3: bgneal@3: if ((mss >> m) && (nss >> n)) bgneal@3: { bgneal@3: pairs.push_back(std::make_pair(m - 1, n - 1)); bgneal@3: } bgneal@3: else bgneal@3: { bgneal@3: throw plugboard_error("invalid settings string"); bgneal@3: } bgneal@3: } bgneal@3: } bgneal@3: bgneal@3: construct_wiring(pairs); bgneal@3: } bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: plugboard::pair_vector plugboard::get_pairs() const bgneal@3: { bgneal@3: std::set> pair_set; bgneal@3: for (int i = 0; i < 26; ++i) bgneal@3: { bgneal@3: const int j = wiring_map[i]; bgneal@3: if (i < j) bgneal@3: { bgneal@3: pair_set.insert(std::make_pair(i, j)); bgneal@3: } bgneal@3: } bgneal@3: bgneal@3: return pair_vector(pair_set.begin(), pair_set.end()); bgneal@3: } bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: std::string plugboard::army_str() const bgneal@3: { bgneal@3: const auto pairs = get_pairs(); bgneal@3: bgneal@3: std::string s; bgneal@3: bgneal@3: for (const auto p : pairs) bgneal@3: { bgneal@3: s += static_cast(p.first + 'A'); bgneal@3: s += static_cast(p.second + 'A'); bgneal@3: s += ' '; bgneal@3: } bgneal@3: s.erase(s.size() - 1); // erase trailing space bgneal@3: return s; bgneal@3: } bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: std::string plugboard::navy_str() const bgneal@3: { bgneal@3: const auto pairs = get_pairs(); bgneal@3: bgneal@3: std::ostringstream os; bgneal@3: for (const auto p : pairs) bgneal@3: { bgneal@3: os << (p.first + 1) << '/' << (p.second + 1) << ' '; bgneal@3: } bgneal@3: bgneal@3: std::string s(os.str()); bgneal@3: s.erase(s.size() - 1); // erase trailing space bgneal@3: return s; bgneal@3: } bgneal@3: bgneal@3: //////////////////////////////////////////////////////////////////////////////// bgneal@3: bgneal@3: void plugboard::construct_wiring(const pair_vector& pairs) bgneal@3: { bgneal@3: if (pairs.size() > max_pairs) bgneal@3: { bgneal@3: throw plugboard_error("Too many pairs"); bgneal@3: } bgneal@3: bgneal@3: // range check the wiring & ensure a path appears at most once bgneal@3: // (the double braces were added because gcc 4.6.3 emits a warning without bgneal@3: // them with -std=c++0x -Wall -Wextra -pedantic) bgneal@3: alpha_int_array counts = {{ 0 }}; bgneal@3: for (const auto& p : pairs) bgneal@3: { bgneal@3: if (p.first < 0 || p.second < 0 || p.first >= 26 || p.second >= 26) bgneal@3: { bgneal@3: throw plugboard_error("invalid wiring pair"); bgneal@3: } bgneal@3: ++counts[p.first]; bgneal@3: ++counts[p.second]; bgneal@3: } bgneal@3: bgneal@3: if (std::find_if(counts.begin(), bgneal@3: counts.end(), bgneal@3: [](int n) { return n > 1; }) != counts.end()) bgneal@3: { bgneal@3: throw plugboard_error("duplicate connection"); bgneal@3: } bgneal@3: bgneal@3: // all checks pass if we made it this far; make the connections bgneal@3: bgneal@3: for (auto& p : pairs) bgneal@3: { bgneal@3: wiring_map[p.first] = p.second; bgneal@3: wiring_map[p.second] = p.first; bgneal@3: } bgneal@3: }