halo2curves_axiom/pluto_eris/fields/
fp6.rs

1use super::fp::Fp;
2use super::fp2::Fp2;
3use crate::ff::Field;
4use core::ops::{Add, Mul, Neg, Sub};
5use rand::RngCore;
6use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
7
8/// -BETA is a cubic non-residue in Fp2. Fp6 = Fp2[X]/(X^3 + BETA)
9/// We introduce the variable v such that v^3 = -BETA
10/// BETA = - 57/(z+3)
11
12/// V_CUBE = 57/(u+3)
13pub(crate) const V_CUBE: Fp2 = Fp2 {
14    // 0xcdb6db6db6dc3b6dbda9924971b3a9ace4a7f2a7bcb449573cd928ee056022c3f6072240ebe2483833bf7b35b701d98ddb6da4b5b6db6e8
15    c0: Fp::from_raw([
16        0xddb6da4b5b6db6e8,
17        0x833bf7b35b701d98,
18        0x3f6072240ebe2483,
19        0x73cd928ee056022c,
20        0xce4a7f2a7bcb4495,
21        0xdbda9924971b3a9a,
22        0x0cdb6db6db6dc3b6,
23    ]),
24    // 0x7b6db6db6db756db71cc2492776bcc3489319197d79f5f3457b57ef5366ce1a8c6d1148d5a5491bb523fb0536dcde8eeb6db62d36db6db3
25    c1: Fp::from_raw([
26        0xeb6db62d36db6db3,
27        0xb523fb0536dcde8e,
28        0x8c6d1148d5a5491b,
29        0x457b57ef5366ce1a,
30        0x489319197d79f5f3,
31        0xb71cc2492776bcc3,
32        0x07b6db6db6db756d,
33    ]),
34};
35
36#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
37/// The `Fp6` element c0 + c1 * v + c2 * v3
38pub struct Fp6 {
39    pub c0: Fp2,
40    pub c1: Fp2,
41    pub c2: Fp2,
42}
43
44impl ConditionallySelectable for Fp6 {
45    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
46        Fp6 {
47            c0: Fp2::conditional_select(&a.c0, &b.c0, choice),
48            c1: Fp2::conditional_select(&a.c1, &b.c1, choice),
49            c2: Fp2::conditional_select(&a.c2, &b.c2, choice),
50        }
51    }
52}
53
54impl ConstantTimeEq for Fp6 {
55    fn ct_eq(&self, other: &Self) -> Choice {
56        self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
57    }
58}
59
60impl Neg for Fp6 {
61    type Output = Fp6;
62
63    #[inline]
64    fn neg(self) -> Fp6 {
65        -&self
66    }
67}
68
69impl<'a> Neg for &'a Fp6 {
70    type Output = Fp6;
71
72    #[inline]
73    fn neg(self) -> Fp6 {
74        self.neg()
75    }
76}
77
78impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 {
79    type Output = Fp6;
80
81    #[inline]
82    fn sub(self, rhs: &'b Fp6) -> Fp6 {
83        self.sub(rhs)
84    }
85}
86
87impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 {
88    type Output = Fp6;
89
90    #[inline]
91    fn add(self, rhs: &'b Fp6) -> Fp6 {
92        self.add(rhs)
93    }
94}
95
96impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 {
97    type Output = Fp6;
98
99    #[inline]
100    fn mul(self, rhs: &'b Fp6) -> Fp6 {
101        self.mul(rhs)
102    }
103}
104
105use crate::{
106    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
107    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
108    impl_sum_prod,
109};
110impl_binops_additive!(Fp6, Fp6);
111impl_binops_multiplicative!(Fp6, Fp6);
112impl_sum_prod!(Fp6);
113
114impl Fp6 {
115    #[inline]
116    pub const fn zero() -> Self {
117        Fp6 {
118            c0: Fp2::ZERO,
119            c1: Fp2::ZERO,
120            c2: Fp2::ZERO,
121        }
122    }
123
124    #[inline]
125    pub const fn one() -> Self {
126        Fp6 {
127            c0: Fp2::ONE,
128            c1: Fp2::ZERO,
129            c2: Fp2::ZERO,
130        }
131    }
132
133    pub fn mul_assign(&mut self, other: &Self) {
134        let mut a_a = self.c0;
135        let mut b_b = self.c1;
136        let mut c_c = self.c2;
137        a_a *= &other.c0;
138        b_b *= &other.c1;
139        c_c *= &other.c2;
140
141        let mut t1 = other.c1;
142        t1 += &other.c2;
143        {
144            let mut tmp = self.c1;
145            tmp += &self.c2;
146
147            t1 *= &tmp;
148            t1 -= &b_b;
149            t1 -= &c_c;
150            t1.mul_by_nonresidue();
151            t1 += &a_a;
152        }
153
154        let mut t3 = other.c0;
155        t3 += &other.c2;
156        {
157            let mut tmp = self.c0;
158            tmp += &self.c2;
159
160            t3 *= &tmp;
161            t3 -= &a_a;
162            t3 += &b_b;
163            t3 -= &c_c;
164        }
165
166        let mut t2 = other.c0;
167        t2 += &other.c1;
168        {
169            let mut tmp = self.c0;
170            tmp += &self.c1;
171
172            t2 *= &tmp;
173            t2 -= &a_a;
174            t2 -= &b_b;
175            c_c.mul_by_nonresidue();
176            t2 += &c_c;
177        }
178
179        self.c0 = t1;
180        self.c1 = t2;
181        self.c2 = t3;
182    }
183
184    pub fn square_assign(&mut self) {
185        // s0 = a^2
186        let mut s0 = self.c0;
187        s0.square_assign();
188        // s1 = 2ab
189        let mut ab = self.c0;
190        ab *= &self.c1;
191        let mut s1 = ab;
192        s1.double_assign();
193        // s2 = (a - b + c)^2
194        let mut s2 = self.c0;
195        s2 -= &self.c1;
196        s2 += &self.c2;
197        s2.square_assign();
198        // bc
199        let mut bc = self.c1;
200        bc *= &self.c2;
201        // s3 = 2bc
202        let mut s3 = bc;
203        s3.double_assign();
204        // s4 = c^2
205        let mut s4 = self.c2;
206        s4.square_assign();
207
208        // new c0 = 2bc.mul_by_xi + a^2
209        self.c0 = s3;
210        self.c0.mul_by_nonresidue();
211        // self.c0.mul_by_xi();
212        self.c0 += &s0;
213
214        // new c1 = (c^2).mul_by_xi + 2ab
215        self.c1 = s4;
216        self.c1.mul_by_nonresidue();
217        // self.c1.mul_by_xi();
218        self.c1 += &s1;
219
220        // new c2 = 2ab + (a - b + c)^2 + 2bc - a^2 - c^2 = b^2 + 2ac
221        self.c2 = s1;
222        self.c2 += &s2;
223        self.c2 += &s3;
224        self.c2 -= &s0;
225        self.c2 -= &s4;
226    }
227
228    pub fn double(&self) -> Self {
229        Self {
230            c0: self.c0.double(),
231            c1: self.c1.double(),
232            c2: self.c2.double(),
233        }
234    }
235
236    pub fn double_assign(&mut self) {
237        self.c0 = self.c0.double();
238        self.c1 = self.c1.double();
239        self.c2 = self.c2.double();
240    }
241
242    pub fn add(&self, other: &Self) -> Self {
243        Self {
244            c0: self.c0 + other.c0,
245            c1: self.c1 + other.c1,
246            c2: self.c2 + other.c2,
247        }
248    }
249
250    pub fn sub(&self, other: &Self) -> Self {
251        Self {
252            c0: self.c0 - other.c0,
253            c1: self.c1 - other.c1,
254            c2: self.c2 - other.c2,
255        }
256    }
257
258    pub fn mul(&self, other: &Self) -> Self {
259        let mut t = *other;
260        t.mul_assign(self);
261        t
262    }
263
264    pub fn square(&self) -> Self {
265        let mut t = *self;
266        t.square_assign();
267        t
268    }
269
270    pub fn neg(&self) -> Self {
271        Self {
272            c0: -self.c0,
273            c1: -self.c1,
274            c2: -self.c2,
275        }
276    }
277
278    pub fn frobenius_map(&mut self, power: usize) {
279        self.c0.frobenius_map(power);
280        self.c1.frobenius_map(power);
281        self.c2.frobenius_map(power);
282
283        self.c1.mul_assign(&FROBENIUS_COEFF_FP6_C1[power % 6]);
284        self.c2.mul_assign(&FROBENIUS_COEFF_FP6_C2[power % 6]);
285    }
286
287    /// Multiply by cubic nonresidue v.
288    pub fn mul_by_nonresidue(&mut self) {
289        use std::mem::swap;
290        swap(&mut self.c0, &mut self.c1);
291        swap(&mut self.c0, &mut self.c2);
292        // c0, c1, c2 -> c2, c0, c1
293        self.c0.mul_by_nonresidue();
294    }
295
296    pub fn mul_by_1(&mut self, c1: &Fp2) {
297        let mut b_b = self.c1;
298        b_b *= c1;
299
300        let mut t1 = *c1;
301        {
302            let mut tmp = self.c1;
303            tmp += &self.c2;
304
305            t1 *= &tmp;
306            t1 -= &b_b;
307            t1.mul_by_nonresidue();
308        }
309
310        let mut t2 = *c1;
311        {
312            let mut tmp = self.c0;
313            tmp += &self.c1;
314
315            t2 *= &tmp;
316            t2 -= &b_b;
317        }
318
319        self.c0 = t1;
320        self.c1 = t2;
321        self.c2 = b_b;
322    }
323
324    pub fn mul_by_01(&mut self, c0: &Fp2, c1: &Fp2) {
325        let mut a_a = self.c0;
326        let mut b_b = self.c1;
327        a_a *= c0;
328        b_b *= c1;
329
330        let mut t1 = *c1;
331        {
332            let mut tmp = self.c1;
333            tmp += &self.c2;
334
335            t1 *= &tmp;
336            t1 -= &b_b;
337            t1.mul_by_nonresidue();
338            t1 += &a_a;
339        }
340
341        let mut t3 = *c0;
342        {
343            let mut tmp = self.c0;
344            tmp += &self.c2;
345
346            t3 *= &tmp;
347            t3 -= &a_a;
348            t3 += &b_b;
349        }
350
351        let mut t2 = *c0;
352        t2 += c1;
353        {
354            let mut tmp = self.c0;
355            tmp += &self.c1;
356
357            t2 *= &tmp;
358            t2 -= &a_a;
359            t2 -= &b_b;
360        }
361
362        self.c0 = t1;
363        self.c1 = t2;
364        self.c2 = t3;
365    }
366
367    fn invert(&self) -> CtOption<Self> {
368        let mut c0 = self.c2;
369        c0.mul_by_nonresidue();
370        c0 *= &self.c1;
371        c0 = -c0;
372        {
373            let mut c0s = self.c0;
374            c0s.square_assign();
375            c0 += &c0s;
376        }
377        let mut c1 = self.c2;
378        c1.square_assign();
379        c1.mul_by_nonresidue();
380        {
381            let mut c01 = self.c0;
382            c01 *= &self.c1;
383            c1 -= &c01;
384        }
385        let mut c2 = self.c1;
386        c2.square_assign();
387        {
388            let mut c02 = self.c0;
389            c02 *= &self.c2;
390            c2 -= &c02;
391        }
392
393        let mut tmp1 = self.c2;
394        tmp1 *= &c1;
395        let mut tmp2 = self.c1;
396        tmp2 *= &c2;
397        tmp1 += &tmp2;
398        tmp1.mul_by_nonresidue();
399        tmp2 = self.c0;
400        tmp2 *= &c0;
401        tmp1 += &tmp2;
402
403        tmp1.invert().map(|t| {
404            let mut tmp = Fp6 {
405                c0: t,
406                c1: t,
407                c2: t,
408            };
409            tmp.c0 *= &c0;
410            tmp.c1 *= &c1;
411            tmp.c2 *= &c2;
412
413            tmp
414        })
415    }
416}
417
418impl Field for Fp6 {
419    const ZERO: Self = Self::zero();
420    const ONE: Self = Self::one();
421
422    fn random(mut rng: impl RngCore) -> Self {
423        Fp6 {
424            c0: Fp2::random(&mut rng),
425            c1: Fp2::random(&mut rng),
426            c2: Fp2::random(&mut rng),
427        }
428    }
429
430    fn is_zero(&self) -> Choice {
431        self.c0.is_zero() & self.c1.is_zero()
432    }
433
434    fn square(&self) -> Self {
435        self.square()
436    }
437
438    fn double(&self) -> Self {
439        self.double()
440    }
441
442    fn sqrt(&self) -> CtOption<Self> {
443        unimplemented!()
444    }
445
446    fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
447        unimplemented!()
448    }
449
450    fn invert(&self) -> CtOption<Self> {
451        self.invert()
452    }
453}
454
455/// Fp2 coefficients for the efficient computation of Frobenius Endomorphism in Fp6.
456pub(crate) const FROBENIUS_COEFF_FP6_C1: [Fp2; 6] = [
457    // Fp2(v^3)**(((p^0) - 1) / 3)
458    Fp2::ONE,
459    // Fp2(v^3)**(((p^1) - 1) / 3)
460    Fp2 {
461        // 0x120de97f024c55bc3bc0d351f4c70da1e3886170077a50986f93678bc921dcd5041bc4bb14cc42dc52e787634eccc335a001825382850d03
462        c0: Fp::from_raw([
463            0xa001825382850d03,
464            0x52e787634eccc335,
465            0x041bc4bb14cc42dc,
466            0x6f93678bc921dcd5,
467            0xe3886170077a5098,
468            0x3bc0d351f4c70da1,
469            0x120de97f024c55bc,
470        ]),
471        // 0x2096f3f804d973afd82becc2ef081b76132461908eadbe3da1a7f5502b7091965efa1ddf4658080413be1b7cd3c9ea0e2772fea378a9b322
472        c1: Fp::from_raw([
473            0x2772fea378a9b322,
474            0x13be1b7cd3c9ea0e,
475            0x5efa1ddf46580804,
476            0xa1a7f5502b709196,
477            0x132461908eadbe3d,
478            0xd82becc2ef081b76,
479            0x2096f3f804d973af,
480        ]),
481    },
482    // Fp2(v^3)**(((p^2) - 1) / 3)
483    Fp2 {
484        // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe
485        c0: Fp::from_raw([
486            0x100004c37ffffffe,
487            0xc8ad8b38dffaf50c,
488            0xc956d01c903d720d,
489            0x50000d7ee0e4a803,
490            0x00000000360001c9,
491            0x0000000000004800,
492            0x0000000000000000,
493        ]),
494        c1: Fp::ZERO,
495    },
496    // Fp2(v^3)**(((p^3) - 1) / 3)
497    Fp2 {
498        // 0x1f9cd069c59f50a72511749de232911d833b798e78bd98c02913e38315a71c287cd52ae30d09b78a8b43b17b4c3ea938a04518fa783eb497
499        c0: Fp::from_raw([
500            0xa04518fa783eb497,
501            0x8b43b17b4c3ea938,
502            0x7cd52ae30d09b78a,
503            0x2913e38315a71c28,
504            0x833b798e78bd98c0,
505            0x2511749de232911d,
506            0x1f9cd069c59f50a7,
507        ]),
508        // 0x23affd628747cbaec26943f93dc9eab63f4af36699fe6d74c0aa2122aa7cb689e8faacb3479a973a4a728fcb77b150ee77240d4066e42ac5
509        c1: Fp::from_raw([
510            0x77240d4066e42ac5,
511            0x4a728fcb77b150ee,
512            0xe8faacb3479a973a,
513            0xc0aa2122aa7cb689,
514            0x3f4af36699fe6d74,
515            0xc26943f93dc9eab6,
516            0x23affd628747cbae,
517        ]),
518    },
519    // Fp2(v^3)**(((p^4) - 1) / 3)
520    Fp2 {
521        // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000002
522        c0: Fp::from_raw([
523            0x8ffff80f80000002,
524            0xd9fa5d8a200bc439,
525            0x1b50d5e1ff708dc8,
526            0xf43f8cddf9a5c478,
527            0xa803ca76be3924a5,
528            0x0130e0000d7f28e4,
529            0x2400000000002400,
530        ]),
531        c1: Fp::ZERO,
532    },
533    // Fp2(v^3)**(((p^5) - 1) / 3)
534    Fp2 {
535        // 0x165546173814a19ca18f781044054309e943b9ef683a6385efd7e9aad64bdffa485e5c5efd860546672498a76502061cffb95e58053c3e68
536        c0: Fp::from_raw([
537            0xffb95e58053c3e68,
538            0x672498a76502061c,
539            0x485e5c5efd860546,
540            0xefd7e9aad64bdffa,
541            0xe943b9ef683a6385,
542            0xa18f781044054309,
543            0x165546173814a19c,
544        ]),
545        // 0x3b90ea573df08a167cc8f43ee2cdb9cfd983ff6bfc6212c262d1e46df2790d7815a816a9169606ee71f263db492378ea168edc22072221b
546        c1: Fp::from_raw([
547            0xa168edc22072221b,
548            0xe71f263db492378e,
549            0x815a816a9169606e,
550            0x262d1e46df2790d7,
551            0xfd983ff6bfc6212c,
552            0x67cc8f43ee2cdb9c,
553            0x03b90ea573df08a1,
554        ]),
555    },
556];
557
558/// Fp2 coefficients for the efficient computation of Frobenius Endomorphism in Fp6.
559pub(crate) const FROBENIUS_COEFF_FP6_C2: [Fp2; 6] = [
560    // Fp2(v^3)**(((2p^0) - 2) / 3)
561    Fp2::ONE,
562    // Fp2(v^3)**(((2p^1) - 2) / 3)
563    Fp2 {
564        // 0x93733692ce3cdcfc34610bac6bd22c4dc590efb038c82998c9549048e7b424cc00e17ffb4a61950d0ec132a7b38f09db0a818e422737f7c
565        c0: Fp::from_raw([
566            0xb0a818e422737f7c,
567            0xd0ec132a7b38f09d,
568            0xc00e17ffb4a61950,
569            0x8c9549048e7b424c,
570            0xdc590efb038c8299,
571            0xc34610bac6bd22c4,
572            0x093733692ce3cdcf,
573        ]),
574        // 0x12cb19daadc92882ba3593aa6f3e6bf426f29bd46039e3036f61d0bd35f39ebecdac3209d9df546061c90b4940d9031c240ce398421dc7dc
575        c1: Fp::from_raw([
576            0x240ce398421dc7dc,
577            0x61c90b4940d9031c,
578            0xcdac3209d9df5460,
579            0x6f61d0bd35f39ebe,
580            0x26f29bd46039e303,
581            0xba3593aa6f3e6bf4,
582            0x12cb19daadc92882,
583        ]),
584    },
585    // Fp2(v^3)**(((2p^2) - 2) / 3)
586    Fp2 {
587        // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000002
588        c0: Fp::from_raw([
589            0x8ffff80f80000002,
590            0xd9fa5d8a200bc439,
591            0x1b50d5e1ff708dc8,
592            0xf43f8cddf9a5c478,
593            0xa803ca76be3924a5,
594            0x0130e0000d7f28e4,
595            0x2400000000002400,
596        ]),
597        c1: Fp::ZERO,
598    },
599    // Fp2(v^3)**(((2p^3) - 2) / 3)
600    Fp2 {
601        // 0x85cc83a7eeba2ef5f7dd2f9f1405312b2ce0cbc85b8561e1657aaf1e85b82299aa5ace8b26b78d88f57e1c7a87f75556885980d6c8d2186
602        c0: Fp::from_raw([
603            0x6885980d6c8d2186,
604            0x8f57e1c7a87f7555,
605            0x9aa5ace8b26b78d8,
606            0x1657aaf1e85b8229,
607            0xb2ce0cbc85b8561e,
608            0x5f7dd2f9f1405312,
609            0x085cc83a7eeba2ef,
610        ]),
611        // 0xda3357ee4e6a9836af75e8ec0dbd23e7abc03d404620899ee0ea8b684b9400d58d5ebe487e523680bbe8a0dd9ea1d312bca2a953ab51c9b
612        c1: Fp::from_raw([
613            0x2bca2a953ab51c9b,
614            0x0bbe8a0dd9ea1d31,
615            0x58d5ebe487e52368,
616            0xee0ea8b684b9400d,
617            0x7abc03d404620899,
618            0x6af75e8ec0dbd23e,
619            0x0da3357ee4e6a983,
620        ]),
621    },
622    // Fp2(v^3)**(((2p^4) - 2) / 3)
623    Fp2 {
624        // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe
625        c0: Fp::from_raw([
626            0x100004c37ffffffe,
627            0xc8ad8b38dffaf50c,
628            0xc956d01c903d720d,
629            0x50000d7ee0e4a803,
630            0x00000000360001c9,
631            0x0000000000004800,
632            0x0000000000000000,
633        ]),
634        c1: Fp::ZERO,
635    },
636    // Fp2(v^3)**(((2p^5) - 2) / 3)
637    Fp2 {
638        // 0x126c045c5430b340de6cfc4b5581fb0d18dcaebf6af44db7a152a66663b3a80589f3e116289c6dad4263f3d0dc4e535286d24be170ff5eff
639        c0: Fp::from_raw([
640            0x86d24be170ff5eff,
641            0x4263f3d0dc4e5352,
642            0x89f3e116289c6dad,
643            0xa152a66663b3a805,
644            0x18dcaebf6af44db7,
645            0xde6cfc4b5581fb0d,
646            0x126c045c5430b340,
647        ]),
648        // 0x391b0a66d5051f9dc03edc6dd6532b206552ace8f9d3ad1e6cf20e91fdd8dafbe2588102de9880e3520536be54398f85028eea5832d1b8a
649        c1: Fp::from_raw([
650            0x5028eea5832d1b8a,
651            0x3520536be54398f8,
652            0xbe2588102de9880e,
653            0xe6cf20e91fdd8daf,
654            0x06552ace8f9d3ad1,
655            0xdc03edc6dd6532b2,
656            0x0391b0a66d5051f9,
657        ]),
658    },
659];
660
661#[cfg(test)]
662use rand::SeedableRng;
663#[cfg(test)]
664use rand_xorshift::XorShiftRng;
665
666#[test]
667fn test_fp6_mul_nonresidue() {
668    let mut rng = XorShiftRng::from_seed([
669        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
670        0xe5,
671    ]);
672
673    let nqr = Fp6 {
674        c0: Fp2::zero(),
675        c1: Fp2::one(),
676        c2: Fp2::zero(),
677    };
678
679    for _ in 0..1000 {
680        let mut a = Fp6::random(&mut rng);
681        let mut b = a;
682        a.mul_by_nonresidue();
683        b.mul_assign(&nqr);
684
685        assert_eq!(a, b);
686    }
687}
688
689#[test]
690fn test_fp6_mul_by_1() {
691    let mut rng = XorShiftRng::from_seed([
692        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
693        0xe5,
694    ]);
695
696    for _ in 0..1000 {
697        let c1 = Fp2::random(&mut rng);
698        let mut a = Fp6::random(&mut rng);
699        let mut b = a;
700
701        a.mul_by_1(&c1);
702        b.mul_assign(&Fp6 {
703            c0: Fp2::zero(),
704            c1,
705            c2: Fp2::zero(),
706        });
707
708        assert_eq!(a, b);
709    }
710}
711
712#[test]
713fn test_fp6_mul_by_01() {
714    let mut rng = XorShiftRng::from_seed([
715        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
716        0xe5,
717    ]);
718
719    for _ in 0..1000 {
720        let c0 = Fp2::random(&mut rng);
721        let c1 = Fp2::random(&mut rng);
722        let mut a = Fp6::random(&mut rng);
723        let mut b = a;
724
725        a.mul_by_01(&c0, &c1);
726        b.mul_assign(&Fp6 {
727            c0,
728            c1,
729            c2: Fp2::zero(),
730        });
731
732        assert_eq!(a, b);
733    }
734}
735
736#[test]
737fn test_squaring() {
738    let mut rng = XorShiftRng::from_seed([
739        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
740        0xe5,
741    ]);
742
743    for _ in 0..1000 {
744        let mut a = Fp6::random(&mut rng);
745        let mut b = a;
746        b.mul_assign(&a);
747        a.square_assign();
748        assert_eq!(a, b);
749    }
750}
751
752#[test]
753fn test_frobenius() {
754    let mut rng = XorShiftRng::from_seed([
755        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
756        0xe5,
757    ]);
758
759    for _ in 0..50 {
760        for i in 0..8 {
761            let mut a = Fp6::random(&mut rng);
762            let mut b = a;
763
764            for _ in 0..i {
765                a = a.pow_vartime([
766                    // p
767                    0x9ffffcd300000001,
768                    0xa2a7e8c30006b945,
769                    0xe4a7a5fe8fadffd6,
770                    0x443f9a5cda8a6c7b,
771                    0xa803ca76f439266f,
772                    0x0130e0000d7f70e4,
773                    0x2400000000002400,
774                ]);
775            }
776            b.frobenius_map(i);
777
778            assert_eq!(a, b);
779        }
780    }
781}
782
783#[test]
784fn test_field() {
785    crate::tests::field::random_field_tests::<Fp6>("fp6".to_string());
786}