use crate::int::algos::div::div_knuth::div_knuth;
use crate::int::algos::mul::mul_schoolbook::mul_schoolbook;
use crate::int::algos::support::limbs::{add_assign, cmp, sub_assign};
use crate::int::types::compute_limbs::MAX_QUADRUPLE_LIMBS;
const BZ_BASECASE: usize = 16;
const BZ_MAX: usize = MAX_QUADRUPLE_LIMBS;
pub(crate) fn div_burnikel_ziegler_with_knuth(
num: &[u64],
den: &[u64],
quot: &mut [u64],
rem: &mut [u64],
) {
let mut n = den.len();
while n > 0 && den[n - 1] == 0 {
n -= 1;
}
assert!(n > 0, "div_burnikel_ziegler_with_knuth: divide by zero");
let mut top = num.len();
while top > 0 && num[top - 1] == 0 {
top -= 1;
}
if n < crate::int::policy::div_rem::BZ_THRESHOLD || top < 2 * n {
div_knuth(num, den, quot, rem);
return;
}
bz_recursive_core(num, den, quot, rem, n, top);
}
pub(crate) fn bz_recursive_core(
num: &[u64],
den: &[u64],
quot: &mut [u64],
rem: &mut [u64],
n: usize,
top: usize,
) {
for q in quot.iter_mut() {
*q = 0;
}
for r in rem.iter_mut() {
*r = 0;
}
let shift = den[n - 1].leading_zeros();
let mut v = [0u64; BZ_MAX];
let mut u = [0u64; BZ_MAX + 2];
debug_assert!(n <= BZ_MAX && top + 1 < BZ_MAX + 2);
if shift == 0 {
v[..n].copy_from_slice(&den[..n]);
u[..top].copy_from_slice(&num[..top]);
} else {
let mut carry: u64 = 0;
for i in 0..n {
let val = den[i];
v[i] = (val << shift) | carry;
carry = val >> (64 - shift);
}
carry = 0;
for i in 0..top {
let val = num[i];
u[i] = (val << shift) | carry;
carry = val >> (64 - shift);
}
u[top] = carry;
}
let u_len = top + 1;
let mut r_norm = [0u64; BZ_MAX];
div_blocks(&u[..u_len], &v[..n], n, quot, &mut r_norm[..n]);
if shift == 0 {
let copy_n = n.min(rem.len());
rem[..copy_n].copy_from_slice(&r_norm[..copy_n]);
} else {
for i in 0..n {
if i < rem.len() {
let lo = r_norm[i] >> shift;
let hi_into_lo = if i + 1 < n {
r_norm[i + 1] << (64 - shift)
} else {
0
};
rem[i] = lo | hi_into_lo;
}
}
}
}
fn div_blocks(u: &[u64], v: &[u64], n: usize, quot: &mut [u64], rem: &mut [u64]) {
let u_len = u.len();
let blocks = u_len.div_ceil(n);
let mut r = [0u64; BZ_MAX]; let mut dividend = [0u64; BZ_MAX]; let mut q_block = [0u64; BZ_MAX];
let mut idx = blocks;
while idx > 0 {
idx -= 1;
let lo = idx * n;
let hi = ((idx + 1) * n).min(u_len);
let block_len = hi - lo;
dividend[..block_len].copy_from_slice(&u[lo..lo + block_len]);
for d in dividend[block_len..n].iter_mut() {
*d = 0;
}
dividend[n..2 * n].copy_from_slice(&r[..n]);
div_2n_1n(÷nd[..2 * n], v, n, &mut q_block[..n], &mut r[..n]);
let store_end = (lo + n).min(quot.len());
let store_len = store_end.saturating_sub(lo);
if store_len > 0 {
quot[lo..lo + store_len].copy_from_slice(&q_block[..store_len]);
}
}
let rem_n = n.min(rem.len());
rem[..rem_n].copy_from_slice(&r[..rem_n]);
}
fn div_2n_1n(a: &[u64], b: &[u64], n: usize, q: &mut [u64], r: &mut [u64]) {
if n <= BZ_BASECASE || n % 2 == 1 {
div_knuth(a, b, q, r);
return;
}
let s = n / 2;
let mut q1 = [0u64; BZ_MAX]; let mut r1 = [0u64; BZ_MAX];
div_3n_2n(&a[s..4 * s], b, s, &mut q1[..s], &mut r1[..2 * s]);
let mut dividend2 = [0u64; BZ_MAX];
dividend2[..s].copy_from_slice(&a[..s]); dividend2[s..3 * s].copy_from_slice(&r1[..2 * s]); div_3n_2n(÷nd2[..3 * s], b, s, &mut q[..s], r);
q[s..2 * s].copy_from_slice(&q1[..s]);
}
fn div_3n_2n(a: &[u64], b: &[u64], s: usize, q: &mut [u64], r: &mut [u64]) {
let b_lo = &b[..s];
let b_hi = &b[s..2 * s];
let a3 = &a[..s]; let a_top2 = &a[s..3 * s]; let a1 = &a[2 * s..3 * s];
let mut q_hat = [0u64; BZ_MAX]; let mut r1 = [0u64; BZ_MAX];
if cmp(a1, b_hi) >= 0 {
for x in q_hat[..s].iter_mut() {
*x = u64::MAX;
}
r1[..s].copy_from_slice(&a[s..2 * s]); if add_assign(&mut r1[..s], b_hi) {
r1[s] = 1;
}
} else {
div_2n_1n(a_top2, b_hi, s, &mut q_hat[..s], &mut r1[..s]);
}
let mut d = [0u64; BZ_MAX];
mul_schoolbook(&q_hat[..s], b_lo, &mut d[..2 * s]);
let mut rr = [0u64; BZ_MAX];
rr[..s].copy_from_slice(a3); rr[s..2 * s + 1].copy_from_slice(&r1[..s + 1]); let _ = sub_assign(&mut rr[..2 * s + 2], &d[..2 * s]);
let mut corrections = 0usize;
while rr[2 * s] != 0 || rr[2 * s + 1] != 0 {
let _ = sub_assign(&mut q_hat[..s], &[1]); let _ = add_assign(&mut rr[..2 * s + 2], &b[..2 * s]); corrections += 1;
debug_assert!(
corrections <= 2,
"div_3n_2n: more than 2 BZ corrections (s={s})"
);
if corrections > 2 {
break;
}
}
q[..s].copy_from_slice(&q_hat[..s]);
r[..2 * s].copy_from_slice(&rr[..2 * s]);
}
#[cfg(feature = "bench-alt")]
pub(crate) fn bz_chunk_core_forced(num: &[u64], den: &[u64], quot: &mut [u64], rem: &mut [u64]) {
let mut n = den.len();
while n > 0 && den[n - 1] == 0 {
n -= 1;
}
assert!(n > 0, "bz_chunk_core_forced: divide by zero");
let mut top = num.len();
while top > 0 && num[top - 1] == 0 {
top -= 1;
}
bz_recursive_core(num, den, quot, rem, n, top);
}
#[cfg(all(test, feature = "xx-wide"))]
mod tests {
use super::{bz_recursive_core, div_burnikel_ziegler_with_knuth};
use crate::int::algos::div::div_knuth::div_knuth;
fn rng(seed: u64) -> impl FnMut() -> u64 {
let mut state = seed | 1;
move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
}
}
fn make(next: &mut impl FnMut() -> u64, top: usize, den_n: usize) -> (Vec<u64>, Vec<u64>) {
let mut num = vec![0u64; top];
let mut den = vec![0u64; den_n];
for x in num.iter_mut() {
*x = next();
}
for x in den.iter_mut() {
*x = next();
}
num[top - 1] |= 0x8000_0000_0000_0000;
den[den_n - 1] |= 0x8000_0000_0000_0000;
(num, den)
}
fn assert_recursive_matches_knuth(num: &[u64], den: &[u64], label: &str) {
let len = num.len();
let mut q_ref = vec![0u64; len + 1];
let mut r_ref = vec![0u64; len + 1];
div_knuth(num, den, &mut q_ref, &mut r_ref);
let mut den_n = den.len();
while den_n > 0 && den[den_n - 1] == 0 {
den_n -= 1;
}
let mut top = num.len();
while top > 0 && num[top - 1] == 0 {
top -= 1;
}
let mut q_bz = vec![0u64; len + 1];
let mut r_bz = vec![0u64; len + 1];
bz_recursive_core(num, den, &mut q_bz, &mut r_bz, den_n, top);
assert_eq!(q_bz, q_ref, "BZ recursive quot mismatch [{label}]");
assert_eq!(
r_bz[..den_n],
r_ref[..den_n],
"BZ recursive rem mismatch [{label}]"
);
}
#[test]
fn bz_recursive_matches_knuth_working_width_2n_over_n() {
let mut next = rng(0x5DEE_CE66_D1B5_4A32);
for &(top, den_n) in &[(134usize, 67usize), (138, 69), (96, 48), (130, 65)] {
for _ in 0..40 {
let (num, den) = make(&mut next, top, den_n);
assert_recursive_matches_knuth(&num, &den, &format!("2n/n {top}/{den_n}"));
}
}
}
#[test]
fn bz_routed_entry_matches_knuth_at_engagement_widths() {
let mut next = rng(0xA0761D64_78BD_642F);
for &(top, den_n) in &[(134usize, 67usize), (138, 69), (130, 65), (128, 64)] {
let (num, den) = make(&mut next, top, den_n);
let len = num.len();
let mut q_ref = vec![0u64; len + 1];
let mut r_ref = vec![0u64; len + 1];
div_knuth(&num, &den, &mut q_ref, &mut r_ref);
let mut q_bz = vec![0u64; len + 1];
let mut r_bz = vec![0u64; len + 1];
div_burnikel_ziegler_with_knuth(&num, &den, &mut q_bz, &mut r_bz);
assert_eq!(q_bz, q_ref, "routed BZ quot mismatch {top}/{den_n}");
assert_eq!(
r_bz[..den_n],
r_ref[..den_n],
"routed BZ rem mismatch {top}/{den_n}"
);
}
}
#[test]
fn bz_recursive_matches_knuth_spread() {
let mut next = rng(0x2545_F491_4F6C_DD1D);
for &den_n in &[17usize, 24, 32, 33, 48, 64, 65, 67, 69] {
for &mul in &[2usize, 3] {
let top = mul * den_n;
for _ in 0..30 {
let (num, den) = make(&mut next, top, den_n);
assert_recursive_matches_knuth(&num, &den, &format!("spread {top}/{den_n}"));
if top > den_n + 1 {
let (num2, den2) = make(&mut next, top - 1, den_n);
assert_recursive_matches_knuth(
&num2,
&den2,
&format!("ragged {}/{den_n}", top - 1),
);
}
}
}
}
}
}