6#include "barretenberg/ecc/curves/bn254/pairing.hpp"
8namespace barretenberg::pairing {
9constexpr fq two_inv = fq(2).invert();
10inline constexpr g2::element mul_by_q(
const g2::element& a)
13 fq2 T0 = a.x.frobenius_map();
14 fq2 T1 = a.y.frobenius_map();
16 fq2::twist_mul_by_q_x() * T0,
17 fq2::twist_mul_by_q_y() * T1,
21constexpr void doubling_step_for_flipped_miller_loop(g2::element& current, fq12::ell_coeffs& ell)
23 fq2 a = current.x.mul_by_fq(two_inv);
26 fq2 b = current.y.sqr();
27 fq2 c = current.z.sqr();
30 fq2 e = d * fq2::twist_coeff_b();
35 g = g.mul_by_fq(two_inv);
36 fq2 h = current.y + current.z;
41 fq2 j = current.x.sqr();
53 ell.o = fq6::mul_by_non_residue(i);
60constexpr void mixed_addition_step_for_flipped_miller_loop(
const g2::element& base,
62 fq12::ell_coeffs& line)
91 line.o = fq6::mul_by_non_residue(h);
97constexpr void precompute_miller_lines(
const g2::element& Q, miller_lines& lines)
99 g2::element Q_neg{ Q.x, -Q.y, fq2::one() };
100 g2::element work_point = Q;
103 for (
unsigned char loop_bit : loop_bits) {
104 doubling_step_for_flipped_miller_loop(work_point, lines.lines[it]);
107 mixed_addition_step_for_flipped_miller_loop(Q, work_point, lines.lines[it]);
109 }
else if (loop_bit == 3) {
110 mixed_addition_step_for_flipped_miller_loop(Q_neg, work_point, lines.lines[it]);
115 g2::element Q1 = mul_by_q(Q);
116 g2::element Q2 = mul_by_q(Q1);
118 mixed_addition_step_for_flipped_miller_loop(Q1, work_point, lines.lines[it]);
120 mixed_addition_step_for_flipped_miller_loop(Q2, work_point, lines.lines[it]);
123constexpr fq12 miller_loop(g1::element& P, miller_lines& lines)
125 fq12 work_scalar = fq12::one();
128 fq12::ell_coeffs work_line;
130 for (
unsigned char loop_bit : loop_bits) {
131 work_scalar = work_scalar.sqr();
133 work_line.o = lines.lines[it].o;
134 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
135 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
136 work_scalar.self_sparse_mul(work_line);
140 work_line.o = lines.lines[it].o;
141 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
142 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
143 work_scalar.self_sparse_mul(work_line);
148 work_line.o = lines.lines[it].o;
149 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
150 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
151 work_scalar.self_sparse_mul(work_line);
153 work_line.o = lines.lines[it].o;
154 work_line.vw = lines.lines[it].vw.mul_by_fq(P.y);
155 work_line.vv = lines.lines[it].vv.mul_by_fq(P.x);
156 work_scalar.self_sparse_mul(work_line);
161constexpr fq12 miller_loop_batch(
const g1::element* points,
const miller_lines* lines,
size_t num_pairs)
163 fq12 work_scalar = fq12::one();
166 fq12::ell_coeffs work_line;
168 for (
unsigned char loop_bit : loop_bits) {
169 work_scalar = work_scalar.sqr();
170 for (
size_t j = 0; j < num_pairs; ++j) {
171 work_line.o = lines[j].lines[it].o;
172 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
173 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
174 work_scalar.self_sparse_mul(work_line);
178 for (
size_t j = 0; j < num_pairs; ++j) {
179 work_line.o = lines[j].lines[it].o;
180 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
181 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
182 work_scalar.self_sparse_mul(work_line);
188 for (
size_t j = 0; j < num_pairs; ++j) {
189 work_line.o = lines[j].lines[it].o;
190 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
191 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
192 work_scalar.self_sparse_mul(work_line);
195 for (
size_t j = 0; j < num_pairs; ++j) {
196 work_line.o = lines[j].lines[it].o;
197 work_line.vw = lines[j].lines[it].vw.mul_by_fq(points[j].y);
198 work_line.vv = lines[j].lines[it].vv.mul_by_fq(points[j].x);
199 work_scalar.self_sparse_mul(work_line);
205constexpr fq12 final_exponentiation_easy_part(
const fq12& elt)
207 fq12 a{ elt.c0, -elt.c1 };
209 return a * a.frobenius_map_two();
212constexpr fq12 final_exponentiation_exp_by_neg_z(
const fq12& elt)
216 for (
bool neg_z_loop_bit : neg_z_loop_bits) {
217 r = r.cyclotomic_squared();
218 if (neg_z_loop_bit) {
222 return r.unitary_inverse();
225constexpr fq12 final_exponentiation_tricky_part(
const fq12& elt)
227 fq12 A = final_exponentiation_exp_by_neg_z(elt);
228 fq12 B = A.cyclotomic_squared();
229 fq12 C = B.cyclotomic_squared();
231 fq12 E = final_exponentiation_exp_by_neg_z(D);
232 fq12 F = E.cyclotomic_squared();
233 fq12 G = final_exponentiation_exp_by_neg_z(F);
234 fq12 H = D.unitary_inverse();
235 fq12 I = G.unitary_inverse();
241 fq12 O = L.frobenius_map_one();
243 fq12 Q = K.frobenius_map_two();
245 fq12 S = elt.unitary_inverse();
247 fq12 U = T.frobenius_map_three();
252constexpr fq12 reduced_ate_pairing(
const g1::affine_element& P_affine,
const g2::affine_element& Q_affine)
254 g1::element P(P_affine);
255 g2::element Q(Q_affine);
258 precompute_miller_lines(Q, lines);
260 fq12 result = miller_loop(P, lines);
261 result = final_exponentiation_easy_part(result);
262 result = final_exponentiation_tricky_part(result);
266fq12 reduced_ate_pairing_batch_precomputed(
const g1::affine_element* P_affines,
267 const miller_lines* lines,
268 const size_t num_points)
270 std::vector<g1::element> P(num_points);
271 for (
size_t i = 0; i < num_points; ++i) {
272 P[i] = g1::element(P_affines[i]);
274 fq12 result = miller_loop_batch(&P[0], &lines[0], num_points);
275 result = final_exponentiation_easy_part(result);
276 result = final_exponentiation_tricky_part(result);
280fq12 reduced_ate_pairing_batch(
const g1::affine_element* P_affines,
281 const g2::affine_element* Q_affines,
282 const size_t num_points)
284 std::vector<g1::element> P(num_points);
285 std::vector<g2::element> Q(num_points);
286 std::vector<miller_lines> lines(num_points);
288 for (
size_t i = 0; i < num_points; ++i) {
289 P[i] = g1::element(P_affines[i]);
290 Q[i] = g2::element(Q_affines[i]);
292 precompute_miller_lines(Q[i], lines[i]);
295 fq12 result = miller_loop_batch(&P[0], &lines[0], num_points);
296 result = final_exponentiation_easy_part(result);
297 result = final_exponentiation_tricky_part(result);