14 const std::span<G1>& points,
const std::vector<size_t>& sequence_counts)
19 std::span<Fq> scratch_space(scratch_space_vector);
22 auto [addition_sequences_, sequence_tags] = construct_thread_data(points, sequence_counts, scratch_space);
23 auto& addition_sequences = addition_sequences_;
25 const size_t num_threads = addition_sequences.size();
26 parallel_for(num_threads, [&](
size_t thread_idx) { batched_affine_add_in_place(addition_sequences[thread_idx]); });
30 reduced_points.reserve(sequence_counts.size());
31 size_t prev_tag = std::numeric_limits<size_t>::max();
32 for (
auto [sequences, tags] :
zip_view(addition_sequences, sequence_tags)) {
34 for (
size_t i = 0; i < sequences.sequence_counts.size(); ++i) {
35 if (tags[i] == prev_tag) {
36 reduced_points.back() = reduced_points.back() + sequences.points[i];
38 reduced_points.emplace_back(sequences.points[i]);
44 return reduced_points;
49 const std::span<G1>& points,
const std::vector<size_t>& sequence_counts,
const std::span<Fq>& scratch_space)
52 std::vector<size_t> sequence_endpoints;
53 sequence_endpoints.reserve(sequence_counts.size());
54 size_t total_count = 0;
55 for (
const auto& count : sequence_counts) {
57 sequence_endpoints.emplace_back(total_count);
60 if (points.size() != total_count) {
61 throw_or_abort(
"Number of input points does not match sequence counts!");
65 const size_t MIN_POINTS_PER_THREAD = 1 << 14;
66 const size_t total_num_points = points.size();
67 const size_t optimal_threads = total_num_points / MIN_POINTS_PER_THREAD;
70 const size_t base_thread_size = total_num_points / num_threads;
71 const size_t leftover_size = total_num_points % num_threads;
72 std::vector<size_t> thread_sizes(num_threads, base_thread_size);
73 for (
size_t i = 0; i < leftover_size; ++i) {
79 thread_points.reserve(num_threads);
81 thread_scratch_space.reserve(num_threads);
82 std::vector<size_t> thread_endpoints;
83 thread_endpoints.reserve(num_threads);
84 size_t point_index = 0;
85 for (
auto size : thread_sizes) {
86 thread_points.push_back(points.subspan(point_index, size));
87 thread_scratch_space.push_back(scratch_space.subspan(point_index, size));
89 thread_endpoints.emplace_back(point_index);
95 std::vector<size_t> all_endpoints;
96 all_endpoints.reserve(thread_endpoints.size() + sequence_endpoints.size());
97 all_endpoints.insert(all_endpoints.end(), thread_endpoints.begin(), thread_endpoints.end());
98 all_endpoints.insert(all_endpoints.end(), sequence_endpoints.begin(), sequence_endpoints.end());
99 std::sort(all_endpoints.begin(), all_endpoints.end());
100 auto last = std::unique(all_endpoints.begin(), all_endpoints.end());
101 all_endpoints.erase(last, all_endpoints.end());
104 size_t prev_endpoint = 0;
105 size_t thread_idx = 0;
106 size_t sequence_idx = 0;
109 for (
auto& endpoint : all_endpoints) {
110 size_t chunk_size = endpoint - prev_endpoint;
111 thread_sequence_counts[thread_idx].emplace_back(chunk_size);
112 thread_sequence_tags[thread_idx].emplace_back(sequence_idx);
113 if (endpoint == thread_endpoints[thread_idx]) {
116 if (endpoint == sequence_endpoints[sequence_idx]) {
119 prev_endpoint = endpoint;
122 if (thread_sequence_counts.size() != thread_points.size()) {
128 addition_sequences.reserve(num_threads);
129 for (
size_t i = 0; i < num_threads; ++i) {
130 addition_sequences.push_back(
141 auto points = add_sequences.
points;
145 size_t total_num_pairs{ 0 };
146 for (
auto& count : sequence_counts) {
147 total_num_pairs += count >> 1;
152 std::span<Fq> denominators = add_sequences.
scratch_space.subspan(0, total_num_pairs);
153 std::span<Fq> differences = add_sequences.
scratch_space.subspan(total_num_pairs, total_num_pairs);
157 size_t point_idx = 0;
159 for (
auto& count : sequence_counts) {
160 const auto num_pairs = count >> 1;
161 for (
size_t j = 0; j < num_pairs; ++j) {
163 const auto& x1 = points[point_idx++].x;
164 const auto& x2 = points[point_idx++].x;
170 differences[pair_idx] = diff;
173 denominators[pair_idx++] = accumulator;
177 point_idx += (count & 0x01ULL);
181 Fq inverse = accumulator.invert();
184 for (
size_t i = 0; i < total_num_pairs; ++i) {
185 size_t idx = total_num_pairs - 1 - i;
186 denominators[idx] *= inverse;
187 inverse *= differences[idx];
196 const size_t num_points = add_sequences.
points.size();
197 if (num_points == 0 || num_points == 1) {
202 std::span<Fq> denominators = batch_compute_point_addition_slope_inverses(add_sequences);
204 auto points = add_sequences.
points;
208 size_t point_idx = 0;
209 size_t result_point_idx = 0;
211 bool more_additions =
false;
212 for (
auto& count : sequence_counts) {
213 const auto num_pairs = count >> 1;
214 const bool overflow =
static_cast<bool>(count & 0x01ULL);
216 for (
size_t j = 0; j < num_pairs; ++j) {
217 const auto& point_1 = points[point_idx++];
218 const auto& point_2 = points[point_idx++];
219 const auto& denominator = denominators[pair_idx++];
220 auto&
result = points[result_point_idx++];
222 result = affine_add_with_denominator(point_1, point_2, denominator);
226 points[result_point_idx++] = points[point_idx++];
230 const uint32_t updated_sequence_count =
static_cast<uint32_t
>(num_pairs) +
static_cast<uint32_t
>(overflow);
231 count = updated_sequence_count;
234 more_additions = more_additions || updated_sequence_count > 1;
238 if (more_additions) {
239 const size_t updated_point_count = result_point_idx;
240 std::span<G1> updated_points(&points[0], updated_point_count);
241 return batched_affine_add_in_place(