use crate::int::algos::support::limbs::add_assign_fixed;
use crate::int::types::Int;
#[inline]
pub(crate) const fn add_ripple_carry<const N: usize>(a: Int<N>, b: Int<N>) -> Int<N> {
let mut limbs = *a.as_limbs();
add_assign_fixed(&mut limbs, b.as_limbs());
Int::<N>::from_limbs(limbs)
}
#[inline]
pub(crate) const fn add_ripple_carry_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 carry = 0u64;
let mut i = 0;
while i < N {
let (s1, c1) = av[i].overflowing_add(bv[i]);
let (s2, c2) = s1.overflowing_add(carry);
out[i] = s2;
carry = (c1 as u64) + (c2 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::add_ripple_carry;
use crate::int::types::Int;
#[test]
fn add_simple_single_limb() {
let a = Int::<1>::from_i64(1);
let b = Int::<1>::from_i64(1);
let got = add_ripple_carry(a, b);
assert_eq!(got.as_i128(), 2);
}
#[test]
fn add_carry_across_limb_boundary() {
let a = Int::<2>::from_u128(u64::MAX as u128);
let b = Int::<2>::from_i64(1);
let got = add_ripple_carry(a, b);
assert_eq!(got.as_i128(), 1_i128 << 64);
}
#[test]
fn add_wraps_at_max() {
let a = Int::<1>::from_i64(i64::MAX);
let b = Int::<1>::from_i64(1);
let got = add_ripple_carry(a, b);
assert_eq!(got.as_i128(), i64::MIN as i128);
}
#[test]
fn add_zero_identity() {
let v = Int::<2>::from_i128(i128::MIN);
let zero = Int::<2>::from_i64(0);
assert_eq!(add_ripple_carry(v, zero).as_i128(), i128::MIN);
assert_eq!(add_ripple_carry(zero, v).as_i128(), i128::MIN);
}
#[test]
fn add_negative_positive_cancel() {
let pos = Int::<3>::from_i64(999_999);
let neg = Int::<3>::from_i64(-999_999);
let got = add_ripple_carry(pos, neg);
assert_eq!(got.as_i128(), 0);
}
#[test]
fn add_checked_fused_boundaries() {
use super::add_ripple_carry_checked;
let got = add_ripple_carry_checked(Int::<2>::from_i128(7), Int::<2>::from_i128(35));
assert_eq!(got.map(|v| v.as_i128()), Some(42));
assert!(add_ripple_carry_checked(Int::<2>::from_i128(i128::MAX), Int::<2>::from_i128(1)).is_none());
assert!(add_ripple_carry_checked(Int::<2>::from_i128(i128::MIN), Int::<2>::from_i128(-1)).is_none());
let got = add_ripple_carry_checked(Int::<2>::from_i128(i128::MAX), Int::<2>::from_i128(i128::MIN));
assert_eq!(got.map(|v| v.as_i128()), Some(-1));
let a = Int::<3>::from_i128(i128::MAX);
let got = add_ripple_carry_checked(a, a);
assert!(got.is_some(), "2*(i128::MAX) fits 192 bits");
}
}