use std::{convert::Infallible, fmt::Debug, marker::PhantomData};
use bon::Builder;
use saltine_core::{
Cipher,
CipherSolution,
CipherSolver,
alphabet::Alphabet,
analyze::TextLike,
character::Character,
language::{English, Language},
};
#[derive(Builder)]
pub struct CaeserCipher<C: Character = char> {
#[builder(name = with_alphabet)]
alphabet: Alphabet<C>,
#[builder(name = with_shift)]
shift: usize,
}
impl CaeserCipher<char> {
pub fn new(shift: usize) -> Self {
CaeserCipher {
alphabet: English::alphabet(),
shift,
}
}
}
impl<C: Character> Cipher<C> for CaeserCipher<C> {
type DecryptionError = Infallible;
fn encrypt<T: TextLike<C> + ?Sized>(&self, plaintext: &T) -> impl TextLike<C> {
plaintext.characters().map(|letter| self.alphabet.shift_char(letter, self.shift)).collect::<Vec<_>>()
}
fn decrypt<T: TextLike<C> + ?Sized>(&self, ciphertext: &T) -> Result<impl TextLike<C>, Self::DecryptionError> {
Ok(ciphertext
.characters()
.map(|letter| self.alphabet.shift_char(letter, self.alphabet.len() - self.shift))
.collect::<Vec<_>>())
}
}
#[derive(Builder, Debug)]
pub struct CaeserCipherSolver<C: Character, L: Language<Character = C>> {
#[builder(name = with_alphabet)]
alphabet: Alphabet<C>,
_phantom: PhantomData<L>,
}
impl CaeserCipherSolver<char, English> {
pub fn new() -> CaeserCipherSolver<char, English> {
CaeserCipherSolver::<char, English> {
alphabet: English::alphabet(),
_phantom: PhantomData,
}
}
}
impl<L: Language> CipherSolver<L> for CaeserCipherSolver<L::Character, L> {
type Solution = CaeserCipherSolution<L::Character>;
fn crack<T: TextLike<L::Character> + ?Sized>(&self, ciphertext: &T) -> CipherSolution<CaeserCipherSolution<L::Character>> {
(0..self.alphabet.len())
.into_iter()
.map(|shift| {
let solver = CaeserCipher::builder().with_alphabet(self.alphabet.clone()).with_shift(shift).build();
let plaintext = solver.decrypt(ciphertext).unwrap();
let score = plaintext.chance_of_being::<L>();
CipherSolution::<L::Character>::new::<CaeserCipherSolution<L::Character>>(
CaeserCipherSolution {
plaintext: plaintext.to::<Vec<_>>(),
shift,
},
score,
)
})
.max_by(|s1, s2| s1.score().partial_cmp(&s2.score()).unwrap_or(std::cmp::Ordering::Equal))
.unwrap()
}
}
#[derive(Debug)]
pub struct CaeserCipherSolution<C> {
plaintext: Vec<C>,
shift: usize,
}
impl<C: Character> CaeserCipherSolution<C> {
pub fn plaintext(&self) -> &impl TextLike<C> {
&self.plaintext
}
pub fn shift(&self) -> usize {
self.shift
}
}