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);
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);
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);
559 const bool negate_add_y = !previous_y.is_negative;
566 if (!add[i].is_full_element) {
575 lambda1_left.emplace_back(add[i].lambda_prev);
576 lambda1_right.emplace_back(negate_add_y ? add[i].x3_prev - add[i].x1_prev
577 : add[i].x1_prev - add[i].x3_prev);
578 lambda1_add.emplace_back(negate_add_y ? add[i].y1_prev : -add[i].y1_prev);
586 lambda1_add.emplace_back(negate_add_y ? -add[i].y3_prev : add[i].y3_prev);
590 Fq denominator = negate_add_y ? add[i].x3_prev - previous_x : previous_x - add[i].x3_prev;
592 Fq::msub_div(lambda1_left, lambda1_right, denominator, lambda1_add,
false);
597 Fq x_3 = lambda1.madd(lambda1, { -add[i].x3_prev, -previous_x });
605 previous_x.assert_is_not_equal(x_3);
606 Fq l2_denominator = previous_y.is_negative ? previous_x - x_3 : x_3 - previous_x;
607 Fq partial_lambda2 = Fq::msub_div(previous_y.mul_left,
608 previous_y.mul_right,
612 partial_lambda2 = partial_lambda2 + partial_lambda2;
613 lambda2 = partial_lambda2 - lambda1;
617 x_4 = lambda2.sqradd({ -x_3, -previous_x });
626 y_4.is_negative = !previous_y.is_negative;
627 y_4.mul_left.emplace_back(lambda2);
628 y_4.mul_right.emplace_back(previous_y.is_negative ? previous_x - x_4 : x_4 - previous_x);
643 Fq x_out = previous_x;
647 Fq y_out = Fq::mult_madd(previous_y.mul_left, previous_y.mul_right, previous_y.add);
648 return element(x_out, y_out,
false);
703 const std::vector<Fr>& scalars,
704 const size_t max_num_bits)
707 BB_ASSERT_GT(points.size(), 0ULL,
"process_strauss_msm: points cannot be empty");
708 BB_ASSERT_EQ(points.size(), scalars.size(),
"process_strauss_msm: points and scalars size mismatch");
711 for (
const auto& scalar : scalars) {
712 const size_t num_scalar_bits =
static_cast<size_t>(
uint512_t(scalar.get_value()).
get_msb()) + 1ULL;
713 BB_ASSERT_LTE(num_scalar_bits, max_num_bits,
"process_strauss_msm: scalar out of range");
717 const size_t num_rounds = max_num_bits;
718 const size_t msm_size = scalars.size();
737 for (
size_t i = 0; i < msm_size; ++i) {
738 naf_entries.emplace_back(compute_naf(scalars[i], num_rounds));
743 const auto [offset_generator_start, offset_generator_end] = compute_offset_generators(num_rounds);
750 constexpr size_t num_rounds_per_iteration = 4;
751 const size_t num_iterations =
numeric::ceil_div((num_rounds - 1), num_rounds_per_iteration);
752 const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration);
754 for (
size_t i = 0; i < num_iterations; ++i) {
756 const size_t inner_num_rounds =
757 (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration;
758 for (
size_t j = 0; j < inner_num_rounds; ++j) {
761 for (
size_t k = 0; k < msm_size; ++k) {
762 nafs[k] = (naf_entries[k][(i * num_rounds_per_iteration) + j + 1]);
774 for (
size_t i = 0; i < msm_size; ++i) {
775 element skew = accumulator - points[i];
780 accumulator = accumulator - offset_generator_end;
820 const std::vector<Fr>& _scalars,
821 const size_t max_num_bits,
822 const bool with_edgecases,
823 const Fr& masking_scalar)
826 BB_ASSERT_GT(_points.size(), 0ULL,
"biggroup batch_mul: no points provided for batch multiplication");
827 BB_ASSERT_EQ(_points.size(), _scalars.size(),
"biggroup batch_mul: points and scalars size mismatch");
830 auto [points, scalars] = handle_points_at_infinity(_points, _scalars);
835 "biggroup batch_mul: points and scalars size mismatch after handling points at infinity");
845 for (
size_t i = 0; i < _points.size(); i++) {
848 for (
size_t i = 0; i < scalars.size(); i++) {
849 points[i].set_origin_tag(empty_tag);
850 scalars[i].set_origin_tag(empty_tag);
854 bool has_constant_terms =
false;
855 typename G::element constant_accumulator = G::element::infinity();
857 std::vector<Fr> new_scalars;
858 for (
size_t i = 0; i < points.size(); ++i) {
859 if (points[i].is_constant() && scalars[i].is_constant()) {
860 const auto& point_value =
typename G::element(points[i].get_value());
861 const auto& scalar_value =
typename G::Fr(scalars[i].get_value());
862 constant_accumulator += (point_value * scalar_value);
863 has_constant_terms =
true;
865 new_points.emplace_back(points[i]);
866 new_scalars.emplace_back(scalars[i]);
870 scalars = new_scalars;
873 if (!with_edgecases) {
875 masking_scalar.is_constant() && masking_scalar.get_value() == 1,
877 "biggroup batch_mul: masking_scalar must be constant (and equal to 1) when with_edgecases is false");
880 if (with_edgecases) {
884 std::tie(points, scalars) = mask_points(points, scalars, masking_scalar);
888 points.size(), scalars.size(),
"biggroup batch_mul: points and scalars size mismatch after handling edgecases");
893 const size_t original_size = scalars.size();
894 std::vector<Fr> big_scalars;
896 std::vector<Fr> small_scalars;
898 for (
size_t i = 0; i < original_size; ++i) {
899 if (max_num_bits == 0) {
900 big_points.emplace_back(points[i]);
901 big_scalars.emplace_back(scalars[i]);
903 const bool is_last_scalar_big = ((i == original_size - 1) && with_edgecases);
904 if (scalars[i].get_value() == 0 || is_last_scalar_big) {
905 big_points.emplace_back(points[i]);
906 big_scalars.emplace_back(scalars[i]);
908 small_points.emplace_back(points[i]);
909 small_scalars.emplace_back(scalars[i]);
915 small_points.size() + big_points.size(),
916 "biggroup batch_mul: points size mismatch after separating big scalars");
919 "biggroup batch_mul: big points and scalars size mismatch after separating big scalars");
921 small_scalars.size(),
922 "biggroup batch_mul: small points and scalars size mismatch after separating big scalars");
927 bool accumulator_initialized =
false;
933 const bool has_no_points = big_points.empty() && small_points.empty();
934 if (has_constant_terms || has_no_points) {
935 accumulator =
element(constant_accumulator);
936 accumulator_initialized =
true;
939 if (!big_points.empty()) {
941 element big_result = element::process_strauss_msm_rounds(big_points, big_scalars, max_num_bits_in_field);
942 accumulator = accumulator_initialized ? accumulator + big_result : big_result;
943 accumulator_initialized =
true;
946 if (!small_points.empty()) {
948 const size_t effective_max_num_bits = (max_num_bits == 0) ? max_num_bits_in_field : max_num_bits;
949 element small_result = element::process_strauss_msm_rounds(small_points, small_scalars, effective_max_num_bits);
950 accumulator = accumulator_initialized ? accumulator + small_result : small_result;
951 accumulator_initialized =
true;