use super::super::div::limbs::{
bdiv_q, binvert_limb, div_qr, div_qr_1, div_qr_2, invert_3by2, invert_limb, qr_itch, rshift,
};
use super::super::mul::limbs::{add_n, cmp_n, mul, mul_itch, sqr, sqr_itch, sub_n};
use super::kernels::redc;
use super::low::{mullo, mullo_itch};
use super::window::{Registers, Ring};
use core::cmp::Ordering;
use core::ptr;
pub(super) const REDC_N: usize = 100;
#[inline(always)]
pub(in super::super) const fn even(n: usize) -> usize {
(n + 1) & !1
}
pub(in super::super) fn rem_itch(nu: usize, n: usize) -> usize {
even(n) + 2 * even(nu + 1) + qr_itch(nu + 1, n).max(qr_itch(nu, n))
}
pub(in super::super) unsafe fn rem(
r: *mut u64,
u: *const u64,
nu: usize,
k: usize,
m: *const u64,
n: usize,
s: *mut u64,
) {
let total = nu + k;
unsafe {
if total < n {
ptr::write_bytes(r, 0, n);
ptr::copy_nonoverlapping(u, r.add(k), nu);
return;
}
let shift = (*m.add(n - 1)).leading_zeros();
let d = s;
let x = d.add(even(n));
let q = x.add(even(total + 1));
let t = q.add(even(total + 1));
lshift(d, m, n, shift);
ptr::write_bytes(x, 0, k);
*x.add(total) = lshift(x.add(k), u, nu, shift);
match n {
1 => {
let rest = div_qr_1::<false>(q, x, total, *x.add(total), *d, invert_limb(*d));
*r = rest >> shift;
}
2 => {
let v = invert_3by2(*d.add(1), *d);
let (r1, r0) = div_qr_2(q, x, total + 1, *d.add(1), *d, v);
(*r, *r.add(1)) = if shift == 0 {
(r0, r1)
} else {
((r0 >> shift) | (r1 << (64 - shift)), r1 >> shift)
};
}
_ => {
let v = invert_3by2(*d.add(n - 1), *d.add(n - 2));
let nn = if *x.add(total) != 0 { total + 1 } else { total };
div_qr(q, x, nn, d, n, v, t);
if shift != 0 {
rshift(x, n, shift);
}
ptr::copy_nonoverlapping(x, r, n);
}
}
}
}
#[inline]
pub(in super::super) unsafe fn lshift(r: *mut u64, x: *const u64, n: usize, s: u32) -> u64 {
unsafe {
if s == 0 {
ptr::copy(x, r, n);
return 0;
}
let out = *x.add(n - 1) >> (64 - s);
for i in (1..n).rev() {
*r.add(i) = (*x.add(i) << s) | (*x.add(i - 1) >> (64 - s));
}
*r = *x << s;
out
}
}
pub(in super::super) struct Montgomery {
pub(in super::super) m: *const u64,
pub(in super::super) n: usize,
minv: [u64; 2],
inv: *const u64,
}
impl Montgomery {
pub(in super::super) fn new_itch(n: usize) -> usize {
if n >= REDC_N { 2 * even(n) } else { 0 }
}
pub(in super::super) fn itch(n: usize) -> usize {
let redc = if n >= REDC_N {
n + 2 * n + mullo_itch(n).max(mul_itch(n, n))
} else {
0
};
2 * n + mul_itch(n, n).max(sqr_itch(n)).max(redc)
}
pub(in super::super) unsafe fn new(m: *const u64, n: usize, s: *mut u64) -> Self {
unsafe {
let v = binvert_limb(*m);
let inv = if n >= REDC_N {
let one = s.add(even(n));
ptr::write_bytes(one, 0, n);
*one = 1;
bdiv_q(s, one, n, m, n, v);
s.cast_const()
} else {
ptr::null()
};
let c = ((u128::from(*m) * u128::from(v)) >> 64) as u64;
let v1 = c
.wrapping_add((*m.add(1)).wrapping_mul(v))
.wrapping_mul(v)
.wrapping_neg();
Montgomery {
m,
n,
minv: [v.wrapping_neg(), !v1],
inv,
}
}
}
#[inline(always)]
pub(in super::super) unsafe fn redc(&self, r: *mut u64, u: *mut u64, t: *mut u64) {
unsafe {
if self.inv.is_null() {
redc_1(r, u, self.m, self.n, self.minv);
} else {
redc_n(r, u, self.m, self.n, self.inv, t);
}
}
}
pub(in super::super) unsafe fn plain(&self, r: *mut u64, a: *const u64, t: *mut u64) {
let n = self.n;
unsafe {
ptr::copy_nonoverlapping(a, t, n);
ptr::write_bytes(t.add(n), 0, n);
self.redc(r, t, t.add(2 * n));
self.reduce(r);
}
}
#[inline(always)]
pub(in super::super) unsafe fn reduce(&self, r: *mut u64) {
unsafe {
if cmp_n(r, self.m, self.n) != Ordering::Less {
sub_n(r, r, self.m, self.n);
}
}
}
}
impl Ring for Montgomery {
#[inline(always)]
fn n(&self) -> usize {
self.n
}
#[inline(always)]
unsafe fn sqr(&self, r: *mut u64, a: *const u64, t: *mut u64) {
let n = self.n;
unsafe {
sqr(t, a, n, t.add(2 * n));
self.redc(r, t, t.add(2 * n));
}
}
#[inline(always)]
unsafe fn mul(&self, r: *mut u64, a: *const u64, b: *const u64, t: *mut u64) {
let n = self.n;
unsafe {
mul(t, a, n, b, n, t.add(2 * n));
self.redc(r, t, t.add(2 * n));
}
}
}
#[inline(always)]
unsafe fn redc_1(r: *mut u64, u: *mut u64, m: *const u64, n: usize, minv: [u64; 2]) {
unsafe {
if redc(r, u, m, n, minv) {
sub_n(r, r, m, n);
}
}
}
unsafe fn redc_n(
r: *mut u64,
u: *const u64,
m: *const u64,
n: usize,
inv: *const u64,
t: *mut u64,
) {
unsafe {
let q = t;
let p = t.add(n);
let rest = p.add(2 * n);
mullo(q, u, inv, n, rest);
mul(p, m, n, q, n, rest);
if sub_n(r, u.add(n), p.add(n), n) {
add_n(r, r, m, n);
}
}
}
#[derive(Clone, Copy)]
pub(in super::super) struct Mont1 {
pub(in super::super) m: u64,
inv: u64,
}
impl Mont1 {
#[inline(always)]
pub(in super::super) fn new(m: u64) -> Self {
Mont1 {
m,
inv: binvert_limb(m),
}
}
#[inline(always)]
pub(in super::super) fn redc(&self, hi: u64, lo: u64) -> u64 {
let q = lo.wrapping_mul(self.inv);
let p = ((u128::from(q) * u128::from(self.m)) >> 64) as u64;
let (r, borrow) = hi.overflowing_sub(p);
if borrow { r.wrapping_add(self.m) } else { r }
}
#[inline(always)]
pub(in super::super) fn plain(&self, a: u64) -> u64 {
self.redc(0, a)
}
}
impl Registers for Mont1 {
type V = u64;
#[inline(always)]
fn sqr(&self, a: u64) -> u64 {
let p = u128::from(a) * u128::from(a);
self.redc((p >> 64) as u64, p as u64)
}
#[inline(always)]
fn mul(&self, a: u64, b: u64) -> u64 {
let p = u128::from(a) * u128::from(b);
self.redc((p >> 64) as u64, p as u64)
}
}
#[derive(Clone, Copy)]
pub(in super::super) struct Mont2 {
pub(in super::super) m: [u64; 2],
minv: [u64; 2],
}
impl Mont2 {
#[inline(always)]
pub(in super::super) fn new(m: [u64; 2]) -> Self {
let v = binvert_limb(m[0]);
let c = ((u128::from(m[0]) * u128::from(v)) >> 64) as u64;
let v1 = c
.wrapping_add(m[1].wrapping_mul(v))
.wrapping_mul(v)
.wrapping_neg();
Mont2 {
m,
minv: [v.wrapping_neg(), !v1],
}
}
#[inline(always)]
pub(in super::super) fn redc(&self, u: [u64; 4]) -> [u64; 2] {
let [m0, m1] = self.m;
let [n0, n1] = self.minv;
let [u0, u1, u2, u3] = u;
let p = u128::from(u0) * u128::from(n0);
let q0 = p as u64;
let q1 = ((p >> 64) as u64)
.wrapping_add(u0.wrapping_mul(n1))
.wrapping_add(u1.wrapping_mul(n0));
let qm = mul_2([q0, q1], [m0, m1]);
let carry = u64::from(u0 | u1 != 0);
let lo = u128::from(u2) | (u128::from(u3) << 64);
let hi = u128::from(qm[2]) | (u128::from(qm[3]) << 64);
let (s, c1) = lo.overflowing_add(hi);
let (s, c2) = s.overflowing_add(u128::from(carry));
let m = u128::from(m0) | (u128::from(m1) << 64);
let r = if c1 | c2 || s >= m {
s.wrapping_sub(m)
} else {
s
};
[r as u64, (r >> 64) as u64]
}
#[inline(always)]
pub(in super::super) fn plain(&self, a: [u64; 2]) -> [u64; 2] {
self.redc([a[0], a[1], 0, 0])
}
}
#[inline(always)]
fn mul_2(a: [u64; 2], b: [u64; 2]) -> [u64; 4] {
let p00 = u128::from(a[0]) * u128::from(b[0]);
let p01 = u128::from(a[0]) * u128::from(b[1]);
let p10 = u128::from(a[1]) * u128::from(b[0]);
let p11 = u128::from(a[1]) * u128::from(b[1]);
let c1 = (p00 >> 64) + u128::from(p01 as u64) + u128::from(p10 as u64);
let c2 = (c1 >> 64) + (p01 >> 64) + (p10 >> 64) + u128::from(p11 as u64);
let c3 = (c2 >> 64) + (p11 >> 64);
[p00 as u64, c1 as u64, c2 as u64, c3 as u64]
}
#[inline(always)]
fn sqr_2(a: [u64; 2]) -> [u64; 4] {
let p00 = u128::from(a[0]) * u128::from(a[0]);
let p01 = u128::from(a[0]) * u128::from(a[1]);
let p11 = u128::from(a[1]) * u128::from(a[1]);
let (d0, d1, d2) = (
(p01 as u64) << 1,
((p01 >> 63) as u64) | (((p01 >> 64) as u64) << 1),
(p01 >> 127) as u64,
);
let c1 = (p00 >> 64) + u128::from(d0);
let c2 = (c1 >> 64) + u128::from(d1) + u128::from(p11 as u64);
let c3 = (c2 >> 64) + u128::from(d2) + (p11 >> 64);
[p00 as u64, c1 as u64, c2 as u64, c3 as u64]
}
impl Registers for Mont2 {
type V = [u64; 2];
#[inline(always)]
fn sqr(&self, a: [u64; 2]) -> [u64; 2] {
self.redc(sqr_2(a))
}
#[inline(always)]
fn mul(&self, a: [u64; 2], b: [u64; 2]) -> [u64; 2] {
self.redc(mul_2(a, b))
}
}
#[derive(Clone, Copy)]
pub(in super::super) struct MontN<const N: usize> {
pub(in super::super) m: [u64; N],
minv: u64,
}
impl<const N: usize> MontN<N> {
#[inline(always)]
pub(in super::super) fn new(m: [u64; N]) -> Self {
MontN {
m,
minv: binvert_limb(m[0]).wrapping_neg(),
}
}
#[inline(always)]
pub(in super::super) fn plain(&self, a: [u64; N]) -> [u64; N] {
let mut one = [0; N];
one[0] = 1;
self.mul(a, one)
}
}
impl<const N: usize> Registers for MontN<N> {
type V = [u64; N];
#[inline(always)]
fn sqr(&self, a: [u64; N]) -> [u64; N] {
self.mul(a, a)
}
#[inline(always)]
fn mul(&self, a: [u64; N], b: [u64; N]) -> [u64; N] {
let m = &self.m;
let mut t = [0u64; N];
let mut top = 0u64;
for &bi in b.iter() {
let mut c = 0u64;
for j in 0..N {
let p = u128::from(a[j]) * u128::from(bi) + u128::from(t[j]) + u128::from(c);
t[j] = p as u64;
c = (p >> 64) as u64;
}
let s = u128::from(top) + u128::from(c);
let (tn, tn1) = (s as u64, (s >> 64) as u64);
let q = t[0].wrapping_mul(self.minv);
let p = u128::from(q) * u128::from(m[0]) + u128::from(t[0]);
let mut c = (p >> 64) as u64;
for j in 1..N {
let p = u128::from(q) * u128::from(m[j]) + u128::from(t[j]) + u128::from(c);
t[j - 1] = p as u64;
c = (p >> 64) as u64;
}
let s = u128::from(tn) + u128::from(c);
t[N - 1] = s as u64;
top = tn1 + (s >> 64) as u64;
}
let mut d = [0u64; N];
let mut borrow = false;
for j in 0..N {
let (x, b1) = t[j].overflowing_sub(m[j]);
let (x, b2) = x.overflowing_sub(u64::from(borrow));
d[j] = x;
borrow = b1 | b2;
}
if top != 0 || !borrow { d } else { t }
}
}