1use super::fq::Fq;
2use super::fq2::Fq2;
3use crate::ff::Field;
4use core::ops::{Add, Mul, Neg, Sub};
5use rand::RngCore;
6use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
7
8#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
14pub struct Fq6 {
15 pub c0: Fq2,
16 pub c1: Fq2,
17 pub c2: Fq2,
18}
19
20impl ConditionallySelectable for Fq6 {
21 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
22 Fq6 {
23 c0: Fq2::conditional_select(&a.c0, &b.c0, choice),
24 c1: Fq2::conditional_select(&a.c1, &b.c1, choice),
25 c2: Fq2::conditional_select(&a.c2, &b.c2, choice),
26 }
27 }
28}
29
30impl ConstantTimeEq for Fq6 {
31 fn ct_eq(&self, other: &Self) -> Choice {
32 self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
33 }
34}
35
36impl Neg for Fq6 {
37 type Output = Fq6;
38
39 #[inline]
40 fn neg(self) -> Fq6 {
41 -&self
42 }
43}
44
45impl<'a> Neg for &'a Fq6 {
46 type Output = Fq6;
47
48 #[inline]
49 fn neg(self) -> Fq6 {
50 self.neg()
51 }
52}
53
54impl<'a, 'b> Sub<&'b Fq6> for &'a Fq6 {
55 type Output = Fq6;
56
57 #[inline]
58 fn sub(self, rhs: &'b Fq6) -> Fq6 {
59 self.sub(rhs)
60 }
61}
62
63impl<'a, 'b> Add<&'b Fq6> for &'a Fq6 {
64 type Output = Fq6;
65
66 #[inline]
67 fn add(self, rhs: &'b Fq6) -> Fq6 {
68 self.add(rhs)
69 }
70}
71
72impl<'a, 'b> Mul<&'b Fq6> for &'a Fq6 {
73 type Output = Fq6;
74
75 #[inline]
76 fn mul(self, rhs: &'b Fq6) -> Fq6 {
77 self.mul(rhs)
78 }
79}
80
81use crate::{
82 impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
83 impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
84 impl_sum_prod,
85};
86impl_binops_additive!(Fq6, Fq6);
87impl_binops_multiplicative!(Fq6, Fq6);
88impl_sum_prod!(Fq6);
89
90impl Fq6 {
91 #[inline]
92 pub const fn zero() -> Self {
93 Fq6 {
94 c0: Fq2::ZERO,
95 c1: Fq2::ZERO,
96 c2: Fq2::ZERO,
97 }
98 }
99
100 #[inline]
101 pub const fn one() -> Self {
102 Fq6 {
103 c0: Fq2::ONE,
104 c1: Fq2::ZERO,
105 c2: Fq2::ZERO,
106 }
107 }
108
109 pub fn mul_assign(&mut self, other: &Self) {
110 let mut a_a = self.c0;
111 let mut b_b = self.c1;
112 let mut c_c = self.c2;
113 a_a *= &other.c0;
114 b_b *= &other.c1;
115 c_c *= &other.c2;
116
117 let mut t1 = other.c1;
118 t1 += &other.c2;
119 {
120 let mut tmp = self.c1;
121 tmp += &self.c2;
122
123 t1 *= &tmp;
124 t1 -= &b_b;
125 t1 -= &c_c;
126 t1.mul_by_nonresidue();
127 t1 += &a_a;
128 }
129
130 let mut t3 = other.c0;
131 t3 += &other.c2;
132 {
133 let mut tmp = self.c0;
134 tmp += &self.c2;
135
136 t3 *= &tmp;
137 t3 -= &a_a;
138 t3 += &b_b;
139 t3 -= &c_c;
140 }
141
142 let mut t2 = other.c0;
143 t2 += &other.c1;
144 {
145 let mut tmp = self.c0;
146 tmp += &self.c1;
147
148 t2 *= &tmp;
149 t2 -= &a_a;
150 t2 -= &b_b;
151 c_c.mul_by_nonresidue();
152 t2 += &c_c;
153 }
154
155 self.c0 = t1;
156 self.c1 = t2;
157 self.c2 = t3;
158 }
159
160 pub fn square_assign(&mut self) {
161 let mut s0 = self.c0;
163 s0.square_assign();
164 let mut ab = self.c0;
166 ab *= &self.c1;
167 let mut s1 = ab;
168 s1.double_assign();
169 let mut s2 = self.c0;
171 s2 -= &self.c1;
172 s2 += &self.c2;
173 s2.square_assign();
174 let mut bc = self.c1;
176 bc *= &self.c2;
177 let mut s3 = bc;
179 s3.double_assign();
180 let mut s4 = self.c2;
182 s4.square_assign();
183
184 self.c0 = s3;
186 self.c0.mul_by_nonresidue();
187 self.c0 += &s0;
189
190 self.c1 = s4;
192 self.c1.mul_by_nonresidue();
193 self.c1 += &s1;
194
195 self.c2 = s1;
197 self.c2 += &s2;
198 self.c2 += &s3;
199 self.c2 -= &s0;
200 self.c2 -= &s4;
201 }
202
203 pub fn double(&self) -> Self {
204 Self {
205 c0: self.c0.double(),
206 c1: self.c1.double(),
207 c2: self.c2.double(),
208 }
209 }
210
211 pub fn double_assign(&mut self) {
212 self.c0 = self.c0.double();
213 self.c1 = self.c1.double();
214 self.c2 = self.c2.double();
215 }
216
217 pub fn add(&self, other: &Self) -> Self {
218 Self {
219 c0: self.c0 + other.c0,
220 c1: self.c1 + other.c1,
221 c2: self.c2 + other.c2,
222 }
223 }
224
225 pub fn sub(&self, other: &Self) -> Self {
226 Self {
227 c0: self.c0 - other.c0,
228 c1: self.c1 - other.c1,
229 c2: self.c2 - other.c2,
230 }
231 }
232
233 pub fn mul(&self, other: &Self) -> Self {
234 let mut t = *other;
235 t.mul_assign(self);
236 t
237 }
238
239 pub fn square(&self) -> Self {
240 let mut t = *self;
241 t.square_assign();
242 t
243 }
244
245 pub fn neg(&self) -> Self {
246 Self {
247 c0: -self.c0,
248 c1: -self.c1,
249 c2: -self.c2,
250 }
251 }
252
253 pub fn frobenius_map(&mut self, power: usize) {
254 self.c0.frobenius_map(power);
255 self.c1.frobenius_map(power);
256 self.c2.frobenius_map(power);
257
258 self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]);
259 self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]);
260 }
261
262 pub fn mul_by_nonresidue(&mut self) {
264 use std::mem::swap;
265 swap(&mut self.c0, &mut self.c1);
266 swap(&mut self.c0, &mut self.c2);
267 self.c0.mul_by_nonresidue();
269 }
270
271 pub fn mul_by_1(&mut self, c1: &Fq2) {
272 let mut b_b = self.c1;
273 b_b *= c1;
274
275 let mut t1 = *c1;
276 {
277 let mut tmp = self.c1;
278 tmp += &self.c2;
279
280 t1 *= &tmp;
281 t1 -= &b_b;
282 t1.mul_by_nonresidue();
283 }
284
285 let mut t2 = *c1;
286 {
287 let mut tmp = self.c0;
288 tmp += &self.c1;
289
290 t2 *= &tmp;
291 t2 -= &b_b;
292 }
293
294 self.c0 = t1;
295 self.c1 = t2;
296 self.c2 = b_b;
297 }
298
299 pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) {
300 let mut a_a = self.c0;
301 let mut b_b = self.c1;
302 a_a *= c0;
303 b_b *= c1;
304
305 let mut t1 = *c1;
306 {
307 let mut tmp = self.c1;
308 tmp += &self.c2;
309
310 t1 *= &tmp;
311 t1 -= &b_b;
312 t1.mul_by_nonresidue();
313 t1 += &a_a;
314 }
315
316 let mut t3 = *c0;
317 {
318 let mut tmp = self.c0;
319 tmp += &self.c2;
320
321 t3 *= &tmp;
322 t3 -= &a_a;
323 t3 += &b_b;
324 }
325
326 let mut t2 = *c0;
327 t2 += c1;
328 {
329 let mut tmp = self.c0;
330 tmp += &self.c1;
331
332 t2 *= &tmp;
333 t2 -= &a_a;
334 t2 -= &b_b;
335 }
336
337 self.c0 = t1;
338 self.c1 = t2;
339 self.c2 = t3;
340 }
341
342 fn invert(&self) -> CtOption<Self> {
343 let mut c0 = self.c2;
344 c0.mul_by_nonresidue();
345 c0 *= &self.c1;
346 c0 = -c0;
347 {
348 let mut c0s = self.c0;
349 c0s.square_assign();
350 c0 += &c0s;
351 }
352 let mut c1 = self.c2;
353 c1.square_assign();
354 c1.mul_by_nonresidue();
355 {
356 let mut c01 = self.c0;
357 c01 *= &self.c1;
358 c1 -= &c01;
359 }
360 let mut c2 = self.c1;
361 c2.square_assign();
362 {
363 let mut c02 = self.c0;
364 c02 *= &self.c2;
365 c2 -= &c02;
366 }
367
368 let mut tmp1 = self.c2;
369 tmp1 *= &c1;
370 let mut tmp2 = self.c1;
371 tmp2 *= &c2;
372 tmp1 += &tmp2;
373 tmp1.mul_by_nonresidue();
374 tmp2 = self.c0;
375 tmp2 *= &c0;
376 tmp1 += &tmp2;
377
378 tmp1.invert().map(|t| {
379 let mut tmp = Fq6 {
380 c0: t,
381 c1: t,
382 c2: t,
383 };
384 tmp.c0 *= &c0;
385 tmp.c1 *= &c1;
386 tmp.c2 *= &c2;
387
388 tmp
389 })
390 }
391}
392
393impl Field for Fq6 {
394 const ZERO: Self = Self::zero();
395 const ONE: Self = Self::one();
396
397 fn random(mut rng: impl RngCore) -> Self {
398 Fq6 {
399 c0: Fq2::random(&mut rng),
400 c1: Fq2::random(&mut rng),
401 c2: Fq2::random(&mut rng),
402 }
403 }
404
405 fn is_zero(&self) -> Choice {
406 self.c0.is_zero() & self.c1.is_zero()
407 }
408
409 fn square(&self) -> Self {
410 self.square()
411 }
412
413 fn double(&self) -> Self {
414 self.double()
415 }
416
417 fn sqrt(&self) -> CtOption<Self> {
418 unimplemented!()
419 }
420
421 fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
422 unimplemented!()
423 }
424
425 fn invert(&self) -> CtOption<Self> {
426 self.invert()
427 }
428}
429
430pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [
431 Fq2 {
433 c0: Fq([
434 0xd35d438dc58f0d9d,
435 0x0a78eb28f5c70b3d,
436 0x666ea36f7879462c,
437 0x0e0a77c19a07df2f,
438 ]),
439 c1: Fq([0x0, 0x0, 0x0, 0x0]),
440 },
441 Fq2 {
444 c0: Fq([
445 0xb5773b104563ab30,
446 0x347f91c8a9aa6454,
447 0x7a007127242e0991,
448 0x1956bcd8118214ec,
449 ]),
450 c1: Fq([
451 0x6e849f1ea0aa4757,
452 0xaa1c7b6d89f89141,
453 0xb6e713cdfae0ca3a,
454 0x26694fbb4e82ebc3,
455 ]),
456 },
457 Fq2 {
460 c0: Fq([
461 0x3350c88e13e80b9c,
462 0x7dce557cdb5e56b9,
463 0x6001b4b8b615564a,
464 0x2682e617020217e0,
465 ]),
466 c1: Fq([0x0, 0x0, 0x0, 0x0]),
467 },
468 Fq2 {
470 c0: Fq([
471 0xc9af22f716ad6bad,
472 0xb311782a4aa662b2,
473 0x19eeaf64e248c7f4,
474 0x20273e77e3439f82,
475 ]),
476 c1: Fq([
477 0xacc02860f7ce93ac,
478 0x3933d5817ba76b4c,
479 0x69e6188b446c8467,
480 0x0a46036d4417cc55,
481 ]),
482 },
483 Fq2 {
485 c0: Fq([
486 0x71930c11d782e155,
487 0xa6bb947cffbe3323,
488 0xaa303344d4741444,
489 0x2c3b3f0d26594943,
490 ]),
491 c1: Fq([0x0, 0x0, 0x0, 0x0]),
492 },
493 Fq2 {
495 c0: Fq([
496 0xf91aba2654e8e3b1,
497 0x4771cb2fdc92ce12,
498 0xdcb16ae0fc8bdf35,
499 0x274aa195cd9d8be4,
500 ]),
501 c1: Fq([
502 0x5cfc50ae18811f8b,
503 0x4bb28433cb43988c,
504 0x4fd35f13c3b56219,
505 0x301949bd2fc8883a,
506 ]),
507 },
508];
509
510pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [
511 Fq2 {
513 c0: Fq([
514 0xd35d438dc58f0d9d,
515 0x0a78eb28f5c70b3d,
516 0x666ea36f7879462c,
517 0x0e0a77c19a07df2f,
518 ]),
519 c1: Fq([0x0, 0x0, 0x0, 0x0]),
520 },
521 Fq2 {
523 c0: Fq([
524 0x7361d77f843abe92,
525 0xa5bb2bd3273411fb,
526 0x9c941f314b3e2399,
527 0x15df9cddbb9fd3ec,
528 ]),
529 c1: Fq([
530 0x5dddfd154bd8c949,
531 0x62cb29a5a4445b60,
532 0x37bc870a0c7dd2b9,
533 0x24830a9d3171f0fd,
534 ]),
535 },
536 Fq2 {
538 c0: Fq([
539 0x71930c11d782e155,
540 0xa6bb947cffbe3323,
541 0xaa303344d4741444,
542 0x2c3b3f0d26594943,
543 ]),
544 c1: Fq([0x0, 0x0, 0x0, 0x0]),
545 },
546 Fq2 {
548 c0: Fq([
549 0x448a93a57b6762df,
550 0xbfd62df528fdeadf,
551 0xd858f5d00e9bd47a,
552 0x06b03d4d3476ec58,
553 ]),
554 c1: Fq([
555 0x2b19daf4bcc936d1,
556 0xa1a54e7a56f4299f,
557 0xb533eee05adeaef1,
558 0x170c812b84dda0b2,
559 ]),
560 },
561 Fq2 {
563 c0: Fq([
564 0x3350c88e13e80b9c,
565 0x7dce557cdb5e56b9,
566 0x6001b4b8b615564a,
567 0x2682e617020217e0,
568 ]),
569 c1: Fq([0x0, 0x0, 0x0, 0x0]),
570 },
571 Fq2 {
573 c0: Fq([
574 0x843420f1d8dadbd6,
575 0x31f010c9183fcdb2,
576 0x436330b527a76049,
577 0x13d47447f11adfe4,
578 ]),
579 c1: Fq([
580 0xef494023a857fa74,
581 0x2a925d02d5ab101a,
582 0x83b015829ba62f10,
583 0x2539111d0c13aea3,
584 ]),
585 },
586];
587
588#[cfg(test)]
589use rand::SeedableRng;
590#[cfg(test)]
591use rand_xorshift::XorShiftRng;
592
593#[test]
594fn test_fq6_mul_nonresidue() {
595 let mut rng = XorShiftRng::from_seed([
596 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
597 0xe5,
598 ]);
599
600 let nqr = Fq6 {
601 c0: Fq2::zero(),
602 c1: Fq2::one(),
603 c2: Fq2::zero(),
604 };
605
606 for _ in 0..1000 {
607 let mut a = Fq6::random(&mut rng);
608 let mut b = a;
609 a.mul_by_nonresidue();
610 b.mul_assign(&nqr);
611
612 assert_eq!(a, b);
613 }
614}
615
616#[test]
617fn test_fq6_mul_by_1() {
618 let mut rng = XorShiftRng::from_seed([
619 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
620 0xe5,
621 ]);
622
623 for _ in 0..1000 {
624 let c1 = Fq2::random(&mut rng);
625 let mut a = Fq6::random(&mut rng);
626 let mut b = a;
627
628 a.mul_by_1(&c1);
629 b.mul_assign(&Fq6 {
630 c0: Fq2::zero(),
631 c1,
632 c2: Fq2::zero(),
633 });
634
635 assert_eq!(a, b);
636 }
637}
638
639#[test]
640fn test_fq6_mul_by_01() {
641 let mut rng = XorShiftRng::from_seed([
642 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
643 0xe5,
644 ]);
645
646 for _ in 0..1000 {
647 let c0 = Fq2::random(&mut rng);
648 let c1 = Fq2::random(&mut rng);
649 let mut a = Fq6::random(&mut rng);
650 let mut b = a;
651
652 a.mul_by_01(&c0, &c1);
653 b.mul_assign(&Fq6 {
654 c0,
655 c1,
656 c2: Fq2::zero(),
657 });
658
659 assert_eq!(a, b);
660 }
661}
662
663#[test]
664fn test_squaring() {
665 let mut rng = XorShiftRng::from_seed([
666 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
667 0xe5,
668 ]);
669
670 for _ in 0..1000 {
671 let mut a = Fq6::random(&mut rng);
672 let mut b = a;
673 b.mul_assign(&a);
674 a.square_assign();
675 assert_eq!(a, b);
676 }
677}
678
679#[test]
680fn test_frobenius() {
681 let mut rng = XorShiftRng::from_seed([
682 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
683 0xe5,
684 ]);
685
686 for _ in 0..100 {
687 for i in 0..14 {
688 let mut a = Fq6::random(&mut rng);
689 let mut b = a;
690
691 for _ in 0..i {
692 a = a.pow_vartime([
693 0x3c208c16d87cfd47,
694 0x97816a916871ca8d,
695 0xb85045b68181585d,
696 0x30644e72e131a029,
697 ]);
698 }
699 b.frobenius_map(i);
700
701 assert_eq!(a, b);
702 }
703 }
704}
705
706#[test]
707fn test_field() {
708 crate::tests::field::random_field_tests::<Fq6>("fq6".to_string());
709}