use crate::{input, Cipher, CipherResult};
static RELATIVE_PRIMES: [i32; 12] = [1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25];
pub struct Affine {
a: i32,
b: i32,
}
impl Affine {
pub fn new(a: i32, b: i32) -> Self {
assert!(0 < a && a < 26, "`a` must be in the range [1, 26)");
assert!(0 <= b && b < 26, "`b` must be in the range [0, 26)");
assert!(
RELATIVE_PRIMES.contains(&a),
"`a` must be relatively prime to 26"
);
Self { a, b }
}
}
impl Cipher for Affine {
fn encipher(&self, ptext: &str) -> CipherResult {
input::is_alpha(ptext)?;
let ptext = ptext.to_ascii_uppercase();
let ctext = ptext
.bytes()
.map(move |c| ((self.a * (c as i32 - 65) + self.b) % 26) as u8 + 65)
.collect();
Ok(String::from_utf8(ctext).unwrap())
}
fn decipher(&self, ctext: &str) -> CipherResult {
input::is_alpha(ctext)?;
let ctext = ctext.to_ascii_uppercase();
let a_inv = invmod(self.a, 26).unwrap();
let ptext = ctext
.bytes()
.map(move |c| (((a_inv * (c as i32 - 65 - self.b)) % 26 + 26) % 26) as u8 + 65)
.collect();
Ok(String::from_utf8(ptext).unwrap())
}
}
fn egcd(a: i32, b: i32) -> (i32, i32, i32) {
match a {
0 => (b, 0, 1),
_ => {
let (g, x, y) = egcd(b % a, a);
(g, y - (b / a) * x, x)
}
}
}
fn invmod(a: i32, m: i32) -> Option<i32> {
let (g, x, _) = egcd(a, m);
match g {
1 => Some((x % m + m) % m),
_ => None,
}
}