use super::fp2::Fp2;
use crate::field::Goldilocks;
use core::ops::{Add, Mul, Neg, Sub};
const SEVEN: Goldilocks = Goldilocks::new(7);
const W_FROB: Goldilocks = Goldilocks::new(0x0001000000000000);
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct Fp4 {
pub c0: Goldilocks,
pub c1: Goldilocks,
pub c2: Goldilocks,
pub c3: Goldilocks,
}
impl Fp4 {
pub const ZERO: Self = Self {
c0: Goldilocks::ZERO,
c1: Goldilocks::ZERO,
c2: Goldilocks::ZERO,
c3: Goldilocks::ZERO,
};
pub const ONE: Self = Self {
c0: Goldilocks::ONE,
c1: Goldilocks::ZERO,
c2: Goldilocks::ZERO,
c3: Goldilocks::ZERO,
};
#[inline]
pub const fn new(c0: Goldilocks, c1: Goldilocks, c2: Goldilocks, c3: Goldilocks) -> Self {
Self { c0, c1, c2, c3 }
}
#[inline]
pub const fn from_base(a: Goldilocks) -> Self {
Self {
c0: a,
c1: Goldilocks::ZERO,
c2: Goldilocks::ZERO,
c3: Goldilocks::ZERO,
}
}
#[inline]
pub const fn from_fp2(x: Fp2) -> Self {
Self {
c0: x.re,
c1: Goldilocks::ZERO,
c2: x.im,
c3: Goldilocks::ZERO,
}
}
#[inline]
pub fn to_fp2_pair(self) -> (Fp2, Fp2) {
(Fp2::new(self.c0, self.c2), Fp2::new(self.c1, self.c3))
}
#[inline]
pub fn conj(self) -> Self {
Self {
c0: self.c0,
c1: -self.c1,
c2: self.c2,
c3: -self.c3,
}
}
pub fn sqr(self) -> Self {
let a0 = self.c0;
let a1 = self.c1;
let a2 = self.c2;
let a3 = self.c3;
let s0 = a0.square();
let s1 = a0 * a1;
let s1 = s1 + s1;
let s2 = a1.square() + a0 * a2 + a0 * a2;
let s3 = a0 * a3 + a1 * a2;
let s3 = s3 + s3;
let s4 = a2.square() + a1 * a3 + a1 * a3;
let s5 = a2 * a3;
let s5 = s5 + s5;
let s6 = a3.square();
Self {
c0: s0 + SEVEN * s4,
c1: s1 + SEVEN * s5,
c2: s2 + SEVEN * s6,
c3: s3,
}
}
pub fn norm_fp2(self) -> Fp2 {
let (a, b) = self.to_fp2_pair();
let a_sq = a.sqr();
let b_sq = b.sqr();
let u_b_sq = Fp2::new(SEVEN * b_sq.im, b_sq.re);
a_sq - u_b_sq
}
pub fn norm(self) -> Goldilocks {
self.norm_fp2().norm()
}
pub fn inv(self) -> Self {
let (a, b) = self.to_fp2_pair();
let n = self.norm_fp2();
let n_inv = n.inv();
let r_a = a * n_inv;
let r_b = -(b * n_inv);
Self {
c0: r_a.re,
c1: r_b.re,
c2: r_a.im,
c3: r_b.im,
}
}
pub fn frobenius(self) -> Self {
Self {
c0: self.c0,
c1: W_FROB * self.c1,
c2: -self.c2,
c3: -(W_FROB * self.c3),
}
}
}
impl core::fmt::Debug for Fp4 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"Fp4({:?}, {:?}, {:?}, {:?})",
self.c0, self.c1, self.c2, self.c3
)
}
}
impl Add for Fp4 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self {
c0: self.c0 + rhs.c0,
c1: self.c1 + rhs.c1,
c2: self.c2 + rhs.c2,
c3: self.c3 + rhs.c3,
}
}
}
impl Sub for Fp4 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self {
c0: self.c0 - rhs.c0,
c1: self.c1 - rhs.c1,
c2: self.c2 - rhs.c2,
c3: self.c3 - rhs.c3,
}
}
}
impl Mul for Fp4 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
let a0 = self.c0;
let a1 = self.c1;
let a2 = self.c2;
let a3 = self.c3;
let b0 = rhs.c0;
let b1 = rhs.c1;
let b2 = rhs.c2;
let b3 = rhs.c3;
let d0 = a0 * b0;
let d1 = a0 * b1 + a1 * b0;
let d2 = a0 * b2 + a1 * b1 + a2 * b0;
let d3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
let d4 = a1 * b3 + a2 * b2 + a3 * b1;
let d5 = a2 * b3 + a3 * b2;
let d6 = a3 * b3;
Self {
c0: d0 + SEVEN * d4,
c1: d1 + SEVEN * d5,
c2: d2 + SEVEN * d6,
c3: d3,
}
}
}
impl Neg for Fp4 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self {
c0: -self.c0,
c1: -self.c1,
c2: -self.c2,
c3: -self.c3,
}
}
}