use algebra::{ fields::{ fp6_3over2::{Fp6, Fp6Parameters}, Field, Fp2Parameters, }, PrimeField, }; use core::{borrow::Borrow, marker::PhantomData}; use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError}; use crate::{prelude::*, Assignment, Vec}; type Fp2Gadget = super::fp2::Fp2Gadget<

::Fp2Params, ConstraintF>; #[derive(Derivative)] #[derivative(Debug(bound = "ConstraintF: PrimeField"))] #[must_use] pub struct Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { pub c0: Fp2Gadget, pub c1: Fp2Gadget, pub c2: Fp2Gadget, #[derivative(Debug = "ignore")] _params: PhantomData

, } impl Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { #[inline] pub fn new( c0: Fp2Gadget, c1: Fp2Gadget, c2: Fp2Gadget, ) -> Self { Self { c0, c1, c2, _params: PhantomData, } } /// Multiply a Fp2Gadget by cubic nonresidue P::NONRESIDUE. #[inline] pub fn mul_fp2_gadget_by_nonresidue>( cs: CS, fe: &Fp2Gadget, ) -> Result, SynthesisError> { fe.mul_by_constant(cs, &P::NONRESIDUE) } #[inline] pub fn mul_by_0_c1_0>( &self, mut cs: CS, c1: &Fp2Gadget, ) -> Result { // Karatsuba multiplication // v0 = a0 * b0 = 0 // v1 = a1 * b1 let v1 = self.c1.mul(cs.ns(|| "first mul"), c1)?; // v2 = a2 * b2 = 0 let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?; let b1_plus_b2 = c1.clone(); let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; // c0 = (NONRESIDUE * ((a1 + a2)*(b1 + b2) - v1 - v2)) + v0 // = NONRESIDUE * ((a1 + a2) * b1 - v1) let c0 = a1_plus_a2 .mul(cs.ns(|| "second mul"), &b1_plus_b2)? .sub(cs.ns(|| "first sub"), &v1)? .mul_by_constant(cs.ns(|| "mul_by_nonresidue"), &P::NONRESIDUE)?; // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + NONRESIDUE * v2 // = (a0 + a1) * b1 - v1 let c1 = a0_plus_a1 .mul(cs.ns(|| "third mul"), &c1)? .sub(cs.ns(|| "second sub"), &v1)?; // c2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1 // = v1 let c2 = v1; Ok(Self::new(c0, c1, c2)) } // #[inline] pub fn mul_by_c0_c1_0>( &self, mut cs: CS, c0: &Fp2Gadget, c1: &Fp2Gadget, ) -> Result { let v0 = self.c0.mul(cs.ns(|| "v0"), c0)?; let v1 = self.c1.mul(cs.ns(|| "v1"), c1)?; // v2 = 0. let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?; let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?; let b1_plus_b2 = c1.clone(); let b0_plus_b1 = c0.add(cs.ns(|| "b0 + b1"), &c1)?; let b0_plus_b2 = c0.clone(); let c0 = { let cs = &mut cs.ns(|| "c0"); a1_plus_a2 .mul(cs.ns(|| "(a1 + a2) * (b1 + b2)"), &b1_plus_b2)? .sub(cs.ns(|| "sub v1"), &v1)? .mul_by_constant(cs.ns(|| "First mul_by_nonresidue"), &P::NONRESIDUE)? .add(cs.ns(|| "add v0"), &v0)? }; let c1 = { let cs = &mut cs.ns(|| "c1"); a0_plus_a1 .mul(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)? .sub(cs.ns(|| "sub v0"), &v0)? .sub(cs.ns(|| "sub v1"), &v1)? }; let c2 = { a0_plus_a2 .mul(cs.ns(|| "(a0 + a2) * (b0 + b2)"), &b0_plus_b2)? .sub(cs.ns(|| "sub v0"), &v0)? .add(cs.ns(|| "add v1"), &v1)? }; Ok(Self::new(c0, c1, c2)) } } impl FieldGadget, ConstraintF> for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { type Variable = ( (ConstraintVar, ConstraintVar), (ConstraintVar, ConstraintVar), (ConstraintVar, ConstraintVar), ); #[inline] fn get_value(&self) -> Option> { match ( self.c0.get_value(), self.c1.get_value(), self.c2.get_value(), ) { (Some(c0), Some(c1), Some(c2)) => Some(Fp6::new(c0, c1, c2)), (..) => None, } } #[inline] fn get_variable(&self) -> Self::Variable { ( self.c0.get_variable(), self.c1.get_variable(), self.c2.get_variable(), ) } #[inline] fn zero>(mut cs: CS) -> Result { let c0 = Fp2Gadget::::zero(cs.ns(|| "c0"))?; let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; let c2 = Fp2Gadget::::zero(cs.ns(|| "c2"))?; Ok(Self::new(c0, c1, c2)) } #[inline] fn one>(mut cs: CS) -> Result { let c0 = Fp2Gadget::::one(cs.ns(|| "c0"))?; let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; let c2 = Fp2Gadget::::zero(cs.ns(|| "c2"))?; Ok(Self::new(c0, c1, c2)) } #[inline] fn conditionally_add_constant>( &self, mut cs: CS, bit: &Boolean, coeff: Fp6

, ) -> Result { let c0 = self .c0 .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; let c1 = self .c1 .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; let c2 = self .c2 .conditionally_add_constant(cs.ns(|| "c2"), bit, coeff.c2)?; Ok(Self::new(c0, c1, c2)) } #[inline] fn add>( &self, mut cs: CS, other: &Self, ) -> Result { let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?; let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?; let c2 = self.c2.add(&mut cs.ns(|| "add c2"), &other.c2)?; Ok(Self::new(c0, c1, c2)) } #[inline] fn sub>( &self, mut cs: CS, other: &Self, ) -> Result { let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?; let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?; let c2 = self.c2.sub(&mut cs.ns(|| "sub c2"), &other.c2)?; Ok(Self::new(c0, c1, c2)) } #[inline] fn negate>( &self, mut cs: CS, ) -> Result { let c0 = self.c0.negate(&mut cs.ns(|| "negate c0"))?; let c1 = self.c1.negate(&mut cs.ns(|| "negate c1"))?; let c2 = self.c2.negate(&mut cs.ns(|| "negate c2"))?; Ok(Self::new(c0, c1, c2)) } #[inline] fn negate_in_place>( &mut self, mut cs: CS, ) -> Result<&mut Self, SynthesisError> { self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?; self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?; self.c2.negate_in_place(&mut cs.ns(|| "negate c2"))?; Ok(self) } /// Use the Toom-Cook-3x method to compute multiplication. #[inline] fn mul>( &self, mut cs: CS, other: &Self, ) -> Result { // Uses Toom-Cool-3x multiplication from // // Reference: // "Multiplication and Squaring on Pairing-Friendly Fields" // Devegili, OhEigeartaigh, Scott, Dahab // v0 = a(0)b(0) = a0 * b0 let v0 = self.c0.mul(&mut cs.ns(|| "Calc v0"), &other.c0)?; // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2) let v1 = { let mut v1_cs = cs.ns(|| "compute v1"); let a0_plus_a1_plus_a2 = self .c0 .add(v1_cs.ns(|| "a0 + a1"), &self.c1)? .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?; let b0_plus_b1_plus_b2 = other .c0 .add(v1_cs.ns(|| "b0 + b1"), &other.c1)? .add(v1_cs.ns(|| "b0 + b1 + b2"), &other.c2)?; a0_plus_a1_plus_a2.mul( v1_cs.ns(|| "(a0 + a1 + a2)(b0 + b1 + b2)"), &b0_plus_b1_plus_b2, )? }; // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2) let v2 = { let mut v2_cs = cs.ns(|| "compute v2"); let a0_minus_a1_plus_a2 = self .c0 .sub(v2_cs.ns(|| "a0 - a1"), &self.c1)? .add(v2_cs.ns(|| "a0 - a1 + a2"), &self.c2)?; let b0_minus_b1_plus_b2 = other .c0 .sub(v2_cs.ns(|| "b0 - b1"), &other.c1)? .add(v2_cs.ns(|| "b0 - b1 + b2"), &other.c2)?; a0_minus_a1_plus_a2.mul( v2_cs.ns(|| "(a0 - a1 + a2)(b0 - b1 + b2)"), &b0_minus_b1_plus_b2, )? }; // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2) let v3 = { let v3_cs = &mut cs.ns(|| "compute v3"); let a1_double = self.c1.double(v3_cs.ns(|| "2 * a1"))?; let a2_quad = self .c2 .double(v3_cs.ns(|| "2 * a2"))? .double(v3_cs.ns(|| "4 * a2"))?; let a0_plus_2_a1_plus_4_a2 = self .c0 .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)? .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?; let b1_double = other.c1.double(v3_cs.ns(|| "2 * b1"))?; let b2_quad = other .c2 .double(v3_cs.ns(|| "2 * b2"))? .double(v3_cs.ns(|| "4 * b2"))?; let b0_plus_2_b1_plus_4_b2 = other .c0 .add(v3_cs.ns(|| "b0 + 2b1"), &b1_double)? .add(v3_cs.ns(|| "b0 + 2b1 + 4b2"), &b2_quad)?; a0_plus_2_a1_plus_4_a2.mul( v3_cs.ns(|| "(a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)"), &b0_plus_2_b1_plus_4_b2, )? }; // v4 = a(∞)b(∞) = a2 * b2 let v4 = self.c2.mul(cs.ns(|| "v2: a2 * b2"), &other.c2)?; let two = ::Fp::one().double(); let six = two.double() + &two; let mut two_and_six = [two, six]; algebra::fields::batch_inversion(&mut two_and_six); let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]); let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?; let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?; let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_six"), &six_inverse)?; let one_sixth_v3 = v3.mul_by_fp_constant(cs.ns(|| "v3_by_six"), &six_inverse)?; let two_v4 = v4.double(cs.ns(|| "2 * v4"))?; // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) let c0 = { let c0_cs = &mut cs.ns(|| "c0"); // No constraints, only get a linear combination back. let temp = half_v0 .sub(c0_cs.ns(|| "sub1"), &half_v1)? .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)? .add(c0_cs.ns(|| "add3"), &one_sixth_v3)? .sub(c0_cs.ns(|| "sub4"), &two_v4)?; let non_residue_times_inner = temp.mul_by_constant(&mut c0_cs.ns(|| "mul5"), &P::NONRESIDUE)?; v0.add(c0_cs.ns(|| "add6"), &non_residue_times_inner)? }; // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 let c1 = { let c1_cs = &mut cs.ns(|| "c1"); let one_third_v2 = one_sixth_v2.double(&mut c1_cs.ns(|| "v2_by_3"))?; let non_residue_v4 = v4.mul_by_constant(&mut c1_cs.ns(|| "mul_by_beta"), &P::NONRESIDUE)?; let result = half_v0 .negate(c1_cs.ns(|| "neg1"))? .add(c1_cs.ns(|| "add2"), &v1)? .sub(c1_cs.ns(|| "sub3"), &one_third_v2)? .sub(c1_cs.ns(|| "sub4"), &one_sixth_v3)? .add(c1_cs.ns(|| "sub5"), &two_v4)? .add(c1_cs.ns(|| "sub6"), &non_residue_v4)?; result }; // -v0 + (1/2)v1 + (1/2)v2 −v4 let c2 = { let c2_cs = &mut cs.ns(|| "c2"); let half_v2 = v2.mul_by_fp_constant(&mut c2_cs.ns(|| "mul1"), &two_inverse)?; let result = half_v1 .add(c2_cs.ns(|| "add1"), &half_v2)? .sub(c2_cs.ns(|| "sub1"), &v4)? .sub(c2_cs.ns(|| "sub2"), &v0)?; result }; Ok(Self::new(c0, c1, c2)) } /// Use the Toom-Cook-3x method to compute multiplication. #[inline] fn square>( &self, mut cs: CS, ) -> Result { // Uses Toom-Cool-3x multiplication from // // Reference: // "Multiplication and Squaring on Pairing-Friendly Fields" // Devegili, OhEigeartaigh, Scott, Dahab // v0 = a(0)^2 = a0^2 let v0 = self.c0.square(&mut cs.ns(|| "Calc v0"))?; // v1 = a(1)^2 = (a0 + a1 + a2)^2 let v1 = { let a0_plus_a1_plus_a2 = self .c0 .add(cs.ns(|| "a0 + a1"), &self.c1)? .add(cs.ns(|| "a0 + a1 + a2"), &self.c2)?; a0_plus_a1_plus_a2.square(&mut cs.ns(|| "(a0 + a1 + a2)^2"))? }; // v2 = a(−1)^2 = (a0 − a1 + a2)^2 let v2 = { let a0_minus_a1_plus_a2 = self .c0 .sub(cs.ns(|| "a0 - a1"), &self.c1)? .add(cs.ns(|| "a0 - a2 + a2"), &self.c2)?; a0_minus_a1_plus_a2.square(&mut cs.ns(|| "(a0 - a1 + a2)^2"))? }; // v3 = a(2)^2 = (a0 + 2a1 + 4a2)^2 let v3 = { let a1_double = self.c1.double(cs.ns(|| "2a1"))?; let a2_quad = self.c2.double(cs.ns(|| "2a2"))?.double(cs.ns(|| "4a2"))?; let a0_plus_2_a1_plus_4_a2 = self .c0 .add(cs.ns(|| "a0 + 2a1"), &a1_double)? .add(cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?; a0_plus_2_a1_plus_4_a2.square(&mut cs.ns(|| "(a0 + 2a1 + 4a2)^2"))? }; // v4 = a(∞)^2 = a2^2 let v4 = self.c2.square(&mut cs.ns(|| "a2^2"))?; let two = ::Fp::one().double(); let six = two.double() + &two; let mut two_and_six = [two, six]; algebra::fields::batch_inversion(&mut two_and_six); let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]); let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?; let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?; let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "one_sixth_v2"), &six_inverse)?; let one_sixth_v3 = v3.mul_by_fp_constant(cs.ns(|| "one_sixth_v3"), &six_inverse)?; let two_v4 = v4.double(cs.ns(|| "double_v4"))?; // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) let c0 = { let mut c0_cs = cs.ns(|| "c0"); // No constraints, only get a linear combination back. let inner = half_v0 .sub(c0_cs.ns(|| "sub1"), &half_v1)? .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)? .add(c0_cs.ns(|| "add3"), &one_sixth_v3)? .sub(c0_cs.ns(|| "sub4"), &two_v4)?; let non_residue_times_inner = inner.mul_by_constant(c0_cs.ns(|| "mul_by_res"), &P::NONRESIDUE)?; v0.add(c0_cs.ns(|| "add5"), &non_residue_times_inner)? }; // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 let c1 = { let mut c1_cs = cs.ns(|| "c1"); let one_third_v2 = one_sixth_v2.double(c1_cs.ns(|| "v2_by_3"))?; let non_residue_v4 = v4.mul_by_constant(c1_cs.ns(|| "mul_by_res"), &P::NONRESIDUE)?; half_v0 .negate(c1_cs.ns(|| "neg1"))? .add(c1_cs.ns(|| "add1"), &v1)? .sub(c1_cs.ns(|| "sub2"), &one_third_v2)? .sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)? .add(c1_cs.ns(|| "add4"), &two_v4)? .add(c1_cs.ns(|| "add5"), &non_residue_v4)? }; // -v0 + (1/2)v1 + (1/2)v2 −v4 let c2 = { let mut c2_cs = cs.ns(|| "c2"); let half_v2 = v2.mul_by_fp_constant(c2_cs.ns(|| "half_v2"), &two_inverse)?; half_v1 .add(c2_cs.ns(|| "add1"), &half_v2)? .sub(c2_cs.ns(|| "sub1"), &v4)? .sub(c2_cs.ns(|| "sub2"), &v0)? }; Ok(Self::new(c0, c1, c2)) } // 18 constaints, we can probably do better but not sure it's worth it. #[inline] fn inverse>( &self, mut cs: CS, ) -> Result { let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || { self.get_value().and_then(|val| val.inverse()).get() })?; let one = Self::one(cs.ns(|| "one"))?; inverse.mul_equals(cs.ns(|| "check inverse"), &self, &one)?; Ok(inverse) } #[inline] fn add_constant>( &self, mut cs: CS, other: &Fp6

