use crate::{
impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
};
use super::fp::*;
use super::fp2::*;
use super::fp6::*;
use core::fmt;
use core::ops::{Add, Mul, Neg, Sub};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use rand_core::RngCore;
pub struct Fp12 {
pub c0: Fp6,
pub c1: Fp6,
}
impl From<Fp> for Fp12 {
fn from(f: Fp) -> Fp12 {
Fp12 {
c0: Fp6::from(f),
c1: Fp6::zero(),
}
}
}
impl From<Fp2> for Fp12 {
fn from(f: Fp2) -> Fp12 {
Fp12 {
c0: Fp6::from(f),
c1: Fp6::zero(),
}
}
}
impl From<Fp6> for Fp12 {
fn from(f: Fp6) -> Fp12 {
Fp12 {
c0: f,
c1: Fp6::zero(),
}
}
}
impl Eq for Fp12 {}
impl PartialEq for Fp12 {
fn eq(&self, other: &Fp12) -> bool {
self.ct_eq(other).into()
}
}
impl Copy for Fp12 {}
impl Clone for Fp12 {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl Default for Fp12 {
fn default() -> Self {
Fp12::zero()
}
}
#[cfg(feature = "zeroize")]
impl zeroize::DefaultIsZeroes for Fp12 {}
impl fmt::Debug for Fp12 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?} + ({:?})*w", self.c0, self.c1)
}
}
impl ConditionallySelectable for Fp12 {
#[inline(always)]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Fp12 {
c0: Fp6::conditional_select(&a.c0, &b.c0, choice),
c1: Fp6::conditional_select(&a.c1, &b.c1, choice),
}
}
}
impl ConstantTimeEq for Fp12 {
#[inline(always)]
fn ct_eq(&self, other: &Self) -> Choice {
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
}
}
impl Fp12 {
#[inline]
pub const fn zero() -> Self {
Fp12 {
c0: Fp6::zero(),
c1: Fp6::zero(),
}
}
#[inline]
pub const fn one() -> Self {
Fp12 {
c0: Fp6::one(),
c1: Fp6::zero(),
}
}
pub(crate) fn random(mut rng: impl RngCore) -> Self {
Fp12 {
c0: Fp6::random(&mut rng),
c1: Fp6::random(&mut rng),
}
}
pub fn double(&self) -> Self {
Self {
c0: self.c0.double(),
c1: self.c1.double(),
}
}
pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 {
let aa = self.c0.mul_by_01(c0, c1);
let bb = self.c1.mul_by_1(c4);
let o = c1 + c4;
let c1 = self.c1 + self.c0;
let c1 = c1.mul_by_01(c0, &o);
let c1 = c1 - aa - bb;
let c0 = bb;
let c0 = c0.mul_by_nonresidue();
let c0 = c0 + aa;
Fp12 { c0, c1 }
}
#[inline(always)]
pub fn is_zero(&self) -> Choice {
self.c0.is_zero() & self.c1.is_zero()
}
#[inline(always)]
pub fn conjugate(&self) -> Self {
Fp12 {
c0: self.c0,
c1: -self.c1,
}
}
#[inline(always)]
pub fn frobenius_map(&self) -> Self {
let c0 = self.c0.frobenius_map();
let c1 = self.c1.frobenius_map();
let c1 = c1
* Fp6::from(Fp2 {
c0: Fp::from_raw_unchecked([
0x0708_9552_b319_d465,
0xc669_5f92_b50a_8313,
0x97e8_3ccc_d117_228f,
0xa35b_aeca_b2dc_29ee,
0x1ce3_93ea_5daa_ce4d,
0x08f2_220f_b0fb_66eb,
]),
c1: Fp::from_raw_unchecked([
0xb2f6_6aad_4ce5_d646,
0x5842_a06b_fc49_7cec,
0xcf48_95d4_2599_d394,
0xc11b_9cba_40a8_e8d0,
0x2e38_13cb_e5a0_de89,
0x110e_efda_8884_7faf,
]),
});
Fp12 { c0, c1 }
}
#[inline]
pub fn square(&self) -> Self {
let ab = self.c0 * self.c1;
let c0c1 = self.c0 + self.c1;
let c0 = self.c1.mul_by_nonresidue();
let c0 = c0 + self.c0;
let c0 = c0 * c0c1;
let c0 = c0 - ab;
let c1 = ab + ab;
let c0 = c0 - ab.mul_by_nonresidue();
Fp12 { c0, c1 }
}
pub fn invert(&self) -> CtOption<Self> {
(self.c0.square() - self.c1.square().mul_by_nonresidue())
.invert()
.map(|t| Fp12 {
c0: self.c0 * t,
c1: self.c1 * -t,
})
}
}
impl ff::Field for Fp12 {
const ZERO: Self = Self::zero();
const ONE: Self = Self::one();
fn random(mut rng: impl RngCore) -> Self {
Fp12 {
c0: Fp6::random(&mut rng),
c1: Fp6::random(&mut rng),
}
}
fn is_zero(&self) -> Choice {
self.c0.is_zero() & self.c1.is_zero()
}
fn square(&self) -> Self {
self.square()
}
fn double(&self) -> Self {
self.double()
}
fn sqrt(&self) -> CtOption<Self> {
unimplemented!()
}
fn invert(&self) -> CtOption<Self> {
self.invert()
}
fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
unimplemented!()
}
}
impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
type Output = Fp12;
#[inline]
fn mul(self, other: &'b Fp12) -> Self::Output {
let aa = self.c0 * other.c0;
let bb = self.c1 * other.c1;
let o = other.c0 + other.c1;
let c1 = self.c1 + self.c0;
let c1 = c1 * o;
let c1 = c1 - aa;
let c1 = c1 - bb;
let c0 = bb.mul_by_nonresidue();
let c0 = c0 + aa;
Fp12 { c0, c1 }
}
}
impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 {
type Output = Fp12;
#[inline]
fn add(self, rhs: &'b Fp12) -> Self::Output {
Fp12 {
c0: self.c0 + rhs.c0,
c1: self.c1 + rhs.c1,
}
}
}
impl<'a> Neg for &'a Fp12 {
type Output = Fp12;
#[inline]
fn neg(self) -> Self::Output {
Fp12 {
c0: -self.c0,
c1: -self.c1,
}
}
}
impl Neg for Fp12 {
type Output = Fp12;
#[inline]
fn neg(self) -> Self::Output {
-&self
}
}
impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 {
type Output = Fp12;
#[inline]
fn sub(self, rhs: &'b Fp12) -> Self::Output {
Fp12 {
c0: self.c0 - rhs.c0,
c1: self.c1 - rhs.c1,
}
}
}
impl_binops_additive!(Fp12, Fp12);
impl_binops_multiplicative!(Fp12, Fp12);
crate::impl_sum_prod!(Fp12);
pub const FROBENIUS_COEFF_FQ12_C1: [Fp2; 12] = [
Fp2 {
c0: Fp([
0x760900000002fffd,
0xebf4000bc40c0002,
0x5f48985753c758ba,
0x77ce585370525745,
0x5c071a97a256ec6d,
0x15f65ec3fa80e493,
]),
c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
},
Fp2 {
c0: Fp([
0x7089552b319d465,
0xc6695f92b50a8313,
0x97e83cccd117228f,
0xa35baecab2dc29ee,
0x1ce393ea5daace4d,
0x8f2220fb0fb66eb,
]),
c1: Fp([
0xb2f66aad4ce5d646,
0x5842a06bfc497cec,
0xcf4895d42599d394,
0xc11b9cba40a8e8d0,
0x2e3813cbe5a0de89,
0x110eefda88847faf,
]),
},
Fp2 {
c0: Fp([
0xecfb361b798dba3a,
0xc100ddb891865a2c,
0xec08ff1232bda8e,
0xd5c13cc6f1ca4721,
0x47222a47bf7b5c04,
0x110f184e51c5f59,
]),
c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
},
Fp2 {
c0: Fp([
0x3e2f585da55c9ad1,
0x4294213d86c18183,
0x382844c88b623732,
0x92ad2afd19103e18,
0x1d794e4fac7cf0b9,
0xbd592fc7d825ec8,
]),
c1: Fp([
0x7bcfa7a25aa30fda,
0xdc17dec12a927e7c,
0x2f088dd86b4ebef1,
0xd1ca2087da74d4a7,
0x2da2596696cebc1d,
0xe2b7eedbbfd87d2,
]),
},
Fp2 {
c0: Fp([
0x30f1361b798a64e8,
0xf3b8ddab7ece5a2a,
0x16a8ca3ac61577f7,
0xc26a2ff874fd029b,
0x3636b76660701c6e,
0x51ba4ab241b6160,
]),
c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
},
Fp2 {
c0: Fp([
0x3726c30af242c66c,
0x7c2ac1aad1b6fe70,
0xa04007fbba4b14a2,
0xef517c3266341429,
0x95ba654ed2226b,
0x2e370eccc86f7dd,
]),
c1: Fp([
0x82d83cf50dbce43f,
0xa2813e53df9d018f,
0xc6f0caa53c65e181,
0x7525cf528d50fe95,
0x4a85ed50f4798a6b,
0x171da0fd6cf8eebd,
]),
},
Fp2 {
c0: Fp([
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x7e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x40ab3263eff0206,
]),
c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
},
Fp2 {
c0: Fp([
0xb2f66aad4ce5d646,
0x5842a06bfc497cec,
0xcf4895d42599d394,
0xc11b9cba40a8e8d0,
0x2e3813cbe5a0de89,
0x110eefda88847faf,
]),
c1: Fp([
0x7089552b319d465,
0xc6695f92b50a8313,
0x97e83cccd117228f,
0xa35baecab2dc29ee,
0x1ce393ea5daace4d,
0x8f2220fb0fb66eb,
]),
},
Fp2 {
c0: Fp([
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x3f97d6e83d050d2,
0x18f0206554638741,
]),
c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
},
Fp2 {
c0: Fp([
0x7bcfa7a25aa30fda,
0xdc17dec12a927e7c,
0x2f088dd86b4ebef1,
0xd1ca2087da74d4a7,
0x2da2596696cebc1d,
0xe2b7eedbbfd87d2,
]),
c1: Fp([
0x3e2f585da55c9ad1,
0x4294213d86c18183,
0x382844c88b623732,
0x92ad2afd19103e18,
0x1d794e4fac7cf0b9,
0xbd592fc7d825ec8,
]),
},
Fp2 {
c0: Fp([
0x890dc9e4867545c3,
0x2af322533285a5d5,
0x50880866309b7e2c,
0xa20d1b8c7e881024,
0x14e4f04fe2db9068,
0x14e56d3f1564853a,
]),
c1: Fp([0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
},
Fp2 {
c0: Fp([
0x82d83cf50dbce43f,
0xa2813e53df9d018f,
0xc6f0caa53c65e181,
0x7525cf528d50fe95,
0x4a85ed50f4798a6b,
0x171da0fd6cf8eebd,
]),
c1: Fp([
0x3726c30af242c66c,
0x7c2ac1aad1b6fe70,
0xa04007fbba4b14a2,
0xef517c3266341429,
0x95ba654ed2226b,
0x2e370eccc86f7dd,
]),
},
];
#[test]
fn test_arithmetic() {
use super::fp::*;
use super::fp2::*;
let a = Fp12 {
c0: Fp6 {
c0: Fp2 {
c0: Fp::from_raw_unchecked([
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
]),
c1: Fp::from_raw_unchecked([
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
]),
},
c1: Fp2 {
c0: Fp::from_raw_unchecked([
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
]),
c1: Fp::from_raw_unchecked([
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
]),
},
c2: Fp2 {
c0: Fp::from_raw_unchecked([
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
]),
c1: Fp::from_raw_unchecked([
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
]),
},
},
c1: Fp6 {
c0: Fp2 {
c0: Fp::from_raw_unchecked([
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
]),
c1: Fp::from_raw_unchecked([
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
]),
},
c1: Fp2 {
c0: Fp::from_raw_unchecked([
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
]),
c1: Fp::from_raw_unchecked([
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
]),
},
c2: Fp2 {
c0: Fp::from_raw_unchecked([
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
]),
c1: Fp::from_raw_unchecked([
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
]),
},
},
};
let b = Fp12 {
c0: Fp6 {
c0: Fp2 {
c0: Fp::from_raw_unchecked([
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d272_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
]),
c1: Fp::from_raw_unchecked([
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
]),
},
c1: Fp2 {
c0: Fp::from_raw_unchecked([
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e348,
]),
c1: Fp::from_raw_unchecked([
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
]),
},
c2: Fp2 {
c0: Fp::from_raw_unchecked([
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
]),
c1: Fp::from_raw_unchecked([
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
]),
},
},
c1: Fp6 {
c0: Fp2 {
c0: Fp::from_raw_unchecked([
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd2_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
]),
c1: Fp::from_raw_unchecked([
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
]),
},
c1: Fp2 {
c0: Fp::from_raw_unchecked([
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
]),
c1: Fp::from_raw_unchecked([
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a117_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
]),
},
c2: Fp2 {
c0: Fp::from_raw_unchecked([
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
]),
c1: Fp::from_raw_unchecked([
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
]),
},
},
};
let c = Fp12 {
c0: Fp6 {
c0: Fp2 {
c0: Fp::from_raw_unchecked([
0x47f9_cb98_71b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
]),
c1: Fp::from_raw_unchecked([
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
]),
},
c1: Fp2 {
c0: Fp::from_raw_unchecked([
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0x7791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
]),
c1: Fp::from_raw_unchecked([
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_133c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
]),
},
c2: Fp2 {
c0: Fp::from_raw_unchecked([
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_40e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
]),
c1: Fp::from_raw_unchecked([
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_1744_c040,
]),
},
},
c1: Fp6 {
c0: Fp2 {
c0: Fp::from_raw_unchecked([
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
]),
c1: Fp::from_raw_unchecked([
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
]),
},
c1: Fp2 {
c0: Fp::from_raw_unchecked([
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d3_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
]),
c1: Fp::from_raw_unchecked([
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
]),
},
c2: Fp2 {
c0: Fp::from_raw_unchecked([
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
]),
c1: Fp::from_raw_unchecked([
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_1040,
]),
},
},
};
let a = a.square().invert().unwrap().square() + c;
let b = b.square().invert().unwrap().square() + a;
let c = c.square().invert().unwrap().square() + b;
assert_eq!(a.square(), a * a);
assert_eq!(b.square(), b * b);
assert_eq!(c.square(), c * c);
assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b));
assert_eq!(
a.invert().unwrap() * b.invert().unwrap(),
(a * b).invert().unwrap()
);
assert_eq!(a.invert().unwrap() * a, Fp12::one());
assert!(a != a.frobenius_map());
assert_eq!(
a,
a.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
.frobenius_map()
);
}
#[cfg(feature = "zeroize")]
#[test]
fn test_zeroize() {
use zeroize::Zeroize;
let mut a = Fp12::one();
a.zeroize();
assert!(bool::from(a.is_zero()));
}