use crate::num::basic::unsigneds::PrimitiveUnsigned;
use crate::num::conversion::traits::{
ConvertibleFrom, Digits, ExactFrom, PowerOf2Digits, WrappingFrom,
};
use alloc::vec::Vec;
use itertools::Itertools;
pub_test! {unsigned_to_digits_asc_naive<
T: ExactFrom<U> + PrimitiveUnsigned,
U: PrimitiveUnsigned + WrappingFrom<T>,
>(
x: &T,
base: U,
) -> Vec<U> {
assert!(base > U::ONE);
let mut digits = Vec::new();
let mut remainder = *x;
let base = T::exact_from(base);
while remainder != T::ZERO {
digits.push(U::wrapping_from(remainder.div_assign_mod(base)));
}
digits
}}
fn to_digits_asc<
T: ConvertibleFrom<U> + ExactFrom<U> + PrimitiveUnsigned + PowerOf2Digits<U>,
U: PrimitiveUnsigned + WrappingFrom<T>,
>(
x: &T,
base: &U,
) -> Vec<U> {
assert!(T::convertible_from(*base));
if *x == T::ZERO {
Vec::new()
} else if let Some(log_base) = base.checked_log_base_2() {
x.to_power_of_2_digits_asc(log_base)
} else {
unsigned_to_digits_asc_naive(x, *base)
}
}
fn to_digits_desc<
T: ConvertibleFrom<U> + ExactFrom<U> + PrimitiveUnsigned + PowerOf2Digits<U>,
U: PrimitiveUnsigned + WrappingFrom<T>,
>(
x: &T,
base: &U,
) -> Vec<U> {
assert!(T::convertible_from(*base));
if *x == T::ZERO {
Vec::new()
} else if let Some(log_base) = base.checked_log_base_2() {
x.to_power_of_2_digits_desc(log_base)
} else {
let mut digits = unsigned_to_digits_asc_naive(x, *base);
digits.reverse();
digits
}
}
fn from_digits_asc<
T: Digits<U> + PowerOf2Digits<U>,
U: PrimitiveUnsigned,
I: Iterator<Item = U>,
>(
base: &U,
digits: I,
) -> Option<T> {
if let Some(log_base) = base.checked_log_base_2() {
T::from_power_of_2_digits_asc(log_base, digits)
} else {
let mut digits = digits.collect_vec();
digits.reverse();
T::from_digits_desc(base, digits.into_iter())
}
}
fn from_digits_desc<
T: Digits<U> + TryFrom<U> + PrimitiveUnsigned + PowerOf2Digits<U>,
U: PrimitiveUnsigned,
I: Iterator<Item = U>,
>(
base: &U,
digits: I,
) -> Option<T> {
assert!(*base > U::ONE);
if let Some(log_base) = base.checked_log_base_2() {
T::from_power_of_2_digits_desc(log_base, digits)
} else {
let base = T::try_from(*base).ok()?;
let mut x = T::ZERO;
for digit in digits {
let digit = T::try_from(digit).ok()?;
if digit >= base {
return None;
}
x = x.checked_mul(base)?.checked_add(digit)?;
}
Some(x)
}
}
macro_rules! impl_digits {
($t:ident) => {
macro_rules! impl_digits_inner {
($u:ident) => {
impl Digits<$u> for $t {
#[inline]
fn to_digits_asc(&self, base: &$u) -> Vec<$u> {
to_digits_asc(self, base)
}
#[inline]
fn to_digits_desc(&self, base: &$u) -> Vec<$u> {
to_digits_desc(self, base)
}
#[inline]
fn from_digits_asc<I: Iterator<Item = $u>>(base: &$u, digits: I) -> Option<$t> {
from_digits_asc(base, digits)
}
#[inline]
fn from_digits_desc<I: Iterator<Item = $u>>(
base: &$u,
digits: I,
) -> Option<$t> {
from_digits_desc(base, digits)
}
}
};
}
apply_to_unsigneds!(impl_digits_inner);
};
}
apply_to_unsigneds!(impl_digits);