arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/r1cs-std/src/fields/fp12.rs
r1cs-std/src/fields/fp12.rsRaw
  1use r1cs_core::{ConstraintSystem, SynthesisError};
  2
  3use algebra::{
  4    fields::{
  5        fp12_2over3over2::{Fp12, Fp12Parameters},
  6        fp6_3over2::{Fp6, Fp6Parameters},
  7        Fp2Parameters,
  8    },
  9    BitIterator, Field, PrimeField,
 10};
 11use num_traits::One;
 12use std::{borrow::Borrow, marker::PhantomData};
 13
 14use crate::{prelude::*, Assignment};
 15
 16type Fp2Gadget<P, ConstraintF> = super::fp2::Fp2Gadget<
 17    <<P as Fp12Parameters>::Fp6Params as Fp6Parameters>::Fp2Params,
 18    ConstraintF,
 19>;
 20type Fp6Gadget<P, ConstraintF> =
 21    super::fp6_3over2::Fp6Gadget<<P as Fp12Parameters>::Fp6Params, ConstraintF>;
 22type Fp6GadgetVariable<P, ConstraintF> = <Fp6Gadget<P, ConstraintF> as FieldGadget<
 23    Fp6<<P as Fp12Parameters>::Fp6Params>,
 24    ConstraintF,
 25>>::Variable;
 26
 27#[derive(Derivative)]
 28#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
 29#[must_use]
 30pub struct Fp12Gadget<P, ConstraintF: PrimeField>
 31where
 32    P: Fp12Parameters,
 33    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
 34{
 35    pub c0: Fp6Gadget<P, ConstraintF>,
 36    pub c1: Fp6Gadget<P, ConstraintF>,
 37    #[derivative(Debug = "ignore")]
 38    _params: PhantomData<P>,
 39}
 40
 41impl<P, ConstraintF: PrimeField> Fp12Gadget<P, ConstraintF>
 42where
 43    P: Fp12Parameters,
 44    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
 45{
 46    #[inline]
 47    pub fn new(c0: Fp6Gadget<P, ConstraintF>, c1: Fp6Gadget<P, ConstraintF>) -> Self {
 48        Self {
 49            c0,
 50            c1,
 51            _params: PhantomData,
 52        }
 53    }
 54
 55    /// Multiply by quadratic nonresidue v.
 56    #[inline]
 57    pub(crate) fn mul_fp6_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
 58        cs: CS,
 59        fe: &Fp6Gadget<P, ConstraintF>,
 60    ) -> Result<Fp6Gadget<P, ConstraintF>, SynthesisError> {
 61        let new_c0 = Fp6Gadget::<P, ConstraintF>::mul_fp2_gadget_by_nonresidue(cs, &fe.c2)?;
 62        let new_c1 = fe.c0.clone();
 63        let new_c2 = fe.c1.clone();
 64        Ok(Fp6Gadget::<P, ConstraintF>::new(new_c0, new_c1, new_c2))
 65    }
 66
 67    #[inline]
 68    pub fn conjugate_in_place<CS: ConstraintSystem<ConstraintF>>(
 69        &mut self,
 70        cs: CS,
 71    ) -> Result<&mut Self, SynthesisError> {
 72        self.c1.negate_in_place(cs)?;
 73        Ok(self)
 74    }
 75
 76    /// Multiplies by an element of the form (c0 = (c0, c1, 0), c1 = (0, d1, 0))
 77    #[inline]
 78    pub fn mul_by_014<CS: ConstraintSystem<ConstraintF>>(
 79        &self,
 80        mut cs: CS,
 81        c0: &Fp2Gadget<P, ConstraintF>,
 82        c1: &Fp2Gadget<P, ConstraintF>,
 83        d1: &Fp2Gadget<P, ConstraintF>,
 84    ) -> Result<Self, SynthesisError> {
 85        let v0 = self.c0.mul_by_c0_c1_0(cs.ns(|| "v0"), &c0, &c1)?;
 86        let v1 = self.c1.mul_by_0_c1_0(cs.ns(|| "v1"), &d1)?;
 87        let new_c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)?
 88            .add(cs.ns(|| "v0 + nonresidue * v1"), &v0)?;
 89
 90        let c1 = {
 91            let tmp = c1.add(cs.ns(|| "c1 + d1"), &d1)?;
 92            let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
 93            a0_plus_a1
 94                .mul_by_c0_c1_0(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &c0, &tmp)?
 95                .sub(cs.ns(|| "sub v0"), &v0)?
 96                .sub(cs.ns(|| "sub v1"), &v1)?
 97        };
 98        Ok(Self::new(new_c0, c1))
 99    }
