use std::fmt::{Debug, Display, Formatter}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use num::bigint::BigUint; use serde::{Deserialize, Serialize}; use crate::extension::{Extendable, FieldExtension, Frobenius, OEF}; use crate::ops::Square; use crate::types::Field; #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(bound = "")] pub struct QuadraticExtension>(pub [F; 2]); impl> Default for QuadraticExtension { fn default() -> Self { Self::ZERO } } impl> OEF<2> for QuadraticExtension { const W: F = F::W; const DTH_ROOT: F = F::DTH_ROOT; } impl> Frobenius<2> for QuadraticExtension {} impl> FieldExtension<2> for QuadraticExtension { type BaseField = F; fn to_basefield_array(&self) -> [F; 2] { self.0 } fn from_basefield_array(arr: [F; 2]) -> Self { Self(arr) } fn from_basefield(x: F) -> Self { x.into() } } impl> From for QuadraticExtension { fn from(x: F) -> Self { Self([x, F::ZERO]) } } impl> Field for QuadraticExtension { const ZERO: Self = Self([F::ZERO; 2]); const ONE: Self = Self([F::ONE, F::ZERO]); const TWO: Self = Self([F::TWO, F::ZERO]); const NEG_ONE: Self = Self([F::NEG_ONE, F::ZERO]); // `p^2 - 1 = (p - 1)(p + 1)`. The `p - 1` term has a two-adicity of `F::TWO_ADICITY`. As // long as `F::TWO_ADICITY >= 2`, `p` can be written as `4n + 1`, so `p + 1` can be written as // `2(2n + 1)`, which has a 2-adicity of 1. const TWO_ADICITY: usize = F::TWO_ADICITY + 1; const CHARACTERISTIC_TWO_ADICITY: usize = F::CHARACTERISTIC_TWO_ADICITY; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self(F::EXT_MULTIPLICATIVE_GROUP_GENERATOR); const POWER_OF_TWO_GENERATOR: Self = Self(F::EXT_POWER_OF_TWO_GENERATOR); const BITS: usize = F::BITS * 2; fn order() -> BigUint { F::order() * F::order() } fn characteristic() -> BigUint { F::characteristic() } // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. fn try_inverse(&self) -> Option { if self.is_zero() { return None; } let a_pow_r_minus_1 = self.frobenius(); let a_pow_r = a_pow_r_minus_1 * *self; debug_assert!(FieldExtension::<2>::is_in_basefield(&a_pow_r)); Some(FieldExtension::<2>::scalar_mul( &a_pow_r_minus_1, a_pow_r.0[0].inverse(), )) } fn from_noncanonical_biguint(n: BigUint) -> Self { F::from_noncanonical_biguint(n).into() } fn from_canonical_u64(n: u64) -> Self { F::from_canonical_u64(n).into() } fn from_noncanonical_u128(n: u128) -> Self { F::from_noncanonical_u128(n).into() } #[cfg(feature = "rand")] fn rand_from_rng(rng: &mut R) -> Self { Self([F::rand_from_rng(rng), F::rand_from_rng(rng)]) } } impl> Display for QuadraticExtension { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{} + {}*a", self.0[0], self.0[1]) } } impl> Debug for QuadraticExtension { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(self, f) } } impl> Neg for QuadraticExtension { type Output = Self; #[inline] fn neg(self) -> Self { Self([-self.0[0], -self.0[1]]) } } impl> Add for QuadraticExtension { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self { Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]]) } } impl> AddAssign for QuadraticExtension { fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } } impl> Sum for QuadraticExtension { fn sum>(iter: I) -> Self { iter.fold(Self::ZERO, |acc, x| acc + x) } } impl> Sub for QuadraticExtension { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self { Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]]) } } impl> SubAssign for QuadraticExtension { #[inline] fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } impl> Mul for QuadraticExtension { type Output = Self; #[inline] default fn mul(self, rhs: Self) -> Self { let Self([a0, a1]) = self; let Self([b0, b1]) = rhs; let c0 = a0 * b0 + >::W * a1 * b1; let c1 = a0 * b1 + a1 * b0; Self([c0, c1]) } } impl> MulAssign for QuadraticExtension { #[inline] fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; } } impl> Square for QuadraticExtension { #[inline(always)] fn square(&self) -> Self { // Specialising mul reduces the computation of c1 from 2 muls // and one add to one mul and a shift let Self([a0, a1]) = *self; let c0 = a0.square() + >::W * a1.square(); let c1 = a0 * a1.double(); Self([c0, c1]) } } impl> Product for QuadraticExtension { fn product>(iter: I) -> Self { iter.fold(Self::ONE, |acc, x| acc * x) } } impl> Div for QuadraticExtension { type Output = Self; #[allow(clippy::suspicious_arithmetic_impl)] fn div(self, rhs: Self) -> Self::Output { self * rhs.inverse() } } impl> DivAssign for QuadraticExtension { fn div_assign(&mut self, rhs: Self) { *self = *self / rhs; } } #[cfg(test)] mod tests { mod goldilocks { use crate::{test_field_arithmetic, test_field_extension}; test_field_extension!(crate::goldilocks_field::GoldilocksField, 2); test_field_arithmetic!( crate::extension::quadratic::QuadraticExtension< crate::goldilocks_field::GoldilocksField, > ); } }