use ark_serialize::{
CanonicalDeserialize, CanonicalDeserializeWithFlags, CanonicalSerialize,
CanonicalSerializeWithFlags, Compress, EmptyFlags, Flags, SerializationError, Valid, Validate,
};
use ark_std::{
cmp::{Ord, Ordering, PartialOrd},
fmt,
io::{Read, Write},
iter::Chain,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
vec::Vec,
};
use num_traits::{One, Zero};
use zeroize::Zeroize;
use ark_std::rand::{
distributions::{Distribution, Standard},
Rng,
};
use crate::{
biginteger::BigInteger,
fields::{Field, LegendreSymbol, PrimeField},
SqrtPrecomputation, ToConstraintField, UniformRand,
};
pub trait QuadExtConfig: 'static + Send + Sync + Sized {
type BasePrimeField: PrimeField;
type BaseField: Field<BasePrimeField = Self::BasePrimeField>;
type FrobCoeff: Field;
const DEGREE_OVER_BASE_PRIME_FIELD: usize;
const NONRESIDUE: Self::BaseField;
const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff];
#[inline(always)]
fn mul_base_field_by_nonresidue_in_place(fe: &mut Self::BaseField) -> &mut Self::BaseField {
*fe *= &Self::NONRESIDUE;
fe
}
#[inline(always)]
fn mul_base_field_by_nonresidue_and_add(y: &mut Self::BaseField, x: &Self::BaseField) {
Self::mul_base_field_by_nonresidue_in_place(y);
*y += x;
}
#[inline(always)]
fn mul_base_field_by_nonresidue_plus_one_and_add(y: &mut Self::BaseField, x: &Self::BaseField) {
let old_y = *y;
Self::mul_base_field_by_nonresidue_and_add(y, x);
*y += old_y;
}
#[inline(always)]
fn sub_and_mul_base_field_by_nonresidue(y: &mut Self::BaseField, x: &Self::BaseField) {
Self::mul_base_field_by_nonresidue_in_place(y);
let mut result = *x;
result -= &*y;
*y = result;
}
fn mul_base_field_by_frob_coeff(fe: &mut Self::BaseField, power: usize);
}
#[derive(Derivative)]
#[derivative(
Default(bound = "P: QuadExtConfig"),
Hash(bound = "P: QuadExtConfig"),
Clone(bound = "P: QuadExtConfig"),
Copy(bound = "P: QuadExtConfig"),
Debug(bound = "P: QuadExtConfig"),
PartialEq(bound = "P: QuadExtConfig"),
Eq(bound = "P: QuadExtConfig")
)]
pub struct QuadExtField<P: QuadExtConfig> {
pub c0: P::BaseField,
pub c1: P::BaseField,
}
impl<P: QuadExtConfig> QuadExtField<P> {
pub const fn new(c0: P::BaseField, c1: P::BaseField) -> Self {
Self { c0, c1 }
}
pub fn conjugate_in_place(&mut self) -> &mut Self {
self.c1 = -self.c1;
self
}
pub fn norm(&self) -> P::BaseField {
let mut result = self.c1.square();
P::sub_and_mul_base_field_by_nonresidue(&mut result, &self.c0.square());
result
}
pub fn mul_assign_by_basefield(&mut self, element: &P::BaseField) {
self.c0 *= element;
self.c1 *= element;
}
}
impl<P: QuadExtConfig> Zero for QuadExtField<P> {
fn zero() -> Self {
QuadExtField::new(P::BaseField::zero(), P::BaseField::zero())
}
fn is_zero(&self) -> bool {
self.c0.is_zero() && self.c1.is_zero()
}
}
impl<P: QuadExtConfig> One for QuadExtField<P> {
fn one() -> Self {
QuadExtField::new(P::BaseField::one(), P::BaseField::zero())
}
fn is_one(&self) -> bool {
self.c0.is_one() && self.c1.is_zero()
}
}
type BaseFieldIter<P> = <<P as QuadExtConfig>::BaseField as Field>::BasePrimeFieldIter;
impl<P: QuadExtConfig> Field for QuadExtField<P> {
type BasePrimeField = P::BasePrimeField;
type BasePrimeFieldIter = Chain<BaseFieldIter<P>, BaseFieldIter<P>>;
const SQRT_PRECOMP: Option<SqrtPrecomputation<Self>> = None;
const ZERO: Self = Self::new(P::BaseField::ZERO, P::BaseField::ZERO);
const ONE: Self = Self::new(P::BaseField::ONE, P::BaseField::ZERO);
fn extension_degree() -> u64 {
2 * P::BaseField::extension_degree()
}
fn from_base_prime_field(elem: Self::BasePrimeField) -> Self {
let fe = P::BaseField::from_base_prime_field(elem);
Self::new(fe, P::BaseField::ZERO)
}
fn to_base_prime_field_elements(&self) -> Self::BasePrimeFieldIter {
self.c0
.to_base_prime_field_elements()
.chain(self.c1.to_base_prime_field_elements())
}
fn from_base_prime_field_elems(elems: &[Self::BasePrimeField]) -> Option<Self> {
if elems.len() != (Self::extension_degree() as usize) {
return None;
}
let base_ext_deg = P::BaseField::extension_degree() as usize;
Some(Self::new(
P::BaseField::from_base_prime_field_elems(&elems[0..base_ext_deg]).unwrap(),
P::BaseField::from_base_prime_field_elems(&elems[base_ext_deg..]).unwrap(),
))
}
fn double(&self) -> Self {
let mut result = *self;
result.double_in_place();
result
}
fn double_in_place(&mut self) -> &mut Self {
self.c0.double_in_place();
self.c1.double_in_place();
self
}
fn neg_in_place(&mut self) -> &mut Self {
self.c0.neg_in_place();
self.c1.neg_in_place();
self
}
fn square(&self) -> Self {
let mut result = *self;
result.square_in_place();
result
}
#[inline]
fn from_random_bytes_with_flags<F: Flags>(bytes: &[u8]) -> Option<(Self, F)> {
let split_at = bytes.len() / 2;
if let Some(c0) = P::BaseField::from_random_bytes(&bytes[..split_at]) {
if let Some((c1, flags)) =
P::BaseField::from_random_bytes_with_flags(&bytes[split_at..])
{
return Some((QuadExtField::new(c0, c1), flags));
}
}
None
}
#[inline]
fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
Self::from_random_bytes_with_flags::<EmptyFlags>(bytes).map(|f| f.0)
}
fn square_in_place(&mut self) -> &mut Self {
if P::NONRESIDUE == -P::BaseField::ONE {
let c0_copy = self.c0;
let mut v0 = self.c0;
v0 -= &self.c1;
self.c0 += self.c1;
self.c0 *= &v0;
self.c1.double_in_place();
self.c1 *= &c0_copy;
self
} else {
let mut v0 = self.c0 - &self.c1;
let mut v3 = self.c1;
P::sub_and_mul_base_field_by_nonresidue(&mut v3, &self.c0);
let v2 = self.c0 * &self.c1;
v0 *= &v3;
self.c1 = v2;
self.c1.double_in_place();
self.c0 = v2;
P::mul_base_field_by_nonresidue_plus_one_and_add(&mut self.c0, &v0);
self
}
}
fn inverse(&self) -> Option<Self> {
if self.is_zero() {
None
} else {
let v1 = self.c1.square();
let mut v0 = v1;
P::sub_and_mul_base_field_by_nonresidue(&mut v0, &self.c0.square());
v0.inverse().map(|v1| {
let c0 = self.c0 * &v1;
let c1 = -(self.c1 * &v1);
Self::new(c0, c1)
})
}
}
fn inverse_in_place(&mut self) -> Option<&mut Self> {
if let Some(inverse) = self.inverse() {
*self = inverse;
Some(self)
} else {
None
}
}
fn frobenius_map_in_place(&mut self, power: usize) {
self.c0.frobenius_map_in_place(power);
self.c1.frobenius_map_in_place(power);
P::mul_base_field_by_frob_coeff(&mut self.c1, power);
}
fn legendre(&self) -> LegendreSymbol {
self.norm().legendre()
}
fn sqrt(&self) -> Option<Self> {
if self.c1.is_zero() {
if self.c0.legendre().is_qr() {
return self.c0.sqrt().map(|c0| Self::new(c0, P::BaseField::ZERO));
} else {
return (self.c0.div(P::NONRESIDUE))
.sqrt()
.map(|res| Self::new(P::BaseField::ZERO, res));
}
}
let alpha = self.norm();
let mut two_inv = P::BasePrimeField::MODULUS;
two_inv.add_with_carry(&1u64.into());
two_inv.div2();
let two_inv = P::BasePrimeField::from(two_inv);
let two_inv = P::BaseField::from_base_prime_field(two_inv);
alpha.sqrt().and_then(|alpha| {
let mut delta = (alpha + &self.c0) * &two_inv;
if delta.legendre().is_qnr() {
delta -= α
}
let c0 = delta.sqrt().expect("Delta must have a square root");
let c0_inv = c0.inverse().expect("c0 must have an inverse");
let sqrt_cand = Self::new(c0, self.c1 * &two_inv * &c0_inv);
if sqrt_cand.square() == *self {
Some(sqrt_cand)
} else {
#[cfg(debug_assertions)]
{
use crate::fields::LegendreSymbol::*;
if self.legendre() != QuadraticNonResidue {
panic!(
"Input has a square root per its legendre symbol, but it was not found"
)
}
}
None
}
})
}
fn sqrt_in_place(&mut self) -> Option<&mut Self> {
(*self).sqrt().map(|sqrt| {
*self = sqrt;
self
})
}
}
impl<P: QuadExtConfig> Ord for QuadExtField<P> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
match self.c1.cmp(&other.c1) {
Ordering::Greater => Ordering::Greater,
Ordering::Less => Ordering::Less,
Ordering::Equal => self.c0.cmp(&other.c0),
}
}
}
impl<P: QuadExtConfig> PartialOrd for QuadExtField<P> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<P: QuadExtConfig> Zeroize for QuadExtField<P> {
fn zeroize(&mut self) {
self.c0.zeroize();
self.c1.zeroize();
}
}
impl<P: QuadExtConfig> From<u128> for QuadExtField<P> {
fn from(other: u128) -> Self {
Self::new(other.into(), P::BaseField::ZERO)
}
}
impl<P: QuadExtConfig> From<i128> for QuadExtField<P> {
#[inline]
fn from(val: i128) -> Self {
let abs = Self::from(val.unsigned_abs());
if val.is_positive() {
abs
} else {
-abs
}
}
}
impl<P: QuadExtConfig> From<u64> for QuadExtField<P> {
fn from(other: u64) -> Self {
Self::new(other.into(), P::BaseField::ZERO)
}
}
impl<P: QuadExtConfig> From<i64> for QuadExtField<P> {
#[inline]
fn from(val: i64) -> Self {
let abs = Self::from(val.unsigned_abs());
if val.is_positive() {
abs
} else {
-abs
}
}
}
impl<P: QuadExtConfig> From<u32> for QuadExtField<P> {
fn from(other: u32) -> Self {
Self::new(other.into(), P::BaseField::ZERO)
}
}
impl<P: QuadExtConfig> From<i32> for QuadExtField<P> {
#[inline]
fn from(val: i32) -> Self {
let abs = Self::from(val.unsigned_abs());
if val.is_positive() {
abs
} else {
-abs
}
}
}
impl<P: QuadExtConfig> From<u16> for QuadExtField<P> {
fn from(other: u16) -> Self {
Self::new(other.into(), P::BaseField::ZERO)
}
}
impl<P: QuadExtConfig> From<i16> for QuadExtField<P> {
#[inline]
fn from(val: i16) -> Self {
let abs = Self::from(val.unsigned_abs());
if val.is_positive() {
abs
} else {
-abs
}
}
}
impl<P: QuadExtConfig> From<u8> for QuadExtField<P> {
fn from(other: u8) -> Self {
Self::new(other.into(), P::BaseField::ZERO)
}
}
impl<P: QuadExtConfig> From<i8> for QuadExtField<P> {
#[inline]
fn from(val: i8) -> Self {
let abs = Self::from(val.unsigned_abs());
if val.is_positive() {
abs
} else {
-abs
}
}
}
impl<P: QuadExtConfig> From<bool> for QuadExtField<P> {
fn from(other: bool) -> Self {
Self::new(u8::from(other).into(), P::BaseField::ZERO)
}
}
impl<P: QuadExtConfig> Neg for QuadExtField<P> {
type Output = Self;
#[inline]
#[must_use]
fn neg(mut self) -> Self {
self.c0.neg_in_place();
self.c1.neg_in_place();
self
}
}
impl<P: QuadExtConfig> Distribution<QuadExtField<P>> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> QuadExtField<P> {
QuadExtField::new(UniformRand::rand(rng), UniformRand::rand(rng))
}
}
impl<'a, P: QuadExtConfig> Add<&'a QuadExtField<P>> for QuadExtField<P> {
type Output = Self;
#[inline]
fn add(mut self, other: &Self) -> Self {
self += other;
self
}
}
impl<'a, P: QuadExtConfig> Sub<&'a QuadExtField<P>> for QuadExtField<P> {
type Output = Self;
#[inline(always)]
fn sub(mut self, other: &Self) -> Self {
self -= other;
self
}
}
impl<'a, P: QuadExtConfig> Mul<&'a QuadExtField<P>> for QuadExtField<P> {
type Output = Self;
#[inline(always)]
fn mul(mut self, other: &Self) -> Self {
self *= other;
self
}
}
impl<'a, P: QuadExtConfig> Div<&'a QuadExtField<P>> for QuadExtField<P> {
type Output = Self;
#[inline]
fn div(mut self, other: &Self) -> Self {
self.mul_assign(&other.inverse().unwrap());
self
}
}
impl<'a, P: QuadExtConfig> AddAssign<&'a Self> for QuadExtField<P> {
#[inline]
fn add_assign(&mut self, other: &Self) {
self.c0 += &other.c0;
self.c1 += &other.c1;
}
}
impl<'a, P: QuadExtConfig> SubAssign<&'a Self> for QuadExtField<P> {
#[inline]
fn sub_assign(&mut self, other: &Self) {
self.c0 -= &other.c0;
self.c1 -= &other.c1;
}
}
impl_additive_ops_from_ref!(QuadExtField, QuadExtConfig);
impl_multiplicative_ops_from_ref!(QuadExtField, QuadExtConfig);
impl<'a, P: QuadExtConfig> MulAssign<&'a Self> for QuadExtField<P> {
#[inline]
fn mul_assign(&mut self, other: &Self) {
if Self::extension_degree() == 2 {
let c1_input = [self.c0, self.c1];
P::mul_base_field_by_nonresidue_in_place(&mut self.c1);
*self = Self::new(
P::BaseField::sum_of_products(&[self.c0, self.c1], &[other.c0, other.c1]),
P::BaseField::sum_of_products(&c1_input, &[other.c1, other.c0]),
)
} else {
let mut v0 = self.c0;
v0 *= &other.c0;
let mut v1 = self.c1;
v1 *= &other.c1;
self.c1 += &self.c0;
self.c1 *= &(other.c0 + &other.c1);
self.c1 -= &v0;
self.c1 -= &v1;
self.c0 = v1;
P::mul_base_field_by_nonresidue_and_add(&mut self.c0, &v0);
}
}
}
impl<'a, P: QuadExtConfig> DivAssign<&'a Self> for QuadExtField<P> {
#[inline]
fn div_assign(&mut self, other: &Self) {
self.mul_assign(&other.inverse().unwrap());
}
}
impl<P: QuadExtConfig> fmt::Display for QuadExtField<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "QuadExtField({} + {} * u)", self.c0, self.c1)
}
}
impl<P: QuadExtConfig> CanonicalSerializeWithFlags for QuadExtField<P> {
#[inline]
fn serialize_with_flags<W: Write, F: Flags>(
&self,
mut writer: W,
flags: F,
) -> Result<(), SerializationError> {
self.c0.serialize_compressed(&mut writer)?;
self.c1.serialize_with_flags(&mut writer, flags)?;
Ok(())
}
#[inline]
fn serialized_size_with_flags<F: Flags>(&self) -> usize {
self.c0.compressed_size() + self.c1.serialized_size_with_flags::<F>()
}
}
impl<P: QuadExtConfig> CanonicalSerialize for QuadExtField<P> {
#[inline]
fn serialize_with_mode<W: Write>(
&self,
writer: W,
_compress: Compress,
) -> Result<(), SerializationError> {
self.serialize_with_flags(writer, EmptyFlags)
}
#[inline]
fn serialized_size(&self, _compress: Compress) -> usize {
self.serialized_size_with_flags::<EmptyFlags>()
}
}
impl<P: QuadExtConfig> CanonicalDeserializeWithFlags for QuadExtField<P> {
#[inline]
fn deserialize_with_flags<R: Read, F: Flags>(
mut reader: R,
) -> Result<(Self, F), SerializationError> {
let c0 = CanonicalDeserialize::deserialize_compressed(&mut reader)?;
let (c1, flags) = CanonicalDeserializeWithFlags::deserialize_with_flags(&mut reader)?;
Ok((QuadExtField::new(c0, c1), flags))
}
}
impl<P: QuadExtConfig> Valid for QuadExtField<P> {
fn check(&self) -> Result<(), SerializationError> {
self.c0.check()?;
self.c1.check()
}
}
impl<P: QuadExtConfig> CanonicalDeserialize for QuadExtField<P> {
#[inline]
fn deserialize_with_mode<R: Read>(
mut reader: R,
compress: Compress,
validate: Validate,
) -> Result<Self, SerializationError> {
let c0: P::BaseField =
CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?;
let c1: P::BaseField =
CanonicalDeserialize::deserialize_with_mode(&mut reader, compress, validate)?;
Ok(QuadExtField::new(c0, c1))
}
}
impl<P: QuadExtConfig> ToConstraintField<P::BasePrimeField> for QuadExtField<P>
where
P::BaseField: ToConstraintField<P::BasePrimeField>,
{
fn to_field_elements(&self) -> Option<Vec<P::BasePrimeField>> {
let mut res = Vec::new();
let mut c0_elems = self.c0.to_field_elements()?;
let mut c1_elems = self.c1.to_field_elements()?;
res.append(&mut c0_elems);
res.append(&mut c1_elems);
Some(res)
}
}
#[cfg(test)]
mod quad_ext_tests {
use super::*;
use ark_std::test_rng;
use ark_test_curves::{
bls12_381::{Fq, Fq2},
Field,
};
#[test]
fn test_from_base_prime_field_elements() {
let ext_degree = Fq2::extension_degree() as usize;
let max_num_elems_to_test = 4;
for d in 0..max_num_elems_to_test {
if d == ext_degree {
continue;
}
let mut random_coeffs = Vec::<Fq>::new();
for _ in 0..d {
random_coeffs.push(Fq::rand(&mut test_rng()));
}
let res = Fq2::from_base_prime_field_elems(&random_coeffs);
assert_eq!(res, None);
}
let number_of_tests = 10;
for _ in 0..number_of_tests {
let mut random_coeffs = Vec::<Fq>::new();
for _ in 0..ext_degree {
random_coeffs.push(Fq::rand(&mut test_rng()));
}
let actual = Fq2::from_base_prime_field_elems(&random_coeffs).unwrap();
let expected = Fq2::new(random_coeffs[0], random_coeffs[1]);
assert_eq!(actual, expected);
}
}
#[test]
fn test_from_base_prime_field_element() {
let ext_degree = Fq2::extension_degree() as usize;
let max_num_elems_to_test = 10;
for _ in 0..max_num_elems_to_test {
let mut random_coeffs = vec![Fq::zero(); ext_degree];
let random_coeff = Fq::rand(&mut test_rng());
let res = Fq2::from_base_prime_field(random_coeff);
random_coeffs[0] = random_coeff;
assert_eq!(
res,
Fq2::from_base_prime_field_elems(&random_coeffs).unwrap()
);
}
}
}