Mercurial > public > cpp-enigma
changeset 1:1459e74fda3f
Finished creating rotor class and factories.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Fri, 22 Jun 2012 20:15:11 -0500 |
parents | 74ebb2150658 |
children | 713fa2a9ea9a |
files | enigma/SConscript enigma/enigma_types.h enigma/enigma_utils.h enigma/rotor.cpp enigma/rotor.h enigma/rotor_data.h enigma/rotor_factory.cpp enigma/rotor_factory.h |
diffstat | 8 files changed, 267 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- a/enigma/SConscript Thu Jun 21 21:05:26 2012 -0500 +++ b/enigma/SConscript Fri Jun 22 20:15:11 2012 -0500 @@ -2,6 +2,7 @@ sources = Split(""" rotor.cpp + rotor_factory.cpp """) env.StaticLibrary('enigma', sources)
--- a/enigma/enigma_types.h Thu Jun 21 21:05:26 2012 -0500 +++ b/enigma/enigma_types.h Fri Jun 22 20:15:11 2012 -0500 @@ -8,6 +8,7 @@ #include <exception> #include <string> +#include <array> namespace enigma { @@ -28,6 +29,10 @@ private: std::string what_arg; }; + + // Arrays of 26 items are very commonly used: + typedef std::array<int, 26> alpha_int_array; + typedef std::array<bool, 26> alpha_bool_array; } #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/enigma_utils.h Fri Jun 22 20:15:11 2012 -0500 @@ -0,0 +1,22 @@ +#ifndef CPP_ENIGMA_ENIGMA_UTILS_H +#define CPP_ENIGMA_ENIGMA_UTILS_H +// Copyright (C) 2012 by Brian Neal. +// This file is part of Cpp-Enigma, the Enigma Machine simulation. +// Cpp-Enigma is released under the MIT License (see License.txt). +// +// enigma_utils.h - This file contains common functions used throughout Cpp-Enigma. + +namespace enigma +{ + // This version of mod acts like Python's with respect to negative dividends. + inline int alpha_mod(int dividend) + { + if (dividend < 0) + { + dividend += 26; + } + return dividend % 26; + } +} + +#endif
--- a/enigma/rotor.cpp Thu Jun 21 21:05:26 2012 -0500 +++ b/enigma/rotor.cpp Fri Jun 22 20:15:11 2012 -0500 @@ -8,21 +8,37 @@ #include <array> #include <algorithm> #include "rotor.h" +#include "enigma_utils.h" using namespace enigma; +//////////////////////////////////////////////////////////////////////////////// + namespace { const char* const ucase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const std::set<char> ucase_set(ucase, ucase + 26); + + // Turned into a function to avoid translation unit initialization order problems. + const std::set<char>& get_ucase_set() + { + static const std::set<char> the_set(ucase, ucase + 26); + return the_set; + } } +//////////////////////////////////////////////////////////////////////////////// + rotor::rotor(const char* name, const char* wiring, int ring_setting, const char* stepping) - : rotor_name(name), - wiring_str(wiring), - ring_setting(ring_setting), - pos(0), - rotations(0) + : rotor_name{name}, + wiring_str{wiring}, + ring_setting{ring_setting}, + pos{0}, + display_val{'A'}, + entry_map(), + exit_map(), + display_map(), + pos_map(), + step_map() { // check wiring length if (wiring_str.size() != 26) @@ -32,12 +48,13 @@ // ensure wiring contains only uppercase letters & every char must appear // exactly once: + const std::set<char>& ucase_set(get_ucase_set()); - std::array<int, 26> letter_counts {{ 0 }}; + // g++ 4.6.3 warns about missing braces unless we double them up, below: + std::array<int, 26> letter_counts = {{ 0 }}; for (int i = 0; i < 26; ++i) { - const char c(wiring_str[i]); - + const char c{wiring_str[i]}; if (ucase_set.find(c) == ucase_set.end()) { throw rotor_error("invalid wiring"); @@ -51,4 +68,58 @@ { throw rotor_error("invalid wiring; duplicate letter"); } + + if (ring_setting < 0 || ring_setting >= 26) + { + throw rotor_error("invalid ring setting"); + } + set_ring_setting(ring_setting); + + // Initialize our two arrays that describe the internal wiring. Arrays are used + // to do fast lookup from both entry (from the right) and exit (from the + // left). + + for (int i = 0; i < 26; ++i) + { + const int v = wiring_str[i] - 'A'; + entry_map[i] = v; + exit_map[v] = i; + } + + // Build a lookup table that tells us when the pawls are allowed to step. + // The index to this array is the current display letter [A-Z] - 'A'. + + if (stepping != nullptr) + { + for (char c = *stepping; *stepping != '\0'; ++stepping) + { + if (ucase_set.find(c) != ucase_set.end()) + { + step_map[c - 'A'] = true; + } + else + { + throw rotor_error("invalid stepping"); + } + } + } + + // set initial position + set_display('A'); } + +//////////////////////////////////////////////////////////////////////////////// + +void rotor::set_ring_setting(int n) +{ + ring_setting = n; + + // Build a mapping from window display values to positions + // and a reverse mapping of position to display value: + for (int i = 0; i < 26; ++i) + { + const int n = alpha_mod(i - ring_setting); + display_map[i] = n; + pos_map[n] = i + 'A'; + } +}
--- a/enigma/rotor.h Thu Jun 21 21:05:26 2012 -0500 +++ b/enigma/rotor.h Fri Jun 22 20:15:11 2012 -0500 @@ -8,6 +8,7 @@ #include <string> #include "enigma_types.h" +#include "enigma_utils.h" namespace enigma @@ -93,39 +94,82 @@ // simply for simulation convenience. // display. - rotor(const char* name, const char* wiring, int ring_setting = 0, const char* stepping = 0); + rotor(const char* name, const char* wiring, int ring_setting = 0, const char* stepping = nullptr); // Returns the rotor name: const std::string& name() const { return rotor_name; } - // Spin the rotor such that the string val appears in the operator window: - void set_display(const char* val); + // Spin the rotor such that the char val appears in the operator window: + void set_display(char val) + { + pos = display_map[val - 'A']; + display_val = val; + } // Returns what is currently being displayed in the operator window: - std::string get_display() const; + char get_display() const { return display_val; } + + + // sets the ring setting to n, where n [0-25]: + void set_ring_setting(int n); + + // get the current ring setting: + int get_ring_setting() const { return ring_setting; } // Simulate a signal entering the rotor from the right at a given pin: // n must be an integer between 0 and 25. // Returns the contact number of the output signal (0-25). - int signal_in(int n) const; + int signal_in(int n) const + { + // determine what pin we have at that position due to rotation + const int pin = (n + pos) % 26; + + // run it through the internal wiring + const int contact = entry_map[pin]; + + // turn back into a position due to rotation + return alpha_mod(contact - pos); + } // Simulate a signal entering the rotor from the left at a given contact position n. // n must be an integer between 0 and 25. // Returns the pin number of the output signal (0-25). - int signal_out(int n) const; + int signal_out(int n) const + { + // determine what contact we have at that position due to rotation + const int contact = (n + pos) % 26; + + // run it through the internal wiring + const int pin = exit_map[contact]; + + // turn back into a position due to rotation + return alpha_mod(pin - pos); + } // Return true if this rotor has a notch in the stepping position and false otherwise: - bool notch_over_pawl() const; + bool notch_over_pawl() const + { + return step_map[display_val - 'A']; + } // Rotate the rotor forward one step: - void rotate(); + void rotate() + { + pos = (pos + 1) % 26; + display_val = pos_map[pos]; + } private: std::string rotor_name; std::string wiring_str; int ring_setting; int pos; - int rotations; + char display_val; + alpha_int_array entry_map; + alpha_int_array exit_map; + alpha_int_array display_map; + alpha_int_array pos_map; + alpha_bool_array step_map; }; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/rotor_data.h Fri Jun 22 20:15:11 2012 -0500 @@ -0,0 +1,47 @@ +#ifndef CPP_ENIGMA_ROTOR_DATA_H +#define CPP_ENIGMA_ROTOR_DATA_H +// Copyright (C) 2012 by Brian Neal. +// This file is part of Cpp-Enigma, the Enigma Machine simulation. +// Cpp-Enigma is released under the MIT License (see License.txt). +// +// rotor_data.h - This file contains standard rotor & reflector data. + +#include <map> +#include <string> + +namespace enigma +{ + struct rotor_data + { + const char* wiring; + const char* stepping; + }; + + typedef std::map<std::string, rotor_data> rotor_data_map; + + const rotor_data_map simulated_rotors + { + { "I", { "EKMFLGDQVZNTOWYHXUSPAIBRCJ", "Q" } }, + { "II", { "AJDKSIRUXBLHWTMCQGZNPYFVOE", "E" } }, + { "III", { "BDFHJLCPRTXVZNYEIWGAKMUSQO", "V" } }, + { "IV", { "ESOVPZJAYQUIRHXLNFTGKDCMWB", "J" } }, + { "V", { "VZBRGITYUPSDNHLXAWMJQOFECK", "Z" } }, + { "VI", { "JPGVOUMFYQBENHZRDKASXLICTW", "ZM"} }, + { "VII", { "NZJHGRCXMYSWBOUFAIVLPEKQDT", "ZM"} }, + { "VIII", { "FKQHTLXOCBJSPDZRAMEWNIUYGV", "ZM" } }, + { "Beta", { "LEYJVCNIXWPBQMDRTAKZGFUHOS", nullptr } }, + { "Gamma", { "FSOKANUERHMBTIYCWLQPZXVGJD", nullptr } }, + }; + + typedef std::map<std::string, const char*> reflector_data_map; + + const reflector_data_map simulated_reflectors + { + { "B", "YRUHQSLDPXNGOKMIEBFZCWVJAT" }, + { "C", "FVPJIAOYEDRZXWGCTKUQSBNMHL" }, + { "B-Thin", "ENKQAUYWJICOPBLMDXZVFTHRGS" }, + { "C-Thin", "RDOBJNTKVEHMLFCWZAXGYIPSUQ" }, + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/rotor_factory.cpp Fri Jun 22 20:15:11 2012 -0500 @@ -0,0 +1,37 @@ +// Copyright (C) 2012 by Brian Neal. +// This file is part of Cpp-Enigma, the Enigma Machine simulation. +// Cpp-Enigma is released under the MIT License (see License.txt). +// +// rotor_factory.cpp - Implementation file for the rotor & reflector factory functions. + +#include <string> +#include "rotor_factory.h" +#include "rotor_data.h" +#include "rotor.h" + +//////////////////////////////////////////////////////////////////////////////// + +std::unique_ptr<enigma::rotor> enigma::create_rotor(const char* name, int ring_setting) +{ + auto iter = simulated_rotors.find(name); + if (iter == simulated_rotors.end()) + { + throw rotor_error("unknown rotor type: " + std::string(name)); + } + + const rotor_data& rd(iter->second); + return std::unique_ptr<rotor>{new rotor{name, rd.wiring, ring_setting, rd.stepping}}; +} + +//////////////////////////////////////////////////////////////////////////////// + +std::unique_ptr<enigma::rotor> enigma::create_reflector(const char* name) +{ + auto iter = simulated_reflectors.find(name); + if (iter == simulated_reflectors.end()) + { + throw rotor_error("unknown reflector type: " + std::string(name)); + } + + return std::unique_ptr<rotor>{new rotor{name, iter->second}}; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/rotor_factory.h Fri Jun 22 20:15:11 2012 -0500 @@ -0,0 +1,22 @@ +#ifndef CPP_ENIGMA_ROTOR_FACTORY_H +#define CPP_ENIGMA_ROTOR_FACTORY_H +// Copyright (C) 2012 by Brian Neal. +// This file is part of Cpp-Enigma, the Enigma Machine simulation. +// Cpp-Enigma is released under the MIT License (see License.txt). +// +// rotor_factory.h - simulated rotor & reflector factory functions + +#include <memory> + +namespace enigma +{ + class rotor; + + // Create a historical rotor with the given ring setting and return it: + std::unique_ptr<rotor> create_rotor(const char* name, int ring_setting = 0); + + // Create a historical reflector and return it: + std::unique_ptr<rotor> create_reflector(const char* name); +} + +#endif