use crate::int::algos::div::div_fixed::div_rem_mag_slice;
use crate::int::algos::mul::mul_schoolbook::mul_schoolbook as mul_slice;
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]
pub(crate) fn mul_schoolbook<const N: usize, const SCALE: u32>(
a: Int<N>,
b: Int<N>,
mode: RoundingMode,
) -> Int<N>
where
Limbs<N>: ComputeLimbs,
{
let neg = a.is_negative() != b.is_negative();
let a_mag = *a.unsigned_abs().as_limbs();
let b_mag = *b.unsigned_abs().as_limbs();
let al = sig_len(&a_mag);
let bl = sig_len(&b_mag);
let mut prod_buf = Limbs::<N>::double_buffered_u64();
let prod = prod_buf.as_mut();
let plen = (al + bl).min(prod.len());
for slot in prod[..plen].iter_mut() {
*slot = 0;
}
mul_slice(&a_mag[..al], &b_mag[..bl], &mut prod[..plen]);
if SCALE == 0 {
let mut out = [0u64; N];
out.copy_from_slice(&prod[..N]);
return apply_sign::<N>(out, neg, "attempt to multiply with overflow");
}
let mut div_buf = Limbs::<N>::double_buffered_u64();
let divisor = div_buf.as_mut();
divisor[0] = 1;
let mut dl = 1usize;
for _ in 0..SCALE {
let mut carry: u64 = 0;
for limb in divisor[..dl].iter_mut() {
let p = (*limb as u128) * 10u128 + carry as u128;
*limb = p as u64;
carry = (p >> 64) as u64;
}
if carry != 0 {
divisor[dl] = carry;
dl += 1;
}
}
let ptop = sig_len(&prod[..plen]);
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();
for slot in quot[..ptop].iter_mut() {
*slot = 0;
}
for slot in rem[..dl].iter_mut() {
*slot = 0;
}
div_rem_mag_slice(&prod[..ptop], &divisor[..dl], &mut quot[..ptop], &mut rem[..dl]);
let rl = sig_len(&rem[..dl]);
let rem_nonzero = !(rl == 1 && rem[0] == 0);
if rem_nonzero {
let cmp_r = cmp_double_vs::<N>(&rem[..dl], &divisor[..dl]);
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 multiply with overflow")
}
#[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
}
}