use crate::int::algos::div::div_mg::DivLimb;
use crate::int::algos::div::div_rem::div_rem;
use crate::int::types::compute_limbs::max_single_limbs;
pub(crate) fn div_knuth(num: &[u64], den: &[u64], quot: &mut [u64], rem: &mut [u64]) {
let mut u = max_single_limbs();
let mut v = max_single_limbs();
div_knuth_into(num, den, quot, rem, &mut u, &mut v);
}
pub(crate) fn div_knuth_into(
num: &[u64],
den: &[u64],
quot: &mut [u64],
rem: &mut [u64],
u: &mut [u64],
v: &mut [u64],
) {
for q in quot.iter_mut() {
*q = 0;
}
for r in rem.iter_mut() {
*r = 0;
}
let mut n = den.len();
while n > 0 && den[n - 1] == 0 {
n -= 1;
}
assert!(n > 0, "div_knuth: divide by zero");
let mut top = num.len();
while top > 0 && num[top - 1] == 0 {
top -= 1;
}
if top < n {
let copy_n = num.len().min(rem.len());
let mut i = 0;
while i < copy_n {
rem[i] = num[i];
i += 1;
}
return;
}
let shift = den[n - 1].leading_zeros();
debug_assert!(top < u.len() && n <= v.len());
if shift == 0 {
u[..top].copy_from_slice(&num[..top]);
u[top] = 0;
v[..n].copy_from_slice(&den[..n]);
} else {
let mut carry: u64 = 0;
for i in 0..top {
let val = num[i];
u[i] = (val << shift) | carry;
carry = val >> (64 - shift);
}
u[top] = carry;
carry = 0;
for i in 0..n {
let val = den[i];
v[i] = (val << shift) | carry;
carry = val >> (64 - shift);
}
}
let m_plus_n = if u[top] != 0 { top + 1 } else { top };
debug_assert!(m_plus_n >= n);
let m = m_plus_n - n;
if n == 1 {
div_rem(num, den, quot, rem);
return;
}
knuth_d_core::<u64>(u, v, n, m, quot);
if shift == 0 {
let copy_n = n.min(rem.len());
rem[..copy_n].copy_from_slice(&u[..copy_n]);
} else {
for i in 0..n {
if i < rem.len() {
let lo = u[i] >> shift;
let hi_into_lo = if i + 1 < n {
u[i + 1] << (64 - shift)
} else {
0
};
rem[i] = lo | hi_into_lo;
}
}
}
}
#[inline]
pub(crate) fn knuth_d_core<L: DivLimb>(u: &mut [L], v: &[L], n: usize, m: usize, quot: &mut [u64]) {
let v_top = v[n - 1]; let v_below = v[n - 2];
let recip = L::new_recip(v_top);
let mut j_plus_one = m + 1;
while j_plus_one > 0 {
j_plus_one -= 1;
let j = j_plus_one;
let jn = j + n;
let u_top = u[jn];
let u_next = u[jn - 1];
debug_assert!(u_top <= v_top, "knuth_d_core: dividend window top exceeds divisor top");
let (mut q_hat, mut r_hat, overflow) = if u_top >= v_top {
let (r, of) = u_next.overflowing_add(v_top);
(L::MAX, r, of)
} else {
let (q, r) = L::est_2by1(&recip, u_top, u_next);
(q, r, false)
};
if !overflow {
loop {
let (p_lo, p_hi) = q_hat.widening_mul(v_below);
if p_hi < r_hat || (p_hi == r_hat && p_lo <= u[jn - 2]) {
break;
}
q_hat = q_hat.overflowing_sub(L::ONE).0;
let (new_r, of) = r_hat.overflowing_add(v_top);
if of {
break;
}
r_hat = new_r;
}
}
let mut carry = L::ACC_ZERO;
let mut i = 0;
while i < n {
let (res, c) = L::mul_sub_step(q_hat, v[i], u[j + i], carry);
u[j + i] = res;
carry = c;
i += 1;
}
let (s2, b1) = L::mul_sub_final(u[jn], carry);
u[jn] = s2;
if b1 {
q_hat = q_hat.overflowing_sub(L::ONE).0;
let mut carry = L::ZERO;
let mut i = 0;
while i < n {
let (s1, c1) = u[j + i].overflowing_add(v[i]);
let (s2, c2) = s1.overflowing_add(carry);
u[j + i] = s2;
carry = L::ZERO.add_carries(c1, c2);
i += 1;
}
u[jn] = u[jn].overflowing_add(carry).0;
}
L::store_quot_digit(quot, j, q_hat);
}
}