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