ark-r1cs-std/r1cs-std/src/fields/fp6_3over2.rs
r1cs-std/src/fields/fp6_3over2.rsRaw
1use algebra::{
2 fields::{
3 fp6_3over2::{Fp6, Fp6Parameters},
4 Field, Fp2Parameters,
5 },
6 PrimeField,
7};
8use core::{borrow::Borrow, marker::PhantomData};
9use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError};
10
11use crate::{prelude::*, Vec};
12
13type Fp2Gadget<P, ConstraintF> =
14 super::fp2::Fp2Gadget<<P as Fp6Parameters>::Fp2Params, ConstraintF>;
15
16#[derive(Derivative)]
17#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
18#[must_use]
19pub struct Fp6Gadget<P, ConstraintF: PrimeField>
20where
21 P: Fp6Parameters,
22 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
23{
24 pub c0: Fp2Gadget<P, ConstraintF>,
25 pub c1: Fp2Gadget<P, ConstraintF>,
26 pub c2: Fp2Gadget<P, ConstraintF>,
27 #[derivative(Debug = "ignore")]
28 _params: PhantomData<P>,
29}
30
31impl<P, ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
32where
33 P: Fp6Parameters,
34 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
35{
36 fn to_constraint_field<CS: ConstraintSystem<ConstraintF>>(
37 &self,
38 mut cs: CS,
39 ) -> Result<Vec<FpGadget<ConstraintF>>, SynthesisError> {
40 let mut res = Vec::new();
41
42 let mut c0_gadget = self.c0.to_constraint_field(&mut cs.ns(|| "c0"))?;
43 let mut c1_gadget = self.c1.to_constraint_field(&mut cs.ns(|| "c1"))?;
44 let mut c2_gadget = self.c2.to_constraint_field(&mut cs.ns(|| "c2"))?;
45
46 res.append(&mut c0_gadget);
47 res.append(&mut c1_gadget);
48 res.append(&mut c2_gadget);
49
50 Ok(res)
51 }
52}
53
54impl<P, ConstraintF: PrimeField> Fp6Gadget<P, ConstraintF>
55where
56 P: Fp6Parameters,
57 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
58{
59 #[inline]
60 pub fn new(
61 c0: Fp2Gadget<P, ConstraintF>,
62 c1: Fp2Gadget<P, ConstraintF>,
63 c2: Fp2Gadget<P, ConstraintF>,
64 ) -> Self {
65 Self {
66 c0,
67 c1,
68 c2,
69 _params: PhantomData,
70 }
71 }
72 /// Multiply a Fp2Gadget by cubic nonresidue P::NONRESIDUE.
73 #[inline]
74 pub fn mul_fp2_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
75 cs: CS,
76 fe: &Fp2Gadget<P, ConstraintF>,
77 ) -> Result<Fp2Gadget<P, ConstraintF>, SynthesisError> {
78 fe.mul_by_constant(cs, &P::NONRESIDUE)
79 }
80
81 #[inline]
82 pub fn mul_by_0_c1_0<CS: ConstraintSystem<ConstraintF>>(
83 &self,
84 mut cs: CS,
85 c1: &Fp2Gadget<P, ConstraintF>,
86 ) -> Result<Self, SynthesisError> {
87 // Karatsuba multiplication
88 // v0 = a0 * b0 = 0
89
90 // v1 = a1 * b1
91 let v1 = self.c1.mul(cs.ns(|| "first mul"), c1)?;
92
93 // v2 = a2 * b2 = 0
94
95 let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?;
96 let b1_plus_b2 = c1.clone();
97
98 let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
99
100 // c0 = (NONRESIDUE * ((a1 + a2)*(b1 + b2) - v1 - v2)) + v0
101 // = NONRESIDUE * ((a1 + a2) * b1 - v1)
102 let c0 = a1_plus_a2
103 .mul(cs.ns(|| "second mul"), &b1_plus_b2)?
104 .sub(cs.ns(|| "first sub"), &v1)?
105 .mul_by_constant(cs.ns(|| "mul_by_nonresidue"), &P::NONRESIDUE)?;
106
107 // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + NONRESIDUE * v2
108 // = (a0 + a1) * b1 - v1
109 let c1 = a0_plus_a1
110 .mul(cs.ns(|| "third mul"), &c1)?
111 .sub(cs.ns(|| "second sub"), &v1)?;
112 // c2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1
113 // = v1
114 let c2 = v1;
115 Ok(Self::new(c0, c1, c2))
116 }
117
118 // #[inline]
119 pub fn mul_by_c0_c1_0<CS: ConstraintSystem<ConstraintF>>(
120 &self,
121 mut cs: CS,
122 c0: &Fp2Gadget<P, ConstraintF>,
123 c1: &Fp2Gadget<P, ConstraintF>,
124 ) -> Result<Self, SynthesisError> {
125 let v0 = self.c0.mul(cs.ns(|| "v0"), c0)?;
126 let v1 = self.c1.mul(cs.ns(|| "v1"), c1)?;
127 // v2 = 0.
128
129 let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?;
130 let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
131 let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?;
132
133 let b1_plus_b2 = c1.clone();
134 let b0_plus_b1 = c0.add(cs.ns(|| "b0 + b1"), &c1)?;
135 let b0_plus_b2 = c0.clone();
136
137 let c0 = {
138 let cs = &mut cs.ns(|| "c0");
139 a1_plus_a2
140 .mul(cs.ns(|| "(a1 + a2) * (b1 + b2)"), &b1_plus_b2)?
141 .sub(cs.ns(|| "sub v1"), &v1)?
142 .mul_by_constant(cs.ns(|| "First mul_by_nonresidue"), &P::NONRESIDUE)?
143 .add(cs.ns(|| "add v0"), &v0)?
144 };
145
146 let c1 = {
147 let cs = &mut cs.ns(|| "c1");
148 a0_plus_a1
149 .mul(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?
150 .sub(cs.ns(|| "sub v0"), &v0)?
151 .sub(cs.ns(|| "sub v1"), &v1)?
152 };
153
154 let c2 = {
155 a0_plus_a2
156 .mul(cs.ns(|| "(a0 + a2) * (b0 + b2)"), &b0_plus_b2)?
157 .sub(cs.ns(|| "sub v0"), &v0)?
158 .add(cs.ns(|| "add v1"), &v1)?
159 };
160
161 Ok(Self::new(c0, c1, c2))
162 }
163}
164
165impl<P, ConstraintF: PrimeField> FieldGadget<Fp6<P>, ConstraintF> for Fp6Gadget<P, ConstraintF>
166where
167 P: Fp6Parameters,
168 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
169{
170 type Variable = (
171 (ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
172 (ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
173 (ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>),
174 );
175
176 #[inline]
177 fn get_value(&self) -> Option<Fp6<P>> {
178 match (
179 self.c0.get_value(),
180 self.c1.get_value(),
181 self.c2.get_value(),
182 ) {
183 (Some(c0), Some(c1), Some(c2)) => Some(Fp6::new(c0, c1, c2)),
184 (..) => None,
185 }
186 }
187
188 #[inline]
189 fn get_variable(&self) -> Self::Variable {
190 (
191 self.c0.get_variable(),
192 self.c1.get_variable(),
193 self.c2.get_variable(),
194 )
195 }
196
197 #[inline]
198 fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
199 let c0 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
200 let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
201 let c2 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c2"))?;
202 Ok(Self::new(c0, c1, c2))
203 }
204
205 #[inline]
206 fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
207 let c0 = Fp2Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
208 let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
209 let c2 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c2"))?;
210 Ok(Self::new(c0, c1, c2))
211 }
212
213 #[inline]
214 fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
215 &self,
216 mut cs: CS,
217 bit: &Boolean,
218 coeff: Fp6<P>,
219 ) -> Result<Self, SynthesisError> {
220 let c0 = self
221 .c0
222 .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?;
223 let c1 = self
224 .c1
225 .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?;
226 let c2 = self
227 .c2
228 .conditionally_add_constant(cs.ns(|| "c2"), bit, coeff.c2)?;
229 Ok(Self::new(c0, c1, c2))
230 }
231
232 #[inline]
233 fn add<CS: ConstraintSystem<ConstraintF>>(
234 &self,
235 mut cs: CS,
236 other: &Self,
237 ) -> Result<Self, SynthesisError> {
238 let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?;
239 let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?;
240 let c2 = self.c2.add(&mut cs.ns(|| "add c2"), &other.c2)?;
241 Ok(Self::new(c0, c1, c2))
242 }
243
244 #[inline]
245 fn sub<CS: ConstraintSystem<ConstraintF>>(
246 &self,
247 mut cs: CS,
248 other: &Self,
249 ) -> Result<Self, SynthesisError> {
250 let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?;
251 let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?;
252 let c2 = self.c2.sub(&mut cs.ns(|| "sub c2"), &other.c2)?;
253 Ok(Self::new(c0, c1, c2))
254 }
255
256 #[inline]
257 fn negate<CS: ConstraintSystem<ConstraintF>>(
258 &self,
259 mut cs: CS,
260 ) -> Result<Self, SynthesisError> {
261 let c0 = self.c0.negate(&mut cs.ns(|| "negate c0"))?;
262 let c1 = self.c1.negate(&mut cs.ns(|| "negate c1"))?;
263 let c2 = self.c2.negate(&mut cs.ns(|| "negate c2"))?;
264 Ok(Self::new(c0, c1, c2))
265 }
266
267 #[inline]
268 fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
269 &mut self,
270 mut cs: CS,
271 ) -> Result<&mut Self, SynthesisError> {
272 self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?;
273 self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?;
274 self.c2.negate_in_place(&mut cs.ns(|| "negate c2"))?;
275 Ok(self)
276 }
277
278 /// Use the Toom-Cook-3x method to compute multiplication.
279 #[inline]
280 fn mul<CS: ConstraintSystem<ConstraintF>>(
281 &self,
282 mut cs: CS,
283 other: &Self,
284 ) -> Result<Self, SynthesisError> {
285 // Uses Toom-Cook-3x multiplication from
286 //
287 // Reference:
288 // "Multiplication and Squaring on Pairing-Friendly Fields"
289 // Devegili, OhEigeartaigh, Scott, Dahab
290
291 // v0 = a(0)b(0) = a0 * b0
292 let v0 = self.c0.mul(&mut cs.ns(|| "Calc v0"), &other.c0)?;
293
294 // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2)
295 let v1 = {
296 let mut v1_cs = cs.ns(|| "compute v1");
297 let a0_plus_a1_plus_a2 = self
298 .c0
299 .add(v1_cs.ns(|| "a0 + a1"), &self.c1)?
300 .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
301 let b0_plus_b1_plus_b2 = other
302 .c0
303 .add(v1_cs.ns(|| "b0 + b1"), &other.c1)?
304 .add(v1_cs.ns(|| "b0 + b1 + b2"), &other.c2)?;
305
306 a0_plus_a1_plus_a2.mul(
307 v1_cs.ns(|| "(a0 + a1 + a2)(b0 + b1 + b2)"),
308 &b0_plus_b1_plus_b2,
309 )?
310 };
311
312 // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2)
313 let v2 = {
314 let mut v2_cs = cs.ns(|| "compute v2");
315
316 let a0_minus_a1_plus_a2 = self
317 .c0
318 .sub(v2_cs.ns(|| "a0 - a1"), &self.c1)?
319 .add(v2_cs.ns(|| "a0 - a1 + a2"), &self.c2)?;
320
321 let b0_minus_b1_plus_b2 = other
322 .c0
323 .sub(v2_cs.ns(|| "b0 - b1"), &other.c1)?
324 .add(v2_cs.ns(|| "b0 - b1 + b2"), &other.c2)?;
325
326 a0_minus_a1_plus_a2.mul(
327 v2_cs.ns(|| "(a0 - a1 + a2)(b0 - b1 + b2)"),
328 &b0_minus_b1_plus_b2,
329 )?
330 };
331
332 // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)
333 let v3 = {
334 let v3_cs = &mut cs.ns(|| "compute v3");
335
336 let a1_double = self.c1.double(v3_cs.ns(|| "2 * a1"))?;
337 let a2_quad = self
338 .c2
339 .double(v3_cs.ns(|| "2 * a2"))?
340 .double(v3_cs.ns(|| "4 * a2"))?;
341
342 let a0_plus_2_a1_plus_4_a2 = self
343 .c0
344 .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)?
345 .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
346
347 let b1_double = other.c1.double(v3_cs.ns(|| "2 * b1"))?;
348 let b2_quad = other
349 .c2
350 .double(v3_cs.ns(|| "2 * b2"))?
351 .double(v3_cs.ns(|| "4 * b2"))?;
352 let b0_plus_2_b1_plus_4_b2 = other
353 .c0
354 .add(v3_cs.ns(|| "b0 + 2b1"), &b1_double)?
355 .add(v3_cs.ns(|| "b0 + 2b1 + 4b2"), &b2_quad)?;
356
357 a0_plus_2_a1_plus_4_a2.mul(
358 v3_cs.ns(|| "(a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)"),
359 &b0_plus_2_b1_plus_4_b2,
360 )?
361 };
362
363 // v4 = a(∞)b(∞) = a2 * b2
364 let v4 = self.c2.mul(cs.ns(|| "v2: a2 * b2"), &other.c2)?;
365
366 let two = <P::Fp2Params as Fp2Parameters>::Fp::one().double();
367 let six = two.double() + &two;
368 let mut two_and_six = [two, six];
369 algebra::fields::batch_inversion(&mut two_and_six);
370 let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
371
372 let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
373 let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
374 let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_six"), &six_inverse)?;
375 let one_sixth_v3 = v3.mul_by_fp_constant(cs.ns(|| "v3_by_six"), &six_inverse)?;
376 let two_v4 = v4.double(cs.ns(|| "2 * v4"))?;
377
378 // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
379 let c0 = {
380 let c0_cs = &mut cs.ns(|| "c0");
381
382 // No constraints, only get a linear combination back.
383 let temp = half_v0
384 .sub(c0_cs.ns(|| "sub1"), &half_v1)?
385 .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
386 .add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
387 .sub(c0_cs.ns(|| "sub4"), &two_v4)?;
388 let non_residue_times_inner =
389 temp.mul_by_constant(&mut c0_cs.ns(|| "mul5"), &P::NONRESIDUE)?;
390 v0.add(c0_cs.ns(|| "add6"), &non_residue_times_inner)?
391 };
392
393 // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
394 let c1 = {
395 let c1_cs = &mut cs.ns(|| "c1");
396 let one_third_v2 = one_sixth_v2.double(&mut c1_cs.ns(|| "v2_by_3"))?;
397 let non_residue_v4 =
398 v4.mul_by_constant(&mut c1_cs.ns(|| "mul_by_beta"), &P::NONRESIDUE)?;
399
400 let result = half_v0
401 .negate(c1_cs.ns(|| "neg1"))?
402 .add(c1_cs.ns(|| "add2"), &v1)?
403 .sub(c1_cs.ns(|| "sub3"), &one_third_v2)?
404 .sub(c1_cs.ns(|| "sub4"), &one_sixth_v3)?
405 .add(c1_cs.ns(|| "sub5"), &two_v4)?
406 .add(c1_cs.ns(|| "sub6"), &non_residue_v4)?;
407 result
408 };
409
410 // -v0 + (1/2)v1 + (1/2)v2 −v4
411 let c2 = {
412 let c2_cs = &mut cs.ns(|| "c2");
413 let half_v2 = v2.mul_by_fp_constant(&mut c2_cs.ns(|| "mul1"), &two_inverse)?;
414 let result = half_v1
415 .add(c2_cs.ns(|| "add1"), &half_v2)?
416 .sub(c2_cs.ns(|| "sub1"), &v4)?
417 .sub(c2_cs.ns(|| "sub2"), &v0)?;
418 result
419 };
420
421 Ok(Self::new(c0, c1, c2))
422 }
423
424 /// Use the Chung-Hasan asymmetric squaring formula.
425 ///
426 /// (Devegili OhEig Scott Dahab --- Multiplication and Squaring on
427 /// Abstract Pairing-Friendly
428 /// Fields.pdf; Section 4 (CH-SQR2))
429 #[inline]
430 fn square<CS: ConstraintSystem<ConstraintF>>(
431 &self,
432 mut cs: CS,
433 ) -> Result<Self, SynthesisError> {
434 let a = self.c0.clone();
435 let b = self.c1.clone();
436 let c = self.c2.clone();
437
438 let s0 = a.square(cs.ns(|| "s0"))?;
439 let ab = a.mul(cs.ns(|| "ab"), &b)?;
440 let s1 = ab.double(cs.ns(|| "s1"))?;
441 let s2 = a
442 .sub(cs.ns(|| "a-b"), &b)?
443 .add(cs.ns(|| "plus c"), &c)?
444 .square(cs.ns(|| "s2"))?;
445 let s3 = b.mul(cs.ns(|| "bc"), &c)?.double(cs.ns(|| "s3"))?;
446 let s4 = c.square(cs.ns(|| "s4"))?;
447
448 let c0 = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "c0 part 1"), &s3)?
449 .add(cs.ns(|| "c0"), &s0)?;
450
451 let c1 = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "c1 part 1"), &s4)?
452 .add(cs.ns(|| "c1"), &s1)?;
453
454 let c2 = s1
455 .add(cs.ns(|| "c2 part1"), &s2)?
456 .add(cs.ns(|| "c2 part2"), &s3)?
457 .sub(cs.ns(|| "c2 part3"), &s0)?
458 .sub(cs.ns(|| "c2 part4"), &s4)?;
459
460 Ok(Self::new(c0, c1, c2))
461 }
462
463 #[inline]
464 fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
465 &self,
466 mut cs: CS,
467 other: &Self,
468 result: &Self,
469 ) -> Result<(), SynthesisError> {
470 // Karatsuba multiplication for Fp3:
471 // v0 = A.c0 * B.c0
472 // v1 = A.c1 * B.c1
473 // v2 = A.c2 * B.c2
474 // result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2)
475 // result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2
476 // result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2,
477 // We enforce this with six constraints:
478 //
479 // v0 = A.c0 * B.c0
480 // v1 = A.c1 * B.c1
481 // v2 = A.c2 * B.c2
482 //
483 // result.c0 - v0 + \beta*(v1 + v2) = β(a1 + a2)(b1 + b2))
484 // result.c1 + v0 + v1 - βv2 = (a0 + a1)(b0 + b1)
485 // result.c2 + v0 - v1 + v2 = (a0 + a2)(b0 + b2)
486 // Reference:
487 // "Multiplication and Squaring on Pairing-Friendly Fields"
488 // Devegili, OhEigeartaigh, Scott, Dahab
489 //
490 // This implementation adapted from
491 // https://github.com/ZencashOfficial/ginger-lib/blob/development/r1cs/gadgets/std/src/fields/fp3.rs
492 let v0 = self.c0.mul(cs.ns(|| "v0 = a0 * b0"), &other.c0)?;
493 let v1 = self.c1.mul(cs.ns(|| "v1 = a1 * b1"), &other.c1)?;
494 let v2 = self.c2.mul(cs.ns(|| "v2 = a2 * b2"), &other.c2)?;
495
496 // Check c0
497 let nr_a1_plus_a2 = self
498 .c1
499 .add(cs.ns(|| "a1 + a2"), &self.c2)?
500 .mul_by_constant(cs.ns(|| "nr*(a1 + a2)"), &P::NONRESIDUE)?;
501 let b1_plus_b2 = other.c1.add(cs.ns(|| "b1 + b2"), &other.c2)?;
502 let nr_v1 = v1.mul_by_constant(cs.ns(|| "nr * v1"), &P::NONRESIDUE)?;
503 let nr_v2 = v2.mul_by_constant(cs.ns(|| "nr * v2"), &P::NONRESIDUE)?;
504 let to_check = result
505 .c0
506 .sub(cs.ns(|| "c0 - v0"), &v0)?
507 .add(cs.ns(|| "c0 - v0 + nr * v1"), &nr_v1)?
508 .add(cs.ns(|| "c0 - v0 + nr * v1 + nr * v2"), &nr_v2)?;
509 nr_a1_plus_a2.mul_equals(cs.ns(|| "check c0"), &b1_plus_b2, &to_check)?;
510
511 // Check c1
512 let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
513 let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?;
514 let to_check = result
515 .c1
516 .sub(cs.ns(|| "c1 - nr * v2"), &nr_v2)?
517 .add(cs.ns(|| "c1 - nr * v2 + v0"), &v0)?
518 .add(cs.ns(|| "c1 - nr * v2 + v0 + v1"), &v1)?;
519 a0_plus_a1.mul_equals(cs.ns(|| "check c1"), &b0_plus_b1, &to_check)?;
520
521 // Check c2
522 let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?;
523 let b0_plus_b2 = other.c0.add(cs.ns(|| "b0 + b2"), &other.c2)?;
524 let to_check = result
525 .c2
526 .add(cs.ns(|| "c2 + v0"), &v0)?
527 .sub(cs.ns(|| "c2 + v0 - v1"), &v1)?
528 .add(cs.ns(|| "c2 + v0 - v1 + v2"), &v2)?;
529 a0_plus_a2.mul_equals(cs.ns(|| "check c2"), &b0_plus_b2, &to_check)?;
530 Ok(())
531 }
532
533 #[inline]
534 fn add_constant<CS: ConstraintSystem<ConstraintF>>(
535 &self,
536 mut cs: CS,
537 other: &Fp6<P>,
538 ) -> Result<Self, SynthesisError> {
539 let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?;
540 let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?;
541 let c2 = self.c2.add_constant(cs.ns(|| "c2"), &other.c2)?;
542
543 Ok(Self::new(c0, c1, c2))
544 }
545
546 #[inline]
547 fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
548 &mut self,
549 mut cs: CS,
550 other: &Fp6<P>,
551 ) -> Result<&mut Self, SynthesisError> {
552 self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?;
553 self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?;
554 self.c2.add_constant_in_place(cs.ns(|| "c2"), &other.c2)?;
555 Ok(self)
556 }
557
558 /// Use the Toom-Cook-3x method to compute multiplication.
559 #[inline]
560 fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
561 &self,
562 mut cs: CS,
563 other: &Fp6<P>,
564 ) -> Result<Self, SynthesisError> {
565 // Uses Toom-Cook-3x multiplication from
566 //
567 // Reference:
568 // "Multiplication and Squaring on Pairing-Friendly Fields"
569 // Devegili, OhEigeartaigh, Scott, Dahab
570
571 // v0 = a(0)b(0) = a0 * b0
572 let v0 = self.c0.mul_by_constant(cs.ns(|| "v0"), &other.c0)?;
573
574 // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2)
575 let v1 = {
576 let mut v1_cs = cs.ns(|| "v1");
577 let mut a0_plus_a1_plus_a2 = self
578 .c0
579 .add(v1_cs.ns(|| "a0 + a1"), &self.c1)?
580 .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?;
581 let b0_plus_b1_plus_b2 = other.c0 + &other.c1 + &other.c2;
582
583 a0_plus_a1_plus_a2.mul_by_constant_in_place(
584 v1_cs.ns(|| "(a0 + a1 + a2)*(b0 + b1 + b2)"),
585 &b0_plus_b1_plus_b2,
586 )?;
587 a0_plus_a1_plus_a2
588 };
589
590 // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2)
591 let mut v2 = {
592 let mut v2_cs = cs.ns(|| "v2");
593 let mut a0_minus_a1_plus_a2 = self
594 .c0
595 .sub(v2_cs.ns(|| "sub1"), &self.c1)?
596 .add(v2_cs.ns(|| "add2"), &self.c2)?;
597 let b0_minus_b1_plus_b2 = other.c0 - &other.c1 + &other.c2;
598 a0_minus_a1_plus_a2.mul_by_constant_in_place(
599 v2_cs.ns(|| "(a0 - a1 + a2)*(b0 - b1 + b2)"),
600 &b0_minus_b1_plus_b2,
601 )?;
602 a0_minus_a1_plus_a2
603 };
604
605 // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)
606 let mut v3 = {
607 let mut v3_cs = cs.ns(|| "v3");
608 let a1_double = self.c1.double(v3_cs.ns(|| "2a1"))?;
609 let a2_quad = self
610 .c2
611 .double(v3_cs.ns(|| "2a2"))?
612 .double(v3_cs.ns(|| "4a2"))?;
613 let mut a0_plus_2_a1_plus_4_a2 = self
614 .c0
615 .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)?
616 .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?;
617
618 let b1_double = other.c1.double();
619 let b2_quad = other.c2.double().double();
620 let b0_plus_2_b1_plus_4_b2 = other.c0 + &b1_double + &b2_quad;
621
622 a0_plus_2_a1_plus_4_a2.mul_by_constant_in_place(
623 v3_cs.ns(|| "(a0 + 2a1 + 4a2)*(b0 + 2b1 + 4b2)"),
624 &b0_plus_2_b1_plus_4_b2,
625 )?;
626 a0_plus_2_a1_plus_4_a2
627 };
628
629 // v4 = a(∞)b(∞) = a2 * b2
630 let v4 = self.c2.mul_by_constant(cs.ns(|| "v4"), &other.c2)?;
631
632 let two = <P::Fp2Params as Fp2Parameters>::Fp::one().double();
633 let six = two.double() + &two;
634 let mut two_and_six = [two, six];
635 algebra::fields::batch_inversion(&mut two_and_six);
636 let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]);
637
638 let mut half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?;
639 let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?;
640 let mut one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_6"), &six_inverse)?;
641 let one_sixth_v3 = v3.mul_by_fp_constant_in_place(cs.ns(|| "v3_by_6"), &six_inverse)?;
642 let two_v4 = v4.double(cs.ns(|| "2v4"))?;
643
644 // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4)
645 let c0 = {
646 let mut c0_cs = cs.ns(|| "c0");
647
648 // No constraints, only get a linear combination back.
649 let mut inner = half_v0
650 .sub(c0_cs.ns(|| "sub1"), &half_v1)?
651 .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)?
652 .add(c0_cs.ns(|| "add3"), &one_sixth_v3)?
653 .sub(c0_cs.ns(|| "sub4"), &two_v4)?;
654 let non_residue_times_inner =
655 inner.mul_by_constant_in_place(&mut c0_cs, &P::NONRESIDUE)?;
656 v0.add(c0_cs.ns(|| "add5"), non_residue_times_inner)?
657 };
658
659 // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4
660 let c1 = {
661 let mut c1_cs = cs.ns(|| "c1");
662 let one_third_v2 = one_sixth_v2.double_in_place(c1_cs.ns(|| "double1"))?;
663 let non_residue_v4 =
664 v4.mul_by_constant(c1_cs.ns(|| "mul_by_const1"), &P::NONRESIDUE)?;
665
666 half_v0
667 .negate_in_place(c1_cs.ns(|| "neg1"))?
668 .add(c1_cs.ns(|| "add1"), &v1)?
669 .sub(c1_cs.ns(|| "sub2"), one_third_v2)?
670 .sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)?
671 .add(c1_cs.ns(|| "add4"), &two_v4)?
672 .add(c1_cs.ns(|| "add5"), &non_residue_v4)?
673 };
674
675 // -v0 + (1/2)v1 + (1/2)v2 −v4
676 let c2 = {
677 let mut c2_cs = cs.ns(|| "c2");
678 let half_v2 = v2.mul_by_fp_constant_in_place(c2_cs.ns(|| "half_v2"), &two_inverse)?;
679 half_v1
680 .add(c2_cs.ns(|| "add1"), half_v2)?
681 .sub(c2_cs.ns(|| "sub2"), &v4)?
682 .sub(c2_cs.ns(|| "sub3"), &v0)?
683 };
684
685 Ok(Self::new(c0, c1, c2))
686 }
687
688 fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
689 &self,
690 cs: CS,
691 power: usize,
692 ) -> Result<Self, SynthesisError> {
693 let mut result = self.clone();
694 result.frobenius_map_in_place(cs, power)?;
695 Ok(result)
696 }
697
698 fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
699 &mut self,
700 mut cs: CS,
701 power: usize,
702 ) -> Result<&mut Self, SynthesisError> {
703 self.c0.frobenius_map_in_place(&mut cs.ns(|| "c0"), power)?;
704 self.c1.frobenius_map_in_place(&mut cs.ns(|| "c1"), power)?;
705 self.c2.frobenius_map_in_place(&mut cs.ns(|| "c2"), power)?;
706
707 self.c1.mul_by_constant_in_place(
708 cs.ns(|| "c1_power"),
709 &P::FROBENIUS_COEFF_FP6_C1[power % 6],
710 )?;
711 self.c2.mul_by_constant_in_place(
712 cs.ns(|| "c2_power"),
713 &P::FROBENIUS_COEFF_FP6_C2[power % 6],
714 )?;
715
716 Ok(self)
717 }
718
719 fn cost_of_mul() -> usize {
720 5 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
721 }
722
723 fn cost_of_mul_equals() -> usize {
724 6 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
725 }
726}
727
728impl<P, ConstraintF: PrimeField> PartialEq for Fp6Gadget<P, ConstraintF>
729where
730 P: Fp6Parameters,
731 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
732{
733 fn eq(&self, other: &Self) -> bool {
734 self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2
735 }
736}
737
738impl<P, ConstraintF: PrimeField> Eq for Fp6Gadget<P, ConstraintF>
739where
740 P: Fp6Parameters,
741 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
742{
743}
744
745impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
746where
747 P: Fp6Parameters,
748 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
749{
750}
751
752impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
753where
754 P: Fp6Parameters,
755 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
756{
757 #[inline]
758 fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
759 &self,
760 mut cs: CS,
761 other: &Self,
762 condition: &Boolean,
763 ) -> Result<(), SynthesisError> {
764 self.c0
765 .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?;
766 self.c1
767 .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?;
768 self.c2
769 .conditional_enforce_equal(&mut cs.ns(|| "c2"), &other.c2, condition)?;
770 Ok(())
771 }
772
773 fn cost() -> usize {
774 3 * <Fp2Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
775 }
776}
777
778impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
779where
780 P: Fp6Parameters,
781 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
782{
783 #[inline]
784 fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
785 &self,
786 mut cs: CS,
787 other: &Self,
788 ) -> Result<(), SynthesisError> {
789 self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?;
790 self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?;
791 self.c2.enforce_not_equal(&mut cs.ns(|| "c2"), &other.c2)?;
792 Ok(())
793 }
794
795 fn cost() -> usize {
796 3 * <Fp2Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
797 }
798}
799
800impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
801where
802 P: Fp6Parameters,
803 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
804{
805 fn to_bits<CS: ConstraintSystem<ConstraintF>>(
806 &self,
807 mut cs: CS,
808 ) -> Result<Vec<Boolean>, SynthesisError> {
809 let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?;
810 let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?;
811 let mut c2 = self.c2.to_bits(cs.ns(|| "c2"))?;
812
813 c0.append(&mut c1);
814 c0.append(&mut c2);
815
816 Ok(c0)
817 }
818
819 fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>(
820 &self,
821 mut cs: CS,
822 ) -> Result<Vec<Boolean>, SynthesisError> {
823 let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?;
824 let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?;
825 let mut c2 = self.c2.to_non_unique_bits(cs.ns(|| "c2"))?;
826
827 c0.append(&mut c1);
828 c0.append(&mut c2);
829
830 Ok(c0)
831 }
832}
833
834impl<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
835where
836 P: Fp6Parameters,
837 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
838{
839 fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
840 &self,
841 mut cs: CS,
842 ) -> Result<Vec<UInt8>, SynthesisError> {
843 let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?;
844 let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?;
845 let mut c2 = self.c2.to_bytes(cs.ns(|| "c2"))?;
846
847 c0.append(&mut c1);
848 c0.append(&mut c2);
849
850 Ok(c0)
851 }
852
853 fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>(
854 &self,
855 mut cs: CS,
856 ) -> Result<Vec<UInt8>, SynthesisError> {
857 let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?;
858 let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?;
859 let mut c2 = self.c2.to_non_unique_bytes(cs.ns(|| "c2"))?;
860
861 c0.append(&mut c1);
862 c0.append(&mut c2);
863
864 Ok(c0)
865 }
866}
867
868impl<P, ConstraintF: PrimeField> Clone for Fp6Gadget<P, ConstraintF>
869where
870 P: Fp6Parameters,
871 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
872{
873 fn clone(&self) -> Self {
874 Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone())
875 }
876}
877
878impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
879where
880 P: Fp6Parameters,
881 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
882{
883 #[inline]
884 fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
885 mut cs: CS,
886 cond: &Boolean,
887 true_value: &Self,
888 false_value: &Self,
889 ) -> Result<Self, SynthesisError> {
890 let c0 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
891 &mut cs.ns(|| "c0"),
892 cond,
893 &true_value.c0,
894 &false_value.c0,
895 )?;
896 let c1 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
897 &mut cs.ns(|| "c1"),
898 cond,
899 &true_value.c1,
900 &false_value.c1,
901 )?;
902 let c2 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
903 &mut cs.ns(|| "c2"),
904 cond,
905 &true_value.c2,
906 &false_value.c2,
907 )?;
908
909 Ok(Self::new(c0, c1, c2))
910 }
911
912 fn cost() -> usize {
913 3 * <Fp2Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
914 }
915}
916
917impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp6Gadget<P, ConstraintF>
918where
919 P: Fp6Parameters,
920 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
921{
922 type TableConstant = Fp6<P>;
923 fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
924 mut cs: CS,
925 b: &[Boolean],
926 c: &[Self::TableConstant],
927 ) -> Result<Self, SynthesisError> {
928 let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
929 let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
930 let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
931 let c0 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
932 let c1 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
933 let c2 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?;
934 Ok(Self::new(c0, c1, c2))
935 }
936
937 fn cost() -> usize {
938 3 * <Fp2Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
939 }
940}
941
942impl<P, ConstraintF: PrimeField> ThreeBitCondNegLookupGadget<ConstraintF>
943 for Fp6Gadget<P, ConstraintF>
944where
945 P: Fp6Parameters,
946 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
947{
948 type TableConstant = Fp6<P>;
949
950 fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
951 mut cs: CS,
952 b: &[Boolean],
953 b0b1: &Boolean,
954 c: &[Self::TableConstant],
955 ) -> Result<Self, SynthesisError> {
956 let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
957 let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
958 let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
959 let c0 = Fp2Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
960 cs.ns(|| "Lookup c0"),
961 b,
962 b0b1,
963 &c0s,
964 )?;
965 let c1 = Fp2Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
966 cs.ns(|| "Lookup c1"),
967 b,
968 b0b1,
969 &c1s,
970 )?;
971 let c2 = Fp2Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
972 cs.ns(|| "Lookup c2"),
973 b,
974 b0b1,
975 &c2s,
976 )?;
977 Ok(Self::new(c0, c1, c2))
978 }
979
980 fn cost() -> usize {
981 3 * <Fp2Gadget<P, ConstraintF> as ThreeBitCondNegLookupGadget<ConstraintF>>::cost()
982 }
983}
984
985impl<P, ConstraintF: PrimeField> AllocGadget<Fp6<P>, ConstraintF> for Fp6Gadget<P, ConstraintF>
986where
987 P: Fp6Parameters,
988 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
989{
990 #[inline]
991 fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
992 mut cs: CS,
993 t: T,
994 ) -> Result<Self, SynthesisError>
995 where
996 T: Borrow<Fp6<P>>,
997 {
998 Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow())
999 }
1000
1001 #[inline]
1002 fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
1003 mut cs: CS,
1004 value_gen: F,
1005 ) -> Result<Self, SynthesisError>
1006 where
1007 F: FnOnce() -> Result<T, SynthesisError>,
1008 T: Borrow<Fp6<P>>,
1009 {
1010 let (c0, c1, c2) = match value_gen() {
1011 Ok(fe) => {
1012 let fe = *fe.borrow();
1013 (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2))
1014 }
1015 _ => (
1016 Err(SynthesisError::AssignmentMissing),
1017 Err(SynthesisError::AssignmentMissing),
1018 Err(SynthesisError::AssignmentMissing),
1019 ),
1020 };
1021
1022 let c0 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
1023 let c1 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
1024 let c2 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c2"), || c2)?;
1025 Ok(Self::new(c0, c1, c2))
1026 }
1027
1028 #[inline]
1029 fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
1030 mut cs: CS,
1031 value_gen: F,
1032 ) -> Result<Self, SynthesisError>
1033 where
1034 F: FnOnce() -> Result<T, SynthesisError>,
1035 T: Borrow<Fp6<P>>,
1036 {
1037 let (c0, c1, c2) = match value_gen() {
1038 Ok(fe) => {
1039 let fe = *fe.borrow();
1040 (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2))
1041 }
1042 _ => (
1043 Err(SynthesisError::AssignmentMissing),
1044 Err(SynthesisError::AssignmentMissing),
1045 Err(SynthesisError::AssignmentMissing),
1046 ),
1047 };
1048
1049 let c0 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
1050 let c1 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
1051 let c2 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c2"), || c2)?;
1052 Ok(Self::new(c0, c1, c2))
1053 }
1054}