#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
use super::aarch64::{mont_add_4, mont_add_6, mont_sub_4, mont_sub_6};
#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
use super::same;
use super::{Field, array_of, halve, number_of};
use crate::math::numbers::{Number, basecase, redc_inverse, redc_limbs};
use core::hint::select_unpredictable;
use core::mem::MaybeUninit;
const KERNEL: usize = 7;
const MAX: usize = 9;
const fn invert_limb(m: u64) -> u64 {
let mut x = m;
let mut i = 0;
while i < 6 {
x = x.wrapping_mul(2u64.wrapping_sub(m.wrapping_mul(x)));
i += 1;
}
x
}
pub(in super::super) struct Mont<const N: usize> {
m: [u64; N],
minv: u64,
minv2: [u64; 2],
one: [u64; N],
r2: [u64; N],
p: Number,
}
impl<const N: usize> Mont<N> {
pub(in super::super) fn new(p: &Number, m: [u64; N]) -> Self {
let r = Number::new_from_u8(1, p.byte_order) << (64 * N);
let one = r.rem_euclid(p);
let r2 = (&one * &one).rem_euclid(p);
Mont {
m,
minv: invert_limb(m[0]).wrapping_neg(),
minv2: redc_inverse(m[0], if N > 1 { m[1 % N] } else { 0 }),
one: array_of(&one),
r2: array_of(&r2),
p: p.clone(),
}
}
#[inline(always)]
unsafe fn reduce(&self, t: *mut u64) -> [u64; N] {
let mut r = MaybeUninit::<[u64; N]>::uninit();
let (r, over) = unsafe {
let over = redc_limbs(r.as_mut_ptr().cast(), t, self.m.as_ptr(), N, self.minv2);
(r.assume_init(), over)
};
let (d, borrow) = sub_n(&r, &self.m);
select(over || !borrow, &d, &r)
}
#[inline(always)]
fn redc_product(&self, a: &[u64; N], b: &[u64; N]) -> [u64; N] {
if N >= KERNEL {
let mut t = MaybeUninit::<[u64; 2 * MAX]>::uninit();
let t = t.as_mut_ptr().cast::<u64>();
return unsafe {
basecase::mul(t, a.as_ptr(), b.as_ptr(), N);
self.reduce(t)
};
}
self.rows(a, b)
}
#[inline(always)]
fn redc_square(&self, a: &[u64; N]) -> [u64; N] {
if N >= KERNEL {
let mut t = MaybeUninit::<[u64; 2 * MAX]>::uninit();
let t = t.as_mut_ptr().cast::<u64>();
return unsafe {
basecase::sqr(t, a.as_ptr(), N);
self.reduce(t)
};
}
self.rows(a, a)
}
#[inline(always)]
fn rows(&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 {
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 (d, borrow) = sub_n(&t, m);
select(top != 0 || !borrow, &d, &t)
}
}
#[inline(always)]
fn add_n<const N: usize>(a: &[u64; N], b: &[u64; N]) -> ([u64; N], bool) {
let mut r = [0; N];
let mut carry = false;
for i in 0..N {
let (x, c1) = a[i].overflowing_add(b[i]);
let (x, c2) = x.overflowing_add(u64::from(carry));
r[i] = x;
carry = c1 | c2;
}
(r, carry)
}
#[inline(always)]
fn sub_n<const N: usize>(a: &[u64; N], b: &[u64; N]) -> ([u64; N], bool) {
let mut r = [0; N];
let mut borrow = false;
for i in 0..N {
let (x, b1) = a[i].overflowing_sub(b[i]);
let (x, b2) = x.overflowing_sub(u64::from(borrow));
r[i] = x;
borrow = b1 | b2;
}
(r, borrow)
}
#[inline(always)]
fn select<const N: usize>(c: bool, a: &[u64; N], b: &[u64; N]) -> [u64; N] {
let mut r = [0; N];
for i in 0..N {
r[i] = select_unpredictable(c, a[i], b[i]);
}
r
}
impl<const N: usize> Field for Mont<N> {
type E = [u64; N];
#[inline(always)]
fn limbs(&self) -> usize {
N
}
#[inline(always)]
fn load(&self, x: &[u64]) -> [u64; N] {
let mut r = [0; N];
r.copy_from_slice(&x[..N]);
r
}
#[inline(always)]
fn store(&self, x: &[u64; N], r: &mut [u64]) {
r[..N].copy_from_slice(x);
}
#[inline(always)]
fn zero(&self) -> [u64; N] {
[0; N]
}
#[inline(always)]
fn one(&self) -> [u64; N] {
self.one
}
#[inline(always)]
fn add(&self, a: &[u64; N], b: &[u64; N]) -> [u64; N] {
#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
match N {
4 => return *same(&mont_add_4(same(a), same(b), same(&self.m))),
6 => return *same(&mont_add_6(same(a), same(b), same(&self.m))),
_ => {}
}
let (s, carry) = add_n(a, b);
let (d, borrow) = sub_n(&s, &self.m);
select(carry || !borrow, &d, &s)
}
#[inline(always)]
fn sub(&self, a: &[u64; N], b: &[u64; N]) -> [u64; N] {
#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
match N {
4 => return *same(&mont_sub_4(same(a), same(b), same(&self.m))),
6 => return *same(&mont_sub_6(same(a), same(b), same(&self.m))),
_ => {}
}
let (d, borrow) = sub_n(a, b);
let (s, _) = add_n(&d, &self.m);
select(borrow, &s, &d)
}
#[inline(always)]
fn neg(&self, a: &[u64; N]) -> [u64; N] {
let (d, _) = sub_n(&self.m, a);
select(self.is_zero(a), a, &d)
}
#[inline(always)]
fn half(&self, a: &[u64; N]) -> [u64; N] {
halve(a, &self.m)
}
#[inline(always)]
fn mul(&self, a: &[u64; N], b: &[u64; N]) -> [u64; N] {
self.redc_product(a, b)
}
#[inline(always)]
fn sqr(&self, a: &[u64; N]) -> [u64; N] {
self.redc_square(a)
}
#[inline(always)]
fn is_zero(&self, a: &[u64; N]) -> bool {
a.iter().fold(0, |x, l| x | l) == 0
}
#[inline(always)]
fn equal(&self, a: &[u64; N], b: &[u64; N]) -> bool {
a == b
}
fn enter(&self, x: &Number) -> [u64; N] {
let r = array_of(&x.rem_euclid(&self.p));
self.redc_product(&r, &self.r2)
}
fn leave(&self, x: &[u64; N]) -> Number {
let mut one = [0; N];
one[0] = 1;
number_of(&self.redc_product(x, &one), self.p.byte_order)
}
#[inline(always)]
fn modulus(&self) -> &Number {
&self.p
}
}