Clearmatics Libsnark  0.1
C++ library for zkSNARK proofs
bls12_377_miller_loop.tcc
Go to the documentation of this file.
1 /** @file
2  *****************************************************************************
3  * @author This file is part of libsnark, developed by Clearmatics Ltd
4  * (originally developed by SCIPR Lab) and contributors
5  * (see AUTHORS).
6  * @copyright MIT license (see LICENSE file)
7  *****************************************************************************/
8 
9 #ifndef LIBSNARK_GADGETLIB1_GADGETS_PAIRING_BW6_761_BLS12_377_BLS12_377_MILLER_LOOP_TCC_
10 #define LIBSNARK_GADGETLIB1_GADGETS_PAIRING_BW6_761_BLS12_377_BLS12_377_MILLER_LOOP_TCC_
11 
12 #include "libsnark/gadgetlib1/gadgets/basic_gadgets.hpp"
13 #include "libsnark/gadgetlib1/gadgets/pairing/bw6_761_bls12_377/bls12_377_miller_loop.hpp"
14 
15 namespace libsnark
16 {
17 
18 // bls12_377_ate_compute_f_ell_P methods
19 
20 template<typename ppT>
21 bls12_377_ate_compute_f_ell_P<ppT>::bls12_377_ate_compute_f_ell_P(
22  protoboard<FieldT> &pb,
23  const pb_linear_combination<FieldT> &Px,
24  const pb_linear_combination<FieldT> &Py,
25  const bls12_377_ate_ell_coeffs<ppT> &ell_coeffs,
26  const Fp12_2over3over2_variable<FqkT> &f,
27  const Fp12_2over3over2_variable<FqkT> &f_out,
28  const std::string &annotation_prefix)
29  : gadget<FieldT>(pb, annotation_prefix)
30  , _compute_ell_vv_times_Px(
31  pb,
32  ell_coeffs.ell_vv,
33  Px,
34  Fqe_variable<ppT>(pb, FMT(annotation_prefix, " ell_vv_times_Px")),
35  FMT(annotation_prefix, " _compute_ell_vv_times_Px"))
36  , _compute_ell_vw_times_Py(
37  pb,
38  ell_coeffs.ell_vw,
39  Py,
40  Fqe_variable<ppT>(pb, FMT(annotation_prefix, " ell_vw_times_Py")),
41  FMT(annotation_prefix, " _compute_ell_vw_times_Py"))
42  , _compute_f_mul_ell_P(
43  pb,
44  f,
45  ell_coeffs.ell_0,
46  _compute_ell_vv_times_Px.result,
47  _compute_ell_vw_times_Py.result,
48  f_out,
49  FMT(annotation_prefix, " _compute_f_mul_ell_P"))
50 {
51 }
52 
53 template<typename ppT>
54 const Fp12_2over3over2_variable<libff::Fqk<other_curve<ppT>>>
55  &bls12_377_ate_compute_f_ell_P<ppT>::result() const
56 {
57  return _compute_f_mul_ell_P.result();
58 }
59 
60 template<typename ppT>
61 void bls12_377_ate_compute_f_ell_P<ppT>::generate_r1cs_constraints()
62 {
63  _compute_ell_vv_times_Px.generate_r1cs_constraints();
64  _compute_ell_vw_times_Py.generate_r1cs_constraints();
65  _compute_f_mul_ell_P.generate_r1cs_constraints();
66 }
67 
68 template<typename ppT>
69 void bls12_377_ate_compute_f_ell_P<ppT>::generate_r1cs_witness()
70 {
71  _compute_ell_vv_times_Px.generate_r1cs_witness();
72  _compute_ell_vw_times_Py.generate_r1cs_witness();
73  _compute_f_mul_ell_P.generate_r1cs_witness();
74 }
75 
76 // bls12_377_miller_loop_gadget methods
77 
78 template<typename ppT>
79 bls12_377_miller_loop_gadget<ppT>::bls12_377_miller_loop_gadget(
80  protoboard<FieldT> &pb,
81  const bls12_377_G1_precomputation<ppT> &prec_P,
82  const bls12_377_G2_precomputation<ppT> &prec_Q,
83  const Fqk_variable<ppT> &result,
84  const std::string &annotation_prefix)
85  : gadget<FieldT>(pb, annotation_prefix)
86  , _f0(pb, FqkT::one(), FMT(annotation_prefix, " f0"))
87 {
88  size_t coeff_idx = 0;
89  const Fp12_2over3over2_variable<FqkT> *f = &_f0;
90 
91  bls12_377_miller_loop_bits bits;
92  while (bits.next()) {
93  // f <- f^2
94  _f_squared.push_back(
95  std::shared_ptr<Fp12_2over3over2_square_gadget<FqkT>>(
96  new Fp12_2over3over2_square_gadget<FqkT>(
97  pb,
98  *f,
99  Fp12_2over3over2_variable<FqkT>(
100  pb, FMT(annotation_prefix, " f^2")),
101  FMT(annotation_prefix, " _f_squared[%zu]", bits.index()))));
102  f = &_f_squared.back()->result();
103 
104  // f <- f^2 * ell(P)
105  _f_ell_P.push_back(std::shared_ptr<bls12_377_ate_compute_f_ell_P<ppT>>(
106  new bls12_377_ate_compute_f_ell_P<ppT>(
107  pb,
108  *prec_P._Px,
109  *prec_P._Py,
110  *prec_Q._coeffs[coeff_idx++],
111  *f,
112  Fp12_2over3over2_variable<FqkT>(
113  pb, FMT(annotation_prefix, " f^2*ell(P)")),
114  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size()))));
115  f = &_f_ell_P.back()->result();
116 
117  if (bits.current()) {
118  // f <- f * ell(P)
119  if (bits.last()) {
120  _f_ell_P.push_back(
121  std::shared_ptr<bls12_377_ate_compute_f_ell_P<ppT>>(
122  new bls12_377_ate_compute_f_ell_P<ppT>(
123  pb,
124  *prec_P._Px,
125  *prec_P._Py,
126  *prec_Q._coeffs[coeff_idx++],
127  *f,
128  result,
129  FMT(annotation_prefix,
130  " _f_ell_P[%zu]",
131  _f_ell_P.size()))));
132  } else {
133  _f_ell_P.push_back(
134  std::shared_ptr<bls12_377_ate_compute_f_ell_P<ppT>>(
135  new bls12_377_ate_compute_f_ell_P<ppT>(
136  pb,
137  *prec_P._Px,
138  *prec_P._Py,
139  *prec_Q._coeffs[coeff_idx++],
140  *f,
141  Fp12_2over3over2_variable<FqkT>(
142  pb, FMT(annotation_prefix, " f*ell(P)")),
143  FMT(annotation_prefix,
144  " _f_ell_P[%zu]",
145  _f_ell_P.size()))));
146  }
147  f = &_f_ell_P.back()->result();
148  }
149  }
150 }
151 
152 template<typename ppT>
153 const Fp12_2over3over2_variable<libff::Fqk<other_curve<ppT>>>
154  &bls12_377_miller_loop_gadget<ppT>::result() const
155 {
156  return _f_ell_P.back()->result();
157 }
158 
159 template<typename ppT>
160 void bls12_377_miller_loop_gadget<ppT>::generate_r1cs_constraints()
161 {
162  // TODO: everything is allocated, so constraint generation does not need
163  // to be done in this order. For now, keep a consistent loop.
164 
165  size_t sqr_idx = 0;
166  size_t f_ell_P_idx = 0;
167  bls12_377_miller_loop_bits bits;
168  while (bits.next()) {
169  _f_squared[sqr_idx++]->generate_r1cs_constraints();
170  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
171  if (bits.current()) {
172  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
173  }
174  }
175 
176  assert(sqr_idx == _f_squared.size());
177  assert(f_ell_P_idx == _f_ell_P.size());
178 }
179 
180 template<typename ppT>
181 void bls12_377_miller_loop_gadget<ppT>::generate_r1cs_witness()
182 {
183  size_t sqr_idx = 0;
184  size_t f_ell_P_idx = 0;
185  bls12_377_miller_loop_bits bits;
186  while (bits.next()) {
187  _f_squared[sqr_idx++]->generate_r1cs_witness();
188  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
189  if (bits.current()) {
190  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
191  }
192  }
193 
194  assert(sqr_idx == _f_squared.size());
195  assert(f_ell_P_idx == _f_ell_P.size());
196 }
197 
198 template<typename ppT>
199 bls12_377_e_over_e_miller_loop_gadget<ppT>::
200  bls12_377_e_over_e_miller_loop_gadget(
201  protoboard<libff::Fr<ppT>> &pb,
202  const bls12_377_G1_precomputation<ppT> &P1_prec,
203  const bls12_377_G2_precomputation<ppT> &Q1_prec,
204  const bls12_377_G1_precomputation<ppT> &P2_prec,
205  const bls12_377_G2_precomputation<ppT> &Q2_prec,
206  const Fp12_2over3over2_variable<FqkT> &result,
207  const std::string &annotation_prefix)
208  : gadget<FieldT>(pb, annotation_prefix)
209  , _f0(pb, FqkT::one(), FMT(annotation_prefix, " f0"))
210  , _minus_P2_Y()
211 {
212  _minus_P2_Y.assign(pb, -(*P2_prec._Py));
213  size_t coeff_idx = 0;
214  const Fp12_2over3over2_variable<FqkT> *f = &_f0;
215 
216  bls12_377_miller_loop_bits bits;
217  while (bits.next()) {
218  // f <- f^2
219  _f_squared.emplace_back(new Fp12_2over3over2_square_gadget<FqkT>(
220  pb,
221  *f,
222  Fp12_2over3over2_variable<FqkT>(pb, FMT(annotation_prefix, "f^2")),
223  FMT(annotation_prefix, " _f_squared[%zu]", _f_squared.size())));
224  f = &_f_squared.back()->result();
225 
226  // f <- f^2 * ell_Q1(P1)
227  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
228  pb,
229  *P1_prec._Px,
230  *P1_prec._Py,
231  *Q1_prec._coeffs[coeff_idx],
232  *f,
233  Fp12_2over3over2_variable<FqkT>(
234  pb, FMT(annotation_prefix, " f^2*ell_Q1(P1)")),
235  FMT(annotation_prefix, " _f_ell_P1[%zu]", _f_ell_P.size())));
236  f = &_f_ell_P.back()->result();
237 
238  // f <- f^2 * ell_Q2(P2)
239  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
240  pb,
241  *P2_prec._Px,
242  _minus_P2_Y,
243  *Q2_prec._coeffs[coeff_idx],
244  *f,
245  Fp12_2over3over2_variable<FqkT>(
246  pb, FMT(annotation_prefix, " f^2*ell_Q2(P2)")),
247  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
248  f = &_f_ell_P.back()->result();
249 
250  assert(0 == _f_ell_P.size() % 2);
251 
252  ++coeff_idx;
253 
254  if (bits.current()) {
255  // f <- f * ell_Q1(P1)
256  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
257  pb,
258  *P1_prec._Px,
259  *P1_prec._Py,
260  *Q1_prec._coeffs[coeff_idx],
261  *f,
262  Fp12_2over3over2_variable<FqkT>(
263  pb, FMT(annotation_prefix, " f*ell_Q1(P2)")),
264  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
265  f = &_f_ell_P.back()->result();
266 
267  // f <- f * ell_Q2(P2)
268  if (bits.last()) {
269  _f_ell_P.emplace_back(
270  std::shared_ptr<bls12_377_ate_compute_f_ell_P<ppT>>(
271  new bls12_377_ate_compute_f_ell_P<ppT>(
272  pb,
273  *P2_prec._Px,
274  _minus_P2_Y,
275  *Q2_prec._coeffs[coeff_idx],
276  *f,
277  result,
278  FMT(annotation_prefix,
279  " _f_ell_P[%zu]",
280  _f_ell_P.size()))));
281  } else {
282  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
283  pb,
284  *P2_prec._Px,
285  _minus_P2_Y,
286  *Q2_prec._coeffs[coeff_idx],
287  *f,
288  Fp12_2over3over2_variable<FqkT>(
289  pb, FMT(annotation_prefix, " f*ell_Q2(P2)")),
290  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
291  }
292  f = &_f_ell_P.back()->result();
293 
294  assert(0 == _f_ell_P.size() % 2);
295 
296  ++coeff_idx;
297  }
298  }
299 }
300 
301 template<typename ppT>
302 void bls12_377_e_over_e_miller_loop_gadget<ppT>::generate_r1cs_constraints()
303 {
304  size_t sqr_idx = 0;
305  size_t f_ell_P_idx = 0;
306  bls12_377_miller_loop_bits bits;
307  while (bits.next()) {
308  _f_squared[sqr_idx++]->generate_r1cs_constraints();
309  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
310  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
311  if (bits.current()) {
312  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
313  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
314  }
315  }
316 
317  assert(sqr_idx == _f_squared.size());
318  assert(f_ell_P_idx == _f_ell_P.size());
319 }
320 
321 template<typename ppT>
322 void bls12_377_e_over_e_miller_loop_gadget<ppT>::generate_r1cs_witness()
323 {
324  _minus_P2_Y.evaluate(this->pb);
325  size_t sqr_idx = 0;
326  size_t f_ell_P_idx = 0;
327  bls12_377_miller_loop_bits bits;
328  while (bits.next()) {
329  _f_squared[sqr_idx++]->generate_r1cs_witness();
330  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
331  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
332  if (bits.current()) {
333  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
334  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
335  }
336  }
337 
338  assert(sqr_idx == _f_squared.size());
339  assert(f_ell_P_idx == _f_ell_P.size());
340 }
341 
342 template<typename ppT>
343 bls12_377_e_times_e_times_e_over_e_miller_loop_gadget<ppT>::
344  bls12_377_e_times_e_times_e_over_e_miller_loop_gadget(
345  protoboard<libff::Fr<ppT>> &pb,
346  const bls12_377_G1_precomputation<ppT> &P1_prec,
347  const bls12_377_G2_precomputation<ppT> &Q1_prec,
348  const bls12_377_G1_precomputation<ppT> &P2_prec,
349  const bls12_377_G2_precomputation<ppT> &Q2_prec,
350  const bls12_377_G1_precomputation<ppT> &P3_prec,
351  const bls12_377_G2_precomputation<ppT> &Q3_prec,
352  const bls12_377_G1_precomputation<ppT> &P4_prec,
353  const bls12_377_G2_precomputation<ppT> &Q4_prec,
354  const Fp12_2over3over2_variable<FqkT> &result,
355  const std::string &annotation_prefix)
356  : gadget<FieldT>(pb, annotation_prefix)
357  , _f0(pb, FqkT::one(), FMT(annotation_prefix, " f0"))
358  , _minus_P4_Y()
359 {
360  _minus_P4_Y.assign(pb, -(*P4_prec._Py));
361  size_t coeff_idx = 0;
362  const Fp12_2over3over2_variable<FqkT> *f = &_f0;
363 
364  bls12_377_miller_loop_bits bits;
365  while (bits.next()) {
366  // f <- f^2
367  _f_squared.emplace_back(new Fp12_2over3over2_square_gadget<FqkT>(
368  pb,
369  *f,
370  Fp12_2over3over2_variable<FqkT>(pb, FMT(annotation_prefix, "f^2")),
371  FMT(annotation_prefix, " _f_squared[%zu]", _f_squared.size())));
372  f = &_f_squared.back()->result();
373 
374  // f <- f^2 * ell_Q1(P1)
375  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
376  pb,
377  *P1_prec._Px,
378  *P1_prec._Py,
379  *Q1_prec._coeffs[coeff_idx],
380  *f,
381  Fp12_2over3over2_variable<FqkT>(
382  pb, FMT(annotation_prefix, " f^2*ell_Q1(P1)")),
383  FMT(annotation_prefix, " _f_ell_P1[%zu]", _f_ell_P.size())));
384  f = &_f_ell_P.back()->result();
385 
386  // f <- f^2 * ell_Q2(P2)
387  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
388  pb,
389  *P2_prec._Px,
390  *P2_prec._Py,
391  *Q2_prec._coeffs[coeff_idx],
392  *f,
393  Fp12_2over3over2_variable<FqkT>(
394  pb, FMT(annotation_prefix, " f^2*ell_Q2(P2)")),
395  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
396  f = &_f_ell_P.back()->result();
397 
398  // f <- f^2 * ell_Q3(P3)
399  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
400  pb,
401  *P3_prec._Px,
402  *P3_prec._Py,
403  *Q3_prec._coeffs[coeff_idx],
404  *f,
405  Fp12_2over3over2_variable<FqkT>(
406  pb, FMT(annotation_prefix, " f^2*ell_Q3(P3)")),
407  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
408  f = &_f_ell_P.back()->result();
409 
410  // f <- f^2 * ell_Q4(P4)
411  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
412  pb,
413  *P4_prec._Px,
414  _minus_P4_Y,
415  *Q4_prec._coeffs[coeff_idx],
416  *f,
417  Fp12_2over3over2_variable<FqkT>(
418  pb, FMT(annotation_prefix, " f^2*ell_Q4(P4)")),
419  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
420  f = &_f_ell_P.back()->result();
421 
422  assert(0 == _f_ell_P.size() % 4);
423 
424  ++coeff_idx;
425 
426  if (bits.current()) {
427  // f <- f * ell_Q1(P1)
428  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
429  pb,
430  *P1_prec._Px,
431  *P1_prec._Py,
432  *Q1_prec._coeffs[coeff_idx],
433  *f,
434  Fp12_2over3over2_variable<FqkT>(
435  pb, FMT(annotation_prefix, " f*ell_Q1(P2)")),
436  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
437  f = &_f_ell_P.back()->result();
438 
439  // f <- f * ell_Q2(P2)
440  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
441  pb,
442  *P2_prec._Px,
443  *P2_prec._Py,
444  *Q2_prec._coeffs[coeff_idx],
445  *f,
446  Fp12_2over3over2_variable<FqkT>(
447  pb, FMT(annotation_prefix, " f*ell_Q2(P2)")),
448  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
449  f = &_f_ell_P.back()->result();
450 
451  // f <- f * ell_Q3(P3)
452  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
453  pb,
454  *P3_prec._Px,
455  *P3_prec._Py,
456  *Q3_prec._coeffs[coeff_idx],
457  *f,
458  Fp12_2over3over2_variable<FqkT>(
459  pb, FMT(annotation_prefix, " f*ell_Q3(P3)")),
460  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
461  f = &_f_ell_P.back()->result();
462 
463  // f <- f * ell_Q4(P4)
464  if (bits.last()) {
465  _f_ell_P.emplace_back(
466  std::shared_ptr<bls12_377_ate_compute_f_ell_P<ppT>>(
467  new bls12_377_ate_compute_f_ell_P<ppT>(
468  pb,
469  *P4_prec._Px,
470  _minus_P4_Y,
471  *Q4_prec._coeffs[coeff_idx],
472  *f,
473  result,
474  FMT(annotation_prefix,
475  " _f_ell_P[%zu]",
476  _f_ell_P.size()))));
477  } else {
478  _f_ell_P.emplace_back(new bls12_377_ate_compute_f_ell_P<ppT>(
479  pb,
480  *P4_prec._Px,
481  _minus_P4_Y,
482  *Q4_prec._coeffs[coeff_idx],
483  *f,
484  Fp12_2over3over2_variable<FqkT>(
485  pb, FMT(annotation_prefix, " f*ell_Q4(P4)")),
486  FMT(annotation_prefix, " _f_ell_P[%zu]", _f_ell_P.size())));
487  }
488  f = &_f_ell_P.back()->result();
489 
490  assert(0 == _f_ell_P.size() % 4);
491 
492  ++coeff_idx;
493  }
494  }
495 }
496 
497 template<typename ppT>
498 void bls12_377_e_times_e_times_e_over_e_miller_loop_gadget<
499  ppT>::generate_r1cs_constraints()
500 {
501  size_t sqr_idx = 0;
502  size_t f_ell_P_idx = 0;
503  bls12_377_miller_loop_bits bits;
504  while (bits.next()) {
505  _f_squared[sqr_idx++]->generate_r1cs_constraints();
506  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
507  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
508  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
509  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
510  if (bits.current()) {
511  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
512  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
513  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
514  _f_ell_P[f_ell_P_idx++]->generate_r1cs_constraints();
515  }
516  }
517 
518  assert(sqr_idx == _f_squared.size());
519  assert(f_ell_P_idx == _f_ell_P.size());
520 }
521 
522 template<typename ppT>
523 void bls12_377_e_times_e_times_e_over_e_miller_loop_gadget<
524  ppT>::generate_r1cs_witness()
525 {
526  _minus_P4_Y.evaluate(this->pb);
527  size_t sqr_idx = 0;
528  size_t f_ell_P_idx = 0;
529  bls12_377_miller_loop_bits bits;
530  while (bits.next()) {
531  _f_squared[sqr_idx++]->generate_r1cs_witness();
532  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
533  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
534  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
535  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
536  if (bits.current()) {
537  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
538  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
539  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
540  _f_ell_P[f_ell_P_idx++]->generate_r1cs_witness();
541  }
542  }
543 
544  assert(sqr_idx == _f_squared.size());
545  assert(f_ell_P_idx == _f_ell_P.size());
546 }
547 
548 } // namespace libsnark
549 
550 #endif // LIBSNARK_GADGETLIB1_GADGETS_PAIRING_BW6_761_BLS12_377_BLS12_377_MILLER_LOOP_TCC_