use crate::int::algos::support::limbs::sub_assign_fixed;
use crate::int::types::Int;
#[inline]
pub(crate) const fn sub_ripple_borrow<const N: usize>(a: Int<N>, b: Int<N>) -> Int<N> {
let mut limbs = *a.as_limbs();
sub_assign_fixed(&mut limbs, b.as_limbs());
Int::<N>::from_limbs(limbs)
}
#[inline]
pub(crate) const fn sub_ripple_borrow_checked<const N: usize>(
a: Int<N>,
b: Int<N>,
) -> Option<Int<N>> {
let av = a.as_limbs();
let bv = b.as_limbs();
let mut out = [0u64; N];
let mut borrow = 0u64;
let mut i = 0;
while i < N {
let (d1, b1) = av[i].overflowing_sub(bv[i]);
let (d2, b2) = d1.overflowing_sub(borrow);
out[i] = d2;
borrow = (b1 as u64) + (b2 as u64);
i += 1;
}
let sa = av[N - 1] >> 63;
let sb = bv[N - 1] >> 63;
let sr = out[N - 1] >> 63;
if sa != sb && sr != sa {
None
} else {
Some(Int::<N>::from_limbs(out))
}
}
#[cfg(test)]
mod tests {
use super::sub_ripple_borrow;
use crate::int::types::Int;
#[test]
fn sub_simple_single_limb() {
let a = Int::<1>::from_i64(5);
let b = Int::<1>::from_i64(3);
let got = sub_ripple_borrow(a, b);
assert_eq!(got.as_i128(), 2);
}
#[test]
fn sub_borrow_across_limb_boundary() {
let a = Int::<2>::from_u128(1_u128 << 64);
let b = Int::<2>::from_i64(1);
let got = sub_ripple_borrow(a, b);
assert_eq!(got.as_i128(), u64::MAX as i128);
}
#[test]
fn sub_wraps_at_min() {
let a = Int::<1>::from_i64(i64::MIN);
let b = Int::<1>::from_i64(1);
let got = sub_ripple_borrow(a, b);
assert_eq!(got.as_i128(), i64::MAX as i128);
}
#[test]
fn sub_self_is_zero() {
let v = Int::<2>::from_i128(-987_654_321_i128);
let got = sub_ripple_borrow(v, v);
assert_eq!(got.as_i128(), 0);
}
#[test]
fn sub_zero_identity() {
let v = Int::<3>::from_i128(1_000_000_000_000_i128);
let zero = Int::<3>::from_i64(0);
assert_eq!(sub_ripple_borrow(v, zero).as_i128(), 1_000_000_000_000);
}
#[test]
fn sub_checked_fused_boundaries() {
use super::sub_ripple_borrow_checked;
let got = sub_ripple_borrow_checked(Int::<2>::from_i128(49), Int::<2>::from_i128(7));
assert_eq!(got.map(|v| v.as_i128()), Some(42));
assert!(sub_ripple_borrow_checked(Int::<2>::from_i128(i128::MIN), Int::<2>::from_i128(1)).is_none());
assert!(sub_ripple_borrow_checked(Int::<2>::from_i128(i128::MAX), Int::<2>::from_i128(-1)).is_none());
let got = sub_ripple_borrow_checked(Int::<2>::from_i128(i128::MIN), Int::<2>::from_i128(i128::MIN));
assert_eq!(got.map(|v| v.as_i128()), Some(0));
}
}