use std::ops::{AddAssign, MulAssign};
#[derive(Debug)]
pub struct UnknownChar(char);
impl std::fmt::Display for UnknownChar {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Encountered char {} not in base", self.0)
}
}
impl std::error::Error for UnknownChar {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
pub struct Base {
base: Vec<char>,
vals: std::collections::HashMap<char, usize>,
}
impl std::fmt::Display for Base {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.base.iter().collect::<String>())
}
}
impl Base {
pub fn new(base: &str) -> Base {
let mut vals: std::collections::HashMap<char, usize> = base
.chars()
.enumerate()
.map(|(i, c)| (c, i))
.collect();
vals.shrink_to_fit();
let mut base: Vec<char> = base.chars().collect();
base.shrink_to_fit();
Base {
base,
vals,
}
}
pub fn from_str<T: AddAssign + std::default::Default + MulAssign + std::convert::From<usize>>(&self, rep: &str) -> Result<T, UnknownChar> {
let mut val: T = Default::default();
let radix = self.base.len();
for c in rep.chars() {
match self.vals.get(&c) {
None => return Err(UnknownChar(c)),
Some(v) => {
val *= T::from(radix);
val += T::from(*v);
}
}
}
Ok(val)
}
pub fn rep<T: std::convert::Into<usize>>(&self, val: T) -> String {
let mut stack = Vec::new();
let radix = self.base.len();
let into = val.into();
let mut rem = into % radix;
stack.push(self.base[rem]);
let mut div = into / radix;
while div > 0 {
rem = div % radix;
div = div / radix;
stack.push(self.base[rem]);
}
stack.reverse();
stack.into_iter().collect()
}
}