101 const bool_ct x_coordinates_match = other.
_x == _x;
102 const bool_ct y_coordinates_match = (_y == other.
_y);
103 const bool_ct infinity_predicate = (x_coordinates_match && !y_coordinates_match);
104 const bool_ct double_predicate = (x_coordinates_match && y_coordinates_match);
105 const bool_ct lhs_infinity = is_point_at_infinity();
107 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
114 const typename G::Fq y_value =
uint256_t(_y.get_value());
115 BB_ASSERT_EQ((y_value == 0),
false,
"Attempting to add a point with y = 0, not allowed.");
119 const Fq add_lambda_numerator = other.
_y - _y;
120 const Fq xx = _x * _x;
121 Fq dbl_lambda_numerator = xx + xx + xx;
122 if constexpr (G::has_a) {
126 dbl_lambda_numerator = dbl_lambda_numerator +
a;
128 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
130 const Fq add_lambda_denominator = other.
_x - _x;
131 const Fq dbl_lambda_denominator = _y + _y;
132 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
136 const bool_ct safe_denominator_needed = has_infinity_input || infinity_predicate;
137 lambda_denominator = Fq::conditional_assign(safe_denominator_needed,
Fq(1), lambda_denominator);
138 const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator);
141 const Fq x3 = lambda.sqradd({ -other.
_x, -_x });
142 const Fq y3 = lambda.madd(_x - x3, { -_y });
146 result.
_x = Fq::conditional_assign(lhs_infinity, other.
_x, result.
_x);
147 result.
_y = Fq::conditional_assign(lhs_infinity, other.
_y, result.
_y);
149 result.
_x = Fq::conditional_assign(rhs_infinity, _x, result.
_x);
150 result.
_y = Fq::conditional_assign(rhs_infinity, _y, result.
_y);
155 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
191 const bool_ct x_coordinates_match = other.
_x == _x;
192 const bool_ct y_coordinates_match = (_y == other.
_y);
193 const bool_ct infinity_predicate = (x_coordinates_match && y_coordinates_match);
194 const bool_ct double_predicate = (x_coordinates_match && !y_coordinates_match);
195 const bool_ct lhs_infinity = is_point_at_infinity();
197 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
201 const Fq add_lambda_numerator = -other.
_y - _y;
202 const Fq xx = _x * _x;
203 Fq dbl_lambda_numerator = xx + xx + xx;
204 if constexpr (G::has_a) {
208 dbl_lambda_numerator = dbl_lambda_numerator +
a;
210 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
212 const Fq add_lambda_denominator = other.
_x - _x;
213 const Fq dbl_lambda_denominator = _y + _y;
214 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
218 lambda_denominator = Fq::conditional_assign(has_infinity_input || infinity_predicate,
Fq(1), lambda_denominator);
219 const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator);
222 const Fq x3 = lambda.sqradd({ -other.
_x, -_x });
223 const Fq y3 = lambda.madd(_x - x3, { -_y });
227 result.
_x = Fq::conditional_assign(lhs_infinity, other.
_x, result.
_x);
228 result.
_y = Fq::conditional_assign(lhs_infinity, -other.
_y, result.
_y);
230 result.
_x = Fq::conditional_assign(rhs_infinity, _x, result.
_x);
231 result.
_y = Fq::conditional_assign(rhs_infinity, _y, result.
_y);
236 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
305 const typename G::Fq y_value =
uint256_t(_y.get_value());
306 BB_ASSERT_EQ((y_value == 0),
false,
"Attempting to dbl a point with y = 0, not allowed.");
309 if constexpr (G::has_a) {
315 Fq neg_lambda = Fq::msub_div({ _x }, { (two_x + _x) }, (_y + _y), {
a },
false);
319 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
323 Fq y_3 = neg_lambda.madd(x_3 - _x, { -_y });
325 element result(x_3, y_3,
false);
333 Fq neg_lambda = Fq::msub_div({ _x }, { (two_x + _x) }, (_y + _y), {},
false);
337 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
341 Fq y_3 = neg_lambda.madd(x_3 - _x, { -_y });
482 bool is_negative =
false;
493 x().assert_is_not_equal(add[0].x3_prev,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
497 if (!add[0].is_full_element) {
504 lambda1 = Fq::msub_div({ add[0].lambda_prev },
505 { add[0].x1_prev - add[0].x3_prev },
506 (x() - add[0].x3_prev),
507 { -add[0].y1_prev, -y() },
513 lambda1 = Fq::div_without_denominator_check({ y() - add[0].y3_prev }, (x() - add[0].x3_prev));
518 Fq x_3 = lambda1.madd(lambda1, { -add[0].x3_prev, -x() });
525 x().assert_is_not_equal(x_3,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
526 Fq lambda2 = Fq::div_without_denominator_check({ y() + y() }, (x() - x_3)) - lambda1;
530 Fq x_4 = lambda2.sqradd({ -x_3, -x() });
542 const bool num_points_even = ((add.size() & 1ULL) == 0);
543 composite_y previous_y;
544 previous_y.add.emplace_back(num_points_even ? y() : -y());
545 previous_y.mul_left.emplace_back(lambda2);
546 previous_y.mul_right.emplace_back(num_points_even ? x_4 - x() : x() - x_4);
547 previous_y.is_negative = num_points_even;
551 for (
size_t i = 1; i < add.size(); ++i) {
555 previous_x.assert_is_not_equal(add[i].x3_prev,
556 "biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
560 const bool negate_add_y = !previous_y.is_negative;
567 if (!add[i].is_full_element) {
576 lambda1_left.emplace_back(add[i].lambda_prev);
577 lambda1_right.emplace_back(negate_add_y ? add[i].x3_prev - add[i].x1_prev
578 : add[i].x1_prev - add[i].x3_prev);
579 lambda1_add.emplace_back(negate_add_y ? add[i].y1_prev : -add[i].y1_prev);
587 lambda1_add.emplace_back(negate_add_y ? -add[i].y3_prev : add[i].y3_prev);
591 Fq denominator = negate_add_y ? add[i].x3_prev - previous_x : previous_x - add[i].x3_prev;
593 Fq::msub_div(lambda1_left, lambda1_right, denominator, lambda1_add,
false);
598 Fq x_3 = lambda1.madd(lambda1, { -add[i].x3_prev, -previous_x });
606 previous_x.assert_is_not_equal(x_3,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
607 Fq l2_denominator = previous_y.is_negative ? previous_x - x_3 : x_3 - previous_x;
608 Fq partial_lambda2 = Fq::msub_div(previous_y.mul_left,
609 previous_y.mul_right,
613 partial_lambda2 = partial_lambda2 + partial_lambda2;
614 lambda2 = partial_lambda2 - lambda1;
618 x_4 = lambda2.sqradd({ -x_3, -previous_x });
627 y_4.is_negative = !previous_y.is_negative;
628 y_4.mul_left.emplace_back(lambda2);
629 y_4.mul_right.emplace_back(previous_y.is_negative ? previous_x - x_4 : x_4 - previous_x);
644 Fq x_out = previous_x;
648 Fq y_out = Fq::mult_madd(previous_y.mul_left, previous_y.mul_right, previous_y.add);
649 return element(x_out, y_out,
false);
704 const std::vector<Fr>& scalars,
705 const size_t max_num_bits)
708 BB_ASSERT_GT(points.size(), 0ULL,
"process_strauss_msm: points cannot be empty");
709 BB_ASSERT_EQ(points.size(), scalars.size(),
"process_strauss_msm: points and scalars size mismatch");
712 for (
const auto& scalar : scalars) {
713 const size_t num_scalar_bits =
static_cast<size_t>(
uint512_t(scalar.get_value()).
get_msb()) + 1ULL;
714 BB_ASSERT_LTE(num_scalar_bits, max_num_bits,
"process_strauss_msm: scalar out of range");
718 const size_t num_rounds = max_num_bits;
719 const size_t msm_size = scalars.size();
738 for (
size_t i = 0; i < msm_size; ++i) {
739 naf_entries.emplace_back(compute_naf(scalars[i], num_rounds));
744 const auto [offset_generator_start, offset_generator_end] = compute_offset_generators(num_rounds);
751 constexpr size_t num_rounds_per_iteration = 4;
752 const size_t num_iterations =
numeric::ceil_div((num_rounds - 1), num_rounds_per_iteration);
753 const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration);
755 for (
size_t i = 0; i < num_iterations; ++i) {
757 const size_t inner_num_rounds =
758 (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration;
759 for (
size_t j = 0; j < inner_num_rounds; ++j) {
762 for (
size_t k = 0; k < msm_size; ++k) {
763 nafs[k] = (naf_entries[k][(i * num_rounds_per_iteration) + j + 1]);
775 for (
size_t i = 0; i < msm_size; ++i) {
776 element skew = accumulator - points[i];
781 accumulator = accumulator - offset_generator_end;
821 const std::vector<Fr>& _scalars,
822 const size_t max_num_bits,
823 const bool with_edgecases,
824 const Fr& masking_scalar)
827 BB_ASSERT_GT(_points.size(), 0ULL,
"biggroup batch_mul: no points provided for batch multiplication");
828 BB_ASSERT_EQ(_points.size(), _scalars.size(),
"biggroup batch_mul: points and scalars size mismatch");
831 auto [points, scalars] = handle_points_at_infinity(_points, _scalars);
836 "biggroup batch_mul: points and scalars size mismatch after handling points at infinity");
846 for (
size_t i = 0; i < _points.size(); i++) {
849 for (
size_t i = 0; i < scalars.size(); i++) {
850 points[i].set_origin_tag(empty_tag);
851 scalars[i].set_origin_tag(empty_tag);
855 bool has_constant_terms =
false;
856 typename G::element constant_accumulator = G::element::infinity();
858 std::vector<Fr> new_scalars;
859 for (
size_t i = 0; i < points.size(); ++i) {
860 if (points[i].is_constant() && scalars[i].is_constant()) {
861 const auto& point_value =
typename G::element(points[i].get_value());
862 const auto& scalar_value =
typename G::Fr(scalars[i].get_value());
863 constant_accumulator += (point_value * scalar_value);
864 has_constant_terms =
true;
866 new_points.emplace_back(points[i]);
867 new_scalars.emplace_back(scalars[i]);
871 scalars = new_scalars;
874 if (!with_edgecases) {
876 masking_scalar.is_constant() && masking_scalar.get_value() == 1,
878 "biggroup batch_mul: masking_scalar must be constant (and equal to 1) when with_edgecases is false");
881 if (with_edgecases) {
885 std::tie(points, scalars) = mask_points(points, scalars, masking_scalar);
889 points.size(), scalars.size(),
"biggroup batch_mul: points and scalars size mismatch after handling edgecases");
894 const size_t original_size = scalars.size();
895 std::vector<Fr> big_scalars;
897 std::vector<Fr> small_scalars;
899 for (
size_t i = 0; i < original_size; ++i) {
900 if (max_num_bits == 0) {
901 big_points.emplace_back(points[i]);
902 big_scalars.emplace_back(scalars[i]);
904 const bool is_last_scalar_big = ((i == original_size - 1) && with_edgecases);
905 if (scalars[i].get_value() == 0 || is_last_scalar_big) {
906 big_points.emplace_back(points[i]);
907 big_scalars.emplace_back(scalars[i]);
909 small_points.emplace_back(points[i]);
910 small_scalars.emplace_back(scalars[i]);
916 small_points.size() + big_points.size(),
917 "biggroup batch_mul: points size mismatch after separating big scalars");
920 "biggroup batch_mul: big points and scalars size mismatch after separating big scalars");
922 small_scalars.size(),
923 "biggroup batch_mul: small points and scalars size mismatch after separating big scalars");
928 bool accumulator_initialized =
false;
934 const bool has_no_points = big_points.empty() && small_points.empty();
935 if (has_constant_terms || has_no_points) {
936 accumulator =
element(constant_accumulator);
937 accumulator_initialized =
true;
940 if (!big_points.empty()) {
942 element big_result = element::process_strauss_msm_rounds(big_points, big_scalars, max_num_bits_in_field);
943 accumulator = accumulator_initialized ? accumulator + big_result : big_result;
944 accumulator_initialized =
true;
947 if (!small_points.empty()) {
949 const size_t effective_max_num_bits = (max_num_bits == 0) ? max_num_bits_in_field : max_num_bits;
950 element small_result = element::process_strauss_msm_rounds(small_points, small_scalars, effective_max_num_bits);
951 accumulator = accumulator_initialized ? accumulator + small_result : small_result;
952 accumulator_initialized =
true;