139 const bool_ct x_coordinates_match = other.
_x == _x;
140 const bool_ct y_coordinates_match = (_y == other.
_y);
141 const bool_ct infinity_predicate = (x_coordinates_match && !y_coordinates_match);
142 const bool_ct double_predicate = (x_coordinates_match && y_coordinates_match);
143 const bool_ct lhs_infinity = is_point_at_infinity();
145 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
154 const typename G::Fq y_value =
uint256_t(_y.get_value());
155 BB_ASSERT_EQ((y_value == 0),
false,
"Attempting to add a point with y = 0, not allowed.");
158 const typename G::Fq other_y_value =
uint256_t(other.
_y.get_value());
159 BB_ASSERT_EQ((other_y_value == 0),
false,
"Attempting to add a point with y = 0, not allowed.");
164 const Fq add_lambda_numerator = other.
_y - _y;
165 const Fq xx = _x * _x;
166 Fq dbl_lambda_numerator = xx + xx + xx;
167 if constexpr (G::has_a) {
171 dbl_lambda_numerator = dbl_lambda_numerator +
a;
173 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
175 const Fq add_lambda_denominator = other.
_x - _x;
176 const Fq dbl_lambda_denominator = _y + _y;
177 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
181 const bool_ct safe_denominator_needed = has_infinity_input || infinity_predicate;
182 lambda_denominator = Fq::conditional_assign(safe_denominator_needed,
Fq(1), lambda_denominator);
186 const Fq lambda = lambda_numerator / lambda_denominator;
189 Fq x3 = lambda.sqradd({ -other.
_x, -_x });
190 Fq y3 = lambda.madd(_x - x3, { -_y });
193 x3 = Fq::conditional_assign(lhs_infinity, other.
_x, x3);
194 y3 = Fq::conditional_assign(lhs_infinity, other.
_y, y3);
196 x3 = Fq::conditional_assign(rhs_infinity, _x, x3);
197 y3 = Fq::conditional_assign(rhs_infinity, _y, y3);
202 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
260 const bool_ct x_coordinates_match = other.
_x == _x;
261 const bool_ct y_coordinates_match = (_y == other.
_y);
262 const bool_ct infinity_predicate = (x_coordinates_match && y_coordinates_match);
263 const bool_ct double_predicate = (x_coordinates_match && !y_coordinates_match);
264 const bool_ct lhs_infinity = is_point_at_infinity();
266 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
270 const Fq add_lambda_numerator = -other.
_y - _y;
271 const Fq xx = _x * _x;
272 Fq dbl_lambda_numerator = xx + xx + xx;
273 if constexpr (G::has_a) {
277 dbl_lambda_numerator = dbl_lambda_numerator +
a;
279 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
281 const Fq add_lambda_denominator = other.
_x - _x;
282 const Fq dbl_lambda_denominator = _y + _y;
283 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
288 const bool_ct safe_denominator_needed = has_infinity_input || infinity_predicate;
289 lambda_denominator = Fq::conditional_assign(safe_denominator_needed,
Fq(1), lambda_denominator);
293 const Fq lambda = lambda_numerator / lambda_denominator;
296 Fq x3 = lambda.sqradd({ -other.
_x, -_x });
297 Fq y3 = lambda.madd(_x - x3, { -_y });
300 x3 = Fq::conditional_assign(lhs_infinity, other.
_x, x3);
301 y3 = Fq::conditional_assign(lhs_infinity, -other.
_y, y3);
303 x3 = Fq::conditional_assign(rhs_infinity, _x, x3);
304 y3 = Fq::conditional_assign(rhs_infinity, _y, y3);
309 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
398 const bool_ct is_infinity = is_point_at_infinity();
407 const typename G::Fq y_value =
uint256_t(_y.get_value());
408 BB_ASSERT_EQ((y_value == 0),
false,
"Attempting to dbl a point with y = 0, not allowed.");
413 const Fq two_y = _y + _y;
414 Fq denominator = Fq::conditional_assign(is_infinity,
Fq(get_context(), 1), two_y);
417 if constexpr (G::has_a) {
424 Fq neg_lambda = Fq::msub_div({ _x }, { (two_x + _x) }, denominator, {
a },
true);
428 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
432 Fq y_3 = neg_lambda.madd(x_3 - _x, { -_y });
435 return element(x_3, y_3, is_infinity,
false);
442 Fq neg_lambda = Fq::msub_div({ _x }, { (two_x + _x) }, denominator, {},
true);
446 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
450 Fq y_3 = neg_lambda.madd(x_3 - _x, { -_y });
453 return element(x_3, y_3, is_infinity,
false);
606 bool is_negative =
false;
617 x().assert_is_not_equal(add[0].x3_prev,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
621 if (!add[0].is_full_element) {
628 lambda1 = Fq::msub_div({ add[0].lambda_prev },
629 { add[0].x1_prev - add[0].x3_prev },
630 (x() - add[0].x3_prev),
631 { -add[0].y1_prev, -y() },
637 lambda1 = Fq::div_without_denominator_check({ y() - add[0].y3_prev }, (x() - add[0].x3_prev));
642 Fq x_3 = lambda1.madd(lambda1, { -add[0].x3_prev, -x() });
649 x().assert_is_not_equal(x_3,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
650 Fq lambda2 = Fq::div_without_denominator_check({ y() + y() }, (x() - x_3)) - lambda1;
654 Fq x_4 = lambda2.sqradd({ -x_3, -x() });
666 const bool num_points_even = ((add.size() & 1ULL) == 0);
667 composite_y previous_y;
668 previous_y.add.emplace_back(num_points_even ? y() : -y());
669 previous_y.mul_left.emplace_back(lambda2);
670 previous_y.mul_right.emplace_back(num_points_even ? x_4 - x() : x() - x_4);
671 previous_y.is_negative = num_points_even;
675 for (
size_t i = 1; i < add.size(); ++i) {
679 previous_x.assert_is_not_equal(add[i].x3_prev,
680 "biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
684 const bool negate_add_y = !previous_y.is_negative;
691 if (!add[i].is_full_element) {
700 lambda1_left.emplace_back(add[i].lambda_prev);
701 lambda1_right.emplace_back(negate_add_y ? add[i].x3_prev - add[i].x1_prev
702 : add[i].x1_prev - add[i].x3_prev);
703 lambda1_add.emplace_back(negate_add_y ? add[i].y1_prev : -add[i].y1_prev);
711 lambda1_add.emplace_back(negate_add_y ? -add[i].y3_prev : add[i].y3_prev);
715 Fq denominator = negate_add_y ? add[i].x3_prev - previous_x : previous_x - add[i].x3_prev;
717 Fq::msub_div(lambda1_left, lambda1_right, denominator, lambda1_add,
false);
722 Fq x_3 = lambda1.madd(lambda1, { -add[i].x3_prev, -previous_x });
730 previous_x.assert_is_not_equal(x_3,
"biggroup::multiple_montgomery_ladder: x-coordinates must be distinct.");
731 Fq l2_denominator = previous_y.is_negative ? previous_x - x_3 : x_3 - previous_x;
732 Fq partial_lambda2 = Fq::msub_div(previous_y.mul_left,
733 previous_y.mul_right,
737 partial_lambda2 = partial_lambda2 + partial_lambda2;
738 lambda2 = partial_lambda2 - lambda1;
742 x_4 = lambda2.sqradd({ -x_3, -previous_x });
751 y_4.is_negative = !previous_y.is_negative;
752 y_4.mul_left.emplace_back(lambda2);
753 y_4.mul_right.emplace_back(previous_y.is_negative ? previous_x - x_4 : x_4 - previous_x);
760 y_4.mul_left.insert(y_4.mul_left.end(), previous_y.mul_left.begin(), previous_y.mul_left.end());
761 y_4.mul_right.insert(y_4.mul_right.end(), previous_y.mul_right.begin(), previous_y.mul_right.end());
762 y_4.add.insert(y_4.add.end(), previous_y.add.begin(), previous_y.add.end());
768 Fq x_out = previous_x;
772 Fq y_out = Fq::mult_madd(previous_y.mul_left, previous_y.mul_right, previous_y.add);
774 return element(x_out, y_out,
bool_ct(x_out.get_context(),
false),
false);
831 const std::vector<Fr>& scalars,
832 const size_t max_num_bits)
835 BB_ASSERT_GT(points.size(), 0ULL,
"process_strauss_msm: points cannot be empty");
836 BB_ASSERT_EQ(points.size(), scalars.size(),
"process_strauss_msm: points and scalars size mismatch");
839 for (
const auto& scalar : scalars) {
840 const size_t num_scalar_bits =
static_cast<size_t>(
uint512_t(scalar.get_value()).
get_msb()) + 1ULL;
841 BB_ASSERT_LTE(num_scalar_bits, max_num_bits,
"process_strauss_msm: scalar out of range");
845 const size_t num_rounds = max_num_bits;
846 const size_t msm_size = scalars.size();
865 for (
size_t i = 0; i < msm_size; ++i) {
866 naf_entries.emplace_back(compute_naf(scalars[i], num_rounds));
871 const auto [offset_generator_start, offset_generator_end] = compute_offset_generators(num_rounds);
878 constexpr size_t num_rounds_per_iteration = 4;
879 const size_t num_iterations =
numeric::ceil_div((num_rounds - 1), num_rounds_per_iteration);
880 const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration);
882 for (
size_t i = 0; i < num_iterations; ++i) {
884 const size_t inner_num_rounds =
885 (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration;
886 for (
size_t j = 0; j < inner_num_rounds; ++j) {
889 for (
size_t k = 0; k < msm_size; ++k) {
890 nafs[k] = (naf_entries[k][(i * num_rounds_per_iteration) + j + 1]);
898 accumulator = accumulator.multiple_montgomery_ladder(to_add);
902 for (
size_t i = 0; i < msm_size; ++i) {
903 element skew = accumulator.subtract_internal(points[i]);
904 accumulator = accumulator.conditional_select(skew, naf_entries[i][num_rounds]);
908 accumulator = accumulator.subtract_internal(offset_generator_end);
951 const std::vector<Fr>& _scalars,
952 const size_t max_num_bits,
953 const bool with_edgecases)
956 BB_ASSERT_GT(_points.size(), 0ULL,
"biggroup batch_mul: no points provided for batch multiplication");
957 BB_ASSERT_EQ(_points.size(), _scalars.size(),
"biggroup batch_mul: points and scalars size mismatch");
960 C*
builder = _points[0].get_context();
963 auto [points, scalars] = handle_points_at_infinity(_points, _scalars);
968 "biggroup batch_mul: points and scalars size mismatch after handling points at infinity");
978 for (
size_t i = 0; i < _points.size(); i++) {
981 for (
size_t i = 0; i < scalars.size(); i++) {
982 points[i].set_origin_tag(empty_tag);
983 scalars[i].set_origin_tag(empty_tag);
987 bool has_constant_terms =
false;
988 typename G::element constant_accumulator = G::element::infinity();
990 std::vector<Fr> new_scalars;
991 for (
size_t i = 0; i < points.size(); ++i) {
992 if (points[i].is_constant() && scalars[i].is_constant()) {
993 const auto& point_value =
typename G::element(points[i].get_value());
994 const auto& scalar_value =
typename G::Fr(scalars[i].get_value());
995 constant_accumulator += (point_value * scalar_value);
996 has_constant_terms =
true;
998 new_points.emplace_back(points[i]);
999 new_scalars.emplace_back(scalars[i]);
1002 points = new_points;
1003 scalars = new_scalars;
1005 if (with_edgecases && !points.empty()) {
1009 auto [masked_points, masked_scalars, _offset_generator] = mask_points(points, scalars);
1015 points.size(), scalars.size(),
"biggroup batch_mul: points and scalars size mismatch after handling edgecases");
1021 const size_t original_size = scalars.size();
1022 std::vector<Fr> big_scalars;
1024 std::vector<Fr> small_scalars;
1026 for (
size_t i = 0; i < original_size; ++i) {
1027 const bool is_last_scalar_big = (i == original_size - 1) && with_edgecases;
1028 if (max_num_bits == 0 || is_last_scalar_big) {
1029 big_points.emplace_back(points[i]);
1030 big_scalars.emplace_back(scalars[i]);
1032 small_points.emplace_back(points[i]);
1033 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;
1086 accumulator.set_origin_tag(
tag);