, ) -> Result { let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?; let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?; let c2 = self.c2.add_constant(cs.ns(|| "c2"), &other.c2)?; Ok(Self::new(c0, c1, c2)) } #[inline] fn add_constant_in_place>( &mut self, mut cs: CS, other: &Fp6

, ) -> Result<&mut Self, SynthesisError> { self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?; self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?; self.c2.add_constant_in_place(cs.ns(|| "c2"), &other.c2)?; Ok(self) } /// Use the Toom-Cook-3x method to compute multiplication. #[inline] fn mul_by_constant>( &self, mut cs: CS, other: &Fp6

, ) -> Result { // Uses Toom-Cook-3x multiplication from // // Reference: // "Multiplication and Squaring on Pairing-Friendly Fields" // Devegili, OhEigeartaigh, Scott, Dahab // v0 = a(0)b(0) = a0 * b0 let v0 = self.c0.mul_by_constant(cs.ns(|| "v0"), &other.c0)?; // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2) let v1 = { let mut v1_cs = cs.ns(|| "v1"); let mut a0_plus_a1_plus_a2 = self .c0 .add(v1_cs.ns(|| "a0 + a1"), &self.c1)? .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?; let b0_plus_b1_plus_b2 = other.c0 + &other.c1 + &other.c2; a0_plus_a1_plus_a2.mul_by_constant_in_place( v1_cs.ns(|| "(a0 + a1 + a2)*(b0 + b1 + b2)"), &b0_plus_b1_plus_b2, )?; a0_plus_a1_plus_a2 }; // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2) let mut v2 = { let mut v2_cs = cs.ns(|| "v2"); let mut a0_minus_a1_plus_a2 = self .c0 .sub(v2_cs.ns(|| "sub1"), &self.c1)? .add(v2_cs.ns(|| "add2"), &self.c2)?; let b0_minus_b1_plus_b2 = other.c0 - &other.c1 + &other.c2; a0_minus_a1_plus_a2.mul_by_constant_in_place( v2_cs.ns(|| "(a0 - a1 + a2)*(b0 - b1 + b2)"), &b0_minus_b1_plus_b2, )?; a0_minus_a1_plus_a2 }; // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2) let mut v3 = { let mut v3_cs = cs.ns(|| "v3"); let a1_double = self.c1.double(v3_cs.ns(|| "2a1"))?; let a2_quad = self .c2 .double(v3_cs.ns(|| "2a2"))? .double(v3_cs.ns(|| "4a2"))?; let mut a0_plus_2_a1_plus_4_a2 = self .c0 .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)? .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?; let b1_double = other.c1.double(); let b2_quad = other.c2.double().double(); let b0_plus_2_b1_plus_4_b2 = other.c0 + &b1_double + &b2_quad; a0_plus_2_a1_plus_4_a2.mul_by_constant_in_place( v3_cs.ns(|| "(a0 + 2a1 + 4a2)*(b0 + 2b1 + 4b2)"), &b0_plus_2_b1_plus_4_b2, )?; a0_plus_2_a1_plus_4_a2 }; // v4 = a(∞)b(∞) = a2 * b2 let v4 = self.c2.mul_by_constant(cs.ns(|| "v4"), &other.c2)?; let two = ::Fp::one().double(); let six = two.double() + &two; let mut two_and_six = [two, six]; algebra::fields::batch_inversion(&mut two_and_six); let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]); let mut half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?; let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?; let mut one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_6"), &six_inverse)?; let one_sixth_v3 = v3.mul_by_fp_constant_in_place(cs.ns(|| "v3_by_6"), &six_inverse)?; let two_v4 = v4.double(cs.ns(|| "2v4"))?; // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) let c0 = { let mut c0_cs = cs.ns(|| "c0"); // No constraints, only get a linear combination back. let mut inner = half_v0 .sub(c0_cs.ns(|| "sub1"), &half_v1)? .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)? .add(c0_cs.ns(|| "add3"), &one_sixth_v3)? .sub(c0_cs.ns(|| "sub4"), &two_v4)?; let non_residue_times_inner = inner.mul_by_constant_in_place(&mut c0_cs, &P::NONRESIDUE)?; v0.add(c0_cs.ns(|| "add5"), non_residue_times_inner)? }; // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 let c1 = { let mut c1_cs = cs.ns(|| "c1"); let one_third_v2 = one_sixth_v2.double_in_place(c1_cs.ns(|| "double1"))?; let non_residue_v4 = v4.mul_by_constant(c1_cs.ns(|| "mul_by_const1"), &P::NONRESIDUE)?; half_v0 .negate_in_place(c1_cs.ns(|| "neg1"))? .add(c1_cs.ns(|| "add1"), &v1)? .sub(c1_cs.ns(|| "sub2"), one_third_v2)? .sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)? .add(c1_cs.ns(|| "add4"), &two_v4)? .add(c1_cs.ns(|| "add5"), &non_residue_v4)? }; // -v0 + (1/2)v1 + (1/2)v2 −v4 let c2 = { let mut c2_cs = cs.ns(|| "c2"); let half_v2 = v2.mul_by_fp_constant_in_place(c2_cs.ns(|| "half_v2"), &two_inverse)?; half_v1 .add(c2_cs.ns(|| "add1"), half_v2)? .sub(c2_cs.ns(|| "sub2"), &v4)? .sub(c2_cs.ns(|| "sub3"), &v0)? }; Ok(Self::new(c0, c1, c2)) } fn frobenius_map>( &self, cs: CS, power: usize, ) -> Result { let mut result = self.clone(); result.frobenius_map_in_place(cs, power)?; Ok(result) } fn frobenius_map_in_place>( &mut self, mut cs: CS, power: usize, ) -> Result<&mut Self, SynthesisError> { self.c0.frobenius_map_in_place(&mut cs.ns(|| "c0"), power)?; self.c1.frobenius_map_in_place(&mut cs.ns(|| "c1"), power)?; self.c2.frobenius_map_in_place(&mut cs.ns(|| "c2"), power)?; self.c1.mul_by_constant_in_place( cs.ns(|| "c1_power"), &P::FROBENIUS_COEFF_FP6_C1[power % 6], )?; self.c2.mul_by_constant_in_place( cs.ns(|| "c2_power"), &P::FROBENIUS_COEFF_FP6_C2[power % 6], )?; Ok(self) } fn cost_of_mul() -> usize { 5 * Fp2Gadget::::cost_of_mul() } fn cost_of_inv() -> usize { Self::cost_of_mul() + >::cost() } } impl PartialEq for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { fn eq(&self, other: &Self) -> bool { self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2 } } impl Eq for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { } impl EqGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { } impl ConditionalEqGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { #[inline] fn conditional_enforce_equal>( &self, mut cs: CS, other: &Self, condition: &Boolean, ) -> Result<(), SynthesisError> { self.c0 .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?; self.c1 .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?; self.c2 .conditional_enforce_equal(&mut cs.ns(|| "c2"), &other.c2, condition)?; Ok(()) } fn cost() -> usize { 3 * as ConditionalEqGadget>::cost() } } impl NEqGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { #[inline] fn enforce_not_equal>( &self, mut cs: CS, other: &Self, ) -> Result<(), SynthesisError> { self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?; self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?; self.c2.enforce_not_equal(&mut cs.ns(|| "c2"), &other.c2)?; Ok(()) } fn cost() -> usize { 3 * as NEqGadget>::cost() } } impl ToBitsGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { fn to_bits>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?; let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?; let mut c2 = self.c2.to_bits(cs.ns(|| "c2"))?; c0.append(&mut c1); c0.append(&mut c2); Ok(c0) } fn to_non_unique_bits>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; let mut c2 = self.c2.to_non_unique_bits(cs.ns(|| "c2"))?; c0.append(&mut c1); c0.append(&mut c2); Ok(c0) } } impl ToBytesGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { fn to_bytes>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?; let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?; let mut c2 = self.c2.to_bytes(cs.ns(|| "c2"))?; c0.append(&mut c1); c0.append(&mut c2); Ok(c0) } fn to_non_unique_bytes>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; let mut c2 = self.c2.to_non_unique_bytes(cs.ns(|| "c2"))?; c0.append(&mut c1); c0.append(&mut c2); Ok(c0) } } impl Clone for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { fn clone(&self) -> Self { Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone()) } } impl CondSelectGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { #[inline] fn conditionally_select>( mut cs: CS, cond: &Boolean, true_value: &Self, false_value: &Self, ) -> Result { let c0 = Fp2Gadget::::conditionally_select( &mut cs.ns(|| "c0"), cond, &true_value.c0, &false_value.c0, )?; let c1 = Fp2Gadget::::conditionally_select( &mut cs.ns(|| "c1"), cond, &true_value.c1, &false_value.c1, )?; let c2 = Fp2Gadget::::conditionally_select( &mut cs.ns(|| "c2"), cond, &true_value.c2, &false_value.c2, )?; Ok(Self::new(c0, c1, c2)) } fn cost() -> usize { 3 * as CondSelectGadget>::cost() } } impl TwoBitLookupGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { type TableConstant = Fp6

; fn two_bit_lookup>( mut cs: CS, b: &[Boolean], c: &[Self::TableConstant], ) -> Result { let c0s = c.iter().map(|f| f.c0).collect::>(); let c1s = c.iter().map(|f| f.c1).collect::>(); let c2s = c.iter().map(|f| f.c2).collect::>(); let c0 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; let c1 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; let c2 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?; Ok(Self::new(c0, c1, c2)) } fn cost() -> usize { 3 * as TwoBitLookupGadget>::cost() } } impl ThreeBitCondNegLookupGadget for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { type TableConstant = Fp6

; fn three_bit_cond_neg_lookup>( mut cs: CS, b: &[Boolean], b0b1: &Boolean, c: &[Self::TableConstant], ) -> Result { let c0s = c.iter().map(|f| f.c0).collect::>(); let c1s = c.iter().map(|f| f.c1).collect::>(); let c2s = c.iter().map(|f| f.c2).collect::>(); let c0 = Fp2Gadget::::three_bit_cond_neg_lookup( cs.ns(|| "Lookup c0"), b, b0b1, &c0s, )?; let c1 = Fp2Gadget::::three_bit_cond_neg_lookup( cs.ns(|| "Lookup c1"), b, b0b1, &c1s, )?; let c2 = Fp2Gadget::::three_bit_cond_neg_lookup( cs.ns(|| "Lookup c2"), b, b0b1, &c2s, )?; Ok(Self::new(c0, c1, c2)) } fn cost() -> usize { 3 * as ThreeBitCondNegLookupGadget>::cost() } } impl AllocGadget, ConstraintF> for Fp6Gadget where P: Fp6Parameters, P::Fp2Params: Fp2Parameters, { #[inline] fn alloc>( mut cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let (c0, c1, c2) = match value_gen() { Ok(fe) => { let fe = *fe.borrow(); (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2)) }, _ => ( Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), ), }; let c0 = Fp2Gadget::::alloc(&mut cs.ns(|| "c0"), || c0)?; let c1 = Fp2Gadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; let c2 = Fp2Gadget::::alloc(&mut cs.ns(|| "c2"), || c2)?; Ok(Self::new(c0, c1, c2)) } #[inline] fn alloc_input>( mut cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let (c0, c1, c2) = match value_gen() { Ok(fe) => { let fe = *fe.borrow(); (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2)) }, _ => ( Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), Err(SynthesisError::AssignmentMissing), ), }; let c0 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; let c1 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; let c2 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c2"), || c2)?; Ok(Self::new(c0, c1, c2)) } }