use core::ops::{Add, Mul, Neg, Sub};
use crate::internal::zeroize::Zeroize;
use super::BYTES_SIZE;
const FE_SIZE: usize = 26;
#[derive(Clone, Debug)]
pub struct FieldElem {
limbs: [i64; FE_SIZE],
}
impl Zeroize for FieldElem {
fn zeroize(&mut self) {
self.limbs.zeroize();
}
}
impl Drop for FieldElem {
fn drop(&mut self) {
self.zeroize();
}
}
impl FieldElem {
#[inline]
pub const fn zero() -> Self {
Self {
limbs: [0i64; FE_SIZE],
}
}
#[inline]
pub const fn one() -> Self {
let mut limbs = [0i64; FE_SIZE];
limbs[0] = 1;
Self { limbs }
}
pub fn unpack(bytes: &[u8; BYTES_SIZE]) -> Self {
let mut result = Self::zero();
for i in 0..FE_SIZE {
result.limbs[i] = bytes[2 * i] as i64 + ((bytes[2 * i + 1] as i64) << 8);
}
result.limbs[25] &= 0x3fff;
result
}
pub fn pack(&self) -> [u8; BYTES_SIZE] {
let mut t = self.clone();
t.reduce();
let mut result = [0u8; BYTES_SIZE];
for i in 0..FE_SIZE {
result[2 * i] = (t.limbs[i] & 0xff) as u8;
result[2 * i + 1] = (t.limbs[i] >> 8) as u8;
}
result
}
#[inline]
pub fn cswap(&mut self, cond: i64, other: &mut Self) {
debug_assert!(cond == 0 || cond == 1);
let mask = !(cond - 1);
for i in 0..FE_SIZE {
let t = mask & (self.limbs[i] ^ other.limbs[i]);
self.limbs[i] ^= t;
other.limbs[i] ^= t;
}
}
#[inline]
pub fn carry(&mut self) {
let mut c: i64;
for i in 0..FE_SIZE {
self.limbs[i] += 1i64 << 16;
c = self.limbs[i] >> 16;
self.limbs[(i + 1) * ((i < 25) as usize)] += c - 1 + 67 * (c - 1) * ((i == 25) as i64);
self.limbs[i] -= c << 16;
}
}
pub fn reduce(&mut self) {
self.carry();
self.carry();
self.carry();
let mut m = Self::zero();
for _ in 0..3 {
m.limbs[0] = self.limbs[0] - 0xffef;
for j in 1..25 {
m.limbs[j] = self.limbs[j] - 0xffff - ((m.limbs[j - 1] >> 16) & 1);
m.limbs[j - 1] &= 0xffff;
}
m.limbs[25] = self.limbs[25] - 0x3fff - ((m.limbs[24] >> 16) & 1);
m.limbs[24] &= 0xffff;
let b = (m.limbs[25] >> 16) & 1;
m.limbs[25] &= 0xffff;
self.cswap(1 - b, &mut m);
}
}
#[inline]
pub fn add_assign(&mut self, other: &Self) {
for i in 0..FE_SIZE {
self.limbs[i] += other.limbs[i];
}
}
#[inline]
pub fn sub_assign(&mut self, other: &Self) {
for i in 0..FE_SIZE {
self.limbs[i] -= other.limbs[i];
}
}
pub fn square(&self) -> Self {
self.mul(self)
}
pub fn mul(&self, other: &Self) -> Self {
let mut u: i64;
let mut result = Self::zero();
for i in 0..FE_SIZE {
u = 0;
for j in 0..=i {
u += self.limbs[j] * other.limbs[i - j];
}
for j in (i + 1)..FE_SIZE {
u += 68 * self.limbs[j] * other.limbs[i + FE_SIZE - j];
}
result.limbs[i] = u;
}
result.carry();
result.carry();
result
}
pub fn is_zero(&self) -> bool {
let mut t = self.clone();
t.reduce();
for limb in t.limbs.iter() {
if *limb != 0 {
return false;
}
}
true
}
pub fn parity(&self) -> u8 {
let packed = self.pack();
packed[0] & 1
}
pub fn inv(&self) -> Self {
let mut result = self.clone();
for i in (0..413).rev() {
result = result.square();
if i != 1 && i != 4 {
result = result.mul(self);
}
}
result
}
}
impl Add for &FieldElem {
type Output = FieldElem;
fn add(self, other: &FieldElem) -> FieldElem {
let mut result = self.clone();
result.add_assign(other);
result
}
}
impl Sub for &FieldElem {
type Output = FieldElem;
fn sub(self, other: &FieldElem) -> FieldElem {
let mut result = self.clone();
result.sub_assign(other);
result
}
}
impl Mul for &FieldElem {
type Output = FieldElem;
fn mul(self, other: &FieldElem) -> FieldElem {
self.mul(other)
}
}
impl PartialEq for FieldElem {
fn eq(&self, other: &Self) -> bool {
let mut a = self.clone();
let mut b = other.clone();
a.reduce();
b.reduce();
a.limbs == b.limbs
}
}
impl Eq for FieldElem {}
impl Neg for &FieldElem {
type Output = FieldElem;
fn neg(self) -> FieldElem {
let mut result = FieldElem::zero();
for i in 0..FE_SIZE {
result.limbs[i] = -self.limbs[i];
}
result
}
}
impl Neg for FieldElem {
type Output = FieldElem;
fn neg(self) -> FieldElem {
-&self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pack_unpack() {
let mut bytes = [0u8; BYTES_SIZE];
bytes[0] = 0x12;
bytes[1] = 0x34;
bytes[50] = 0xAB;
bytes[51] = 0x3F;
let fe = FieldElem::unpack(&bytes);
let repacked = fe.pack();
let mut expected = bytes;
expected[51] &= 0x3F;
assert_eq!(repacked, expected);
}
#[test]
fn test_add_sub_inverse() {
let mut a_bytes = [0u8; BYTES_SIZE];
a_bytes[0] = 100;
let a = FieldElem::unpack(&a_bytes);
let mut b_bytes = [0u8; BYTES_SIZE];
b_bytes[0] = 50;
let b = FieldElem::unpack(&b_bytes);
let sum = &a + &b;
let diff = &sum - &b;
assert_eq!(a, diff);
}
#[test]
fn test_mul_one() {
let mut a_bytes = [0u8; BYTES_SIZE];
a_bytes[0] = 42;
let a = FieldElem::unpack(&a_bytes);
let one = FieldElem::one();
let prod = a.mul(&one);
assert_eq!(a, prod);
}
#[test]
fn test_mul_zero() {
let mut a_bytes = [0u8; BYTES_SIZE];
a_bytes[0] = 42;
let a = FieldElem::unpack(&a_bytes);
let zero = FieldElem::zero();
let prod = a.mul(&zero);
assert!(prod.is_zero());
}
#[test]
fn test_square() {
let mut a_bytes = [0u8; BYTES_SIZE];
a_bytes[0] = 7;
let a = FieldElem::unpack(&a_bytes);
let sq = a.square();
let mul = a.mul(&a);
assert_eq!(sq, mul);
}
#[test]
fn test_cswap() {
let mut a_bytes = [0u8; BYTES_SIZE];
a_bytes[0] = 1;
let mut a = FieldElem::unpack(&a_bytes);
let mut b_bytes = [0u8; BYTES_SIZE];
b_bytes[0] = 2;
let mut b = FieldElem::unpack(&b_bytes);
let a_orig = a.clone();
let b_orig = b.clone();
a.cswap(0, &mut b);
assert_eq!(a, a_orig);
assert_eq!(b, b_orig);
a.cswap(1, &mut b);
assert_eq!(a, b_orig);
assert_eq!(b, a_orig);
}
}