arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/src/fields/fp12.rs
src/fields/fp12.rsRaw
  1use crate::fields::{fp2::Fp2Var, fp6_3over2::Fp6Var, quadratic_extension::*, FieldVar};
  2use ark_ff::{
  3    fields::{fp12_2over3over2::*, Field},
  4    fp6_3over2::Fp6Config,
  5    QuadExtConfig,
  6};
  7use ark_relations::r1cs::SynthesisError;
  8
  9/// A degree-12 extension field constructed as the tower of a
 10/// quadratic extension over a cubic extension over a quadratic extension field.
 11/// This is the R1CS equivalent of `ark_ff::fp12_2over3over2::Fp12<P>`.
 12pub type Fp12Var<P> = QuadExtVar<Fp6Var<<P as Fp12Config>::Fp6Config>, Fp12ConfigWrapper<P>>;
 13
 14type Fp2Config<P> = <<P as Fp12Config>::Fp6Config as Fp6Config>::Fp2Config;
 15
 16impl<P: Fp12Config> QuadExtVarConfig<Fp6Var<P::Fp6Config>> for Fp12ConfigWrapper<P> {
 17    fn mul_base_field_var_by_frob_coeff(fe: &mut Fp6Var<P::Fp6Config>, power: usize) {
 18        fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
 19        fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
 20        fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
 21    }
 22}
 23
 24impl<P: Fp12Config> Fp12Var<P> {
 25    /// Multiplies by a sparse element of the form `(c0 = (c0, c1, 0), c1 = (0,
 26    /// d1, 0))`.
 27    #[inline]
 28    pub fn mul_by_014(
 29        &self,
 30        c0: &Fp2Var<Fp2Config<P>>,
 31        c1: &Fp2Var<Fp2Config<P>>,
 32        d1: &Fp2Var<Fp2Config<P>>,
 33    ) -> Result<Self, SynthesisError> {
 34        let v0 = self.c0.mul_by_c0_c1_0(&c0, &c1)?;
 35        let v1 = self.c1.mul_by_0_c1_0(&d1)?;
 36        let new_c0 = Self::mul_base_field_by_nonresidue(&v1)? + &v0;
 37
 38        let new_c1 = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &(c1 + d1))? - &v0 - &v1;
 39        Ok(Self::new(new_c0, new_c1))
 40    }
 41
 42    /// Multiplies by a sparse element of the form `(c0 = (c0, 0, 0), c1 = (d0,
 43    /// d1, 0))`.
 44    #[inline]
 45    pub fn mul_by_034(
 46        &self,
 47        c0: &Fp2Var<Fp2Config<P>>,
 48        d0: &Fp2Var<Fp2Config<P>>,
 49        d1: &Fp2Var<Fp2Config<P>>,
 50    ) -> Result<Self, SynthesisError> {
 51        let a0 = &self.c0.c0 * c0;
 52        let a1 = &self.c0.c1 * c0;
 53        let a2 = &self.c0.c2 * c0;
 54        let a = Fp6Var::new(a0, a1, a2);
 55        let b = self.c1.mul_by_c0_c1_0(&d0, &d1)?;
 56
 57        let c0 = c0 + d0;
 58        let c1 = d1;
 59        let e = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &c1)?;
 60        let new_c1 = e - (&a + &b);
 61        let new_c0 = Self::mul_base_field_by_nonresidue(&b)? + &a;
 62
 63        Ok(Self::new(new_c0, new_c1))
 64    }
 65
 66    /// Squares `self` when `self` is in the cyclotomic subgroup.
 67    pub fn cyclotomic_square(&self) -> Result<Self, SynthesisError> {
 68        if characteristic_square_mod_6_is_one(Fp12::<P>::characteristic()) {
 69            let fp2_nr = <P::Fp6Config as Fp6Config>::NONRESIDUE;
 70
 71            let z0 = &self.c0.c0;
 72            let z4 = &self.c0.c1;
 73            let z3 = &self.c0.c2;
 74            let z2 = &self.c1.c0;
 75            let z1 = &self.c1.c1;
 76            let z5 = &self.c1.c2;
 77
 78            // t0 + t1*y = (z0 + z1*y)^2 = a^2
 79            let tmp = z0 * z1;
 80            let t0 = {
 81                let tmp1 = z0 + z1;
 82                let tmp2 = z1 * fp2_nr + z0;
 83                let tmp4 = &tmp * fp2_nr + &tmp;
 84                tmp1 * tmp2 - tmp4
 85            };
 86            let t1 = tmp.double()?;
 87
 88            // t2 + t3*y = (z2 + z3*y)^2 = b^2
 89            let tmp = z2 * z3;
 90            let t2 = {
 91                // (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr);
 92                let tmp1 = z2 + z3;
 93                let tmp2 = z3 * fp2_nr + z2;
 94                let tmp4 = &tmp * fp2_nr + &tmp;
 95                tmp1 * tmp2 - tmp4
 96            };
 97            let t3 = tmp.double()?;
 98
 99            // t4 + t5*y = (z4 + z5*y)^2 = c^2
100            let tmp = z4 * z5;
101            let t4 = {
102                // (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr);
103                let tmp1 = z4 + z5;
104                let tmp2 = (z5 * fp2_nr) + z4;
105                let tmp4 = (&tmp * fp2_nr) + &tmp;
106                (tmp1 * tmp2) - tmp4
107            };
108            let t5 = tmp.double()?;
109
110            // for A
111
112            // z0 = 3 * t0 - 2 * z0
113            let c0_c0 = (&t0 - z0).double()? + &t0;
114
115            // z1 = 3 * t1 + 2 * z1
116            let c1_c1 = (&t1 + z1).double()? + &t1;
117
118            // for B
119
120            // z2 = 3 * (xi * t5) + 2 * z2
121            let c1_c0 = {
122                let tmp = &t5 * fp2_nr;
123                (z2 + &tmp).double()? + &tmp
124            };
125
126            // z3 = 3 * t4 - 2 * z3
127            let c0_c2 = (&t4 - z3).double()? + &t4;
128
129            // for C
130
131            // z4 = 3 * t2 - 2 * z4
132            let c0_c1 = (&t2 - z4).double()? + &t2;
133
134            // z5 = 3 * t3 + 2 * z5
135            let c1_c2 = (&t3 + z5).double()? + &t3;
136            let c0 = Fp6Var::new(c0_c0, c0_c1, c0_c2);
137            let c1 = Fp6Var::new(c1_c0, c1_c1, c1_c2);
138
139            Ok(Self::new(c0, c1))
140        } else {
141            self.square()
142        }
143    }
144
145    /// Like `Self::cyclotomic_exp`, but additionally uses cyclotomic squaring.
146    pub fn optimized_cyclotomic_exp(
147        &self,
148        exponent: impl AsRef<[u64]>,
149    ) -> Result<Self, SynthesisError> {
150        use ark_ff::biginteger::arithmetic::find_naf;
151        let mut res = Self::one();
152        let self_inverse = self.unitary_inverse()?;
153
154        let mut found_nonzero = false;
155        let naf = find_naf(exponent.as_ref());
156
157        for &value in naf.iter().rev() {
158            if found_nonzero {
159                res = res.cyclotomic_square()?;
160            }
161
162            if value != 0 {
163                found_nonzero = true;
164
165                if value > 0 {
166                    res *= self;
167                } else {
168                    res *= &self_inverse;
169                }
170            }
171        }
172
173        Ok(res)
174    }
175}