use std::collections::BTreeSet;
use std::fmt;
use crate::Base;
use crate::name::Name;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Character {
Name(Name),
Placeholder,
Zero,
One,
}
impl Character {
pub fn from_char(c: char) -> Result<Self, String> {
Ok(match c {
'.' => Self::Placeholder,
'0' => Self::Zero,
'1' => Self::One,
_ => Self::Name(Name::new(c)?),
})
}
pub fn is_literal(self) -> bool {
self == Self::Zero || self == Self::One
}
pub const fn to_name(self) -> Option<Name> {
if let Self::Name(name) = self {
Some(name)
} else {
None
}
}
pub const fn to_char(self) -> char {
match self {
Self::Placeholder => '.',
Self::Zero => '0',
Self::One => '1',
Self::Name(name) => name.to_char(),
}
}
}
pub struct Characters(Vec<Character>);
impl Characters {
pub fn from_str(text: &str, base: Base) -> Self {
let characters: Vec<Character> = text.chars()
.filter(|&c| c != ' ')
.flat_map(|c| {
let characters: Box<dyn Iterator<Item = Character>>;
if base == Base::Hexadecimal {
if let Some(array) = Self::hex_digit_to_array(c) {
characters = Box::new(array.into_iter());
return characters;
}
}
if let Ok(character) = Character::from_char(c) {
characters = Box::new(std::iter::repeat(character).take(base.bits_per_digit()));
} else {
panic!("Invalid template char '{c}' in template '{text}'.");
}
characters
})
.collect();
assert!(characters.len() <= 128, "Template size was greater than 128 bits. Template: '{text}'");
Self(characters)
}
pub fn extract_literal(&self) -> Option<u128> {
if !self.0.iter().any(|c| c.is_literal()) {
return None;
}
let literal_string: String = self.0.iter()
.map(|&c| if c == Character::One { '1' } else { '0' })
.collect();
Some(u128::from_str_radix(&literal_string, 2).expect("All digits should be '0' or '1'"))
}
pub fn literal_mask(&self) -> u128 {
let literal_string: String = self.0.iter()
.map(|&c| if c == Character::Zero || c == Character::One { '1' } else { '0' })
.collect();
u128::from_str_radix(&literal_string, 2).expect("All digits should be '0' or '1'")
}
pub fn has_placeholders(&self) -> bool {
self.0.iter()
.any(|&character| character == Character::Placeholder)
}
pub fn to_names(&self) -> Vec<Name> {
let mut uniques = BTreeSet::new();
let mut names = Vec::new();
for character in &self.0 {
if let Some(name) = character.to_name() {
if uniques.insert(name) {
names.push(name);
}
}
}
names
}
pub fn width(&self) -> u8 {
u8::try_from(self.0.len()).expect("Template width should be under 256")
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item=&Character> {
self.0.iter()
}
const fn hex_digit_to_array(digit: char) -> Option<[Character; 4]> {
const fn conv(value: u32) -> Character {
if value == 0 { Character::Zero } else { Character::One }
}
let n = match digit {
'0'..='9' => digit as u32 - '0' as u32,
'A'..='F' => digit as u32 - 'A' as u32 + 0xA,
_ => return None,
};
Some([conv(n & 0b1000), conv(n & 0b0100), conv(n & 0b0010), conv(n & 0b0001)])
}
}
impl fmt::Display for Characters {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &character in &self.0 {
write!(f, "{}", character.to_char())?;
}
Ok(())
}
}