use core::mem::MaybeUninit;
use super::utils::{Letter, DEFAULT_ROTOR_NUM};
use super::Rotor;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Wheel<const ROTOR_NUM: usize = DEFAULT_ROTOR_NUM> {
walzenlage: [Rotor; ROTOR_NUM],
double_step: bool,
}
impl<const ROTOR_NUM: usize> Default for Wheel<ROTOR_NUM> {
fn default() -> Self {
Wheel {
walzenlage: [Rotor::default(); ROTOR_NUM],
double_step: false,
}
}
}
impl<const ROTOR_NUM: usize> Wheel<ROTOR_NUM> {
#[must_use]
pub fn new(rotors: [Rotor; ROTOR_NUM]) -> Self {
Wheel {
walzenlage: rotors,
double_step: false,
}
}
pub fn forward(&mut self, letter: Letter) -> Letter {
self.walzenlage
.iter()
.rev()
.fold(letter, |a, e| e.wiring_map(a))
}
fn simple_step(&mut self, from: usize) {
self.walzenlage.iter_mut().take(from).rev().all(Rotor::step);
}
pub fn step(&mut self) {
self.simple_step(ROTOR_NUM);
if self.double_step {
self.double_step = false;
self.simple_step(ROTOR_NUM - 1);
} else {
let mut idx = 1;
while idx < ROTOR_NUM - 1 && !self.walzenlage[idx].will_turn() {
idx += 1;
}
if idx < ROTOR_NUM - 1 {
self.double_step = true;
}
}
}
#[must_use]
pub fn backward(&self, letter: Letter) -> Letter {
self.walzenlage
.iter()
.fold(letter, |a, e| e.rev_wiring_map(a))
}
pub fn set_positions(&mut self, positions: [Letter; ROTOR_NUM]) {
positions
.iter()
.zip(self.walzenlage.iter_mut())
.for_each(|(pos, rotor)| rotor.set_grundstellung(*pos));
}
pub fn set_ring_wiring_offset(&mut self, positions: [u8; ROTOR_NUM]) {
positions
.iter()
.zip(self.walzenlage.iter_mut())
.for_each(|(pos, rotor)| rotor.set_ringstellung(*pos));
}
#[must_use]
pub fn get_positions(&self) -> [Letter; ROTOR_NUM] {
let mut ret: [MaybeUninit<Letter>; ROTOR_NUM] =
unsafe { MaybeUninit::uninit().assume_init() };
self.walzenlage
.iter()
.enumerate()
.for_each(|(i, x)| ret[i] = MaybeUninit::new(x.get_grundstellung()));
unsafe { core::mem::transmute_copy::<_, [Letter; ROTOR_NUM]>(&ret) }
}
}
#[cfg(test)]
mod tests {
use crate::components::{ROTOR_I, ROTOR_II, ROTOR_III};
use super::*;
#[test]
fn test_single_step() {
let mut wheel = Wheel::new([ROTOR_I, ROTOR_II, ROTOR_III]);
wheel.set_positions([Letter::A, Letter::A, Letter::U]);
assert_eq!([Letter::A, Letter::A, Letter::U], wheel.get_positions());
wheel.step();
assert_eq!([Letter::A, Letter::A, Letter::V], wheel.get_positions());
wheel.step();
assert_eq!([Letter::A, Letter::B, Letter::W], wheel.get_positions());
wheel.step();
assert_eq!([Letter::A, Letter::B, Letter::X], wheel.get_positions());
}
#[test]
fn test_double_step() {
let mut wheel = Wheel::new([ROTOR_I, ROTOR_II, ROTOR_III]);
wheel.set_positions([Letter::A, Letter::D, Letter::U]);
assert_eq!([Letter::A, Letter::D, Letter::U], wheel.get_positions());
wheel.step();
assert_eq!([Letter::A, Letter::D, Letter::V], wheel.get_positions());
wheel.step();
assert_eq!([Letter::A, Letter::E, Letter::W], wheel.get_positions());
wheel.step();
assert_eq!([Letter::B, Letter::F, Letter::X], wheel.get_positions());
wheel.step();
assert_eq!([Letter::B, Letter::F, Letter::Y], wheel.get_positions());
}
}