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