mod exact;
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_pointer_width = "64",
any(
target_vendor = "apple",
target_os = "linux",
target_os = "android",
target_os = "windows",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia"
),
not(miri)
))]
#[path = "div/aarch64.rs"]
mod kernels;
#[cfg(all(target_arch = "x86_64", not(miri)))]
#[path = "div/x86_64.rs"]
mod kernels;
#[cfg(not(any(
all(
target_arch = "aarch64",
target_endian = "little",
target_pointer_width = "64",
any(
target_vendor = "apple",
target_os = "linux",
target_os = "android",
target_os = "windows",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia"
),
not(miri)
),
all(target_arch = "x86_64", not(miri))
)))]
#[path = "div/portable.rs"]
mod kernels;
pub(super) mod limbs;
mod newton;
mod rem1;
pub(super) use limbs::{div_2by1 as divide_2by1, invert_limb as reciprocal};
pub(super) use rem1::{
Powers, REM_1_BYTES, REM_1_BYTES_WIDE, REM_1_MAX, REM_1_PREPARED, limb_of, rem_1,
};
use super::add::{SMALL, read, read_small, small, write_small};
use super::mul::limbs::{add_1, sub_n};
use super::mul::{
Operand, Product, STACK_LIMBS, WIDE, Wide, on_heap, on_stack, put_wide, read_wide,
};
use super::sub::Negated;
use super::{ByteOrder, Number};
use core::cmp::Ordering::Less;
use core::mem::{self, MaybeUninit};
use core::ops::{Div, DivAssign, Rem, RemAssign};
use core::ptr;
use limbs::{
DC_DIV, DC_DIV_Q, Found, check_itch, check_q_top, div_2by1, div_3by2, div_q, div_q_split,
div_q_top, div_qr, div_qr_1, div_qr_2, from_top, invert_3by2, invert_limb, q_itch, qr_itch,
rshift, split_itch, split_low, top_itch,
};
pub(super) use newton::{Prepared, mu, mu_prepared, prepare};
use newton::{mu_div_prepared, mu_div_q};
impl Div<&Number> for &Number {
type Output = Number;
#[track_caller]
fn div(self, rhs: &Number) -> Number {
if small(self, rhs) {
let (q, _) = small_division::<TRUNCATED, false>(self, rhs);
return small_number(self.byte_order, q);
}
ref_by_ref::<TRUNCATED, false>(self, rhs)
}
}
impl Div<&Number> for Number {
type Output = Number;
#[track_caller]
fn div(mut self, rhs: &Number) -> Number {
self /= rhs;
self
}
}
impl Div<Number> for &Number {
type Output = Number;
#[track_caller]
fn div(self, mut rhs: Number) -> Number {
if small(self, &rhs) {
let (q, _) = small_division::<TRUNCATED, false>(self, &rhs);
put_small(&mut rhs.value, self.byte_order, q);
return Number {
value: rhs.value,
negative: q.negative,
byte_order: self.byte_order,
};
}
ref_by_value::<false>(self, &mut rhs)
}
}
impl Div<Number> for Number {
type Output = Number;
#[track_caller]
fn div(mut self, rhs: Number) -> Number {
self /= rhs;
self
}
}
impl DivAssign<&Number> for Number {
#[inline]
#[track_caller]
fn div_assign(&mut self, rhs: &Number) {
if small(self, rhs) {
let (q, _) = small_division::<TRUNCATED, false>(self, rhs);
put_small(&mut self.value, self.byte_order, q);
self.negative = q.negative;
return;
}
assign_ref::<false>(self, rhs);
}
}
impl DivAssign<Number> for Number {
#[inline]
#[track_caller]
fn div_assign(&mut self, mut rhs: Number) {
if small(self, &rhs) {
let (q, _) = small_division::<TRUNCATED, false>(self, &rhs);
if self.value.capacity() < q.len && rhs.value.capacity() >= q.len {
mem::swap(&mut self.value, &mut rhs.value);
}
put_small(&mut self.value, self.byte_order, q);
self.negative = q.negative;
return;
}
assign_value::<false>(self, &mut rhs);
}
}
impl Rem<&Number> for &Number {
type Output = Number;
#[track_caller]
fn rem(self, rhs: &Number) -> Number {
if small(self, rhs) {
let (_, r) = small_division::<TRUNCATED, true>(self, rhs);
return small_number(self.byte_order, r);
}
ref_by_ref::<TRUNCATED, true>(self, rhs)
}
}
impl Rem<&Number> for Number {
type Output = Number;
#[track_caller]
fn rem(mut self, rhs: &Number) -> Number {
self %= rhs;
self
}
}
impl Rem<Number> for &Number {
type Output = Number;
#[track_caller]
fn rem(self, mut rhs: Number) -> Number {
if small(self, &rhs) {
let (_, r) = small_division::<TRUNCATED, true>(self, &rhs);
put_small(&mut rhs.value, self.byte_order, r);
return Number {
value: rhs.value,
negative: r.negative,
byte_order: self.byte_order,
};
}
ref_by_value::<true>(self, &mut rhs)
}
}
impl Rem<Number> for Number {
type Output = Number;
#[track_caller]
fn rem(mut self, rhs: Number) -> Number {
self %= rhs;
self
}
}
impl RemAssign<&Number> for Number {
#[inline]
#[track_caller]
fn rem_assign(&mut self, rhs: &Number) {
if small(self, rhs) {
let (_, r) = small_division::<TRUNCATED, true>(self, rhs);
put_small(&mut self.value, self.byte_order, r);
self.negative = r.negative;
return;
}
assign_ref::<true>(self, rhs);
}
}
impl RemAssign<Number> for Number {
#[inline]
#[track_caller]
fn rem_assign(&mut self, mut rhs: Number) {
if small(self, &rhs) {
let (_, r) = small_division::<TRUNCATED, true>(self, &rhs);
if self.value.capacity() < r.len {
mem::swap(&mut self.value, &mut rhs.value);
}
put_small(&mut self.value, self.byte_order, r);
self.negative = r.negative;
return;
}
assign_value::<true>(self, &mut rhs);
}
}
impl Number {
#[track_caller]
pub fn div_rem(&self, rhs: &Number) -> (Number, Number) {
if small(self, rhs) {
let (q, r) = small_division::<TRUNCATED, false>(self, rhs);
return (
small_number(self.byte_order, q),
small_number(self.byte_order, r),
);
}
both::<TRUNCATED>(self, rhs)
}
#[track_caller]
pub fn div_euclid(&self, rhs: &Number) -> Number {
if small(self, rhs) {
let (q, _) = small_division::<EUCLID, false>(self, rhs);
return small_number(self.byte_order, q);
}
ref_by_ref::<EUCLID, false>(self, rhs)
}
#[track_caller]
pub fn rem_euclid(&self, rhs: &Number) -> Number {
if small(self, rhs) {
let (_, r) = small_division::<EUCLID, true>(self, rhs);
return small_number(self.byte_order, r);
}
ref_by_ref::<EUCLID, true>(self, rhs)
}
}
impl Number {
#[track_caller]
pub fn div_floor(&self, rhs: &Number) -> Number {
if rhs.is_negative() {
-Negated::new(self).div_euclid(rhs)
} else {
self.div_euclid(rhs)
}
}
#[track_caller]
pub fn rem_floor(&self, rhs: &Number) -> Number {
if rhs.is_negative() {
-Negated::new(self).rem_euclid(rhs)
} else {
self.rem_euclid(rhs)
}
}
}
pub(super) const TRUNCATED: bool = false;
pub(super) const EUCLID: bool = true;
#[cold]
#[inline(never)]
#[track_caller]
pub(super) fn by_zero<const REM: bool>() -> ! {
if REM {
panic!("attempt to calculate the remainder with a divisor of zero")
} else {
panic!("attempt to divide by zero")
}
}
#[derive(Clone, Copy)]
struct Small {
lo: u64,
hi: u64,
len: usize,
negative: bool,
}
impl Small {
#[inline(always)]
fn of(m: u128, negative: bool) -> Small {
Small {
lo: m as u64,
hi: (m >> 64) as u64,
len: 16 - (m | 1).leading_zeros() as usize / 8,
negative: negative && m != 0,
}
}
}
#[inline(always)]
#[track_caller]
fn small_division<const EUCLIDEAN: bool, const REM: bool>(
a: &Number,
b: &Number,
) -> (Small, Small) {
let (x, y) = unsafe {
(
read_small(&a.value, a.byte_order),
read_small(&b.value, b.byte_order),
)
};
if y == 0 {
by_zero::<REM>();
}
let (q, r) = div_u128(x, y);
let negative_a = a.negative && x != 0;
if EUCLIDEAN && negative_a && r != 0 {
return (
Small::of(q + 1, a.negative != b.negative),
Small::of(y - r, false),
);
}
(
Small::of(q, a.negative != b.negative),
Small::of(r, negative_a && !EUCLIDEAN),
)
}
#[inline(always)]
fn div_u128(x: u128, y: u128) -> (u128, u128) {
if y >> 64 == 0 {
let y = y as u64;
if x >> 64 == 0 {
let x = x as u64;
return (u128::from(x / y), u128::from(x % y));
}
let s = y.leading_zeros();
let (d, u) = (y << s, x << s);
let v = invert_limb(d);
let top = ((x >> 64) as u64 >> 1) >> (63 - s);
let (q1, t) = div_2by1(top, (u >> 64) as u64, d, v);
let (q0, r) = div_2by1(t, u as u64, d, v);
return ((u128::from(q1) << 64) | u128::from(q0), u128::from(r >> s));
}
(x / y, x % y)
}
#[inline(always)]
fn put_small(v: &mut Vec<u8>, order: ByteOrder, s: Small) {
if v.capacity() >= s.len {
unsafe {
write_small(v.as_mut_ptr(), order, s.lo, s.hi, s.len);
v.set_len(s.len);
}
} else {
replace_small(v, order, s);
}
}
#[cold]
#[inline(never)]
fn replace_small(v: &mut Vec<u8>, order: ByteOrder, s: Small) {
*v = new_small(order, s);
}
#[inline(always)]
fn new_small(order: ByteOrder, s: Small) -> Vec<u8> {
let mut v = Vec::with_capacity(s.len);
unsafe {
write_small(v.as_mut_ptr(), order, s.lo, s.hi, s.len);
v.set_len(s.len);
}
v
}
#[inline(never)]
fn small_number(order: ByteOrder, s: Small) -> Number {
Number {
value: new_small(order, s),
negative: s.negative,
byte_order: order,
}
}
pub(super) const WIDER: usize = 64;
#[inline(always)]
fn wide(a: &Number, b: &Number) -> bool {
a.value.len() <= WIDER && b.value.len() <= WIDE
}
#[inline(always)]
#[track_caller]
unsafe fn register_division<const EUCLIDEAN: bool, const REM: bool>(
a: &Number,
b: &Number,
) -> (Wide, Wide) {
unsafe {
let y = if b.value.len() <= SMALL {
read_small(&b.value, b.byte_order)
} else {
let y = read_wide(&b.value, b.byte_order);
if y[2] | y[3] != 0 {
return by_medium::<EUCLIDEAN>(a, Medium::of(y, b.negative));
}
u128::from(y[0]) | (u128::from(y[1]) << 64)
};
if y == 0 {
by_zero::<REM>();
}
let y = Short::of(y, b.negative);
if a.value.len() <= WIDE {
wide_division::<EUCLIDEAN, 4>(a, y)
} else {
wider_division::<EUCLIDEAN>(a, y)
}
}
}
#[derive(Clone, Copy)]
pub(super) struct Short {
pub(super) y: u128,
s: u32,
d: u128,
v: u64,
negative: bool,
}
impl Short {
#[inline(always)]
pub(super) fn of(y: u128, negative: bool) -> Short {
let (y1, y0) = ((y >> 64) as u64, y as u64);
let s = if y1 == 0 {
y0.leading_zeros()
} else {
y1.leading_zeros()
};
let d = y << s;
let v = if y1 == 0 {
invert_limb(d as u64)
} else {
invert_3by2((d >> 64) as u64, d as u64)
};
Short {
y,
s,
d,
v,
negative,
}
}
}
#[inline(always)]
pub(super) unsafe fn wide_division<const EUCLIDEAN: bool, const LIMBS: usize>(
a: &Number,
y: Short,
) -> (Wide, Wide) {
let x = unsafe {
if LIMBS == 2 {
let x = read_small(&a.value, a.byte_order);
[x as u64, (x >> 64) as u64, 0, 0]
} else {
read_wide(&a.value, a.byte_order)
}
};
let u = shifted(x, y.s);
let mut q = [0; 4];
let r = if y.y >> 64 == 0 {
let d = y.d as u64;
let mut t = u[LIMBS];
for i in (0..LIMBS).rev() {
(q[i], t) = div_2by1(t, u[i], d, y.v);
}
u128::from(t >> y.s)
} else {
let (d1, d0) = ((y.d >> 64) as u64, y.d as u64);
let (mut t1, mut t0) = (u[LIMBS], u[LIMBS - 1]);
for i in (0..LIMBS - 1).rev() {
(q[i], t1, t0) = div_3by2(t1, t0, u[i], d1, d0, y.v);
}
((u128::from(t1) << 64) | u128::from(t0)) >> y.s
};
let negative_a = a.negative && x != [0; 4];
if EUCLIDEAN && negative_a && r != 0 {
let mut carry = true;
for l in &mut q {
(*l, carry) = l.overflowing_add(u64::from(carry));
}
return wide_results::<true>(q4(q), y.y - r, a.negative != y.negative, false);
}
if y.y >> 64 == 0 {
wide_results::<true>(q4(q), r, a.negative != y.negative, negative_a && !EUCLIDEAN)
} else {
wide_results::<false>(q4(q), r, a.negative != y.negative, negative_a && !EUCLIDEAN)
}
}
#[inline(always)]
fn q4(q: [u64; 4]) -> [u64; 8] {
[q[0], q[1], q[2], q[3], 0, 0, 0, 0]
}
#[inline(always)]
fn wide_results<const BRANCHLESS: bool>(
q: [u64; 8],
r: u128,
negative_q: bool,
negative_r: bool,
) -> (Wide, Wide) {
(
wide_of::<BRANCHLESS>(q, negative_q),
wide_of::<false>([r as u64, (r >> 64) as u64, 0, 0, 0, 0, 0, 0], negative_r),
)
}
#[inline(always)]
pub(super) unsafe fn wider_division<const EUCLIDEAN: bool>(a: &Number, y: Short) -> (Wide, Wide) {
let x = Operand {
ptr: a.value.as_ptr(),
len: a.value.len(),
big_endian: a.byte_order == ByteOrder::BigEndian,
significant: a.value.len(),
};
let n = x.len.div_ceil(8);
let mut u = [0; 9];
unsafe { u[n] = to_limbs_shifted(x, u.as_mut_ptr(), y.s) };
let mut q = [0; 8];
let r = if y.y >> 64 == 0 {
let d = y.d as u64;
let mut t = u[n];
for i in (0..n).rev() {
(q[i], t) = div_2by1(t, u[i], d, y.v);
}
u128::from(t >> y.s)
} else {
let (d1, d0) = ((y.d >> 64) as u64, y.d as u64);
let (mut t1, mut t0) = (u[n], u[n - 1]);
for i in (0..n - 1).rev() {
(q[i], t1, t0) = div_3by2(t1, t0, u[i], d1, d0, y.v);
}
((u128::from(t1) << 64) | u128::from(t0)) >> y.s
};
let negative_a = a.negative && u != [0; 9];
if EUCLIDEAN && negative_a && r != 0 {
let mut carry = true;
for l in &mut q {
(*l, carry) = l.overflowing_add(u64::from(carry));
}
return wide_results::<false>(q, y.y - r, a.negative != y.negative, false);
}
wide_results::<false>(q, r, a.negative != y.negative, negative_a && !EUCLIDEAN)
}
#[derive(Clone, Copy)]
pub(super) struct Medium {
d: [u64; 4],
s: u32,
n: usize,
v: u64,
negative: bool,
}
impl Medium {
#[inline(always)]
pub(super) fn of(y: [u64; 4], negative: bool) -> Medium {
let n = if y[3] != 0 { 4 } else { 3 };
let s = y[n - 1].leading_zeros();
let above = |l: u64| (l >> 1) >> (63 - s);
let d = [
y[0] << s,
(y[1] << s) | above(y[0]),
(y[2] << s) | above(y[1]),
(y[3] << s) | above(y[2]),
];
let v = invert_3by2(d[n - 1], d[n - 2]);
Medium {
d,
s,
n,
v,
negative,
}
}
#[inline(always)]
pub(super) unsafe fn normalized(
d: *const u64,
n: usize,
s: u32,
v: u64,
negative: bool,
) -> Medium {
let mut limbs = [0; 4];
unsafe { ptr::copy_nonoverlapping(d, limbs.as_mut_ptr(), n) };
Medium {
d: limbs,
s,
n,
v,
negative,
}
}
}
#[inline(always)]
pub(super) unsafe fn by_medium<const EUCLIDEAN: bool>(a: &Number, y: Medium) -> (Wide, Wide) {
unsafe {
if y.n == 4 {
wider_by_wide::<EUCLIDEAN, 4>(a, y)
} else {
wider_by_wide::<EUCLIDEAN, 3>(a, y)
}
}
}
#[inline(always)]
unsafe fn wider_by_wide<const EUCLIDEAN: bool, const NY: usize>(
a: &Number,
y: Medium,
) -> (Wide, Wide) {
debug_assert!(y.n == NY);
let (ny, d, s, v) = (NY, y.d, y.s, y.v);
let x = Operand {
ptr: a.value.as_ptr(),
len: a.value.len(),
big_endian: a.byte_order == ByteOrder::BigEndian,
significant: a.value.len(),
};
let n = x.len.div_ceil(8);
let mut u = [0; 9];
unsafe { u[n] = to_limbs_shifted(x, u.as_mut_ptr(), s) };
let negative_a = a.negative && u != [0; 9];
let (d1, d0) = (d[ny - 1], d[ny - 2]);
let mut q = [0; 8];
let nn = n.max(ny);
let steps = if u[nn] == 0 {
let k = nn - ny;
if (0..ny)
.rev()
.map(|j| u[k + j].cmp(&d[j]))
.find(|o| o.is_ne())
!= Some(Less)
{
let mut borrow = false;
for j in 0..ny {
let (l, b1) = u[k + j].overflowing_sub(d[j]);
let (l, b2) = l.overflowing_sub(u64::from(borrow));
(u[k + j], borrow) = (l, b1 | b2);
}
q[k] = 1;
}
k
} else {
nn - ny + 1
};
for i in (0..steps).rev() {
let (u2, u1) = (u[i + ny], u[i + ny - 1]);
if u2 == d1 && u1 == d0 {
q[i] = all_ones_step(&mut u, i, &d, ny);
continue;
}
let (mut qi, mut r1, mut r0) = div_3by2(u2, u1, u[i + ny - 2], d1, d0, v);
let mut borrow = 0;
for j in 0..ny - 2 {
let p = u128::from(qi) * u128::from(d[j]) + u128::from(borrow);
let (l, b) = u[i + j].overflowing_sub(p as u64);
u[i + j] = l;
borrow = (p >> 64) as u64 + u64::from(b);
}
let b0;
(r0, b0) = r0.overflowing_sub(borrow);
let b1;
(r1, b1) = r1.overflowing_sub(u64::from(b0));
if b1 {
let mut carry = false;
for j in 0..ny - 2 {
(u[i + j], carry) = carrying(u[i + j], d[j], carry);
}
(r0, carry) = carrying(r0, d0, carry);
r1 = r1.wrapping_add(d1).wrapping_add(u64::from(carry));
qi -= 1;
}
(u[i + ny - 2], u[i + ny - 1], u[i + ny]) = (r0, r1, 0);
q[i] = qi;
}
let remainder = u[..ny] != [0; NY];
if EUCLIDEAN && negative_a && remainder {
let mut carry = true;
for l in &mut q {
(*l, carry) = l.overflowing_add(u64::from(carry));
}
let mut borrow = false;
for j in 0..ny {
let (l, b1) = d[j].overflowing_sub(u[j]);
let (l, b2) = l.overflowing_sub(u64::from(borrow));
(u[j], borrow) = (l, b1 | b2);
}
}
let below = |l: u64| (l << 1) << (63 - s);
let mut r = [0; 8];
for j in 0..ny {
r[j] = (u[j] >> s) | below(u[j + 1]);
}
(
wide_of::<false>(q, a.negative != y.negative),
wide_of::<false>(r, negative_a && !EUCLIDEAN),
)
}
#[inline(always)]
fn carrying(a: u64, b: u64, carry: bool) -> (u64, bool) {
let (s, c1) = a.overflowing_add(b);
let (s, c2) = s.overflowing_add(u64::from(carry));
(s, c1 | c2)
}
#[cold]
#[inline(never)]
fn all_ones_step(u: &mut [u64; 9], i: usize, d: &[u64; 4], ny: usize) -> u64 {
let mut carry = false;
for j in 0..ny {
(u[i + j], carry) = carrying(u[i + j], d[j], carry);
}
u[i + ny] = u[i + ny].wrapping_add(u64::from(carry));
let mut borrow = false;
for j in 0..ny {
let (l, b1) = u[i + j + 1].overflowing_sub(d[j]);
let (l, b2) = l.overflowing_sub(u64::from(borrow));
(u[i + j + 1], borrow) = (l, b1 | b2);
}
debug_assert!(u[i + ny] == 0, "the top limb cancels");
u64::MAX
}
#[inline(always)]
fn shifted(x: [u64; 4], s: u32) -> [u64; 5] {
let out = |l: u64| (l >> 1) >> (63 - s);
[
x[0] << s,
(x[1] << s) | out(x[0]),
(x[2] << s) | out(x[1]),
(x[3] << s) | out(x[2]),
out(x[3]),
]
}
#[inline(always)]
fn wide_of<const BRANCHLESS: bool>(l: [u64; 8], negative: bool) -> Wide {
let len = if BRANCHLESS {
let mut nonzero = 1;
for (i, &x) in l.iter().enumerate() {
nonzero |= u32::from(x != 0) << i;
}
let t = 31 - nonzero.leading_zeros() as usize;
(8 * t + 8 - l[t].leading_zeros() as usize / 8).max(1)
} else {
match l.iter().rposition(|&x| x != 0) {
Some(t) => 8 * t + 8 - l[t].leading_zeros() as usize / 8,
None => 1,
}
};
Wide {
limbs: l,
len,
negative: negative && l != [0; 8],
}
}
#[inline(always)]
pub(super) fn wide_number(order: ByteOrder, w: &Wide) -> Number {
let mut value = Vec::new();
put_wide(&mut value, order, w);
Number {
value,
negative: w.negative,
byte_order: order,
}
}
#[inline(always)]
#[track_caller]
fn operands<const REM: bool>(a: &Number, b: &Number) -> (Operand, Operand) {
let (x, y) = (Operand::of(a), Operand::of(b));
if y.significant == 0 {
by_zero::<REM>();
}
(x, y)
}
#[inline(never)]
#[track_caller]
fn ref_by_ref<const EUCLIDEAN: bool, const REM: bool>(a: &Number, b: &Number) -> Number {
let order = a.byte_order;
if wide(a, b) {
let (q, r) = unsafe { register_division::<EUCLIDEAN, REM>(a, b) };
return wide_number(order, if REM { &r } else { &q });
}
let (x, y) = operands::<REM>(a, b);
unsafe {
divide::<EUCLIDEAN, _>(
x,
y,
a.negative,
b.negative,
Want::of(EUCLIDEAN, REM),
|q, r| new_number(if REM { r } else { q }, order),
)
}
}
#[inline(never)]
#[track_caller]
fn both<const EUCLIDEAN: bool>(a: &Number, b: &Number) -> (Number, Number) {
let order = a.byte_order;
if wide(a, b) {
let (q, r) = unsafe { register_division::<EUCLIDEAN, false>(a, b) };
return (wide_number(order, &q), wide_number(order, &r));
}
let (x, y) = operands::<false>(a, b);
unsafe {
divide::<EUCLIDEAN, _>(x, y, a.negative, b.negative, Want::Both, |q, r| {
(new_number(q, order), new_number(r, order))
})
}
}
#[inline(never)]
#[track_caller]
fn ref_by_value<const REM: bool>(a: &Number, b: &mut Number) -> Number {
let order = a.byte_order;
if wide(a, b) {
let (q, r) = unsafe { register_division::<TRUNCATED, REM>(a, b) };
let w = if REM { &r } else { &q };
put_wide(&mut b.value, order, w);
return Number {
value: mem::take(&mut b.value),
negative: w.negative,
byte_order: order,
};
}
let (x, y) = operands::<REM>(a, b);
unsafe {
divide::<TRUNCATED, _>(
x,
y,
a.negative,
b.negative,
Want::of(TRUNCATED, REM),
|q, r| {
let p = if REM { r } else { q };
p.put_in(&mut b.value, order);
Number {
value: mem::take(&mut b.value),
negative: p.negative,
byte_order: order,
}
},
)
}
}
#[inline(never)]
#[track_caller]
fn assign_ref<const REM: bool>(a: &mut Number, b: &Number) {
let order = a.byte_order;
if wide(a, b) {
let (q, r) = unsafe { register_division::<TRUNCATED, REM>(a, b) };
let w = if REM { &r } else { &q };
put_wide(&mut a.value, order, w);
a.negative = w.negative;
return;
}
let (x, y) = operands::<REM>(a, b);
unsafe {
divide::<TRUNCATED, _>(
x,
y,
a.negative,
b.negative,
Want::of(TRUNCATED, REM),
|q, r| {
let p = if REM { r } else { q };
p.put_in(&mut a.value, order);
a.negative = p.negative;
},
);
}
}
#[inline(never)]
#[track_caller]
fn assign_value<const REM: bool>(a: &mut Number, b: &mut Number) {
let order = a.byte_order;
if wide(a, b) {
let (q, r) = unsafe { register_division::<TRUNCATED, REM>(a, b) };
let w = if REM { &r } else { &q };
if a.value.capacity() < w.len && b.value.capacity() >= w.len {
mem::swap(&mut a.value, &mut b.value);
}
put_wide(&mut a.value, order, w);
a.negative = w.negative;
return;
}
let (x, y) = operands::<REM>(a, b);
unsafe {
divide::<TRUNCATED, _>(
x,
y,
a.negative,
b.negative,
Want::of(TRUNCATED, REM),
|q, r| {
let p = if REM { r } else { q };
if a.value.capacity() < p.len && b.value.capacity() >= p.len {
mem::swap(&mut a.value, &mut b.value);
}
p.put_in(&mut a.value, order);
a.negative = p.negative;
},
);
}
}
#[inline(always)]
unsafe fn new_number(p: &Product, order: ByteOrder) -> Number {
let mut v = Vec::with_capacity(p.len);
unsafe {
p.write(v.as_mut_ptr(), order);
v.set_len(p.len);
}
Number {
value: v,
negative: p.negative,
byte_order: order,
}
}
#[inline(always)]
unsafe fn divide<const EUCLIDEAN: bool, R>(
x: Operand,
y: Operand,
negative_x: bool,
negative_y: bool,
want: Want,
place: impl FnOnce(&Product, &Product) -> R,
) -> R {
let ny = y.significant.div_ceil(8);
if ny == 1 && want == Want::Remainder && x.significant >= REM_1_BYTES.min(REM_1_BYTES_WIDE) {
let d = unsafe { limb_of(y) };
let from = if d <= REM_1_MAX {
REM_1_BYTES
} else {
REM_1_BYTES_WIDE
};
if d >= 2 && x.significant >= from {
let r = unsafe { rem_1(x, &Powers::of(d)) };
return remainder_of_1::<EUCLIDEAN, R>(r, d, negative_x && x.significant != 0, place);
}
}
let nx = x.significant.div_ceil(8).max(ny);
let need = ny + 1 + working_memory(nx, ny, want);
let compute = |s: *mut MaybeUninit<u64>| {
unsafe { compute::<EUCLIDEAN, R>(s.cast(), x, y, negative_x, negative_y, want, place) }
};
if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(need, compute)
}
}
#[inline(always)]
pub(super) fn remainder_of_1<const EUCLIDEAN: bool, R>(
r: u64,
d: u64,
negative_x: bool,
place: impl FnOnce(&Product, &Product) -> R,
) -> R {
let r = if EUCLIDEAN && negative_x && r != 0 {
d - r
} else {
r
};
let remainder = Product {
r: if r == 0 { ptr::null() } else { &r },
len: 8 - (r | 1).leading_zeros() as usize / 8,
negative: negative_x && !EUCLIDEAN && r != 0,
};
place(&ZERO, &remainder)
}
#[inline(always)]
pub(super) fn working_memory(nx: usize, ny: usize, want: Want) -> usize {
let qn = nx - ny + 1;
let mut need = (nx + 1) + (qn + 1);
if want == Want::Quotient && ny >= 3 && from_top(nx + 1, ny) {
let k = qn;
let top = (2 * k + 2) + 1 + 2 * (k + 1) + top_itch(k);
need += top.max(check_itch(nx + 1, ny));
} else if ny >= DC_DIV {
let itch = |nn: usize| {
let mut itch = qr_itch(nn, ny);
if want == Want::Quotient && ny >= 3 {
if mu(nn, ny) {
} else if ny < DC_DIV_Q {
itch = itch.max(q_itch(nn, ny));
} else if let l @ 1.. = split_low(nn, ny) {
itch = itch.max(split_itch(nn, ny, l));
}
}
itch
};
need += itch(nx + 1).max(itch(nx));
}
need
}
#[derive(Clone, Copy)]
pub(super) struct Normal {
pub(super) d: *const u64,
pub(super) n: usize,
pub(super) shift: u32,
pub(super) v: u64,
pub(super) pre: *const Prepared,
}
#[inline(always)]
unsafe fn normalizing_shift(y: Operand) -> u32 {
let top = unsafe {
if y.big_endian {
*y.ptr.add(y.len - y.significant)
} else {
*y.ptr.add(y.significant - 1)
}
};
8 * (8 * y.significant.div_ceil(8) - y.significant) as u32 + top.leading_zeros()
}
#[inline(always)]
pub(super) unsafe fn normalize(y: Operand, d: *mut u64) -> Normal {
let n = y.significant.div_ceil(8);
unsafe {
let shift = normalizing_shift(y);
to_limbs_shifted(y, d, shift);
let v = if n == 1 {
invert_limb(*d)
} else {
invert_3by2(*d.add(n - 1), *d.add(n - 2))
};
Normal {
d,
n,
shift,
v,
pre: ptr::null(),
}
}
}
#[inline(always)]
unsafe fn compute<const EUCLIDEAN: bool, R>(
s: *mut u64,
x: Operand,
y: Operand,
negative_x: bool,
negative_y: bool,
want: Want,
place: impl FnOnce(&Product, &Product) -> R,
) -> R {
let ny = y.significant.div_ceil(8);
let nx = x.significant.div_ceil(8).max(ny);
unsafe {
let d = s.add((s.addr() / 8) & 1);
let w = s.add(ny + 1);
if want == Want::Quotient && ny >= 3 && from_top(nx + 1, ny) {
let k = nx + 1 - ny;
let (u, q) = (w, w.add(nx + 1));
let t = q.add(k + 1);
let shift = normalizing_shift(y);
quotient_from_top(x, Top::Operand(y, d), ny, shift, u, q, t);
let quotient = result(q, k, (negative_x && x.significant != 0) != negative_y);
return place("ient, &ZERO);
}
let normal = normalize(y, d);
compute_by::<EUCLIDEAN, R>(w, x, normal, negative_x, negative_y, want, place)
}
}
#[inline(always)]
pub(super) unsafe fn compute_by<const EUCLIDEAN: bool, R>(
s: *mut u64,
x: Operand,
y: Normal,
negative_x: bool,
negative_y: bool,
want: Want,
place: impl FnOnce(&Product, &Product) -> R,
) -> R {
let (d, ny, shift) = (y.d, y.n, y.shift);
let lx = x.significant.div_ceil(8);
let nx = lx.max(ny);
let qn = nx - ny + 1;
let negative_x = negative_x && x.significant != 0;
unsafe {
let u = s;
let q = u.add(nx + 1);
let t = q.add(qn + 1);
if want == Want::Quotient && ny >= 3 && from_top(nx + 1, ny) {
quotient_from_top(x, Top::Normal(d), ny, shift, u, q, t);
let quotient = result(q, qn, negative_x != negative_y);
return place("ient, &ZERO);
}
read_dividend(x, u, nx, shift);
*q.add(qn) = 0;
match ny {
1 => {
*u = if want == Want::Remainder {
div_qr_1::<false>(q, u, nx, *u.add(nx), *d, y.v)
} else {
div_qr_1::<true>(q, u, nx, *u.add(nx), *d, y.v)
};
}
2 => {
let (r1, r0) = div_qr_2(q, u, nx + 1, *d.add(1), *d, y.v);
(*u, *u.add(1)) = (r0, r1);
}
_ => {
let nn = if *u.add(nx) != 0 { nx + 1 } else { nx };
let qh = if !y.pre.is_null() && mu_prepared(nn, ny) {
mu_div_prepared(q, u, nn, d, ny, y.v, &*y.pre, want != Want::Quotient)
} else if want != Want::Quotient {
div_qr(q, u, nn, d, ny, y.v, t)
} else if mu(nn, ny) {
mu_div_q(q, u, nn, d, ny)
} else if ny < DC_DIV_Q {
match div_q(q, u, nn, d, ny, y.v, t) {
Some(qh) => qh,
None => exactly_after_all(q, u, nn, x, y, t),
}
} else if split_low(nn, ny) > 0 {
div_q_split(q, u, nn, d, ny, y.v, t)
} else {
div_qr(q, u, nn, d, ny, y.v, t)
};
if nn == nx {
*q.add(nx - ny) = u64::from(qh);
}
}
}
if want == Want::Quotient {
let quotient = result(q, qn + 1, negative_x != negative_y);
return place("ient, &ZERO);
}
let remainder = (0..ny).any(|i| *u.add(i) != 0);
if EUCLIDEAN && negative_x && remainder {
sub_n(u, d, u, ny);
if want != Want::Remainder {
add_1(q, qn + 1, 1);
}
}
if shift != 0 {
rshift(u, ny, shift);
}
let quotient = if want == Want::Remainder {
ZERO
} else {
result(q, qn + 1, negative_x != negative_y)
};
let remainder = result(u, ny, negative_x && !EUCLIDEAN);
place("ient, &remainder)
}
}
#[inline(always)]
unsafe fn read_dividend(x: Operand, u: *mut u64, nx: usize, shift: u32) {
let lx = x.significant.div_ceil(8);
unsafe {
*u.add(lx) = to_limbs_shifted(x, u, shift);
if lx < nx {
ptr::write_bytes(u.add(lx + 1), 0, nx - lx);
}
}
}
#[cold]
#[inline(never)]
unsafe fn exactly_after_all(
q: *mut u64,
u: *mut u64,
nn: usize,
x: Operand,
y: Normal,
t: *mut u64,
) -> bool {
unsafe {
read_dividend(x, u, x.significant.div_ceil(8).max(y.n), y.shift);
div_qr(q, u, nn, y.d, y.n, y.v, t)
}
}
#[derive(Clone, Copy)]
enum Top {
Operand(Operand, *mut u64),
Normal(*const u64),
}
#[inline(never)]
unsafe fn quotient_from_top(
x: Operand,
y: Top,
ny: usize,
shift: u32,
u: *mut u64,
q: *mut u64,
t: *mut u64,
) {
let lx = x.significant.div_ceil(8);
let nx = lx.max(ny);
let k = nx + 1 - ny;
unsafe {
let w = t;
let dt = w.add(2 * k + 2);
let dt = dt.add((dt.addr() / 8) & 1);
let h = dt.add(k + 1);
let top: *const u64 = match y {
Top::Operand(y, _) => {
shifted_limbs(y, ny - (k + 1), ny, shift, dt);
dt
}
Top::Normal(d) => d.add(ny - (k + 1)),
};
shifted_limbs(x, nx + 1 - (2 * k + 2), nx + 1, shift, w);
let v = invert_3by2(*top.add(k), *top.add(k - 1));
let found = div_q_top(h, w, k, top, v, h.add(k + 1));
debug_assert!(
found != Found::AllOnes,
"the dividend's top limb is below the divisor's"
);
ptr::copy_nonoverlapping(h.add(1), q, k);
if found == Found::Check {
read_dividend(x, u, nx, shift);
let d = match y {
Top::Operand(y, d) => {
to_limbs_shifted(y, d, shift);
d.cast_const()
}
Top::Normal(d) => d,
};
check_q_top(q, u, nx + 1, d, ny, t);
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub(super) enum Want {
Both,
Quotient,
Remainder,
}
impl Want {
#[inline(always)]
pub(super) const fn of(euclidean: bool, rem: bool) -> Want {
match (euclidean, rem) {
(false, false) => Want::Quotient,
(_, true) => Want::Remainder,
(true, false) => Want::Both,
}
}
}
const ZERO: Product = Product {
r: ptr::null(),
len: 1,
negative: false,
};
#[inline(always)]
unsafe fn limb(x: Operand, i: usize) -> u64 {
let l = x.significant;
let (full, rem) = (l / 8, l % 8);
unsafe {
let end = x.ptr.add(x.len);
if i < full {
if x.big_endian {
u64::from_be_bytes(read(end.sub(8 * i + 8)))
} else {
u64::from_le_bytes(read(x.ptr.add(8 * i)))
}
} else if i == full && rem > 0 {
if l >= 8 {
let v = if x.big_endian {
u64::from_be_bytes(read(end.sub(l)))
} else {
u64::from_le_bytes(read(x.ptr.add(l - 8)))
};
v >> (8 * (8 - rem))
} else if x.big_endian {
read_small(
core::slice::from_raw_parts(end.sub(l), rem),
ByteOrder::BigEndian,
) as u64
} else {
read_small(
core::slice::from_raw_parts(x.ptr, rem),
ByteOrder::LittleEndian,
) as u64
}
} else {
0
}
}
}
#[inline(always)]
unsafe fn shifted_limbs(x: Operand, from: usize, to: usize, s: u32, out: *mut u64) {
let above = |v: u64| (v >> 1) >> (63 - s);
let full = (x.significant / 8).clamp(from, to);
unsafe {
let mut below = if from > 0 { limb(x, from - 1) } else { 0 };
let end = x.ptr.add(x.len);
if x.big_endian {
for i in from..full {
let v = u64::from_be_bytes(read(end.sub(8 * i + 8)));
*out.add(i - from) = (v << s) | above(below);
below = v;
}
} else {
for i in from..full {
let v = u64::from_le_bytes(read(x.ptr.add(8 * i)));
*out.add(i - from) = (v << s) | above(below);
below = v;
}
}
for i in full..to {
let v = limb(x, i);
*out.add(i - from) = (v << s) | above(below);
below = v;
}
}
}
#[inline(always)]
unsafe fn to_limbs_shifted(x: Operand, out: *mut u64, s: u32) -> u64 {
let l = x.significant;
let (full, rem) = (l / 8, l % 8);
let above = |v: u64| (v >> 1) >> (63 - s);
let mut below = 0;
unsafe {
let end = x.ptr.add(x.len);
for i in 0..full {
let v = if x.big_endian {
u64::from_be_bytes(read(end.sub(8 * i + 8)))
} else {
u64::from_le_bytes(read(x.ptr.add(8 * i)))
};
*out.add(i) = (v << s) | above(below);
below = v;
}
if rem > 0 {
let v = if l >= 8 {
let v = if x.big_endian {
u64::from_be_bytes(read(end.sub(l)))
} else {
u64::from_le_bytes(read(x.ptr.add(l - 8)))
};
v >> (8 * (8 - rem))
} else if x.big_endian {
read_small(
core::slice::from_raw_parts(end.sub(l), rem),
ByteOrder::BigEndian,
) as u64
} else {
read_small(
core::slice::from_raw_parts(x.ptr, rem),
ByteOrder::LittleEndian,
) as u64
};
*out.add(full) = (v << s) | above(below);
below = v;
}
}
above(below)
}
#[inline(always)]
unsafe fn result(r: *const u64, n: usize, negative: bool) -> Product {
unsafe {
let mut top = n;
while top > 0 && *r.add(top - 1) == 0 {
top -= 1;
}
if top == 0 {
return Product {
r: ptr::null(),
len: 1,
negative: false,
};
}
let len = 8 * top - (*r.add(top - 1)).leading_zeros() as usize / 8;
Product { r, len, negative }
}
}