use super::super::mul::limbs::{add_1, add_n, cmp_n, mul, mul_itch, sub_1, sub_n};
use super::kernels::submul_1;
use super::newton::{mu, mu_div_q, mu_div_qr};
use core::cmp::Ordering;
pub(in super::super) const DC_DIV: usize = 40;
pub(super) const DC_DIV_Q: usize = 256;
const FOLD_1: usize = 20;
const RECIPROCALS: [u16; 256] = {
let mut t = [0; 256];
let mut i = 0;
while i < 256 {
t[i] = (((1 << 19) - 3 * (1 << 8)) / (256 + i as u32)) as u16;
i += 1;
}
t
};
#[inline]
pub(in super::super) fn invert_limb(d: u64) -> u64 {
debug_assert!(d >> 63 == 1);
let d0 = d & 1;
let d9 = d >> 55;
let d40 = (d >> 24) + 1;
let d63 = (d >> 1) + d0;
let v0 = u64::from(RECIPROCALS[(d9 - 256) as usize]);
let v1 = (v0 << 11) - ((v0 * v0 * d40) >> 40) - 1;
let v2 = (v1 << 13) + ((v1 * ((1 << 60) - v1 * d40)) >> 47);
let e = ((v2 >> 1) & d0.wrapping_neg()).wrapping_sub(v2.wrapping_mul(d63));
let v3 = (v2 << 31).wrapping_add(((u128::from(v2) * u128::from(e)) >> 65) as u64);
let p = u128::from(v3) * u128::from(d) + u128::from(d);
v3.wrapping_sub(((p >> 64) as u64).wrapping_add(d))
}
#[inline]
pub(in super::super) fn invert_3by2(d1: u64, d0: u64) -> u64 {
let mut v = invert_limb(d1);
let mut p = d1.wrapping_mul(v).wrapping_add(d0);
if p < d0 {
v = v.wrapping_sub(1);
if p >= d1 {
v = v.wrapping_sub(1);
p = p.wrapping_sub(d1);
}
p = p.wrapping_sub(d1);
}
let t = u128::from(d0) * u128::from(v);
let (t1, t0) = ((t >> 64) as u64, t as u64);
let (p, carry) = p.overflowing_add(t1);
if carry {
v = v.wrapping_sub(1);
if p >= d1 && (p > d1 || t0 >= d0) {
v = v.wrapping_sub(1);
}
}
v
}
#[inline(always)]
pub(in super::super) fn div_2by1(u1: u64, u0: u64, d: u64, v: u64) -> (u64, u64) {
debug_assert!(u1 < d);
let p =
(u128::from(v) * u128::from(u1)).wrapping_add((u128::from(u1 + 1) << 64) | u128::from(u0));
let (q, q0) = ((p >> 64) as u64, p as u64);
let r = u0.wrapping_sub(q.wrapping_mul(d));
let mask = u64::from(r > q0).wrapping_neg();
let (q, r) = (q.wrapping_add(mask), r.wrapping_add(mask & d));
if r >= d {
return once_more(q, r, d);
}
(q, r)
}
#[cold]
#[inline(never)]
fn once_more(q: u64, r: u64, d: u64) -> (u64, u64) {
(q + 1, r - d)
}
#[inline(always)]
pub(super) fn div_3by2(u2: u64, u1: u64, u0: u64, d1: u64, d0: u64, v: u64) -> (u64, u64, u64) {
let p = (u128::from(v) * u128::from(u2)).wrapping_add((u128::from(u2) << 64) | u128::from(u1));
let (q, q0) = ((p >> 64) as u64, p as u64);
let d = (u128::from(d1) << 64) | u128::from(d0);
let r1 = u1.wrapping_sub(d1.wrapping_mul(q));
let r = ((u128::from(r1) << 64) | u128::from(u0))
.wrapping_sub(d)
.wrapping_sub(u128::from(d0) * u128::from(q));
let q = q.wrapping_add(1);
let mask = u64::from((r >> 64) as u64 >= q0).wrapping_neg();
let wide = (u128::from(mask) << 64) | u128::from(mask);
let (mut q, mut r) = (q.wrapping_add(mask), r.wrapping_add(d & wide));
if r >= d {
(q, r) = once_more_2(q, r, d);
}
(q, (r >> 64) as u64, r as u64)
}
#[cold]
#[inline(never)]
fn once_more_2(q: u64, r: u128, d: u128) -> (u64, u128) {
(q + 1, r - d)
}
pub(in super::super) unsafe fn div_qr_1<const QUOTIENT: bool>(
q: *mut u64,
u: *const u64,
n: usize,
top: u64,
d: u64,
v: u64,
) -> u64 {
debug_assert!(top < d);
unsafe {
if n < FOLD_1 {
let mut r = top;
for i in (0..n).rev() {
let (qi, ri) = div_2by1(r, *u.add(i), d, v);
if QUOTIENT {
*q.add(i) = qi;
}
r = ri;
}
return r;
}
let b2 = d.wrapping_mul(v).wrapping_neg();
let t = u128::from(top) * u128::from(v);
let mut pending = (t >> 64) as u64 + top;
let mut low = t as u64;
let sum = (u128::from(*u.add(n - 1)) << 64 | u128::from(*u.add(n - 2)))
.overflowing_add(u128::from(top) * u128::from(b2));
let (mut r, mut carry) = sum;
for j in (0..n - 2).rev() {
let (r1, r0) = ((r >> 64) as u64, r as u64);
let (r0, c) = r0.overflowing_add(if carry { b2 } else { 0 });
let r0 = if c { r0.wrapping_sub(d) } else { r0 };
if QUOTIENT {
let p = u128::from(r1) * u128::from(v);
let s = u128::from(r1)
+ u128::from(low)
+ u128::from((p >> 64) as u64 + u64::from(c))
+ if carry { (1 << 64) | u128::from(v) } else { 0 };
low = p as u64;
let (above, over) = pending.overflowing_add((s >> 64) as u64);
*q.add(j + 2) = above;
if over {
add_1(q.add(j + 3), n - j - 3, 1);
}
pending = s as u64;
}
(r, carry) = (u128::from(r0) << 64 | u128::from(*u.add(j)))
.overflowing_add(u128::from(r1) * u128::from(b2));
}
let (mut r1, r0) = ((r >> 64) as u64, r as u64);
let mut up = u64::from(carry);
if carry {
r1 = r1.wrapping_sub(d);
}
if r1 >= d {
r1 -= d;
up += 1;
}
let (t, rem) = div_2by1(r1, r0, d, v);
if QUOTIENT {
let (q0, c) = low.overflowing_add(t);
*q = q0;
let (q1, over) = pending.overflowing_add(up + u64::from(c));
*q.add(1) = q1;
if over {
add_1(q.add(2), n - 2, 1);
}
}
rem
}
}
pub(in super::super) unsafe fn div_qr_2(
q: *mut u64,
u: *const u64,
n: usize,
d1: u64,
d0: u64,
v: u64,
) -> (u64, u64) {
unsafe {
let (mut r1, mut r0) = (*u.add(n - 1), *u.add(n - 2));
for i in (0..n - 2).rev() {
let (qi, a, b) = div_3by2(r1, r0, *u.add(i), d1, d0, v);
*q.add(i) = qi;
(r1, r0) = (a, b);
}
(r1, r0)
}
}
#[inline(never)]
pub(super) unsafe fn sb_div_qr(
q: *mut u64,
n: *mut u64,
nn: usize,
d: *const u64,
dn: usize,
v: u64,
) -> bool {
debug_assert!(dn > 2 && nn >= dn);
unsafe {
let top = n.add(nn - dn);
let qh = cmp_n(top, d, dn) != Ordering::Less;
if qh {
sub_n(top, top, d, dn);
}
let n1 = sb_steps(q, n, d, dn, v, 0..nn - dn, *n.add(nn - 1));
*n.add(dn - 1) = n1;
qh
}
}
#[inline(always)]
unsafe fn sb_steps(
q: *mut u64,
n: *mut u64,
d: *const u64,
dn: usize,
v: u64,
steps: core::ops::Range<usize>,
mut n1: u64,
) -> u64 {
unsafe {
let (d1, d0) = (*d.add(dn - 1), *d.add(dn - 2));
for i in steps.rev() {
let n2 = *n.add(i + dn - 1);
let qi = if n1 == d1 && n2 == d0 {
submul_1(n.add(i), d, dn, u64::MAX);
n1 = *n.add(i + dn - 1);
u64::MAX
} else {
let (mut qi, mut r1, r0) = div_3by2(n1, n2, *n.add(i + dn - 2), d1, d0, v);
let borrow = submul_1(n.add(i), d, dn - 2, qi);
let (r0, b0) = r0.overflowing_sub(borrow);
let b1;
(r1, b1) = r1.overflowing_sub(u64::from(b0));
*n.add(i + dn - 2) = r0;
if b1 {
let carry = add_n(n.add(i), n.add(i), d, dn - 1);
r1 = r1.wrapping_add(d1).wrapping_add(u64::from(carry));
qi -= 1;
}
n1 = r1;
qi
};
*q.add(i) = qi;
}
n1
}
}
#[inline(always)]
pub(in super::super) unsafe fn div_qr(
q: *mut u64,
n: *mut u64,
nn: usize,
d: *const u64,
dn: usize,
v: u64,
t: *mut u64,
) -> bool {
unsafe {
if mu(nn, dn) {
mu_div_qr(q, n, nn, d, dn)
} else if dn >= DC_DIV && nn >= dn + 3 {
dc_div_qr(q, n, nn, d, dn, v, t)
} else {
sb_div_qr(q, n, nn, d, dn, v)
}
}
}
pub(super) unsafe fn div_qr_classic(
q: *mut u64,
n: *mut u64,
nn: usize,
d: *const u64,
dn: usize,
v: u64,
t: *mut u64,
) -> bool {
unsafe {
if dn >= DC_DIV && nn >= dn + 3 {
dc_div_qr(q, n, nn, d, dn, v, t)
} else {
sb_div_qr(q, n, nn, d, dn, v)
}
}
}
pub(in super::super) fn qr_itch(nn: usize, dn: usize) -> usize {
if mu(nn, dn) {
0
} else if dn >= DC_DIV && nn >= dn + 3 {
dc_itch(nn, dn)
} else {
0
}
}
pub(super) fn shrink_m(nn: usize, dn: usize) -> usize {
(nn - dn).min(dn - 1)
}
#[inline(never)]
pub(super) unsafe fn shrinking(
q: *mut u64,
n: *mut u64,
m: usize,
d: *const u64,
dn: usize,
v: u64,
) -> bool {
unsafe { shrink_steps(q, n, m, d, dn, v, *n.add(m + dn - 1)) }
}
#[inline(always)]
unsafe fn shrink_steps(
q: *mut u64,
n: *mut u64,
m: usize,
d: *const u64,
dn: usize,
v: u64,
mut n1: u64,
) -> bool {
debug_assert!(dn > 2 && m < dn);
unsafe {
let (d1, d0) = (*d.add(dn - 1), *d.add(dn - 2));
let c = dn - 2;
for i in (0..m).rev() {
let n2 = *n.add(i + dn - 1);
if n1 > d1 || (n1 == d1 && n2 >= d0) {
return false;
}
let (mut qi, mut r1, r0) = div_3by2(n1, n2, *n.add(i + dn - 2), d1, d0, v);
let borrow = if i > 0 {
submul_1(n.add(c), d.add(c - i), i, qi)
} else {
0
};
let (r0, b0) = r0.overflowing_sub(borrow);
let b1;
(r1, b1) = r1.overflowing_sub(u64::from(b0));
*n.add(i + dn - 2) = r0;
if b1 {
let carry = add_n(n.add(c), n.add(c), d.add(c - i), i + 1);
r1 = r1.wrapping_add(d1).wrapping_add(u64::from(carry));
qi -= 1;
}
n1 = r1;
*q.add(i) = qi;
}
let e = m.min(c);
if n1 < e as u64 {
*n.add(c + 1) = n1;
for i in 0..e {
let borrow = submul_1(n.add(i), d, c - i, *q.add(i));
let (r0, b0) = (*n.add(c)).overflowing_sub(borrow);
let (r1, b1) = (*n.add(c + 1)).overflowing_sub(u64::from(b0));
(*n.add(c), *n.add(c + 1)) = (r0, r1);
if b1 {
sub_1(q, m, 1);
break;
}
}
}
true
}
}
#[inline(never)]
unsafe fn sb_div_q(
q: *mut u64,
n: *mut u64,
nn: usize,
d: *const u64,
dn: usize,
v: u64,
) -> Option<bool> {
let (qn, m) = (nn - dn, shrink_m(nn, dn));
unsafe {
let top = n.add(qn);
let qh = cmp_n(top, d, dn) != Ordering::Less;
if qh {
sub_n(top, top, d, dn);
}
let n1 = sb_steps(q, n, d, dn, v, m..qn, *n.add(nn - 1));
shrink_steps(q, n, m, d, dn, v, n1).then_some(qh)
}
}
#[inline(always)]
pub(super) unsafe fn div_q(
q: *mut u64,
n: *mut u64,
nn: usize,
d: *const u64,
dn: usize,
v: u64,
t: *mut u64,
) -> Option<bool> {
let m = shrink_m(nn, dn);
unsafe {
if dn >= DC_DIV && nn - m >= dn + 3 {
let qh = div_qr(q.add(m), n.add(m), nn - m, d, dn, v, t);
shrinking(q, n, m, d, dn, v).then_some(qh)
} else {
sb_div_q(q, n, nn, d, dn, v)
}
}
}
pub(super) fn q_itch(nn: usize, dn: usize) -> usize {
qr_itch(nn - shrink_m(nn, dn), dn)
}
const TOP: usize = 4;
pub(super) fn from_top(nn: usize, dn: usize) -> bool {
dn >= 2 * (nn - dn) + 2 + TOP
}
pub(super) fn top_itch(k: usize) -> usize {
if mu(2 * k + 2, k + 1) {
0
} else if k + 1 < DC_DIV_Q {
(2 * k + 2) + qr_itch(2 * k + 2, k + 1).max(q_itch(2 * k + 2, k + 1))
} else {
match split_low(2 * k + 2, k + 1) {
0 => qr_itch(2 * k + 2, k + 1),
l => split_itch(2 * k + 2, k + 1, l),
}
}
}
pub(super) fn check_itch(nn: usize, dn: usize) -> usize {
nn + mul_itch(dn, nn - dn)
}
#[derive(Clone, Copy, PartialEq)]
pub(super) enum Found {
Exact,
Check,
AllOnes,
}
pub(super) unsafe fn div_q_top(
h: *mut u64,
w: *mut u64,
k: usize,
dt: *const u64,
v: u64,
s: *mut u64,
) -> Found {
unsafe {
let top = w.add(k + 1);
if cmp_n(top, dt, k + 1) != Ordering::Less {
return Found::AllOnes;
}
if k == 1 {
div_qr_2(h, w, 4, *dt.add(1), *dt, v);
} else {
let high = if mu(2 * k + 2, k + 1) {
mu_div_q(h, w, 2 * k + 2, dt, k + 1)
} else if k + 1 < DC_DIV_Q {
let (c, s) = (s, s.add(2 * k + 2));
core::ptr::copy_nonoverlapping(w, c, 2 * k + 2);
match div_q(h, c, 2 * k + 2, dt, k + 1, v, s) {
Some(qh) => qh,
None => div_qr(h, w, 2 * k + 2, dt, k + 1, v, s),
}
} else if split_low(2 * k + 2, k + 1) > 0 {
div_q_split(h, w, 2 * k + 2, dt, k + 1, v, s)
} else {
div_qr(h, w, 2 * k + 2, dt, k + 1, v, s)
};
debug_assert!(
!high,
"the top limbs of a dividend were below the divisor's"
);
}
if *h < 2 && (1..=k).any(|i| *h.add(i) != 0) {
Found::Check
} else {
Found::Exact
}
}
}
const SPLIT_MIN: usize = 4;
pub(super) fn split_low(nn: usize, dn: usize) -> usize {
let k = nn - dn;
let l = (dn.saturating_sub(2 + TOP) / 2).min(k.saturating_sub(1));
if l >= SPLIT_MIN { l } else { 0 }
}
pub(super) fn split_itch(nn: usize, dn: usize, l: usize) -> usize {
let high = qr_itch(nn - l, dn);
let low = (2 * l + 2) + (l + 1) + top_itch(l).max(check_itch(l + dn, dn));
high.max(low)
}
pub(super) unsafe fn div_q_split(
q: *mut u64,
n: *mut u64,
nn: usize,
d: *const u64,
dn: usize,
v: u64,
t: *mut u64,
) -> bool {
let l = split_low(nn, dn);
debug_assert!(l > 0);
unsafe {
let qh = div_qr(q.add(l), n.add(l), nn - l, d, dn, v, t);
let (w, h) = (t, t.add(2 * l + 2));
let s = h.add(l + 1);
core::ptr::copy_nonoverlapping(n.add(dn - l - 2), w, 2 * l + 2);
match div_q_top(h, w, l, d.add(dn - l - 1), v, s) {
Found::AllOnes => core::ptr::write_bytes(q, 0xFF, l),
top => {
core::ptr::copy_nonoverlapping(h.add(1), q, l);
if top == Found::Check {
check_q_top(q, n, l + dn, d, dn, s);
}
}
}
qh
}
}
pub(super) unsafe fn check_q_top(
q: *mut u64,
n: *const u64,
nn: usize,
d: *const u64,
dn: usize,
s: *mut u64,
) {
let k = nn - dn;
unsafe {
mul(s, d, dn, q, k, s.add(nn));
if cmp_n(s, n, nn) == Ordering::Greater {
sub_1(q, k, 1);
}
}
}
pub(super) fn dc_itch(nn: usize, dn: usize) -> usize {
let qn = nn - dn;
let first = if qn > dn { (qn - 1) % dn + 1 } else { qn };
let mut itch = if qn > dn { dc_n_itch(dn) } else { 0 };
if first >= 3 {
if first >= DC_DIV {
itch = itch.max(dc_n_itch(first));
}
if first < dn {
let (long, short) = if first > dn - first {
(first, dn - first)
} else {
(dn - first, first)
};
itch = itch.max(mul_itch(long, short));
}
}
dn + itch
}
fn dc_n_itch(n: usize) -> usize {
let (lo, hi) = (n / 2, n - n / 2);
let mut itch = mul_itch(hi, lo);
if hi >= DC_DIV {
itch = itch.max(dc_n_itch(hi));
}
if lo >= DC_DIV {
itch = itch.max(dc_n_itch(lo));
}
itch
}
pub(super) unsafe fn dc_div_qr(
q: *mut u64,
n: *mut u64,
nn: usize,
d: *const u64,
dn: usize,
v: u64,
t: *mut u64,
) -> bool {
debug_assert!(dn >= 6 && nn >= dn + 3);
let qn = nn - dn;
let first = if qn > dn { (qn - 1) % dn + 1 } else { qn };
let qh = unsafe {
let (qf, nf) = (q.add(qn - first), n.add(nn - dn - first));
if first < 3 {
sb_div_qr(qf, nf, dn + first, d, dn, v)
} else {
let (top, dtop) = (n.add(nn - 2 * first), d.add(dn - first));
let mut qh = if first < DC_DIV {
sb_div_qr(qf, top, 2 * first, dtop, first, v)
} else {
dc_div_qr_n(qf, top, dtop, first, v, t)
};
if first < dn {
let low = dn - first;
if first > low {
mul(t, qf, first, d, low, t.add(dn));
} else {
mul(t, d, low, qf, first, t.add(dn));
}
let mut borrow = u64::from(sub_n(nf, nf, t, dn));
if qh {
borrow += u64::from(sub_n(nf.add(first), nf.add(first), d, low));
}
while borrow != 0 {
let borrow_out = sub_1(qf, first, 1);
qh = qh && borrow_out == 0;
borrow -= u64::from(add_n(nf, nf, d, dn));
}
}
qh
}
};
unsafe {
let mut done = first;
while done < qn {
done += dn;
let block = qn - done;
let high = dc_div_qr_n(q.add(block), n.add(block), d, dn, v, t);
debug_assert!(
!high,
"a block's partial remainder was not below the divisor"
);
}
}
qh
}
unsafe fn dc_div_qr_n(
q: *mut u64,
u: *mut u64,
d: *const u64,
n: usize,
v: u64,
t: *mut u64,
) -> bool {
let (lo, hi) = (n / 2, n - n / 2);
unsafe {
let mut qh = if hi < DC_DIV {
sb_div_qr(q.add(lo), u.add(2 * lo), 2 * hi, d.add(lo), hi, v)
} else {
dc_div_qr_n(q.add(lo), u.add(2 * lo), d.add(lo), hi, v, t)
};
mul(t, q.add(lo), hi, d, lo, t.add(n));
let mut borrow = u64::from(sub_n(u.add(lo), u.add(lo), t, n));
if qh {
borrow += u64::from(sub_n(u.add(n), u.add(n), d, lo));
}
while borrow != 0 {
let borrow_out = sub_1(q.add(lo), hi, 1);
qh = qh && borrow_out == 0;
borrow -= u64::from(add_n(u.add(lo), u.add(lo), d, n));
}
let mut ql = if lo < DC_DIV {
sb_div_qr(q, u.add(hi), 2 * lo, d.add(hi), lo, v)
} else {
dc_div_qr_n(q, u.add(hi), d.add(hi), lo, v, t)
};
mul(t, d, hi, q, lo, t.add(n));
let mut borrow = u64::from(sub_n(u, u, t, n));
if ql {
borrow += u64::from(sub_n(u.add(lo), u.add(lo), d, hi));
}
while borrow != 0 {
let borrow_out = sub_1(q, lo, 1);
ql = ql && borrow_out == 0;
borrow -= u64::from(add_n(u, u, d, n));
}
debug_assert!(!ql, "the low half of a quotient carried out of its limbs");
qh
}
}
#[inline]
pub(in super::super) fn binvert_limb(d: u64) -> u64 {
debug_assert!(d & 1 == 1);
let mut v = d.wrapping_mul(3) ^ 2;
for _ in 0..4 {
v = v.wrapping_mul(2u64.wrapping_sub(d.wrapping_mul(v)));
}
v
}
pub(in super::super) unsafe fn bdiv_q(
q: *mut u64,
n: *mut u64,
qn: usize,
d: *const u64,
dn: usize,
v: u64,
) {
unsafe {
for i in 0..qn {
let qi = (*n.add(i)).wrapping_mul(v);
*q.add(i) = qi;
let len = (qn - i).min(dn);
let borrow = submul_1(n.add(i), d, len, qi);
if i + len < qn {
sub_1(n.add(i + len), qn - i - len, borrow);
}
}
}
}
pub(in super::super) unsafe fn rshift(x: *mut u64, n: usize, s: u32) {
debug_assert!(0 < s && s < 64);
unsafe {
for i in 0..n - 1 {
*x.add(i) = (*x.add(i) >> s) | (*x.add(i + 1) << (64 - s));
}
*x.add(n - 1) >>= s;
}
}