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> 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<F, T, CS: ConstraintSystem<ConstraintF>>(
678        mut cs: CS,
679        value_gen: F,
680    ) -> Result<Self, SynthesisError>
681    where
682        F: FnOnce() -> Result<T, SynthesisError>,
683        T: Borrow<Fp4<P>>,
684    {
685        let (c0, c1) = match value_gen() {
686            Ok(fe) => {
687                let fe = *fe.borrow();
688                (Ok(fe.c0), Ok(fe.c1))
689            }
690            Err(_) => (
691                Err(SynthesisError::AssignmentMissing),
692                Err(SynthesisError::AssignmentMissing),
693            ),
694        };
695
696        let c0 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
697        let c1 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
698        Ok(Self::new(c0, c1))
699    }
700
701    #[inline]
702    fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
703        mut cs: CS,
704        value_gen: F,
705    ) -> Result<Self, SynthesisError>
706    where
707        F: FnOnce() -> Result<T, SynthesisError>,
708        T: Borrow<Fp4<P>>,
709    {
710        let (c0, c1) = match value_gen() {
711            Ok(fe) => {
712                let fe = *fe.borrow();
713                (Ok(fe.c0), Ok(fe.c1))
714            }
715            Err(_) => (
716                Err(SynthesisError::AssignmentMissing),
717                Err(SynthesisError::AssignmentMissing),
718            ),
719        };
720
721        let c0 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
722        let c1 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
723        Ok(Self::new(c0, c1))
724    }
725}