use core::{cmp::PartialOrd, convert::TryFrom};
use num_traits::{AsPrimitive, CheckedAdd, CheckedSub, FromPrimitive, Num, Signed};
use crate::encoding::ternary::{Btrit, RawEncoding, RawEncodingBuf, Trit, TritBuf, Trits, Utrit};
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
Empty,
Overflow,
Underflow,
}
macro_rules! signed_try_from_trits {
($int:ident) => {
impl<'a, T: RawEncoding<Trit = Btrit> + ?Sized> TryFrom<&'a Trits<T>> for $int {
type Error = Error;
fn try_from(trits: &'a Trits<T>) -> Result<Self, Self::Error> {
trits_to_int(trits)
}
}
impl<T: RawEncodingBuf> From<$int> for TritBuf<T>
where
T::Slice: RawEncoding<Trit = Btrit>,
{
fn from(x: $int) -> Self {
signed_int_trits(x).collect()
}
}
};
}
#[cfg(has_i128)]
signed_try_from_trits!(i128);
signed_try_from_trits!(i64);
signed_try_from_trits!(i32);
signed_try_from_trits!(i16);
signed_try_from_trits!(i8);
macro_rules! unsigned_try_from_trits {
($int:ident) => {
impl<'a, T: RawEncoding<Trit = Utrit> + ?Sized> TryFrom<&'a Trits<T>> for $int {
type Error = Error;
fn try_from(trits: &'a Trits<T>) -> Result<Self, Self::Error> {
trits_to_int(trits)
}
}
impl<T: RawEncodingBuf> From<$int> for TritBuf<T>
where
T::Slice: RawEncoding<Trit = Utrit>,
{
fn from(x: $int) -> Self {
unsigned_int_trits(x).collect()
}
}
};
}
#[cfg(has_u128)]
unsigned_try_from_trits!(u128);
unsigned_try_from_trits!(u64);
unsigned_try_from_trits!(u32);
unsigned_try_from_trits!(u16);
unsigned_try_from_trits!(u8);
pub fn trits_to_int<I, T: RawEncoding + ?Sized>(trits: &Trits<T>) -> Result<I, Error>
where
I: Clone + CheckedAdd + CheckedSub + PartialOrd + Num,
{
if trits.is_empty() {
Err(Error::Empty)
} else {
let mut acc = I::zero();
for trit in trits.iter().rev() {
let old_acc = acc.clone();
acc = trit
.add_to_num(acc)?
.checked_add(&old_acc)
.and_then(|acc| acc.checked_add(&old_acc))
.ok_or_else(|| {
if old_acc < I::zero() {
Error::Underflow
} else {
Error::Overflow
}
})?;
}
Ok(acc)
}
}
pub fn signed_int_trits<I>(x: I) -> impl Iterator<Item = Btrit> + Clone
where
I: Clone + AsPrimitive<i8> + FromPrimitive + Signed,
{
let is_neg = x.is_negative();
let mut x = if is_neg { x } else { -x };
let radix = I::from_i8(3).unwrap();
core::iter::from_fn(move || {
if x.is_zero() {
None
} else {
let modulus = ((x + I::one()).abs() % radix).as_();
x = x / radix;
if modulus == 1 {
x = x + -I::one();
}
Some(Btrit::try_from(((modulus + 2) % 3 - 1) * if is_neg { -1 } else { 1 }).unwrap())
}
})
.chain(Some(Btrit::Zero).filter(|_| x.is_zero()))
}
pub fn unsigned_int_trits<I>(mut x: I) -> impl Iterator<Item = Utrit> + Clone
where
I: Clone + AsPrimitive<u8> + FromPrimitive + Num,
{
let radix = I::from_u8(3).unwrap();
core::iter::from_fn(move || {
if x.is_zero() {
None
} else {
let modulus = (x % radix).as_();
x = x / radix;
Some(Utrit::try_from(modulus).unwrap())
}
})
.chain(Some(Utrit::Zero).filter(|_| x.is_zero()))
}