use core::cmp::Ordering;
use crate::{Nat, array::CopyArr};
type PopDigit<N> = crate::Eval<crate::expr::_Shr<N, crate::consts::PtrBits>>;
#[crate::utils::apply(crate::expr::nat_expr)]
pub type _DigitLenRec<N: crate::NatExpr> = _DigitLen<PopDigit<N>>;
#[crate::utils::apply(crate::expr::nat_expr)]
pub type _DigitLen<N: crate::NatExpr> = crate::expr::If<
N,
crate::expr::_Inc<_DigitLenRec<N>>, crate::lit!(0),
>;
type DigitLen<N> = crate::Eval<_DigitLen<N>>;
type DigitArr<N> = CopyArr<usize, DigitLen<N>>;
pub(crate) struct Fin<N: Nat> {
unsafe_digits: DigitArr<N>,
}
#[inline]
const fn digit_max(a: usize, b: usize) -> usize {
if a < b { b } else { a }
}
impl<N: Nat> Fin<N> {
const unsafe fn from_digits_unchecked(digits: DigitArr<N>) -> Self {
Self {
unsafe_digits: digits,
}
}
const unsafe fn as_num_mut(&mut self) -> &mut NumSlice {
NumSlice::from_digits_mut(self.unsafe_digits.as_mut_slice())
}
pub const fn as_num(&self) -> &NumSlice {
NumSlice::from_digits(self.unsafe_digits.as_slice())
}
#[expect(dead_code)]
pub const fn saturating_sub_assign(&mut self, rhs: &NumSlice) {
unsafe { self.as_num_mut() }.saturating_sub_assign(rhs);
}
#[expect(dead_code)]
pub const fn cmp(&self, rhs: &NumSlice) -> Ordering {
self.as_num().cmp(rhs)
}
pub(crate) const fn saturating_dec(&mut self) -> bool {
unsafe { self.as_num_mut() }.saturating_dec()
}
pub const unsafe fn inc_unchecked(&mut self) {
let mut digits = self.unsafe_digits.as_mut_slice();
while let [lsd, rest @ ..] = digits {
digits = rest;
let ovfl;
(*lsd, ovfl) = lsd.overflowing_add(1);
if !ovfl {
return;
}
}
}
pub const fn is_zero(&self) -> bool {
self.as_num().is_zero()
}
pub const ZERO: Self = {
unsafe { Self::from_digits_unchecked(CopyArr::of(0)) }
};
pub const MAX: Self = {
const fn doit<N: Nat>() -> DigitArr<N> {
const {
if crate::is_zero::<N>() {
CopyArr::of(0)
} else {
doit::<PopDigit<N>>()
.concat_arr([crate::to_usize_overflowing::<N>().0])
.try_retype()
.unwrap()
}
}
}
unsafe { Self::from_digits_unchecked(doit::<N>()) }
};
pub const fn to_usize(&self) -> Option<usize> {
match &self.as_num().digits {
[] => Some(0),
[lsd, rest @ ..] if NumSlice::from_digits(rest).is_zero() => Some(*lsd),
_ => None,
}
}
}
#[repr(transparent)]
pub(crate) struct NumSlice {
digits: [usize],
}
impl NumSlice {
const fn from_digits(digits: &[usize]) -> &Self {
unsafe { &*(core::ptr::from_ref(digits) as *const NumSlice) }
}
const fn from_digits_mut(digits: &mut [usize]) -> &mut Self {
unsafe { &mut *(core::ptr::from_mut(digits) as *mut NumSlice) }
}
const fn get_digit(&self, idx: usize) -> usize {
if idx < self.digits.len() {
self.digits[idx]
} else {
0
}
}
pub const fn cmp(&self, rhs: &Self) -> Ordering {
let mut i = digit_max(self.digits.len(), rhs.digits.len());
while i > 0 {
i -= 1;
let l = self.get_digit(i);
let r = rhs.get_digit(i);
if l < r {
return Ordering::Less;
}
if l > r {
return Ordering::Greater;
}
}
Ordering::Equal
}
pub const fn is_zero(&self) -> bool {
let mut digits = &self.digits;
while let &[ref rest @ .., last] = digits {
digits = rest;
if last != 0 {
return false;
}
}
true
}
const fn sub_assign_unchecked(&mut self, rhs: &Self) {
let mut carry = false;
let mut i = 0;
while i < self.digits.len() {
let (d, c1) = self.digits[i].overflowing_sub(rhs.get_digit(i));
let (r, c2) = d.overflowing_sub(carry as _);
self.digits[i] = r;
carry = c1 || c2;
i += 1;
}
debug_assert!(!carry);
}
pub const fn saturating_sub_assign(&mut self, rhs: &Self) {
match self.cmp(rhs) {
Ordering::Greater => self.sub_assign_unchecked(rhs),
Ordering::Less | Ordering::Equal => {
let mut i = 0;
while i < self.digits.len() {
self.digits[i] = 0;
i += 1;
}
}
}
}
const fn saturating_dec(&mut self) -> bool {
if self.is_zero() {
return false;
}
let mut digits = &mut self.digits;
while let [lsd, rest @ ..] = digits {
digits = rest;
let ovfl;
(*lsd, ovfl) = lsd.overflowing_sub(1);
if !ovfl {
break;
}
}
true
}
}