1#![allow(clippy::needless_borrow)]
5use core::fmt;
6use core::ops::{Add, Mul, Neg, Sub};
7use ff::{Field, WithSmallOrderMulGroup};
8use rand_core::RngCore;
9use std::cmp::Ordering;
10use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
11
12use crate::{
13 impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
14 impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
15};
16
17use super::fp::Fp;
18
19#[derive(Copy, Clone)]
20pub struct Fp2 {
21 pub c0: Fp,
22 pub c1: Fp,
23}
24
25impl fmt::Debug for Fp2 {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 write!(f, "{:?} + {:?}*u", self.c0, self.c1)
28 }
29}
30
31impl Default for Fp2 {
32 fn default() -> Self {
33 Fp2::zero()
34 }
35}
36
37#[cfg(feature = "zeroize")]
38impl zeroize::DefaultIsZeroes for Fp2 {}
39
40impl From<Fp> for Fp2 {
41 fn from(f: Fp) -> Fp2 {
42 Fp2 {
43 c0: f,
44 c1: Fp::zero(),
45 }
46 }
47}
48
49impl ConstantTimeEq for Fp2 {
50 fn ct_eq(&self, other: &Self) -> Choice {
51 self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
52 }
53}
54
55impl Eq for Fp2 {}
56impl PartialEq for Fp2 {
57 #[inline]
58 fn eq(&self, other: &Self) -> bool {
59 bool::from(self.ct_eq(other))
60 }
61}
62
63impl Ord for Fp2 {
64 #[inline(always)]
65 fn cmp(&self, other: &Fp2) -> Ordering {
66 match self.c1.cmp(&other.c1) {
67 Ordering::Greater => Ordering::Greater,
68 Ordering::Less => Ordering::Less,
69 Ordering::Equal => self.c0.cmp(&other.c0),
70 }
71 }
72}
73
74impl PartialOrd for Fp2 {
75 #[inline(always)]
76 fn partial_cmp(&self, other: &Fp2) -> Option<Ordering> {
77 Some(self.cmp(other))
78 }
79}
80
81impl ConditionallySelectable for Fp2 {
82 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
83 Fp2 {
84 c0: Fp::conditional_select(&a.c0, &b.c0, choice),
85 c1: Fp::conditional_select(&a.c1, &b.c1, choice),
86 }
87 }
88}
89
90impl<'a> Neg for &'a Fp2 {
91 type Output = Fp2;
92
93 #[inline]
94 fn neg(self) -> Fp2 {
95 self.neg()
96 }
97}
98
99impl Neg for Fp2 {
100 type Output = Fp2;
101
102 #[inline]
103 fn neg(self) -> Fp2 {
104 -&self
105 }
106}
107
108impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 {
109 type Output = Fp2;
110
111 #[inline]
112 fn sub(self, rhs: &'b Fp2) -> Fp2 {
113 self.sub(rhs)
114 }
115}
116
117impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 {
118 type Output = Fp2;
119
120 #[inline]
121 fn add(self, rhs: &'b Fp2) -> Fp2 {
122 self.add(rhs)
123 }
124}
125
126impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 {
127 type Output = Fp2;
128
129 #[inline]
130 fn mul(self, rhs: &'b Fp2) -> Fp2 {
131 self.mul(rhs)
132 }
133}
134
135impl_binops_additive!(Fp2, Fp2);
136impl_binops_multiplicative!(Fp2, Fp2);
137
138impl Fp2 {
139 #[inline]
140 pub const fn zero() -> Fp2 {
141 Fp2 {
142 c0: Fp::zero(),
143 c1: Fp::zero(),
144 }
145 }
146
147 #[inline]
148 pub const fn one() -> Fp2 {
149 Fp2 {
150 c0: Fp::one(),
151 c1: Fp::zero(),
152 }
153 }
154
155 pub fn is_zero(&self) -> Choice {
156 self.c0.is_zero() & self.c1.is_zero()
157 }
158
159 pub(crate) fn random(mut rng: impl RngCore) -> Fp2 {
160 Fp2 {
161 c0: Fp::random(&mut rng),
162 c1: Fp::random(&mut rng),
163 }
164 }
165
166 pub fn double(&self) -> Self {
167 Self {
168 c0: self.c0.double(),
169 c1: self.c1.double(),
170 }
171 }
172
173 #[inline(always)]
175 pub fn frobenius_map(&self) -> Self {
176 self.conjugate()
179 }
180
181 #[inline(always)]
182 pub fn conjugate(&self) -> Self {
183 Fp2 {
184 c0: self.c0,
185 c1: -self.c1,
186 }
187 }
188
189 #[inline(always)]
190 pub fn mul_by_nonresidue(&self) -> Fp2 {
191 Fp2 {
197 c0: self.c0 - self.c1,
198 c1: self.c0 + self.c1,
199 }
200 }
201
202 #[inline]
205 pub fn lexicographically_largest(&self) -> Choice {
206 self.c1.lexicographically_largest()
213 | (self.c1.is_zero() & self.c0.lexicographically_largest())
214 }
215
216 pub const fn square(&self) -> Fp2 {
217 let a = (&self.c0).add(&self.c1);
230 let b = (&self.c0).sub(&self.c1);
231 let c = (&self.c0).add(&self.c0);
232
233 Fp2 {
234 c0: (&a).mul(&b),
235 c1: (&c).mul(&self.c1),
236 }
237 }
238
239 pub fn mul(&self, rhs: &Fp2) -> Fp2 {
240 Fp2 {
253 c0: Fp::sum_of_products([self.c0, -self.c1], [rhs.c0, rhs.c1]),
254 c1: Fp::sum_of_products([self.c0, self.c1], [rhs.c1, rhs.c0]),
255 }
256 }
257
258 pub const fn add(&self, rhs: &Fp2) -> Fp2 {
259 Fp2 {
260 c0: (&self.c0).add(&rhs.c0),
261 c1: (&self.c1).add(&rhs.c1),
262 }
263 }
264
265 pub const fn sub(&self, rhs: &Fp2) -> Fp2 {
266 Fp2 {
267 c0: (&self.c0).sub(&rhs.c0),
268 c1: (&self.c1).sub(&rhs.c1),
269 }
270 }
271
272 pub const fn neg(&self) -> Fp2 {
273 Fp2 {
274 c0: (&self.c0).neg(),
275 c1: (&self.c1).neg(),
276 }
277 }
278
279 pub fn sqrt(&self) -> CtOption<Self> {
280 CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| {
284 let a1 = self.pow_vartime(&[
286 0xee7f_bfff_ffff_eaaa,
287 0x07aa_ffff_ac54_ffff,
288 0xd9cc_34a8_3dac_3d89,
289 0xd91d_d2e1_3ce1_44af,
290 0x92c6_e9ed_90d2_eb35,
291 0x0680_447a_8e5f_f9a6,
292 ]);
293
294 let alpha = a1.square() * self;
296
297 let x0 = a1 * self;
299
300 CtOption::new(
305 Fp2 {
306 c0: -x0.c1,
307 c1: x0.c0,
308 },
309 alpha.ct_eq(&(&Fp2::one()).neg()),
310 )
311 .or_else(|| {
313 CtOption::new(
314 (alpha + Fp2::one()).pow_vartime(&[
315 0xdcff_7fff_ffff_d555,
316 0x0f55_ffff_58a9_ffff,
317 0xb398_6950_7b58_7b12,
318 0xb23b_a5c2_79c2_895f,
319 0x258d_d3db_21a5_d66b,
320 0x0d00_88f5_1cbf_f34d,
321 ]) * x0,
322 Choice::from(1),
323 )
324 })
325 .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self)))
328 })
329 }
330
331 pub fn invert(&self) -> CtOption<Self> {
335 (self.c0.square() + self.c1.square()).invert().map(|t| Fp2 {
350 c0: self.c0 * t,
351 c1: self.c1 * -t,
352 })
353 }
354
355 pub fn pow_vartime(&self, by: &[u64]) -> Self {
359 let mut res = Self::one();
360 for e in by.iter().rev() {
361 for i in (0..64).rev() {
362 res = res.square();
363
364 if ((*e >> i) & 1) == 1 {
365 res *= self;
366 }
367 }
368 }
369 res
370 }
371
372 pub fn pow_vartime_extended(&self, by: &[u64]) -> Self {
375 let mut res = Self::one();
376 for e in by.iter().rev() {
377 for i in (0..64).rev() {
378 res = res.square();
379
380 if ((*e >> i) & 1) == 1 {
381 res *= self;
382 }
383 }
384 }
385 res
386 }
387
388 pub fn from_bytes(bytes: &[u8; 96]) -> CtOption<Fp2> {
391 let c0 = Fp::from_bytes(bytes[0..48].try_into().unwrap());
392 let c1 = Fp::from_bytes(bytes[48..96].try_into().unwrap());
393 CtOption::new(
394 Fp2 {
395 c0: c0.unwrap(),
396 c1: c1.unwrap(),
397 },
398 c0.is_some() & c1.is_some(),
399 )
400 }
401
402 pub fn to_bytes(&self) -> [u8; 96] {
403 let mut res = [0u8; 96];
404 let c0_bytes = self.c0.to_bytes();
405 let c1_bytes = self.c1.to_bytes();
406 res[0..48].copy_from_slice(&c0_bytes[..]);
407 res[48..96].copy_from_slice(&c1_bytes[..]);
408 res
409 }
410}
411
412crate::impl_sum_prod!(Fp2);
413
414impl ff::Field for Fp2 {
415 const ZERO: Self = Self::zero();
416 const ONE: Self = Self::one();
417
418 fn random(mut rng: impl RngCore) -> Self {
419 Fp2 {
420 c0: Fp::random(&mut rng),
421 c1: Fp::random(&mut rng),
422 }
423 }
424
425 fn is_zero(&self) -> Choice {
426 self.c0.is_zero() & self.c1.is_zero()
427 }
428
429 fn square(&self) -> Self {
430 self.square()
431 }
432
433 fn double(&self) -> Self {
434 self.double()
435 }
436
437 fn sqrt(&self) -> CtOption<Self> {
438 CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| {
442 let a1 = self.pow_vartime(&[
444 0xee7f_bfff_ffff_eaaa,
445 0x07aa_ffff_ac54_ffff,
446 0xd9cc_34a8_3dac_3d89,
447 0xd91d_d2e1_3ce1_44af,
448 0x92c6_e9ed_90d2_eb35,
449 0x0680_447a_8e5f_f9a6,
450 ]);
451
452 let alpha = a1.square() * self;
454
455 let x0 = a1 * self;
457
458 CtOption::new(
463 Fp2 {
464 c0: -x0.c1,
465 c1: x0.c0,
466 },
467 alpha.ct_eq(&(Fp2::one()).neg()),
468 )
469 .or_else(|| {
471 CtOption::new(
472 (alpha + Fp2::one()).pow_vartime(&[
473 0xdcff_7fff_ffff_d555,
474 0x0f55_ffff_58a9_ffff,
475 0xb398_6950_7b58_7b12,
476 0xb23b_a5c2_79c2_895f,
477 0x258d_d3db_21a5_d66b,
478 0x0d00_88f5_1cbf_f34d,
479 ]) * x0,
480 Choice::from(1),
481 )
482 })
483 .and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self)))
486 })
487 }
488
489 fn invert(&self) -> CtOption<Self> {
490 self.invert()
491 }
492
493 fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
494 const C1: usize = 3;
496 const C3: &[u64] = &[
513 12856264008172771555,
514 15550602622668079850,
515 3558621301769835471,
516 10839030838996704116,
517 12867602560861149700,
518 1285362188954994119,
519 17245150020539748080,
520 363211364668136614,
521 5075092668351637693,
522 11397987295268635755,
523 8411923172691210846,
524 11896141554398716,
525 ];
526 const C4: &[u64] = &[7];
528 const C5: &[u64] = &[4];
530 const C6: Fp2 = Fp2 {
532 c0: Fp::from_raw_unchecked([
533 8921533702591418330,
534 15859389534032789116,
535 3389114680249073393,
536 15116930867080254631,
537 3288288975085550621,
538 1021049300055853010,
539 ]),
540 c1: Fp::from_raw_unchecked([
541 8921533702591418330,
542 15859389534032789116,
543 3389114680249073393,
544 15116930867080254631,
545 3288288975085550621,
546 1021049300055853010,
547 ]),
548 };
549 const C7: Fp2 = Fp2 {
551 c0: Fp::from_raw_unchecked([
552 1921729236329761493,
553 9193968980645934504,
554 9862280504246317678,
555 6861748847800817560,
556 10375788487011937166,
557 4460107375738415,
558 ]),
559 c1: Fp::from_raw_unchecked([
560 16821121318233475459,
561 10183025025229892778,
562 1779012082459463630,
563 3442292649700377418,
564 1061500799026501234,
565 1352426537312017168,
566 ]),
567 };
568 let mut tv1 = C6; let tv2 = div.pow_vartime(C4); let tv3 = tv2.square(); let tv3 = tv3 * div; let tv5 = num * tv3; let tv5 = tv5.pow_vartime(C3); let tv5 = tv5 * tv2; let tv2 = tv5 * div; let mut tv3 = tv5 * num; let mut tv4 = tv3 * tv2; let tv5 = tv4.pow_vartime(C5); let is_square = tv5.ct_eq(&Fp2::one()); let tv2 = tv3 * C7; let tv5 = tv4 * tv1; tv3.conditional_assign(&tv2, !is_square); tv4.conditional_assign(&tv5, !is_square); for i in (2..=C1).rev() {
586 let tv5 = i as u32 - 2; let tv5 = num_bigint::BigUint::from(2u64).pow(tv5); let tv5 = tv4.pow_vartime(&tv5.to_u64_digits()); let e1 = tv5.ct_eq(&Fp2::one()); let tv2 = tv3 * tv1; tv1 = tv1 * tv1; let tv5 = tv4 * tv1; tv3.conditional_assign(&tv2, !e1); tv4.conditional_assign(&tv5, !e1); }
596 (is_square, tv3)
597 }
598}
599
600#[derive(Clone, Copy, Debug)]
601pub struct Fp2Bytes {
602 pub slice: [u8; 96],
603}
604
605impl Default for Fp2Bytes {
606 fn default() -> Self {
607 Self { slice: [0u8; 96] }
608 }
609}
610
611impl AsMut<[u8]> for Fp2Bytes {
612 fn as_mut(&mut self) -> &mut [u8] {
613 &mut self.slice
614 }
615}
616
617impl AsRef<[u8]> for Fp2Bytes {
618 fn as_ref(&self) -> &[u8] {
619 &self.slice
620 }
621}
622
623impl ff::PrimeField for Fp2 {
625 type Repr = Fp2Bytes;
626
627 const NUM_BITS: u32 = 381;
628 const CAPACITY: u32 = 381 - 1;
629 const MODULUS: &'static str =
630 "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab";
631 #[doc(hidden)]
632 const MULTIPLICATIVE_GENERATOR: Self = unimplemented!();
633 const ROOT_OF_UNITY: Self = Self {
634 c0: Fp::from_raw_unchecked([
635 0x7bcf_a7a2_5aa3_0fda,
636 0xdc17_dec1_2a92_7e7c,
637 0x2f08_8dd8_6b4e_bef1,
638 0xd1ca_2087_da74_d4a7,
639 0x2da2_5966_96ce_bc1d,
640 0x0e2b_7eed_bbfd_87d2,
641 ]),
642 c1: Fp::from_raw_unchecked([
643 0x7bcf_a7a2_5aa3_0fda,
644 0xdc17_dec1_2a92_7e7c,
645 0x2f08_8dd8_6b4e_bef1,
646 0xd1ca_2087_da74_d4a7,
647 0x2da2_5966_96ce_bc1d,
648 0x0e2b_7eed_bbfd_87d2,
649 ]),
650 };
651 const ROOT_OF_UNITY_INV: Self = Fp2 {
652 c0: Fp::from_raw_unchecked([
653 0x3e2f_585d_a55c_9ad1,
654 0x4294_213d_86c1_8183,
655 0x3828_44c8_8b62_3732,
656 0x92ad_2afd_1910_3e18,
657 0x1d79_4e4f_ac7c_f0b9,
658 0x0bd5_92fc_7d82_5ec8,
659 ]),
660 c1: Fp::from_raw_unchecked([
661 0x7bcf_a7a2_5aa3_0fda,
662 0xdc17_dec1_2a92_7e7c,
663 0x2f08_8dd8_6b4e_bef1,
664 0xd1ca_2087_da74_d4a7,
665 0x2da2_5966_96ce_bc1d,
666 0x0e2b_7eed_bbfd_87d2,
667 ]),
668 };
669 const DELTA: Self = Fp2 {
670 c0: Fp::zero(),
671 c1: Fp::zero(),
672 };
673 const TWO_INV: Fp2 = Fp2 {
674 c0: Fp::from_raw_unchecked([
675 0x1804_0000_0001_5554,
676 0x8550_0005_3ab0_0001,
677 0x633c_b57c_253c_276f,
678 0x6e22_d1ec_31eb_b502,
679 0xd391_6126_f2d1_4ca2,
680 0x17fb_b857_1a00_6596,
681 ]),
682 c1: Fp::zero(),
683 };
684 const S: u32 = 0;
685
686 fn from_repr(r: Self::Repr) -> CtOption<Self> {
687 Self::from_bytes(&r.slice)
690 }
691
692 fn to_repr(&self) -> Self::Repr {
693 Fp2Bytes {
694 slice: self.to_bytes(),
695 }
696 }
697
698 fn is_odd(&self) -> Choice {
699 Choice::from(self.to_bytes()[0] & 1)
700 }
701}
702
703impl From<u64> for Fp2 {
704 fn from(val: u64) -> Self {
705 Fp2 {
706 c0: Fp::from(val),
707 c1: Fp::zero(),
708 }
709 }
710}
711
712impl WithSmallOrderMulGroup<3> for Fp2 {
715 const ZETA: Self = Fp2 {
717 c0: Fp::from_raw_unchecked([
718 0x30f1_361b_798a_64e8,
719 0xf3b8_ddab_7ece_5a2a,
720 0x16a8_ca3a_c615_77f7,
721 0xc26a_2ff8_74fd_029b,
722 0x3636_b766_6070_1c6e,
723 0x051b_a4ab_241b_6160,
724 ]),
725 c1: Fp::zero(),
726 };
727}
728
729#[test]
730fn test_conditional_selection() {
731 let a = Fp2 {
732 c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
733 c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
734 };
735 let b = Fp2 {
736 c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]),
737 c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]),
738 };
739
740 assert_eq!(
741 ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)),
742 a
743 );
744 assert_eq!(
745 ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)),
746 b
747 );
748}
749
750#[test]
751fn test_equality() {
752 fn is_equal(a: &Fp2, b: &Fp2) -> bool {
753 let eq = a == b;
754 let ct_eq = a.ct_eq(b);
755
756 assert_eq!(eq, bool::from(ct_eq));
757
758 eq
759 }
760
761 assert!(is_equal(
762 &Fp2 {
763 c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
764 c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
765 },
766 &Fp2 {
767 c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
768 c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
769 }
770 ));
771
772 assert!(!is_equal(
773 &Fp2 {
774 c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]),
775 c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
776 },
777 &Fp2 {
778 c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
779 c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
780 }
781 ));
782
783 assert!(!is_equal(
784 &Fp2 {
785 c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
786 c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]),
787 },
788 &Fp2 {
789 c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
790 c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
791 }
792 ));
793}
794
795#[test]
796fn test_squaring() {
797 let a = Fp2 {
798 c0: Fp::from_raw_unchecked([
799 0xc9a2_1831_63ee_70d4,
800 0xbc37_70a7_196b_5c91,
801 0xa247_f8c1_304c_5f44,
802 0xb01f_c2a3_726c_80b5,
803 0xe1d2_93e5_bbd9_19c9,
804 0x04b7_8e80_020e_f2ca,
805 ]),
806 c1: Fp::from_raw_unchecked([
807 0x952e_a446_0462_618f,
808 0x238d_5edd_f025_c62f,
809 0xf6c9_4b01_2ea9_2e72,
810 0x03ce_24ea_c1c9_3808,
811 0x0559_50f9_45da_483c,
812 0x010a_768d_0df4_eabc,
813 ]),
814 };
815 let b = Fp2 {
816 c0: Fp::from_raw_unchecked([
817 0xa1e0_9175_a4d2_c1fe,
818 0x8b33_acfc_204e_ff12,
819 0xe244_15a1_1b45_6e42,
820 0x61d9_96b1_b6ee_1936,
821 0x1164_dbe8_667c_853c,
822 0x0788_557a_cc7d_9c79,
823 ]),
824 c1: Fp::from_raw_unchecked([
825 0xda6a_87cc_6f48_fa36,
826 0x0fc7_b488_277c_1903,
827 0x9445_ac4a_dc44_8187,
828 0x0261_6d5b_c909_9209,
829 0xdbed_4677_2db5_8d48,
830 0x11b9_4d50_76c7_b7b1,
831 ]),
832 };
833
834 assert_eq!(a.square(), b);
835}
836
837#[test]
838fn test_multiplication() {
839 let a = Fp2 {
840 c0: Fp::from_raw_unchecked([
841 0xc9a2_1831_63ee_70d4,
842 0xbc37_70a7_196b_5c91,
843 0xa247_f8c1_304c_5f44,
844 0xb01f_c2a3_726c_80b5,
845 0xe1d2_93e5_bbd9_19c9,
846 0x04b7_8e80_020e_f2ca,
847 ]),
848 c1: Fp::from_raw_unchecked([
849 0x952e_a446_0462_618f,
850 0x238d_5edd_f025_c62f,
851 0xf6c9_4b01_2ea9_2e72,
852 0x03ce_24ea_c1c9_3808,
853 0x0559_50f9_45da_483c,
854 0x010a_768d_0df4_eabc,
855 ]),
856 };
857 let b = Fp2 {
858 c0: Fp::from_raw_unchecked([
859 0xa1e0_9175_a4d2_c1fe,
860 0x8b33_acfc_204e_ff12,
861 0xe244_15a1_1b45_6e42,
862 0x61d9_96b1_b6ee_1936,
863 0x1164_dbe8_667c_853c,
864 0x0788_557a_cc7d_9c79,
865 ]),
866 c1: Fp::from_raw_unchecked([
867 0xda6a_87cc_6f48_fa36,
868 0x0fc7_b488_277c_1903,
869 0x9445_ac4a_dc44_8187,
870 0x0261_6d5b_c909_9209,
871 0xdbed_4677_2db5_8d48,
872 0x11b9_4d50_76c7_b7b1,
873 ]),
874 };
875 let c = Fp2 {
876 c0: Fp::from_raw_unchecked([
877 0xf597_483e_27b4_e0f7,
878 0x610f_badf_811d_ae5f,
879 0x8432_af91_7714_327a,
880 0x6a9a_9603_cf88_f09e,
881 0xf05a_7bf8_bad0_eb01,
882 0x0954_9131_c003_ffae,
883 ]),
884 c1: Fp::from_raw_unchecked([
885 0x963b_02d0_f93d_37cd,
886 0xc95c_e1cd_b30a_73d4,
887 0x3087_25fa_3126_f9b8,
888 0x56da_3c16_7fab_0d50,
889 0x6b50_86b5_f4b6_d6af,
890 0x09c3_9f06_2f18_e9f2,
891 ]),
892 };
893
894 assert_eq!(a * b, c);
895}
896
897#[test]
898fn test_addition() {
899 let a = Fp2 {
900 c0: Fp::from_raw_unchecked([
901 0xc9a2_1831_63ee_70d4,
902 0xbc37_70a7_196b_5c91,
903 0xa247_f8c1_304c_5f44,
904 0xb01f_c2a3_726c_80b5,
905 0xe1d2_93e5_bbd9_19c9,
906 0x04b7_8e80_020e_f2ca,
907 ]),
908 c1: Fp::from_raw_unchecked([
909 0x952e_a446_0462_618f,
910 0x238d_5edd_f025_c62f,
911 0xf6c9_4b01_2ea9_2e72,
912 0x03ce_24ea_c1c9_3808,
913 0x0559_50f9_45da_483c,
914 0x010a_768d_0df4_eabc,
915 ]),
916 };
917 let b = Fp2 {
918 c0: Fp::from_raw_unchecked([
919 0xa1e0_9175_a4d2_c1fe,
920 0x8b33_acfc_204e_ff12,
921 0xe244_15a1_1b45_6e42,
922 0x61d9_96b1_b6ee_1936,
923 0x1164_dbe8_667c_853c,
924 0x0788_557a_cc7d_9c79,
925 ]),
926 c1: Fp::from_raw_unchecked([
927 0xda6a_87cc_6f48_fa36,
928 0x0fc7_b488_277c_1903,
929 0x9445_ac4a_dc44_8187,
930 0x0261_6d5b_c909_9209,
931 0xdbed_4677_2db5_8d48,
932 0x11b9_4d50_76c7_b7b1,
933 ]),
934 };
935 let c = Fp2 {
936 c0: Fp::from_raw_unchecked([
937 0x6b82_a9a7_08c1_32d2,
938 0x476b_1da3_39ba_5ba4,
939 0x848c_0e62_4b91_cd87,
940 0x11f9_5955_295a_99ec,
941 0xf337_6fce_2255_9f06,
942 0x0c3f_e3fa_ce8c_8f43,
943 ]),
944 c1: Fp::from_raw_unchecked([
945 0x6f99_2c12_73ab_5bc5,
946 0x3355_1366_17a1_df33,
947 0x8b0e_f74c_0aed_aff9,
948 0x062f_9246_8ad2_ca12,
949 0xe146_9770_738f_d584,
950 0x12c3_c3dd_84bc_a26d,
951 ]),
952 };
953
954 assert_eq!(a + b, c);
955}
956
957#[test]
958fn test_subtraction() {
959 let a = Fp2 {
960 c0: Fp::from_raw_unchecked([
961 0xc9a2_1831_63ee_70d4,
962 0xbc37_70a7_196b_5c91,
963 0xa247_f8c1_304c_5f44,
964 0xb01f_c2a3_726c_80b5,
965 0xe1d2_93e5_bbd9_19c9,
966 0x04b7_8e80_020e_f2ca,
967 ]),
968 c1: Fp::from_raw_unchecked([
969 0x952e_a446_0462_618f,
970 0x238d_5edd_f025_c62f,
971 0xf6c9_4b01_2ea9_2e72,
972 0x03ce_24ea_c1c9_3808,
973 0x0559_50f9_45da_483c,
974 0x010a_768d_0df4_eabc,
975 ]),
976 };
977 let b = Fp2 {
978 c0: Fp::from_raw_unchecked([
979 0xa1e0_9175_a4d2_c1fe,
980 0x8b33_acfc_204e_ff12,
981 0xe244_15a1_1b45_6e42,
982 0x61d9_96b1_b6ee_1936,
983 0x1164_dbe8_667c_853c,
984 0x0788_557a_cc7d_9c79,
985 ]),
986 c1: Fp::from_raw_unchecked([
987 0xda6a_87cc_6f48_fa36,
988 0x0fc7_b488_277c_1903,
989 0x9445_ac4a_dc44_8187,
990 0x0261_6d5b_c909_9209,
991 0xdbed_4677_2db5_8d48,
992 0x11b9_4d50_76c7_b7b1,
993 ]),
994 };
995 let c = Fp2 {
996 c0: Fp::from_raw_unchecked([
997 0xe1c0_86bb_bf1b_5981,
998 0x4faf_c3a9_aa70_5d7e,
999 0x2734_b5c1_0bb7_e726,
1000 0xb2bd_7776_af03_7a3e,
1001 0x1b89_5fb3_98a8_4164,
1002 0x1730_4aef_6f11_3cec,
1003 ]),
1004 c1: Fp::from_raw_unchecked([
1005 0x74c3_1c79_9519_1204,
1006 0x3271_aa54_79fd_ad2b,
1007 0xc9b4_7157_4915_a30f,
1008 0x65e4_0313_ec44_b8be,
1009 0x7487_b238_5b70_67cb,
1010 0x0952_3b26_d0ad_19a4,
1011 ]),
1012 };
1013
1014 assert_eq!(a - b, c);
1015}
1016
1017#[test]
1018fn test_negation() {
1019 let a = Fp2 {
1020 c0: Fp::from_raw_unchecked([
1021 0xc9a2_1831_63ee_70d4,
1022 0xbc37_70a7_196b_5c91,
1023 0xa247_f8c1_304c_5f44,
1024 0xb01f_c2a3_726c_80b5,
1025 0xe1d2_93e5_bbd9_19c9,
1026 0x04b7_8e80_020e_f2ca,
1027 ]),
1028 c1: Fp::from_raw_unchecked([
1029 0x952e_a446_0462_618f,
1030 0x238d_5edd_f025_c62f,
1031 0xf6c9_4b01_2ea9_2e72,
1032 0x03ce_24ea_c1c9_3808,
1033 0x0559_50f9_45da_483c,
1034 0x010a_768d_0df4_eabc,
1035 ]),
1036 };
1037 let b = Fp2 {
1038 c0: Fp::from_raw_unchecked([
1039 0xf05c_e7ce_9c11_39d7,
1040 0x6274_8f57_97e8_a36d,
1041 0xc4e8_d9df_c664_96df,
1042 0xb457_88e1_8118_9209,
1043 0x6949_13d0_8772_930d,
1044 0x1549_836a_3770_f3cf,
1045 ]),
1046 c1: Fp::from_raw_unchecked([
1047 0x24d0_5bb9_fb9d_491c,
1048 0xfb1e_a120_c12e_39d0,
1049 0x7067_879f_c807_c7b1,
1050 0x60a9_269a_31bb_dab6,
1051 0x45c2_56bc_fd71_649b,
1052 0x18f6_9b5d_2b8a_fbde,
1053 ]),
1054 };
1055
1056 assert_eq!(-a, b);
1057}
1058
1059#[test]
1060fn test_sqrt() {
1061 let a = Fp2 {
1063 c0: Fp::from_raw_unchecked([
1064 0x2bee_d146_27d7_f9e9,
1065 0xb661_4e06_660e_5dce,
1066 0x06c4_cc7c_2f91_d42c,
1067 0x996d_7847_4b7a_63cc,
1068 0xebae_bc4c_820d_574e,
1069 0x1886_5e12_d93f_d845,
1070 ]),
1071 c1: Fp::from_raw_unchecked([
1072 0x7d82_8664_baf4_f566,
1073 0xd17e_6639_96ec_7339,
1074 0x679e_ad55_cb40_78d0,
1075 0xfe3b_2260_e001_ec28,
1076 0x3059_93d0_43d9_1b68,
1077 0x0626_f03c_0489_b72d,
1078 ]),
1079 };
1080
1081 assert_eq!(a.sqrt().unwrap().square(), a);
1082
1083 let b = Fp2 {
1086 c0: Fp::from_raw_unchecked([
1087 0x6631_0000_0010_5545,
1088 0x2114_0040_0eec_000d,
1089 0x3fa7_af30_c820_e316,
1090 0xc52a_8b8d_6387_695d,
1091 0x9fb4_e61d_1e83_eac5,
1092 0x005c_b922_afe8_4dc7,
1093 ]),
1094 c1: Fp::zero(),
1095 };
1096
1097 assert_eq!(b.sqrt().unwrap().square(), b);
1098
1099 let c = Fp2 {
1102 c0: Fp::from_raw_unchecked([
1103 0x44f6_0000_0051_ffae,
1104 0x86b8_0141_9948_0043,
1105 0xd715_9952_f1f3_794a,
1106 0x755d_6e3d_fe1f_fc12,
1107 0xd36c_d6db_5547_e905,
1108 0x02f8_c8ec_bf18_67bb,
1109 ]),
1110 c1: Fp::zero(),
1111 };
1112
1113 assert_eq!(c.sqrt().unwrap().square(), c);
1114
1115 assert!(bool::from(
1118 Fp2 {
1119 c0: Fp::from_raw_unchecked([
1120 0xc5fa_1bc8_fd00_d7f6,
1121 0x3830_ca45_4606_003b,
1122 0x2b28_7f11_04b1_02da,
1123 0xa7fb_30f2_8230_f23e,
1124 0x339c_db9e_e953_dbf0,
1125 0x0d78_ec51_d989_fc57,
1126 ]),
1127 c1: Fp::from_raw_unchecked([
1128 0x27ec_4898_cf87_f613,
1129 0x9de1_394e_1abb_05a5,
1130 0x0947_f85d_c170_fc14,
1131 0x586f_bc69_6b61_14b7,
1132 0x2b34_75a4_077d_7169,
1133 0x13e1_c895_cc4b_6c22,
1134 ])
1135 }
1136 .sqrt()
1137 .is_none()
1138 ));
1139}
1140
1141#[test]
1142fn test_inversion() {
1143 let a = Fp2 {
1144 c0: Fp::from_raw_unchecked([
1145 0x1128_ecad_6754_9455,
1146 0x9e7a_1cff_3a4e_a1a8,
1147 0xeb20_8d51_e08b_cf27,
1148 0xe98a_d408_11f5_fc2b,
1149 0x736c_3a59_232d_511d,
1150 0x10ac_d42d_29cf_cbb6,
1151 ]),
1152 c1: Fp::from_raw_unchecked([
1153 0xd328_e37c_c2f5_8d41,
1154 0x948d_f085_8a60_5869,
1155 0x6032_f9d5_6f93_a573,
1156 0x2be4_83ef_3fff_dc87,
1157 0x30ef_61f8_8f48_3c2a,
1158 0x1333_f55a_3572_5be0,
1159 ]),
1160 };
1161
1162 let b = Fp2 {
1163 c0: Fp::from_raw_unchecked([
1164 0x0581_a133_3d4f_48a6,
1165 0x5824_2f6e_f074_8500,
1166 0x0292_c955_349e_6da5,
1167 0xba37_721d_dd95_fcd0,
1168 0x70d1_6790_3aa5_dfc5,
1169 0x1189_5e11_8b58_a9d5,
1170 ]),
1171 c1: Fp::from_raw_unchecked([
1172 0x0eda_09d2_d7a8_5d17,
1173 0x8808_e137_a7d1_a2cf,
1174 0x43ae_2625_c1ff_21db,
1175 0xf85a_c9fd_f7a7_4c64,
1176 0x8fcc_dda5_b8da_9738,
1177 0x08e8_4f0c_b32c_d17d,
1178 ]),
1179 };
1180
1181 assert_eq!(a.invert().unwrap(), b);
1182
1183 assert!(bool::from(Fp2::zero().invert().is_none()));
1184}
1185
1186#[test]
1187fn test_lexicographic_largest() {
1188 assert!(!bool::from(Fp2::zero().lexicographically_largest()));
1189 assert!(!bool::from(Fp2::one().lexicographically_largest()));
1190 assert!(bool::from(
1191 Fp2 {
1192 c0: Fp::from_raw_unchecked([
1193 0x1128_ecad_6754_9455,
1194 0x9e7a_1cff_3a4e_a1a8,
1195 0xeb20_8d51_e08b_cf27,
1196 0xe98a_d408_11f5_fc2b,
1197 0x736c_3a59_232d_511d,
1198 0x10ac_d42d_29cf_cbb6,
1199 ]),
1200 c1: Fp::from_raw_unchecked([
1201 0xd328_e37c_c2f5_8d41,
1202 0x948d_f085_8a60_5869,
1203 0x6032_f9d5_6f93_a573,
1204 0x2be4_83ef_3fff_dc87,
1205 0x30ef_61f8_8f48_3c2a,
1206 0x1333_f55a_3572_5be0,
1207 ]),
1208 }
1209 .lexicographically_largest()
1210 ));
1211 assert!(!bool::from(
1212 Fp2 {
1213 c0: -Fp::from_raw_unchecked([
1214 0x1128_ecad_6754_9455,
1215 0x9e7a_1cff_3a4e_a1a8,
1216 0xeb20_8d51_e08b_cf27,
1217 0xe98a_d408_11f5_fc2b,
1218 0x736c_3a59_232d_511d,
1219 0x10ac_d42d_29cf_cbb6,
1220 ]),
1221 c1: -Fp::from_raw_unchecked([
1222 0xd328_e37c_c2f5_8d41,
1223 0x948d_f085_8a60_5869,
1224 0x6032_f9d5_6f93_a573,
1225 0x2be4_83ef_3fff_dc87,
1226 0x30ef_61f8_8f48_3c2a,
1227 0x1333_f55a_3572_5be0,
1228 ]),
1229 }
1230 .lexicographically_largest()
1231 ));
1232 assert!(!bool::from(
1233 Fp2 {
1234 c0: Fp::from_raw_unchecked([
1235 0x1128_ecad_6754_9455,
1236 0x9e7a_1cff_3a4e_a1a8,
1237 0xeb20_8d51_e08b_cf27,
1238 0xe98a_d408_11f5_fc2b,
1239 0x736c_3a59_232d_511d,
1240 0x10ac_d42d_29cf_cbb6,
1241 ]),
1242 c1: Fp::zero(),
1243 }
1244 .lexicographically_largest()
1245 ));
1246 assert!(bool::from(
1247 Fp2 {
1248 c0: -Fp::from_raw_unchecked([
1249 0x1128_ecad_6754_9455,
1250 0x9e7a_1cff_3a4e_a1a8,
1251 0xeb20_8d51_e08b_cf27,
1252 0xe98a_d408_11f5_fc2b,
1253 0x736c_3a59_232d_511d,
1254 0x10ac_d42d_29cf_cbb6,
1255 ]),
1256 c1: Fp::zero(),
1257 }
1258 .lexicographically_largest()
1259 ));
1260}
1261
1262#[cfg(feature = "zeroize")]
1263#[test]
1264fn test_zeroize() {
1265 use zeroize::Zeroize;
1266
1267 let mut a = Fp2::one();
1268 a.zeroize();
1269 assert!(bool::from(a.is_zero()));
1270}
1271
1272#[test]
1273fn test_root_of_unity_inv() {
1274 use ff::PrimeField;
1275 assert_eq!(Fp2::ROOT_OF_UNITY * Fp2::ROOT_OF_UNITY_INV, Fp2::ONE)
1276}