use super::super::add::read;
use super::super::mul::Operand;
use super::limbs::{div_2by1, invert_limb};
pub(in super::super) const REM_1_MAX: u64 = u64::MAX / 9;
pub(in super::super) const REM_1_BYTES: usize = 80;
pub(in super::super) const REM_1_BYTES_WIDE: usize = 112;
pub(in super::super) const REM_1_PREPARED: usize = 65;
#[derive(Clone, Copy)]
pub(in super::super) struct Powers {
pub(in super::super) d: u64,
c: [u64; 11],
shift: u32,
dn: u64,
v: u64,
}
impl Powers {
#[inline]
pub(in super::super) fn of(d: u64) -> Powers {
debug_assert!(d >= 2);
let shift = d.leading_zeros();
let dn = d << shift;
let v = invert_limb(dn);
let product = |t: u64, c: u64| {
let p = u128::from(t) * u128::from(c);
div_2by1((p >> 64) as u64, p as u64, dn, v).1
};
let mut t = [0u64; 11];
t[1] = if shift == 0 {
dn.wrapping_neg()
} else {
let q = (1 << shift) | (v >> (64 - shift));
let r = dn.wrapping_mul(q).wrapping_neg();
if r >= dn { r - dn } else { r }
};
let c1 = t[1] >> shift;
t[2] = product(t[1], c1);
t[3] = product(t[2], c1);
t[4] = product(t[2], t[2] >> shift);
for k in 1..=4 {
t[4 + k] = product(t[4], t[k] >> shift);
}
t[9] = product(t[8], c1);
t[10] = product(t[8], t[2] >> shift);
let mut c = [0; 11];
for k in 1..11 {
c[k] = t[k] >> shift;
}
Powers { d, c, shift, dn, v }
}
}
#[inline(always)]
fn split(x: u128) -> (u64, u64) {
((x >> 64) as u64, x as u64)
}
#[inline(always)]
fn mul(a: u64, b: u64) -> u128 {
u128::from(a) * u128::from(b)
}
#[inline(never)]
pub(in super::super) unsafe fn rem_1(x: Operand, p: &Powers) -> u64 {
unsafe {
if p.d <= REM_1_MAX {
rem_2(x, p)
} else {
rem_3(x, p)
}
}
}
#[inline(always)]
unsafe fn limb(x: Operand, k: usize) -> u64 {
unsafe {
if x.big_endian {
u64::from_be_bytes(read(x.ptr.add(x.len - 8 * k - 8)))
} else {
u64::from_le_bytes(read(x.ptr.add(8 * k)))
}
}
}
#[inline(always)]
unsafe fn top(x: Operand) -> u64 {
let s = x.significant;
let mut top = 0u64;
for i in (s / 8 * 8..s).rev() {
let b = unsafe {
if x.big_endian {
*x.ptr.add(x.len - 1 - i)
} else {
*x.ptr.add(i)
}
};
top = (top << 8) | u64::from(b);
}
top
}
#[inline(always)]
unsafe fn rem_2(x: Operand, p: &Powers) -> u64 {
let c = &p.c;
let limb = |k: usize| unsafe { limb(x, k) };
let (mut r1, mut r0) = (0u64, unsafe { top(x) });
let mut k = x.significant / 8;
if k % 8 >= 4 {
k -= 4;
let low = mul(limb(k + 3), c[3]) + mul(limb(k + 2), c[2]) + mul(limb(k + 1), c[1]);
(r1, r0) = split(mul(r0, c[4]) + low + u128::from(limb(k)));
}
while !k.is_multiple_of(8) {
k -= 1;
(r1, r0) = split(mul(r1, c[2]) + mul(r0, c[1]) + u128::from(limb(k)));
}
while k > 0 {
k -= 8;
let mut low = u128::from(limb(k));
for (i, &ci) in c.iter().enumerate().take(8).skip(1) {
low += mul(limb(k + i), ci);
}
(r1, r0) = split(mul(r1, c[9]) + mul(r0, c[8]) + low);
}
let sh = p.shift;
let (n2, n1, n0) = (r1 >> (64 - sh), (r1 << sh) | (r0 >> (64 - sh)), r0 << sh);
let r = div_2by1(n2, n1, p.dn, p.v).1;
div_2by1(r, n0, p.dn, p.v).1 >> sh
}
#[inline(always)]
fn add3(high: &mut u64, low: &mut u128, p: u128) {
let (s, carry) = low.overflowing_add(p);
*low = s;
*high += u64::from(carry);
}
#[inline(always)]
unsafe fn rem_3(x: Operand, p: &Powers) -> u64 {
let c = &p.c;
let limb = |k: usize| unsafe { limb(x, k) };
let (mut r2, mut r1, mut r0) = (0u64, 0u64, unsafe { top(x) });
let mut k = x.significant / 8;
if k % 8 >= 4 {
k -= 4;
let (mut high, mut low) = (0, u128::from(limb(k)));
for (i, &ci) in c.iter().enumerate().take(4).skip(1) {
add3(&mut high, &mut low, mul(limb(k + i), ci));
}
add3(&mut high, &mut low, mul(r0, c[4]));
(r2, (r1, r0)) = (high, split(low));
}
while !k.is_multiple_of(8) {
k -= 1;
let (mut high, mut low) = (0, u128::from(limb(k)));
add3(&mut high, &mut low, mul(r0, c[1]));
add3(&mut high, &mut low, mul(r1, c[2]));
add3(&mut high, &mut low, mul(r2, c[3]));
(r2, (r1, r0)) = (high, split(low));
}
while k > 0 {
k -= 8;
let (mut high, mut low) = (0, u128::from(limb(k)));
for (i, &ci) in c.iter().enumerate().take(8).skip(1) {
add3(&mut high, &mut low, mul(limb(k + i), ci));
}
add3(&mut high, &mut low, mul(r0, c[8]));
add3(&mut high, &mut low, mul(r1, c[9]));
add3(&mut high, &mut low, mul(r2, c[10]));
(r2, (r1, r0)) = (high, split(low));
}
let sh = p.shift;
let (n2, n1, n0) = if sh == 0 {
(r2, r1, r0)
} else {
(
(r2 << sh) | (r1 >> (64 - sh)),
(r1 << sh) | (r0 >> (64 - sh)),
r0 << sh,
)
};
let r = div_2by1(n2, n1, p.dn, p.v).1;
div_2by1(r, n0, p.dn, p.v).1 >> sh
}
#[inline(always)]
pub(in super::super) unsafe fn limb_of(y: Operand) -> u64 {
debug_assert!(y.significant <= 8);
let mut d = 0u64;
for i in (0..y.significant).rev() {
let b = unsafe {
if y.big_endian {
*y.ptr.add(y.len - 1 - i)
} else {
*y.ptr.add(i)
}
};
d = (d << 8) | u64::from(b);
}
d
}