137 const bool_ct x_coordinates_match = other.
_x == _x;
138 const bool_ct y_coordinates_match = (_y == other.
_y);
139 const bool_ct infinity_predicate = (x_coordinates_match && !y_coordinates_match);
140 const bool_ct double_predicate = (x_coordinates_match && y_coordinates_match);
141 const bool_ct lhs_infinity = is_point_at_infinity();
143 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
152 const typename G::Fq y_value =
uint256_t(_y.get_value());
153 BB_ASSERT_EQ((y_value == 0),
false,
"Attempting to add a point with y = 0, not allowed.");
156 const typename G::Fq other_y_value =
uint256_t(other.
_y.get_value());
157 BB_ASSERT_EQ((other_y_value == 0),
false,
"Attempting to add a point with y = 0, not allowed.");
162 const Fq add_lambda_numerator = other.
_y - _y;
163 const Fq xx = _x * _x;
164 Fq dbl_lambda_numerator = xx + xx + xx;
165 if constexpr (G::has_a) {
169 dbl_lambda_numerator = dbl_lambda_numerator +
a;
171 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
173 const Fq add_lambda_denominator = other.
_x - _x;
174 const Fq dbl_lambda_denominator = _y + _y;
175 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
179 const bool_ct safe_denominator_needed = has_infinity_input || infinity_predicate;
180 lambda_denominator = Fq::conditional_assign(safe_denominator_needed,
Fq(1), lambda_denominator);
184 const Fq lambda = lambda_numerator / lambda_denominator;
187 Fq x3 = lambda.sqradd({ -other.
_x, -_x });
188 Fq y3 = lambda.madd(_x - x3, { -_y });
191 x3 = Fq::conditional_assign(lhs_infinity, other.
_x, x3);
192 y3 = Fq::conditional_assign(lhs_infinity, other.
_y, y3);
194 x3 = Fq::conditional_assign(rhs_infinity, _x, x3);
195 y3 = Fq::conditional_assign(rhs_infinity, _y, y3);
200 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
203 element result(x3, y3, result_is_infinity,
false);
258 const bool_ct x_coordinates_match = other.
_x == _x;
259 const bool_ct y_coordinates_match = (_y == other.
_y);
260 const bool_ct infinity_predicate = (x_coordinates_match && y_coordinates_match);
261 const bool_ct double_predicate = (x_coordinates_match && !y_coordinates_match);
262 const bool_ct lhs_infinity = is_point_at_infinity();
264 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
268 const Fq add_lambda_numerator = -other.
_y - _y;
269 const Fq xx = _x * _x;
270 Fq dbl_lambda_numerator = xx + xx + xx;
271 if constexpr (G::has_a) {
275 dbl_lambda_numerator = dbl_lambda_numerator +
a;
277 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
279 const Fq add_lambda_denominator = other.
_x - _x;
280 const Fq dbl_lambda_denominator = _y + _y;
281 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
286 const bool_ct safe_denominator_needed = has_infinity_input || infinity_predicate;
287 lambda_denominator = Fq::conditional_assign(safe_denominator_needed,
Fq(1), lambda_denominator);
291 const Fq lambda = lambda_numerator / lambda_denominator;
294 Fq x3 = lambda.sqradd({ -other.
_x, -_x });
295 Fq y3 = lambda.madd(_x - x3, { -_y });
298 x3 = Fq::conditional_assign(lhs_infinity, other.
_x, x3);
299 y3 = Fq::conditional_assign(lhs_infinity, -other.
_y, y3);
301 x3 = Fq::conditional_assign(rhs_infinity, _x, x3);
302 y3 = Fq::conditional_assign(rhs_infinity, _y, y3);
307 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
310 element result(x3, y3, result_is_infinity,
false);
396 const bool_ct is_infinity = is_point_at_infinity();
405 const typename G::Fq y_value =
uint256_t(_y.get_value());
406 BB_ASSERT_EQ((y_value == 0),
false,
"Attempting to dbl a point with y = 0, not allowed.");
411 const Fq two_y = _y + _y;
412 Fq denominator = Fq::conditional_assign(is_infinity,
Fq(get_context(), 1), two_y);
415 if constexpr (G::has_a) {
422 Fq neg_lambda = Fq::msub_div({ _x }, { (two_x + _x) }, denominator, {
a },
true);
426 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
430 Fq y_3 = neg_lambda.madd(x_3 - _x, { -_y });
433 return element(x_3, y_3, is_infinity,
false);
440 Fq neg_lambda = Fq::msub_div({ _x }, { (two_x + _x) }, denominator, {},
true);
444 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
448 Fq y_3 = neg_lambda.madd(x_3 - _x, { -_y });
451 return element(x_3, y_3, is_infinity,
false);
604 bool is_negative =
false;
615 x().assert_is_not_equal(add[0].x3_prev,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
619 if (!add[0].is_full_element) {
626 lambda1 = Fq::msub_div({ add[0].lambda_prev },
627 { add[0].x1_prev - add[0].x3_prev },
628 (x() - add[0].x3_prev),
629 { -add[0].y1_prev, -y() },
635 lambda1 = Fq::div_without_denominator_check({ y() - add[0].y3_prev }, (x() - add[0].x3_prev));
640 Fq x_3 = lambda1.madd(lambda1, { -add[0].x3_prev, -x() });
647 x().assert_is_not_equal(x_3,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
648 Fq lambda2 = Fq::div_without_denominator_check({ y() + y() }, (x() - x_3)) - lambda1;
652 Fq x_4 = lambda2.sqradd({ -x_3, -x() });
664 const bool num_points_even = ((add.size() & 1ULL) == 0);
665 composite_y previous_y;
666 previous_y.add.emplace_back(num_points_even ? y() : -y());
667 previous_y.mul_left.emplace_back(lambda2);
668 previous_y.mul_right.emplace_back(num_points_even ? x_4 - x() : x() - x_4);
669 previous_y.is_negative = num_points_even;
673 for (
size_t i = 1; i < add.size(); ++i) {
677 previous_x.assert_is_not_equal(add[i].x3_prev,
678 "biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
682 const bool negate_add_y = !previous_y.is_negative;
689 if (!add[i].is_full_element) {
698 lambda1_left.emplace_back(add[i].lambda_prev);
699 lambda1_right.emplace_back(negate_add_y ? add[i].x3_prev - add[i].x1_prev
700 : add[i].x1_prev - add[i].x3_prev);
701 lambda1_add.emplace_back(negate_add_y ? add[i].y1_prev : -add[i].y1_prev);
709 lambda1_add.emplace_back(negate_add_y ? -add[i].y3_prev : add[i].y3_prev);
713 Fq denominator = negate_add_y ? add[i].x3_prev - previous_x : previous_x - add[i].x3_prev;
715 Fq::msub_div(lambda1_left, lambda1_right, denominator, lambda1_add,
false);
720 Fq x_3 = lambda1.madd(lambda1, { -add[i].x3_prev, -previous_x });
728 previous_x.assert_is_not_equal(x_3,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
729 Fq l2_denominator = previous_y.is_negative ? previous_x - x_3 : x_3 - previous_x;
730 Fq partial_lambda2 = Fq::msub_div(previous_y.mul_left,
731 previous_y.mul_right,
735 partial_lambda2 = partial_lambda2 + partial_lambda2;
736 lambda2 = partial_lambda2 - lambda1;
740 x_4 = lambda2.sqradd({ -x_3, -previous_x });
749 y_4.is_negative = !previous_y.is_negative;
750 y_4.mul_left.emplace_back(lambda2);
751 y_4.mul_right.emplace_back(previous_y.is_negative ? previous_x - x_4 : x_4 - previous_x);
766 Fq x_out = previous_x;
770 Fq y_out = Fq::mult_madd(previous_y.mul_left, previous_y.mul_right, previous_y.add);
772 return element(x_out, y_out,
bool_ct(x_out.get_context(),
false),
false);
829 const std::vector<Fr>& scalars,
830 const size_t max_num_bits)
833 BB_ASSERT_GT(points.size(), 0ULL,
"process_strauss_msm: points cannot be empty");
834 BB_ASSERT_EQ(points.size(), scalars.size(),
"process_strauss_msm: points and scalars size mismatch");
837 for (
const auto& scalar : scalars) {
838 const size_t num_scalar_bits =
static_cast<size_t>(
uint512_t(scalar.get_value()).
get_msb()) + 1ULL;
839 BB_ASSERT_LTE(num_scalar_bits, max_num_bits,
"process_strauss_msm: scalar out of range");
843 const size_t num_rounds = max_num_bits;
844 const size_t msm_size = scalars.size();
863 for (
size_t i = 0; i < msm_size; ++i) {
864 naf_entries.emplace_back(compute_naf(scalars[i], num_rounds));
869 const auto [offset_generator_start, offset_generator_end] = compute_offset_generators(num_rounds);
876 constexpr size_t num_rounds_per_iteration = 4;
877 const size_t num_iterations =
numeric::ceil_div((num_rounds - 1), num_rounds_per_iteration);
878 const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration);
880 for (
size_t i = 0; i < num_iterations; ++i) {
882 const size_t inner_num_rounds =
883 (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration;
884 for (
size_t j = 0; j < inner_num_rounds; ++j) {
887 for (
size_t k = 0; k < msm_size; ++k) {
888 nafs[k] = (naf_entries[k][(i * num_rounds_per_iteration) + j + 1]);
900 for (
size_t i = 0; i < msm_size; ++i) {
949 const std::vector<Fr>& _scalars,
950 const size_t max_num_bits,
951 const bool with_edgecases)
954 BB_ASSERT_GT(_points.size(), 0ULL,
"biggroup batch_mul: no points provided for batch multiplication");
955 BB_ASSERT_EQ(_points.size(), _scalars.size(),
"biggroup batch_mul: points and scalars size mismatch");
958 C*
builder = _points[0].get_context();
961 auto [points, scalars] = handle_points_at_infinity(_points, _scalars);
966 "biggroup batch_mul: points and scalars size mismatch after handling points at infinity");
976 for (
size_t i = 0; i < _points.size(); i++) {
979 for (
size_t i = 0; i < scalars.size(); i++) {
980 points[i].set_origin_tag(empty_tag);
981 scalars[i].set_origin_tag(empty_tag);
985 bool has_constant_terms =
false;
986 typename G::element constant_accumulator = G::element::infinity();
988 std::vector<Fr> new_scalars;
989 for (
size_t i = 0; i < points.size(); ++i) {
990 if (points[i].is_constant() && scalars[i].is_constant()) {
991 const auto& point_value =
typename G::element(points[i].get_value());
992 const auto& scalar_value =
typename G::Fr(scalars[i].get_value());
993 constant_accumulator += (point_value * scalar_value);
994 has_constant_terms =
true;
996 new_points.emplace_back(points[i]);
997 new_scalars.emplace_back(scalars[i]);
1000 points = new_points;
1001 scalars = new_scalars;
1003 if (with_edgecases && !points.empty()) {
1007 std::tie(points, scalars) = mask_points(points, scalars);
1011 points.size(), scalars.size(),
"biggroup batch_mul: points and scalars size mismatch after handling edgecases");
1016 const size_t original_size = scalars.size();
1017 std::vector<Fr> big_scalars;
1019 std::vector<Fr> small_scalars;
1021 for (
size_t i = 0; i < original_size; ++i) {
1022 if (max_num_bits == 0) {
1023 big_points.emplace_back(points[i]);
1024 big_scalars.emplace_back(scalars[i]);
1026 const bool is_last_scalar_big = ((i == original_size - 1) && with_edgecases);
1027 if (scalars[i].get_value() == 0 || is_last_scalar_big) {
1028 big_points.emplace_back(points[i]);
1029 big_scalars.emplace_back(scalars[i]);
1031 small_points.emplace_back(points[i]);
1032 small_scalars.emplace_back(scalars[i]);
1038 small_points.size() + big_points.size(),
1039 "biggroup batch_mul: points size mismatch after separating big scalars");
1042 "biggroup batch_mul: big points and scalars size mismatch after separating big scalars");
1044 small_scalars.size(),
1045 "biggroup batch_mul: small points and scalars size mismatch after separating big scalars");
1050 bool accumulator_initialized =
false;
1056 const bool has_no_points = big_points.empty() && small_points.empty();
1057 if (has_constant_terms || has_no_points) {
1059 typename G::affine_element constant_accumulator_affine(constant_accumulator);
1060 if (constant_accumulator_affine.is_point_at_infinity()) {
1064 accumulator =
element(zero_fq, zero_fq);
1066 accumulator =
element(constant_accumulator_affine.x, constant_accumulator_affine.y);
1068 accumulator_initialized =
true;
1071 if (!big_points.empty()) {
1073 element big_result = element::process_strauss_msm_rounds(big_points, big_scalars, max_num_bits_in_field);
1074 accumulator = accumulator_initialized ? accumulator.
add_internal(big_result) : big_result;
1075 accumulator_initialized =
true;
1078 if (!small_points.empty()) {
1080 const size_t effective_max_num_bits = (max_num_bits == 0) ? max_num_bits_in_field : max_num_bits;
1081 element small_result = element::process_strauss_msm_rounds(small_points, small_scalars, effective_max_num_bits);
1082 accumulator = accumulator_initialized ? accumulator.
add_internal(small_result) : small_result;
1083 accumulator_initialized =
true;