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