halo2curves_axiom/bn256/
engine.rs

1#![allow(clippy::suspicious_arithmetic_impl)]
2use crate::bn256::curve::*;
3use crate::bn256::fq::*;
4use crate::bn256::fq12::*;
5use crate::bn256::fq2::*;
6use crate::bn256::fq6::FROBENIUS_COEFF_FQ6_C1;
7use crate::bn256::fr::*;
8use crate::ff::{Field, PrimeField};
9use crate::group::cofactor::CofactorCurveAffine;
10use crate::group::Group;
11use core::borrow::Borrow;
12use core::iter::Sum;
13use core::ops::{Add, Mul, MulAssign, Neg, Sub};
14use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine};
15use rand_core::RngCore;
16use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
17
18pub const BN_X: u64 = 4965661367192848881;
19
20// 6U+2 for in NAF form
21pub const SIX_U_PLUS_2_NAF: [i8; 65] = [
22    0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0,
23    1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0,
24    0, 1, 0, 1, 1,
25];
26
27pub const XI_TO_Q_MINUS_1_OVER_2: Fq2 = Fq2 {
28    c0: Fq([
29        0xe4bbdd0c2936b629,
30        0xbb30f162e133bacb,
31        0x31a9d1b6f9645366,
32        0x253570bea500f8dd,
33    ]),
34    c1: Fq([
35        0xa1d77ce45ffe77c7,
36        0x07affd117826d1db,
37        0x6d16bd27bb7edc6b,
38        0x2c87200285defecc,
39    ]),
40};
41
42impl PairingCurveAffine for G1Affine {
43    type Pair = G2Affine;
44    type PairingResult = Gt;
45
46    fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
47        pairing(self, other)
48    }
49}
50
51impl PairingCurveAffine for G2Affine {
52    type Pair = G1Affine;
53    type PairingResult = Gt;
54
55    fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
56        pairing(other, self)
57    }
58}
59
60#[derive(Copy, Clone, Debug, Default)]
61pub struct Gt(pub(crate) Fq12);
62
63impl std::fmt::Display for Gt {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        write!(f, "{self:?}")
66    }
67}
68
69impl ConstantTimeEq for Gt {
70    fn ct_eq(&self, other: &Self) -> Choice {
71        self.0.ct_eq(&other.0)
72    }
73}
74
75impl ConditionallySelectable for Gt {
76    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
77        Gt(Fq12::conditional_select(&a.0, &b.0, choice))
78    }
79}
80
81impl Eq for Gt {}
82impl PartialEq for Gt {
83    #[inline]
84    fn eq(&self, other: &Self) -> bool {
85        bool::from(self.ct_eq(other))
86    }
87}
88
89impl Gt {
90    /// Returns the group identity, which is $1$.
91    pub fn identity() -> Gt {
92        Gt(Fq12::ONE)
93    }
94
95    /// Doubles this group element.
96    pub fn double(&self) -> Gt {
97        Gt(self.0.square())
98    }
99}
100
101impl<'a> Neg for &'a Gt {
102    type Output = Gt;
103
104    #[inline]
105    fn neg(self) -> Gt {
106        // The element is unitary, so we just conjugate.
107        let mut u = self.0;
108        u.conjugate();
109        Gt(u)
110    }
111}
112
113impl Neg for Gt {
114    type Output = Gt;
115
116    #[inline]
117    fn neg(self) -> Gt {
118        -&self
119    }
120}
121
122impl<'a, 'b> Add<&'b Gt> for &'a Gt {
123    type Output = Gt;
124
125    #[inline]
126    fn add(self, rhs: &'b Gt) -> Gt {
127        Gt(self.0 * rhs.0)
128    }
129}
130
131impl<'a, 'b> Sub<&'b Gt> for &'a Gt {
132    type Output = Gt;
133
134    #[inline]
135    fn sub(self, rhs: &'b Gt) -> Gt {
136        self + (-rhs)
137    }
138}
139
140impl<'a, 'b> Mul<&'b Fr> for &'a Gt {
141    type Output = Gt;
142
143    fn mul(self, other: &'b Fr) -> Self::Output {
144        let mut acc = Gt::identity();
145
146        for bit in other
147            .to_repr()
148            .iter()
149            .rev()
150            .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
151            .skip(1)
152        {
153            acc = acc.double();
154            acc = Gt::conditional_select(&acc, &(acc + self), bit);
155        }
156
157        acc
158    }
159}
160
161use crate::{
162    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
163    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
164};
165impl_binops_additive!(Gt, Gt);
166impl_binops_multiplicative!(Gt, Fr);
167
168impl<T> Sum<T> for Gt
169where
170    T: Borrow<Gt>,
171{
172    fn sum<I>(iter: I) -> Self
173    where
174        I: Iterator<Item = T>,
175    {
176        iter.fold(Self::identity(), |acc, item| acc + item.borrow())
177    }
178}
179
180impl Group for Gt {
181    type Scalar = Fr;
182
183    fn random(_: impl RngCore) -> Self {
184        unimplemented!();
185    }
186
187    fn identity() -> Self {
188        Self::identity()
189    }
190
191    fn generator() -> Self {
192        unimplemented!();
193    }
194
195    fn is_identity(&self) -> Choice {
196        self.ct_eq(&Self::identity())
197    }
198
199    #[must_use]
200    fn double(&self) -> Self {
201        self.double()
202    }
203}
204
205#[derive(Clone, Debug)]
206pub struct G2Prepared {
207    pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>,
208    pub(crate) infinity: bool,
209}
210
211impl G2Prepared {
212    pub fn is_zero(&self) -> bool {
213        self.infinity
214    }
215
216    pub fn from_affine(q: G2Affine) -> Self {
217        if bool::from(q.is_identity()) {
218            return G2Prepared {
219                coeffs: vec![],
220                infinity: true,
221            };
222        }
223
224        fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) {
225            // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
226            let mut tmp0 = r.x;
227            tmp0.square_assign();
228
229            let mut tmp1 = r.y;
230            tmp1.square_assign();
231
232            let mut tmp2 = tmp1;
233            tmp2.square_assign();
234
235            let mut tmp3 = tmp1;
236            tmp3 += &r.x;
237            tmp3.square_assign();
238            tmp3 -= &tmp0;
239            tmp3 -= &tmp2;
240            tmp3.double_assign();
241
242            let mut tmp4 = tmp0;
243            tmp4.double_assign();
244            tmp4 += &tmp0;
245
246            let mut tmp6 = r.x;
247            tmp6 += &tmp4;
248
249            let mut tmp5 = tmp4;
250            tmp5.square_assign();
251
252            let mut zsquared = r.z;
253            zsquared.square_assign();
254
255            r.x = tmp5;
256            r.x -= &tmp3;
257            r.x -= &tmp3;
258
259            r.z += &r.y;
260            r.z.square_assign();
261            r.z -= &tmp1;
262            r.z -= &zsquared;
263
264            r.y = tmp3;
265            r.y -= &r.x;
266            r.y.mul_assign(&tmp4);
267
268            tmp2.double_assign();
269            tmp2.double_assign();
270            tmp2.double_assign();
271
272            r.y -= &tmp2;
273
274            // up to here everything was by algorith, line 11
275            // use R instead of new T
276
277            // tmp3 is the first part of line 12
278            tmp3 = tmp4;
279            tmp3.mul_assign(&zsquared);
280            tmp3.double_assign();
281            tmp3 = tmp3.neg();
282
283            // tmp6 is from line 14
284            tmp6.square_assign();
285            tmp6 -= &tmp0;
286            tmp6 -= &tmp5;
287
288            tmp1.double_assign();
289            tmp1.double_assign();
290
291            tmp6 -= &tmp1;
292
293            // tmp0 is the first part of line 16
294            tmp0 = r.z;
295            tmp0.mul_assign(&zsquared);
296            tmp0.double_assign();
297
298            (tmp0, tmp3, tmp6)
299        }
300
301        fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) {
302            // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
303            let mut zsquared = r.z;
304            zsquared.square_assign();
305
306            let mut ysquared = q.y;
307            ysquared.square_assign();
308
309            // t0 corresponds to line 1
310            let mut t0 = zsquared;
311            t0.mul_assign(&q.x);
312
313            // t1 corresponds to lines 2 and 3
314            let mut t1 = q.y;
315            t1 += &r.z;
316            t1.square_assign();
317            t1 -= &ysquared;
318            t1 -= &zsquared;
319            t1.mul_assign(&zsquared);
320
321            // t2 corresponds to line 4
322            let mut t2 = t0;
323            t2 -= &r.x;
324
325            // t3 corresponds to line 5
326            let mut t3 = t2;
327            t3.square_assign();
328
329            // t4 corresponds to line 6
330            let mut t4 = t3;
331            t4.double_assign();
332            t4.double_assign();
333
334            // t5 corresponds to line 7
335            let mut t5 = t4;
336            t5.mul_assign(&t2);
337
338            // t6 corresponds to line 8
339            let mut t6 = t1;
340            t6 -= &r.y;
341            t6 -= &r.y;
342
343            // t9 corresponds to line 9
344            let mut t9 = t6;
345            t9.mul_assign(&q.x);
346
347            // corresponds to line 10
348            let mut t7 = t4;
349            t7.mul_assign(&r.x);
350
351            // corresponds to line 11, but assigns to r.x instead of T.x
352            r.x = t6;
353            r.x.square_assign();
354            r.x -= &t5;
355            r.x -= &t7;
356            r.x -= &t7;
357
358            // corresponds to line 12, but assigns to r.z instead of T.z
359            r.z += &t2;
360            r.z.square_assign();
361            r.z -= &zsquared;
362            r.z -= &t3;
363
364            // corresponds to line 13
365            let mut t10 = q.y;
366            t10 += &r.z;
367
368            // corresponds to line 14
369            let mut t8 = t7;
370            t8 -= &r.x;
371            t8.mul_assign(&t6);
372
373            // corresponds to line 15
374            t0 = r.y;
375            t0.mul_assign(&t5);
376            t0.double_assign();
377
378            // corresponds to line 12, but assigns to r.y instead of T.y
379            r.y = t8;
380            r.y -= &t0;
381
382            // corresponds to line 17
383            t10.square_assign();
384            t10 -= &ysquared;
385
386            let mut ztsquared = r.z;
387            ztsquared.square_assign();
388
389            t10 -= &ztsquared;
390
391            // corresponds to line 18
392            t9.double_assign();
393            t9 -= &t10;
394
395            // t10 = 2*Zt from Algo 27, line 19
396            t10 = r.z;
397            t10.double_assign();
398
399            // t1 = first multiplicator of line 21
400            t6 = t6.neg();
401
402            t1 = t6;
403            t1.double_assign();
404
405            // t9 corresponds to t9 from Algo 27
406            (t10, t1, t9)
407        }
408
409        let mut coeffs = vec![];
410        let mut r: G2 = q.into();
411
412        let mut negq = q;
413        negq = -negq;
414
415        for i in (1..SIX_U_PLUS_2_NAF.len()).rev() {
416            coeffs.push(doubling_step(&mut r));
417            let x = SIX_U_PLUS_2_NAF[i - 1];
418            match x {
419                1 => {
420                    coeffs.push(addition_step(&mut r, &q));
421                }
422                -1 => {
423                    coeffs.push(addition_step(&mut r, &negq));
424                }
425                _ => continue,
426            }
427        }
428
429        let mut q1 = q;
430
431        q1.x.c1 = q1.x.c1.neg();
432        q1.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[1]);
433
434        q1.y.c1 = q1.y.c1.neg();
435        q1.y.mul_assign(&XI_TO_Q_MINUS_1_OVER_2);
436
437        coeffs.push(addition_step(&mut r, &q1));
438
439        let mut minusq2 = q;
440        minusq2.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[2]);
441
442        coeffs.push(addition_step(&mut r, &minusq2));
443
444        G2Prepared {
445            coeffs,
446            infinity: false,
447        }
448    }
449}
450
451impl From<G2Affine> for G2Prepared {
452    fn from(q: G2Affine) -> G2Prepared {
453        G2Prepared::from_affine(q)
454    }
455}
456
457impl MillerLoopResult for Gt {
458    type Gt = Self;
459    // pub fn final_exponentiation(r: &Fq12) -> CtOption<Fq12> {
460    fn final_exponentiation(&self) -> Gt {
461        fn exp_by_x(f: &mut Fq12) {
462            let x = BN_X;
463            let mut res = Fq12::ONE;
464            for i in (0..64).rev() {
465                res.cyclotomic_square();
466                if ((x >> i) & 1) == 1 {
467                    res.mul_assign(f);
468                }
469            }
470            *f = res;
471        }
472
473        let r = self.0;
474        let mut f1 = self.0;
475        f1.conjugate();
476
477        Gt(r.invert()
478            .map(|mut f2| {
479                let mut r = f1;
480                r.mul_assign(&f2);
481                f2 = r;
482                r.frobenius_map(2);
483                r.mul_assign(&f2);
484
485                let mut fp = r;
486                fp.frobenius_map(1);
487
488                let mut fp2 = r;
489                fp2.frobenius_map(2);
490                let mut fp3 = fp2;
491                fp3.frobenius_map(1);
492
493                let mut fu = r;
494                exp_by_x(&mut fu);
495
496                let mut fu2 = fu;
497                exp_by_x(&mut fu2);
498
499                let mut fu3 = fu2;
500                exp_by_x(&mut fu3);
501
502                let mut y3 = fu;
503                y3.frobenius_map(1);
504
505                let mut fu2p = fu2;
506                fu2p.frobenius_map(1);
507
508                let mut fu3p = fu3;
509                fu3p.frobenius_map(1);
510
511                let mut y2 = fu2;
512                y2.frobenius_map(2);
513
514                let mut y0 = fp;
515                y0.mul_assign(&fp2);
516                y0.mul_assign(&fp3);
517
518                let mut y1 = r;
519                y1.conjugate();
520
521                let mut y5 = fu2;
522                y5.conjugate();
523
524                y3.conjugate();
525
526                let mut y4 = fu;
527                y4.mul_assign(&fu2p);
528                y4.conjugate();
529
530                let mut y6 = fu3;
531                y6.mul_assign(&fu3p);
532                y6.conjugate();
533
534                y6.cyclotomic_square();
535                y6.mul_assign(&y4);
536                y6.mul_assign(&y5);
537
538                let mut t1 = y3;
539                t1.mul_assign(&y5);
540                t1.mul_assign(&y6);
541
542                y6.mul_assign(&y2);
543
544                t1.cyclotomic_square();
545                t1.mul_assign(&y6);
546                t1.cyclotomic_square();
547
548                let mut t0 = t1;
549                t0.mul_assign(&y1);
550
551                t1.mul_assign(&y0);
552
553                t0.cyclotomic_square();
554                t0.mul_assign(&t1);
555
556                t0
557            })
558            .unwrap())
559    }
560}
561
562pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> Gt {
563    let mut pairs = vec![];
564    for &(p, q) in terms {
565        if !bool::from(p.is_identity()) && !q.is_zero() {
566            pairs.push((p, q.coeffs.iter()));
567        }
568    }
569
570    // Final steps of the line function on prepared coefficients
571    fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) {
572        let mut c0 = coeffs.0;
573        let mut c1 = coeffs.1;
574
575        c0.c0.mul_assign(&p.y);
576        c0.c1.mul_assign(&p.y);
577
578        c1.c0.mul_assign(&p.x);
579        c1.c1.mul_assign(&p.x);
580
581        // Sparse multiplication in Fq12
582        f.mul_by_034(&c0, &c1, &coeffs.2);
583    }
584
585    let mut f = Fq12::ONE;
586
587    for i in (1..SIX_U_PLUS_2_NAF.len()).rev() {
588        if i != SIX_U_PLUS_2_NAF.len() - 1 {
589            f.square_assign();
590        }
591        for &mut (p, ref mut coeffs) in &mut pairs {
592            ell(&mut f, coeffs.next().unwrap(), p);
593        }
594        let x = SIX_U_PLUS_2_NAF[i - 1];
595        match x {
596            1 => {
597                for &mut (p, ref mut coeffs) in &mut pairs {
598                    ell(&mut f, coeffs.next().unwrap(), p);
599                }
600            }
601            -1 => {
602                for &mut (p, ref mut coeffs) in &mut pairs {
603                    ell(&mut f, coeffs.next().unwrap(), p);
604                }
605            }
606            _ => continue,
607        }
608    }
609
610    for &mut (p, ref mut coeffs) in &mut pairs {
611        ell(&mut f, coeffs.next().unwrap(), p);
612    }
613
614    for &mut (p, ref mut coeffs) in &mut pairs {
615        ell(&mut f, coeffs.next().unwrap(), p);
616    }
617
618    for &mut (_p, ref mut coeffs) in &mut pairs {
619        assert_eq!(coeffs.next(), None);
620    }
621
622    Gt(f)
623}
624
625pub fn pairing(g1: &G1Affine, g2: &G2Affine) -> Gt {
626    let g2 = G2Prepared::from_affine(*g2);
627    let terms: &[(&G1Affine, &G2Prepared)] = &[(g1, &g2)];
628    let u = multi_miller_loop(terms);
629    u.final_exponentiation()
630}
631
632#[derive(Clone, Debug)]
633pub struct Bn256;
634
635impl Engine for Bn256 {
636    type Fr = Fr;
637    type G1 = G1;
638    type G1Affine = G1Affine;
639    type G2 = G2;
640    type G2Affine = G2Affine;
641    type Gt = Gt;
642
643    fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt {
644        pairing(p, q)
645    }
646}
647
648impl MultiMillerLoop for Bn256 {
649    type G2Prepared = G2Prepared;
650    type Result = Gt;
651
652    fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result {
653        multi_miller_loop(terms)
654    }
655}
656
657#[cfg(test)]
658use rand::SeedableRng;
659#[cfg(test)]
660use rand_xorshift::XorShiftRng;
661
662#[test]
663fn test_pairing() {
664    let g1 = G1::generator();
665    let mut g2 = G2::generator();
666    g2 = g2.double();
667    let pair12 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
668
669    let mut g1 = G1::generator();
670    let g2 = G2::generator();
671    g1 = g1.double();
672    let pair21 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
673
674    assert_eq!(pair12, pair21);
675
676    let g1 = G1::generator();
677    let mut g2 = G2::generator();
678    g2 = g2.double().double();
679    let pair12 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
680
681    let mut g1 = G1::generator();
682    let mut g2 = G2::generator();
683    g1 = g1.double();
684    g2 = g2.double();
685    let pair21 = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
686
687    assert_eq!(pair12, pair21);
688
689    let mut rng = XorShiftRng::from_seed([
690        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
691        0xe5,
692    ]);
693    for _ in 0..1000 {
694        let a = Fr::random(&mut rng);
695        let b = Fr::random(&mut rng);
696
697        let mut g1 = G1::generator();
698        g1.mul_assign(a);
699
700        let mut g2 = G2::generator();
701        g1.mul_assign(b);
702
703        let pair_ab = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
704
705        g1 = G1::generator();
706        g1.mul_assign(b);
707
708        g2 = G2::generator();
709        g1.mul_assign(a);
710
711        let pair_ba = Bn256::pairing(&G1Affine::from(g1), &G2Affine::from(g2));
712
713        assert_eq!(pair_ab, pair_ba);
714    }
715}
716
717#[test]
718fn random_bilinearity_tests() {
719    let mut rng = XorShiftRng::from_seed([
720        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
721        0xe5,
722    ]);
723
724    for _ in 0..1000 {
725        let mut a = G1::generator();
726        let ka = Fr::random(&mut rng);
727        a.mul_assign(ka);
728
729        let mut b = G2::generator();
730        let kb = Fr::random(&mut rng);
731        b.mul_assign(kb);
732
733        let c = Fr::random(&mut rng);
734        let d = Fr::random(&mut rng);
735
736        let mut ac = a;
737        ac.mul_assign(c);
738
739        let mut ad = a;
740        ad.mul_assign(d);
741
742        let mut bc = b;
743        bc.mul_assign(c);
744
745        let mut bd = b;
746        bd.mul_assign(d);
747
748        let acbd = Bn256::pairing(&G1Affine::from(ac), &G2Affine::from(bd));
749        let adbc = Bn256::pairing(&G1Affine::from(ad), &G2Affine::from(bc));
750
751        let mut cd = c;
752        cd.mul_assign(&d);
753
754        cd *= Fr([1, 0, 0, 0]);
755
756        let abcd = Gt(Bn256::pairing(&G1Affine::from(a), &G2Affine::from(b))
757            .0
758            .pow_vartime(cd.0));
759
760        assert_eq!(acbd, adbc);
761        assert_eq!(acbd, abcd);
762    }
763}
764
765#[test]
766pub fn engine_tests() {
767    let mut rng = XorShiftRng::from_seed([
768        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
769        0xe5,
770    ]);
771
772    for _ in 0..10 {
773        let a = G1Affine::from(G1::random(&mut rng));
774        let b = G2Affine::from(G2::random(&mut rng));
775
776        assert!(a.pairing_with(&b) == b.pairing_with(&a));
777        assert!(a.pairing_with(&b) == pairing(&a, &b));
778    }
779
780    for _ in 0..1000 {
781        let z1 = G1Affine::identity();
782        let z2 = G2Prepared::from(G2Affine::identity());
783
784        let a = G1Affine::from(G1::random(&mut rng));
785        let b = G2Prepared::from(G2Affine::from(G2::random(&mut rng)));
786        let c = G1Affine::from(G1::random(&mut rng));
787        let d = G2Prepared::from(G2Affine::from(G2::random(&mut rng)));
788
789        assert_eq!(
790            Fq12::ONE,
791            multi_miller_loop(&[(&z1, &b)]).final_exponentiation().0,
792        );
793
794        assert_eq!(
795            Fq12::ONE,
796            multi_miller_loop(&[(&a, &z2)]).final_exponentiation().0,
797        );
798
799        assert_eq!(
800            multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(),
801            multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation(),
802        );
803
804        assert_eq!(
805            multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(),
806            multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation(),
807        );
808    }
809}
810
811#[test]
812fn random_miller_loop_tests() {
813    let mut rng = XorShiftRng::from_seed([
814        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
815        0xe5,
816    ]);
817
818    // Exercise a double miller loop
819    for _ in 0..1000 {
820        let a = G1Affine::from(G1::random(&mut rng));
821        let b = G2Affine::from(G2::random(&mut rng));
822        let c = G1Affine::from(G1::random(&mut rng));
823        let d = G2Affine::from(G2::random(&mut rng));
824
825        let ab = pairing(&a, &b);
826        let cd = pairing(&c, &d);
827
828        let mut abcd = ab;
829        abcd = Gt(abcd.0 * cd.0);
830
831        let b = G2Prepared::from(b);
832        let d = G2Prepared::from(d);
833
834        let abcd_with_double_loop = multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation();
835
836        assert_eq!(abcd, abcd_with_double_loop);
837    }
838}