#![allow(dead_code)]
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use rand::Rng;
use crate::{
fields::{FieldElement, Fq, FQ},
u256::{Error, U256},
u512::U512,
One, Zero,
};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(C)]
pub struct Fq2 {
pub(crate) c0: Fq,
pub(crate) c1: Fq,
}
impl Fq2 {
pub fn new(c0: Fq, c1: Fq) -> Self {
Fq2 { c0, c1 }
}
pub fn scale(&self, by: &Fq) -> Self {
Fq2 {
c0: self.c0 * by,
c1: self.c1 * by,
}
}
#[inline(always)]
pub fn unitary_inverse(&self) -> Fq2 {
Fq2 {
c0: self.c0,
c1: -self.c1,
}
}
#[inline(always)]
pub fn mul_by_nonresidue(&self) -> Self {
Fq2 {
c0: -self.c1.double(),
c1: self.c0,
}
}
#[inline(always)]
pub fn div2(&self) -> Self {
Fq2 {
c0: self.c0.div2(),
c1: self.c1.div2(),
}
}
pub fn real(&self) -> &Fq {
&self.c0
}
pub fn imaginary(&self) -> &Fq {
&self.c1
}
pub fn i() -> Self {
Fq2::new(Fq::zero(), Fq::one())
}
pub fn sqrt(&self) -> Option<Self> {
if self.is_zero() {
return Some(Self::zero());
}
let b = self.c1;
let a = self.c0;
let bb = b.squared();
let aa = a.squared();
let u = aa + bb.double();
let mut y = Fq::zero();
u.sqrt().and_then(|w| {
let v = (a + w).div2();
let m = v.sqrt().map(|t| {
y = t;
});
if m.is_none() {
let v = (a - w).div2();
y = v.sqrt()?;
}
let y2 = y.double();
let z1 = if y.is_zero() {
w.div2().sqrt()?
} else {
b * y2.inverse()?
};
let z0 = y;
let sqrt_cand = Self::new(z0, z1);
if sqrt_cand.squared() == *self {
Some(sqrt_cand)
} else {
None
}
})
}
pub fn to_u512(self) -> U512 {
let c0: U256 = (*self.real()).into();
let c1: U256 = (*self.imaginary()).into();
U512::new(&c1, &c0, &FQ)
}
pub fn to_slice(self) -> [u8; 64] {
let mut res = [0u8; 64];
let b1 = self.c1.to_slice();
let b0 = self.c0.to_slice();
res[..32].copy_from_slice(&b1);
res[32..].copy_from_slice(&b0);
res
}
pub fn from_slice(s: &[u8]) -> Result<Fq2, Error> {
if s.len() != 64 {
return Err(Error::InvalidLength {
expected: 64,
actual: s.len(),
});
}
let c1 = Fq::from_slice(&s[..32]).unwrap();
let c0 = Fq::from_slice(&s[32..]).unwrap();
Ok(Fq2 { c0, c1 })
}
#[inline]
pub fn neg_inplace(&self) -> Fq2 {
Fq2 {
c0: self.c0.neg_inplace(),
c1: self.c1.neg_inplace(),
}
}
#[inline]
pub fn sub_inplace(&self, rhs: &Fq2) -> Fq2 {
Fq2 {
c0: self.c0.sub_inplace(&rhs.c0),
c1: self.c1.sub_inplace(&rhs.c1),
}
}
#[inline]
pub fn add_inplace(&self, rhs: &Fq2) -> Fq2 {
Fq2 {
c0: self.c0.add_inplace(&rhs.c0),
c1: self.c1.add_inplace(&rhs.c1),
}
}
#[inline]
pub fn mul_inplace(&self, b: &Fq2) -> Fq2 {
let a = self;
Fq2 {
c0: Fq::sum_of_products(&[a.c0, -a.c1.double()], &[b.c0, b.c1]),
c1: Fq::sum_of_products(&[a.c0, a.c1], &[b.c1, b.c0]),
}
}
}
impl_binops_additive!(Fq2, Fq2);
impl_binops_multiplicative!(Fq2, Fq2);
impl_binops_negative!(Fq2);
impl Zero for Fq2 {
#[inline]
fn zero() -> Self {
Fq2 {
c0: Fq::zero(),
c1: Fq::zero(),
}
}
#[inline(always)]
fn is_zero(&self) -> bool {
self.c0.is_zero() && self.c1.is_zero()
}
}
impl One for Fq2 {
#[inline]
fn one() -> Self {
Fq2 {
c0: Fq::one(),
c1: Fq::zero(),
}
}
}
impl FieldElement for Fq2 {
fn random<R: Rng>(rng: &mut R) -> Self {
Fq2 {
c0: Fq::random(rng),
c1: Fq::random(rng),
}
}
#[inline(always)]
fn double(&self) -> Self {
Fq2 {
c0: self.c0.double(),
c1: self.c1.double(),
}
}
#[inline(always)]
fn triple(&self) -> Self {
Fq2 {
c0: self.c0.triple(),
c1: self.c1.triple(),
}
}
fn squared(&self) -> Self {
let a0 = self.c0;
let a1 = self.c1;
let v0 = a0 * a1;
Fq2 {
c0: (a0 + a1) * (a0 - a1.double()) + v0,
c1: v0.double(),
}
}
fn inverse(&self) -> Option<Self> {
(self.c0.squared() + self.c1.squared().double())
.inverse()
.map(|t| Self::new(self.c0 * t, -(self.c1 * t)))
}
}