1use std::marker::PhantomData;
2
3use crate::ff::PrimeField as _;
4use crate::impl_field_ext_chip_common;
5
6use super::{
7 vector::{FieldVector, FieldVectorChip},
8 FieldChip, FieldExtConstructor, PrimeFieldChip,
9};
10
11use halo2_base::{
12 utils::{modulus, BigPrimeField},
13 AssignedValue, Context,
14};
15use num_bigint::BigUint;
16
17#[derive(Clone, Copy, Debug)]
24pub struct Fp12Chip<'a, F: BigPrimeField, FpChip: FieldChip<F>, Fp12, const XI_0: i64>(
25 pub FieldVectorChip<'a, F, FpChip>,
26 PhantomData<Fp12>,
27);
28
29impl<'a, F, FpChip, Fp12, const XI_0: i64> Fp12Chip<'a, F, FpChip, Fp12, XI_0>
30where
31 F: BigPrimeField,
32 FpChip: PrimeFieldChip<F>,
33 FpChip::FieldType: BigPrimeField,
34 Fp12: crate::ff::Field,
35{
36 pub fn new(fp_chip: &'a FpChip) -> Self {
38 assert_eq!(
39 modulus::<FpChip::FieldType>() % 4usize,
40 BigUint::from(3u64),
41 "p must be 3 (mod 4) for the polynomial u^2 + 1 to be irreducible"
42 );
43 Self(FieldVectorChip::new(fp_chip), PhantomData)
44 }
45
46 pub fn fp_chip(&self) -> &FpChip {
47 self.0.fp_chip
48 }
49
50 pub fn fp2_mul_no_carry(
51 &self,
52 ctx: &mut Context<F>,
53 fp12_pt: FieldVector<FpChip::UnsafeFieldPoint>,
54 fp2_pt: FieldVector<FpChip::UnsafeFieldPoint>,
55 ) -> FieldVector<FpChip::UnsafeFieldPoint> {
56 let fp12_pt = fp12_pt.0;
57 let fp2_pt = fp2_pt.0;
58 assert_eq!(fp12_pt.len(), 12);
59 assert_eq!(fp2_pt.len(), 2);
60
61 let fp_chip = self.fp_chip();
62 let mut out_coeffs = Vec::with_capacity(12);
63 for i in 0..6 {
64 let coeff1 = fp_chip.mul_no_carry(ctx, fp12_pt[i].clone(), fp2_pt[0].clone());
65 let coeff2 = fp_chip.mul_no_carry(ctx, fp12_pt[i + 6].clone(), fp2_pt[1].clone());
66 let coeff = fp_chip.sub_no_carry(ctx, coeff1, coeff2);
67 out_coeffs.push(coeff);
68 }
69 for i in 0..6 {
70 let coeff1 = fp_chip.mul_no_carry(ctx, fp12_pt[i + 6].clone(), fp2_pt[0].clone());
71 let coeff2 = fp_chip.mul_no_carry(ctx, fp12_pt[i].clone(), fp2_pt[1].clone());
72 let coeff = fp_chip.add_no_carry(ctx, coeff1, coeff2);
73 out_coeffs.push(coeff);
74 }
75 FieldVector(out_coeffs)
76 }
77
78 pub fn conjugate(
80 &self,
81 ctx: &mut Context<F>,
82 a: FieldVector<FpChip::FieldPoint>,
83 ) -> FieldVector<FpChip::FieldPoint> {
84 let a = a.0;
85 assert_eq!(a.len(), 12);
86
87 let coeffs = a
88 .into_iter()
89 .enumerate()
90 .map(|(i, c)| if i % 2 == 0 { c } else { self.fp_chip().negate(ctx, c) })
91 .collect();
92 FieldVector(coeffs)
93 }
94}
95
96pub fn mul_no_carry_w6<F: BigPrimeField, FC: FieldChip<F>, const XI_0: i64>(
101 fp_chip: &FC,
102 ctx: &mut Context<F>,
103 a: FieldVector<FC::UnsafeFieldPoint>,
104) -> FieldVector<FC::UnsafeFieldPoint> {
105 let [a0, a1]: [_; 2] = a.0.try_into().unwrap();
106 let a0_xi0 = fp_chip.scalar_mul_no_carry(ctx, a0.clone(), XI_0);
109 let out0_0_nocarry = fp_chip.sub_no_carry(ctx, a0_xi0, a1.clone());
110 let out0_1_nocarry = fp_chip.scalar_mul_and_add_no_carry(ctx, a1, a0, XI_0);
111 FieldVector(vec![out0_0_nocarry, out0_1_nocarry])
112}
113
114impl<F, FpChip, Fp12, const XI_0: i64> FieldChip<F> for Fp12Chip<'_, F, FpChip, Fp12, XI_0>
118where
119 F: BigPrimeField,
120 FpChip: PrimeFieldChip<F>,
121 FpChip::FieldType: BigPrimeField,
122 Fp12: crate::ff::Field + FieldExtConstructor<FpChip::FieldType, 12>,
123 FieldVector<FpChip::UnsafeFieldPoint>: From<FieldVector<FpChip::FieldPoint>>,
124 FieldVector<FpChip::FieldPoint>: From<FieldVector<FpChip::ReducedFieldPoint>>,
125{
126 const PRIME_FIELD_NUM_BITS: u32 = FpChip::FieldType::NUM_BITS;
127 type UnsafeFieldPoint = FieldVector<FpChip::UnsafeFieldPoint>;
128 type FieldPoint = FieldVector<FpChip::FieldPoint>;
129 type ReducedFieldPoint = FieldVector<FpChip::ReducedFieldPoint>;
130 type FieldType = Fp12;
131 type RangeChip = FpChip::RangeChip;
132
133 fn get_assigned_value(&self, x: &Self::UnsafeFieldPoint) -> Fp12 {
134 assert_eq!(x.0.len(), 12);
135 let values = x.0.iter().map(|v| self.fp_chip().get_assigned_value(v)).collect::<Vec<_>>();
136 Fp12::new(values.try_into().unwrap())
137 }
138
139 fn mul_no_carry(
141 &self,
142 ctx: &mut Context<F>,
143 a: impl Into<Self::UnsafeFieldPoint>,
144 b: impl Into<Self::UnsafeFieldPoint>,
145 ) -> Self::UnsafeFieldPoint {
146 let a = a.into().0;
147 let b = b.into().0;
148 assert_eq!(a.len(), 12);
149 assert_eq!(b.len(), 12);
150
151 let fp_chip = self.fp_chip();
152 let mut a0b0_coeffs: Vec<FpChip::UnsafeFieldPoint> = Vec::with_capacity(11);
155 let mut a0b1_coeffs: Vec<FpChip::UnsafeFieldPoint> = Vec::with_capacity(11);
156 let mut a1b0_coeffs: Vec<FpChip::UnsafeFieldPoint> = Vec::with_capacity(11);
157 let mut a1b1_coeffs: Vec<FpChip::UnsafeFieldPoint> = Vec::with_capacity(11);
158 for i in 0..6 {
159 for j in 0..6 {
160 let coeff00 = fp_chip.mul_no_carry(ctx, &a[i], &b[j]);
161 let coeff01 = fp_chip.mul_no_carry(ctx, &a[i], &b[j + 6]);
162 let coeff10 = fp_chip.mul_no_carry(ctx, &a[i + 6], &b[j]);
163 let coeff11 = fp_chip.mul_no_carry(ctx, &a[i + 6], &b[j + 6]);
164 if i + j < a0b0_coeffs.len() {
165 a0b0_coeffs[i + j] = fp_chip.add_no_carry(ctx, &a0b0_coeffs[i + j], coeff00);
166 a0b1_coeffs[i + j] = fp_chip.add_no_carry(ctx, &a0b1_coeffs[i + j], coeff01);
167 a1b0_coeffs[i + j] = fp_chip.add_no_carry(ctx, &a1b0_coeffs[i + j], coeff10);
168 a1b1_coeffs[i + j] = fp_chip.add_no_carry(ctx, &a1b1_coeffs[i + j], coeff11);
169 } else {
170 a0b0_coeffs.push(coeff00);
171 a0b1_coeffs.push(coeff01);
172 a1b0_coeffs.push(coeff10);
173 a1b1_coeffs.push(coeff11);
174 }
175 }
176 }
177
178 let mut a0b0_minus_a1b1 = Vec::with_capacity(11);
179 let mut a0b1_plus_a1b0 = Vec::with_capacity(11);
180 for i in 0..11 {
181 let a0b0_minus_a1b1_entry = fp_chip.sub_no_carry(ctx, &a0b0_coeffs[i], &a1b1_coeffs[i]);
182 let a0b1_plus_a1b0_entry = fp_chip.add_no_carry(ctx, &a0b1_coeffs[i], &a1b0_coeffs[i]);
183
184 a0b0_minus_a1b1.push(a0b0_minus_a1b1_entry);
185 a0b1_plus_a1b0.push(a0b1_plus_a1b0_entry);
186 }
187
188 let mut out_coeffs = Vec::with_capacity(12);
191 for i in 0..6 {
192 if i < 5 {
193 let mut coeff = fp_chip.scalar_mul_and_add_no_carry(
194 ctx,
195 &a0b0_minus_a1b1[i + 6],
196 &a0b0_minus_a1b1[i],
197 XI_0,
198 );
199 coeff = fp_chip.sub_no_carry(ctx, coeff, &a0b1_plus_a1b0[i + 6]);
200 out_coeffs.push(coeff);
201 } else {
202 out_coeffs.push(a0b0_minus_a1b1[i].clone());
203 }
204 }
205 for i in 0..6 {
206 if i < 5 {
207 let mut coeff =
208 fp_chip.add_no_carry(ctx, &a0b1_plus_a1b0[i], &a0b0_minus_a1b1[i + 6]);
209 coeff =
210 fp_chip.scalar_mul_and_add_no_carry(ctx, &a0b1_plus_a1b0[i + 6], coeff, XI_0);
211 out_coeffs.push(coeff);
212 } else {
213 out_coeffs.push(a0b1_plus_a1b0[i].clone());
214 }
215 }
216 FieldVector(out_coeffs)
217 }
218
219 impl_field_ext_chip_common!();
220}
221
222mod bn254 {
223 use crate::fields::FieldExtConstructor;
224 use crate::halo2_proofs::halo2curves::bn256::{Fq, Fq12, Fq2, Fq6};
225 impl FieldExtConstructor<Fq, 12> for Fq12 {
228 fn new(c: [Fq; 12]) -> Self {
229 Fq12 {
230 c0: Fq6 {
231 c0: Fq2 { c0: c[0], c1: c[6] },
232 c1: Fq2 { c0: c[2], c1: c[8] },
233 c2: Fq2 { c0: c[4], c1: c[10] },
234 },
235 c1: Fq6 {
236 c0: Fq2 { c0: c[1], c1: c[7] },
237 c1: Fq2 { c0: c[3], c1: c[9] },
238 c2: Fq2 { c0: c[5], c1: c[11] },
239 },
240 }
241 }
242
243 fn coeffs(&self) -> Vec<Fq> {
244 let x = self;
245 vec![
246 x.c0.c0.c0, x.c1.c0.c0, x.c0.c1.c0, x.c1.c1.c0, x.c0.c2.c0, x.c1.c2.c0, x.c0.c0.c1,
247 x.c1.c0.c1, x.c0.c1.c1, x.c1.c1.c1, x.c0.c2.c1, x.c1.c2.c1,
248 ]
249 }
250 }
251}