#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
use super::aarch64::{
pseudo_add_4, pseudo_add_6, pseudo_add_9, pseudo_mul_4, pseudo_sqr_4, pseudo_sub_4,
pseudo_sub_6, pseudo_sub_9,
};
#[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};
use core::mem::MaybeUninit;
pub(in super::super) struct Pseudo<const N: usize> {
c: u64,
f: u64,
s: u32,
m: [u64; N],
p: Number,
}
impl<const N: usize> Pseudo<N> {
pub(in super::super) fn new(p: &Number) -> Option<Self> {
let k = p.bits() as usize;
let s = (64 * N - k) as u32;
let c = (Number::new_from_u8(1, p.byte_order) << k) - p;
let c = limbs_low(&c)?;
if s >= 62 || c >= 1 << (62 - s) {
return None;
}
Some(Pseudo {
c,
f: c << s,
s,
m: array_of(p),
p: p.clone(),
})
}
#[inline(always)]
fn canonical(&self, a: &[u64; N]) -> [u64; N] {
let mut r = *a;
if self.s != 0 {
let t = r[N - 1] >> (64 - self.s);
r[N - 1] &= u64::MAX >> self.s;
r = add_1(&r, t * self.c).0;
}
let (mut b, carry) = add_1(&r, self.c);
let reached = if self.s == 0 {
carry
} else {
let top = b[N - 1] >> (64 - self.s) != 0;
b[N - 1] &= u64::MAX >> self.s;
top
};
let mask = u64::from(reached).wrapping_neg();
for i in 0..N {
r[i] = (b[i] & mask) | (r[i] & !mask);
}
r
}
#[inline(always)]
fn portable_add(&self, a: &[u64; N], b: &[u64; N]) -> [u64; N] {
let (s, carry) = add_n(a, b);
let (s, carry) = add_1(&s, self.f & u64::from(carry).wrapping_neg());
let mut r = s;
r[0] = r[0].wrapping_add(self.f & u64::from(carry).wrapping_neg());
r
}
#[inline(always)]
fn portable_sub(&self, a: &[u64; N], b: &[u64; N]) -> [u64; N] {
let mut f = [0; N];
let (d, borrow) = sub_n(a, b);
f[0] = self.f & u64::from(borrow).wrapping_neg();
let (d, borrow) = sub_n(&d, &f);
f[0] = self.f & u64::from(borrow).wrapping_neg();
sub_n(&d, &f).0
}
#[inline(always)]
fn fold<const W: usize>(&self, t: &[u64; W]) -> [u64; N] {
let f = self.f;
let mut r = [0u64; N];
let mut c = 0u64;
for i in 0..N {
let x = u128::from(t[N + i]) * u128::from(f) + u128::from(t[i]) + u128::from(c);
r[i] = x as u64;
c = (x >> 64) as u64;
}
let x = u128::from(c) * u128::from(f);
let (a, carry) = add_2(&r, x as u64, (x >> 64) as u64);
let (a, _) = add_2(&a, f & u64::from(carry).wrapping_neg(), 0);
a
}
}
fn limbs_low(x: &Number) -> Option<u64> {
let l = super::limbs_of(x);
match l.len() {
0 => Some(0),
1 => Some(l[0]),
_ => None,
}
}
#[inline(always)]
fn add_1<const N: usize>(a: &[u64; N], x: u64) -> ([u64; N], bool) {
add_2(a, x, 0)
}
#[inline(always)]
fn add_2<const N: usize>(a: &[u64; N], x0: u64, x1: u64) -> ([u64; N], bool) {
let mut r = *a;
let (x, c1) = r[0].overflowing_add(x0);
r[0] = x;
let (y, c2) = r[1].overflowing_add(x1);
let (y, c3) = y.overflowing_add(u64::from(c1));
r[1] = y;
let mut carry = c2 | c3;
for l in r.iter_mut().skip(2) {
let (x, c) = l.overflowing_add(u64::from(carry));
*l = x;
carry = c;
}
(r, carry)
}
#[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 product<const N: usize, const W: usize>(a: &[u64; N], b: &[u64; N]) -> [u64; W] {
let mut r = [0u64; W];
for i in 0..N {
let mut c = 0u64;
for j in 0..N {
let x = u128::from(a[j]) * u128::from(b[i]) + u128::from(r[i + j]) + u128::from(c);
r[i + j] = x as u64;
c = (x >> 64) as u64;
}
r[i + N] = c;
}
r
}
macro_rules! products {
($($n:literal => $w:literal),+) => {
pub(in super::super) trait Product<const N: usize> {
fn mul(&self, a: &[u64; N], b: &[u64; N]) -> [u64; N];
fn sqr(&self, a: &[u64; N]) -> [u64; N];
}
$(impl Product<$n> for Pseudo<$n> {
#[inline(always)]
fn mul(&self, a: &[u64; $n], b: &[u64; $n]) -> [u64; $n] {
#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
if $n == 4 {
return *same(&pseudo_mul_4(same(a), same(b), self.f));
}
if $n > 4 {
let mut t = MaybeUninit::<[u64; $w]>::uninit();
let t = unsafe {
basecase::mul(t.as_mut_ptr().cast(), a.as_ptr(), b.as_ptr(), $n);
t.assume_init()
};
return self.fold::<$w>(&t);
}
self.fold::<$w>(&product::<$n, $w>(a, b))
}
#[inline(always)]
fn sqr(&self, a: &[u64; $n]) -> [u64; $n] {
#[cfg(all(target_arch = "aarch64", target_endian = "little", not(miri)))]
if $n == 4 {
return *same(&pseudo_sqr_4(same(a), self.f));
}
if $n > 4 {
let mut t = MaybeUninit::<[u64; $w]>::uninit();
let t = unsafe {
basecase::sqr(t.as_mut_ptr().cast(), a.as_ptr(), $n);
t.assume_init()
};
return self.fold::<$w>(&t);
}
self.fold::<$w>(&product::<$n, $w>(a, a))
}
})+
};
}
products!(4 => 8, 6 => 12, 9 => 18);
impl<const N: usize> Field for Pseudo<N>
where
Pseudo<N>: Product<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] {
let mut r = [0; N];
r[0] = 1;
r
}
#[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(&pseudo_add_4(same(a), same(b), self.f)),
6 => return *same(&pseudo_add_6(same(a), same(b), self.f)),
9 => return *same(&pseudo_add_9(same(a), same(b), self.f)),
_ => {}
}
self.portable_add(a, b)
}
#[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(&pseudo_sub_4(same(a), same(b), self.f)),
6 => return *same(&pseudo_sub_6(same(a), same(b), self.f)),
9 => return *same(&pseudo_sub_9(same(a), same(b), self.f)),
_ => {}
}
self.portable_sub(a, b)
}
#[inline(always)]
fn neg(&self, a: &[u64; N]) -> [u64; N] {
self.sub(&[0; N], a)
}
#[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] {
Product::mul(self, a, b)
}
#[inline(always)]
fn sqr(&self, a: &[u64; N]) -> [u64; N] {
Product::sqr(self, a)
}
#[inline(always)]
fn mul_word(&self, a: &[u64; N], w: u64, _e: &[u64; N]) -> [u64; N] {
let mut r = [0u64; N];
let mut c = 0u64;
for i in 0..N {
let x = u128::from(a[i]) * u128::from(w) + u128::from(c);
r[i] = x as u64;
c = (x >> 64) as u64;
}
let x = u128::from(c) * u128::from(self.f);
let (r, carry) = add_2(&r, x as u64, (x >> 64) as u64);
add_2(&r, self.f & u64::from(carry).wrapping_neg(), 0).0
}
#[inline(always)]
fn is_zero(&self, a: &[u64; N]) -> bool {
self.canonical(a).iter().fold(0, |x, l| x | l) == 0
}
fn enter(&self, x: &Number) -> [u64; N] {
array_of(&x.rem_euclid(&self.p))
}
fn leave(&self, x: &[u64; N]) -> Number {
number_of(&self.canonical(x), self.p.byte_order)
}
#[inline(always)]
fn modulus(&self) -> &Number {
&self.p
}
}