halo2curves_axiom/pluto_eris/fields/
fp2.rs

1use super::fp::{Fp, MODULUS_STR};
2use crate::ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
3use crate::ff_ext::Legendre;
4use core::convert::TryInto;
5use core::ops::{Add, Mul, Neg, Sub};
6use rand::RngCore;
7use std::cmp::Ordering;
8use std::ops::MulAssign;
9use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
10
11#[cfg(feature = "derive_serde")]
12use serde::{Deserialize, Serialize};
13
14/// -ALPHA is a quadratic non-residue in Fp. Fp2 = Fp[X]/(X^2 + ALPHA)
15/// We introduce the variable u such that u^2 = -ALPHA
16
17/// U_SQUARE = -5
18/// 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd2fffffffc
19const U_SQUARE: Fp = Fp::from_raw([
20    0x9ffffcd2fffffffc,
21    0xa2a7e8c30006b945,
22    0xe4a7a5fe8fadffd6,
23    0x443f9a5cda8a6c7b,
24    0xa803ca76f439266f,
25    0x0130e0000d7f70e4,
26    0x2400000000002400,
27]);
28
29const NEG_ONE: Fp2 = Fp2 {
30    c0: super::fp::NEG_ONE,
31    c1: Fp::ZERO,
32};
33
34/// An element of Fp2, represented by c0 + c1 * u.
35#[derive(Copy, Clone, Debug, Eq, PartialEq)]
36#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))]
37pub struct Fp2 {
38    pub c0: Fp,
39    pub c1: Fp,
40}
41
42/// `Fp2` elements are ordered lexicographically.
43impl Ord for Fp2 {
44    #[inline(always)]
45    fn cmp(&self, other: &Fp2) -> Ordering {
46        match self.c1.cmp(&other.c1) {
47            Ordering::Greater => Ordering::Greater,
48            Ordering::Less => Ordering::Less,
49            Ordering::Equal => self.c0.cmp(&other.c0),
50        }
51    }
52}
53
54impl PartialOrd for Fp2 {
55    #[inline(always)]
56    fn partial_cmp(&self, other: &Fp2) -> Option<Ordering> {
57        Some(self.cmp(other))
58    }
59}
60
61impl ConditionallySelectable for Fp2 {
62    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
63        Fp2 {
64            c0: Fp::conditional_select(&a.c0, &b.c0, choice),
65            c1: Fp::conditional_select(&a.c1, &b.c1, choice),
66        }
67    }
68}
69
70impl ConstantTimeEq for Fp2 {
71    fn ct_eq(&self, other: &Self) -> Choice {
72        self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
73    }
74}
75
76impl Default for Fp2 {
77    #[inline]
78    fn default() -> Self {
79        Self::ZERO
80    }
81}
82
83impl From<Fp2> for [u8; 112] {
84    fn from(value: Fp2) -> [u8; 112] {
85        value.to_bytes()
86    }
87}
88
89impl<'a> From<&'a Fp2> for [u8; 112] {
90    fn from(value: &'a Fp2) -> [u8; 112] {
91        value.to_bytes()
92    }
93}
94
95impl Neg for Fp2 {
96    type Output = Fp2;
97
98    #[inline]
99    fn neg(self) -> Fp2 {
100        -&self
101    }
102}
103
104impl<'a> Neg for &'a Fp2 {
105    type Output = Fp2;
106
107    #[inline]
108    fn neg(self) -> Fp2 {
109        self.neg()
110    }
111}
112
113impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 {
114    type Output = Fp2;
115
116    #[inline]
117    fn sub(self, rhs: &'b Fp2) -> Fp2 {
118        self.sub(rhs)
119    }
120}
121
122impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 {
123    type Output = Fp2;
124
125    #[inline]
126    fn add(self, rhs: &'b Fp2) -> Fp2 {
127        self.add(rhs)
128    }
129}
130
131impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 {
132    type Output = Fp2;
133
134    #[inline]
135    fn mul(self, rhs: &'b Fp2) -> Fp2 {
136        self.mul(rhs)
137    }
138}
139
140use crate::{
141    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
142    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
143    impl_sum_prod,
144};
145impl_binops_additive!(Fp2, Fp2);
146impl_binops_multiplicative!(Fp2, Fp2);
147impl_sum_prod!(Fp2);
148
149/// Size in bytes of a `Fp2` element.
150const SIZE: usize = 112;
151/// Size in bytes of a each coefficient of `Fp2`.
152const COEF_SIZE: usize = 56;
153
154impl Fp2 {
155    /// Returns the zero element.
156    #[inline]
157    pub const fn zero() -> Fp2 {
158        Fp2 {
159            c0: Fp::zero(),
160            c1: Fp::zero(),
161        }
162    }
163
164    /// Returns the unit.
165    #[inline]
166    pub const fn one() -> Fp2 {
167        Fp2 {
168            c0: Fp::one(),
169            c1: Fp::zero(),
170        }
171    }
172
173    /// Given its `Fp` coefficients c0, c1. Returns the element of `Fp2`:  c0 + c1 * u.
174    pub const fn new(c0: Fp, c1: Fp) -> Self {
175        Fp2 { c0, c1 }
176    }
177
178    /// Returns the size in bytes of a `Fp2` element.
179    pub const fn size() -> usize {
180        SIZE
181    }
182
183    /// Attempts to convert a little-endian byte representation of
184    /// a scalar into a `Fp`, failing if the input is not canonical.
185    pub fn from_bytes(bytes: &[u8; SIZE]) -> CtOption<Fp2> {
186        let c0 = Fp::from_bytes(bytes[0..COEF_SIZE].try_into().unwrap());
187        let c1 = Fp::from_bytes(bytes[COEF_SIZE..SIZE].try_into().unwrap());
188        CtOption::new(
189            Fp2 {
190                c0: c0.unwrap(),
191                c1: c1.unwrap(),
192            },
193            c0.is_some() & c1.is_some(),
194        )
195    }
196
197    /// Converts an element of `Fp` into a byte representation in
198    /// little-endian byte order.
199    pub fn to_bytes(self) -> [u8; SIZE] {
200        let mut res = [0u8; SIZE];
201        let c0_bytes = self.c0.to_bytes();
202        let c1_bytes = self.c1.to_bytes();
203        res[0..COEF_SIZE].copy_from_slice(&c0_bytes[..]);
204        res[COEF_SIZE..SIZE].copy_from_slice(&c1_bytes[..]);
205        res
206    }
207
208    // TODO: This is a naive method using 4 multiplications
209    pub fn mul_assign(&mut self, other: &Self) {
210        // r0 = s0 * s0 + U_SQUARE * s1 * o1
211        // r1 = s0 * o1 - s1 * o0
212
213        let t0 = self.c0 * other.c0;
214        let t1 = self.c0 * other.c1;
215        let t2 = self.c1 * other.c0;
216        let t3 = self.c1 * other.c1;
217
218        self.c0 = t0 + U_SQUARE * t3;
219        self.c1 = t1 + t2
220    }
221
222    // TODO: This is a naive method using 3 multiplications
223    pub fn square_assign(&mut self) {
224        // r0 = s0^2 + U_SQUARE * s1^2
225        // r1 = 2* s0s1
226
227        let ab = self.c0 * self.c1;
228        let a2 = self.c0 * self.c0;
229        let b2 = self.c1 * self.c1;
230
231        self.c1 = ab.double();
232        self.c0 = a2 + U_SQUARE * b2;
233    }
234
235    pub fn double(&self) -> Self {
236        Self {
237            c0: self.c0.double(),
238            c1: self.c1.double(),
239        }
240    }
241
242    pub fn double_assign(&mut self) {
243        self.c0 = self.c0.double();
244        self.c1 = self.c1.double();
245    }
246
247    pub fn add(&self, other: &Self) -> Self {
248        Self {
249            c0: self.c0.add(&other.c0),
250            c1: self.c1.add(&other.c1),
251        }
252    }
253
254    pub fn sub(&self, other: &Self) -> Self {
255        Self {
256            c0: self.c0.sub(&other.c0),
257            c1: self.c1.sub(&other.c1),
258        }
259    }
260
261    pub fn mul(&self, other: &Self) -> Self {
262        let mut t = *other;
263        t.mul_assign(self);
264        t
265    }
266
267    pub fn square(&self) -> Self {
268        let mut t = *self;
269        t.square_assign();
270        t
271    }
272
273    pub fn neg(&self) -> Self {
274        Self {
275            c0: self.c0.neg(),
276            c1: self.c1.neg(),
277        }
278    }
279
280    // conjucate by negating c1
281    pub fn conjugate(&mut self) {
282        self.c1 = -self.c1;
283    }
284
285    pub fn frobenius_map(&mut self, power: usize) {
286        //TODO Replace with constant time version if needed
287        if power % 2 != 0 {
288            self.conjugate()
289        }
290    }
291
292    /// Multiply this element by cubic nonresidue: V_CUBE = 57/(u+3)
293    pub fn mul_by_nonresidue(&mut self) {
294        // (x + y * u) * 57/(u + 3)
295        self.mul_assign(&super::fp6::V_CUBE)
296    }
297
298    pub fn invert(&self) -> CtOption<Self> {
299        let mut t1 = self.c1;
300        t1 = t1.square();
301        t1 *= U_SQUARE;
302        let mut t0 = self.c0;
303        t0 = t0.square();
304        //t0 = c0^2 - U_SQUARE c1^2
305        t0 -= &t1;
306        t0.invert().map(|t| {
307            let mut tmp = Fp2 {
308                c0: self.c0,
309                c1: self.c1,
310            };
311            tmp.c0 *= &t;
312            tmp.c1 *= &t;
313            tmp.c1 = -tmp.c1;
314
315            tmp
316        })
317    }
318
319    /// Norm of Fp2 as extension field in u over Fp
320    fn norm(&self) -> Fp {
321        // norm = self * self.cojungate()
322        let t0 = self.c0.square();
323        let t1 = self.c1.square() * U_SQUARE;
324        t1 - t0
325    }
326}
327
328impl Legendre for Fp2 {
329    fn legendre(&self) -> i64 {
330        self.norm().legendre()
331    }
332}
333
334impl Field for Fp2 {
335    const ZERO: Self = Self::zero();
336    const ONE: Self = Self::one();
337
338    fn random(mut rng: impl RngCore) -> Self {
339        Fp2 {
340            c0: Fp::random(&mut rng),
341            c1: Fp::random(&mut rng),
342        }
343    }
344
345    fn is_zero(&self) -> Choice {
346        self.c0.is_zero() & self.c1.is_zero()
347    }
348
349    fn square(&self) -> Self {
350        self.square()
351    }
352
353    fn double(&self) -> Self {
354        self.double()
355    }
356
357    fn sqrt(&self) -> CtOption<Self> {
358        // Algorithm 10, https://eprint.iacr.org/2012/685.pdf
359
360        // Aux elements. Described in PRECOMPUTATION of Algorithm 10.
361        // As element of Fp2: E = 0 +  U *
362        // 0x13e275a1fa6a13af7a82a3d83bc9e63a667c70cf991a36e603b21f15823a404a021848271d63f0875d232408689b4c6c67153f9701e19938
363        const E: Fp2 = Fp2 {
364            c0: Fp::ZERO,
365            c1: Fp::from_raw([
366                0x67153f9701e19938,
367                0x5d232408689b4c6c,
368                0x021848271d63f087,
369                0x03b21f15823a404a,
370                0x667c70cf991a36e6,
371                0x7a82a3d83bc9e63a,
372                0x13e275a1fa6a13af,
373            ]),
374        };
375
376        // As element of Fp2: f = 5 + 0 * U
377        // 0x5
378        const F: Fp2 = Fp2 {
379            c0: Fp::from_raw([0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
380            c1: Fp::ZERO,
381        };
382
383        // Algorithm (not constant time)
384        let b = self.pow_vartime([
385            // (p-1)/4 =
386            // 0x900000000000900004c3800035fdc392a00f29dbd0e499bd10fe69736a29b1ef929e97fa3eb7ff5a8a9fa30c001ae5167ffff34c0000000
387            0x67ffff34c0000000,
388            0xa8a9fa30c001ae51,
389            0xf929e97fa3eb7ff5,
390            0xd10fe69736a29b1e,
391            0x2a00f29dbd0e499b,
392            0x004c3800035fdc39,
393            0x0900000000000900,
394        ]);
395
396        let b_2 = b.square();
397        let mut b_2_q = b_2;
398        b_2_q.frobenius_map(1);
399
400        let a0 = b_2_q * b_2;
401        if a0 == NEG_ONE {
402            CtOption::new(a0, Choice::from(0))
403        } else {
404            let mut x = b;
405            x.frobenius_map(1);
406            if x * b == Fp2::ONE {
407                let x0 = (b_2 * self).c0.sqrt().unwrap();
408                x.c0.mul_assign(x0);
409                x.c1.mul_assign(x0);
410                CtOption::new(x, Choice::from(1))
411            } else {
412                let x0 = (self * b_2 * F).sqrt().unwrap();
413                x *= x0 * E;
414                CtOption::new(x, Choice::from(1))
415            }
416        }
417    }
418
419    fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
420        ff::helpers::sqrt_ratio_generic(num, div)
421    }
422
423    fn invert(&self) -> CtOption<Self> {
424        self.invert()
425    }
426}
427
428impl From<bool> for Fp2 {
429    fn from(bit: bool) -> Fp2 {
430        if bit {
431            Fp2::ONE
432        } else {
433            Fp2::ZERO
434        }
435    }
436}
437
438impl From<u64> for Fp2 {
439    fn from(val: u64) -> Self {
440        Fp2 {
441            c0: Fp::from(val),
442            c1: Fp::zero(),
443        }
444    }
445}
446
447// This trait is only implemented to satisfy the requirement of CurveExt
448impl PrimeField for Fp2 {
449    type Repr = Fp2Bytes;
450
451    const MODULUS: &'static str = MODULUS_STR;
452    const MULTIPLICATIVE_GENERATOR: Self = Fp2 {
453        c0: Fp::MULTIPLICATIVE_GENERATOR,
454        c1: Fp::ZERO,
455    };
456    const NUM_BITS: u32 = 446;
457    const CAPACITY: u32 = 445;
458    const S: u32 = 0;
459
460    // TODO: Check that we can just 0 this and forget.
461    const ROOT_OF_UNITY: Self = Fp2::zero();
462    const ROOT_OF_UNITY_INV: Self = Fp2 {
463        c0: Fp::zero(),
464        c1: Fp::zero(),
465    };
466    const DELTA: Self = Fp2 {
467        c0: Fp::zero(),
468        c1: Fp::zero(),
469    };
470    const TWO_INV: Self = Fp2 {
471        c0: Fp::TWO_INV,
472        c1: Fp::zero(),
473    };
474
475    fn from_repr(repr: Self::Repr) -> CtOption<Self> {
476        let c0 = Fp::from_bytes(&repr.0[..COEF_SIZE].try_into().unwrap());
477        let c1 = Fp::from_bytes(&repr.0[COEF_SIZE..].try_into().unwrap());
478        // Disallow overflow representation
479        CtOption::new(Fp2::new(c0.unwrap(), c1.unwrap()), Choice::from(1))
480    }
481
482    fn to_repr(&self) -> Self::Repr {
483        Fp2Bytes(self.to_bytes())
484    }
485
486    fn is_odd(&self) -> Choice {
487        Choice::from(self.to_repr().as_ref()[0] & 1)
488    }
489}
490
491impl FromUniformBytes<64> for Fp2 {
492    fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
493        Self::new(Fp::from_uniform_bytes(bytes), Fp::zero())
494    }
495}
496#[derive(Clone, Copy, Debug)]
497/// Canonical little-endian representation of a `Fp2` element.
498/// First half of the bytes represent `c0`, the second half represent `c1`.
499pub struct Fp2Bytes([u8; SIZE]);
500
501impl Default for Fp2Bytes {
502    fn default() -> Self {
503        Self([0u8; SIZE])
504    }
505}
506
507impl AsMut<[u8]> for Fp2Bytes {
508    fn as_mut(&mut self) -> &mut [u8] {
509        &mut self.0
510    }
511}
512
513impl AsRef<[u8]> for Fp2Bytes {
514    fn as_ref(&self) -> &[u8] {
515        &self.0
516    }
517}
518
519impl crate::serde::SerdeObject for Fp2 {
520    fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self {
521        debug_assert_eq!(bytes.len(), 112);
522        let [c0, c1] = [0, 56].map(|i| Fp::from_raw_bytes_unchecked(&bytes[i..i + 56]));
523        Self { c0, c1 }
524    }
525    fn from_raw_bytes(bytes: &[u8]) -> Option<Self> {
526        if bytes.len() != SIZE {
527            return None;
528        }
529        let [c0, c1] = [0, COEF_SIZE].map(|i| Fp::from_raw_bytes(&bytes[i..i + COEF_SIZE]));
530        c0.zip(c1).map(|(c0, c1)| Self { c0, c1 })
531    }
532    fn to_raw_bytes(&self) -> Vec<u8> {
533        let mut res = Vec::with_capacity(SIZE);
534        for limb in self.c0.0.iter().chain(self.c1.0.iter()) {
535            res.extend_from_slice(&limb.to_le_bytes());
536        }
537        res
538    }
539    fn read_raw_unchecked<R: std::io::Read>(reader: &mut R) -> Self {
540        let [c0, c1] = [(); 2].map(|_| Fp::read_raw_unchecked(reader));
541        Self { c0, c1 }
542    }
543    fn read_raw<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
544        let c0 = Fp::read_raw(reader)?;
545        let c1 = Fp::read_raw(reader)?;
546        Ok(Self { c0, c1 })
547    }
548    fn write_raw<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
549        self.c0.write_raw(writer)?;
550        self.c1.write_raw(writer)
551    }
552}
553
554impl WithSmallOrderMulGroup<3> for Fp2 {
555    const ZETA: Self = Fp2 {
556        // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000002
557        c0: Fp::from_raw([
558            0x8ffff80f80000002,
559            0xd9fa5d8a200bc439,
560            0x1b50d5e1ff708dc8,
561            0xf43f8cddf9a5c478,
562            0xa803ca76be3924a5,
563            0x0130e0000d7f28e4,
564            0x2400000000002400,
565        ]),
566        c1: Fp::zero(),
567    };
568}
569
570#[cfg(test)]
571use rand::SeedableRng;
572#[cfg(test)]
573use rand_xorshift::XorShiftRng;
574
575#[test]
576fn test_ser() {
577    let mut rng = XorShiftRng::from_seed([
578        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
579        0xe5,
580    ]);
581
582    let a0 = Fp2::random(&mut rng);
583    let a_bytes = a0.to_bytes();
584    let a1 = Fp2::from_bytes(&a_bytes).unwrap();
585    assert_eq!(a0, a1);
586}
587
588#[test]
589fn test_fp2_ordering() {
590    let mut a = Fp2 {
591        c0: Fp::zero(),
592        c1: Fp::zero(),
593    };
594
595    let mut b = a;
596
597    assert!(a.cmp(&b) == Ordering::Equal);
598    b.c0 += &Fp::one();
599    assert!(a.cmp(&b) == Ordering::Less);
600    a.c0 += &Fp::one();
601    assert!(a.cmp(&b) == Ordering::Equal);
602    b.c1 += &Fp::one();
603    assert!(a.cmp(&b) == Ordering::Less);
604    a.c0 += &Fp::one();
605    assert!(a.cmp(&b) == Ordering::Less);
606    a.c1 += &Fp::one();
607    assert!(a.cmp(&b) == Ordering::Greater);
608    b.c0 += &Fp::one();
609    assert!(a.cmp(&b) == Ordering::Equal);
610}
611
612#[test]
613fn test_fp2_basics() {
614    assert_eq!(
615        Fp2 {
616            c0: Fp::zero(),
617            c1: Fp::zero(),
618        },
619        Fp2::ZERO
620    );
621    assert_eq!(
622        Fp2 {
623            c0: Fp::one(),
624            c1: Fp::zero(),
625        },
626        Fp2::ONE
627    );
628    assert_eq!(Fp2::ZERO.is_zero().unwrap_u8(), 1);
629    assert_eq!(Fp2::ONE.is_zero().unwrap_u8(), 0);
630    assert_eq!(
631        Fp2 {
632            c0: Fp::zero(),
633            c1: Fp::one(),
634        }
635        .is_zero()
636        .unwrap_u8(),
637        0
638    );
639}
640
641#[test]
642fn test_fp2_squaring() {
643    // u + 1
644    let mut a = Fp2 {
645        c0: Fp::one(),
646        c1: Fp::one(),
647    };
648    // (u + 1) ^2 = 1 + u^2 + 2u = -4 + 2u
649    a.square_assign();
650    let minus_4 = -Fp::from(4u64);
651    assert_eq!(
652        a,
653        Fp2 {
654            c0: minus_4,
655            c1: Fp::one() + Fp::one(),
656        }
657    );
658
659    // u
660    let mut a = Fp2 {
661        c0: Fp::zero(),
662        c1: Fp::one(),
663    };
664    // u^2
665    a.square_assign();
666    assert_eq!(
667        a,
668        Fp2 {
669            c0: U_SQUARE,
670            c1: Fp::zero(),
671        }
672    );
673}
674
675#[test]
676fn test_fp2_mul_nonresidue() {
677    let mut rng = XorShiftRng::from_seed([
678        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
679        0xe5,
680    ]);
681    let nqr = super::fp6::V_CUBE;
682    for _ in 0..1000 {
683        let mut a = Fp2::random(&mut rng);
684        let mut b = a;
685        a.mul_by_nonresidue();
686        b.mul_assign(&nqr);
687
688        assert_eq!(a, b);
689    }
690}
691
692#[test]
693pub fn test_sqrt() {
694    let mut rng = XorShiftRng::from_seed([
695        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
696        0xe5,
697    ]);
698    const N_ITER: usize = 1000;
699    for _ in 0..N_ITER {
700        let a = Fp2::random(&mut rng);
701        if a.legendre() == -1 {
702            assert!(bool::from(a.sqrt().is_none()));
703        }
704    }
705
706    for _ in 0..N_ITER {
707        let a = Fp2::random(&mut rng);
708        let mut b = a;
709        b.square_assign();
710        assert_eq!(b.legendre(), 1);
711
712        let b = b.sqrt().unwrap();
713        let mut negb = b;
714        negb = negb.neg();
715
716        assert!(a == b || a == negb);
717    }
718
719    let mut c = Fp2::ONE;
720    for _ in 0..N_ITER {
721        let mut b = c;
722        b.square_assign();
723        assert_eq!(b.legendre(), 1);
724
725        b = b.sqrt().unwrap();
726
727        if b != c {
728            b = b.neg();
729        }
730
731        assert_eq!(b, c);
732
733        c += &Fp2::ONE;
734    }
735}
736
737#[test]
738fn test_frobenius() {
739    let mut rng = XorShiftRng::from_seed([
740        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
741        0xe5,
742    ]);
743
744    for _ in 0..50 {
745        for i in 0..8 {
746            let mut a = Fp2::random(&mut rng);
747            let mut b = a;
748
749            for _ in 0..i {
750                a = a.pow_vartime([
751                    0x9ffffcd300000001,
752                    0xa2a7e8c30006b945,
753                    0xe4a7a5fe8fadffd6,
754                    0x443f9a5cda8a6c7b,
755                    0xa803ca76f439266f,
756                    0x0130e0000d7f70e4,
757                    0x2400000000002400,
758                ]);
759            }
760            b.frobenius_map(i);
761
762            assert_eq!(a, b);
763        }
764    }
765}
766
767#[test]
768fn test_field() {
769    crate::tests::field::random_field_tests::<Fp2>("fp2".to_string());
770}
771
772#[test]
773fn test_serialization() {
774    crate::tests::field::random_serialization_test::<Fp2>("fp2".to_string());
775    #[cfg(feature = "derive_serde")]
776    crate::tests::field::random_serde_test::<Fp2>("fp2".to_string());
777}