use crate::utils::UNIT_LETTER_MAP;
use super::utils::{string_to_inv_letter_map, string_to_letter_map, Letter, LetterMapping};
const MAX_NOTCHES: usize = 2;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Rotor {
ringstellung: u8,
step: Letter,
wiring: LetterMapping,
reverse_wiring: LetterMapping,
notches: [Letter; MAX_NOTCHES],
fixed: bool,
}
impl Default for Rotor {
fn default() -> Self {
Rotor {
ringstellung: u8::default(),
step: Letter::default(),
wiring: UNIT_LETTER_MAP,
reverse_wiring: UNIT_LETTER_MAP,
notches: [Letter::default(); MAX_NOTCHES],
fixed: false,
}
}
}
impl Rotor {
#[must_use]
pub const fn from_static_str(
string: &'static str,
notches: [Letter; MAX_NOTCHES],
fixed: bool,
) -> Rotor {
Rotor {
wiring: string_to_letter_map(string),
reverse_wiring: string_to_inv_letter_map(string),
step: Letter::A,
ringstellung: 0,
notches,
fixed,
}
}
pub fn set_grundstellung(&mut self, letter: Letter) {
self.step = letter;
}
pub fn set_ringstellung(&mut self, ringstellung: u8) {
assert!(ringstellung > 0);
self.ringstellung = ringstellung - 1;
}
#[must_use]
pub fn wiring_map(&self, index: Letter) -> Letter {
self.wiring[index - self.ringstellung + self.step] + self.ringstellung - self.step
}
#[must_use]
pub fn rev_wiring_map(&self, index: Letter) -> Letter {
self.reverse_wiring[index - self.ringstellung + self.step] + self.ringstellung - self.step
}
pub fn step(&mut self) -> bool {
if self.fixed {
false
} else {
self.step += 1;
self.notches.iter().any(|&x| x == self.step)
}
}
#[must_use]
pub fn get_grundstellung(&self) -> Letter {
self.step
}
#[must_use]
pub fn will_turn(&self) -> bool {
self.notches.iter().any(|&x| x == self.step + 1)
}
}
#[allow(missing_docs)]
pub const ROTOR_I: Rotor =
Rotor::from_static_str("EKMFLGDQVZNTOWYHXUSPAIBRCJ", [Letter::R, Letter::R], false);
#[allow(missing_docs)]
pub const ROTOR_II: Rotor =
Rotor::from_static_str("AJDKSIRUXBLHWTMCQGZNPYFVOE", [Letter::F, Letter::F], false);
#[allow(missing_docs)]
pub const ROTOR_III: Rotor =
Rotor::from_static_str("BDFHJLCPRTXVZNYEIWGAKMUSQO", [Letter::W, Letter::W], false);
#[allow(missing_docs)]
pub const ROTOR_IV: Rotor =
Rotor::from_static_str("ESOVPZJAYQUIRHXLNFTGKDCMWB", [Letter::K, Letter::K], false);
#[allow(missing_docs)]
pub const ROTOR_V: Rotor =
Rotor::from_static_str("VZBRGITYUPSDNHLXAWMJQOFECK", [Letter::A, Letter::A], false);
#[allow(missing_docs)]
pub const ROTOR_VI: Rotor =
Rotor::from_static_str("JPGVOUMFYQBENHZRDKASXLICTW", [Letter::A, Letter::N], false);
#[allow(missing_docs)]
pub const ROTOR_VII: Rotor =
Rotor::from_static_str("NZJHGRCXMYSWBOUFAIVLPEKQDT", [Letter::A, Letter::N], false);
#[allow(missing_docs)]
pub const ROTOR_VIII: Rotor =
Rotor::from_static_str("FKQHTLXOCBJSPDZRAMEWNIUYGV", [Letter::A, Letter::N], false);
#[allow(missing_docs)]
pub const ROTOR_BETA: Rotor =
Rotor::from_static_str("LEYJVCNIXWPBQMDRTAKZGFUHOS", [Letter::A, Letter::A], true);
#[allow(missing_docs)]
pub const ROTOR_GAMMA: Rotor =
Rotor::from_static_str("FSOKANUERHMBTIYCWLQPZXVGJD", [Letter::A, Letter::A], true);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_at() {
let rotor =
Rotor::from_static_str("ZYXWVUTSRQPONMLKJIHGFEDCBA", [Letter::A, Letter::A], false);
assert_eq!(rotor.wiring_map(Letter::A), Letter::Z);
assert_eq!(rotor.wiring_map(Letter::C), Letter::X);
assert_eq!(rotor.wiring_map(Letter::Z), Letter::A);
}
#[test]
fn test_offset() {
let mut rotor = ROTOR_I;
rotor.set_grundstellung(Letter::B);
assert_eq!(rotor.wiring_map(Letter::A), Letter::J)
}
#[test]
fn test_ringstellung() {
let mut rotor = ROTOR_I;
rotor.set_grundstellung(Letter::A);
rotor.set_ringstellung(2);
assert_eq!(rotor.wiring_map(Letter::A), Letter::K)
}
#[test]
fn test_reverse() {
let rotor =
Rotor::from_static_str("NMLKJIHGFEDCBAZYXWVUTSRQPO", [Letter::A, Letter::A], false);
let forward = rotor.wiring_map(Letter::C);
assert_eq!(rotor.rev_wiring_map(forward), Letter::C);
}
#[test]
fn test_step() {
let mut rotor =
Rotor::from_static_str("NMLKJIHGFEDCBAZYXWVUTSRQPO", [Letter::C, Letter::C], false);
assert!(!rotor.step());
assert!(rotor.step());
assert!(!rotor.step());
}
#[test]
fn test_get_pos() {
let mut rotor =
Rotor::from_static_str("NMLKJIHGFEDCBAZYXWVUTSRQPO", [Letter::C, Letter::C], false);
rotor.set_grundstellung(Letter::G);
assert_eq!(Letter::G, rotor.get_grundstellung());
}
}