ark-r1cs-std/r1cs-std/src/fields/fp4.rs
r1cs-std/src/fields/fp4.rsRaw
1use algebra::{
2 fields::{Fp2, Fp2Parameters, Fp4, Fp4Parameters},
3 BigInteger, PrimeField,
4};
5use core::{borrow::Borrow, marker::PhantomData};
6use r1cs_core::{ConstraintSystem, SynthesisError};
7
8use crate::{prelude::*, Vec};
9
10type Fp2Gadget<P, ConstraintF> =
11 super::fp2::Fp2Gadget<<P as Fp4Parameters>::Fp2Params, ConstraintF>;
12type Fp2GadgetVariable<P, ConstraintF> = <Fp2Gadget<P, ConstraintF> as FieldGadget<
13 Fp2<<P as Fp4Parameters>::Fp2Params>,
14 ConstraintF,
15>>::Variable;
16
17#[derive(Derivative)]
18#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
19#[must_use]
20pub struct Fp4Gadget<P, ConstraintF: PrimeField>
21where
22 P: Fp4Parameters,
23 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
24{
25 pub c0: Fp2Gadget<P, ConstraintF>,
26 pub c1: Fp2Gadget<P, ConstraintF>,
27 #[derivative(Debug = "ignore")]
28 _params: PhantomData<P>,
29}
30
31impl<P, ConstraintF: PrimeField> Fp4Gadget<P, ConstraintF>
32where
33 P: Fp4Parameters,
34 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
35{
36 pub fn new(c0: Fp2Gadget<P, ConstraintF>, c1: Fp2Gadget<P, ConstraintF>) -> Self {
37 Self {
38 c0,
39 c1,
40 _params: PhantomData,
41 }
42 }
43
44 /// Multiply a Fp2Gadget by quadratic nonresidue P::NONRESIDUE.
45 #[inline]
46 pub fn mul_fp2_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
47 cs: CS,
48 fe: &Fp2Gadget<P, ConstraintF>,
49 ) -> Result<Fp2Gadget<P, ConstraintF>, SynthesisError> {
50 let new_c0 = Fp2Gadget::<P, ConstraintF>::mul_fp_gadget_by_nonresidue(cs, &fe.c1)?;
51 let new_c1 = fe.c0.clone();
52 Ok(Fp2Gadget::<P, ConstraintF>::new(new_c0, new_c1))
53 }
54
55 /// Multiply a Fp4Gadget by an element of fp.
56 #[inline]
57 pub fn mul_by_fp_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
58 &mut self,
59 mut cs: CS,
60 fe: &<<P as Fp4Parameters>::Fp2Params as Fp2Parameters>::Fp,
61 ) -> Result<&mut Self, SynthesisError> {
62 self.c0.mul_by_fp_constant_in_place(cs.ns(|| "c0"), fe)?;
63 self.c1.mul_by_fp_constant_in_place(cs.ns(|| "c1"), fe)?;
64 Ok(self)
65 }
66
67 /// Multiply a Fp4Gadget by an element of fp.
68 #[inline]
69 pub fn mul_by_fp_constant<CS: ConstraintSystem<ConstraintF>>(
70 &self,
71 cs: CS,
72 fe: &<<P as Fp4Parameters>::Fp2Params as Fp2Parameters>::Fp,
73 ) -> Result<Self, SynthesisError> {
74 let mut result = self.clone();
75 result.mul_by_fp_constant_in_place(cs, fe)?;
76 Ok(result)
77 }
78
79 pub fn unitary_inverse<CS: ConstraintSystem<ConstraintF>>(
80 &self,
81 cs: CS,
82 ) -> Result<Self, SynthesisError> {
83 Ok(Self::new(self.c0.clone(), self.c1.negate(cs)?))
84 }
85
86 #[inline]
87 pub fn cyclotomic_exp<CS: ConstraintSystem<ConstraintF>, B: BigInteger>(
88 &self,
89 mut cs: CS,
90 exponent: &B,
91 ) -> Result<Self, SynthesisError> {
92 let mut res = Self::one(cs.ns(|| "one"))?;
93 let self_inverse = self.unitary_inverse(cs.ns(|| "unitary inverse"))?;
94
95 let mut found_nonzero = false;
96 let naf = exponent.find_wnaf();
97
98 for (i, &value) in naf.iter().rev().enumerate() {
99 if found_nonzero {
100 res.square_in_place(cs.ns(|| format!("square {}", i)))?;
101 }
102
103 if value != 0 {
104 found_nonzero = true;
105
106 if value > 0 {
107 res.mul_in_place(cs.ns(|| format!("res *= self {}", i)), &self)?;
108 } else {
109 res.mul_in_place(
110 cs.ns(|| format!("res *= self_inverse {}", i)),
111 &self_inverse,
112 )?;
113 }
114 }
115 }
116
117 Ok(res)
118 }
119}
120
121impl<P, ConstraintF: PrimeField> FieldGadget<Fp4<P>, ConstraintF> for Fp4Gadget<P, ConstraintF>
122where
123 P: Fp4Parameters,
124 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
125{
126 type Variable = (
127 Fp2GadgetVariable<P, ConstraintF>,
128 Fp2GadgetVariable<P, ConstraintF>,
129 );
130
131 #[inline]
132 fn get_value(&self) -> Option<Fp4<P>> {
133 match (self.c0.get_value(), self.c1.get_value()) {
134 (Some(c0), Some(c1)) => Some(Fp4::new(c0, c1)),
135 (..) => None,
136 }
137 }
138
139 #[inline]
140 fn get_variable(&self) -> Self::Variable {
141 (self.c0.get_variable(), self.c1.get_variable())
142 }
143
144 #[inline]
145 fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
146 let c0 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
147 let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
148 Ok(Self::new(c0, c1))
149 }
150
151 #[inline]
152 fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
153 let c0 = Fp2Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
154 let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
155 Ok(Self::new(c0, c1))
156 }
157
158 #[inline]
159 fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
160 &self,
161 mut cs: CS,
162 bit: &Boolean,
163 coeff: Fp4<P>,
164 ) -> Result<Self, SynthesisError> {
165 let c0 = self
166 .c0
167 .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?;
168 let c1 = self
169 .c1
170 .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?;
171 Ok(Self::new(c0, c1))
172 }
173
174 #[inline]
175 fn add<CS: ConstraintSystem<ConstraintF>>(
176 &self,
177 mut cs: CS,
178 other: &Self,
179 ) -> Result<Self, SynthesisError> {
180 let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?;
181 let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?;
182 Ok(Self::new(c0, c1))
183 }
184
185 #[inline]
186 fn sub<CS: ConstraintSystem<ConstraintF>>(
187 &self,
188 mut cs: CS,
189 other: &Self,
190 ) -> Result<Self, SynthesisError> {
191 let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?;
192 let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?;
193 Ok(Self::new(c0, c1))
194 }
195
196 #[inline]
197 fn double<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
198 let mut result = self.clone();
199 result.double_in_place(cs)?;
200 Ok(result)
201 }
202
203 #[inline]
204 fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
205 &mut self,
206 mut cs: CS,
207 ) -> Result<&mut Self, SynthesisError> {
208 self.c0.double_in_place(&mut cs.ns(|| "double c0"))?;
209 self.c1.double_in_place(&mut cs.ns(|| "double c1"))?;
210 Ok(self)
211 }
212
213 #[inline]
214 fn negate<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
215 let mut result = self.clone();
216 result.negate_in_place(cs)?;
217 Ok(result)
218 }
219
220 #[inline]
221 fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
222 &mut self,
223 mut cs: CS,
224 ) -> Result<&mut Self, SynthesisError> {
225 self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?;
226 self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?;
227 Ok(self)
228 }
229
230 #[inline]
231 fn mul<CS: ConstraintSystem<ConstraintF>>(
232 &self,
233 mut cs: CS,
234 other: &Self,
235 ) -> Result<Self, SynthesisError> {
236 // Karatsuba multiplication for Fp4:
237 // v0 = A.c0 * B.c0
238 // v1 = A.c1 * B.c1
239 // result.c0 = v0 + non_residue * v1
240 // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
241 // Enforced with 3 constraints:
242 // A.c1 * B.c1 = v1
243 // A.c0 * B.c0 = result.c0 - non_residue * v1
244 // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
245 // Reference:
246 // "Multiplication and Squaring on Pairing-Friendly Fields"
247 // Devegili, OhEigeartaigh, Scott, Dahab
248 let mul_cs = &mut cs.ns(|| "mul");
249
250 let v0 = self.c0.mul(mul_cs.ns(|| "v0"), &other.c0)?;
251 let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
252 let c0 = {
253 let non_residue_times_v1 =
254 Self::mul_fp2_gadget_by_nonresidue(mul_cs.ns(|| "first mul_by_nr"), &v1)?;
255 v0.add(mul_cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)?
256 };
257 let c1 = {
258 let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
259 let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
260 let a0_plus_a1_times_b0_plus_b1 =
261 a0_plus_a1.mul(&mut mul_cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?;
262 a0_plus_a1_times_b0_plus_b1
263 .sub(mul_cs.ns(|| "res - v0"), &v0)?
264 .sub(mul_cs.ns(|| "res - v0 - v1"), &v1)?
265 };
266 Ok(Self::new(c0, c1))
267 }
268
269 #[inline]
270 fn square<CS: ConstraintSystem<ConstraintF>>(
271 &self,
272 mut cs: CS,
273 ) -> Result<Self, SynthesisError> {
274 // From Libsnark/fp4_gadget.tcc
275 // Complex multiplication for Fp4:
276 // v0 = A.c0 * A.c1
277 // result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
278 // non_residue) * v0 result.c1 = 2 * v0
279 // Enforced with 2 constraints:
280 // (2*A.c0) * A.c1 = result.c1
281 // (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
282 // + non_residue)/2 Reference:
283 // "Multiplication and Squaring on Pairing-Friendly Fields"
284 // Devegili, OhEigeartaigh, Scott, Dahab
285
286 let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
287 let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
288
289 let non_residue_c1 =
290 Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "non_residue * a1"), &self.c1)?;
291 let a0_plus_non_residue_c1 = self
292 .c0
293 .add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_c1)?;
294 let one_plus_non_residue_v0 =
295 Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "non_residue * v0"), &v0)?
296 .add(cs.ns(|| "plus v0"), &v0)?;
297
298 let c0 = a0_plus_a1
299 .mul(
300 cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
301 &a0_plus_non_residue_c1,
302 )?
303 .sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
304
305 v0.double_in_place(cs.ns(|| "2v0"))?;
306 let c1 = v0;
307
308 Ok(Self::new(c0, c1))
309 }
310
311 fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
312 &self,
313 mut cs: CS,
314 other: &Self,
315 result: &Self,
316 ) -> Result<(), SynthesisError> {
317 // Karatsuba multiplication for Fp4:
318 // v0 = A.c0 * B.c0
319 // v1 = A.c1 * B.c1
320 // result.c0 = v0 + non_residue * v1
321 // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
322 // Enforced with 3 constraints:
323 // A.c1 * B.c1 = v1
324 // A.c0 * B.c0 = result.c0 - non_residue * v1
325 // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
326 // Reference:
327 // "Multiplication and Squaring on Pairing-Friendly Fields"
328 // Devegili, OhEigeartaigh, Scott, Dahab
329 let mul_cs = &mut cs.ns(|| "mul");
330
331 // Compute v1
332 let mut v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
333
334 // Perform second check
335 let non_residue_times_v1 =
336 Self::mul_fp2_gadget_by_nonresidue(mul_cs.ns(|| "nr * v1"), &v1)?;
337 let rhs = result
338 .c0
339 .sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?;
340 self.c0
341 .mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?;
342
343 // Last check
344 let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
345 let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
346 let one_minus_non_residue_v1 =
347 v1.sub_in_place(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?;
348
349 let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result
350 .c1
351 .add(mul_cs.ns(|| "c1 + c0"), &result.c0)?
352 .add(mul_cs.ns(|| "rest of stuff"), one_minus_non_residue_v1)?;
353
354 a0_plus_a1.mul_equals(
355 mul_cs.ns(|| "third check"),
356 &b0_plus_b1,
357 &result_c1_plus_result_c0_plus_one_minus_non_residue_v1,
358 )?;
359
360 Ok(())
361 }
362
363 fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
364 &self,
365 cs: CS,
366 power: usize,
367 ) -> Result<Self, SynthesisError> {
368 let mut result = self.clone();
369 let _ = result.frobenius_map_in_place(cs, power)?;
370 Ok(result)
371 }
372
373 fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
374 &mut self,
375 mut cs: CS,
376 power: usize,
377 ) -> Result<&mut Self, SynthesisError> {
378 self.c0
379 .frobenius_map_in_place(cs.ns(|| "frob_map1"), power)?;
380 self.c1
381 .frobenius_map_in_place(cs.ns(|| "frob_map2"), power)?;
382 self.c1
383 .mul_by_fp_constant_in_place(cs.ns(|| "mul"), &P::FROBENIUS_COEFF_FP4_C1[power % 4])?;
384 Ok(self)
385 }
386
387 #[inline]
388 fn add_constant<CS: ConstraintSystem<ConstraintF>>(
389 &self,
390 cs: CS,
391 other: &Fp4<P>,
392 ) -> Result<Self, SynthesisError> {
393 let mut result = self.clone();
394 let _ = result.add_constant_in_place(cs, other)?;
395 Ok(result)
396 }
397
398 #[inline]
399 fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
400 &mut self,
401 mut cs: CS,
402 other: &Fp4<P>,
403 ) -> Result<&mut Self, SynthesisError> {
404 self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?;
405 self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?;
406 Ok(self)
407 }
408
409 fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
410 &self,
411 mut cs: CS,
412 fe: &Fp4<P>,
413 ) -> Result<Self, SynthesisError> {
414 // Karatsuba multiplication (see mul above).
415 // Doesn't need any constraints; returns linear combinations of
416 // `self`'s variables.
417 //
418 // (The operations below are guaranteed to return linear combinations)
419 let (a0, a1) = (&self.c0, &self.c1);
420 let (b0, b1) = (fe.c0, fe.c1);
421 let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?;
422 let mut v1 = Self::mul_fp2_gadget_by_nonresidue(&mut cs.ns(|| "v1"), a1)?;
423 let beta_v1 = v1.mul_by_constant_in_place(&mut cs.ns(|| "beta * v1"), &b1)?;
424
425 v0.add_in_place(&mut cs.ns(|| "c0"), &beta_v1)?;
426 let c0 = v0;
427
428 let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?;
429 let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?;
430 a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?;
431 let c1 = a0b1;
432 Ok(Self::new(c0, c1))
433 }
434
435 fn cost_of_mul() -> usize {
436 3 * Fp2Gadget::<P, ConstraintF>::cost_of_mul()
437 }
438
439 fn cost_of_mul_equals() -> usize {
440 Self::cost_of_mul()
441 }
442}
443
444impl<P, ConstraintF: PrimeField> PartialEq for Fp4Gadget<P, ConstraintF>
445where
446 P: Fp4Parameters,
447 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
448{
449 fn eq(&self, other: &Self) -> bool {
450 self.c0 == other.c0 && self.c1 == other.c1
451 }
452}
453
454impl<P, ConstraintF: PrimeField> Eq for Fp4Gadget<P, ConstraintF>
455where
456 P: Fp4Parameters,
457 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
458{
459}
460
461impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
462where
463 P: Fp4Parameters,
464 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
465{
466}
467
468impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
469where
470 P: Fp4Parameters,
471 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
472{
473 #[inline]
474 fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
475 &self,
476 mut cs: CS,
477 other: &Self,
478 condition: &Boolean,
479 ) -> Result<(), SynthesisError> {
480 self.c0
481 .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?;
482 self.c1
483 .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?;
484 Ok(())
485 }
486
487 fn cost() -> usize {
488 2 * <Fp2Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
489 }
490}
491
492impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
493where
494 P: Fp4Parameters,
495 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
496{
497 #[inline]
498 fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
499 &self,
500 mut cs: CS,
501 other: &Self,
502 ) -> Result<(), SynthesisError> {
503 self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?;
504 self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?;
505 Ok(())
506 }
507
508 fn cost() -> usize {
509 2 * <Fp2Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
510 }
511}
512
513impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
514where
515 P: Fp4Parameters,
516 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
517{
518 fn to_bits<CS: ConstraintSystem<ConstraintF>>(
519 &self,
520 mut cs: CS,
521 ) -> Result<Vec<Boolean>, SynthesisError> {
522 let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?;
523 let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?;
524 c0.append(&mut c1);
525 Ok(c0)
526 }
527
528 fn to_non_unique_bits<CS: ConstraintSystem<ConstraintF>>(
529 &self,
530 mut cs: CS,
531 ) -> Result<Vec<Boolean>, SynthesisError> {
532 let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?;
533 let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?;
534 c0.append(&mut c1);
535 Ok(c0)
536 }
537}
538
539impl<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
540where
541 P: Fp4Parameters,
542 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
543{
544 fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
545 &self,
546 mut cs: CS,
547 ) -> Result<Vec<UInt8>, SynthesisError> {
548 let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?;
549 let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?;
550 c0.append(&mut c1);
551 Ok(c0)
552 }
553
554 fn to_non_unique_bytes<CS: ConstraintSystem<ConstraintF>>(
555 &self,
556 mut cs: CS,
557 ) -> Result<Vec<UInt8>, SynthesisError> {
558 let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?;
559 let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?;
560 c0.append(&mut c1);
561 Ok(c0)
562 }
563}
564
565impl<P, ConstraintF: PrimeField> Clone for Fp4Gadget<P, ConstraintF>
566where
567 P: Fp4Parameters,
568 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
569{
570 fn clone(&self) -> Self {
571 Self {
572 c0: self.c0.clone(),
573 c1: self.c1.clone(),
574 _params: PhantomData,
575 }
576 }
577}
578
579impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
580where
581 P: Fp4Parameters,
582 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
583{
584 #[inline]
585 fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
586 mut cs: CS,
587 cond: &Boolean,
588 first: &Self,
589 second: &Self,
590 ) -> Result<Self, SynthesisError> {
591 let c0 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
592 &mut cs.ns(|| "c0"),
593 cond,
594 &first.c0,
595 &second.c0,
596 )?;
597 let c1 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
598 &mut cs.ns(|| "c1"),
599 cond,
600 &first.c1,
601 &second.c1,
602 )?;
603
604 Ok(Self::new(c0, c1))
605 }
606
607 fn cost() -> usize {
608 2 * <Fp2Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
609 }
610}
611
612impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
613where
614 P: Fp4Parameters,
615 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
616{
617 type TableConstant = Fp4<P>;
618 fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
619 mut cs: CS,
620 b: &[Boolean],
621 c: &[Self::TableConstant],
622 ) -> Result<Self, SynthesisError> {
623 let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
624 let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
625 let c0 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
626 let c1 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
627 Ok(Self::new(c0, c1))
628 }
629
630 fn cost() -> usize {
631 2 * <Fp2Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
632 }
633}
634
635impl<P, ConstraintF: PrimeField> ThreeBitCondNegLookupGadget<ConstraintF>
636 for Fp4Gadget<P, ConstraintF>
637where
638 P: Fp4Parameters,
639 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
640{
641 type TableConstant = Fp4<P>;
642
643 fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
644 mut cs: CS,
645 b: &[Boolean],
646 b0b1: &Boolean,
647 c: &[Self::TableConstant],
648 ) -> Result<Self, SynthesisError> {
649 let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
650 let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
651 let c0 = Fp2Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
652 cs.ns(|| "Lookup c0"),
653 b,
654 b0b1,
655 &c0s,
656 )?;
657 let c1 = Fp2Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
658 cs.ns(|| "Lookup c1"),
659 b,
660 b0b1,
661 &c1s,
662 )?;
663 Ok(Self::new(c0, c1))
664 }
665
666 fn cost() -> usize {
667 2 * <Fp2Gadget<P, ConstraintF> as ThreeBitCondNegLookupGadget<ConstraintF>>::cost()
668 }
669}
670
671impl<P, ConstraintF: PrimeField> AllocGadget<Fp4<P>, ConstraintF> for Fp4Gadget<P, ConstraintF>
672where
673 P: Fp4Parameters,
674 P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
675{
676 #[inline]
677 fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
678 mut cs: CS,
679 t: T,
680 ) -> Result<Self, SynthesisError>
681 where
682 T: Borrow<Fp4<P>>,
683 {
684 Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow())
685 }
686
687 #[inline]
688 fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
689 mut cs: CS,
690 value_gen: F,
691 ) -> Result<Self, SynthesisError>
692 where
693 F: FnOnce() -> Result<T, SynthesisError>,
694 T: Borrow<Fp4<P>>,
695 {
696 let (c0, c1) = match value_gen() {
697 Ok(fe) => {
698 let fe = *fe.borrow();
699 (Ok(fe.c0), Ok(fe.c1))
700 }
701 Err(_) => (
702 Err(SynthesisError::AssignmentMissing),
703 Err(SynthesisError::AssignmentMissing),
704 ),
705 };
706
707 let c0 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
708 let c1 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
709 Ok(Self::new(c0, c1))
710 }
711
712 #[inline]
713 fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
714 mut cs: CS,
715 value_gen: F,
716 ) -> Result<Self, SynthesisError>
717 where
718 F: FnOnce() -> Result<T, SynthesisError>,
719 T: Borrow<Fp4<P>>,
720 {
721 let (c0, c1) = match value_gen() {
722 Ok(fe) => {
723 let fe = *fe.borrow();
724 (Ok(fe.c0), Ok(fe.c1))
725 }
726 Err(_) => (
727 Err(SynthesisError::AssignmentMissing),
728 Err(SynthesisError::AssignmentMissing),
729 ),
730 };
731
732 let c0 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
733 let c1 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
734 Ok(Self::new(c0, c1))
735 }
736}