use std::{
convert::{TryFrom, TryInto},
fmt::Display,
};
use thiserror::Error;
pub trait AlphabetCharacter: Into<u8> + Into<char> + TryFrom<u8> + TryFrom<char> + Display {
const ALPHABET_SIZE: u8;
fn index(&self) -> u8;
fn from_index(index: u8) -> Result<Self, AlphabetError>;
fn from_index_ref(index: u8) -> Result<&'static Self, AlphabetError>;
fn complement(&self) -> Self;
}
pub trait Alphabet: Sized {
const SIZE: u8 = Self::CharacterType::ALPHABET_SIZE;
type CharacterType: AlphabetCharacter + Eq + Ord + Clone + 'static;
fn ascii_to_character(ascii: u8) -> Result<Self::CharacterType, AlphabetError> {
ascii
.try_into()
.map_err(|_| AlphabetError::AsciiNotPartOfAlphabet {
ascii: ascii.into(),
})
}
fn character_to_ascii(character: Self::CharacterType) -> u8 {
character.into()
}
fn iter() -> impl Iterator<Item = Self::CharacterType> {
(0..Self::SIZE).map(|index| Self::CharacterType::from_index(index).unwrap())
}
}
#[derive(Debug, Clone, Eq, PartialEq, Error)]
pub enum AlphabetError {
#[error("found an ASCII character that is not part of the alphabet: {ascii:}")]
AsciiNotPartOfAlphabet {
ascii: char,
},
#[error("found an index that is not part of the alphabet: {index}")]
IndexNotPartOfAlphabet {
index: u8,
},
}
#[cfg(feature = "rand")]
pub mod rand {
use std::marker::PhantomData;
use rand::{distr::Uniform, prelude::Distribution};
use super::{Alphabet, AlphabetCharacter};
pub struct UniformAlphabetDistribution<AlphabetType: Alphabet> {
phantom_data: PhantomData<AlphabetType>,
}
impl<AlphabetType: Alphabet> Distribution<AlphabetType::CharacterType>
for UniformAlphabetDistribution<AlphabetType>
{
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> AlphabetType::CharacterType {
let index_distribution = Uniform::new(0, AlphabetType::SIZE).unwrap();
let index = index_distribution.sample(rng);
AlphabetType::CharacterType::from_index(index).unwrap()
}
}
impl<AlphabetType: Alphabet> Default for UniformAlphabetDistribution<AlphabetType> {
fn default() -> Self {
Self {
phantom_data: Default::default(),
}
}
}
}