11#include <gtest/gtest.h>
28 static inline std::vector<ScalarField>
scalars{};
32 size_t total_points = input_scalars.size();
34 std::vector<Element> expected_accs(num_threads);
35 size_t range_per_thread = (total_points + num_threads - 1) / num_threads;
38 expected_thread_acc.self_set_infinity();
39 size_t start = thread_idx * range_per_thread;
40 size_t end = ((thread_idx + 1) * range_per_thread > total_points) ? total_points
41 : (thread_idx + 1) * range_per_thread;
42 bool skip = start >= total_points;
44 for (
size_t i = start; i < end; ++i) {
45 expected_thread_acc += input_points[i] * input_scalars[i];
48 expected_accs[thread_idx] = expected_thread_acc;
52 expected_acc.self_set_infinity();
53 for (
auto& acc : expected_accs) {
64 for (
size_t i = start; i < end; ++i) {
78 constexpr uint32_t fr_size = 254;
79 constexpr uint32_t slice_bits = 7;
80 constexpr uint32_t num_slices = (fr_size + 6) / 7;
81 constexpr uint32_t last_slice_bits = fr_size - ((num_slices - 1) * slice_bits);
83 for (
size_t x = 0; x < 100; ++x) {
85 input_u256.
data[3] = input_u256.
data[3] & 0x3FFFFFFFFFFFFFFF;
86 while (input_u256 > ScalarField::modulus) {
87 input_u256 -= ScalarField::modulus;
89 std::vector<uint32_t> slices(num_slices);
92 for (uint32_t i = 0; i < num_slices; ++i) {
93 uint32_t mask = ((1U << slice_bits) - 1U);
94 uint32_t shift = slice_bits;
96 mask = ((1U << last_slice_bits) - 1U);
97 shift = last_slice_bits;
99 slices[num_slices - 1 - i] =
static_cast<uint32_t
>((acc & mask).
data[0]);
104 input.self_from_montgomery_form_reduced();
106 ASSERT_EQ(input.data[0], input_u256.
data[0]);
107 ASSERT_EQ(input.data[1], input_u256.
data[1]);
108 ASSERT_EQ(input.data[2], input_u256.
data[2]);
109 ASSERT_EQ(input.data[3], input_u256.
data[3]);
111 for (uint32_t i = 0; i < num_slices; ++i) {
113 EXPECT_EQ(result, slices[i]);
120 const size_t total_points = 30071;
121 const size_t num_buckets = 128;
123 std::vector<uint64_t> input_point_schedule;
124 for (
size_t i = 0; i < total_points; ++i) {
126 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
127 input_point_schedule.push_back(schedule);
132 input_point_schedule,
generators, affine_data, bucket_data);
134 std::vector<Element> expected_buckets(num_buckets);
135 for (
auto& e : expected_buckets) {
136 e.self_set_infinity();
138 for (
size_t i = 0; i < total_points; ++i) {
139 uint64_t bucket = input_point_schedule[i] & 0xFFFFFFFF;
140 EXPECT_LT(
static_cast<size_t>(bucket), num_buckets);
141 expected_buckets[
static_cast<size_t>(bucket)] +=
generators[i];
143 for (
size_t i = 0; i < num_buckets; ++i) {
144 if (!expected_buckets[i].is_point_at_infinity()) {
146 EXPECT_EQ(expected, bucket_data.
buckets[i]);
155 const size_t total_points = 30071;
156 const size_t num_buckets = 128;
158 std::vector<uint64_t> input_point_schedule;
159 for (
size_t i = 0; i < total_points; ++i) {
161 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
162 input_point_schedule.push_back(schedule);
167 input_point_schedule,
generators, affine_data, bucket_data);
172 expected_acc.self_set_infinity();
174 std::vector<Element> expected_accs(num_threads);
175 size_t range_per_thread = (total_points + num_threads - 1) / num_threads;
178 expected_thread_acc.self_set_infinity();
179 size_t start = thread_idx * range_per_thread;
180 size_t end = (thread_idx == num_threads - 1) ? total_points : (thread_idx + 1) * range_per_thread;
181 bool skip = start >= total_points;
183 for (
size_t i = start; i < end; ++i) {
184 ScalarField scalar = input_point_schedule[i] & 0xFFFFFFFF;
185 expected_thread_acc +=
generators[i] * scalar;
188 expected_accs[thread_idx] = expected_thread_acc;
191 for (
size_t i = 0; i < num_threads; ++i) {
192 expected_acc += expected_accs[i];
200 const size_t total_points = 30071;
202 std::vector<uint64_t> input_point_schedule;
203 for (
size_t i = 0; i < total_points; ++i) {
205 uint64_t schedule =
static_cast<uint64_t
>(bucket) + (
static_cast<uint64_t
>(i) << 32);
206 input_point_schedule.push_back(schedule);
210 &input_point_schedule[0], input_point_schedule.size(), 7);
214 for (
size_t i = 0; i < total_points; ++i) {
215 expected +=
static_cast<size_t>((input_point_schedule[i] & 0xFFFFFFFF) == 0);
217 EXPECT_EQ(result, expected);
220 for (
size_t i = 1; i < total_points; ++i) {
221 uint32_t prev_bucket =
static_cast<uint32_t
>(input_point_schedule[i - 1]);
222 uint32_t curr_bucket =
static_cast<uint32_t
>(input_point_schedule[i]);
223 EXPECT_LE(prev_bucket, curr_bucket) <<
"Array not sorted at index " << i;
233 EXPECT_EQ(result, expected);
241 std::vector<AffineElement> expected(num_msms);
247 size_t vector_offset = 0;
248 for (
size_t k = 0; k < num_msms; ++k) {
251 ASSERT_LT(vector_offset + num_pts,
num_points);
252 std::span<const AffineElement> batch_points(&
generators[vector_offset], num_pts);
254 batch_scalars_copies[k].resize(num_pts);
255 for (
size_t i = 0; i < num_pts; ++i) {
256 batch_scalars_copies[k][i] =
scalars[vector_offset + i];
259 vector_offset += num_pts;
260 batch_points_span.push_back(batch_points);
261 batch_scalars_spans.push_back(batch_scalars_copies[k]);
263 expected[k] =
naive_msm(batch_scalars_spans[k], batch_points_span[k]);
266 std::vector<AffineElement> result =
269 EXPECT_EQ(result, expected);
274 const size_t num_msms = 10;
275 std::vector<AffineElement> expected(num_msms);
281 for (
size_t k = 0; k < num_msms; ++k) {
282 const size_t num_pts = 33;
283 auto& test_scalars = batch_scalars[k];
285 test_scalars.resize(num_pts);
287 size_t fixture_offset = k * num_pts;
290 for (
size_t i = 0; i < 13; ++i) {
293 for (
size_t i = 13; i < 23; ++i) {
294 test_scalars[i] =
scalars[fixture_offset + i + 13];
296 for (
size_t i = 23; i < num_pts; ++i) {
299 batch_points_span.push_back(batch_points);
300 batch_scalars_spans.push_back(batch_scalars[k]);
302 expected[k] =
naive_msm(batch_scalars[k], batch_points);
305 std::vector<AffineElement> result =
308 EXPECT_EQ(result, expected);
313 const size_t start_index = 1234;
314 const size_t num_pts =
num_points - start_index;
322 EXPECT_EQ(result, expected);
327 const size_t start_index = 1234;
328 const size_t num_pts =
num_points - start_index;
329 std::vector<ScalarField> test_scalars(num_pts, ScalarField::zero());
334 EXPECT_EQ(result, Group::affine_point_at_infinity);
339 std::vector<ScalarField> test_scalars;
340 std::vector<AffineElement> input_points;
344 EXPECT_EQ(result, Group::affine_point_at_infinity);
349 const size_t num_pts = 100;
350 std::vector<ScalarField> test_scalars(num_pts);
351 std::vector<ScalarField> scalars_copy(num_pts);
353 for (
size_t i = 0; i < num_pts; ++i) {
355 scalars_copy[i] = test_scalars[i];
358 std::span<const AffineElement> points(&
generators[0], num_pts);
363 for (
size_t i = 0; i < num_pts; ++i) {
364 EXPECT_EQ(test_scalars[i], scalars_copy[i]) <<
"Scalar at index " << i <<
" was modified";
370 const size_t num_msms = 3;
371 const size_t num_pts = 100;
378 for (
size_t k = 0; k < num_msms; ++k) {
379 batch_scalars[k].resize(num_pts);
380 scalars_copies[k].resize(num_pts);
382 for (
size_t i = 0; i < num_pts; ++i) {
383 batch_scalars[k][i] =
scalars[k * num_pts + i];
384 scalars_copies[k][i] = batch_scalars[k][i];
387 batch_points.push_back(std::span<const AffineElement>(&
generators[k * num_pts], num_pts));
388 batch_scalar_spans.push_back(batch_scalars[k]);
393 for (
size_t k = 0; k < num_msms; ++k) {
394 for (
size_t i = 0; i < num_pts; ++i) {
395 EXPECT_EQ(batch_scalars[k][i], scalars_copies[k][i])
396 <<
"Scalar at MSM " << k <<
", index " << i <<
" was modified";
403 const size_t num_pts = 5;
404 std::vector<ScalarField> test_scalars(num_pts, ScalarField::one());
405 std::span<const AffineElement> points(&
generators[0], num_pts);
411 expected.self_set_infinity();
412 for (
size_t i = 0; i < num_pts; ++i) {
413 expected += points[i];
421 const size_t num_pts = 5;
422 std::vector<ScalarField> test_scalars(num_pts, -ScalarField::one());
423 std::span<const AffineElement> points(&
generators[0], num_pts);
429 expected.self_set_infinity();
430 for (
size_t i = 0; i < num_pts; ++i) {
431 expected -= points[i];
439 std::vector<ScalarField> test_scalars = {
scalars[0] };
440 std::span<const AffineElement> points(&
generators[0], 1);
446 EXPECT_EQ(result, expected);
451 std::vector<size_t> test_sizes = { 1, 2, 15, 16, 17, 50, 127, 128, 129, 256, 512 };
453 for (
size_t num_pts : test_sizes) {
456 std::vector<ScalarField> test_scalars(num_pts);
457 for (
size_t i = 0; i < num_pts; ++i) {
461 std::span<const AffineElement> points(&
generators[0], num_pts);
467 EXPECT_EQ(result, expected) <<
"Failed for size " << num_pts;
474 const size_t num_pts = 32;
477 std::vector<AffineElement> points(num_pts, base_point);
478 std::vector<ScalarField> test_scalars(num_pts);
481 for (
size_t i = 0; i < num_pts; ++i) {
483 scalar_sum += test_scalars[i];
492 EXPECT_EQ(result, expected);
497 const size_t num_pts = 100;
498 std::vector<ScalarField> test_scalars(num_pts);
500 expected.self_set_infinity();
502 for (
size_t i = 0; i < num_pts; ++i) {
504 test_scalars[i] = ScalarField::zero();
511 std::span<const AffineElement> points(&
generators[0], num_pts);
520 const size_t num_pts = 200;
521 std::vector<ScalarField> test_scalars(num_pts);
522 for (
size_t i = 0; i < num_pts; ++i) {
526 std::span<const AffineElement> points(&
generators[0], num_pts);
529 auto result = scalar_multiplication::pippenger<Curve>(scalar_span, points);
537 const size_t num_pts = 200;
538 std::vector<ScalarField> test_scalars(num_pts);
539 for (
size_t i = 0; i < num_pts; ++i) {
543 std::span<const AffineElement> points(&
generators[0], num_pts);
546 auto result = scalar_multiplication::pippenger_unsafe<Curve>(scalar_span, points);
553using CurveTypes = ::testing::Types<bb::curve::BN254, bb::curve::Grumpkin>;
560 this->test_get_scalar_slice();
564 this->test_consume_point_batch();
568 this->test_consume_point_batch_and_accumulate();
572 this->test_radix_sort_count_zero_entries();
576 this->test_pippenger_low_memory();
580 this->test_batch_multi_scalar_mul();
584 this->test_batch_multi_scalar_mul_sparse();
592 this->test_msm_all_zeroes();
596 this->test_msm_empty_polynomial();
600 this->test_scalars_unchanged_after_msm();
604 this->test_scalars_unchanged_after_batch_multi_scalar_mul();
608 this->test_scalar_one();
612 this->test_scalar_minus_one();
616 this->test_single_point();
620 this->test_size_thresholds();
624 this->test_duplicate_points();
628 this->test_mixed_zero_scalars();
632 this->test_pippenger_free_function();
636 this->test_pippenger_unsafe_free_function();
640TEST(ScalarMultiplication, SmallInputsExplicit)
642 uint256_t x0(0x68df84429941826a, 0xeb08934ed806781c, 0xc14b6a2e4f796a73, 0x08dc1a9a11a3c8db);
643 uint256_t y0(0x8ae5c31aa997f141, 0xe85f20c504f2c11b, 0x81a94193f3b1ce2b, 0x26f2c37372adb5b7);
644 uint256_t x1(0x80f5a592d919d32f, 0x1362652b984e51ca, 0xa0b26666f770c2a1, 0x142c6e1964e5c3c5);
645 uint256_t y1(0xb6c322ebb5ae4bc5, 0xf9fef6c7909c00f8, 0xb37ca1cc9af3b421, 0x1e331c7fa73d6a59);
646 uint256_t s0(0xe48bf12a24272e08, 0xf8dd0182577f3567, 0xec8fd222b8a6becb, 0x102d76b945612c9b);
647 uint256_t s1(0x098ae8d69f1e4e9e, 0xb5c8313c0f6040ed, 0xf78041e30cc46c44, 0x1d1e6e0c21892e13);
#define BB_BENCH_NAME(name)
BB_INLINE bool get(size_t index) const noexcept
void test_pippenger_low_memory()
typename Curve::ScalarField ScalarField
static std::vector< AffineElement > generators
void test_mixed_zero_scalars()
void test_batch_multi_scalar_mul_sparse()
void test_duplicate_points()
void test_consume_point_batch()
void test_scalars_unchanged_after_batch_multi_scalar_mul()
void test_msm_all_zeroes()
void test_pippenger_unsafe_free_function()
void test_batch_multi_scalar_mul()
static constexpr size_t num_points
static void SetUpTestSuite()
void test_scalar_minus_one()
typename Curve::Element Element
void test_consume_point_batch_and_accumulate()
void test_msm_empty_polynomial()
void test_pippenger_free_function()
typename Curve::Group Group
void test_get_scalar_slice()
void test_scalars_unchanged_after_msm()
static std::vector< ScalarField > scalars
typename Curve::AffineElement AffineElement
static AffineElement naive_msm(std::span< ScalarField > input_scalars, std::span< const AffineElement > input_points)
void test_radix_sort_count_zero_entries()
void test_size_thresholds()
typename Group::element Element
typename grumpkin::g1 Group
typename Group::affine_element AffineElement
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
group_elements::affine_element< Fq, Fr, Params > affine_element
virtual uint8_t get_random_uint8()=0
virtual uint16_t get_random_uint16()=0
virtual uint256_t get_random_uint256()=0
static Element accumulate_buckets(BucketType &bucket_accumulators) noexcept
Reduce buckets to single point using running (suffix) sum from high to low: R = sum(k * B_k)
static uint32_t get_scalar_slice(const ScalarField &scalar, size_t round, size_t slice_size) noexcept
Extract c-bit slice from scalar for bucket index computation.
static AffineElement msm(std::span< const AffineElement > points, PolynomialSpan< const ScalarField > scalars, bool handle_edge_cases=false) noexcept
Main entry point for single MSM computation.
static std::vector< AffineElement > batch_multi_scalar_mul(std::span< std::span< const AffineElement > > points, std::span< std::span< ScalarField > > scalars, bool handle_edge_cases=true) noexcept
Compute multiple MSMs in parallel with work balancing.
static void batch_accumulate_points_into_buckets(std::span< const uint64_t > point_schedule, std::span< const AffineElement > points, AffineAdditionData &affine_data, BucketAccumulators &bucket_data) noexcept
Process sorted point schedule into bucket accumulators using batched affine additions.
const std::vector< MemoryValue > data
size_t sort_point_schedule_and_count_zero_buckets(uint64_t *point_schedule, const size_t num_entries, const uint32_t bucket_index_bits) noexcept
Sort point schedule by bucket index and count zero-bucket entries.
Entry point for Barretenberg command-line interface.
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
::testing::Types< curve::BN254, curve::Grumpkin > CurveTypes
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
void parallel_for_range(size_t num_points, const std::function< void(size_t, size_t)> &func, size_t no_multhreading_if_less_or_equal)
Split a loop into several loops running in parallel.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Scratch space for batched affine point additions (one per thread)
Affine bucket accumulators for the fast affine-trick Pippenger variant.
std::vector< AffineElement > buckets