use super::{Field, array_of, number_of};
use crate::math::numbers::Number;
const BITS: u32 = 58;
const LOW: u64 = (1 << BITS) - 1;
const EIGHT_P: [u64; 9] = {
let mut m = [4 * LOW; 9];
m[0] -= 4;
m
};
pub(in super::super) struct P521 {
p: Number,
}
impl P521 {
pub(in super::super) fn new(p: &Number) -> Self {
P521 { p: p.clone() }
}
fn canonical(&self, a: &[u64; 9]) -> [u64; 9] {
let mut r = *a;
for _ in 0..2 {
for i in 0..8 {
r[i + 1] += r[i] >> BITS;
r[i] &= LOW;
}
r[0] += r[8] >> (BITS - 1);
r[8] &= LOW >> 1;
}
if r[..8].iter().all(|&l| l == LOW) && r[8] == LOW >> 1 {
return [0; 9];
}
r
}
}
#[inline(always)]
fn pin(x: u64) -> u64 {
#[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
{
let mut x = x;
unsafe {
core::arch::asm!("/* {0} */", inout(reg) x, options(pure, nomem, nostack, preserves_flags));
}
x
}
#[cfg(not(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri))))]
x
}
#[inline(always)]
fn wide(a: u64, b: u64) -> u128 {
u128::from(a) * u128::from(b)
}
#[inline(always)]
fn column(a: &[u64; 9], b: &[u64; 9], b2: &[u64; 9], k: usize) -> u128 {
let mut s = 0;
for i in 0..=k {
s += wide(a[i], b[k - i]);
}
for i in k + 1..9 {
s += wide(a[i], b2[k + 9 - i]);
}
s
}
#[inline(always)]
fn square_column(a: &[u64; 9], a2: &[u64; 9], k: usize) -> u128 {
let mut s = 0;
for i in 0..k.div_ceil(2) {
s += wide(a2[i], a[k - i]);
}
for i in k + 1..(k + 10) / 2 {
s += wide(a2[i], a2[k + 9 - i]);
}
if k.is_multiple_of(2) {
s += wide(a[k / 2], a[k / 2]);
} else {
s += wide(a[(k + 9) / 2], a2[(k + 9) / 2]);
}
s
}
#[inline(always)]
fn columns(column: impl Fn(usize) -> u128) -> [u128; 9] {
[
column(0),
column(1),
column(2),
column(3),
column(4),
column(5),
column(6),
column(7),
column(8),
]
}
#[inline(always)]
fn spread(c: &[u128; 9]) -> [u64; 9] {
let mut r = [0; 9];
for i in 0..9 {
let mid = (c[i] >> BITS) as u64 & LOW;
let top = (c[i] >> (2 * BITS)) as u64;
r[i] += c[i] as u64 & LOW;
if i < 8 {
r[i + 1] += mid;
} else {
r[0] += 2 * mid;
}
if i < 7 {
r[i + 2] += top;
} else {
r[i - 7] += 2 * top;
}
}
r
}
#[inline(always)]
fn carry(x: &[u64; 9]) -> [u64; 9] {
let mut r = [0; 9];
r[0] = pin((x[0] & LOW) + 2 * (x[8] >> BITS));
for i in 1..9 {
r[i] = pin((x[i] & LOW) + (x[i - 1] >> BITS));
}
r
}
fn pack(x: &[u64; 9]) -> [u64; 9] {
let mut r = [0; 9];
for (i, &l) in x.iter().enumerate() {
let (at, s) = (BITS as usize * i / 64, BITS as usize * i % 64);
r[at] |= l << s;
if s > 64 - BITS as usize {
r[at + 1] |= l >> (64 - s);
}
}
r
}
fn unpack(x: &[u64; 9]) -> [u64; 9] {
let mut r = [0; 9];
for (i, l) in r.iter_mut().enumerate() {
let (at, s) = (BITS as usize * i / 64, BITS as usize * i % 64);
let mut v = x[at] >> s;
if s > 64 - BITS as usize {
v |= x[at + 1] << (64 - s);
}
*l = v & LOW;
}
r
}
impl Field for P521 {
type E = [u64; 9];
const CHEAP_SQUARES: bool = true;
#[inline(always)]
fn limbs(&self) -> usize {
9
}
#[inline(always)]
fn load(&self, x: &[u64]) -> [u64; 9] {
let mut r = [0; 9];
r.copy_from_slice(&x[..9]);
r
}
#[inline(always)]
fn store(&self, x: &[u64; 9], r: &mut [u64]) {
r[..9].copy_from_slice(x);
}
#[inline(always)]
fn zero(&self) -> [u64; 9] {
[0; 9]
}
#[inline(always)]
fn one(&self) -> [u64; 9] {
let mut r = [0; 9];
r[0] = 1;
r
}
#[inline(always)]
fn add(&self, a: &[u64; 9], b: &[u64; 9]) -> [u64; 9] {
let mut r = [0; 9];
for i in 0..9 {
r[i] = pin(a[i] + b[i]);
}
carry(&r)
}
#[inline(always)]
fn sub(&self, a: &[u64; 9], b: &[u64; 9]) -> [u64; 9] {
let mut r = [0; 9];
for i in 0..9 {
r[i] = pin(a[i] + EIGHT_P[i] - b[i]);
}
carry(&r)
}
#[inline(always)]
fn neg(&self, a: &[u64; 9]) -> [u64; 9] {
self.sub(&[0; 9], a)
}
#[inline(always)]
fn half(&self, a: &[u64; 9]) -> [u64; 9] {
let mask = (a[0] & 1).wrapping_neg();
let mut s = [0; 9];
for i in 0..9 {
let p = if i < 8 { LOW } else { LOW >> 1 };
s[i] = pin(a[i] + (p & mask));
}
let mut r = [0; 9];
for i in 0..8 {
r[i] = pin((s[i] >> 1) + ((s[i + 1] & 1) << (BITS - 1)));
}
r[8] = pin(s[8] >> 1);
r
}
#[inline(always)]
fn mul(&self, a: &[u64; 9], b: &[u64; 9]) -> [u64; 9] {
let mut b2 = [0; 9];
for j in 0..9 {
b2[j] = 2 * b[j];
}
spread(&columns(|k| column(a, b, &b2, k)))
}
#[inline(always)]
fn sqr(&self, a: &[u64; 9]) -> [u64; 9] {
let mut a2 = [0; 9];
for j in 0..9 {
a2[j] = 2 * a[j];
}
spread(&columns(|k| square_column(a, &a2, k)))
}
#[inline(always)]
fn mul_word(&self, a: &[u64; 9], w: u64, _e: &[u64; 9]) -> [u64; 9] {
spread(&columns(|k| wide(a[k], w)))
}
#[inline(always)]
fn is_zero(&self, a: &[u64; 9]) -> bool {
self.canonical(a) == [0; 9]
}
fn enter(&self, x: &Number) -> [u64; 9] {
unpack(&array_of(&x.rem_euclid(&self.p)))
}
fn leave(&self, x: &[u64; 9]) -> Number {
number_of(&pack(&self.canonical(x)), self.p.byte_order)
}
#[inline(always)]
fn modulus(&self) -> &Number {
&self.p
}
}