bgneal@0: // Copyright (C) 2012 by Brian Neal. bgneal@0: // This file is part of Cpp-Enigma, the Enigma Machine simulation. bgneal@0: // Cpp-Enigma is released under the MIT License (see License.txt). bgneal@0: // bgneal@0: // rotor.cpp - Implementation file for the rotor class. bgneal@0: bgneal@0: #include bgneal@0: #include bgneal@0: #include bgneal@0: #include "rotor.h" bgneal@1: #include "enigma_utils.h" bgneal@0: bgneal@0: using namespace enigma; bgneal@0: bgneal@1: //////////////////////////////////////////////////////////////////////////////// bgneal@1: bgneal@0: namespace bgneal@0: { bgneal@0: const char* const ucase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; bgneal@1: bgneal@1: // Turned into a function to avoid translation unit initialization order problems. bgneal@1: const std::set& get_ucase_set() bgneal@1: { bgneal@1: static const std::set the_set(ucase, ucase + 26); bgneal@1: return the_set; bgneal@1: } bgneal@0: } bgneal@0: bgneal@1: //////////////////////////////////////////////////////////////////////////////// bgneal@1: bgneal@0: rotor::rotor(const char* name, const char* wiring, int ring_setting, const char* stepping) bgneal@1: : rotor_name{name}, bgneal@1: wiring_str{wiring}, bgneal@1: ring_setting{ring_setting}, bgneal@1: pos{0}, bgneal@1: display_val{'A'}, bgneal@1: entry_map(), bgneal@1: exit_map(), bgneal@1: display_map(), bgneal@1: pos_map(), bgneal@1: step_map() bgneal@0: { bgneal@0: // check wiring length bgneal@0: if (wiring_str.size() != 26) bgneal@0: { bgneal@0: throw rotor_error("invalid wiring length"); bgneal@0: } bgneal@0: bgneal@0: // ensure wiring contains only uppercase letters & every char must appear bgneal@0: // exactly once: bgneal@1: const std::set& ucase_set(get_ucase_set()); bgneal@0: bgneal@1: // g++ 4.6.3 warns about missing braces unless we double them up, below: bgneal@1: std::array letter_counts = {{ 0 }}; bgneal@0: for (int i = 0; i < 26; ++i) bgneal@0: { bgneal@1: const char c{wiring_str[i]}; bgneal@0: if (ucase_set.find(c) == ucase_set.end()) bgneal@0: { bgneal@0: throw rotor_error("invalid wiring"); bgneal@0: } bgneal@0: ++letter_counts[c - 'A']; bgneal@0: } bgneal@0: bgneal@0: if (std::find_if(letter_counts.begin(), bgneal@0: letter_counts.end(), bgneal@0: [](int n) { return n != 1; }) != letter_counts.end()) bgneal@0: { bgneal@0: throw rotor_error("invalid wiring; duplicate letter"); bgneal@0: } bgneal@1: bgneal@1: if (ring_setting < 0 || ring_setting >= 26) bgneal@1: { bgneal@1: throw rotor_error("invalid ring setting"); bgneal@1: } bgneal@1: set_ring_setting(ring_setting); bgneal@1: bgneal@1: // Initialize our two arrays that describe the internal wiring. Arrays are used bgneal@1: // to do fast lookup from both entry (from the right) and exit (from the bgneal@1: // left). bgneal@1: bgneal@1: for (int i = 0; i < 26; ++i) bgneal@1: { bgneal@1: const int v = wiring_str[i] - 'A'; bgneal@1: entry_map[i] = v; bgneal@1: exit_map[v] = i; bgneal@1: } bgneal@1: bgneal@1: // Build a lookup table that tells us when the pawls are allowed to step. bgneal@1: // The index to this array is the current display letter [A-Z] - 'A'. bgneal@1: bgneal@1: if (stepping != nullptr) bgneal@1: { bgneal@2: for (char c = *stepping; *stepping != '\0'; c = *(++stepping)) bgneal@1: { bgneal@1: if (ucase_set.find(c) != ucase_set.end()) bgneal@1: { bgneal@1: step_map[c - 'A'] = true; bgneal@1: } bgneal@1: else bgneal@1: { bgneal@1: throw rotor_error("invalid stepping"); bgneal@1: } bgneal@1: } bgneal@1: } bgneal@1: bgneal@1: // set initial position bgneal@1: set_display('A'); bgneal@0: } bgneal@1: bgneal@1: //////////////////////////////////////////////////////////////////////////////// bgneal@1: bgneal@1: void rotor::set_ring_setting(int n) bgneal@1: { bgneal@1: ring_setting = n; bgneal@1: bgneal@1: // Build a mapping from window display values to positions bgneal@1: // and a reverse mapping of position to display value: bgneal@1: for (int i = 0; i < 26; ++i) bgneal@1: { bgneal@1: const int n = alpha_mod(i - ring_setting); bgneal@1: display_map[i] = n; bgneal@1: pos_map[n] = i + 'A'; bgneal@1: } bgneal@1: }