use super::super::Number;
use super::super::add::{read_small, small};
use super::super::mul::{Operand, Product, STACK_LIMBS, on_heap, on_stack};
use super::limbs::{DC_DIV, bdiv_q, binvert_limb, rshift};
use super::{
TRUNCATED, Want, div_u128, divide, operands, register_division, result, small_division,
small_number, wide, wide_number,
};
use core::mem::MaybeUninit;
const HENSEL_LIMIT: usize = 1 << 18;
fn by_hensel(qn: usize, dn: usize) -> bool {
let m = qn.min(dn);
dn < DC_DIV || qn * m - m * m / 2 < HENSEL_LIMIT
}
impl Number {
#[track_caller]
pub fn div_exact(&self, rhs: &Number) -> Number {
if small(self, rhs) {
let (q, _) = small_division::<TRUNCATED, false>(self, rhs);
return small_number(self.byte_order, q);
}
exact_quotient(self, rhs)
}
pub fn is_multiple_of(&self, rhs: &Number) -> bool {
if small(self, rhs) {
let (x, y) = unsafe {
(
read_small(&self.value, self.byte_order),
read_small(&rhs.value, rhs.byte_order),
)
};
return if y == 0 {
x == 0
} else {
div_u128(x, y).1 == 0
};
}
multiple(self, rhs)
}
}
#[inline(never)]
#[track_caller]
fn exact_quotient(a: &Number, b: &Number) -> Number {
let order = a.byte_order;
if wide(a, b) {
let (q, _) = unsafe { register_division::<TRUNCATED, false>(a, b) };
return wide_number(order, &q);
}
let (x, y) = operands::<false>(a, b);
let (lx, ny) = (x.significant.div_ceil(8), y.significant.div_ceil(8));
let negative = (a.negative && x.significant != 0) != b.negative;
let place = |q: &Product| {
let mut v = Vec::with_capacity(q.len);
unsafe {
q.write(v.as_mut_ptr(), order);
v.set_len(q.len);
}
Number {
value: v,
negative: q.negative,
byte_order: order,
}
};
if ny == 1 || lx < ny || !by_hensel(lx - ny + 1, ny) {
return unsafe {
divide::<TRUNCATED, _>(x, y, a.negative, b.negative, Want::Quotient, |q, _| {
place(q)
})
};
}
unsafe { hensel(x, y, negative, place) }
}
#[inline(always)]
unsafe fn hensel<R>(
x: Operand,
y: Operand,
negative: bool,
place: impl FnOnce(&Product) -> R,
) -> R {
let (lx, ny) = (x.significant.div_ceil(8), y.significant.div_ceil(8));
let qn = lx - ny + 1;
let zeros = unsafe { trailing_zeros(y) };
let (skip, bits) = (zeros / 64, (zeros % 64) as u32);
let dn = (8 * y.significant - zeros).div_ceil(64).min(qn);
let need = 1 + (qn + 1) + (dn + 1) + qn;
let compute = |s: *mut MaybeUninit<u64>| {
let s: *mut u64 = s.cast();
unsafe {
let n = s.add(qn & 1);
let d = n.add(qn + 1);
let q = d.add(dn + 1);
limbs_from(x, skip, qn + 1, n);
limbs_from(y, skip, dn + 1, d);
if bits != 0 {
rshift(n, qn + 1, bits);
rshift(d, dn + 1, bits);
}
bdiv_q(q, n, qn, d, dn, binvert_limb(*d));
place(&result(q, qn, negative))
}
};
if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(need, compute)
}
}
#[inline(never)]
fn multiple(a: &Number, b: &Number) -> bool {
let (x, y) = (Operand::of(a), Operand::of(b));
if y.significant == 0 || x.significant == 0 {
return x.significant == 0;
}
if x.significant < y.significant {
return false;
}
if unsafe { trailing_zeros(x) < trailing_zeros(y) } {
return false;
}
if wide(a, b) {
let (_, r) = unsafe { register_division::<TRUNCATED, true>(a, b) };
return r.limbs == [0; 8];
}
unsafe { divide::<TRUNCATED, _>(x, y, false, false, Want::Remainder, |_, r| r.r.is_null()) }
}
unsafe fn trailing_zeros(x: Operand) -> usize {
let mut i = 0;
loop {
let l = unsafe { super::limb(x, i) };
if l != 0 {
return 64 * i + l.trailing_zeros() as usize;
}
i += 1;
}
}
#[inline(always)]
unsafe fn limbs_from(x: Operand, from: usize, n: usize, out: *mut u64) {
unsafe { super::shifted_limbs(x, from, from + n, 0, out) }
}