use crate::int::policy::div_rem::dispatch as div_rem_dispatch;
use crate::int::types::{Int, Uint};
#[inline]
fn mag_fits_u128<const N: usize>(mag: &Uint<N>) -> (bool, u128) {
let l = mag.as_limbs();
let lo = l[0] as u128;
let hi = if N >= 2 { l[1] as u128 } else { 0 };
let mut fits = true;
let mut i = 2;
while i < N {
if l[i] != 0 {
fits = false;
break;
}
i += 1;
}
(fits, lo | (hi << 64))
}
#[inline]
pub(crate) fn rem_small_fast<const N: usize>(a: Int<N>, b: Int<N>) -> Int<N> {
assert!(
!b.is_zero(),
"attempt to calculate the remainder with a divisor of zero"
);
let a_mag = a.unsigned_abs();
let b_mag = b.unsigned_abs();
let neg_r = a.is_negative();
if a_mag < b_mag {
return a;
}
let (a_fits, a_u) = mag_fits_u128::<N>(&a_mag);
let (b_fits, b_u) = mag_fits_u128::<N>(&b_mag);
let mut rem = [0u64; N];
if a_fits && b_fits {
let r = a_u % b_u;
rem[0] = r as u64;
if N >= 2 {
rem[1] = (r >> 64) as u64;
}
} else {
let mut quot = [0u64; N];
div_rem_dispatch(a_mag.as_limbs(), b_mag.as_limbs(), &mut quot, &mut rem);
}
Int::<N>::from_mag_limbs(&rem, neg_r)
}
#[cfg(test)]
mod tests {
use super::rem_small_fast;
use crate::int::algos::rem::rem_via_div_rem::rem_via_div_rem;
use crate::int::types::Int;
#[test]
fn matches_via_div_rem_wide() {
let small: &[(i128, i128)] = &[
(100, 7),
(-100, 7),
(100, -7),
(-100, -7),
(0, 5),
(i128::MAX, 3),
(i128::MIN + 1, 3),
];
for &(a, b) in small {
let ia = Int::<8>::from_i128(a);
let ib = Int::<8>::from_i128(b);
assert_eq!(
rem_small_fast::<8>(ia, ib),
rem_via_div_rem::<8>(ia, ib),
"fast path ({a} % {b})"
);
}
let mut a_lim = [0u64; 8];
let mut b_lim = [0u64; 8];
for i in 0..8 {
a_lim[i] = 0x9E37_79B9_7F4A_7C15u64.wrapping_mul(i as u64 + 1);
b_lim[i] = 0xD1B5_4A32_D192_ED03u64.wrapping_mul(i as u64 + 3);
}
let ia = Int::<8>::from_mag_limbs(&a_lim, false);
let ib = Int::<8>::from_mag_limbs(&b_lim, false);
assert_eq!(
rem_small_fast::<8>(ia, ib),
rem_via_div_rem::<8>(ia, ib),
"fallback path full-width"
);
}
}