86 const bool_ct x_coordinates_match = other.
x == x;
87 const bool_ct y_coordinates_match = (y == other.
y);
88 const bool_ct infinity_predicate = (x_coordinates_match && !y_coordinates_match);
89 const bool_ct double_predicate = (x_coordinates_match && y_coordinates_match);
90 const bool_ct lhs_infinity = is_point_at_infinity();
94 const Fq add_lambda_numerator = other.
y - y;
96 const Fq dbl_lambda_numerator = xx + xx + xx;
97 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
99 const Fq add_lambda_denominator = other.
x - x;
100 const Fq dbl_lambda_denominator = y + y;
101 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
105 Fq safe_edgecase_denominator =
Fq(1);
106 lambda_denominator = Fq::conditional_assign(
107 lhs_infinity || rhs_infinity || infinity_predicate, safe_edgecase_denominator, lambda_denominator);
108 const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator);
110 const Fq x3 = lambda.sqradd({ -other.
x, -x });
111 const Fq y3 = lambda.madd(x - x3, { -y });
115 result.
x = Fq::conditional_assign(lhs_infinity, other.
x, result.
x);
116 result.
y = Fq::conditional_assign(lhs_infinity, other.
y, result.
y);
118 result.
x = Fq::conditional_assign(rhs_infinity, x, result.
x);
119 result.
y = Fq::conditional_assign(rhs_infinity, y, result.
y);
125 bool_ct result_is_infinity = infinity_predicate && (!lhs_infinity && !rhs_infinity);
129 result_is_infinity = result_is_infinity || (lhs_infinity && rhs_infinity);
164 const bool_ct x_coordinates_match = other.
x == x;
165 const bool_ct y_coordinates_match = (y == other.
y);
166 const bool_ct infinity_predicate = (x_coordinates_match && y_coordinates_match);
167 const bool_ct double_predicate = (x_coordinates_match && !y_coordinates_match);
168 const bool_ct lhs_infinity = is_point_at_infinity();
172 const Fq add_lambda_numerator = -other.
y - y;
174 const Fq dbl_lambda_numerator = xx + xx + xx;
175 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
177 const Fq add_lambda_denominator = other.
x - x;
178 const Fq dbl_lambda_denominator = y + y;
179 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
183 Fq safe_edgecase_denominator =
Fq(1);
184 lambda_denominator = Fq::conditional_assign(
185 lhs_infinity || rhs_infinity || infinity_predicate, safe_edgecase_denominator, lambda_denominator);
186 const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator);
188 const Fq x3 = lambda.sqradd({ -other.
x, -x });
189 const Fq y3 = lambda.madd(x - x3, { -y });
193 result.
x = Fq::conditional_assign(lhs_infinity, other.
x, result.
x);
194 result.
y = Fq::conditional_assign(lhs_infinity, -other.
y, result.
y);
196 result.
x = Fq::conditional_assign(rhs_infinity, x, result.
x);
197 result.
y = Fq::conditional_assign(rhs_infinity, y, result.
y);
203 bool_ct result_is_infinity = infinity_predicate && (!lhs_infinity && !rhs_infinity);
207 result_is_infinity = result_is_infinity || (lhs_infinity && rhs_infinity);
278 if constexpr (G::has_a) {
280 Fq neg_lambda = Fq::msub_div({ x }, { (two_x + x) }, (y + y), {
a });
281 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
282 Fq y_3 = neg_lambda.madd(x_3 - x, { -y });
285 Fq neg_lambda = Fq::msub_div({ x }, { (two_x + x) }, (y + y), {});
286 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
287 Fq y_3 = neg_lambda.madd(x_3 - x, { -y });
507 const Fq two_x = x + x;
510 if constexpr (G::has_a) {
512 minus_lambda_dbl = Fq::msub_div({ x }, { (two_x + x) }, (y + y), {
a });
513 x_1 = minus_lambda_dbl.sqradd({ -(two_x) });
515 minus_lambda_dbl = Fq::msub_div({ x }, { (two_x + x) }, (y + y), {});
516 x_1 = minus_lambda_dbl.sqradd({ -(two_x) });
520 to_add[0].x.assert_is_not_equal(x_1);
522 const Fq x_minus_x_1 = x - x_1;
524 const Fq lambda_1 = Fq::msub_div({ minus_lambda_dbl }, { x_minus_x_1 }, (x_1 - to_add[0].x), { to_add[0].y, y });
526 const Fq x_3 = lambda_1.sqradd({ -to_add[0].x, -x_1 });
528 const Fq half_minus_lambda_2_minus_lambda_1 =
529 Fq::msub_div({ minus_lambda_dbl }, { x_minus_x_1 }, (x_3 - x_1), { y });
531 const Fq minus_lambda_2_minus_lambda_1 = half_minus_lambda_2_minus_lambda_1 + half_minus_lambda_2_minus_lambda_1;
532 const Fq minus_lambda_2 = minus_lambda_2_minus_lambda_1 + lambda_1;
534 const Fq x_4 = minus_lambda_2.sqradd({ -x_1, -x_3 });
536 const Fq x_4_sub_x_1 = x_4 - x_1;
538 if (to_add.size() == 1) {
539 const Fq y_4 = Fq::dual_madd(minus_lambda_2, x_4_sub_x_1, minus_lambda_dbl, x_minus_x_1, { y });
542 to_add[1].x.assert_is_not_equal(to_add[0].x);
544 Fq minus_lambda_3 = Fq::msub_div(
545 { minus_lambda_dbl, minus_lambda_2 }, { x_minus_x_1, x_4_sub_x_1 }, (x_4 - to_add[1].x), { y, -(to_add[1].y) });
548 const Fq x_5 = minus_lambda_3.sqradd({ -x_4, -to_add[1].x });
550 if (to_add.size() == 2) {
552 const Fq y_5 = minus_lambda_3.madd(x_5 - to_add[1].x, { -to_add[1].y });
557 Fq minus_lambda_prev = minus_lambda_3;
559 for (
size_t i = 2; i < to_add.size(); ++i) {
561 to_add[i].x.assert_is_not_equal(to_add[i - 1].x);
564 const Fq minus_lambda = Fq::msub_div({ minus_lambda_prev },
565 { to_add[i - 1].x - x_prev },
566 (to_add[i].x - x_prev),
567 { to_add[i - 1].y, to_add[i].y });
569 const Fq x_out = minus_lambda.sqradd({ -x_prev, -to_add[i].x });
572 minus_lambda_prev = minus_lambda;
574 const Fq y_out = minus_lambda_prev.madd(x_prev - to_add[to_add.size() - 1].x, { -to_add[to_add.size() - 1].y });
606 bool is_negative =
false;
611 for (
size_t i = 0; i < add.size(); ++i) {
612 previous_x.assert_is_not_equal(add[i].x3_prev);
615 bool negate_add_y = (i > 0) && !previous_y.is_negative;
621 lambda1_add.emplace_back(-y);
623 lambda1_left = previous_y.mul_left;
624 lambda1_right = previous_y.mul_right;
625 lambda1_add = previous_y.add;
628 if (!add[i].is_element) {
629 lambda1_left.emplace_back(add[i].lambda_prev);
630 lambda1_right.emplace_back(negate_add_y ? add[i].x3_prev - add[i].x1_prev
631 : add[i].x1_prev - add[i].x3_prev);
632 lambda1_add.emplace_back(negate_add_y ? add[i].y1_prev : -add[i].y1_prev);
634 lambda1_add.emplace_back(negate_add_y ? -add[i].y3_prev : add[i].y3_prev);
644 if (!add[i].is_element || i > 0) {
645 bool flip_lambda1_denominator = !negate_add_y;
646 Fq denominator = flip_lambda1_denominator ? previous_x - add[i].x3_prev : add[i].x3_prev - previous_x;
647 lambda1 = Fq::msub_div(lambda1_left, lambda1_right, denominator, lambda1_add);
649 lambda1 = Fq::div_without_denominator_check({ add[i].y3_prev - y }, (add[i].x3_prev - x));
652 Fq x_3 = lambda1.madd(lambda1, { -add[i].x3_prev, -previous_x });
660 lambda2 = Fq::div_without_denominator_check({ y + y }, (previous_x - x_3)) - lambda1;
662 Fq l2_denominator = previous_y.is_negative ? previous_x - x_3 : x_3 - previous_x;
664 Fq::msub_div(previous_y.mul_left, previous_y.mul_right, l2_denominator, previous_y.
add);
665 partial_lambda2 = partial_lambda2 + partial_lambda2;
666 lambda2 = partial_lambda2 - lambda1;
669 Fq x_4 = lambda2.sqradd({ -x_3, -previous_x });
675 bool num_points_even = ((add.size() & 0x01UL) == 0);
676 y_4.add.emplace_back(num_points_even ? y : -y);
677 y_4.mul_left.emplace_back(lambda2);
678 y_4.mul_right.emplace_back(num_points_even ? x_4 - previous_x : previous_x - x_4);
679 y_4.is_negative = num_points_even;
681 y_4.is_negative = !previous_y.is_negative;
682 y_4.mul_left.emplace_back(lambda2);
683 y_4.mul_right.emplace_back(previous_y.is_negative ? previous_x - x_4 : x_4 - previous_x);
696 Fq x_out = previous_x;
698 ASSERT(!previous_y.is_negative);
700 Fq y_out = Fq::mult_madd(previous_y.mul_left, previous_y.mul_right, previous_y.add);
772 const std::vector<Fr>& _scalars,
773 const size_t max_num_bits,
774 const bool with_edgecases)
776 auto [points, scalars] = handle_points_at_infinity(_points, _scalars);
780 for (
size_t i = 0; i < _points.size(); i++) {
781 tag =
OriginTag(tag,
OriginTag(_points[i].get_origin_tag(), _scalars[i].get_origin_tag()));
783 for (
size_t i = 0; i < scalars.size(); i++) {
791 points[i].set_origin_tag(empty_tag);
792 scalars[i].set_origin_tag(empty_tag);
796 if (with_edgecases) {
797 std::tie(points, scalars) = mask_points(points, scalars);
799 const size_t num_points = points.size();
803 const size_t num_rounds = (max_num_bits == 0) ?
Fr::modulus.
get_msb() + 1 : max_num_bits;
806 for (
size_t i = 0; i < num_points; ++i) {
807 naf_entries.emplace_back(compute_naf(scalars[i], max_num_bits));
809 const auto offset_generators = compute_offset_generators(num_rounds);
813 constexpr size_t num_rounds_per_iteration = 4;
814 size_t num_iterations = num_rounds / num_rounds_per_iteration;
815 num_iterations += ((num_iterations * num_rounds_per_iteration) == num_rounds) ? 0 : 1;
816 const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration);
817 for (
size_t i = 0; i < num_iterations; ++i) {
821 const size_t inner_num_rounds =
822 (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration;
823 for (
size_t j = 0; j < inner_num_rounds; ++j) {
824 for (
size_t k = 0; k < num_points; ++k) {
825 nafs[k] = (naf_entries[k][i * num_rounds_per_iteration + j + 1]);
831 for (
size_t i = 0; i < num_points; ++i) {
832 element skew = accumulator - points[i];
833 Fq out_x = accumulator.
x.conditional_select(skew.
x, naf_entries[i][num_rounds]);
834 Fq out_y = accumulator.
y.conditional_select(skew.
y, naf_entries[i][num_rounds]);
835 accumulator =
element(out_x, out_y);
837 accumulator = accumulator - offset_generators.second;
890 bool_ct is_point_at_infinity = this->is_point_at_infinity();
892 const size_t num_rounds = (max_num_bits == 0) ?
Fr::modulus.
get_msb() + 1 : max_num_bits;
895 if (max_num_bits != 0) {
897 result = element::bn254_endo_batch_mul({}, {}, { *
this }, { scalar }, num_rounds);
900 result = element::bn254_endo_batch_mul({ *
this }, { scalar }, {}, {}, num_rounds);
904 result.
x = Fq::conditional_assign(is_point_at_infinity, x, result.
x);
905 result.
y = Fq::conditional_assign(is_point_at_infinity, y, result.
y);