1.0
This commit is contained in:
commit
2dab896a09
11 changed files with 1116 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'enigma'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=enigma",
|
||||||
|
"--package=enigma"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "enigma",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'enigma'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bin=enigma",
|
||||||
|
"--package=enigma"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "enigma",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
224
Cargo.lock
generated
Normal file
224
Cargo.lock
generated
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enigma"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pretty_env_logger",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||||
|
dependencies = [
|
||||||
|
"quick-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty_env_logger"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||||
|
dependencies = [
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"rand_hc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "enigma"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["vodofrede <frederik@palmoe.dk>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4.14"
|
||||||
|
pretty_env_logger = "0.4.0"
|
||||||
|
rand = "0.8.3"
|
67
src/enigma.rs
Normal file
67
src/enigma.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Enigma {
|
||||||
|
rotors: Vec<Rotor>,
|
||||||
|
plugboard: Plugboard,
|
||||||
|
reflector: Reflector,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Enigma {
|
||||||
|
pub fn new(rotors: Vec<Rotor>, plugboard: Plugboard, reflector: Reflector) -> Self {
|
||||||
|
Enigma {
|
||||||
|
rotors,
|
||||||
|
plugboard,
|
||||||
|
reflector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn encode(&mut self, input: &str) -> Result<String, &'static str> {
|
||||||
|
let output: Result<String, &'static str> = input
|
||||||
|
.chars()
|
||||||
|
.map(|c| {
|
||||||
|
self.rotate_rotors();
|
||||||
|
|
||||||
|
let mut letter = Letter::from_str(&c.to_string())?;
|
||||||
|
letter = self.plugboard.letter(letter);
|
||||||
|
|
||||||
|
for rotor in self.rotors.iter() {
|
||||||
|
letter = rotor.forwards(letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
letter = self.reflector.letter(letter);
|
||||||
|
|
||||||
|
for rotor in self.rotors.iter().rev() {
|
||||||
|
letter = rotor.backwards(letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
letter = self.plugboard.letter(letter);
|
||||||
|
|
||||||
|
Ok(char::from(letter))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_rotors(&mut self) {
|
||||||
|
let mut rotate = true;
|
||||||
|
|
||||||
|
for rotor in self.rotors.iter_mut() {
|
||||||
|
let rotate_next = rotor.at_notch();
|
||||||
|
|
||||||
|
if rotate {
|
||||||
|
rotor.rotate();
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate = rotate_next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Random for Enigma {
|
||||||
|
fn random() -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
212
src/letter.rs
Normal file
212
src/letter.rs
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
use crate::*;
|
||||||
|
use rand::prelude::*;
|
||||||
|
use std::{
|
||||||
|
ops::{Add, Sub},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const AMOUNT_OF_LETTERS: usize = 26;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord)]
|
||||||
|
pub enum Letter {
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
C = 2,
|
||||||
|
D = 3,
|
||||||
|
E = 4,
|
||||||
|
F = 5,
|
||||||
|
G = 6,
|
||||||
|
H = 7,
|
||||||
|
I = 8,
|
||||||
|
J = 9,
|
||||||
|
K = 10,
|
||||||
|
L = 11,
|
||||||
|
M = 12,
|
||||||
|
N = 13,
|
||||||
|
O = 14,
|
||||||
|
P = 15,
|
||||||
|
Q = 16,
|
||||||
|
R = 17,
|
||||||
|
S = 18,
|
||||||
|
T = 19,
|
||||||
|
U = 20,
|
||||||
|
V = 21,
|
||||||
|
W = 22,
|
||||||
|
X = 23,
|
||||||
|
Y = 24,
|
||||||
|
Z = 25,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Letter {
|
||||||
|
pub fn all() -> Vec<Letter> {
|
||||||
|
use Letter::*;
|
||||||
|
|
||||||
|
vec![
|
||||||
|
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Letter {
|
||||||
|
type Output = Letter;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Letter::from((self as usize + rhs as usize) % 26)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Letter {
|
||||||
|
type Output = Letter;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
let sum = (self as isize - rhs as isize).rem_euclid(26);
|
||||||
|
Letter::from(sum as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Random for Letter {
|
||||||
|
fn random() -> Self {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let num = rng.gen_range(0..AMOUNT_OF_LETTERS);
|
||||||
|
Letter::from(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for Letter {
|
||||||
|
fn from(n: usize) -> Self {
|
||||||
|
let index = n % 26;
|
||||||
|
|
||||||
|
match index {
|
||||||
|
0 => Letter::A,
|
||||||
|
1 => Letter::B,
|
||||||
|
2 => Letter::C,
|
||||||
|
3 => Letter::D,
|
||||||
|
4 => Letter::E,
|
||||||
|
5 => Letter::F,
|
||||||
|
6 => Letter::G,
|
||||||
|
7 => Letter::H,
|
||||||
|
8 => Letter::I,
|
||||||
|
9 => Letter::J,
|
||||||
|
10 => Letter::K,
|
||||||
|
11 => Letter::L,
|
||||||
|
12 => Letter::M,
|
||||||
|
13 => Letter::N,
|
||||||
|
14 => Letter::O,
|
||||||
|
15 => Letter::P,
|
||||||
|
16 => Letter::Q,
|
||||||
|
17 => Letter::R,
|
||||||
|
18 => Letter::S,
|
||||||
|
19 => Letter::T,
|
||||||
|
20 => Letter::U,
|
||||||
|
21 => Letter::V,
|
||||||
|
22 => Letter::W,
|
||||||
|
23 => Letter::X,
|
||||||
|
24 => Letter::Y,
|
||||||
|
25 => Letter::Z,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<char> for Letter {
|
||||||
|
fn from(c: char) -> Self {
|
||||||
|
match c {
|
||||||
|
'A' => Letter::A,
|
||||||
|
'B' => Letter::B,
|
||||||
|
'C' => Letter::C,
|
||||||
|
'D' => Letter::D,
|
||||||
|
'E' => Letter::E,
|
||||||
|
'F' => Letter::F,
|
||||||
|
'G' => Letter::G,
|
||||||
|
'H' => Letter::H,
|
||||||
|
'I' => Letter::I,
|
||||||
|
'J' => Letter::J,
|
||||||
|
'K' => Letter::K,
|
||||||
|
'L' => Letter::L,
|
||||||
|
'M' => Letter::M,
|
||||||
|
'N' => Letter::N,
|
||||||
|
'O' => Letter::O,
|
||||||
|
'P' => Letter::P,
|
||||||
|
'Q' => Letter::Q,
|
||||||
|
'R' => Letter::R,
|
||||||
|
'S' => Letter::S,
|
||||||
|
'T' => Letter::T,
|
||||||
|
'U' => Letter::U,
|
||||||
|
'V' => Letter::V,
|
||||||
|
'W' => Letter::W,
|
||||||
|
'X' => Letter::X,
|
||||||
|
'Y' => Letter::Y,
|
||||||
|
'Z' => Letter::Z,
|
||||||
|
_ => panic!("Tried to parse unknown letter: '{}'", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Letter {
|
||||||
|
type Err = &'static str;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"A" => Ok(Letter::A),
|
||||||
|
"B" => Ok(Letter::B),
|
||||||
|
"C" => Ok(Letter::C),
|
||||||
|
"D" => Ok(Letter::D),
|
||||||
|
"E" => Ok(Letter::E),
|
||||||
|
"F" => Ok(Letter::F),
|
||||||
|
"G" => Ok(Letter::G),
|
||||||
|
"H" => Ok(Letter::H),
|
||||||
|
"I" => Ok(Letter::I),
|
||||||
|
"J" => Ok(Letter::J),
|
||||||
|
"K" => Ok(Letter::K),
|
||||||
|
"L" => Ok(Letter::L),
|
||||||
|
"M" => Ok(Letter::M),
|
||||||
|
"N" => Ok(Letter::N),
|
||||||
|
"O" => Ok(Letter::O),
|
||||||
|
"P" => Ok(Letter::P),
|
||||||
|
"Q" => Ok(Letter::Q),
|
||||||
|
"R" => Ok(Letter::R),
|
||||||
|
"S" => Ok(Letter::S),
|
||||||
|
"T" => Ok(Letter::T),
|
||||||
|
"U" => Ok(Letter::U),
|
||||||
|
"V" => Ok(Letter::V),
|
||||||
|
"W" => Ok(Letter::W),
|
||||||
|
"X" => Ok(Letter::X),
|
||||||
|
"Y" => Ok(Letter::Y),
|
||||||
|
"Z" => Ok(Letter::Z),
|
||||||
|
_ => Err("Failed to parse string as letter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Letter> for char {
|
||||||
|
fn from(letter: Letter) -> Self {
|
||||||
|
match letter {
|
||||||
|
Letter::A => 'A',
|
||||||
|
Letter::B => 'B',
|
||||||
|
Letter::C => 'C',
|
||||||
|
Letter::D => 'D',
|
||||||
|
Letter::E => 'E',
|
||||||
|
Letter::F => 'F',
|
||||||
|
Letter::G => 'G',
|
||||||
|
Letter::H => 'H',
|
||||||
|
Letter::I => 'I',
|
||||||
|
Letter::J => 'J',
|
||||||
|
Letter::K => 'K',
|
||||||
|
Letter::L => 'L',
|
||||||
|
Letter::M => 'M',
|
||||||
|
Letter::N => 'N',
|
||||||
|
Letter::O => 'O',
|
||||||
|
Letter::P => 'P',
|
||||||
|
Letter::Q => 'Q',
|
||||||
|
Letter::R => 'R',
|
||||||
|
Letter::S => 'S',
|
||||||
|
Letter::T => 'T',
|
||||||
|
Letter::U => 'U',
|
||||||
|
Letter::V => 'V',
|
||||||
|
Letter::W => 'W',
|
||||||
|
Letter::X => 'X',
|
||||||
|
Letter::Y => 'Y',
|
||||||
|
Letter::Z => 'Z',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
203
src/main.rs
Normal file
203
src/main.rs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
mod enigma;
|
||||||
|
mod letter;
|
||||||
|
mod plugboard;
|
||||||
|
mod random;
|
||||||
|
mod reflector;
|
||||||
|
mod rotor;
|
||||||
|
|
||||||
|
pub use enigma::*;
|
||||||
|
pub use letter::*;
|
||||||
|
pub use plugboard::*;
|
||||||
|
pub use random::*;
|
||||||
|
pub use reflector::*;
|
||||||
|
pub use rotor::*;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
|
let rotors = choose_rotors();
|
||||||
|
let plugboard = choose_plugboard();
|
||||||
|
let reflector = choose_reflector();
|
||||||
|
println!("Plugboard configuration: {}", plugboard);
|
||||||
|
let enigma_machine = Enigma::new(rotors, plugboard, reflector);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut enigma_machine = enigma_machine.clone();
|
||||||
|
|
||||||
|
println!("Input some text to encrypt:");
|
||||||
|
|
||||||
|
let input = clean_input(get_input_from_stdin());
|
||||||
|
let output = enigma_machine.encode(&input);
|
||||||
|
|
||||||
|
match output {
|
||||||
|
Ok(output) => println!("Input: {}\nOutput: {}", input, output),
|
||||||
|
Err(e) => println!("Wrong input: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_rotors() -> Vec<Rotor> {
|
||||||
|
let mut rotors = vec![];
|
||||||
|
|
||||||
|
while rotors.is_empty() {
|
||||||
|
rotors.clear();
|
||||||
|
println!("Available rotors:");
|
||||||
|
for rotor in Rotor::all() {
|
||||||
|
println!("\t{}", rotor.variant);
|
||||||
|
}
|
||||||
|
println!("Choose rotors (comma-separated, right to left):");
|
||||||
|
let input = clean_input(get_input_from_stdin());
|
||||||
|
|
||||||
|
for choice in input.split(',') {
|
||||||
|
let rotor = match choice {
|
||||||
|
"I" => Rotor::variant(RotorVariant::I),
|
||||||
|
"II" => Rotor::variant(RotorVariant::II),
|
||||||
|
"III" => Rotor::variant(RotorVariant::III),
|
||||||
|
"IV" => Rotor::variant(RotorVariant::IV),
|
||||||
|
"V" => Rotor::variant(RotorVariant::V),
|
||||||
|
"VI" => Rotor::variant(RotorVariant::VI),
|
||||||
|
"VII" => Rotor::variant(RotorVariant::VII),
|
||||||
|
"VIII" => Rotor::variant(RotorVariant::VIII),
|
||||||
|
"IDENTITY" => Rotor::variant(RotorVariant::Identity),
|
||||||
|
_ => {
|
||||||
|
println!("Invalid rotor choice: {}", choice);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rotors.push(rotor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rotors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_plugboard() -> Plugboard {
|
||||||
|
let mut plugboard = None;
|
||||||
|
|
||||||
|
while plugboard.is_none() {
|
||||||
|
println!("Available plugboards:");
|
||||||
|
println!("\tRandom");
|
||||||
|
println!("\tIdentity");
|
||||||
|
println!("Choose plugboard (only one):");
|
||||||
|
let input = clean_input(get_input_from_stdin());
|
||||||
|
|
||||||
|
plugboard = match &input[..] {
|
||||||
|
"IDENTITY" => Some(Plugboard::identity()),
|
||||||
|
"RANDOM" => Some(Plugboard::random()),
|
||||||
|
_ => {
|
||||||
|
println!("Invalid plugboard choice: {}", input);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
plugboard.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_reflector() -> Reflector {
|
||||||
|
let mut reflector = None;
|
||||||
|
|
||||||
|
while reflector.is_none() {
|
||||||
|
println!("Available reflectors:");
|
||||||
|
for reflector in Reflector::all() {
|
||||||
|
println!("\t{}", reflector.variant);
|
||||||
|
}
|
||||||
|
println!("Choose reflector (only one):");
|
||||||
|
let input = clean_input(get_input_from_stdin());
|
||||||
|
|
||||||
|
reflector = match &input[..] {
|
||||||
|
"A" => Some(Reflector::variant(ReflectorVariant::A)),
|
||||||
|
"B" => Some(Reflector::variant(ReflectorVariant::B)),
|
||||||
|
"C" => Some(Reflector::variant(ReflectorVariant::C)),
|
||||||
|
"BETA" => Some(Reflector::variant(ReflectorVariant::Beta)),
|
||||||
|
"GAMMA" => Some(Reflector::variant(ReflectorVariant::Gamma)),
|
||||||
|
"BTHIN" => Some(Reflector::variant(ReflectorVariant::BThin)),
|
||||||
|
"CTHIN" => Some(Reflector::variant(ReflectorVariant::CThin)),
|
||||||
|
"IDENTITY" => Some(Reflector::variant(ReflectorVariant::Identity)),
|
||||||
|
_ => {
|
||||||
|
println!("Invalid reflector choice: {}", input);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
reflector.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_input_from_stdin() -> String {
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean_input(input: String) -> String {
|
||||||
|
input
|
||||||
|
.trim()
|
||||||
|
.chars()
|
||||||
|
.filter(|c| c != &' ' && c != &'\r' && c != &'\n')
|
||||||
|
.collect::<String>()
|
||||||
|
.to_uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enigma_machine_encodes_correctly() {
|
||||||
|
let rotors = vec![
|
||||||
|
Rotor::variant(RotorVariant::I),
|
||||||
|
Rotor::variant(RotorVariant::II),
|
||||||
|
Rotor::variant(RotorVariant::III),
|
||||||
|
];
|
||||||
|
let plugboard = Plugboard::identity();
|
||||||
|
let reflector = Reflector::variant(ReflectorVariant::B);
|
||||||
|
let enigma_machine = Enigma::new(rotors, plugboard, reflector);
|
||||||
|
|
||||||
|
let mut em1 = enigma_machine.clone();
|
||||||
|
let mut em2 = enigma_machine.clone();
|
||||||
|
|
||||||
|
println!("Encoding string");
|
||||||
|
let result = em1
|
||||||
|
.encode("MAGNUSERPISSEDAARLIGTILLEAGUEOFLEGENDS")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result, "HTMPRVHFYZUHSANJWQCCAOHUTDRICUYUJJTHBE");
|
||||||
|
|
||||||
|
println!("Decoding string");
|
||||||
|
let result = em2
|
||||||
|
.encode("HTMPRVHFYZUHSANJWQCCAOHUTDRICUYUJJTHBE")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result, "MAGNUSERPISSEDAARLIGTILLEAGUEOFLEGENDS");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn random_plugboard_is_complete() {
|
||||||
|
let plugboard = Plugboard::random();
|
||||||
|
|
||||||
|
for letter in Letter::all() {
|
||||||
|
println!("Testing if '{:?}' is mapped in plugboard", letter);
|
||||||
|
plugboard.letter(letter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rotor_rotates_as_expected() {
|
||||||
|
let mut rotor = Rotor::variant(RotorVariant::Identity);
|
||||||
|
|
||||||
|
println!("{:?}", rotor.mappings);
|
||||||
|
assert_eq!(rotor.forwards(Letter::A), Letter::A);
|
||||||
|
rotor.rotate();
|
||||||
|
assert_eq!(rotor.forwards(Letter::A), Letter::A);
|
||||||
|
|
||||||
|
for _ in 0..26 {
|
||||||
|
rotor.rotate();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{:?}", rotor.mappings);
|
||||||
|
assert_eq!(rotor.forwards(Letter::A), Letter::A);
|
||||||
|
}
|
||||||
|
}
|
122
src/plugboard.rs
Normal file
122
src/plugboard.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
use crate::*;
|
||||||
|
use rand::prelude::*;
|
||||||
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
|
// MUST be less than total amount of letters divided by two
|
||||||
|
const CABLES: usize = 10;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Plugboard {
|
||||||
|
mappings: Vec<Letter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugboard {
|
||||||
|
pub fn new(mappings: Vec<Letter>) -> Self {
|
||||||
|
Plugboard { mappings }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identity() -> Self {
|
||||||
|
let mappings = vec![
|
||||||
|
Letter::A,
|
||||||
|
Letter::B,
|
||||||
|
Letter::C,
|
||||||
|
Letter::D,
|
||||||
|
Letter::E,
|
||||||
|
Letter::F,
|
||||||
|
Letter::G,
|
||||||
|
Letter::H,
|
||||||
|
Letter::I,
|
||||||
|
Letter::J,
|
||||||
|
Letter::K,
|
||||||
|
Letter::L,
|
||||||
|
Letter::M,
|
||||||
|
Letter::N,
|
||||||
|
Letter::O,
|
||||||
|
Letter::P,
|
||||||
|
Letter::Q,
|
||||||
|
Letter::R,
|
||||||
|
Letter::S,
|
||||||
|
Letter::T,
|
||||||
|
Letter::U,
|
||||||
|
Letter::V,
|
||||||
|
Letter::W,
|
||||||
|
Letter::X,
|
||||||
|
Letter::Y,
|
||||||
|
Letter::Z,
|
||||||
|
];
|
||||||
|
|
||||||
|
Plugboard::new(mappings)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self, index: usize) -> usize {
|
||||||
|
*self.mappings.get(index).expect(
|
||||||
|
&format!(
|
||||||
|
"Switchboard is missing mapping for a letter: {:?}",
|
||||||
|
Letter::from(index)
|
||||||
|
)
|
||||||
|
.to_string(),
|
||||||
|
) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn letter(&self, letter: Letter) -> Letter {
|
||||||
|
Letter::from(self.index(letter as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Plugboard {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let mut pairs = HashMap::new();
|
||||||
|
|
||||||
|
for (i, mapping) in self.mappings.iter().enumerate() {
|
||||||
|
let left_letter = Letter::from(i);
|
||||||
|
let right_letter = *mapping;
|
||||||
|
|
||||||
|
if left_letter != right_letter
|
||||||
|
&& !pairs.get(&left_letter).is_some()
|
||||||
|
&& !pairs.get(&right_letter).is_some()
|
||||||
|
{
|
||||||
|
pairs.insert(left_letter, right_letter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
if pairs.is_empty() {
|
||||||
|
s = "Identity".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (left_letter, right_letter) in pairs.iter() {
|
||||||
|
s = format!("{:?}{:?} {}", left_letter, right_letter, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.write_str(&s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Random for Plugboard {
|
||||||
|
fn random() -> Self {
|
||||||
|
let mut mappings = vec![Letter::A; AMOUNT_OF_LETTERS];
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let mut input = Letter::all();
|
||||||
|
input.shuffle(&mut rng);
|
||||||
|
input.truncate(CABLES);
|
||||||
|
|
||||||
|
let mut output: Vec<Letter> = Letter::all()
|
||||||
|
.iter()
|
||||||
|
.filter(|l| !input.contains(l))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let remaining = output.split_off(CABLES);
|
||||||
|
|
||||||
|
for (input, output) in input.iter().zip(output.iter()) {
|
||||||
|
mappings.insert(*input as usize, *output);
|
||||||
|
mappings.insert(*output as usize, *input);
|
||||||
|
}
|
||||||
|
|
||||||
|
for letter in remaining {
|
||||||
|
mappings.insert(letter as usize, letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugboard::new(mappings)
|
||||||
|
}
|
||||||
|
}
|
3
src/random.rs
Normal file
3
src/random.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub trait Random {
|
||||||
|
fn random() -> Self;
|
||||||
|
}
|
96
src/reflector.rs
Normal file
96
src/reflector.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use crate::*;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Reflector {
|
||||||
|
pub variant: ReflectorVariant,
|
||||||
|
mappings: Vec<Letter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reflector {
|
||||||
|
pub fn variant(variant: ReflectorVariant) -> Self {
|
||||||
|
let mappings = match variant {
|
||||||
|
ReflectorVariant::Identity => mappings(VARIANT_IDENTITY),
|
||||||
|
ReflectorVariant::Beta => mappings(VARIANT_BETA),
|
||||||
|
ReflectorVariant::Gamma => mappings(VARIANT_GAMMA),
|
||||||
|
ReflectorVariant::A => mappings(VARIANT_A),
|
||||||
|
ReflectorVariant::B => mappings(VARIANT_B),
|
||||||
|
ReflectorVariant::C => mappings(VARIANT_C),
|
||||||
|
ReflectorVariant::BThin => mappings(VARIANT_B_THIN),
|
||||||
|
ReflectorVariant::CThin => mappings(VARIANT_C_THIN),
|
||||||
|
};
|
||||||
|
|
||||||
|
Reflector { variant, mappings }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all() -> Vec<Reflector> {
|
||||||
|
vec![
|
||||||
|
Reflector::variant(ReflectorVariant::A),
|
||||||
|
Reflector::variant(ReflectorVariant::B),
|
||||||
|
Reflector::variant(ReflectorVariant::C),
|
||||||
|
Reflector::variant(ReflectorVariant::Beta),
|
||||||
|
Reflector::variant(ReflectorVariant::Gamma),
|
||||||
|
Reflector::variant(ReflectorVariant::BThin),
|
||||||
|
Reflector::variant(ReflectorVariant::CThin),
|
||||||
|
Reflector::variant(ReflectorVariant::Identity),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self, letter: usize) -> usize {
|
||||||
|
*self
|
||||||
|
.mappings
|
||||||
|
.get(letter)
|
||||||
|
.expect(&format!("Reflector is missing mapping for a letter: {:?}", letter).to_string())
|
||||||
|
as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn letter(&self, letter: Letter) -> Letter {
|
||||||
|
Letter::from(self.index(letter as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VARIANT_IDENTITY: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
const VARIANT_BETA: &'static str = "LEYJVCNIXWPBQMDRTAKZGFUHOS";
|
||||||
|
const VARIANT_GAMMA: &'static str = "FSOKANUERHMBTIYCWLQPZXVGJD";
|
||||||
|
const VARIANT_A: &'static str = "EJMZALYXVBWFCRQUONTSPIKHGD";
|
||||||
|
const VARIANT_B: &'static str = "YRUHQSLDPXNGOKMIEBFZCWVJAT";
|
||||||
|
const VARIANT_C: &'static str = "FVPJIAOYEDRZXWGCTKUQSBNMHL";
|
||||||
|
const VARIANT_B_THIN: &'static str = "ENKQAUYWJICOPBLMDXZVFTHRGS";
|
||||||
|
const VARIANT_C_THIN: &'static str = "RDOBJNTKVEHMLFCWZAXGYIPSUQ";
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum ReflectorVariant {
|
||||||
|
Identity,
|
||||||
|
Beta,
|
||||||
|
Gamma,
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
BThin,
|
||||||
|
CThin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ReflectorVariant {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ReflectorVariant::Identity => f.write_str("Identity"),
|
||||||
|
ReflectorVariant::Beta => f.write_str("Beta"),
|
||||||
|
ReflectorVariant::Gamma => f.write_str("Gamma"),
|
||||||
|
ReflectorVariant::A => f.write_str("A"),
|
||||||
|
ReflectorVariant::B => f.write_str("B"),
|
||||||
|
ReflectorVariant::C => f.write_str("C"),
|
||||||
|
ReflectorVariant::BThin => f.write_str("B Thin"),
|
||||||
|
ReflectorVariant::CThin => f.write_str("C Thin"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mappings(input: &'static str) -> Vec<Letter> {
|
||||||
|
let mut mappings = vec![];
|
||||||
|
|
||||||
|
for c in input.chars() {
|
||||||
|
mappings.push(Letter::from(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings
|
||||||
|
}
|
131
src/rotor.rs
Normal file
131
src/rotor.rs
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
use crate::*;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Rotor {
|
||||||
|
pub variant: RotorVariant,
|
||||||
|
pub mappings: Vec<Letter>,
|
||||||
|
pub position: Letter,
|
||||||
|
pub notches: Vec<Letter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rotor {
|
||||||
|
pub fn variant(variant: RotorVariant) -> Self {
|
||||||
|
let (mappings, notches) = match variant {
|
||||||
|
RotorVariant::Identity => (mappings(IDENTITY), vec![Letter::Z]),
|
||||||
|
RotorVariant::I => (mappings(VARIANT_I), vec![Letter::Q]),
|
||||||
|
RotorVariant::II => (mappings(VARIANT_II), vec![Letter::E]),
|
||||||
|
RotorVariant::III => (mappings(VARIANT_III), vec![Letter::V]),
|
||||||
|
RotorVariant::IV => (mappings(VARIANT_IV), vec![Letter::J]),
|
||||||
|
RotorVariant::V => (mappings(VARIANT_V), vec![Letter::Z]),
|
||||||
|
RotorVariant::VI => (mappings(VARIANT_VI), vec![Letter::Z, Letter::M]),
|
||||||
|
RotorVariant::VII => (mappings(VARIANT_VII), vec![Letter::Z, Letter::M]),
|
||||||
|
RotorVariant::VIII => (mappings(VARIANT_VIII), vec![Letter::Z, Letter::M]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let position = Letter::A;
|
||||||
|
|
||||||
|
Rotor {
|
||||||
|
variant,
|
||||||
|
mappings,
|
||||||
|
position,
|
||||||
|
notches,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all() -> Vec<Rotor> {
|
||||||
|
vec![
|
||||||
|
Rotor::variant(RotorVariant::I),
|
||||||
|
Rotor::variant(RotorVariant::II),
|
||||||
|
Rotor::variant(RotorVariant::III),
|
||||||
|
Rotor::variant(RotorVariant::IV),
|
||||||
|
Rotor::variant(RotorVariant::V),
|
||||||
|
Rotor::variant(RotorVariant::VI),
|
||||||
|
Rotor::variant(RotorVariant::VII),
|
||||||
|
Rotor::variant(RotorVariant::VIII),
|
||||||
|
Rotor::variant(RotorVariant::Identity),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_position(&mut self, position: Letter) {
|
||||||
|
self.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forwards(&self, letter: Letter) -> Letter {
|
||||||
|
*self
|
||||||
|
.mappings
|
||||||
|
.get((letter + self.position) as usize)
|
||||||
|
.expect(&format!("Rotor is missing mapping for a letter: {:?}", letter).to_string())
|
||||||
|
- self.position
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backwards(&self, letter: Letter) -> Letter {
|
||||||
|
Letter::from(
|
||||||
|
self.mappings
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, l)| (**l - self.position) == letter)
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.0,
|
||||||
|
) - self.position
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at_notch(&self) -> bool {
|
||||||
|
self.notches.contains(&self.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate(&mut self) {
|
||||||
|
self.position = Letter::from((self.position as usize + 1) % AMOUNT_OF_LETTERS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const IDENTITY: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
const VARIANT_I: &'static str = "EKMFLGDQVZNTOWYHXUSPAIBRCJ";
|
||||||
|
const VARIANT_II: &'static str = "AJDKSIRUXBLHWTMCQGZNPYFVOE";
|
||||||
|
const VARIANT_III: &'static str = "BDFHJLCPRTXVZNYEIWGAKMUSQO";
|
||||||
|
const VARIANT_IV: &'static str = "ESOVPZJAYQUIRHXLNFTGKDCMWB";
|
||||||
|
const VARIANT_V: &'static str = "VZBRGITYUPSDNHLXAWMJQOFECK";
|
||||||
|
const VARIANT_VI: &'static str = "JPGVOUMFYQBENHZRDKASXLICTW";
|
||||||
|
const VARIANT_VII: &'static str = "NZJHGRCXMYSWBOUFAIVLPEKQDT";
|
||||||
|
const VARIANT_VIII: &'static str = "FKQHTLXOCBJSPDZRAMEWNIUYGV";
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum RotorVariant {
|
||||||
|
Identity,
|
||||||
|
I,
|
||||||
|
II,
|
||||||
|
III,
|
||||||
|
IV,
|
||||||
|
V,
|
||||||
|
VI,
|
||||||
|
VII,
|
||||||
|
VIII,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RotorVariant {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
RotorVariant::Identity => f.write_str("Identity"),
|
||||||
|
RotorVariant::I => f.write_str("I"),
|
||||||
|
RotorVariant::II => f.write_str("II"),
|
||||||
|
RotorVariant::III => f.write_str("III"),
|
||||||
|
RotorVariant::IV => f.write_str("IV"),
|
||||||
|
RotorVariant::V => f.write_str("V"),
|
||||||
|
RotorVariant::VI => f.write_str("VI"),
|
||||||
|
RotorVariant::VII => f.write_str("VII"),
|
||||||
|
RotorVariant::VIII => f.write_str("VIII"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mappings(input: &'static str) -> Vec<Letter> {
|
||||||
|
let mut mappings = vec![];
|
||||||
|
|
||||||
|
for c in input.chars() {
|
||||||
|
mappings.push(Letter::from(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings
|
||||||
|
}
|
Loading…
Reference in a new issue