#![cfg_attr(not(feature = "ed25519"), allow(dead_code))]
pub(crate) const MASK: u64 = (1 << 51) - 1;
pub(crate) const P_INV: super::modinv::Modulus = super::modinv::Modulus::new([0xffff_ffff_ffff_ffed, u64::MAX, u64::MAX, 0x7fff_ffff_ffff_ffff]);
#[derive(Clone, Copy, Debug)]
pub(crate) struct Fe(pub [u64; 5]);
#[inline(always)]
fn load8(b: &[u8], i: usize) -> u64 {
let mut x = [0u8; 8];
let end = (i + 8).min(b.len());
x[..end - i].copy_from_slice(&b[i..end]);
u64::from_le_bytes(x)
}
impl Fe {
pub const ZERO: Fe = Fe([0; 5]);
pub const ONE: Fe = Fe([1, 0, 0, 0, 0]);
pub fn from_u64(v: u64) -> Fe {
Fe([v & MASK, v >> 51, 0, 0, 0])
}
pub fn from_bytes(b: &[u8; 32]) -> Fe {
Fe([
load8(b, 0) & MASK,
(load8(b, 6) >> 3) & MASK,
(load8(b, 12) >> 6) & MASK,
(load8(b, 19) >> 1) & MASK,
(load8(b, 24) >> 12) & MASK,
])
}
#[inline(always)]
fn carry(mut h: [u64; 5]) -> Fe {
let mut c = h[0] >> 51;
h[0] &= MASK;
h[1] += c;
c = h[1] >> 51;
h[1] &= MASK;
h[2] += c;
c = h[2] >> 51;
h[2] &= MASK;
h[3] += c;
c = h[3] >> 51;
h[3] &= MASK;
h[4] += c;
c = h[4] >> 51;
h[4] &= MASK;
h[0] += c * 19;
c = h[0] >> 51;
h[0] &= MASK;
h[1] += c;
Fe(h)
}
#[inline(never)]
pub fn to_bytes(self) -> [u8; 32] {
let mut h = Fe::carry(self.0).0;
let mut q = (h[0] + 19) >> 51;
q = (h[1] + q) >> 51;
q = (h[2] + q) >> 51;
q = (h[3] + q) >> 51;
q = (h[4] + q) >> 51;
h[0] += 19 * q;
for i in 0..4 {
h[i + 1] += h[i] >> 51;
h[i] &= MASK;
}
h[4] &= MASK;
let v = [h[0] | (h[1] << 51), (h[1] >> 13) | (h[2] << 38), (h[2] >> 26) | (h[3] << 25), (h[3] >> 39) | (h[4] << 12)];
let mut out = [0u8; 32];
for i in 0..4 {
out[8 * i..8 * i + 8].copy_from_slice(&v[i].to_le_bytes());
}
out
}
#[inline(never)]
pub fn add(self, o: Fe) -> Fe {
let a = self.0;
let b = o.0;
Fe::carry([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3], a[4] + b[4]])
}
#[inline(never)]
pub fn sub(self, o: Fe) -> Fe {
const P4: [u64; 5] = [0x1f_ffff_ffff_ffb4, 0x1f_ffff_ffff_fffc, 0x1f_ffff_ffff_fffc, 0x1f_ffff_ffff_fffc, 0x1f_ffff_ffff_fffc];
let a = self.0;
let b = o.0;
Fe::carry([a[0] + P4[0] - b[0], a[1] + P4[1] - b[1], a[2] + P4[2] - b[2], a[3] + P4[3] - b[3], a[4] + P4[4] - b[4]])
}
#[inline(always)]
pub fn add_nc(self, o: Fe) -> Fe {
let (a, b) = (self.0, o.0);
Fe([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3], a[4] + b[4]])
}
#[inline(always)]
pub fn sub_nc(self, o: Fe) -> Fe {
const P4: [u64; 5] = [0x1f_ffff_ffff_ffb4, 0x1f_ffff_ffff_fffc, 0x1f_ffff_ffff_fffc, 0x1f_ffff_ffff_fffc, 0x1f_ffff_ffff_fffc];
let (a, b) = (self.0, o.0);
Fe([a[0] + P4[0] - b[0], a[1] + P4[1] - b[1], a[2] + P4[2] - b[2], a[3] + P4[3] - b[3], a[4] + P4[4] - b[4]])
}
pub fn neg(self) -> Fe {
Fe::ZERO.sub(self)
}
#[inline(always)]
fn reduce_wide(t: [u128; 5]) -> Fe {
let mut r = [0u64; 5];
let mut c = t[0] >> 51;
r[0] = t[0] as u64 & MASK;
let t1 = t[1] + c;
c = t1 >> 51;
r[1] = t1 as u64 & MASK;
let t2 = t[2] + c;
c = t2 >> 51;
r[2] = t2 as u64 & MASK;
let t3 = t[3] + c;
c = t3 >> 51;
r[3] = t3 as u64 & MASK;
let t4 = t[4] + c;
c = t4 >> 51;
r[4] = t4 as u64 & MASK;
r[0] += c as u64 * 19;
r[1] += r[0] >> 51;
r[0] &= MASK;
Fe(r)
}
#[inline(never)]
pub fn mul(self, o: Fe) -> Fe {
self.mul_inline(o)
}
#[inline(never)]
pub fn square(self) -> Fe {
self.square_inline()
}
#[inline(always)]
pub fn mul_inline(self, o: Fe) -> Fe {
let a = self.0;
let b = o.0;
let m = |x: u64, y: u64| x as u128 * y as u128;
let (b1, b2, b3, b4) = (b[1] * 19, b[2] * 19, b[3] * 19, b[4] * 19);
Fe::reduce_wide([
m(a[0], b[0]) + m(a[1], b4) + m(a[2], b3) + m(a[3], b2) + m(a[4], b1),
m(a[0], b[1]) + m(a[1], b[0]) + m(a[2], b4) + m(a[3], b3) + m(a[4], b2),
m(a[0], b[2]) + m(a[1], b[1]) + m(a[2], b[0]) + m(a[3], b4) + m(a[4], b3),
m(a[0], b[3]) + m(a[1], b[2]) + m(a[2], b[1]) + m(a[3], b[0]) + m(a[4], b4),
m(a[0], b[4]) + m(a[1], b[3]) + m(a[2], b[2]) + m(a[3], b[1]) + m(a[4], b[0]),
])
}
#[inline(always)]
pub fn square_inline(self) -> Fe {
let a = self.0;
let m = |x: u64, y: u64| x as u128 * y as u128;
let (d0, d1, d2) = (2 * a[0], 2 * a[1], 2 * a[2]);
let (a3_19, a4_19) = (19 * a[3], 19 * a[4]);
Fe::reduce_wide([
m(a[0], a[0]) + m(d1, a4_19) + m(d2, a3_19),
m(d0, a[1]) + m(d2, a4_19) + m(a[3], a3_19),
m(d0, a[2]) + m(a[1], a[1]) + m(2 * a[3], a4_19),
m(d0, a[3]) + m(d1, a[2]) + m(a[4], a4_19),
m(d0, a[4]) + m(d1, a[3]) + m(a[2], a[2]),
])
}
pub fn pow2k(self, k: u32) -> Fe {
let mut r = self;
for _ in 0..k {
r = r.square();
}
r
}
pub fn mul_small(self, k: u32) -> Fe {
let a = self.0;
let k = k as u128;
Fe::reduce_wide([a[0] as u128 * k, a[1] as u128 * k, a[2] as u128 * k, a[3] as u128 * k, a[4] as u128 * k])
}
fn pow_chain(self) -> Fe {
let z2 = self.square();
let z9 = z2.pow2k(2).mul(self);
let z11 = z9.mul(z2);
let z2_5_0 = z11.square().mul(z9);
let z2_10_0 = z2_5_0.pow2k(5).mul(z2_5_0);
let z2_20_0 = z2_10_0.pow2k(10).mul(z2_10_0);
let z2_40_0 = z2_20_0.pow2k(20).mul(z2_20_0);
let z2_50_0 = z2_40_0.pow2k(10).mul(z2_10_0);
let z2_100_0 = z2_50_0.pow2k(50).mul(z2_50_0);
let z2_200_0 = z2_100_0.pow2k(100).mul(z2_100_0);
z2_200_0.pow2k(50).mul(z2_50_0)
}
pub fn invert(self) -> Fe {
let b = self.to_bytes();
let limbs: [u64; 4] = core::array::from_fn(|i| u64::from_le_bytes(b[8 * i..8 * i + 8].try_into().unwrap()));
let inv = super::modinv::invert(&limbs, &P_INV);
let mut out = [0u8; 32];
for (i, l) in inv.iter().enumerate() {
out[8 * i..8 * i + 8].copy_from_slice(&l.to_le_bytes());
}
Fe::from_bytes(&out)
}
pub fn pow22523(self) -> Fe {
self.pow_chain().pow2k(2).mul(self)
}
pub fn is_negative(self) -> u8 {
self.to_bytes()[0] & 1
}
pub fn is_zero(self) -> bool {
self.to_bytes().iter().fold(0, |a, b| a | b) == 0
}
pub fn ct_eq(self, o: Fe) -> bool {
crate::util::ct_eq(&self.to_bytes(), &o.to_bytes())
}
#[inline(always)]
pub fn select(self, o: Fe, flag: u64) -> Fe {
let m = crate::util::ct_mask(flag);
let mut l = self.0;
for (v, w) in l.iter_mut().zip(o.0) {
*v ^= m & (*v ^ w);
}
Fe(l)
}
#[inline(always)]
pub fn cswap(a: &mut Fe, b: &mut Fe, flag: u64) {
let m = crate::util::ct_mask(flag);
for i in 0..5 {
let t = m & (a.0[i] ^ b.0[i]);
a.0[i] ^= t;
b.0[i] ^= t;
}
}
}