use crate::fp512bn::big;
use crate::fp512bn::big::BIG;
use crate::fp512bn::dbig::DBIG;
use crate::fp512bn::fp;
use crate::fp512bn::fp::FP;
use crate::fp512bn::rom;
use crate::rand::RAND;
#[derive(Copy, Clone)]
pub struct FP2 {
a: FP,
b: FP,
}
#[cfg(feature = "std")]
impl std::fmt::Debug for FP2 {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "{}", self.tostring())
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for FP2 {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "{}", self.tostring())
}
}
impl FP2 {
pub const fn new() -> FP2 {
FP2 {
a: FP::new(),
b: FP::new(),
}
}
pub fn new_int(a: isize) -> FP2 {
let mut f = FP2::new();
f.a.copy(&FP::new_int(a));
f.b.zero();
f
}
pub fn new_ints(a: isize, b: isize) -> FP2 {
let mut f = FP2::new();
f.a.copy(&FP::new_int(a));
f.b.copy(&FP::new_int(b));
f
}
pub fn new_copy(x: &FP2) -> FP2 {
let mut f = FP2::new();
f.a.copy(&x.a);
f.b.copy(&x.b);
f
}
pub fn new_fps(c: &FP, d: &FP) -> FP2 {
let mut f = FP2::new();
f.a.copy(c);
f.b.copy(d);
f
}
pub fn new_bigs(c: &BIG, d: &BIG) -> FP2 {
let mut f = FP2::new();
f.a.copy(&FP::new_big(c));
f.b.copy(&FP::new_big(d));
f
}
pub fn new_fp(c: &FP) -> FP2 {
let mut f = FP2::new();
f.a.copy(c);
f.b.zero();
f
}
pub fn new_big(c: &BIG) -> FP2 {
let mut f = FP2::new();
f.a.copy(&FP::new_big(c));
f.b.zero();
f
}
pub fn new_rand(rng: &mut impl RAND) -> FP2 {
FP2::new_fps(&FP::new_rand(rng),&FP::new_rand(rng))
}
pub fn reduce(&mut self) {
self.a.reduce();
self.b.reduce();
}
pub fn norm(&mut self) {
self.a.norm();
self.b.norm();
}
pub fn iszilch(&self) -> bool {
self.a.iszilch() && self.b.iszilch()
}
pub fn islarger(&self) -> isize {
if self.iszilch() {
return 0;
}
let cmp=self.b.islarger();
if cmp!=0 {
return cmp;
}
self.a.islarger()
}
pub fn tobytes(&self,bf: &mut [u8]) {
const MB:usize = big::MODBYTES as usize;
let mut t: [u8; MB] = [0; MB];
self.b.tobytes(&mut t);
for i in 0..MB {
bf[i]=t[i];
}
self.a.tobytes(&mut t);
for i in 0..MB {
bf[i+MB]=t[i];
}
}
pub fn frombytes(bf: &[u8]) -> FP2 {
const MB:usize = big::MODBYTES as usize;
let mut t: [u8; MB] = [0; MB];
for i in 0..MB {
t[i]=bf[i];
}
let tb=FP::frombytes(&t);
for i in 0..MB {
t[i]=bf[i+MB];
}
let ta=FP::frombytes(&t);
FP2::new_fps(&ta,&tb)
}
pub fn cmove(&mut self, g: &FP2, d: isize) {
self.a.cmove(&g.a, d);
self.b.cmove(&g.b, d);
}
pub fn isunity(&self) -> bool {
let one = FP::new_int(1);
self.a.equals(&one) && self.b.iszilch()
}
pub fn equals(&self, x: &FP2) -> bool {
self.a.equals(&x.a) && self.b.equals(&x.b)
}
#[allow(non_snake_case)]
pub fn getA(&mut self) -> FP {
self.a
}
#[allow(non_snake_case)]
pub fn getB(&mut self) -> FP {
self.b
}
pub fn geta(&mut self) -> BIG {
self.a.redc()
}
pub fn getb(&mut self) -> BIG {
self.b.redc()
}
pub fn copy(&mut self, x: &FP2) {
self.a.copy(&x.a);
self.b.copy(&x.b);
}
pub fn set_fp(&mut self, x: &FP) {
self.a.copy(x);
self.b.zero();
}
pub fn zero(&mut self) {
self.a.zero();
self.b.zero();
}
pub fn one(&mut self) {
self.a.one();
self.b.zero();
}
pub fn sign(&self) -> isize {
let mut p1=self.a.sign();
let mut p2=self.b.sign();
if fp::BIG_ENDIAN_SIGN {
let u=self.b.iszilch() as isize;
p2^=(p1^p2)&u;
p2
} else {
let u=self.a.iszilch() as isize;
p1^=(p1^p2)&u;
p1
}
}
pub fn neg(&mut self) {
let mut m = FP::new_copy(&self.a);
let mut t = FP::new();
m.add(&self.b);
m.neg();
t.copy(&m);
t.add(&self.b);
self.b.copy(&m);
self.b.add(&self.a);
self.a.copy(&t);
}
pub fn conj(&mut self) {
self.b.neg();
self.b.norm();
}
pub fn add(&mut self, x: &FP2) {
self.a.add(&x.a);
self.b.add(&x.b);
}
pub fn dbl(&mut self) {
self.a.dbl();
self.b.dbl();
}
pub fn sub(&mut self, x: &FP2) {
let mut m = FP2::new_copy(x);
m.neg();
self.add(&m);
}
pub fn rsub(&mut self, x: &FP2) {
self.neg();
self.add(x);
}
pub fn pmul(&mut self, s: &FP) {
self.a.mul(s);
self.b.mul(s);
}
pub fn imul(&mut self, c: isize) {
self.a.imul(c);
self.b.imul(c);
}
pub fn sqr(&mut self) {
let mut w1 = FP::new_copy(&self.a);
let mut w3 = FP::new_copy(&self.a);
let mut mb = FP::new_copy(&self.b);
w1.add(&self.b);
w3.add(&self.a);
w3.norm();
self.b.mul(&w3);
mb.neg();
self.a.add(&mb);
w1.norm();
self.a.norm();
self.a.mul(&w1);
}
pub fn mul(&mut self, y: &FP2) {
if ((self.a.xes + self.b.xes) as i64) * ((y.a.xes + y.b.xes) as i64) > fp::FEXCESS as i64 {
if self.a.xes > 1 {
self.a.reduce()
}
if self.b.xes > 1 {
self.b.reduce()
}
}
let p = BIG::new_ints(&rom::MODULUS);
let mut pr = DBIG::new();
pr.ucopy(&p);
let mut c = BIG::new_copy(&(self.a.x));
let mut d = BIG::new_copy(&(y.a.x));
let mut a = BIG::mul(&self.a.x, &y.a.x);
let mut b = BIG::mul(&self.b.x, &y.b.x);
c.add(&self.b.x);
c.norm();
d.add(&y.b.x);
d.norm();
let mut e = BIG::mul(&c, &d);
let mut f = DBIG::new_copy(&a);
f.add(&b);
b.rsub(&pr);
a.add(&b);
a.norm();
e.sub(&f);
e.norm();
self.a.x.copy(&FP::modulo(&mut a));
self.a.xes = 3;
self.b.x.copy(&FP::modulo(&mut e));
self.b.xes = 2;
}
pub fn qr(&mut self,h:Option<&mut FP>) -> isize {
let mut c=FP2::new_copy(self);
c.conj();
c.mul(self);
c.getA().qr(h)
}
pub fn sqrt(&mut self,h:Option<&FP>) {
if self.iszilch() {
return;
}
let mut w1 = FP::new_copy(&self.b);
let mut w2 = FP::new_copy(&self.a);
let mut w3 = FP::new_copy(&self.a);
let mut w4 = FP::new();
let mut hint = FP::new();
w1.sqr();
w2.sqr();
w1.add(&w2); w1.norm();
w2.copy(&w1.sqrt(h));
w1.copy(&w2);
w2.copy(&self.a);
w2.add(&w1);
w2.norm();
w2.div2();
w1.copy(&self.b); w1.div2();
let qr=w2.qr(Some(&mut hint));
w3.copy(&hint); w3.neg(); w3.norm();
w4.copy(&w2); w4.neg(); w4.norm();
w2.cmove(&w4,1-qr);
hint.cmove(&w3,1-qr);
self.a.copy(&w2.sqrt(Some(&hint)));
w3.copy(&w2); w3.inverse(Some(&hint));
w3.mul(&self.a);
self.b.copy(&w3); self.b.mul(&w1);
w4.copy(&self.a);
self.a.cmove(&self.b,1-qr);
self.b.cmove(&w4,1-qr);
let sgn=self.sign();
let mut nr=FP2::new_copy(&self);
nr.neg(); nr.norm();
self.cmove(&nr,sgn);
}
#[cfg(feature = "std")]
pub fn tostring(&self) -> String {
format!("[{},{}]", self.a.tostring(), self.b.tostring())
}
pub fn inverse(&mut self,h:Option<&FP>) {
self.norm();
let mut w1 = FP::new_copy(&self.a);
let mut w2 = FP::new_copy(&self.b);
w1.sqr();
w2.sqr();
w1.add(&w2);
w1.inverse(h);
self.a.mul(&w1);
w1.neg();
w1.norm();
self.b.mul(&w1);
}
pub fn div2(&mut self) {
self.a.div2();
self.b.div2();
}
pub fn times_i(&mut self) {
let z = FP::new_copy(&self.a);
self.a.copy(&self.b);
self.a.neg();
self.b.copy(&z);
}
pub fn mul_ip(&mut self) {
let mut t = FP2::new_copy(self);
let mut i = fp::QNRI;
self.times_i();
while i > 0 {
t.dbl();
t.norm();
i -= 1;
}
self.add(&t);
if fp::TOWER == fp::POSITOWER {
self.norm();
self.neg();
}
}
pub fn div_ip(&mut self) {
let mut z = FP2::new_ints(1 << fp::QNRI, 1);
z.inverse(None);
self.norm();
self.mul(&z);
if fp::TOWER == fp::POSITOWER {
self.neg();
self.norm();
}
}
}