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