arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
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}