use crate::int::algos::div::div_knuth::div_knuth_into;
use crate::int::algos::div::div_knuth_u128_limb::div_knuth_u128_limb_into;
use crate::int::policy::mul::dispatch_slice as mul_slice;
use crate::int::policy::div_rem::{select_for_limbs, Algorithm};
use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
use crate::int::types::Int;
use crate::support::rounding::{should_bump, RoundingMode};
#[inline]
fn sig_len(a: &[u64]) -> usize {
let mut l = a.len();
while l > 1 && a[l - 1] == 0 {
l -= 1;
}
l
}
#[inline]
fn cmp_double_vs<const N: usize>(rem: &[u64], divisor: &[u64]) -> core::cmp::Ordering
where
Limbs<N>: ComputeLimbs,
{
let mut two_r_buf = Limbs::<N>::single_buffered_u64();
let two_r = two_r_buf.as_mut();
let mut carry: u64 = 0;
for (i, &r) in rem.iter().enumerate() {
let v = ((r as u128) << 1) | carry as u128;
two_r[i] = v as u64;
carry = (v >> 64) as u64;
}
let mut len = rem.len();
if carry != 0 {
two_r[len] = carry;
len += 1;
}
let dl = divisor.len();
let maxl = len.max(dl);
let mut k = maxl;
while k > 0 {
k -= 1;
let lhs = if k < len { two_r[k] } else { 0 };
let rhs = if k < dl { divisor[k] } else { 0 };
if lhs != rhs {
return if lhs > rhs {
core::cmp::Ordering::Greater
} else {
core::cmp::Ordering::Less
};
}
}
core::cmp::Ordering::Equal
}
#[inline]
fn apply_sign<const N: usize>(out: [u64; N], neg: bool, msg: &str) -> Int<N> {
let mag = Int::<N>::from_limbs(out);
if mag.is_negative() && !(neg && mag == Int::<N>::MIN) {
panic!("{msg}");
}
if neg {
mag.wrapping_neg()
} else {
mag
}
}
#[inline]
pub(crate) fn div_widen_scale<const N: usize>(
a: Int<N>,
b: Int<N>,
mult: Int<N>,
mode: RoundingMode,
) -> Int<N>
where
Limbs<N>: ComputeLimbs,
{
if b == Int::<N>::ZERO {
panic!("attempt to divide by zero");
}
let neg = a.is_negative() != b.is_negative();
let a_mag = *a.unsigned_abs().as_limbs();
let m_mag = *mult.as_limbs(); let b_mag = *b.unsigned_abs().as_limbs();
let al = sig_len(&a_mag);
let ml = sig_len(&m_mag);
let bl = sig_len(&b_mag);
let lz_a = a.unsigned_abs().leading_zeros();
let lz_m = mult.unsigned_abs().leading_zeros();
if lz_a + lz_m > <Int<N>>::BITS {
let num_mag = *a.wrapping_mul(mult).unsigned_abs().as_limbs();
let nl_fast = sig_len(&num_mag);
let mut quot = [0u64; N];
let mut rem = [0u64; N];
let mut u_buf = Limbs::<N>::single_buffered_u64();
let mut v_buf = Limbs::<N>::single_buffered_u64();
div_knuth_into(
&num_mag[..nl_fast],
&b_mag[..bl],
&mut quot,
&mut rem,
u_buf.as_mut(),
v_buf.as_mut(),
);
let rl = sig_len(&rem[..bl.max(1)]);
let rem_nonzero = !(rl == 1 && rem[0] == 0);
if rem_nonzero {
let cmp_r = cmp_double_vs::<N>(&rem[..bl.max(1)], &b_mag[..bl]);
let q_is_odd = (quot[0] & 1) != 0;
if should_bump(mode, cmp_r, q_is_odd, !neg) {
let mut carry: u64 = 1;
for limb in quot.iter_mut() {
let (s, c) = limb.overflowing_add(carry);
*limb = s;
if !c {
carry = 0;
break;
}
}
let _ = carry;
}
}
return apply_sign::<N>(quot, neg, "attempt to divide with overflow");
}
let mut num_buf = Limbs::<N>::double_buffered_u64();
let num = num_buf.as_mut();
let nlen = (al + ml).min(num.len());
for slot in num[..nlen].iter_mut() {
*slot = 0;
}
mul_slice(&a_mag[..al], &m_mag[..ml], &mut num[..nlen]);
let ntop = sig_len(&num[..nlen]);
let mut quot_buf = Limbs::<N>::double_buffered_u64();
let quot = quot_buf.as_mut();
let mut rem_buf = Limbs::<N>::double_buffered_u64();
let rem = rem_buf.as_mut();
let qlen = ntop.max(1);
for slot in quot[..qlen].iter_mut() {
*slot = 0;
}
for slot in rem[..bl.max(1)].iter_mut() {
*slot = 0;
}
let num_s = &num[..ntop];
let den_s = &b_mag[..bl];
let q = &mut quot[..qlen];
let r = &mut rem[..bl.max(1)];
match select_for_limbs(num_s, den_s) {
Algorithm::KnuthU128Limb => {
let mut u64buf = Limbs::<N>::double_buffered_u64();
let mut v64buf = Limbs::<N>::single_buffered_u64();
let mut u128_u = Limbs::<N>::double_buffered_u128();
let mut u128_v = Limbs::<N>::single_u128();
div_knuth_u128_limb_into(
num_s,
den_s,
q,
r,
u64buf.as_mut(),
v64buf.as_mut(),
u128_u.as_mut(),
u128_v.as_mut(),
);
}
Algorithm::Rem
| Algorithm::Knuth
| Algorithm::BurnikelZieglerWithKnuth
| Algorithm::Schoolbook => {
let mut u_buf = Limbs::<N>::double_buffered_u64();
let mut v_buf = Limbs::<N>::single_buffered_u64();
div_knuth_into(num_s, den_s, q, r, u_buf.as_mut(), v_buf.as_mut());
}
}
let rl = sig_len(&rem[..bl.max(1)]);
let rem_nonzero = !(rl == 1 && rem[0] == 0);
if rem_nonzero {
let cmp_r = cmp_double_vs::<N>(&rem[..bl.max(1)], &b_mag[..bl]);
let q_is_odd = (quot[0] & 1) != 0;
if should_bump(mode, cmp_r, q_is_odd, !neg) {
let mut carry: u64 = 1;
for limb in quot.iter_mut() {
let (s, c) = limb.overflowing_add(carry);
*limb = s;
if !c {
carry = 0;
break;
}
}
let _ = carry;
}
}
let mut out = [0u64; N];
out.copy_from_slice("[..N]);
apply_sign::<N>(out, neg, "attempt to divide with overflow")
}