1use crate::fields::{const_fq, FieldElement, Fq, Fq12, Fq2, Fr, fq2_nonresidue};
2use crate::arith::U256;
3use core::{fmt, ops::{Add, Mul, Neg, Sub}};
4use rand::Rng;
5use alloc::vec::Vec;
6#[cfg(test)]
7use alloc::vec;
8
9const ATE_LOOP_COUNT_NAF : [u8; 64] = [1,0,1,0,0,0,3,0,3,0,0,0,3,0,1,0,3,0,0,3,0,0,0,0,0,1,0,0,3,0,1,0,0,3,0,0,0,0,3,0,1,0,0,0,3,0,3,0,0,1,0,0,0,3,0,0,3,0,1,0,1,0,0,0];
15
16pub trait GroupElement
17 : Sized
18 + Copy
19 + Clone
20 + PartialEq
21 + Eq
22 + fmt::Debug
23 + Add<Output = Self>
24 + Sub<Output = Self>
25 + Neg<Output = Self>
26 + Mul<Fr, Output = Self> {
27 fn zero() -> Self;
28 fn one() -> Self;
29 fn random<R: Rng>(rng: &mut R) -> Self;
30 fn is_zero(&self) -> bool;
31 fn double(&self) -> Self;
32}
33
34pub trait GroupParams: Sized + fmt::Debug {
35 type Base: FieldElement;
36
37 fn name() -> &'static str;
38 fn one() -> G<Self>;
39 fn coeff_b() -> Self::Base;
40 fn check_order() -> bool {
41 false
42 }
43}
44
45#[repr(C)]
46pub struct G<P: GroupParams> {
47 x: P::Base,
48 y: P::Base,
49 z: P::Base,
50}
51
52impl<P: GroupParams> G<P> {
53 pub fn new(x: P::Base, y: P::Base, z: P::Base) -> Self {
54 G { x: x, y: y, z: z }
55 }
56
57 pub fn x(&self) -> &P::Base {
58 &self.x
59 }
60
61 pub fn x_mut(&mut self) -> &mut P::Base {
62 &mut self.x
63 }
64
65 pub fn y(&self) -> &P::Base {
66 &self.y
67 }
68
69 pub fn y_mut(&mut self) -> &mut P::Base {
70 &mut self.y
71 }
72
73 pub fn z(&self) -> &P::Base {
74 &self.z
75 }
76
77 pub fn z_mut(&mut self) -> &mut P::Base {
78 &mut self.z
79 }
80}
81
82#[derive(Debug)]
83pub struct AffineG<P: GroupParams> {
84 x: P::Base,
85 y: P::Base,
86}
87
88#[derive(Debug)]
89pub enum Error {
90 NotOnCurve,
91 NotInSubgroup,
92}
93
94impl<P: GroupParams> AffineG<P> {
95 pub fn new(x: P::Base, y: P::Base) -> Result<Self, Error> {
96 if y.squared() == (x.squared() * x) + P::coeff_b() {
97 if P::check_order() {
98 let p: G<P> = G {
99 x: x,
100 y: y,
101 z: P::Base::one(),
102 };
103
104 if (p * (-Fr::one())) + p != G::zero() {
105 return Err(Error::NotInSubgroup);
106 }
107 }
108
109 Ok(AffineG { x: x, y: y })
110 } else {
111 Err(Error::NotOnCurve)
112 }
113 }
114
115 pub fn x(&self) -> &P::Base {
116 &self.x
117 }
118
119 pub fn x_mut(&mut self) -> &mut P::Base {
120 &mut self.x
121 }
122
123 pub fn y(&self) -> &P::Base {
124 &self.y
125 }
126
127 pub fn y_mut(&mut self) -> &mut P::Base {
128 &mut self.y
129 }
130}
131
132impl<P: GroupParams> PartialEq for AffineG<P> {
133 fn eq(&self, other: &Self) -> bool {
134 self.x == other.x && self.y == other.y
135 }
136}
137
138impl<P: GroupParams> Eq for AffineG<P> {}
139
140impl<P: GroupParams> fmt::Debug for G<P> {
141 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142 write!(f, "{}({:?}, {:?}, {:?})", P::name(), self.x, self.y, self.z)
143 }
144}
145
146impl<P: GroupParams> Clone for G<P> {
147 fn clone(&self) -> Self {
148 G {
149 x: self.x,
150 y: self.y,
151 z: self.z,
152 }
153 }
154}
155
156impl<P: GroupParams> Copy for G<P> {}
157
158impl<P: GroupParams> Clone for AffineG<P> {
159 fn clone(&self) -> Self {
160 AffineG {
161 x: self.x,
162 y: self.y,
163 }
164 }
165}
166
167impl<P: GroupParams> Copy for AffineG<P> {}
168
169impl<P: GroupParams> PartialEq for G<P> {
170 fn eq(&self, other: &Self) -> bool {
171 if self.is_zero() {
172 return other.is_zero();
173 }
174
175 if other.is_zero() {
176 return false;
177 }
178
179 let z1_squared = self.z.squared();
180 let z2_squared = other.z.squared();
181
182 if self.x * z2_squared != other.x * z1_squared {
183 return false;
184 }
185
186 let z1_cubed = self.z * z1_squared;
187 let z2_cubed = other.z * z2_squared;
188
189 if self.y * z2_cubed != other.y * z1_cubed {
190 return false;
191 }
192
193 return true;
194 }
195}
196impl<P: GroupParams> Eq for G<P> {}
197
198impl<P: GroupParams> G<P> {
199 pub fn to_affine(&self) -> Option<AffineG<P>> {
200 if self.z.is_zero() {
201 None
202 } else if self.z == P::Base::one() {
203 Some(AffineG {
204 x: self.x,
205 y: self.y,
206 })
207 } else {
208 let zinv = self.z.inverse().unwrap();
209 let zinv_squared = zinv.squared();
210
211 Some(AffineG {
212 x: self.x * zinv_squared,
213 y: self.y * (zinv_squared * zinv),
214 })
215 }
216 }
217}
218
219impl<P: GroupParams> AffineG<P> {
220 pub fn to_jacobian(&self) -> G<P> {
221 G {
222 x: self.x,
223 y: self.y,
224 z: P::Base::one(),
225 }
226 }
227}
228
229impl<P: GroupParams> GroupElement for G<P> {
230 fn zero() -> Self {
231 G {
232 x: P::Base::zero(),
233 y: P::Base::one(),
234 z: P::Base::zero(),
235 }
236 }
237
238 fn one() -> Self {
239 P::one()
240 }
241
242 fn random<R: Rng>(rng: &mut R) -> Self {
243 P::one() * Fr::random(rng)
244 }
245
246 fn is_zero(&self) -> bool {
247 self.z.is_zero()
248 }
249
250 fn double(&self) -> Self {
251 let a = self.x.squared();
252 let b = self.y.squared();
253 let c = b.squared();
254 let mut d = (self.x + b).squared() - a - c;
255 d = d + d;
256 let e = a + a + a;
257 let f = e.squared();
258 let x3 = f - (d + d);
259 let mut eight_c = c + c;
260 eight_c = eight_c + eight_c;
261 eight_c = eight_c + eight_c;
262 let y1z1 = self.y * self.z;
263
264 G {
265 x: x3,
266 y: e * (d - x3) - eight_c,
267 z: y1z1 + y1z1,
268 }
269 }
270}
271
272impl<P: GroupParams> Mul<Fr> for G<P> {
273 type Output = G<P>;
274
275 fn mul(self, other: Fr) -> G<P> {
276 let mut res = G::zero();
277 let mut found_one = false;
278
279 for i in U256::from(other).bits() {
280 if found_one {
281 res = res.double();
282 }
283
284 if i {
285 found_one = true;
286 res = res + self;
287 }
288 }
289
290 res
291 }
292}
293
294impl<P: GroupParams> Add<G<P>> for G<P> {
295 type Output = G<P>;
296
297 fn add(self, other: G<P>) -> G<P> {
298 if self.is_zero() {
299 return other;
300 }
301
302 if other.is_zero() {
303 return self;
304 }
305
306 let z1_squared = self.z.squared();
307 let z2_squared = other.z.squared();
308 let u1 = self.x * z2_squared;
309 let u2 = other.x * z1_squared;
310 let z1_cubed = self.z * z1_squared;
311 let z2_cubed = other.z * z2_squared;
312 let s1 = self.y * z2_cubed;
313 let s2 = other.y * z1_cubed;
314
315 if u1 == u2 && s1 == s2 {
316 self.double()
317 } else {
318 let h = u2 - u1;
319 let s2_minus_s1 = s2 - s1;
320 let i = (h + h).squared();
321 let j = h * i;
322 let r = s2_minus_s1 + s2_minus_s1;
323 let v = u1 * i;
324 let s1_j = s1 * j;
325 let x3 = r.squared() - j - (v + v);
326
327 G {
328 x: x3,
329 y: r * (v - x3) - (s1_j + s1_j),
330 z: ((self.z + other.z).squared() - z1_squared - z2_squared) * h,
331 }
332 }
333 }
334}
335
336impl<P: GroupParams> Neg for G<P> {
337 type Output = G<P>;
338
339 fn neg(self) -> G<P> {
340 if self.is_zero() {
341 self
342 } else {
343 G {
344 x: self.x,
345 y: -self.y,
346 z: self.z,
347 }
348 }
349 }
350}
351
352impl<P: GroupParams> Neg for AffineG<P> {
353 type Output = AffineG<P>;
354
355 fn neg(self) -> AffineG<P> {
356 AffineG {
357 x: self.x,
358 y: -self.y,
359 }
360 }
361}
362
363impl<P: GroupParams> Sub<G<P>> for G<P> {
364 type Output = G<P>;
365
366 fn sub(self, other: G<P>) -> G<P> {
367 self + (-other)
368 }
369}
370
371#[derive(Debug)]
372pub struct G1Params;
373
374impl GroupParams for G1Params {
375 type Base = Fq;
376
377 fn name() -> &'static str {
378 "G1"
379 }
380
381 fn one() -> G<Self> {
382 G {
383 x: Fq::one(),
384 y: const_fq([
385 0xa6ba871b8b1e1b3a,
386 0x14f1d651eb8e167b,
387 0xccdd46def0f28c58,
388 0x1c14ef83340fbe5e,
389 ]),
390 z: Fq::one(),
391 }
392 }
393
394 fn coeff_b() -> Fq {
395 const_fq([
396 0x7a17caa950ad28d7,
397 0x1f6ac17ae15521b9,
398 0x334bea4e696bd284,
399 0x2a1f6744ce179d8e,
400 ])
401 }
402}
403
404pub type G1 = G<G1Params>;
405
406pub type AffineG1 = AffineG<G1Params>;
407
408#[derive(Debug)]
409pub struct G2Params;
410
411impl GroupParams for G2Params {
412 type Base = Fq2;
413
414 fn name() -> &'static str {
415 "G2"
416 }
417
418 fn one() -> G<Self> {
419 G {
420 x: Fq2::new(
421 const_fq([
422 0x8e83b5d102bc2026,
423 0xdceb1935497b0172,
424 0xfbb8264797811adf,
425 0x19573841af96503b,
426 ]),
427 const_fq([
428 0xafb4737da84c6140,
429 0x6043dd5a5802d8c4,
430 0x09e950fc52a02f86,
431 0x14fef0833aea7b6b,
432 ]),
433 ),
434 y: Fq2::new(
435 const_fq([
436 0x619dfa9d886be9f6,
437 0xfe7fd297f59e9b78,
438 0xff9e1a62231b7dfe,
439 0x28fd7eebae9e4206,
440 ]),
441 const_fq([
442 0x64095b56c71856ee,
443 0xdc57f922327d3cbb,
444 0x55f935be33351076,
445 0x0da4a0e693fd6482,
446 ]),
447 ),
448 z: Fq2::one(),
449 }
450 }
451
452 fn coeff_b() -> Fq2 {
453 Fq2::new(
454 const_fq([
455 0x3bf938e377b802a8,
456 0x020b1b273633535d,
457 0x26b7edf049755260,
458 0x2514c6324384a86d,
459 ]),
460 const_fq([
461 0x38e7ecccd1dcff67,
462 0x65f0b37d93ce0d3e,
463 0xd749d0dd22ac00aa,
464 0x0141b9ce4a688d4d,
465 ]),
466 )
467 }
468
469 fn check_order() -> bool {
470 true
471 }
472}
473
474pub type G2 = G<G2Params>;
475
476pub type AffineG2 = AffineG<G2Params>;
477
478#[cfg(test)]
479mod tests;
480
481#[test]
482fn test_g1() {
483 tests::group_trials::<G1>();
484}
485
486#[test]
487fn test_g2() {
488 tests::group_trials::<G2>();
489}
490
491#[test]
492fn test_affine_jacobian_conversion() {
493 let rng = &mut ::rand::thread_rng();
494
495 assert!(G1::zero().to_affine().is_none());
496 assert!(G2::zero().to_affine().is_none());
497
498 for _ in 0..1000 {
499 let a = G1::one() * Fr::random(rng);
500 let b = a.to_affine().unwrap();
501 let c = b.to_jacobian();
502
503 assert_eq!(a, c);
504 }
505
506 for _ in 0..1000 {
507 let a = G2::one() * Fr::random(rng);
508 let b = a.to_affine().unwrap();
509 let c = b.to_jacobian();
510
511 assert_eq!(a, c);
512 }
513}
514
515#[inline]
516fn twist() -> Fq2 {
517 fq2_nonresidue()
518}
519
520#[inline]
521fn two_inv() -> Fq {
522 const_fq([
523 9781510331150239090,
524 15059239858463337189,
525 10331104244869713732,
526 2249375503248834476,
527 ])
528}
529
530#[inline]
531fn twist_mul_by_q_x() -> Fq2 {
532 Fq2::new(
533 const_fq([
534 13075984984163199792,
535 3782902503040509012,
536 8791150885551868305,
537 1825854335138010348,
538 ]),
539 const_fq([
540 7963664994991228759,
541 12257807996192067905,
542 13179524609921305146,
543 2767831111890561987,
544 ]),
545 )
546}
547
548#[inline]
549fn twist_mul_by_q_y() -> Fq2 {
550 Fq2::new(
551 const_fq([
552 16482010305593259561,
553 13488546290961988299,
554 3578621962720924518,
555 2681173117283399901,
556 ]),
557 const_fq([
558 11661927080404088775,
559 553939530661941723,
560 7860678177968807019,
561 3208568454732775116,
562 ]),
563 )
564}
565
566#[derive(PartialEq, Eq)]
567pub struct EllCoeffs {
568 pub ell_0: Fq2,
569 pub ell_vw: Fq2,
570 pub ell_vv: Fq2,
571}
572
573#[derive(PartialEq, Eq)]
574pub struct G2Precomp {
575 pub q: AffineG<G2Params>,
576 pub coeffs: Vec<EllCoeffs>,
577}
578
579impl G2Precomp {
580 pub fn miller_loop(&self, g1: &AffineG<G1Params>) -> Fq12 {
581 let mut f = Fq12::one();
582
583 let mut idx = 0;
584
585 for i in ATE_LOOP_COUNT_NAF.iter() {
586 let c = &self.coeffs[idx];
587 idx += 1;
588 f = f.squared()
589 .mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
590
591 if *i != 0 {
592 let c = &self.coeffs[idx];
593 idx += 1;
594 f = f.mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
595 }
596 }
597
598 let c = &self.coeffs[idx];
599 idx += 1;
600 f = f.mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
601
602 let c = &self.coeffs[idx];
603 f = f.mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
604
605 f
606 }
607}
608
609pub fn miller_loop_batch(g2_precomputes: &Vec<G2Precomp>, g1_vec: &Vec<AffineG<G1Params>>) -> Fq12 {
610 let mut f = Fq12::one();
611
612 let mut idx = 0;
613
614 for i in ATE_LOOP_COUNT_NAF.iter() {
615 f = f.squared();
616 for (g2_precompute, g1) in g2_precomputes.iter().zip(g1_vec.iter()) {
617 let c = &g2_precompute.coeffs[idx];
618 f = f.mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
619 }
620 idx += 1;
621 if *i != 0 {
622 for (g2_precompute, g1) in g2_precomputes.iter().zip(g1_vec.iter()) {
623 let c = &g2_precompute.coeffs[idx];
624 f = f.mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
625 }
626 idx += 1;
627 }
628 }
629
630 for (g2_precompute, g1) in g2_precomputes.iter().zip(g1_vec.iter()) {
631 let c = &g2_precompute.coeffs[idx];
632 f = f.mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
633 }
634 idx += 1;
635 for (g2_precompute, g1) in g2_precomputes.iter().zip(g1_vec.iter()) {
636 let c = &g2_precompute.coeffs[idx];
637 f = f.mul_by_024(c.ell_0, c.ell_vw.scale(g1.y), c.ell_vv.scale(g1.x));
638 }
639 f
640}
641
642#[test]
643fn test_miller_loop() {
644 use crate::fields::Fq6;
645
646 let g1 = G1::one()
647 * Fr::from_str(
648 "18097487326282793650237947474982649264364522469319914492172746413872781676",
649 ).unwrap();
650 let g2 = G2::one()
651 * Fr::from_str(
652 "20390255904278144451778773028944684152769293537511418234311120800877067946",
653 ).unwrap();
654
655 let g1_pre = g1.to_affine().unwrap();
656 let g2_pre = g2.to_affine().unwrap().precompute();
657
658 let gt = g2_pre.miller_loop(&g1_pre);
659
660 let expected: Fq12 = Fq12::new(
661 Fq6::new(
662 Fq2::new(
663 Fq::new(U256([51910954035973319022896381997847359481, 49070349125448662928383548013678560320])).unwrap(),
664 Fq::new(U256([150594250655925940766158230906714822921, 45067780486977162411874315270532662559])).unwrap(),
665 ),
666 Fq2::new(
667 Fq::new(U256([293313211826787380313097274184299135668, 28033688961864567415258173424862015279])).unwrap(),
668 Fq::new(U256([167463228417728651969785007140185669229, 7077084888790581611350259269763958251])).unwrap(),
669 ),
670 Fq2::new(
671 Fq::new(U256([166574695108782631900870170909221310910, 36301755601680728879208628452507017454])).unwrap(),
672 Fq::new(U256([61790765844042689836493058059938629070, 8459680572251855304146082351314233167])).unwrap(),
673 )
674 ),
675 Fq6::new(
676 Fq2::new(
677 Fq::new(U256([274725556782132290265566702453516000786, 47645385003117491559484060631887523335])).unwrap(),
678 Fq::new(U256([218741759704184655717004970623859820160, 5768209145436844234600983552836237590])).unwrap(),
679 ),
680 Fq2::new(
681 Fq::new(U256([166365676746880051357185694330614395245, 44422629177536239628987108174157680084])).unwrap(),
682 Fq::new(U256([188797990739833756731082975171894736944, 643465180603364587407484249282263717])).unwrap(),
683 ),
684 Fq2::new(
685 Fq::new(U256([271144479861903489720584548513988144824, 10463758518630442972881156820224659715])).unwrap(),
686 Fq::new(U256([214759070354702766397810519515686065785, 63150584453541665372008601383729030318])).unwrap(),
687 )
688 )
689 );
690 assert_eq!(gt, expected);
691}
692
693impl AffineG<G2Params> {
694 fn mul_by_q(&self) -> Self {
695 AffineG {
696 x: twist_mul_by_q_x() * self.x.frobenius_map(1),
697 y: twist_mul_by_q_y() * self.y.frobenius_map(1),
698 }
699 }
700
701 pub fn precompute(&self) -> G2Precomp {
702 let mut r = self.to_jacobian();
703
704 let mut coeffs = Vec::with_capacity(102);
705
706 let q_neg = self.neg();
707 for i in ATE_LOOP_COUNT_NAF.iter() {
708 coeffs.push(r.doubling_step_for_flipped_miller_loop());
709
710 if *i == 1 {
711 coeffs.push(r.mixed_addition_step_for_flipped_miller_loop(self));
712 }
713 if *i == 3 {
714 coeffs.push(r.mixed_addition_step_for_flipped_miller_loop(&q_neg));
715 }
716 }
717 let q1 = self.mul_by_q();
718 let q2 = -(q1.mul_by_q());
719
720 coeffs.push(r.mixed_addition_step_for_flipped_miller_loop(&q1));
721 coeffs.push(r.mixed_addition_step_for_flipped_miller_loop(&q2));
722
723 G2Precomp {
724 q: *self,
725 coeffs: coeffs,
726 }
727 }
728}
729
730impl G2 {
731 fn mixed_addition_step_for_flipped_miller_loop(
732 &mut self,
733 base: &AffineG<G2Params>,
734 ) -> EllCoeffs {
735 let d = self.x - self.z * base.x;
736 let e = self.y - self.z * base.y;
737 let f = d.squared();
738 let g = e.squared();
739 let h = d * f;
740 let i = self.x * f;
741 let j = self.z * g + h - (i + i);
742
743 self.x = d * j;
744 self.y = e * (i - j) - h * self.y;
745 self.z = self.z * h;
746
747 EllCoeffs {
748 ell_0: twist() * (e * base.x - d * base.y),
749 ell_vv: e.neg(),
750 ell_vw: d,
751 }
752 }
753
754 fn doubling_step_for_flipped_miller_loop(&mut self) -> EllCoeffs {
755 let a = (self.x * self.y).scale(two_inv());
756 let b = self.y.squared();
757 let c = self.z.squared();
758 let d = c + c + c;
759 let e = G2Params::coeff_b() * d;
760 let f = e + e + e;
761 let g = (b + f).scale(two_inv());
762 let h = (self.y + self.z).squared() - (b + c);
763 let i = e - b;
764 let j = self.x.squared();
765 let e_sq = e.squared();
766
767 self.x = a * (b - f);
768 self.y = g.squared() - (e_sq + e_sq + e_sq);
769 self.z = b * h;
770
771 EllCoeffs {
772 ell_0: twist() * i,
773 ell_vw: h.neg(),
774 ell_vv: j + j + j,
775 }
776 }
777}
778
779#[test]
780fn test_prepared_g2() {
781 let g2 = G2::one()
782 * Fr::from_str(
783 "20390255904278144451778773028944684152769293537511418234311120800877067946",
784 ).unwrap();
785
786 let g2_p = g2.to_affine().unwrap().precompute();
787
788 let expected_g2_p = G2Precomp {
789 q: AffineG {
790 x: Fq2::new(
791 Fq::new(U256([286108132425575157823044300810193365120, 40955922372263072279965766273066553545])).unwrap(),
792 Fq::new(U256([51787456028377068413742525949644831103, 18727496177066613612143648473641354138])).unwrap()
793 ),
794 y: Fq2::new(
795 Fq::new(U256([105235307093009526756443741536710213857, 56697136982397507595538316605420403515])).unwrap(),
796 Fq::new(U256([329285813328264858787093963282305459902, 40620233227497131789363095249389429612])).unwrap()
797 )
798 },
799 coeffs: vec![
800 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([145915152615018094207274265949237364577, 7720188347992263845704223037750674843])).unwrap(), Fq::new(U256([1642602221754736777334297091439332137, 43455737427254701713230610624368790806])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([192300176042178641247789718482757908684, 15253255261571338892647481759611271748])).unwrap(), Fq::new(U256([84481530492606440649863882423335628050, 47407062771372090504997924471673219553])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([232941999867492381013751617901190279960, 33161335727531633874118709394498248694])).unwrap(), Fq::new(U256([205159091107726234051689046255658875895, 18784742195738106358087607099254122817])).unwrap()) },
801 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([44903717410722925336080339155272113598, 2432148164440313442445265360131108750])).unwrap(), Fq::new(U256([115058944839679151514931430187558137193, 19913547532675326995005163334665928687])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([162225342243516818284353803749727397649, 2902812998987289816105769597190405892])).unwrap(), Fq::new(U256([280882305470180330735174149091034017688, 45000755139204939118391532933575113126])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([81439170073299243743458979294198176188, 8269567577349210062003854855333109246])).unwrap(), Fq::new(U256([229588642195598894684486216729209468602, 50120631775179825949138513731773141671])).unwrap()) },
802 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([62571086408084349816772432183764211635, 50233214695378430835319156584725532968])).unwrap(), Fq::new(U256([187531012616569709709565459550531626708, 25399966597901061874032352675540749982])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([125202410210363842487043120679844927964, 32132029049461004568346717732357115368])).unwrap(), Fq::new(U256([40047990517661970904320150752358301419, 29547330482153702904408059941549019061])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([72725441636162608820485881978479394755, 4335829118654814821890758647864621059])).unwrap(), Fq::new(U256([3808530996119731737716737121624391430, 14840119309897919310042357130477699760])).unwrap()) },
803 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([230923838046707456761202857863657685779, 30663581424308030115949734185266015957])).unwrap(), Fq::new(U256([75804784823545649980980649059385057066, 40783805432413650791639865054248573254])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([220403174695454806701510356109644664970, 61694366506643321007589345694791625235])).unwrap(), Fq::new(U256([111050616308506297168851165107648527276, 29953858315513175963460036136567176100])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([339360714692284035291265180647668192278, 51066683198194726198561068977822786062])).unwrap(), Fq::new(U256([20532264324124291215869821260466642056, 53772344434441015474139364759811842825])).unwrap()) },
804 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([53390632401876602131528763251836289052, 7039696239984002955384050250007606842])).unwrap(), Fq::new(U256([120756148133186395786471763371515340851, 62560956052144901655506280432592733452])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([308576049186755490218939550330371718607, 60158766280438165965298754059752443327])).unwrap(), Fq::new(U256([36519660971413271753359371158311247925, 33166369001790145692925310192829466902])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([178902296336418821251231963767960641976, 54039139529656231144797458475123200306])).unwrap(), Fq::new(U256([327709063289359470903820254941214044104, 29921523827667065809753199129798632038])).unwrap()) },
805 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([293947345738284310028696202770439717312, 23629199470233866978708839571836323911])).unwrap(), Fq::new(U256([270255405451020462647135094276398320650, 10214498461032217021941670870340839535])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([159737597162744224577379609113239040296, 42414292048147664393527940182644080820])).unwrap(), Fq::new(U256([319166235401593060755720596880844332847, 23942967675619615172318074973192451617])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([322582854267025287594765649802803266076, 43815838906866149475744535388480851056])).unwrap(), Fq::new(U256([194731262617323725381582694441938082298, 6318043913263416116808511289860376787])).unwrap()) },
806 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([340258202167813930211448229264749672038, 49309561646299510355653200516855272117])).unwrap(), Fq::new(U256([314009511129121341897071879080163927420, 46733418159091100849346822998375177924])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([296127326349185873181435771941022502705, 27092339993712929447599322475752229815])).unwrap(), Fq::new(U256([14090774041595571810302865485069892149, 9416959189726308679959984066033622764])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([331192186309261416893996962356905314610, 6133490180262551399580874298514378708])).unwrap(), Fq::new(U256([52146008553438513232574426823996261862, 51692389190081578424424830745703412620])).unwrap()) },
807 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([304886724958645105936240400377782311329, 24452949520921718326419149662293231897])).unwrap(), Fq::new(U256([191644523888771552477578834463319391872, 21762407072532056308389337796258587948])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([131875657381595539531276734057336373872, 46145756403556903236477737806904884925])).unwrap(), Fq::new(U256([15513558811446045405402043661936649706, 6164660913794769127674854991137805768])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([37628908878688989915703006228108158588, 32333568458099455743134394992114761192])).unwrap(), Fq::new(U256([310028653933656868572053471966984499925, 50927285178735796712220443510555951806])).unwrap()) },
808 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([323579290279633699483736616036574636320, 26462458255852549637810201830373030787])).unwrap(), Fq::new(U256([212356178148004645362110693008071812466, 7147771462841335914859391157142419270])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([33793486114682505251748396905247663179, 12971208030293312519168903516345112146])).unwrap(), Fq::new(U256([105470215179403498779276452125929471612, 13243999928262666388833873861601164323])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([336774965397927402479055329499391144750, 14092422716202439890370586466555281245])).unwrap(), Fq::new(U256([128524990965480598411024491536330888971, 16339349487558512101009117692791080358])).unwrap()) },
809 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([308807709363499650659552856561459119304, 20549512613498060437905021698882254003])).unwrap(), Fq::new(U256([296831825878801592442122280385770051837, 12999356604250035352468537408632919011])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([6416952549311962005690662486801715420, 6562024922514861810686738146979657369])).unwrap(), Fq::new(U256([208034701293282569885312328681897330015, 32565296199581814532888690261728578093])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([158821090683358871468006667057662171383, 43639936137708212270934870661514052516])).unwrap(), Fq::new(U256([273462267971100585683746592136866263017, 33686004986452254374790215788772597950])).unwrap()) },
810 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([239050458738191424179259026508283200850, 59033062634877072212375745660456082761])).unwrap(), Fq::new(U256([42735232027681689333255156554135804585, 3645324355274008091830665999900460711])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([108736755942765297679539162065532976047, 53005795603772553534072950762621113576])).unwrap(), Fq::new(U256([161143215360647768117453097687908448132, 47995551574176504820342605188594172079])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([6657347986892301669776633258074701543, 11902481233202623619409687758723915636])).unwrap(), Fq::new(U256([222956595276083444090603007056080975801, 36382879863546362219365726153085767156])).unwrap()) },
811 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([313682093040115002688525404999246872446, 42956501626298358982264420981703353471])).unwrap(), Fq::new(U256([16910327688907897736932577003378075753, 1359671211117678708781834746493106650])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([6643221945930263241861939865579251413, 53936065521860576156419486432192485437])).unwrap(), Fq::new(U256([309056291241266457640134736864478816427, 37247957207987268075661977484645707642])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([222157301260856896664619946160442713805, 49516872999212369072183141842522894943])).unwrap(), Fq::new(U256([205513791444704603844975928062124661076, 57468270224470125473802906477068197830])).unwrap()) },
812 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([8285963000933560101573593109316673578, 14786122523410515396563958444109027710])).unwrap(), Fq::new(U256([122158657553570962061566147876423950513, 52139940048286310424786112221915773798])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([334881138980927901869290199603379387015, 25426740963889929124688873103192284934])).unwrap(), Fq::new(U256([15041987314727689102877759056668987416, 53279076961030137249765180937167164331])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([219851721339103004718273461131600868626, 36871882814964809764289908414024625695])).unwrap(), Fq::new(U256([131269942067631553063094618567097759738, 53556533347945713183394135477679452243])).unwrap()) },
813 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([303322002694907044040904386256578801369, 47818402373691165133570276489205539247])).unwrap(), Fq::new(U256([23840328058016663665484464556433078317, 57137757685631244994429644684135564308])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([313347268925459844355339673983448992252, 63059125438530014597733264556246943889])).unwrap(), Fq::new(U256([332951624867081114956044501931839262097, 21446965953220512177611603903918604526])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([43308962429154740283739513584277671586, 36726788540078024416606994278261486896])).unwrap(), Fq::new(U256([201462004284693135670392448782496773481, 37361169637624833127302583281977615660])).unwrap()) },
814 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([23765318454509223469921591210222962901, 44990033695398054388153021046567582164])).unwrap(), Fq::new(U256([31305053791054889490552967007711680166, 27103171064041391806294729422128985527])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([276126480562835962332188871122724756846, 16736136826306680287985573076016202896])).unwrap(), Fq::new(U256([4414319697794720805306194236550991584, 62019193108561650400059767099940339027])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([264396677944255651896935058261233808058, 56759185289081036894005736531633938393])).unwrap(), Fq::new(U256([203628704629834831927134686796555271069, 48471469187143841926703236705753907861])).unwrap()) },
815 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([239115594351165984647685996782641428320, 44545149465284571473177119694335581490])).unwrap(), Fq::new(U256([213078788930221853484251159402089402698, 16763897914971151155224113448725254022])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([114008097138551618719252989046711208100, 59881147407771812558336453348641148192])).unwrap(), Fq::new(U256([73126778233281675579715222201222232223, 38826300453213723195010574430400377747])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([241042448915505783580706972981273226980, 56417003146210087305012985177072964806])).unwrap(), Fq::new(U256([169177487653248888684114813249577243086, 32380354283087538368804702239086564787])).unwrap()) },
816 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([158019800920508883600584012748066500495, 26898142140797843980908437809386681141])).unwrap(), Fq::new(U256([306502797874464726523541130582372927512, 24906313146242057911294566839234262300])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([285760763932996537001129740504018931266, 61805280449958461312688755177468072593])).unwrap(), Fq::new(U256([135545131264687967800977791145788692523, 58139772512775079850670752006123037712])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([70329035009259499088740735594160571447, 39714506367478198523957872987679138518])).unwrap(), Fq::new(U256([140879819390078281356249388675648473246, 43854973736398482797037510623636581696])).unwrap()) },
817 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([38618198701391964654224360704900902096, 38909748519501972274789640400408937483])).unwrap(), Fq::new(U256([180923031305898977193921630254659948144, 27460338595509200370239569387614912648])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([70057505024221161194312385639309591716, 18962666975131076741596858262806841593])).unwrap(), Fq::new(U256([214987899942827030987983709303642547609, 7354730872853772995022889424236171557])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([915509610432328048899540473086774484, 34629556430715177067961193484853677166])).unwrap(), Fq::new(U256([210548802885276746342162088554780886285, 59946451357630926783981469963615555141])).unwrap()) },
818 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([192587880731165909190755427373555634019, 10976459312692912490911357520717353871])).unwrap(), Fq::new(U256([167196051583193420579377374296283755878, 49401460467894024879692901111127305932])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([280565789968090605174946518022513606221, 37294483328987043033011583201807512274])).unwrap(), Fq::new(U256([43162969061744994465193287354736460751, 22121767444104992380234797550707180209])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([253802905125777094707425424576407998829, 61686094369188045360896737944218403088])).unwrap(), Fq::new(U256([182974256593102716284343824242596548794, 7848028085342627691056691434137748535])).unwrap()) },
819 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([84344031984214336466355469526466525167, 27093607600401090432285884248864703045])).unwrap(), Fq::new(U256([149328956099164822232925589118520984554, 2238105695079930209978670615986667957])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([164524345257317963111443719298495629688, 60976929030698527695079882388987310770])).unwrap(), Fq::new(U256([306918226890341242065585311833805224781, 55722896155980110701397483880645840750])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([93128913154066328529254602049518310767, 33148637643432199376041913783211199036])).unwrap(), Fq::new(U256([232426184416524951250597496934702636450, 43720696322889678655980069182616949616])).unwrap()) },
820 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([102654491688396809766739755399745558766, 38605113922396003219627575078289843591])).unwrap(), Fq::new(U256([19593962899181063954599229676070513364, 60350965404501231355169027639339453296])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([159636507003615123705820979675398042179, 54313579941440479276773225578829814974])).unwrap(), Fq::new(U256([119356164563490256235392395335237959869, 39953340372362730589797197026608570557])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([63485981942258213001157530320762394972, 31200716913018312733656963741875248687])).unwrap(), Fq::new(U256([12902614517986449375080893856817237357, 18985985729109591831690040180758616083])).unwrap()) },
821 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([42308655177239821867892255001741444297, 48752963962065714616191625548108808])).unwrap(), Fq::new(U256([128521931437576558829282023068349026555, 41085812991756679343188097975462933419])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([10035402786640614748259945360125292132, 38557266307605033249489727552659930268])).unwrap(), Fq::new(U256([261287166540169381393014929830322479654, 42514049292646924562828996896910004962])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([56319885731071444245573249522306017699, 53577564523791470572591472840806985932])).unwrap(), Fq::new(U256([124520094647080351085028512262335797477, 34908241061842840630524194497782012074])).unwrap()) },
822 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([126634618253919797596379978688751079772, 52145187142657016821034369446677208400])).unwrap(), Fq::new(U256([331622287147942589908508350657315302543, 33514242158733995981408830992771427380])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([120940515437555419164907643421042686361, 17509271053436215565086784812667241751])).unwrap(), Fq::new(U256([115780741523467210253488502554959480585, 32352495928341192491861819481034439523])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([3619315753849520875056567968398971518, 24276420029996570458336249792816120452])).unwrap(), Fq::new(U256([211494135803191326215038551543926917828, 43184746342435212339590913052197238451])).unwrap()) },
823 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([299074873552726234274845052736068685064, 17100980781941753342716208137755315141])).unwrap(), Fq::new(U256([132717845842422445158092928854959930670, 44970515850781790514922204901459656647])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([216867730679061124743178802618784487478, 57695717306148677588162001948381364803])).unwrap(), Fq::new(U256([270218131380365097755408360718075538527, 57477676726794026534017337223493668494])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([165279544895097377741304748618110436848, 20425223618131822879513743747480509436])).unwrap(), Fq::new(U256([74939006713979852471760437945055491248, 13009460391442509409368588074771283312])).unwrap()) },
824 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([66759827770349370888034213457290956508, 9005955800938032302448930728853089346])).unwrap(), Fq::new(U256([132741330683696165661450722498530220360, 7366906791443646993778243377846220425])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([253540438895289112523278690834655986794, 6116090159416232554407405788526490758])).unwrap(), Fq::new(U256([30829083910163512740218467627901622426, 55620601051021580444864016142195538972])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([250454368142865329167214266233567636554, 62357304312342518984982585510065736692])).unwrap(), Fq::new(U256([188759902529624961292182168219378957599, 46090313440960353890072197786322881372])).unwrap()) },
825 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([273524456438634570870477083331972978477, 9134704483036112456254510849911524401])).unwrap(), Fq::new(U256([67478468272116511526567438494812739067, 25741563822514337717816912706935692500])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([116593352960394465976476340555861894686, 41112534697095493718894382902662572752])).unwrap(), Fq::new(U256([120743240887113383410964567068981375379, 38598788085595749947162720656886407615])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([312187803926537773957821532171857044863, 19370576499152094014450666672812249236])).unwrap(), Fq::new(U256([240827398452930199439299722869808431504, 49509218268486878713361004562421178491])).unwrap()) },
826 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([290888165778751654611450374067314903262, 37392757737237359968235356696866333927])).unwrap(), Fq::new(U256([252204440608865557722718990121112495320, 55975286037277175922442767802285953079])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([270039432299546232993592658461203399951, 60656209400809596126720306545905143183])).unwrap(), Fq::new(U256([285724268091089729569589544862374658568, 31963711474983717334851248248346512047])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([3884505529011790540935411500477268151, 13223063167340752683657032423528371760])).unwrap(), Fq::new(U256([159305752483642939978115090933392332739, 7151624608160356980135319367119889070])).unwrap()) },
827 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([272676514700352082082833454466828720931, 25063412692149354657238579769317314943])).unwrap(), Fq::new(U256([293621566718932528694833049559922653569, 45585304922174719737960365050252747127])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([54835393883782456964222308618682542993, 37980634195943561261187934261099825702])).unwrap(), Fq::new(U256([59974402342646963556531661011524247325, 35696051147487701670582425632191066303])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([259456005420154607862207518414346338959, 26130920677564288823895676956971235383])).unwrap(), Fq::new(U256([116130095060524137327607346021153250193, 10807286360635090367088053443993301396])).unwrap()) },
828 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([49392952615759746255132704409838115781, 47072336582571607445080612120532993304])).unwrap(), Fq::new(U256([176567394438796320529775330793638953568, 28322815468520297894419530294472748019])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([80922979608932498062547214350892680237, 39454405473363814575369344229244849847])).unwrap(), Fq::new(U256([91401220192762005776280059066692026476, 15657465297119889794043496656394015039])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([66039352326985904039959490473173702089, 53019684969492620390541557616482571566])).unwrap(), Fq::new(U256([241714503488139707849386627025007386925, 26075944201145781355709956722513636060])).unwrap()) },
829 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([282595284318374809009793390206732488298, 45748314157085313197379566607025256839])).unwrap(), Fq::new(U256([270827203295353120306891953779231995484, 2061164657869992110070755346638481586])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([122772591180024083766405253289036218458, 22573428243213343555967649475581629995])).unwrap(), Fq::new(U256([335389040951271989935196787085438096490, 19801736168278388699669751501679386348])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([339013185026928567100232707644090060309, 19772463825247091662201186215246641510])).unwrap(), Fq::new(U256([4804384610649622501550615795150497154, 23912076036945346828195974346279787457])).unwrap()) },
830 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([214353258010296277687883233632193811097, 5219474783704014986415232032947372339])).unwrap(), Fq::new(U256([79624479932762034850342328421303227111, 56787485322265889115274972394242117773])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([184200025840089548499823017798379240877, 48381517011877532244879858559402925202])).unwrap(), Fq::new(U256([107565933978602112957892502247036378430, 45988606090744912079186414974711188065])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([110593764674444376346243358175616189408, 35712801833060284055338218769830573236])).unwrap(), Fq::new(U256([151909254176585476474604484796159407774, 47158276852552318249500199498589330637])).unwrap()) },
831 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([322742519015404082194067614300530448854, 59892171075434557254836830922854923608])).unwrap(), Fq::new(U256([32065386578298932256366582168915019527, 26752577181504287044232901657950904669])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([174803672897784440584915958306641383601, 41091446500573741637560530719439634516])).unwrap(), Fq::new(U256([209867082646016733058415245870200692364, 62329211607238784395268250532556435532])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([306258598889945544178464082933019504953, 54721033386841216374878966418312958051])).unwrap(), Fq::new(U256([60603024880089835430212078876452798438, 46149383487322374768616169869877411190])).unwrap()) },
832 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([217026022228859342220342856144348485905, 57413523264998851221438276699831981764])).unwrap(), Fq::new(U256([124187198815039194427087238512103681551, 5443344443330946400667796667492661757])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([329826946228202959132718054863501702187, 48520872455619090037740041830322500851])).unwrap(), Fq::new(U256([84830400552123989859098307569998867522, 48734171266537512281517863759978814030])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([223121539165880683751973996773385673012, 25883364325220384256766392563193579368])).unwrap(), Fq::new(U256([299825902106679285294937343573961127742, 60789713632874624396137039249203910802])).unwrap()) },
833 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([256516074149138488025765885988857744378, 14065986710590565469822003697004376208])).unwrap(), Fq::new(U256([234079806620020815932181140203001517322, 23367862088314049165195572485764983176])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([196437028129330799836797952742061629213, 48672430434915053819319219832418834574])).unwrap(), Fq::new(U256([325437437809364731749456582922626432887, 48166506837989600793252437510600468487])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([159308630334191903727834955555702331135, 62436879262294424125843661083177310796])).unwrap(), Fq::new(U256([36766573815532717355926225449599862176, 45422312033048180700369981630678147962])).unwrap()) },
834 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([242582781609674647644149440448686667005, 38749506523024653240132899426202698768])).unwrap(), Fq::new(U256([175781266358339061379292613713370680613, 18670420110220807116103915283744134837])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([330308428247032904787707230285980997411, 57858504610626027326513601505745660271])).unwrap(), Fq::new(U256([137930876960001856905683673437505764610, 51655613850377410017968900339454298922])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([313120701922067853802383887994976410392, 45975781155280265622735990880466050470])).unwrap(), Fq::new(U256([335042362116388769047290965043326509112, 60559196975175152299532260702584038813])).unwrap()) },
835 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([168129439507943874458360855948848921748, 49297302702020077848733359456178701712])).unwrap(), Fq::new(U256([269089028431861389215030548077466532939, 39860705713699087717989916919780377864])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([13752419440807207645064267622022228455, 58622973202646179187733854793315255353])).unwrap(), Fq::new(U256([214305013520593573583121266790878302778, 38633624705329558986475404541639527124])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([149534517263071862076005511513725705381, 33923419970735724678578488530397069094])).unwrap(), Fq::new(U256([52240222008682291290000393139971676649, 13553337484321113195355299279586301175])).unwrap()) },
836 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([272900340425393991345320871434898164480, 38006376657960211098766747755417992280])).unwrap(), Fq::new(U256([261987172512287267237176115671442326513, 8128050373266945066047548013741537435])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([262663552198367871113333688806502970707, 16999213382693802141971886256404278345])).unwrap(), Fq::new(U256([82246501115823748400993076962512494264, 9513280785331020628797944368204762745])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([68225301292307439157467124956340428751, 23984526331358356113154550150873972672])).unwrap(), Fq::new(U256([322267803776402446716363035932937663522, 7643551605057856674267205902314118400])).unwrap()) },
837 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([226851502497426762485425012577563658166, 33899046058167810318161894461593654070])).unwrap(), Fq::new(U256([106137231667287271021611800141669940221, 38201180579053106362613109983520968531])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([75276265667487657641526123222570637963, 18790008468742777802887427569490235644])).unwrap(), Fq::new(U256([236348579709234796216705203173042603149, 51488118889105930334369568118204095717])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([70847827090593073015786004614483673839, 3475230874891506896203634678143642692])).unwrap(), Fq::new(U256([8314731801106602343291488634630259831, 3521008420700564106163167660007668706])).unwrap()) },
838 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([92354268952660546820822603858288251059, 19778715140389467112147527814081030657])).unwrap(), Fq::new(U256([329385425284714431570594794971018721246, 60587846563454266929636649850787073194])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([288633840471857542459144118014949000217, 51605528849039220650036727626483827846])).unwrap(), Fq::new(U256([251176740648089198193669671101929833609, 48200613470991338988108533743364275499])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([329193234492690486040808567065220826749, 21378867659438236011740749197423248550])).unwrap(), Fq::new(U256([285725508694128806244839238836446595834, 16111887615812423492270406970991867951])).unwrap()) },
839 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([152431519506548878061194990588057484057, 49184327280433466363535994383523121304])).unwrap(), Fq::new(U256([176897083100713735838942116510311567986, 37010995217032206230292299420436992452])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([130722401240365261778198584122678322362, 19293082368638790806988358431355209633])).unwrap(), Fq::new(U256([336032502313799023638100083369289206306, 31063427179065887680551186789065013379])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([313119215174514058940660103265063450959, 25103967051083955792570154250792420707])).unwrap(), Fq::new(U256([31520718700780462452153016807890400365, 3961179668062303572693429066523266858])).unwrap()) },
840 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([36667789591031625960240713279402427923, 5637784774941607531109129433890086293])).unwrap(), Fq::new(U256([286537786902526732905389646221547183400, 56733382321156059969864849624810202662])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([318803650206508773198520172514605937449, 26727846312556415159035648438885744059])).unwrap(), Fq::new(U256([296466727057750056890238712141155450998, 27938872020780344351123790536034462604])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([324800882000687209140271724886698306645, 44039571306759586933457684739133508444])).unwrap(), Fq::new(U256([47608245752536926992583482090528259005, 47831283599118732387313994410846427415])).unwrap()) },
841 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([234409386597337888556308771844488457344, 12956567414802650084616113864217789370])).unwrap(), Fq::new(U256([54897214305565100948138138335662741789, 15577398627840234231085370089137424292])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([133034516621699526624516176710921958663, 21900941110792896857560826682314104825])).unwrap(), Fq::new(U256([91786723574963827227124861422083359642, 15631994703072556950984703411138300334])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([61994842824519647915165015802348883298, 3606506510538278544457628965895793792])).unwrap(), Fq::new(U256([176676927874382884508332121131239871282, 40115823781693004388411447312641595826])).unwrap()) },
842 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([16486756791893393092802670694010735350, 51213785425023838862657045286249924761])).unwrap(), Fq::new(U256([127374475998848055784786954745585192546, 61927128032062116041960763307324749312])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([57390628646817442883586335851238696952, 1896752620999083872406878608493454725])).unwrap(), Fq::new(U256([221112496886805666932828220912733522283, 2796002105546343313342619098780470642])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([126113866998638086000522716026151108800, 58788515162206661637618804912085907736])).unwrap(), Fq::new(U256([19079621171649876430748555077055696995, 46826087543975014201271501761421031441])).unwrap()) },
843 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([287527947651847089840085422221128698763, 3282429064220775329203352366444259855])).unwrap(), Fq::new(U256([307065141447870398567299540185758941637, 48193280250277647112409463651594786821])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([39874018308944266937776827490734641649, 37791912819895325896728519088674532352])).unwrap(), Fq::new(U256([11619188332176047954604673895207823813, 12983193006960451462302777892755566431])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([76045219079383487030610001932731241273, 16472513195230803643409094386807276517])).unwrap(), Fq::new(U256([244195048886093761135684126358283143738, 19479715688038787901591036330200598003])).unwrap()) },
844 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([10563934263428977131610186732868968998, 14179527485555674710937849675581267906])).unwrap(), Fq::new(U256([257836314204976892854269567329818300595, 50763620022812667001295845381267741649])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([160125271831790681583846356333205971645, 38979573670559551140762745448476278558])).unwrap(), Fq::new(U256([258822435332825467396267733276444939733, 19361127335094998017975714923507638644])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([5086484109635987997951722709219418410, 11993539151823720275736182499510363133])).unwrap(), Fq::new(U256([171423009691627362399764794909459189507, 20404136165000279368092981943358482669])).unwrap()) },
845 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([249500614137833023351305910264605008487, 42310425481082032237299673828774704531])).unwrap(), Fq::new(U256([253496613223900195266106558972296162241, 19046240361322320039639197826388768369])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([72263736846331351747703353269055941107, 19969702258117494907412570199791763548])).unwrap(), Fq::new(U256([126173921786290239044307538655562024455, 18006158804314393556918633600879748967])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([16201000244836956196708576962189993382, 32135871927111623648702296852432295139])).unwrap(), Fq::new(U256([186963801770492707056456887595022261993, 2392014174637078162701730926122882282])).unwrap()) },
846 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([8922998024015454766590940715881630670, 55567280047347356367358986337109964651])).unwrap(), Fq::new(U256([313173491437683437471834863194199707492, 3438176908844635046641649505356812159])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([276513541262028309851175912267910407247, 11212571122829858982221117734299851084])).unwrap(), Fq::new(U256([259509606552160006313745402323745013955, 43114059504717558659877851383190095613])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([28098698558455079499553496395853067214, 2999821831816440733771768849050005165])).unwrap(), Fq::new(U256([334260605709723946550826958885726232933, 11843826148467149350077605819950881153])).unwrap()) },
847 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([283455530950531157108702282345810919101, 52521643621580203161269969006017546759])).unwrap(), Fq::new(U256([118200123115287742628646618978307449884, 14795263733303838767894982171654042612])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([132641709504834974317245159550618583253, 28570895840581787308435502085893978558])).unwrap(), Fq::new(U256([229949535634549796967397304164923056337, 42352202734287711800564772982472784839])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([128199924683494278990710814519750517837, 58638050185358930355922065483565476451])).unwrap(), Fq::new(U256([4192016302378839786647752861109219112, 61114929591783829703998838288847230811])).unwrap()) },
848 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([113754079486581376183180167710451964178, 25998717875687967363117010408717972365])).unwrap(), Fq::new(U256([133697001740616163002906224555636440440, 23546896556625148713807775573505304385])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([227150749988479615370826356819617461792, 41368715815045829029754124051658999245])).unwrap(), Fq::new(U256([222514455614853854819390285868145864687, 27579121989432504016710179821357885225])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([128433415390196263739951350076595027296, 55636438757722652255615208065796848545])).unwrap(), Fq::new(U256([94671524825895380554049635623924644741, 9791488862788864605119131922310440121])).unwrap()) },
849 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([153881172058568095361353223261899310845, 17156484263879687903526208855029043640])).unwrap(), Fq::new(U256([192077265925835218260989605323461142787, 32318605747689071742310867751273886645])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([317408271159096505221916390262196469787, 49717575675851229633286204770028665263])).unwrap(), Fq::new(U256([230095008714744652782426759714312292445, 34768179553549400629211217553610554586])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([259609023305192462223688164733847327699, 55498873698881710203435569648274404478])).unwrap(), Fq::new(U256([256325550082466559964506871761831536884, 46357653853657184428683736043713888406])).unwrap()) },
850 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([174577378801081039438668831531067795857, 53157547858752109585449689851969780390])).unwrap(), Fq::new(U256([82151408345597494862089436182070514285, 16466308473185906030465208465158400278])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([245839213042247079917412337301921026922, 22457019238909031929740892448482221879])).unwrap(), Fq::new(U256([109513130235370254599453293800543710331, 47936140417415756744920707247895308699])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([10776161146456696103487000724300150234, 21638563281306406703950273508675616623])).unwrap(), Fq::new(U256([207085916216710302318982426148546767798, 7534717897684081051796825732586695012])).unwrap()) },
851 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([155690435801319682239901337205468120840, 57696234800836220244083348626584391674])).unwrap(), Fq::new(U256([126757665472044690642504190674454727065, 27689844381974512633117281405461511493])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([111371524430469273469505773333501881358, 41915381107432591576214180621073813739])).unwrap(), Fq::new(U256([8591307386286176660318558848651389629, 19322060932256059989553597776338707006])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([34843642275668259465089406915935512474, 25199006933559492770352887141455099490])).unwrap(), Fq::new(U256([10645844381812821922686446509476566858, 35688555865712593739042131731320400759])).unwrap()) },
852 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([189947043026848705532544415056376972054, 16798314122488336019972329871173393651])).unwrap(), Fq::new(U256([308591971026665862143792543801958855960, 31580607213628921122716343407923691193])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([163093806147198705684115256712150030567, 61044717475711501197927838344963107641])).unwrap(), Fq::new(U256([307478741843426518207523544103444625011, 32813498165003357356133773951397928550])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([95291548361985054080716711033104720817, 32740343556226865497613504752972186774])).unwrap(), Fq::new(U256([99656074525833092595857684423204299816, 52556239373424263650635019541397500598])).unwrap()) },
853 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([131443621763425639580622258813874957855, 17694455882904396881053896673232601550])).unwrap(), Fq::new(U256([57787363176533727883752817750279248594, 1322856562657561354884935596241784834])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([41788280636884607828680584839495219344, 51762903848994175839707598487572362464])).unwrap(), Fq::new(U256([327908654890111464339978492292915303796, 47344360741808824501224988176645616993])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([263873593879772810642440946076120656306, 60210233792451977377881584251511840173])).unwrap(), Fq::new(U256([77405826008973124400288647208797749719, 53019697808811506999139211676029205579])).unwrap()) },
854 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([254578463202394703187741702356839561011, 21096342408485278014732435848864790919])).unwrap(), Fq::new(U256([230473548197181635567135409745417796610, 22147822186055995413422331229074559071])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([336878015160301245042116518989872047941, 3472301140759760061593082376347546380])).unwrap(), Fq::new(U256([16684778019642037346902548590480489922, 63174559216409847644418505532384053845])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([7942031074055265340884029455787976403, 46511448608441944387405531466341650044])).unwrap(), Fq::new(U256([201615416576767918339868727158350994412, 64255187168173681561527383354144167567])).unwrap()) },
855 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([243308496406996226443557446925779452296, 45849597498959240205941430238383669623])).unwrap(), Fq::new(U256([48147614977633577164856356428453305437, 24480032415168951986931189038013914221])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([74830884455458736654394458059429787198, 19093844097308332947749289150298249126])).unwrap(), Fq::new(U256([33643899737753431509498018706471379301, 1054576208141392093174173023229894557])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([25010096200987042473393926604632958161, 36666296164531908118513449509789076579])).unwrap(), Fq::new(U256([33471717379111372311065514803349918045, 39155528164590001712332400960607038189])).unwrap()) },
856 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([130247467451091212947023092388792369109, 5581090585326037371051920053369586280])).unwrap(), Fq::new(U256([322360327312842929444256001314179239601, 39584478520709259399389327249557202594])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([109138162516199992788447200019766811785, 47525457821584643247627414608651841972])).unwrap(), Fq::new(U256([76309968110600274074720854748516338453, 54739068402347163521228676077893779893])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([42180409310074737345249452446852603163, 4374831491626595184699821621506201010])).unwrap(), Fq::new(U256([130057601620482713644106051600317093239, 12010679381934251249551375242240386988])).unwrap()) },
857 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([203271863519350650976092478448210829293, 52275506975977654460248547356331731574])).unwrap(), Fq::new(U256([87019891938199989232789187109647269328, 44030460834732836505939430240249354505])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([110171920943726136564495503552694726540, 18110786866783187141705660513534498930])).unwrap(), Fq::new(U256([86561692963802460817471572747090778194, 16449770553500671507593674575394564582])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([221876651023252602014468911202182579931, 20555077542463658872780769250687782117])).unwrap(), Fq::new(U256([121263539082468326970413896423548540112, 61907715120666399953423277772754945448])).unwrap()) },
858 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([290357192776207049084050762947655748975, 57738753410091871200747406135207764151])).unwrap(), Fq::new(U256([135358190114239175182066780052123869069, 56081507988571629614406051210751609147])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([195622544431419990666685034787625747326, 54874770374981417815900652767969731611])).unwrap(), Fq::new(U256([6243075893789260529022348241785972764, 56521196313521047823704039910006760241])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([243495103440406073534289072917982313090, 57589106164518822864160786869003317813])).unwrap(), Fq::new(U256([235848100009165241948327269818774314127, 37511141642844469126769577281189903937])).unwrap()) },
859 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([78592775623001882471215007061407141105, 34941191764695857355641077076235001298])).unwrap(), Fq::new(U256([267356444067983260493350102628306009780, 28391440678450825886400816194304466425])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([315751120363918319477358949808863604966, 2531298718421943078485957285990819438])).unwrap(), Fq::new(U256([242692025957487623878146464744369629015, 52949313651228011397330521709355671746])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([223701370913329345484604486557612973051, 34658421329859066183602046886271845301])).unwrap(), Fq::new(U256([239281725801587867757289553693045231533, 14055606950515704853984812857124180593])).unwrap()) },
860 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([321292144065398009067550865125623060600, 18828918489183113618746569726487918435])).unwrap(), Fq::new(U256([44953266506250560322305758112518632685, 12818035770624926325618902943072758802])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([198712001500603483304766131655796332238, 37755493054877351200992330081944773222])).unwrap(), Fq::new(U256([203494901390506520787336076745404879355, 9719695048988798612029815523720059188])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([324082713664478232334150476671698670935, 57968755532718074766231332303127747698])).unwrap(), Fq::new(U256([172967914786650717580471091827836918517, 39836011047291872323399953583350854813])).unwrap()) },
861 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([104693003047177326696685168159983773290, 21362453846755563999603040010467525220])).unwrap(), Fq::new(U256([56374029098225126238873495696162227687, 61374137561202742141169238350854689065])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([158941255788517659316631775720407312680, 2477940982651390357755438379586767771])).unwrap(), Fq::new(U256([212374631437158170731256215648153937637, 51786402687200191668238627015343248084])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([67431426417348416607065438525684723676, 59371917376780773574999923335758781547])).unwrap(), Fq::new(U256([329921395426288970367695061271959172005, 18586382421107856878975478269722973139])).unwrap()) },
862 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([207312856868420470351121539398556490708, 6749469271717320482346458817170377645])).unwrap(), Fq::new(U256([297463942398989836675066926378547228117, 42180709737629195771943741993025816145])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([294946811578231549301710677543341218799, 20992621396953319301177509266206083412])).unwrap(), Fq::new(U256([334489506001987366917405458471890123881, 5644882162624043756459843063788196004])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([339439573540481749394421794993912483199, 45246606008318294125455064627263238236])).unwrap(), Fq::new(U256([270454941633781906426514756020454116063, 13672349860189847846584888881208438638])).unwrap()) },
863 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([184109810967303467375292693011959132443, 22047527326459095654736206729881706277])).unwrap(), Fq::new(U256([306855976122543936818762044270256507301, 13817693020515802987708208420701920996])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([18944943876649574380290951381571111173, 54681599103691370082120691137707858843])).unwrap(), Fq::new(U256([274481158071926596330090943889570839876, 27656594457831811691825162550052997369])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([72068179718393541085353916977402777797, 23085120169523399820600846468009840798])).unwrap(), Fq::new(U256([39623239893391127442673085065505018616, 49714238177847283522861975755429582165])).unwrap()) },
864 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([326109292257788796875387755492793672492, 38017828106840559651298055936334805586])).unwrap(), Fq::new(U256([300970874956733001966255469483871058465, 61693366793354972449623979259561628170])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([247117608403708963869106314083852845952, 6270392892469923670893298889464309224])).unwrap(), Fq::new(U256([85229127169148541816114014402805254942, 23692121466582475015916841187427174063])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([322377160078698123442118813943355848949, 44790955831224275605828973863316334451])).unwrap(), Fq::new(U256([246121457999548771204074791900055840778, 36237050391238930724599650306620108395])).unwrap()) },
865 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([6479203335512729615298164829455402801, 18899857059703106109444283718983930606])).unwrap(), Fq::new(U256([307909459343553081188134777979054198524, 23104930848877581327335450138633822165])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([117408687141894236273811591312978416850, 27064268635283187986411799061361560149])).unwrap(), Fq::new(U256([89156958054425667145216654832344296184, 28498155840544447742658664925302722255])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([264148561516468171084137463578412456470, 56169366101528802291286278394614323282])).unwrap(), Fq::new(U256([80748178838045963455037482306103002135, 31627837956246452962075717178352705444])).unwrap()) },
866 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([228462396223758741175275124462032775241, 541217865154375519097785562072294622])).unwrap(), Fq::new(U256([147690209661782719555035340726175029695, 47332569057322758675859735205860987883])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([332785070356059610611838282826287767824, 28411804506527712891888004500289036321])).unwrap(), Fq::new(U256([80347503246503526995863756906237840538, 40694963808412110626720215103350226072])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([73246772632733278293170183313248743910, 51739954405360462951359314934075204478])).unwrap(), Fq::new(U256([323288654986485051471927026636004277374, 55328786519754064217639655113348868700])).unwrap()) },
867 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([107183915141555030394480811055835932411, 28743337205734819613101267731330261274])).unwrap(), Fq::new(U256([265627562010734120334801493421007962860, 21113833505065027987742094370311235794])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([145390576010968264278778301175950417032, 25603093564950634850451680305056487937])).unwrap(), Fq::new(U256([171224041010792018070371064846900704811, 50628689539088695975060303147297926726])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([181636860313080877557100227907605412643, 19106550849436726002022081116470620327])).unwrap(), Fq::new(U256([97037887458346176230370696042525047476, 38315199704917257726469887714210863756])).unwrap()) },
868 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([165755565692898412861884552154775259871, 23293168176203049828541858307098176114])).unwrap(), Fq::new(U256([111579679857180892572143097811395790681, 21526844626039124934190021704344618627])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([335258764315672309726009417334449368453, 103404408330729181499563204707141002])).unwrap(), Fq::new(U256([191071126894978514432059671553769626880, 5078535341242500657674641815850178322])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([72579254643315478022492189073789591450, 44167090301255006150406576419615402343])).unwrap(), Fq::new(U256([183377394594390630206602640102963629039, 23990882412966103396841140729654425350])).unwrap()) },
869 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([66679892701097417538832992488778691458, 58183623071000462877229489086029090922])).unwrap(), Fq::new(U256([294287188350011499287443971281382798030, 18151493196929461249661140690411179165])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([74805142807574287323605988196524192535, 18100483139591925759594156937304667397])).unwrap(), Fq::new(U256([139120256621259665435015294709846943744, 33412960622211929574259219789625651855])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([220224993980394484792559335583346867939, 59970476024877867633551960552156066815])).unwrap(), Fq::new(U256([165016476912320413641798011850430383400, 40087637389842239140931509158661317913])).unwrap()) },
870 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([26669149284356063486638395246213038984, 48932308059862148467989673144064276455])).unwrap(), Fq::new(U256([112017260916309235481572722060511554523, 1292432061742789600481111238179731344])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([162967204317649679020730851544536806310, 32746563765578881184210706548921981702])).unwrap(), Fq::new(U256([337655071624981772776297963145482993016, 33145701869840355692613219913227327224])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([116821007696021810040981824168902376385, 46826353416371587869648603174437608629])).unwrap(), Fq::new(U256([286148963257224477643539266261574758187, 20903220425259595580283778435979469601])).unwrap()) },
871 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([265822762895337622674318289864003560145, 62593533822281897460585212079268870737])).unwrap(), Fq::new(U256([180099245396770402139578639916315358651, 5289891249371566111607452004558281256])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([215101732208902323596827687492609836402, 60151379040875297433910120987825047988])).unwrap(), Fq::new(U256([200376206977547885622799626268846642009, 36107786828287031009478318323095814795])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([228632312969356396843036040954049410317, 29609317291386275502321244693464627541])).unwrap(), Fq::new(U256([250663071554126685512943663183005740350, 26707628083885320745488595167117920892])).unwrap()) },
872 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([167686658731994607869572641962208584698, 32951815034866853047881968952398296677])).unwrap(), Fq::new(U256([59732843077777643290110085014121270991, 19690792480201713304572701985281652804])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([231501326469976058677041937508913323742, 58052555179693233540250119022219467367])).unwrap(), Fq::new(U256([268839178939407644460103390562050250791, 51244123519915889574699519125947948433])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([15335721378364717505397406493265760457, 50543654003772338617069451450467156120])).unwrap(), Fq::new(U256([147668560194510325215853847922114742688, 63436405408432434981818242494869996954])).unwrap()) },
873 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([324251215335356818847634371978327094047, 53597895382917031159515977918983297046])).unwrap(), Fq::new(U256([336456284126450597413571076848285070208, 9834107003348358663468869495065030828])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([4893147365574829346121011245181836636, 48500338802265501645481354399443896938])).unwrap(), Fq::new(U256([242843203647079174857073639694991742532, 39587639177217829196831837775949647839])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([260649755517395052354543412976006056345, 41595453161552128661866319661777368912])).unwrap(), Fq::new(U256([186473459113497941493205881377276393540, 10232971634855792150439771169030969984])).unwrap()) },
874 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([153156583597535672033657311231041916285, 12870713391441384499488044032601826356])).unwrap(), Fq::new(U256([160011046903032247780660416127755862604, 59740658966179967701258870119449151446])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([53781592683706300755113958689829175735, 5160246898475719650527746461036900035])).unwrap(), Fq::new(U256([127406077100448076503048857464807666048, 5027357614298608514576108334690214373])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([123323183388079598137911866134337668968, 30831375761327507469744048674597209517])).unwrap(), Fq::new(U256([282757148554018853521114389661374143994, 41725892078174834143829265912887863334])).unwrap()) },
875 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([231142251602796484920728141361531696801, 43369687276260799996121293373394088506])).unwrap(), Fq::new(U256([193386979673076292922455602081629373614, 13517077812823754339540367661413555482])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([32945933209167472962271089890685376310, 23645956766949617286909797728539496219])).unwrap(), Fq::new(U256([128873344219688070096765758438736759675, 54069131826062143440208582926638727688])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([307107650784744827303698892877513591771, 60627460508970098579939906160617347629])).unwrap(), Fq::new(U256([12937320851881845879608263929438137281, 2854427512588805421842219517935335360])).unwrap()) },
876 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([262645642925640470181426114122199935672, 23413555848620303130105885294683582394])).unwrap(), Fq::new(U256([23944562046020585348174388998952320194, 45084472027505841447432550475379032518])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([270949855981998457946423634423510659239, 52707980377915683827433393510154134085])).unwrap(), Fq::new(U256([171166926104504281831828631675145047352, 56745888676097625411354427758332611365])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([185108543406354812098225836322332681649, 38838551534016041901193249287881822376])).unwrap(), Fq::new(U256([220861944829798693206584739921489268335, 13318922669002030127185287983351463417])).unwrap()) },
877 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([262922874647306006405720155830997014046, 60319938491997842161822791958118330349])).unwrap(), Fq::new(U256([267514688756785010198929321975858316586, 13431251784763107399729556927577885114])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([305051645126359797430782278231595423685, 47480212315012773203275805598650271426])).unwrap(), Fq::new(U256([61134576323692903306240935503639110628, 44357224521089864059608299955085658945])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([288996190497897556231840221598596395445, 7566958477031738465971309099100507442])).unwrap(), Fq::new(U256([133814060971580680876860604680251720534, 59538638355154696335532006425868258220])).unwrap()) },
878 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([255893828824714285725760712618255298338, 62054226651551009305279711173141456137])).unwrap(), Fq::new(U256([278488875745654026562827558863613836490, 21008509582420141439141693540430274581])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([275369357162135051507223962619748317962, 42306683939383379782070254815636483252])).unwrap(), Fq::new(U256([33692084190064829046021773249716169044, 16341905693988596015816579527114077079])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([50744916563901235043837175537752247974, 10172930551818555335663216915059687204])).unwrap(), Fq::new(U256([130434124352272975884189586048532260990, 23515228462564504103971218058319726320])).unwrap()) },
879 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([315580665939248091007437797584737352150, 29708290546079112870849940807626622733])).unwrap(), Fq::new(U256([74442409874403535705044740324158268395, 1505199293980260993350511103843027993])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([250928935731107472086814134230515792409, 5910959187805062997477270179924851551])).unwrap(), Fq::new(U256([280398933657384735588696583124789483429, 54783439113329460653893087793006061735])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([251724005393716093632013371894721049773, 58848512269437108583523559065211202801])).unwrap(), Fq::new(U256([31822195116686689031909059702809975955, 24789755710735999475756589726034740758])).unwrap()) },
880 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([311444467767445140793669110935330311306, 49680899512402416135008064018078765713])).unwrap(), Fq::new(U256([260075513883026034919987130520464438138, 71886459212490540714376620236740887])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([218811104197169142519581806122315915406, 2003975639040616906277810278389252752])).unwrap(), Fq::new(U256([162449781759514417953768678035934414521, 20826616094394726852079185751274539294])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([50122791277121629311774878848574398797, 41715208027917549489096484827508261925])).unwrap(), Fq::new(U256([61219715357759165032395798940008313361, 55168729445645977375765036089053114792])).unwrap()) },
881 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([79415664581913745231517567945561341244, 19961313692291707660858657690356279927])).unwrap(), Fq::new(U256([169697929611525566517558862344260578764, 32844991405556424161021671060175948696])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([97951068758528710273694591785781637499, 18749313896215455583118043227454839626])).unwrap(), Fq::new(U256([86416223342992670848217929911878122430, 45508713650090104780284816480988042321])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([205166366708889542887913296220698256110, 64283667930878381361054417761490454821])).unwrap(), Fq::new(U256([127337780289640176933683040648197123026, 45189848010560984741741084451460237213])).unwrap()) },
882 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([339284773562100413239152438122508204497, 50631498218339719105248005011418978450])).unwrap(), Fq::new(U256([33417073692493560040015373020490319634, 35461627756915585142929292352046627148])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([65706590332479893819786959846945487398, 49780412881756049082474794087182699607])).unwrap(), Fq::new(U256([52838637386752308153448355705238086197, 25418435995504719731730063829115376943])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([272342513391057040499052435082268704444, 40734611488359738470851428580967866388])).unwrap(), Fq::new(U256([195464905670800875597868841505577455579, 4567343913211817054294335942572391310])).unwrap()) },
883 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([116585277318795490267488822790585572050, 60535270002127930300661104257194060095])).unwrap(), Fq::new(U256([60938698090087736292473391678184666708, 13034546647626058553077909902189865751])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([173096860849358760769153772521753734944, 46051492674049147015395144791836438516])).unwrap(), Fq::new(U256([217847244022302146556080020387033222160, 3714600728561149832835084325311638745])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([86749998713441153819371623717671780463, 22072421598347798931100735874660737662])).unwrap(), Fq::new(U256([318879363093875678699747171034610586590, 20824335590094416898946169150944599166])).unwrap()) },
884 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([64488889092438538888826783837043953900, 37184887689753051095864638902885179863])).unwrap(), Fq::new(U256([30545663928156310707528798028105042841, 35594636889744346303301050561815356233])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([25983064025798529721395241242693795182, 8818840193887973907465639723423785685])).unwrap(), Fq::new(U256([258083038864362618956338229292791076964, 59924965332411444209484507096908691451])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([220038260912754873591576914680586914279, 13066051346629987308799710602199271472])).unwrap(), Fq::new(U256([145490690982645862542497983334649610263, 61905193484912278316593689230425266660])).unwrap()) },
885 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([91140715068468329987534450751429662322, 19286363177619277557153390898202185703])).unwrap(), Fq::new(U256([93820352751188715792914230189639637744, 34108640942617057347076989500332659863])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([64028159517497609933876978072339213461, 15242545675596643010631683990284567829])).unwrap(), Fq::new(U256([196818140250582584748747602707707107768, 19674391967426090834762688793787807910])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([240272916169613868056785616677104041776, 43506134861603450917738367078393340621])).unwrap(), Fq::new(U256([4195648020339163006604321850508278540, 4672904647050343041579830671683109182])).unwrap()) },
886 EllCoeffs { ell_0: Fq2::new(Fq::new(U256([76224289879065773445423898190273853011, 29552147447995384759425668183293749353])).unwrap(), Fq::new(U256([131775855227043960655312514543203001277, 11525949759137095338012468817574456927])).unwrap()), ell_vw: Fq2::new(Fq::new(U256([24164999216876611682317527533876599098, 1468136824339634997687523096410027135])).unwrap(), Fq::new(U256([287955683679784091578386069448645460818, 42392463637589581183769087503942163916])).unwrap()), ell_vv: Fq2::new(Fq::new(U256([277415168387483520146499744266583303231, 43682907322954483721445867111493680565])).unwrap(), Fq::new(U256([231497467135626786731515493713118585761, 56904388641135605566397108514408579757])).unwrap()) }
887 ]
888 };
889
890 assert!(expected_g2_p == g2_p);
891 assert!(expected_g2_p.coeffs.len() == 87);
892}
893
894pub fn pairing(p: &G1, q: &G2) -> Fq12 {
895 match (p.to_affine(), q.to_affine()) {
896 (None, _) | (_, None) => Fq12::one(),
897 (Some(p), Some(q)) => q.precompute()
898 .miller_loop(&p)
899 .final_exponentiation()
900 .expect("miller loop cannot produce zero"),
901 }
902}
903
904pub fn pairing_batch(ps: &[G1], qs: &[G2]) -> Fq12 {
905 let mut p_affines: Vec<AffineG<G1Params>> = Vec::new();
906 let mut q_precomputes: Vec<G2Precomp> = Vec::new();
907 for (p, q) in ps.into_iter().zip(qs.into_iter()) {
908
909 let p_affine = p.to_affine();
910 let q_affine = q.to_affine();
911 let exists = match(p_affine, q_affine)
912 {
913 (None, _) | (_, None) => false,
914 (Some(_p_affine), Some(_q_affine)) => true,
915 };
916
917 if exists {
918 p_affines.push(p.to_affine().unwrap());
919 q_precomputes.push(q.to_affine().unwrap().precompute());
920 }
921 }
922 if q_precomputes.len() == 0 {
923 return Fq12::one();
924 }
925 miller_loop_batch(&q_precomputes, &p_affines).final_exponentiation().expect("miller loop cannot produce zero")
926}
927
928#[test]
929fn test_reduced_pairing() {
930 use crate::fields::Fq6;
931
932 let g1 = G1::one()
933 * Fr::from_str(
934 "18097487326282793650237947474982649264364522469319914492172746413872781676",
935 ).unwrap();
936 let g2 = G2::one()
937 * Fr::from_str(
938 "20390255904278144451778773028944684152769293537511418234311120800877067946",
939 ).unwrap();
940
941 let gt = pairing(&g1, &g2);
942
943 let expected = Fq12::new(
944 Fq6::new(
945 Fq2::new(
946 Fq::from_str(
947 "7520311483001723614143802378045727372643587653754534704390832890681688842501",
948 ).unwrap(),
949 Fq::from_str(
950 "20265650864814324826731498061022229653175757397078253377158157137251452249882",
951 ).unwrap(),
952 ),
953 Fq2::new(
954 Fq::from_str(
955 "11942254371042183455193243679791334797733902728447312943687767053513298221130",
956 ).unwrap(),
957 Fq::from_str(
958 "759657045325139626991751731924144629256296901790485373000297868065176843620",
959 ).unwrap(),
960 ),
961 Fq2::new(
962 Fq::from_str(
963 "16045761475400271697821392803010234478356356448940805056528536884493606035236",
964 ).unwrap(),
965 Fq::from_str(
966 "4715626119252431692316067698189337228571577552724976915822652894333558784086",
967 ).unwrap(),
968 ),
969 ),
970 Fq6::new(
971 Fq2::new(
972 Fq::from_str(
973 "14901948363362882981706797068611719724999331551064314004234728272909570402962",
974 ).unwrap(),
975 Fq::from_str(
976 "11093203747077241090565767003969726435272313921345853819385060670210834379103",
977 ).unwrap(),
978 ),
979 Fq2::new(
980 Fq::from_str(
981 "17897835398184801202802503586172351707502775171934235751219763553166796820753",
982 ).unwrap(),
983 Fq::from_str(
984 "1344517825169318161285758374052722008806261739116142912817807653057880346554",
985 ).unwrap(),
986 ),
987 Fq2::new(
988 Fq::from_str(
989 "11123896897251094532909582772961906225000817992624500900708432321664085800838",
990 ).unwrap(),
991 Fq::from_str(
992 "17453370448280081813275586256976217762629631160552329276585874071364454854650",
993 ).unwrap(),
994 ),
995 ),
996 );
997
998 assert_eq!(expected, gt);
999}
1000
1001#[test]
1002fn predefined_pair() {
1003 let g1 = AffineG1::new(
1004 Fq::from_str("1").expect("Fq(1) should exist"),
1005 Fq::from_str("2").expect("Fq(2) should exist"),
1006 ).expect("Point (1,2) should exist in G1")
1007 .to_jacobian();
1008
1009 let g2 = AffineG2::new(
1010 Fq2::new(
1011 Fq::from_str("10857046999023057135944570762232829481370756359578518086990519993285655852781")
1012 .expect("a-coeff of g2 x generator is of the right order"),
1013 Fq::from_str("11559732032986387107991004021392285783925812861821192530917403151452391805634")
1014 .expect("b-coeff of g2 x generator is of the right order"),
1015 ),
1016 Fq2::new(
1017 Fq::from_str("8495653923123431417604973247489272438418190587263600148770280649306958101930")
1018 .expect("a-coeff of g2 y generator is of the right order"),
1019 Fq::from_str("4082367875863433681332203403145435568316851327593401208105741076214120093531")
1020 .expect("b-coeff of g2 y generator is of the right order"),
1021 ),
1022 ).expect("Point(11559732032986387107991004021392285783925812861821192530917403151452391805634 * i + 10857046999023057135944570762232829481370756359578518086990519993285655852781, 4082367875863433681332203403145435568316851327593401208105741076214120093531 * i + 8495653923123431417604973247489272438418190587263600148770280649306958101930) is a valid generator for G2")
1023 .to_jacobian();
1024
1025 let p = pairing(&g1, &g2);
1026
1027 let g1_vec : Vec<G1> = vec![g1, g1];
1028 let g2_vec : Vec<G2> = vec![g2, g2];
1029 let p2 = pairing_batch(&g1_vec, &g2_vec);
1030 assert!(!p2.is_zero());
1031 assert!(!p.is_zero());
1032}
1033
1034#[test]
1035fn test_batch_bilinearity_empty() {
1036 let p_vec : Vec<G1> = Vec::new();
1037 let q_vec : Vec<G2> = Vec::new();
1038 let r = pairing_batch(&p_vec, &q_vec);
1039 assert_eq!(r, Fq12::one());
1040}
1041
1042#[test]
1043fn test_batch_bilinearity_one() {
1044 use rand::{SeedableRng, rngs::StdRng};
1045 let seed = [
1046 0, 0, 0, 0, 0, 0, 64, 13, 0, 0, 0, 0, 0, 0, 176, 2, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 96, 7u8, ];
1051 let mut rng = StdRng::from_seed(seed);
1052 let p_vec : Vec<G1> = vec![G1::random(&mut rng)];
1053 let q_vec : Vec<G2> = vec![G2::random(&mut rng)];
1054 let s = Fr::random(&mut rng);
1055 let sp_vec : Vec<G1> = vec![p_vec[0] * s];
1056 let sq_vec : Vec<G2> = vec![q_vec[0] * s];
1057 let b = pairing_batch(&sp_vec, &q_vec);
1058 let c = pairing_batch(&p_vec, &sq_vec);
1059 assert_eq!(b, c);
1060}
1061
1062#[test]
1063fn test_batch_bilinearity_fifty() {
1064 use rand::{SeedableRng, rngs::StdRng};
1065 let seed = [
1066 0, 0, 0, 0, 0, 0, 64, 13, 0, 0, 0, 0, 0, 0, 176, 2, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 96, 7u8, ];
1071 let mut rng = StdRng::from_seed(seed);
1072
1073 let mut p_vec : Vec<G1> = Vec::new();
1074 let mut q_vec : Vec<G2> = Vec::new();
1075 let mut sp_vec : Vec<G1> = Vec::new();
1076 let mut sq_vec : Vec<G2> = Vec::new();
1077
1078 for _ in 0..50 {
1079 let p = G1::random(&mut rng);
1080 let q = G2::random(&mut rng);
1081 let s = Fr::random(&mut rng);
1082 let sp = p * s;
1083 let sq = q * s;
1084 sp_vec.push(sp);
1085 q_vec.push(q);
1086 sq_vec.push(sq);
1087 p_vec.push(p);
1088 }
1089 let b_batch = pairing_batch(&sp_vec, &q_vec);
1090 let c_batch = pairing_batch(&p_vec, &sq_vec);
1091 assert_eq!(b_batch, c_batch);
1092}
1093
1094#[test]
1095fn test_bilinearity() {
1096 use rand::{SeedableRng, rngs::StdRng};
1097 let seed = [
1098 0, 0, 0, 0, 0, 0, 64, 13, 0, 0, 0, 0, 0, 0, 176, 2, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 96, 7u8, ];
1103 let mut rng = StdRng::from_seed(seed);
1104
1105 for _ in 0..50 {
1106 let p = G1::random(&mut rng);
1107 let q = G2::random(&mut rng);
1108 let s = Fr::random(&mut rng);
1109 let sp = p * s;
1110 let sq = q * s;
1111
1112 let a = pairing(&p, &q).pow(s);
1113 let b = pairing(&sp, &q);
1114 let c = pairing(&p, &sq);
1115
1116 assert_eq!(a, b);
1117 assert_eq!(b, c);
1118
1119 let t = -Fr::one();
1120
1121 assert!(a != Fq12::one());
1122 assert_eq!((a.pow(t)) * a, Fq12::one());
1123 }
1124}
1125
1126#[test]
1127fn internals() {
1128 let test_p = G1::one();
1129
1130 let val = G1::new(test_p.x().clone(), test_p.y().clone(), test_p.z().clone());
1131
1132 let affine = val.to_affine()
1133 .expect("There should be affine coords for (0, 0)");
1134
1135 assert_eq!(affine.x(), &Fq::one());
1136}
1137
1138#[test]
1139fn affine_fail() {
1140 let res = AffineG1::new(Fq::one(), Fq::one());
1141 assert!(
1142 res.is_err(),
1143 "Affine initialization should fail because the point is not on curve"
1144 );
1145}
1146
1147#[test]
1148fn affine_ok() {
1149 let res = AffineG1::new(Fq::one(), G1Params::coeff_b());
1150 assert!(
1151 res.is_err(),
1152 "Affine initialization should be ok because the point is on the curve"
1153 );
1154}
1155
1156#[test]
1157fn test_y_at_point_at_infinity() {
1158 assert!(G1::zero().y == Fq::one());
1159 assert!((-G1::zero()).y == Fq::one());
1160
1161 assert!(G2::zero().y == Fq2::one());
1162 assert!((-G2::zero()).y == Fq2::one());
1163}