use std::fmt;
use super::{CharIndex, ToChar};
use constants::{ROTORS, NOTCHES};
#[derive(Clone, Debug)]
pub struct Rotor {
mapping: Vec<char>,
inverse: Vec<char>,
notches: Vec<usize>,
pub offset: usize,
pub key_setting: usize,
pub ring_setting: usize,
}
impl Rotor {
pub fn new(mapping: &str, notches: &str, key: char, ring: char) -> Rotor {
let mapping: Vec<char> = mapping.chars().collect();
if mapping.len() != 26 {
panic!("Rotor mappings must be 26 characters long.");
}
let mut inverse = vec!['A'; 26];
for (i, &c) in mapping.iter().enumerate() {
inverse[c.index() % 26] = i.to_char();
}
Rotor {
mapping: mapping,
inverse: inverse,
notches: notches.chars().map(|c| c.index()).collect(),
offset: key.index(),
key_setting: key.index(),
ring_setting: ring.index(),
}
}
pub fn from_enigma(num: usize, key: char, ring: char) -> Rotor {
Rotor::new(ROTORS[num - 1], NOTCHES[num - 1], key, ring)
}
fn map(&self, c: char, mapping: &[char]) -> char {
let offset = 26 + self.offset - self.ring_setting;
let index = mapping[(c.index() + offset) % 26].index();
(52 + index - offset).to_char()
}
pub fn substitute(&self, c: char) -> char {
self.map(c, &self.mapping)
}
pub fn invert(&self, c: char) -> char {
self.map(c, &self.inverse)
}
pub fn advance(&mut self) {
self.offset = (self.offset + 1) % 26;
}
pub fn at_notch(&self) -> bool {
self.notches.iter().any(|&n| n == self.offset)
}
pub fn reset(&mut self) {
self.offset = self.key_setting;
}
}
impl fmt::Display for Rotor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, &rotor) in ROTORS.iter().enumerate() {
if self.mapping.iter().collect::<String>() == rotor {
return write!(f, "{}", i + 1)
}
}
write!(f, "?")
}
}
#[cfg(test)]
mod tests {
use super::Rotor;
#[test]
fn char_substitution() {
let rotor = Rotor::from_enigma(1, 'A', 'A');
assert_eq!(rotor.substitute('A'), 'E');
assert_eq!(rotor.substitute('B'), 'K');
}
#[test]
fn step_rotor() {
let mut rotor = Rotor::from_enigma(1, 'A', 'A');
assert_eq!(rotor.substitute('A'), 'E');
rotor.advance();
assert_eq!(rotor.offset, 1);
assert_eq!(rotor.substitute('A'), 'J');
rotor.advance();
assert_eq!(rotor.offset, 2);
assert_eq!(rotor.substitute('A'), 'K');
}
#[test]
fn step_inverse() {
let mut rotor = Rotor::from_enigma(1, 'A', 'A');
assert_eq!(rotor.invert('E'), 'A');
rotor.advance();
assert_eq!(rotor.invert('K'), 'D');
rotor.advance();
assert_eq!(rotor.invert('M'), 'K');
}
#[test]
fn key_setting() {
let rotor = Rotor::from_enigma(1, 'D', 'A');
assert_eq!(rotor.offset, 3)
}
#[test]
fn ring_setting() {
let rotor = Rotor::from_enigma(1, 'A', 'B');
assert_eq!(rotor.substitute('A'), 'K');
}
#[test]
fn reset_rotor() {
let mut rotor = Rotor::from_enigma(1, 'A', 'A');
assert_eq!(rotor.offset, 0);
for _ in 0..10 {
rotor.advance();
}
assert_eq!(rotor.offset, 10);
rotor.reset();
assert_eq!(rotor.offset, 0);
}
#[test]
fn inverse_mapping() {
let rotor = Rotor::from_enigma(1, 'A', 'A');
let inverse: String = rotor.inverse.into_iter().collect();
assert_eq!(&inverse, "UWYGADFPVZBECKMTHXSLRINQOJ");
}
#[test]
fn matching_inverses() {
let mut rotor = Rotor::from_enigma(1, 'A', 'A');
for i in b'A'..(b'Z' + 1) {
let c = i as char;
assert_eq!(c, rotor.invert(rotor.substitute(c)));
rotor.advance();
}
}
#[test]
#[should_panic]
fn invalid_rotor() {
Rotor::new("ABC", "A", 'A', 'A');
}
}