100
101    /// Multiplies by an element of the form (c0 = (c0, 0, 0), c1 = (d0, d1, 0))
102    #[inline]
103    pub fn mul_by_034<CS: ConstraintSystem<ConstraintF>>(
104        &self,
105        mut cs: CS,
106        c0: &Fp2Gadget<P, ConstraintF>,
107        d0: &Fp2Gadget<P, ConstraintF>,
108        d1: &Fp2Gadget<P, ConstraintF>,
109    ) -> Result<Self, SynthesisError> {
110        let a0 = self.c0.c0.mul(cs.ns(|| "a0"), &c0)?;
111        let a1 = self.c0.c1.mul(cs.ns(|| "a1"), &c0)?;
112        let a2 = self.c0.c2.mul(cs.ns(|| "a2"), &c0)?;
113        let a = Fp6Gadget::<P, ConstraintF>::new(a0, a1, a2);
114        let b = self.c1.mul_by_c0_c1_0(cs.ns(|| "b"), &d0, &d1)?;
115
116        let c0 = c0.add(cs.ns(|| "c0 + d0"), &d0)?;
117        let c1 = d1;
118        let e = self
119            .c0
120            .add(cs.ns(|| "self.c0 + self.c1"), &self.c1)?
121            .mul_by_c0_c1_0(cs.ns(|| "compute e"), &c0, &c1)?;
122        let a_plus_b = a.add(cs.ns(|| "a + b"), &b)?;
123        let c1 = e.sub(cs.ns(|| "e - (a + b)"), &a_plus_b)?;
124        let c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "b *nr"), &b)?.add(cs.ns(|| "plus a"), &a)?;
125
126        Ok(Self::new(c0, c1))
127    }
128
129    pub fn cyclotomic_square<CS: ConstraintSystem<ConstraintF>>(
130        &self,
131        mut cs: CS,
132    ) -> Result<Self, SynthesisError> {
133        let mut result = Self::zero(cs.ns(|| "alloc result"))?;
134        let fp2_nr = <P::Fp6Params as Fp6Parameters>::NONRESIDUE;
135
136        let z0 = &self.c0.c0;
137        let z4 = &self.c0.c1;
138        let z3 = &self.c0.c2;
139        let z2 = &self.c1.c0;
140        let z1 = &self.c1.c1;
141        let z5 = &self.c1.c2;
142
143        // t0 + t1*y = (z0 + z1*y)^2 = a^2
144        let tmp = z0.mul(cs.ns(|| "first mul"), &z1)?;
145        let t0 = {
146            // (z0 + &z1) * &(z0 + &(fp2_nr * &z1)) - &tmp - &(tmp * &fp2_nr);
147            let mut cs = cs.ns(|| "t0");
148            let tmp1 = z0.add(cs.ns(|| "tmp1"), &z1)?;
149            let tmp2 = z1
150                .mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
151                .add(cs.ns(|| "tmp2.2"), &z0)?;
152            let tmp4 = tmp
153                .mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
154                .add(cs.ns(|| "tmp4.2"), &tmp)?;
155            tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
156                .sub(cs.ns(|| "tmp3.2"), &tmp4)?
157        };
158        let t1 = tmp.double(cs.ns(|| "t1"))?;
159
160        // t2 + t3*y = (z2 + z3*y)^2 = b^2
161        let tmp = z2.mul(cs.ns(|| "second mul"), &z3)?;
162        let t2 = {
163            // (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr);
164            let mut cs = cs.ns(|| "t2");
165            let tmp1 = z2.add(cs.ns(|| "tmp1"), &z3)?;
166            let tmp2 = z3
167                .mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
168                .add(cs.ns(|| "tmp2.2"), &z2)?;
169            let tmp4 = tmp
170                .mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
171                .add(cs.ns(|| "tmp4.2"), &tmp)?;
172            tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
173                .sub(cs.ns(|| "tmp3.2"), &tmp4)?
174        };
175        let t3 = tmp.double(cs.ns(|| "t3"))?;
176
177        // t4 + t5*y = (z4 + z5*y)^2 = c^2
178        let tmp = z4.mul(cs.ns(|| "third mul"), &z5)?;
179        let t4 = {
180            // (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr);
181            let mut cs = cs.ns(|| "t4");
182            let tmp1 = z4.add(cs.ns(|| "tmp1"), &z5)?;
183            let tmp2 = z5
184                .mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)?
185                .add(cs.ns(|| "tmp2.2"), &z4)?;
186            let tmp4 = tmp
187                .mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)?
188                .add(cs.ns(|| "tmp4.2"), &tmp)?;
189            tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)?
190                .sub(cs.ns(|| "tmp3.2"), &tmp4)?
191        };
192        let t5 = tmp.double(cs.ns(|| "t5"))?;
193
194        // for A
195
196        // z0 = 3 * t0 - 2 * z0
197        result.c0.c0 = {
198            let mut cs = cs.ns(|| "result.c0.c0");
199            t0.sub(cs.ns(|| "1"), &z0)?
200                .double(cs.ns(|| "2"))?
201                .add(cs.ns(|| "3"), &t0)?
202        };
203
204        // z1 = 3 * t1 + 2 * z1
205        result.c1.c1 = {
206            let mut cs = cs.ns(|| "result.c1.c1");
207            t1.add(cs.ns(|| "1"), &z1)?
208                .double(cs.ns(|| "2"))?
209                .add(cs.ns(|| "3"), &t1)?
210        };
211
212        // for B
213
214        // z2 = 3 * (xi * t5) + 2 * z2
215        result.c1.c0 = {
216            let mut cs = cs.ns(|| "result.c1.c0");
217            let tmp = t5.mul_by_constant(cs.ns(|| "1"), &fp2_nr)?;
218            z2.add(cs.ns(|| "2"), &tmp)?
219                .double(cs.ns(|| "3"))?
220                .add(cs.ns(|| "4"), &tmp)?
221        };
222
223        // z3 = 3 * t4 - 2 * z3
224        result.c0.c2 = {
225            let mut cs = cs.ns(|| "result.c0.c2");
226            t4.sub(cs.ns(|| "1"), &z3)?
227                .double(cs.ns(|| "2"))?
228                .add(cs.ns(|| "3"), &t4)?
229        };
230
231        // for C
232
233        // z4 = 3 * t2 - 2 * z4
234        result.c0.c1 = {
235            let mut cs = cs.ns(|| "result.c0.c1");
236            t2.sub(cs.ns(|| "1"), &z4)?
237                .double(cs.ns(|| "2"))?
238                .add(cs.ns(|| "3"), &t2)?
239        };
240
241        // z5 = 3 * t3 + 2 * z5
242        result.c1.c2 = {
243            let mut cs = cs.ns(|| "result.c1.c2");
244            t3.add(cs.ns(|| "1"), &z5)?
245                .double(cs.ns(|| "2"))?
246                .add(cs.ns(|| "3"), &t3)?
247        };
248
249        Ok(result)
250    }
251
252    #[inline]
253    pub fn cyclotomic_exp<CS: ConstraintSystem<ConstraintF>, S: AsRef<[u64]>>(
254        &self,
255        mut cs: CS,
256        exp: S,
257    ) -> Result<Self, SynthesisError> {
258        let mut res = Self::one(cs.ns(|| "one"))?;
259        let mut found_one = false;
260        for (j, i) in BitIterator::new(exp).enumerate() {
261            if found_one {
262                res = res.cyclotomic_square(cs.ns(|| format!("res_square_{:?}", j)))?;
263            } else {
264                found_one = i;
265            }
266            if i {
267                res.mul_in_place(cs.ns(|| format!("res_mul2_{:?}", j)), self)?;
268            }
269        }
270        Ok(res)
271    }
272}
273
274impl<P, ConstraintF: PrimeField> FieldGadget<Fp12<P>, ConstraintF> for Fp12Gadget<P, ConstraintF>
275where
276    P: Fp12Parameters,
277    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
278{
279    type Variable = (
280        Fp6GadgetVariable<P, ConstraintF>,
281        Fp6GadgetVariable<P, ConstraintF>,
282    );
283
284    #[inline]
285    fn get_value(&self) -> Option<Fp12<P>> {
286        Some(Fp12::new(self.c0.get_value()?, self.c1.get_value()?))
287    }
288
289    #[inline]
290    fn get_variable(&self) -> Self::Variable {
291        (self.c0.get_variable(), self.c1.get_variable())
292    }
293
294    #[inline]
295    fn zero<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
296        let c0 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
297        let c1 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
298        Ok(Self::new(c0, c1))
299    }
300
301    #[inline]
302    fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
303        let c0 = Fp6Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
304        let c1 = Fp6Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
305        Ok(Self::new(c0, c1))
306    }
307
308    #[inline]
309    fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
310        &self,
311        mut cs: CS,
312        bit: &Boolean,
313        coeff: Fp12<P>,
314    ) -> Result<Self, SynthesisError> {
315        let c0 = self
316            .c0
317            .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?;
318        let c1 = self
319            .c1
320            .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?;
321        Ok(Self::new(c0, c1))
322    }
323
324    #[inline]
325    fn add<CS: ConstraintSystem<ConstraintF>>(
326        &self,
327        mut cs: CS,
328        other: &Self,
329    ) -> Result<Self, SynthesisError> {
330        let c0 = self.c0.add(cs.ns(|| "c0"), &other.c0)?;
331        let c1 = self.c1.add(cs.ns(|| "c1"), &other.c1)?;
332        Ok(Self::new(c0, c1))
333    }
334
335    #[inline]
336    fn add_in_place<CS: ConstraintSystem<ConstraintF>>(
337        &mut self,
338        mut cs: CS,
339        other: &Self,
340    ) -> Result<&mut Self, SynthesisError> {
341        self.c0.add_in_place(cs.ns(|| "c0"), &other.c0)?;
342        self.c1.add_in_place(cs.ns(|| "c1"), &other.c1)?;
343        Ok(self)
344    }
345
346    #[inline]
347    fn sub<CS: ConstraintSystem<ConstraintF>>(
348        &self,
349        mut cs: CS,
350        other: &Self,
351    ) -> Result<Self, SynthesisError> {
352        let c0 = self.c0.sub(cs.ns(|| "c0"), &other.c0)?;
353        let c1 = self.c1.sub(cs.ns(|| "c1"), &other.c1)?;
354        Ok(Self::new(c0, c1))
355    }
356
357    #[inline]
358    fn sub_in_place<CS: ConstraintSystem<ConstraintF>>(
359        &mut self,
360        mut cs: CS,
361        other: &Self,
362    ) -> Result<&mut Self, SynthesisError> {
363        self.c0.sub_in_place(cs.ns(|| "c0"), &other.c0)?;
364        self.c1.sub_in_place(cs.ns(|| "c1"), &other.c1)?;
365        Ok(self)
366    }
367
368    #[inline]
369    fn negate<CS: ConstraintSystem<ConstraintF>>(
370        &self,
371        mut cs: CS,
372    ) -> Result<Self, SynthesisError> {
373        let c0 = self.c0.negate(cs.ns(|| "c0"))?;
374        let c1 = self.c1.negate(cs.ns(|| "c1"))?;
375        Ok(Self::new(c0, c1))
376    }
377
378    #[inline]
379    fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
380        &mut self,
381        mut cs: CS,
382    ) -> Result<&mut Self, SynthesisError> {
383        self.c0.negate_in_place(cs.ns(|| "c0"))?;
384        self.c1.negate_in_place(cs.ns(|| "c1"))?;
385        Ok(self)
386    }
387
388    #[inline]
389    fn mul<CS: ConstraintSystem<ConstraintF>>(
390        &self,
391        mut cs: CS,
392        other: &Self,
393    ) -> Result<Self, SynthesisError> {
394        // Karatsuba multiplication:
395        // v0 = A.c0 * B.c0
396        // v1 = A.c1 * B.c1
397        // result.c0 = v0 + non_residue * v1
398        // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
399        //
400        // Enforced with 3 Fp3_mul_gadget's that ensure that:
401        // A.c1 * B.c1 = v1
402        // A.c0 * B.c0 = v0
403        // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + v0 + v1
404
405        let v0 = self.c0.mul(cs.ns(|| "v0"), &other.c0)?;
406        let v1 = self.c1.mul(cs.ns(|| "v1"), &other.c1)?;
407        let c0 = {
408            let non_residue_times_v1 =
409                Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)?;
410            v0.add(cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)?
411        };
412        let c1 = {
413            let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
414            let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?;
415            let a0_plus_a1_times_b0_plus_b1 =
416                a0_plus_a1.mul(&mut cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?;
417            a0_plus_a1_times_b0_plus_b1
418                .sub(cs.ns(|| "res - v0"), &v0)?
419                .sub(cs.ns(|| "res - v0 - v1"), &v1)?
420        };
421
422        Ok(Self::new(c0, c1))
423    }
424
425    fn square<CS: ConstraintSystem<ConstraintF>>(
426        &self,
427        mut cs: CS,
428    ) -> Result<Self, SynthesisError> {
429        // From Libsnark/fp2_gadget.tcc
430        // Complex multiplication for Fp2:
431        //     v0 = A.c0 * A.c1
432        //     result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 +
433        // non_residue) * v0     result.c1 = 2 * v0
434        // Enforced with 2 constraints:
435        //     (2*A.c0) * A.c1 = result.c1
436        //     (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1
437        // + non_residue)/2 Reference:
438        //     "Multiplication and Squaring on Pairing-Friendly Fields"
439        //     Devegili, OhEigeartaigh, Scott, Dahab
440
441        let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?;
442        let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
443
444        let non_residue_a1 = Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * a1"), &self.c1)?;
445        let a0_plus_non_residue_a1 = self
446            .c0
447            .add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_a1)?;
448        let one_plus_non_residue_v0 =
449            Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * v0"), &v0)?
450                .add(cs.ns(|| "plus v0"), &v0)?;
451
452        let c0 = a0_plus_a1
453            .mul(
454                cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"),
455                &a0_plus_non_residue_a1,
456            )?
457            .sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?;
458
459        v0.double_in_place(cs.ns(|| "2v0"))?;
460        let c1 = v0;
461
462        Ok(Self {
463            c0,
464            c1,
465            _params: PhantomData,
466        })
467    }
468
469    #[inline]
470    fn add_constant<CS: ConstraintSystem<ConstraintF>>(
471        &self,
472        mut cs: CS,
473        other: &Fp12<P>,
474    ) -> Result<Self, SynthesisError> {
475        let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?;
476        let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?;
477
478        Ok(Self::new(c0, c1))
479    }
480
481    #[inline]
482    fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
483        &mut self,
484        mut cs: CS,
485        other: &Fp12<P>,
486    ) -> Result<&mut Self, SynthesisError> {
487        self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?;
488        self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?;
489        Ok(self)
490    }
491
492    fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
493        &self,
494        mut cs: CS,
495        other: &Fp12<P>,
496    ) -> Result<Self, SynthesisError> {
497        // Karatsuba multiplication (see mul above).
498        // Doesn't need any constraints; returns linear combinations of
499        // `self`'s variables.
500        //
501        // (The operations below are guaranteed to return linear combinations)
502        let (a0, a1) = (&self.c0, &self.c1);
503        let (b0, b1) = (other.c0, other.c1);
504        let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?;
505        let mut v1 = Self::mul_fp6_by_nonresidue(&mut cs.ns(|| "v1"), a1)?;
506        let beta_v1 = v1.mul_by_constant_in_place(&mut cs.ns(|| "beta * v1"), &b1)?;
507
508        v0.add_in_place(&mut cs.ns(|| "c0"), beta_v1)?;
509        let c0 = v0;
510
511        let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?;
512        let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?;
513        a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?;
514        let c1 = a0b1;
515        Ok(Self::new(c0, c1))
516    }
517
518    fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
519        &self,
520        cs: CS,
521        power: usize,
522    ) -> Result<Self, SynthesisError> {
523        let mut res = self.clone();
524        res.frobenius_map_in_place(cs, power)?;
525        Ok(res)
526    }
527
528    fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
529        &mut self,
530        mut cs: CS,
531        power: usize,
532    ) -> Result<&mut Self, SynthesisError> {
533        self.c0
534            .frobenius_map_in_place(cs.ns(|| "frob_map1"), power)?;
535        self.c1
536            .frobenius_map_in_place(cs.ns(|| "frob_map2"), power)?;
537
538        self.c1
539            .c0
540            .mul_by_constant_in_place(cs.ns(|| "mul1"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
541        self.c1
542            .c1
543            .mul_by_constant_in_place(cs.ns(|| "mul2"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
544        self.c1
545            .c2
546            .mul_by_constant_in_place(cs.ns(|| "mul3"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?;
547        Ok(self)
548    }
549
550    fn inverse<CS: ConstraintSystem<ConstraintF>>(
551        &self,
552        mut cs: CS,
553    ) -> Result<Self, SynthesisError> {
554        let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || {
555            self.get_value().and_then(|val| val.inverse()).get()
556        })?;
557
558        // Karatsuba multiplication for Fp2 with the inverse:
559        //     v0 = A.c0 * B.c0
560        //     v1 = A.c1 * B.c1
561        //
562        //      1 = v0 + non_residue * v1
563        //  => v0 = 1 - non_residue * v1
564        //
565        //      0 = result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
566        //  => v0 + v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
567        //  => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
568        // Enforced with 2 constraints:
569        //     A.c1 * B.c1 = v1
570        //  => 1 + (1 - non_residue) * v1 = (A.c0 + A.c1) * (B.c0 + B.c1)
571        // Reference:
572        // "Multiplication and Squaring on Pairing-Friendly Fields"
573        // Devegili, OhEigeartaigh, Scott, Dahab
574
575        // Constraint 1
576        let v1 = self.c1.mul(cs.ns(|| "inv_constraint_1"), &inverse.c1)?;
577
578        // Constraint 2
579        let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?;
580        let b0_plus_b1 = inverse.c0.add(cs.ns(|| "b0 + b1"), &inverse.c1)?;
581
582        let one = Fp6::<P::Fp6Params>::one();
583        let rhs = Self::mul_fp6_by_nonresidue(cs.ns(|| "nr * v1"), &v1)?
584            .sub(cs.ns(|| "sub v1"), &v1)?
585            .negate(cs.ns(|| "negate it"))?
586            .add_constant(cs.ns(|| "add one"), &one)?;
587        a0_plus_a1.mul_equals(cs.ns(|| "inv_constraint_2"), &b0_plus_b1, &rhs)?;
588        Ok(inverse)
589    }
590
591    fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
592        &self,
593        mut cs: CS,
594        other: &Self,
595        result: &Self,
596    ) -> Result<(), SynthesisError> {
597        // Karatsuba multiplication for Fp2:
598        //     v0 = A.c0 * B.c0
599        //     v1 = A.c1 * B.c1
600        //     result.c0 = v0 + non_residue * v1
601        //     result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1
602        // Enforced with 3 constraints:
603        //     A.c1 * B.c1 = v1
604        //     A.c0 * B.c0 = result.c0 - non_residue * v1
605        //     (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1
606        // Reference:
607        // "Multiplication and Squaring on Pairing-Friendly Fields"
608        // Devegili, OhEigeartaigh, Scott, Dahab
609        let mul_cs = &mut cs.ns(|| "mul");
610
611        // Compute v1
612        let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?;
613
614        // Perform second check
615        let non_residue_times_v1 = Self::mul_fp6_by_nonresidue(mul_cs.ns(|| "nr * v1"), &v1)?;
616        let rhs = result
617            .c0
618            .sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?;
619        self.c0
620            .mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?;
621
622        // Last check
623        let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?;
624        let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?;
625        let one_minus_non_residue_v1 =
626            v1.sub(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?;
627
628        let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result
629            .c1
630            .add(mul_cs.ns(|| "c1 + c0"), &result.c0)?
631            .add(mul_cs.ns(|| "rest of stuff"), &one_minus_non_residue_v1)?;
632
633        a0_plus_a1.mul_equals(
634            mul_cs.ns(|| "third check"),
635            &b0_plus_b1,
636            &result_c1_plus_result_c0_plus_one_minus_non_residue_v1,
637        )?;
638
639        Ok(())
640    }
641
642    fn cost_of_mul() -> usize {
643        unimplemented!()
644    }
645
646    fn cost_of_inv() -> usize {
647        Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
648    }
649}
650
651impl<P, ConstraintF: PrimeField> PartialEq for Fp12Gadget<P, ConstraintF>
652where
653    P: Fp12Parameters,
654    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
655{
656    fn eq(&self, other: &Self) -> bool {
657        self.c0 == other.c0 && self.c1 == other.c1
658    }
659}
660
661impl<P, ConstraintF: PrimeField> Eq for Fp12Gadget<P, ConstraintF>
662where
663    P: Fp12Parameters,
664    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
665{
666}
667
668impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
669where
670    P: Fp12Parameters,
671    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
672{
673}
674
675impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
676where
677    P: Fp12Parameters,
678    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
679{
680    #[inline]
681    fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
682        &self,
683        mut cs: CS,
684        other: &Self,
685        condition: &Boolean,
686    ) -> Result<(), SynthesisError> {
687        self.c0
688            .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?;
689        self.c1
690            .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?;
691        Ok(())
692    }
693
694    fn cost() -> usize {
695        2 * <Fp6Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
696    }
697}
698
699impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
700where
701    P: Fp12Parameters,
702    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
703{
704    #[inline]
705    fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
706        &self,
707        mut cs: CS,
708        other: &Self,
709    ) -> Result<(), SynthesisError> {
710        self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?;
711        self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?;
712        Ok(())
713    }
714
715    fn cost() -> usize {
716        2 * <Fp6Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
717    }
718}
719
720impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
721where
722    P: Fp12Parameters,
723    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
724{
725    fn to_bits<CS: ConstraintSystem<ConstraintF>>(
726        &self,
727        mut cs: CS,
728    ) -> Result<Vec<Boolean>, SynthesisError> {
729        let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?;
730        let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?;
731        c0.append(&mut c1);
732        Ok(c0)
733    }
734
735    fn to_bits_strict<CS: ConstraintSystem<ConstraintF>>(
736        &self,
737        mut cs: CS,
738    ) -> Result<Vec<Boolean>, SynthesisError> {
739        let mut c0 = self.c0.to_bits_strict(cs.ns(|| "c0"))?;
740        let mut c1 = self.c1.to_bits_strict(cs.ns(|| "c1"))?;
741        c0.append(&mut c1);
742        Ok(c0)
743    }
744}
745
746impl<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
747where
748    P: Fp12Parameters,
749    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
750{
751    fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
752        &self,
753        mut cs: CS,
754    ) -> Result<Vec<UInt8>, SynthesisError> {
755        let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?;
756        let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?;
757        c0.append(&mut c1);
758        Ok(c0)
759    }
760
761    fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
762        &self,
763        mut cs: CS,
764    ) -> Result<Vec<UInt8>, SynthesisError> {
765        let mut c0 = self.c0.to_bytes_strict(cs.ns(|| "c0"))?;
766        let mut c1 = self.c1.to_bytes_strict(cs.ns(|| "c1"))?;
767        c0.append(&mut c1);
768        Ok(c0)
769    }
770}
771
772impl<P, ConstraintF: PrimeField> Clone for Fp12Gadget<P, ConstraintF>
773where
774    P: Fp12Parameters,
775    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
776{
777    fn clone(&self) -> Self {
778        Self::new(self.c0.clone(), self.c1.clone())
779    }
780}
781
782impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
783where
784    P: Fp12Parameters,
785    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
786{
787    #[inline]
788    fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
789        mut cs: CS,
790        cond: &Boolean,
791        first: &Self,
792        second: &Self,
793    ) -> Result<Self, SynthesisError> {
794        let c0 = Fp6Gadget::<P, ConstraintF>::conditionally_select(
795            &mut cs.ns(|| "c0"),
796            cond,
797            &first.c0,
798            &second.c0,
799        )?;
800        let c1 = Fp6Gadget::<P, ConstraintF>::conditionally_select(
801            &mut cs.ns(|| "c1"),
802            cond,
803            &first.c1,
804            &second.c1,
805        )?;
806
807        Ok(Self::new(c0, c1))
808    }
809
810    fn cost() -> usize {
811        2 * <Fp6Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
812    }
813}
814
815impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp12Gadget<P, ConstraintF>
816where
817    P: Fp12Parameters,
818    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
819{
820    type TableConstant = Fp12<P>;
821    fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
822        mut cs: CS,
823        b: &[Boolean],
824        c: &[Self::TableConstant],
825    ) -> Result<Self, SynthesisError> {
826        let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
827        let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
828        let c0 = Fp6Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
829        let c1 = Fp6Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
830        Ok(Self::new(c0, c1))
831    }
832
833    fn cost() -> usize {
834        2 * <Fp6Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
835    }
836}
837
838impl<P, ConstraintF: PrimeField> ThreeBitCondNegLookupGadget<ConstraintF>
839    for Fp12Gadget<P, ConstraintF>
840where
841    P: Fp12Parameters,
842    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
843{
844    type TableConstant = Fp12<P>;
845
846    fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
847        mut cs: CS,
848        b: &[Boolean],
849        b0b1: &Boolean,
850        c: &[Self::TableConstant],
851    ) -> Result<Self, SynthesisError> {
852        let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
853        let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
854        let c0 = Fp6Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
855            cs.ns(|| "Lookup c0"),
856            b,
857            b0b1,
858            &c0s,
859        )?;
860        let c1 = Fp6Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
861            cs.ns(|| "Lookup c1"),
862            b,
863            b0b1,
864            &c1s,
865        )?;
866        Ok(Self::new(c0, c1))
867    }
868
869    fn cost() -> usize {
870        2 * <Fp6Gadget<P, ConstraintF> as ThreeBitCondNegLookupGadget<ConstraintF>>::cost()
871    }
872}
873
874impl<P, ConstraintF: PrimeField> AllocGadget<Fp12<P>, ConstraintF> for Fp12Gadget<P, ConstraintF>
875where
876    P: Fp12Parameters,
877    <P::Fp6Params as Fp6Parameters>::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
878{
879    #[inline]
880    fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
881        mut cs: CS,
882        value_gen: F,
883    ) -> Result<Self, SynthesisError>
884    where
885        F: FnOnce() -> Result<T, SynthesisError>,
886        T: Borrow<Fp12<P>>,
887    {
888        let (c0, c1) = match value_gen() {
889            Ok(fe) => {
890                let fe = *fe.borrow();
891                (Ok(fe.c0), Ok(fe.c1))
892            },
893            Err(_) => (
894                Err(SynthesisError::AssignmentMissing),
895                Err(SynthesisError::AssignmentMissing),
896            ),
897        };
898
899        let c0 = Fp6Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
900        let c1 = Fp6Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
901        Ok(Self::new(c0, c1))
902    }
903
904    #[inline]
905    fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
906        mut cs: CS,
907        value_gen: F,
908    ) -> Result<Self, SynthesisError>
909    where
910        F: FnOnce() -> Result<T, SynthesisError>,
911        T: Borrow<Fp12<P>>,
912    {
913        let (c0, c1) = match value_gen() {
914            Ok(fe) => {
915                let fe = *fe.borrow();
916                (Ok(fe.c0), Ok(fe.c1))
917            },
918            Err(_) => (
919                Err(SynthesisError::AssignmentMissing),
920                Err(SynthesisError::AssignmentMissing),
921            ),
922        };
923
924        let c0 = Fp6Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
925        let c1 = Fp6Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
926        Ok(Self::new(c0, c1))
927    }
928}