use crate::int::algos::div::div_knuth::knuth_d_core;
use crate::int::types::compute_limbs::{Limb, MAX_SINGLE_LIMBS};
const SCRATCH_LIMBS_128: usize = MAX_SINGLE_LIMBS / 2 + 2;
pub(crate) fn div_knuth_u128_limb(num: &[u64], den: &[u64], quot: &mut [u64], rem: &mut [u64]) {
let mut u64buf = [0u64; MAX_SINGLE_LIMBS];
let mut v64buf = [0u64; MAX_SINGLE_LIMBS];
let mut u = [0u128; SCRATCH_LIMBS_128];
let mut v = [0u128; SCRATCH_LIMBS_128];
div_knuth_u128_limb_into(
num, den, quot, rem, &mut u64buf, &mut v64buf, &mut u, &mut v,
);
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn div_knuth_u128_limb_into(
num: &[u64],
den: &[u64],
quot: &mut [u64],
rem: &mut [u64],
u64buf: &mut [u64],
v64buf: &mut [u64],
u: &mut [u128],
v: &mut [u128],
) {
for q in quot.iter_mut() {
*q = 0;
}
for r in rem.iter_mut() {
*r = 0;
}
for x in u64buf.iter_mut() {
*x = 0;
}
for x in v64buf.iter_mut() {
*x = 0;
}
for x in u.iter_mut() {
*x = 0;
}
for x in v.iter_mut() {
*x = 0;
}
let mut n64 = den.len();
while n64 > 0 && den[n64 - 1] == 0 {
n64 -= 1;
}
assert!(n64 > 0, "div_knuth_u128_limb: divide by zero");
let mut top64 = num.len();
while top64 > 0 && num[top64 - 1] == 0 {
top64 -= 1;
}
if top64 < n64 {
let copy = num.len().min(rem.len());
rem[..copy].copy_from_slice(&num[..copy]);
return;
}
if n64 < 4 || !n64.is_multiple_of(2) {
crate::int::algos::div::div_knuth::div_knuth_into(num, den, quot, rem, u64buf, v64buf);
return;
}
let shift = den[n64 - 1].leading_zeros();
debug_assert!(top64 < u64buf.len() && n64 <= v64buf.len());
if shift == 0 {
u64buf[..top64].copy_from_slice(&num[..top64]);
v64buf[..n64].copy_from_slice(&den[..n64]);
} else {
let mut carry = 0u64;
for i in 0..top64 {
let val = num[i];
u64buf[i] = (val << shift) | carry;
carry = val >> (64 - shift);
}
u64buf[top64] = carry;
carry = 0;
for i in 0..n64 {
let val = den[i];
v64buf[i] = (val << shift) | carry;
carry = val >> (64 - shift);
}
}
let mut u_len64 = if u64buf[top64] != 0 { top64 + 1 } else { top64 };
u_len64 += u_len64 & 1; let n128 = n64 / 2;
let u_len128 = u_len64 / 2;
debug_assert!(u_len128 < u.len() && n128 <= v.len());
<u128 as Limb>::pack(&u64buf[..u_len64], &mut u[..u_len128]);
<u128 as Limb>::pack(&v64buf[..n64], &mut v[..n128]);
let m128 = u_len128 - n128;
knuth_d_core::<u128>(&mut u[..=u_len128], &v[..n128], n128, m128, quot);
let r64 = u64buf;
<u128 as Limb>::unpack(&u[..n128], &mut r64[..n64]);
if shift == 0 {
let copy = n64.min(rem.len());
rem[..copy].copy_from_slice(&r64[..copy]);
} else {
for i in 0..n64 {
if i < rem.len() {
let lo = r64[i] >> shift;
let hi = if i + 1 < n64 { r64[i + 1] << (64 - shift) } else { 0 };
rem[i] = lo | hi;
}
}
}
}
#[cfg(test)]
mod tests {
use super::div_knuth_u128_limb;
#[cfg(feature = "xx-wide")]
use super::div_knuth_u128_limb_into;
use crate::int::algos::div::div_knuth::div_knuth;
#[cfg(feature = "xx-wide")]
#[test]
fn u128_limb_knuth_matches_div_knuth() {
let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
for &n64 in &[2usize, 4, 6, 8, 12, 16, 24, 32, 48, 64] {
for _ in 0..400 {
let extra = 1 + (next() % (n64 as u64 + 2)) as usize; let top64 = n64 + extra;
let mut num = vec![0u64; top64];
let mut den = vec![0u64; n64];
for x in num.iter_mut() {
*x = next();
}
for x in den.iter_mut() {
*x = next();
}
if den[n64 - 1] == 0 {
den[n64 - 1] = 1; }
let mut q_ref = vec![0u64; top64 + 1];
let mut r_ref = vec![0u64; top64 + 1];
div_knuth(&num, &den, &mut q_ref, &mut r_ref);
let mut q_c = vec![0u64; top64 + 1];
let mut r_c = vec![0u64; top64 + 1];
div_knuth_u128_limb(&num, &den, &mut q_c, &mut r_c);
assert_eq!(q_c, q_ref, "quot mismatch n64={n64} num={num:?} den={den:?}");
assert_eq!(
r_c[..n64],
r_ref[..n64],
"rem mismatch n64={n64} num={num:?} den={den:?}"
);
}
}
}
#[test]
fn u128_limb_knuth_odd_falls_back() {
let num = vec![0x1234u64, 0x5678, 0x9abc, 0xdef0, 0x1111];
for den in [
vec![0x3u64], vec![0x7u64, 0x9, 0xb], ] {
let mut q_ref = vec![0u64; num.len() + 1];
let mut r_ref = vec![0u64; num.len() + 1];
div_knuth(&num, &den, &mut q_ref, &mut r_ref);
let mut q_c = vec![0u64; num.len() + 1];
let mut r_c = vec![0u64; num.len() + 1];
div_knuth_u128_limb(&num, &den, &mut q_c, &mut r_c);
assert_eq!(q_c, q_ref, "fallback quot mismatch den={den:?}");
assert_eq!(r_c, r_ref, "fallback rem mismatch den={den:?}");
}
}
#[cfg(feature = "xx-wide")]
#[test]
fn u128_limb_into_exact_scratch_wide_shape() {
let mut state: u64 = 0xD1B5_4A32_D192_ED03;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
for &n in &[24usize, 32, 48, 64] {
let u64buf_len = 2 * n + n.div_ceil(2); let v64buf_len = n + 2; let u128_u_len = (2 * n + n.div_ceil(2)).div_ceil(2); let u128_v_len = n.div_ceil(2); for _ in 0..200 {
let top = 2 * n; let mut num = vec![0u64; top];
let mut den = vec![0u64; n];
for x in num.iter_mut() {
*x = next();
}
for x in den.iter_mut() {
*x = next();
}
den[n - 1] |= 1 << 63; let mut q_ref = vec![0u64; top + 1];
let mut r_ref = vec![0u64; top + 1];
div_knuth(&num, &den, &mut q_ref, &mut r_ref);
let mut q_c = vec![0u64; top + 1];
let mut r_c = vec![0u64; n];
let mut u64buf = vec![0u64; u64buf_len];
let mut v64buf = vec![0u64; v64buf_len];
let mut u128_u = vec![0u128; u128_u_len];
let mut u128_v = vec![0u128; u128_v_len];
div_knuth_u128_limb_into(
&num,
&den,
&mut q_c,
&mut r_c,
&mut u64buf,
&mut v64buf,
&mut u128_u,
&mut u128_v,
);
assert_eq!(q_c, q_ref, "into quot mismatch n={n}");
assert_eq!(r_c[..n], r_ref[..n], "into rem mismatch n={n}");
}
}
}
}