use crate::{
component::EnigmaComponent,
error::{EnigmaError, EnigmaResult},
state::EnigmaState,
};
#[derive(Debug, Clone)]
pub struct Rotor {
forward: [u8; 256],
backward: [u8; 256],
index: usize,
}
fn lcg_next(state: &mut u32) -> u32 {
*state = state.wrapping_mul(1664525).wrapping_add(1013904223);
*state
}
impl Rotor {
pub fn new(permutation: [u8; 256], index: usize) -> EnigmaResult<Self> {
let mut backward = [0u8; 256];
let mut seen = [false; 256];
for (i, &v) in permutation.iter().enumerate() {
let j = v as usize;
if seen[j] {
return Err(EnigmaError::InvalidConfiguration(
"rotor permutation must be bijective".into(),
));
}
seen[j] = true;
backward[j] = i as u8;
}
Ok(Self {
forward: permutation,
backward,
index,
})
}
pub fn identity(index: usize) -> Self {
let mut perm = [0u8; 256];
for (i, v) in perm.iter_mut().enumerate() {
*v = i as u8;
}
Self {
forward: perm,
backward: perm,
index,
}
}
fn position(&self, state: &EnigmaState) -> Result<u32, EnigmaError> {
state
.rotor_positions
.get(self.index)
.copied()
.ok_or_else(|| {
EnigmaError::InvalidState(format!("rotor index {} out of bounds", self.index))
})
}
pub fn shifted(index: usize, shift: u8) -> Self {
let mut forward = [0u8; 256];
let mut backward = [0u8; 256];
for (i, v) in forward.iter_mut().enumerate() {
let shifted = (i as u8).wrapping_add(shift);
*v = shifted;
backward[shifted as usize] = i as u8;
}
Self {
forward,
backward,
index,
}
}
pub fn from_seed(index: usize, seed: u64) -> Self {
let mut forward = [0u8; 256];
let mut backward = [0u8; 256];
for (i, v) in forward.iter_mut().enumerate() {
*v = i as u8;
}
let mut rng = (seed as u32).wrapping_add((index as u32).wrapping_mul(0x9E3779B9));
for i in (1..256).rev() {
let j = (lcg_next(&mut rng) % (i as u32 + 1)) as usize;
forward.swap(i, j);
}
for (i, &v) in forward.iter().enumerate() {
backward[v as usize] = i as u8;
}
Self {
forward,
backward,
index,
}
}
}
impl EnigmaComponent for Rotor {
fn forward(&self, input: u8, state: &EnigmaState) -> u8 {
let pos = self.position(state).unwrap_or(0);
let shifted = input.wrapping_add(pos as u8);
let mapped = self.forward[shifted as usize];
mapped.wrapping_sub(pos as u8)
}
fn backward(&self, input: u8, state: &EnigmaState) -> u8 {
let pos = self.position(state).unwrap_or(0);
let shifted = input.wrapping_add(pos as u8);
let mapped = self.backward[shifted as usize];
mapped.wrapping_sub(pos as u8)
}
}