Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah_parallel_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
37#include <gtest/gtest.h>
38#include <cstdlib>
39#include <ah-parallel.H>
40#include <vector>
41#include <list>
42#include <deque>
43#include <array>
44#include <string>
45#include <numeric>
46#include <atomic>
47#include <chrono>
48#include <random>
49#include <algorithm>
50#include <cmath>
51
52using namespace Aleph;
53
54// =============================================================================
55// Test Fixture
56// =============================================================================
57
58class ParallelTest : public ::testing::Test
59{
60protected:
62
63 std::vector<int> empty_vec;
64 std::vector<int> single_vec = {42};
65 std::vector<int> small_vec = {1, 2, 3, 4, 5};
66 std::vector<int> large_vec;
67
68 void SetUp() override
69 {
70 large_vec.resize(10000);
71 std::iota(large_vec.begin(), large_vec.end(), 1);
72 }
73};
74
75// =============================================================================
76// pmaps Tests
77// =============================================================================
78
80{
81 auto result = pmaps(pool, small_vec, [](int x) { return x * x; });
82 ASSERT_EQ(result.size(), 5u);
83 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
84}
85
87{
88 auto result = pmaps<double>(pool, small_vec, [](int x) { return x * 1.5; });
89 ASSERT_EQ(result.size(), 5u);
90 EXPECT_NEAR(result[0], 1.5, 0.001);
91 EXPECT_NEAR(result[4], 7.5, 0.001);
92}
93
95{
96 auto result = pmaps(pool, small_vec, [](int x) { return std::to_string(x); });
97 ASSERT_EQ(result.size(), 5u);
98 EXPECT_EQ(result[0], "1");
99 EXPECT_EQ(result[4], "5");
100}
101
103{
104 auto result = pmaps(pool, empty_vec, [](int x) { return x * 2; });
105 EXPECT_TRUE(result.empty());
106}
107
109{
110 auto result = pmaps(pool, single_vec, [](int x) { return x * 2; });
111 ASSERT_EQ(result.size(), 1u);
112 EXPECT_EQ(result[0], 84);
113}
114
116{
117 auto result = pmaps(pool, large_vec, [](int x) { return x * 2; });
118 ASSERT_EQ(result.size(), 10000u);
119 for (size_t i = 0; i < result.size(); ++i)
120 EXPECT_EQ(result[i], static_cast<int>((i + 1) * 2));
121}
122
124{
125 std::list<int> lst = {1, 2, 3, 4, 5};
126 auto result = pmaps(pool, lst, [](int x) { return x * x; });
127 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
128}
129
131{
132 auto result = pmaps(pool, large_vec, [](int x) { return x; });
133 for (size_t i = 0; i < result.size(); ++i)
134 EXPECT_EQ(result[i], static_cast<int>(i + 1));
135}
136
138{
140 options.pool = &pool;
141 options.min_size = large_vec.size() + 1;
142 options.chunk_size = 7;
143 options.max_tasks = 2;
144
145 auto result = pmaps(large_vec, [](int x) { return x + 1; }, options);
146 ASSERT_EQ(result.size(), large_vec.size());
147 EXPECT_EQ(result.front(), 2);
148 EXPECT_EQ(result.back(), 10001);
149}
150
151// =============================================================================
152// pfilter Tests
153// =============================================================================
154
156{
157 auto result = pfilter(pool, small_vec, [](int x) { return x % 2 == 0; });
158 EXPECT_EQ(result, (std::vector<int>{2, 4}));
159}
160
162{
163 auto result = pfilter(pool, small_vec, [](int x) { return x % 2 != 0; });
164 EXPECT_EQ(result, (std::vector<int>{1, 3, 5}));
165}
166
168{
169 auto result = pfilter(pool, small_vec, [](int x) { return x > 100; });
170 EXPECT_TRUE(result.empty());
171}
172
174{
175 auto result = pfilter(pool, small_vec, [](int x) { return x > 0; });
176 EXPECT_EQ(result.size(), 5u);
177}
178
180{
181 auto result = pfilter(pool, empty_vec, [](int) { return true; });
182 EXPECT_TRUE(result.empty());
183}
184
186{
187 std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
188 auto result = pfilter(pool, nums, [](int x) { return x % 3 == 0; });
189 EXPECT_EQ(result, (std::vector<int>{3, 6, 9}));
190}
191
193{
194 auto result = pfilter(pool, large_vec, [](int x) { return x % 100 == 0; });
195 EXPECT_EQ(result.size(), 100u);
196 EXPECT_EQ(result[0], 100);
197 EXPECT_EQ(result[99], 10000);
198}
199
201{
202 CancellationSource source;
204 options.pool = &pool;
205 options.chunk_size = 32;
206 options.cancel_token = source.token();
207
208 EXPECT_THROW(pmaps(large_vec,
209 [&source](int x) {
210 if (x == 128)
211 source.request_cancel();
212 return x * 2;
213 },
214 options),
216}
217
218// =============================================================================
219// pfoldl Tests
220// =============================================================================
221
223{
224 int sum = pfoldl(pool, small_vec, 0, std::plus<int>());
225 EXPECT_EQ(sum, 15);
226}
227
229{
230 int product = pfoldl(pool, small_vec, 1, std::multiplies<int>());
231 EXPECT_EQ(product, 120);
232}
233
235{
236 int max_val = pfoldl(pool, small_vec, INT_MIN,
237 [](int a, int b) { return std::max(a, b); });
238 EXPECT_EQ(max_val, 5);
239}
240
242{
243 int result = pfoldl(pool, empty_vec, 42, std::plus<int>());
244 EXPECT_EQ(result, 42);
245}
246
248{
249 long long sum = pfoldl(pool, large_vec, 0LL,
250 [](long long a, int b) { return a + b; });
251 // Sum 1..10000 = 10000*10001/2 = 50005000
252 EXPECT_EQ(sum, 50005000LL);
253}
254
255// =============================================================================
256// pfor_each Tests
257// =============================================================================
258
260{
261 std::vector<int> data = {1, 2, 3, 4, 5};
262 pfor_each(pool, data, [](int& x) { x *= 2; });
263 EXPECT_EQ(data, (std::vector<int>{2, 4, 6, 8, 10}));
264}
265
267{
268 std::atomic<int> count{0};
269 pfor_each(pool, large_vec, [&count](const int& x) {
270 if (x % 2 == 0) ++count;
271 });
272 EXPECT_EQ(count.load(), 5000);
273}
274
276{
277 std::atomic<int> count{0};
278 pfor_each(pool, empty_vec, [&count](int) { ++count; });
279 EXPECT_EQ(count.load(), 0);
280}
281
283{
284 std::vector<int> data = {1, 2, 3, 4};
287 options.max_tasks = 2;
288
289 pfor_each(data, [](int & x) { x += 5; }, options);
290 EXPECT_EQ(data, (std::vector<int>{6, 7, 8, 9}));
291}
292
293// =============================================================================
294// pall, pexists, pnone Tests
295// =============================================================================
296
298{
299 bool result = pall(pool, small_vec, [](int x) { return x > 0; });
300 EXPECT_TRUE(result);
301}
302
304{
305 bool result = pall(pool, small_vec, [](int x) { return x > 3; });
306 EXPECT_FALSE(result);
307}
308
310{
311 bool result = pall(pool, empty_vec, [](int) { return false; });
312 EXPECT_TRUE(result); // Vacuous truth
313}
314
316{
317 bool result = pexists(pool, small_vec, [](int x) { return x == 3; });
318 EXPECT_TRUE(result);
319}
320
322{
323 bool result = pexists(pool, small_vec, [](int x) { return x > 100; });
324 EXPECT_FALSE(result);
325}
326
328{
329 bool result = pexists(pool, empty_vec, [](int) { return true; });
330 EXPECT_FALSE(result);
331}
332
334{
335 bool result = pnone(pool, small_vec, [](int x) { return x < 0; });
336 EXPECT_TRUE(result);
337}
338
340{
341 bool result = pnone(pool, small_vec, [](int x) { return x == 3; });
342 EXPECT_FALSE(result);
343}
344
345// =============================================================================
346// pcount_if Tests
347// =============================================================================
348
350{
351 size_t count = pcount_if(pool, small_vec, [](int x) { return x % 2 == 0; });
352 EXPECT_EQ(count, 2u);
353}
354
356{
357 size_t count = pcount_if(pool, large_vec, [](int x) { return x % 100 == 0; });
358 EXPECT_EQ(count, 100u);
359}
360
362{
363 size_t count = pcount_if(pool, small_vec, [](int x) { return x > 0; });
364 EXPECT_EQ(count, 5u);
365}
366
368{
369 size_t count = pcount_if(pool, small_vec, [](int x) { return x > 100; });
370 EXPECT_EQ(count, 0u);
371}
372
374{
375 size_t count = pcount_if(pool, large_vec, [](int x) { return x % 7 == 0; });
376 EXPECT_EQ(count, 1428u); // floor(10000/7) = 1428
377}
378
379// =============================================================================
380// pfind Tests
381// =============================================================================
382
384{
385 auto idx = pfind(pool, small_vec, [](int x) { return x == 3; });
386 ASSERT_TRUE(idx.has_value());
387 EXPECT_EQ(*idx, 2u);
388}
389
391{
392 auto idx = pfind(pool, small_vec, [](int x) { return x == 100; });
393 EXPECT_FALSE(idx.has_value());
394}
395
397{
398 std::vector<int> data = {1, 2, 3, 3, 3, 4, 5};
399 auto idx = pfind(pool, data, [](int x) { return x == 3; });
400 ASSERT_TRUE(idx.has_value());
401 EXPECT_EQ(*idx, 2u); // First occurrence
402}
403
405{
406 auto idx = pfind(pool, empty_vec, [](int) { return true; });
407 EXPECT_FALSE(idx.has_value());
408}
409
411{
413 options.pool = &pool;
414 options.chunk_size = 64;
415 options.max_tasks = 4;
416
417 auto idx = pfind(large_vec, [](int x) { return x == 7777; }, options);
418 ASSERT_TRUE(idx.has_value());
419 EXPECT_EQ(*idx, 7776u);
420}
421
423{
424 std::vector<std::string> words = {"apple", "banana", "cherry"};
425 auto found = pfind_value(pool, words,
426 [](const std::string& s) { return s.length() > 5; });
427 ASSERT_TRUE(found.has_value());
428 EXPECT_EQ(*found, "banana");
429}
430
431// =============================================================================
432// psum, pproduct Tests
433// =============================================================================
434
436{
437 auto sum = psum(pool, small_vec);
438 EXPECT_EQ(sum, 15);
439}
440
442{
443 auto sum = psum(pool, small_vec, 100);
444 EXPECT_EQ(sum, 115);
445}
446
448{
449 auto sum = psum(pool, empty_vec);
450 EXPECT_EQ(sum, 0);
451}
452
454{
455 auto sum = psum<std::vector<int>, long long>(pool, large_vec, 0LL);
456 EXPECT_EQ(sum, 50005000LL);
457}
458
460{
461 auto product = pproduct(pool, small_vec);
462 EXPECT_EQ(product, 120);
463}
464
466{
467 auto product = pproduct(pool, empty_vec);
468 EXPECT_EQ(product, 1); // Identity for multiplication
469}
470
471// =============================================================================
472// pmin, pmax, pminmax Tests
473// =============================================================================
474
476{
477 auto min_val = pmin(pool, small_vec);
478 ASSERT_TRUE(min_val.has_value());
479 EXPECT_EQ(*min_val, 1);
480}
481
483{
484 auto max_val = pmax(pool, small_vec);
485 ASSERT_TRUE(max_val.has_value());
486 EXPECT_EQ(*max_val, 5);
487}
488
490{
491 auto min_val = pmin(pool, empty_vec);
492 EXPECT_FALSE(min_val.has_value());
493}
494
496{
497 auto max_val = pmax(pool, empty_vec);
498 EXPECT_FALSE(max_val.has_value());
499}
500
502{
503 auto result = pminmax(pool, small_vec);
504 ASSERT_TRUE(result.has_value());
505 EXPECT_EQ(result->first, 1);
506 EXPECT_EQ(result->second, 5);
507}
508
510{
511 std::vector<int> data = {5, 1, 3, 2, 4};
512 auto result = pminmax(pool, data);
513 ASSERT_TRUE(result.has_value());
514 EXPECT_EQ(result->first, 1);
515 EXPECT_EQ(result->second, 5);
516}
517
519{
520 auto result = pminmax(pool, large_vec);
521 ASSERT_TRUE(result.has_value());
522 EXPECT_EQ(result->first, 1);
523 EXPECT_EQ(result->second, 10000);
524}
525
526// =============================================================================
527// psort Tests
528// =============================================================================
529
531{
532 std::vector<int> data = {5, 2, 8, 1, 9, 3, 7, 4, 6};
533 psort(pool, data);
534 EXPECT_EQ(data, (std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
535}
536
538{
539 std::vector<int> data = {5, 2, 8, 1, 9, 3, 7, 4, 6};
540 psort(pool, data, std::greater<int>());
541 EXPECT_EQ(data, (std::vector<int>{9, 8, 7, 6, 5, 4, 3, 2, 1}));
542}
543
545{
546 std::vector<int> data;
547 psort(pool, data);
548 EXPECT_TRUE(data.empty());
549}
550
552{
553 std::vector<int> data = {42};
554 psort(pool, data);
555 EXPECT_EQ(data[0], 42);
556}
557
559{
560 std::vector<int> data = {1, 2, 3, 4, 5};
561 psort(pool, data);
562 EXPECT_EQ(data, (std::vector<int>{1, 2, 3, 4, 5}));
563}
564
566{
567 std::vector<int> data = large_vec;
568 std::mt19937 rng(42); // Fixed seed for reproducibility
569 std::shuffle(data.begin(), data.end(), rng);
570 psort(pool, data);
571 for (size_t i = 0; i < data.size(); ++i)
572 EXPECT_EQ(data[i], static_cast<int>(i + 1));
573}
574
576{
577 std::vector<int> data = {7, 1, 5, 9, 3, 2};
579 options.pool = &pool;
580 options.min_size = 2;
581 options.max_tasks = 2;
582 psort(data, std::less<int>{}, options);
583 EXPECT_EQ(data, (std::vector<int>{1, 2, 3, 5, 7, 9}));
584}
585
587{
588 std::vector<int> data(4096);
589 std::iota(data.begin(), data.end(), 0);
590 std::mt19937 rng(77);
591 std::shuffle(data.begin(), data.end(), rng);
592
593 CancellationSource source;
595 options.pool = &pool;
596 options.min_size = 64;
597 options.chunk_size = 64;
598 options.cancel_token = source.token();
599 source.request_cancel();
600
601 EXPECT_THROW(psort(data, std::less<int>{}, options), operation_canceled);
602}
603
604// =============================================================================
605// pzip_for_each Tests
606// =============================================================================
607
609{
610 std::vector<int> a = {1, 2, 3};
611 std::vector<int> b = {4, 5, 6};
612 std::atomic<int> sum{0};
613
614 pzip_for_each(pool, a, b, [&sum](int x, int y) { sum += x * y; });
615 EXPECT_EQ(sum.load(), 32); // 1*4 + 2*5 + 3*6
616}
617
619{
620 std::vector<int> a = {1, 2, 3, 4, 5};
621 std::vector<int> b = {10, 20, 30};
622 std::atomic<int> sum{0};
623
624 pzip_for_each(pool, a, b, [&sum](int x, int y) { sum += x + y; });
625 EXPECT_EQ(sum.load(), 66); // (1+10) + (2+20) + (3+30)
626}
627
629{
630 std::vector<int> a;
631 std::vector<int> b = {1, 2, 3};
632 std::atomic<int> count{0};
633
634 pzip_for_each(pool, a, b, [&count](int, int) { ++count; });
635 EXPECT_EQ(count.load(), 0);
636}
637
638// =============================================================================
639// pzip_maps Tests
640// =============================================================================
641
643{
644 std::vector<int> a = {1, 2, 3};
645 std::vector<int> b = {4, 5, 6};
646
647 auto result = pzip_maps(pool, a, b, [](int x, int y) { return x * y; });
648 EXPECT_EQ(result, (std::vector<int>{4, 10, 18}));
649}
650
652{
653 std::vector<int> a = {1, 2, 3};
654 std::vector<double> b = {1.5, 2.5, 3.5};
655
656 auto result = pzip_maps(pool, a, b, [](int x, double y) { return x + y; });
657 EXPECT_NEAR(result[0], 2.5, 0.001);
658 EXPECT_NEAR(result[1], 4.5, 0.001);
659 EXPECT_NEAR(result[2], 6.5, 0.001);
660}
661
663{
664 std::vector<int> a = {1, 2, 3, 4};
665 std::vector<int> b = {10, 20, 30, 40};
667 options.pool = &pool;
668 options.chunk_size = 2;
669
670 auto mapped = pzip_maps(a, b, [](int x, int y) { return x + y; }, options);
671 EXPECT_EQ(mapped, (std::vector<int>{11, 22, 33, 44}));
672
673 auto folded = pzip_foldl(a, b, 0, [](int acc, int x, int y) {
674 return acc + x * y;
675 }, options);
676 EXPECT_EQ(folded, 300);
677
678 std::atomic<int> visits{0};
679 pzip_for_each(a, b, [&visits](int, int) { ++visits; }, options);
680 EXPECT_EQ(visits.load(), 4);
681}
682
683// =============================================================================
684// ppartition Tests
685// =============================================================================
686
688{
689 auto [evens, odds] = ppartition(pool, small_vec, [](int x) { return x % 2 == 0; });
690 EXPECT_EQ(evens, (std::vector<int>{2, 4}));
691 EXPECT_EQ(odds, (std::vector<int>{1, 3, 5}));
692}
693
695{
696 auto [yes, no] = ppartition(pool, small_vec, [](int x) { return x > 0; });
697 EXPECT_EQ(yes.size(), 5u);
698 EXPECT_TRUE(no.empty());
699}
700
702{
703 auto [yes, no] = ppartition(pool, small_vec, [](int x) { return x < 0; });
704 EXPECT_TRUE(yes.empty());
705 EXPECT_EQ(no.size(), 5u);
706}
707
709{
710 auto [yes, no] = ppartition(pool, empty_vec, [](int) { return true; });
711 EXPECT_TRUE(yes.empty());
712 EXPECT_TRUE(no.empty());
713}
714
716{
717 std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
718 auto [evens, odds] = ppartition(pool, data, [](int x) { return x % 2 == 0; });
719 EXPECT_EQ(evens, (std::vector<int>{2, 4, 6, 8, 10}));
720 EXPECT_EQ(odds, (std::vector<int>{1, 3, 5, 7, 9}));
721}
722
724{
726 options.pool = &pool;
727 options.chunk_size = 3;
728
729 std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8};
730 auto [evens, odds] = ppartition(data, [](int x) { return x % 2 == 0; }, options);
731 EXPECT_EQ(evens, (std::vector<int>{2, 4, 6, 8}));
732 EXPECT_EQ(odds, (std::vector<int>{1, 3, 5, 7}));
733}
734
736{
737 struct NoDefault
738 {
739 int value;
740
741 NoDefault() = delete;
742 explicit NoDefault(int v) : value(v) {}
743 NoDefault(const NoDefault &) = default;
744 NoDefault(NoDefault &&) noexcept = default;
745 NoDefault & operator=(const NoDefault &) = default;
746 NoDefault & operator=(NoDefault &&) noexcept = default;
747 };
748
749 std::vector<NoDefault> data;
750 for (int i = 1; i <= 6; ++i)
751 data.emplace_back(i);
752
753 auto [small, large] = ppartition(pool, data, [](const NoDefault & x) {
754 return x.value <= 3;
755 });
756
757 ASSERT_EQ(small.size(), 3u);
758 ASSERT_EQ(large.size(), 3u);
759 EXPECT_EQ(small[0].value, 1);
760 EXPECT_EQ(small[1].value, 2);
761 EXPECT_EQ(small[2].value, 3);
762 EXPECT_EQ(large[0].value, 4);
763 EXPECT_EQ(large[1].value, 5);
764 EXPECT_EQ(large[2].value, 6);
765}
766
768{
769 CancellationSource source;
771 options.pool = &pool;
772 options.chunk_size = 16;
773 options.cancel_token = source.token();
774 source.request_cancel();
775
776 std::vector<int> data(512, 1);
777 EXPECT_THROW((void) ppartition(data, [](int x) { return x % 2 == 0; }, options),
779}
780
782{
783 struct NoDefault
784 {
785 int value;
786
787 NoDefault() = delete;
788 explicit NoDefault(int v) : value(v) {}
789 NoDefault(const NoDefault &) = default;
790 NoDefault(NoDefault &&) noexcept = default;
791 NoDefault & operator=(const NoDefault &) = default;
792 NoDefault & operator=(NoDefault &&) noexcept = default;
793 };
794
795 std::vector<NoDefault> data;
796 for (int i = 1; i <= 8; ++i)
797 data.emplace_back(i);
798
799 auto result = pfilter(pool, data, [](const NoDefault & x) {
800 return x.value % 2 == 0;
801 });
802
803 ASSERT_EQ(result.size(), 4u);
804 EXPECT_EQ(result[0].value, 2);
805 EXPECT_EQ(result[1].value, 4);
806 EXPECT_EQ(result[2].value, 6);
807 EXPECT_EQ(result[3].value, 8);
808}
809
810// =============================================================================
811// pscan / pexclusive_scan / pmerge Tests
812// =============================================================================
813
815{
816 std::vector<int> data(256);
817 std::iota(data.begin(), data.end(), 1);
818 std::vector<int> expected(data.size());
819 std::partial_sum(data.begin(), data.end(), expected.begin());
820
821 auto result = pscan(pool, data, std::plus<int>{});
822 EXPECT_EQ(result, expected);
823}
824
826{
827 std::vector<int> data = {1, 2, 3, 4, 5};
828 auto result = pexclusive_scan(pool, data, 0, std::plus<int>{});
829 EXPECT_EQ(result, (std::vector<int>{0, 1, 3, 6, 10}));
830}
831
833{
835 options.pool = &pool;
836 options.chunk_size = 8;
837
838 std::vector<int> data = {3, 1, 4, 1, 5, 9};
839 auto inclusive = pscan(data, std::plus<int>{}, options);
840 EXPECT_EQ(inclusive, (std::vector<int>{3, 4, 8, 9, 14, 23}));
841
842 auto exclusive = pexclusive_scan(data, 10, std::plus<int>{}, options);
843 EXPECT_EQ(exclusive, (std::vector<int>{10, 13, 14, 18, 19, 24}));
844
845 std::vector<int> left = {1, 3, 5, 7};
846 std::vector<int> right = {2, 4, 6, 8};
847 auto merged = pmerge(left, right, std::less<int>{}, options);
848 EXPECT_EQ(merged, (std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8}));
849}
850
852{
853 CancellationSource source;
855 options.pool = &pool;
856 options.chunk_size = 16;
857 options.cancel_token = source.token();
858 source.request_cancel();
859
860 std::vector<int> data(512, 1);
861 EXPECT_THROW((void) pscan(data, std::plus<int>{}, options), operation_canceled);
862 EXPECT_THROW((void) pexclusive_scan(data, 0, std::plus<int>{}, options),
864
865 std::vector<int> left(256), right(256);
866 std::iota(left.begin(), left.end(), 0);
867 std::iota(right.begin(), right.end(), 0);
868 EXPECT_THROW((void) pmerge(left, right, std::less<int>{}, options),
870}
871
873{
874 struct NoDefault
875 {
876 int value;
877
878 NoDefault() = delete;
879 explicit NoDefault(int v) : value(v) {}
880 NoDefault(const NoDefault &) = default;
881 NoDefault(NoDefault &&) noexcept = default;
882 NoDefault & operator=(const NoDefault &) = default;
883 NoDefault & operator=(NoDefault &&) noexcept = default;
884 };
885
886 std::vector<NoDefault> data;
887 for (int i = 1; i <= 4; ++i)
888 data.emplace_back(i);
889
890 auto inclusive = pscan(data,
891 [](const NoDefault & a, const NoDefault & b) {
892 return NoDefault(a.value + b.value);
893 });
894 ASSERT_EQ(inclusive.size(), 4u);
895 EXPECT_EQ(inclusive[0].value, 1);
896 EXPECT_EQ(inclusive[1].value, 3);
897 EXPECT_EQ(inclusive[2].value, 6);
898 EXPECT_EQ(inclusive[3].value, 10);
899
900 auto exclusive = pexclusive_scan(data, NoDefault(0),
901 [](const NoDefault & a, const NoDefault & b) {
902 return NoDefault(a.value + b.value);
903 });
904 ASSERT_EQ(exclusive.size(), 4u);
905 EXPECT_EQ(exclusive[0].value, 0);
906 EXPECT_EQ(exclusive[1].value, 1);
907 EXPECT_EQ(exclusive[2].value, 3);
908 EXPECT_EQ(exclusive[3].value, 6);
909
910 std::vector<NoDefault> left, right;
911 left.emplace_back(1);
912 left.emplace_back(3);
913 left.emplace_back(5);
914 right.emplace_back(2);
915 right.emplace_back(4);
916 right.emplace_back(6);
917
918 auto merged = pmerge(left, right,
919 [](const NoDefault & a, const NoDefault & b) {
920 return a.value < b.value;
921 });
922 ASSERT_EQ(merged.size(), 6u);
923 for (size_t i = 0; i < merged.size(); ++i)
924 EXPECT_EQ(merged[i].value, static_cast<int>(i + 1));
925}
926
927// =============================================================================
928// Correctness: Sequential vs Parallel Comparison
929// =============================================================================
930
932{
933 // Sequential
934 std::vector<int> seq_result;
935 for (int x : large_vec)
936 seq_result.push_back(x * 2 + 1);
937
938 // Parallel
939 auto par_result = pmaps(pool, large_vec, [](int x) { return x * 2 + 1; });
940
942}
943
945{
946 // Sequential
947 std::vector<int> seq_result;
948 for (int x : large_vec)
949 if (x % 17 == 0)
950 seq_result.push_back(x);
951
952 // Parallel
953 auto par_result = pfilter(pool, large_vec, [](int x) { return x % 17 == 0; });
954
956}
957
959{
960 // Sequential
961 long long seq_sum = 0;
962 for (int x : large_vec)
963 seq_sum += x;
964
965 // Parallel
966 long long par_sum = pfoldl(pool, large_vec, 0LL,
967 [](long long a, int b) { return a + b; });
968
970}
971
972// =============================================================================
973// Performance Benchmarks
974// =============================================================================
975
976namespace
977{
979 {
980 return std::getenv("ALEPH_RUN_PARALLEL_BENCHMARKS") != nullptr;
981 }
982}
983
985{
987 GTEST_SKIP() << "Skipping timing-sensitive parallel benchmarks; "
988 << "set ALEPH_RUN_PARALLEL_BENCHMARKS=1 to enable";
989
990 std::vector<int> data(1000000);
991 std::iota(data.begin(), data.end(), 0);
992
993 // Sequential
994 auto seq_start = std::chrono::high_resolution_clock::now();
995 std::vector<int> seq_result;
996 seq_result.reserve(data.size());
997 for (int x : data)
998 seq_result.push_back(x * x + 2 * x + 1);
999 auto seq_end = std::chrono::high_resolution_clock::now();
1000 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
1001 seq_end - seq_start).count();
1002
1003 // Parallel
1004 auto par_start = std::chrono::high_resolution_clock::now();
1005 auto par_result = pmaps(pool, data, [](int x) { return x * x + 2 * x + 1; });
1006 auto par_end = std::chrono::high_resolution_clock::now();
1007 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
1008 par_end - par_start).count();
1009
1011
1012 double speedup = static_cast<double>(seq_duration) / par_duration;
1013 std::cout << "\n=== Benchmark: pmaps ===\n";
1014 std::cout << "Data size: 1M elements\n";
1015 std::cout << "Sequential: " << seq_duration << " μs\n";
1016 std::cout << "Parallel: " << par_duration << " μs\n";
1017 std::cout << "Speedup: " << speedup << "x\n";
1018 std::cout << "========================\n\n";
1019
1020 // With multiple threads, parallel overhead may exceed benefits for trivial ops
1021 // On CI machines with limited cores or high load, speedup can be < 1.0
1022 // Use very relaxed threshold - this test validates correctness, not performance
1023 // Performance is validated by filter/sort benchmarks which have higher per-element cost
1024 if (pool.num_threads() > 1)
1025 EXPECT_GT(speedup, 0.3); // Very relaxed: just ensure parallelization isn't broken
1026}
1027
1029{
1031 GTEST_SKIP() << "Skipping timing-sensitive parallel benchmarks; "
1032 << "set ALEPH_RUN_PARALLEL_BENCHMARKS=1 to enable";
1033
1034 std::vector<int> data(1000000);
1035 std::iota(data.begin(), data.end(), 0);
1036
1037 auto pred = [](int x) { return x % 7 == 0 || x % 11 == 0; };
1038
1039 // Sequential
1040 auto seq_start = std::chrono::high_resolution_clock::now();
1041 std::vector<int> seq_result;
1042 for (int x : data)
1043 if (pred(x))
1044 seq_result.push_back(x);
1045 auto seq_end = std::chrono::high_resolution_clock::now();
1046 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
1047 seq_end - seq_start).count();
1048
1049 // Parallel
1050 auto par_start = std::chrono::high_resolution_clock::now();
1051 auto par_result = pfilter(pool, data, pred);
1052 auto par_end = std::chrono::high_resolution_clock::now();
1053 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
1054 par_end - par_start).count();
1055
1057
1058 double speedup = static_cast<double>(seq_duration) / par_duration;
1059 std::cout << "\n=== Benchmark: pfilter ===\n";
1060 std::cout << "Data size: 1M elements\n";
1061 std::cout << "Sequential: " << seq_duration << " μs\n";
1062 std::cout << "Parallel: " << par_duration << " μs\n";
1063 std::cout << "Speedup: " << speedup << "x\n";
1064 std::cout << "==========================\n\n";
1065}
1066
1068{
1070 GTEST_SKIP() << "Skipping timing-sensitive parallel benchmarks; "
1071 << "set ALEPH_RUN_PARALLEL_BENCHMARKS=1 to enable";
1072
1073 std::vector<int> data(100000);
1074 std::iota(data.begin(), data.end(), 0);
1075 std::mt19937 rng(123); // Fixed seed for reproducibility
1076 std::shuffle(data.begin(), data.end(), rng);
1077
1078 std::vector<int> data_copy = data;
1079
1080 // Sequential
1081 auto seq_start = std::chrono::high_resolution_clock::now();
1082 std::sort(data.begin(), data.end());
1083 auto seq_end = std::chrono::high_resolution_clock::now();
1084 auto seq_duration = std::chrono::duration_cast<std::chrono::microseconds>(
1085 seq_end - seq_start).count();
1086
1087 // Parallel
1088 auto par_start = std::chrono::high_resolution_clock::now();
1089 psort(pool, data_copy);
1090 auto par_end = std::chrono::high_resolution_clock::now();
1091 auto par_duration = std::chrono::duration_cast<std::chrono::microseconds>(
1092 par_end - par_start).count();
1093
1094 EXPECT_EQ(data, data_copy);
1095
1096 double speedup = static_cast<double>(seq_duration) / par_duration;
1097 std::cout << "\n=== Benchmark: psort ===\n";
1098 std::cout << "Data size: 100K elements\n";
1099 std::cout << "std::sort: " << seq_duration << " μs\n";
1100 std::cout << "psort: " << par_duration << " μs\n";
1101 std::cout << "Speedup: " << speedup << "x\n";
1102 std::cout << "========================\n\n";
1103}
1104
1105// =============================================================================
1106// Thread Safety Tests
1107// =============================================================================
1108
1110{
1111 // Run multiple parallel operations simultaneously
1112 auto future1 = std::async(std::launch::async, [this]() {
1113 return pmaps(pool, large_vec, [](int x) { return x * 2; });
1114 });
1115
1116 auto future2 = std::async(std::launch::async, [this]() {
1117 return pfilter(pool, large_vec, [](int x) { return x % 2 == 0; });
1118 });
1119
1120 auto future3 = std::async(std::launch::async, [this]() {
1121 return psum<std::vector<int>, long long>(pool, large_vec, 0LL);
1122 });
1123
1124 auto result1 = future1.get();
1125 auto result2 = future2.get();
1126 auto result3 = future3.get();
1127
1128 EXPECT_EQ(result1.size(), 10000u);
1129 EXPECT_EQ(result2.size(), 5000u);
1130 EXPECT_EQ(result3, 50005000LL);
1131}
1132
1133// =============================================================================
1134// Edge Cases
1135// =============================================================================
1136
1138{
1140
1141 auto result = pmaps(single_pool, small_vec, [](int x) { return x * x; });
1142 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
1143}
1144
1146{
1148
1149 auto result = pmaps(many_pool, small_vec, [](int x) { return x * x; });
1150 EXPECT_EQ(result, (std::vector<int>{1, 4, 9, 16, 25}));
1151}
1152
1154{
1155 std::vector<int> huge(100000);
1156 std::iota(huge.begin(), huge.end(), 0);
1157
1158 auto sum = psum<std::vector<int>, long long>(pool, huge, 0LL);
1159 // Sum 0..99999 = 99999*100000/2 = 4999950000
1160 EXPECT_EQ(sum, 4999950000LL);
1161}
1162
1163// =============================================================================
1164// Variadic Zip Tests (N containers)
1165// =============================================================================
1166
1168{
1169 std::vector<int> a = {1, 2, 3};
1170 std::vector<int> b = {4, 5, 6};
1171 std::vector<int> c = {7, 8, 9};
1172 std::atomic<int> sum{0};
1173
1174 pzip_for_each_n(pool, [&sum](int x, int y, int z) {
1175 sum += x + y + z;
1176 }, a, b, c);
1177
1178 EXPECT_EQ(sum.load(), 45); // (1+4+7) + (2+5+8) + (3+6+9)
1179}
1180
1182{
1183 std::vector<int> a = {1, 2};
1184 std::vector<int> b = {3, 4};
1185 std::vector<int> c = {5, 6};
1186 std::vector<int> d = {7, 8};
1187 std::atomic<int> sum{0};
1188
1189 pzip_for_each_n(pool, [&sum](int w, int x, int y, int z) {
1190 sum += w * x * y * z;
1191 }, a, b, c, d);
1192
1193 EXPECT_EQ(sum.load(), 1*3*5*7 + 2*4*6*8); // 105 + 384 = 489
1194}
1195
1197{
1198 std::vector<int> a = {1, 2, 3, 4, 5};
1199 std::vector<int> b = {10, 20, 30};
1200 std::vector<int> c = {100, 200, 300, 400};
1201 std::atomic<int> count{0};
1202
1203 pzip_for_each_n(pool, [&count](int, int, int) {
1204 ++count;
1205 }, a, b, c);
1206
1207 EXPECT_EQ(count.load(), 3); // Min length
1208}
1209
1211{
1212 std::vector<int> a = {1, 2, 3};
1213 std::vector<int> b = {4, 5, 6};
1214 std::vector<int> c = {7, 8, 9};
1215
1216 auto result = pzip_maps_n(pool, [](int x, int y, int z) {
1217 return x + y + z;
1218 }, a, b, c);
1219
1220 EXPECT_EQ(result, (std::vector<int>{12, 15, 18}));
1221}
1222
1224{
1225 std::vector<int> a;
1226 std::vector<int> b = {1, 2, 3};
1227
1228 auto result = pzip_maps_n(pool, [](int x, int y) {
1229 return x + y;
1230 }, a, b);
1231
1232 EXPECT_TRUE(result.empty());
1233}
1234
1236{
1237 std::vector<int> a = {1, 2, 3};
1238 std::vector<int> b = {4, 5, 6};
1239
1240 // Dot product using variadic version with combiner
1241 int dot = pzip_foldl_n(pool, 0,
1242 [](int acc, int x, int y) { return acc + x * y; },
1243 std::plus<int>(),
1244 a, b);
1245
1246 EXPECT_EQ(dot, 32); // 1*4 + 2*5 + 3*6
1247}
1248
1250{
1251 std::vector<int> a = {1, 2, 3};
1252 std::vector<int> b = {2, 3, 4};
1253 std::vector<int> c = {3, 4, 5};
1254
1255 bool result = pzip_all_n(pool, [](int x, int y, int z) {
1256 return x < y && y < z;
1257 }, a, b, c);
1258
1259 EXPECT_TRUE(result);
1260}
1261
1263{
1264 std::vector<int> a = {1, 2, 3};
1265 std::vector<int> b = {2, 3, 4};
1266 std::vector<int> c = {3, 2, 5}; // 2 < 3 is true, but 3 < 2 is false
1267
1268 bool result = pzip_all_n(pool, [](int x, int y, int z) {
1269 return x < y && y < z;
1270 }, a, b, c);
1271
1272 EXPECT_FALSE(result);
1273}
1274
1276{
1277 std::vector<int> a = {1, 2, 3};
1278 std::vector<int> b = {4, 2, 6};
1279
1280 bool result = pzip_exists_n(pool, [](int x, int y) {
1281 return x == y;
1282 }, a, b);
1283
1284 EXPECT_TRUE(result); // 2 == 2
1285}
1286
1288{
1289 std::vector<int> a = {1, 2, 3};
1290 std::vector<int> b = {4, 5, 6};
1291
1292 bool result = pzip_exists_n(pool, [](int x, int y) {
1293 return x == y;
1294 }, a, b);
1295
1296 EXPECT_FALSE(result);
1297}
1298
1300{
1301 std::vector<int> a = {1, 2, 3, 4, 5};
1302 std::vector<int> b = {5, 4, 3, 2, 1};
1303
1304 size_t count = pzip_count_if_n(pool, [](int x, int y) {
1305 return x + y == 6;
1306 }, a, b);
1307
1308 EXPECT_EQ(count, 5u); // All pairs sum to 6
1309}
1310
1312{
1313 std::vector<int> a = {1, 2, 3, 4, 5};
1314 std::vector<int> b = {1, 2, 3, 4, 5};
1315
1316 size_t count = pzip_count_if_n(pool, [](int x, int y) {
1317 return x == y && x % 2 == 0;
1318 }, a, b);
1319
1320 EXPECT_EQ(count, 2u); // 2 and 4
1321}
1322
1324{
1325 std::vector<int> a = {1, 2, 3};
1326 std::vector<int> b = {4, 5, 6};
1327 std::vector<int> c = {7, 8, 9};
1329 options.pool = &pool;
1330 options.chunk_size = 1;
1331
1332 auto mapped = pzip_maps_n([](int x, int y, int z) { return x + y + z; },
1333 options, a, b, c);
1334 EXPECT_EQ(mapped, (std::vector<int>{12, 15, 18}));
1335
1336 auto folded = pzip_foldl_n(0,
1337 [](int acc, int x, int y, int z) {
1338 return acc + x + y + z;
1339 },
1340 std::plus<int>{},
1341 options, a, b, c);
1342 EXPECT_EQ(folded, 45);
1343
1344 EXPECT_TRUE(pzip_all_n([](int x, int y, int z) { return x < y && y < z; },
1345 options, a, b, c));
1346 EXPECT_TRUE(pzip_exists_n([](int x, int y, int z) { return x + y + z == 15; },
1347 options, a, b, c));
1348 EXPECT_EQ(pzip_count_if_n([](int x, int y, int z) { return x + y + z > 14; },
1349 options, a, b, c),
1350 2u);
1351}
1352
1353// =============================================================================
1354// Enumerate Tests
1355// =============================================================================
1356
1358{
1359 std::vector<int> data(100, 0);
1360
1361 penumerate_for_each(pool, data, [](size_t i, int& x) {
1362 x = static_cast<int>(i * 2);
1363 });
1364
1365 for (size_t i = 0; i < data.size(); ++i)
1366 EXPECT_EQ(data[i], static_cast<int>(i * 2));
1367}
1368
1370{
1371 const std::vector<int> data = {10, 20, 30, 40, 50};
1372 std::atomic<int> weighted_sum{0};
1373
1374 penumerate_for_each(pool, data, [&weighted_sum](size_t i, int x) {
1375 weighted_sum += static_cast<int>(i) * x;
1376 });
1377
1378 // 0*10 + 1*20 + 2*30 + 3*40 + 4*50 = 0 + 20 + 60 + 120 + 200 = 400
1379 EXPECT_EQ(weighted_sum.load(), 400);
1380}
1381
1383{
1384 std::vector<std::string> words = {"a", "bb", "ccc"};
1385
1386 auto result = penumerate_maps(pool, words,
1387 [](size_t i, const std::string& s) {
1388 return std::to_string(i) + ":" + s;
1389 });
1390
1391 EXPECT_EQ(result[0], "0:a");
1392 EXPECT_EQ(result[1], "1:bb");
1393 EXPECT_EQ(result[2], "2:ccc");
1394}
1395
1397{
1398 std::vector<int> empty;
1399
1400 auto result = penumerate_maps(pool, empty,
1401 [](size_t i, int x) { return static_cast<int>(i + x); });
1402
1403 EXPECT_TRUE(result.empty());
1404}
1405
1407{
1408 std::vector<int> data(6, 0);
1410 options.pool = &pool;
1411 options.chunk_size = 2;
1412
1413 penumerate_for_each(data, [](size_t i, int & x) {
1414 x = static_cast<int>(i + 10);
1415 }, options);
1416 EXPECT_EQ(data, (std::vector<int>{10, 11, 12, 13, 14, 15}));
1417
1418 auto labels = penumerate_maps(data, [](size_t i, int x) {
1419 return std::to_string(i) + "=" + std::to_string(x);
1420 }, options);
1421 EXPECT_EQ(labels[0], "0=10");
1422 EXPECT_EQ(labels[5], "5=15");
1423}
1424
1426{
1427 std::vector<int> a(256, 1), b(256, 2), c(256, 3);
1428 CancellationSource source;
1430 options.pool = &pool;
1431 options.chunk_size = 16;
1432 options.cancel_token = source.token();
1433 source.request_cancel();
1434
1435 EXPECT_THROW((void) pzip_maps(a, b, [](int x, int y) { return x + y; }, options),
1437 EXPECT_THROW((void) pzip_maps_n([](int x, int y, int z) { return x + y + z; },
1438 options, a, b, c),
1440 EXPECT_THROW((void) penumerate_maps(a, [](size_t i, int x) {
1441 return static_cast<int>(i) + x;
1442 }, options),
1444}
1445
1446// =============================================================================
1447// Large Data Variadic Tests
1448// =============================================================================
1449
1451{
1452 std::vector<int> a(10000), b(10000), c(10000);
1453 std::iota(a.begin(), a.end(), 0);
1454 std::iota(b.begin(), b.end(), 10000);
1455 std::iota(c.begin(), c.end(), 20000);
1456
1457 std::atomic<long long> sum{0};
1458
1459 pzip_for_each_n(pool, [&sum](int x, int y, int z) {
1460 sum += x + y + z;
1461 }, a, b, c);
1462
1463 // Sum of 0..9999 + 10000..19999 + 20000..29999
1464 // = 3 * (0+1+...+9999) + 10000*10000 + 20000*10000
1465 // = 3 * 49995000 + 100000000 + 200000000
1466 // = 149985000 + 300000000 = 449985000
1467 EXPECT_EQ(sum.load(), 449985000LL);
1468}
1469
1471{
1472 std::vector<int> a(10000), b(10000);
1473 std::iota(a.begin(), a.end(), 0);
1474 std::iota(b.begin(), b.end(), 0);
1475
1476 auto result = pzip_maps_n(pool, [](int x, int y) {
1477 return x * y;
1478 }, a, b);
1479
1480 for (size_t i = 0; i < result.size(); ++i)
1481 EXPECT_EQ(result[i], static_cast<int>(i * i));
1482}
1483
1485{
1486 std::vector<int> a(1000), b(1000), c(1000);
1487 std::iota(a.begin(), a.end(), 0);
1488 std::iota(b.begin(), b.end(), 1000);
1489 std::iota(c.begin(), c.end(), 2000);
1490
1491 // Sequential
1492 std::vector<int> seq_result;
1493 for (size_t i = 0; i < a.size(); ++i)
1494 seq_result.push_back(a[i] + b[i] + c[i]);
1495
1496 // Parallel
1497 auto par_result = pzip_maps_n(pool, [](int x, int y, int z) {
1498 return x + y + z;
1499 }, a, b, c);
1500
1502}
1503
1504int main(int argc, char **argv)
1505{
1506 ::testing::InitGoogleTest(&argc, argv);
1507 return RUN_ALL_TESTS();
1508}
Parallel functional programming operations using ThreadPool.
TEST_F(ParallelTest, PmapsSquare)
long double w
Definition btreepic.C:153
Cooperative cancellation source paired with CancellationToken.
CancellationToken token() const noexcept
Return a token observing this source.
void request_cancel() noexcept
Request cancellation for all derived tokens.
A reusable thread pool for efficient parallel task execution.
Exception thrown when cooperative cancellation is observed.
std::vector< int > single_vec
void SetUp() override
std::vector< int > empty_vec
std::vector< int > small_vec
std::vector< int > large_vec
static mt19937 rng
Freq_Node * pred
Predecessor node in level-order traversal.
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
bool pall(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel all predicate (short-circuit).
bool pnone(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel none predicate.
auto pmaps(ThreadPool &pool, const Container &c, Op op, size_t chunk_size=0)
Parallel map operation.
auto pmin(ThreadPool &pool, const Container &c, size_t chunk_size=0)
Parallel minimum element.
T pzip_foldl_n(ThreadPool &pool, T init, Op op, Combiner combiner, const Containers &... cs)
Parallel fold/reduce over N zipped containers (variadic).
T pzip_foldl(ThreadPool &pool, const Container1 &c1, const Container2 &c2, T init, Op op, size_t chunk_size=0)
Parallel zip + fold.
void pzip_for_each(ThreadPool &pool, const Container1 &c1, const Container2 &c2, Op op, size_t chunk_size=0)
Parallel zip + for_each.
auto penumerate_maps(ThreadPool &pool, const Container &c, Op op, size_t chunk_size=0)
Parallel enumerate with map.
void penumerate_for_each(ThreadPool &pool, Container &c, Op op, size_t chunk_size=0)
Parallel for_each with index (enumerate).
bool pzip_all_n(ThreadPool &pool, Pred pred, const Containers &... cs)
Parallel all predicate over N zipped containers (variadic).
size_t pcount_if(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel count_if operation.
auto pexclusive_scan(ThreadPool &pool, const Container &c, T init, BinaryOp op, size_t chunk_size=0)
Parallel exclusive scan over a container.
std::optional< size_t > pfind(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel find operation (returns index).
bool pzip_exists_n(ThreadPool &pool, Pred pred, const Containers &... cs)
Parallel exists predicate over N zipped containers (variadic).
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
void psort(ThreadPool &pool, Container &c, Compare cmp=Compare{}, const size_t min_parallel_size=1024)
Parallel sort (in-place).
void pfor_each(ThreadPool &pool, Container &c, Op op, size_t chunk_size=0)
Parallel for_each operation.
auto pfilter(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel filter operation.
auto ppartition(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel partition (stable).
auto pmerge(ThreadPool &pool, const Container1 &c1, const Container2 &c2, Compare comp=Compare{}, size_t chunk_size=0)
Parallel merge of two sorted containers.
T pproduct(ThreadPool &pool, const Container &c, T init=T{1}, size_t chunk_size=0)
Parallel product of elements.
T product(const Container &container, const T &init=T{1})
Compute product of all elements.
auto pmax(ThreadPool &pool, const Container &c, size_t chunk_size=0)
Parallel maximum element.
void pzip_for_each_n(ThreadPool &pool, Op op, const Containers &... cs)
Parallel for_each over N zipped containers (variadic).
auto pzip_maps_n(ThreadPool &pool, Op op, const Containers &... cs)
Parallel map over N zipped containers (variadic).
auto pscan(ThreadPool &pool, const Container &c, BinaryOp op, size_t chunk_size=0)
Parallel inclusive scan over a container.
T pfoldl(ThreadPool &pool, const Container &c, T init, BinaryOp op, size_t chunk_size=0)
Parallel left fold (reduce).
auto pfind_value(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel find with value return.
auto pzip_maps(ThreadPool &pool, const Container1 &c1, const Container2 &c2, Op op, size_t chunk_size=0)
Parallel zip + map.
T psum(ThreadPool &pool, const Container &c, T init=T{}, size_t chunk_size=0)
Parallel sum of elements.
auto pminmax(ThreadPool &pool, const Container &c, size_t chunk_size=0)
Parallel min and max elements.
size_t pzip_count_if_n(ThreadPool &pool, Pred pred, const Containers &... cs)
Parallel count over N zipped containers (variadic).
bool pexists(ThreadPool &pool, const Container &c, Pred pred, size_t chunk_size=0)
Parallel exists predicate (short-circuit).
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
static struct argp_option options[]
Definition ntreepic.C:1886
#define LL
Definition ran_array.c:24
Common configuration object for parallel algorithms.
size_t chunk_size
Elements per chunk (0 = auto).
ThreadPool * pool
Executor to use (nullptr = default_pool()).