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