These helper functions create views into an existing sequence. They do not copy elements; instead, they return lightweight objects that refer to the original sequence. Note that this means the original sequences must still be alive, or these views will be dangling, and using them would be undefined behavior.
subrange |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
subrange returns a view of the elements of S with indices in the half-open range
[i, j).
par::array_sequence a{0, 1, 2, 3, 4, 5};
auto x = subrange(a, 1, 4); // x == {1, 2, 3}
subseq |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
subseq returns a view of k elements of S, starting at index i.
This is equivalent to subrange(S, i, i + k).
par::array_sequence a{0, 1, 2, 3, 4, 5};
auto x = subseq(a, 2, 3); // x == {2, 3, 4}
split_mid |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
split_mid splits a sequence into two subranges consisting of the first
|S|/2 elements and the remaining elements, and returns them as a pair.
par::array_sequence a{0, 1, 2, 3, 4};
auto [L, R] = par::split_mid(a); // L == {0, 1}, R == {2, 3, 4}
reduce |
Work | Span |
|---|---|---|
|
$O(|S|)$ | $O(\log |S|)$ |
reduce combines all elements of a sequence into a single value using a binary operation f. The bounds assumes that invoking f takes constant time. The binary operation f should be associative and identity should act as an identity
element for f.
For example, we can compute the sum of a sequence of integers by writing
par::array_sequence a{1, 2, 3, 4};
auto s = par::reduce(a, 0, std::plus<>{}); // s == 10
As another example, we can compute the maximum element of a sequence
par::array_sequence a{5, 1, 9, 3};
auto m = par::reduce(a, 0, par::max); // m == 9
scan |
Work | Span |
|---|---|---|
|
$O(|S|)$ | $O(\log |S|)$ |
Returns: a pair (P, total), where P is the prefix-scan sequence
and total is the reduction of the entire input.
scan computes the exclusive prefix scan of a sequence using a binary operation f.
The result is a sequence P of the same length as S, where
P[i] is the result of combining all elements of S before index i,
starting from identity. The bounds assumes that invoking f takes constant time. The binary operation f should be associative and identity should act as an identity
element for f.
par::array_sequence a{1, 2, 3, 4};
auto [p, total] = par::scan(a, 0, std::plus<>{}); // p == {0, 1, 3, 6}, total = 10
scan_inclusive |
Work | Span |
|---|---|---|
|
$O(|S|)$ | $O(\log |S|)$ |
scan_inclusive computes the inclusive prefix scan of a sequence using a binary operation f.
The result is a sequence P of the same length as S, where
P[i] includes the element S[i] in the prefix. The bounds assumes that invoking f takes constant time. The binary operation f should be associative and identity should act as an identity
par::array_sequence a{1, 2, 3, 4};
auto p = par::scan_inclusive(a, 0, std::plus<>{}); // p == {1, 3, 6, 10}
append |
Work | Span |
|---|---|---|
|
$O(|S1| + |S2|)$ | $O(\log(|S1| + |S2|))$ |
append returns a new sequence consisting of the elements of S1
followed by the elements of S2. The value type of S2 must be the same as S1.
par::array_sequence a{1, 2, 3};
par::array_sequence b{4, 5};
auto c = par::append(a, b); // c == {1, 2, 3, 4, 5}
reverse |
Work | Span |
|---|---|---|
|
$O(|S|)$ | $O(\log |S|)$ |
reverse returns a new sequence containing the elements of S
in reverse order.
par::array_sequence a{1, 2, 3, 4};
auto b = par::reverse(a); // b == {4, 3, 2, 1}
flatten |
Work | Span |
|---|---|---|
|
$O\!\left(\displaystyle\sum_{s \in S} \left(|s|+1\right)\right)$ | $O(\log |S|)$ |
flatten takes a sequence whose elements are themselves sequences and concatenates
all inner sequences into a single output sequence, preserving their order.
par::array_sequence<par::array_sequence<int>> a{
{1, 2},
{3},
{4, 5}
};
auto b = par::flatten(a); // b == {1, 2, 3, 4, 5}
filter |
Work | Span |
|---|---|---|
|
$\displaystyle\sum_{x \in S} \mathcal{W}(p(x))$ | $O(\log |S|) + \displaystyle\max_{x \in S} \mathcal{S}(p(x))$ |
filter returns a new sequence containing exactly those elements of S
for which p returns true, preserving their original order.
The predicate p is applied once to each element of the sequence.
par::array_sequence a{1, 2, 3, 4, 5};
auto b = par::filter(a, [](int x) { return x % 2 == 0; }); // b == {2, 4}
unique |
Work | Span |
|---|---|---|
|
$O(|S|)$ | $O(\log |S|)$ |
unique returns a new sequence obtained by removing consecutive duplicate
elements from S. Two adjacent elements are considered equal if eq
returns true. If no equality predicate is provided, == is used.
The work and span bound assumes that eq can be invoked in constant time.
par::array_sequence a{1, 1, 2, 2, 2, 3, 1};
auto b = par::unique(a); // b == {1, 2, 3, 1}
par::array_sequence a{1, 4, 7, 2, 5, 3, 6};
auto c = par::unique(a, [](int x, int y) {
return (x % 3) == (y % 3);
}); // c == {1, 2, 3}
merge |
Work | Span |
|---|---|---|
|
$O(|S1| + |S2|)$ | $O(\log(|S1| + |S2|))$ |
merge combines two sorted sequences into a single sorted sequence.
Both input sequences must already be sorted according to comp and
have the same value type. If no comparison function is provided, < is used. The work
and span bounds assume that invoking comp is constant time.
par::array_sequence a{1, 3, 5};
par::array_sequence b{2, 4, 6};
auto c = par::merge(a, b); // c == {1, 2, 3, 4, 5, 6}
sort |
Work | Span |
|---|---|---|
|
$O(|S| \log |S|)$ | $O(\log^2 |S|)$ |
sort returns a new sequence containing the elements of S
sorted according to the comparison function comp. If no comparison function is provided,
< is used. The work
and span bounds assume that invoking comp is constant time.
par::array_sequence a{3, 1, 4, 1, 5};
auto b = par::sort(a); // b == {1, 1, 3, 4, 5}
collect |
Work | Span |
|---|---|---|
|
$O(|S| \log |S|)$ | $O(\log^2 |S|)$ |
collect groups together values with equal keys.
The input sequence is assumed to contain pairs of the form (key, value).
The output is a sequence of pairs (key, values), where each values
is a sequence containing all values associated with that key.
If no comparison function is provided, operator< is used to compare keys.
par::array_sequence a{
std::pair{1, 'a'},
std::pair{2, 'b'},
std::pair{1, 'c'},
std::pair{2, 'd'}
};
auto c = par::collect(a); // c == {(1, {'a','c'}), (2, {'b','d'})}
map |
Work | Span |
|---|---|---|
|
$\displaystyle\sum_{x \in S} \mathcal{W}(f(x))$ | $\displaystyle\max_{x \in S} \mathcal{S}(f(x))$ |
map applies a function f independently to each element of a sequence
and returns the resulting sequence, preserving the original order. The type of the resulting
sequence is the return type of f.
par::array_sequence a{1, 2, 3, 4};
auto b = par::map(a, [](int x) { return x * x; }); // b == {1, 4, 9, 16}
zip_with |
Work | Span |
|---|---|---|
|
$\displaystyle\sum_{i=0}^{|S|-1} \mathcal{W}\!\left(f(S_1[i], \ldots, S_k[i])\right)$ | $\displaystyle\max_{i=0}^{|S|-1} \mathcal{S}\!\left(f(S_1[i], \ldots, S_k[i])\right)$ |
zip_with applies a function f elementwise to multiple sequences.
The i-th element of the output is f(S1[i], S2[i], ...). The length of the output sequence is the minimum length of the input sequences.
par::array_sequence a{1, 2, 3};
par::array_sequence b{4, 5, 6};
auto c = par::zip_with([](int x, int y) {
return x + y;
}, a, b); // c == {5, 7, 9}
zip |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
zip returns a view that groups together elements from multiple
sequences. The i-th element of the result is a tuple containing the
i-th elements of each input sequence.
The length of the zipped view is the minimum length of the input sequences. Elements of the view refer to elements of the original sequences rather than making copies. This means that the original sequences must outlive the zip view.
par::array_sequence a{1, 2, 3};
par::array_sequence b{4, 5, 6};
auto z = par::zip(a, b); // z = {(1,4), (2,5), (3,6)}
for_each |
Work | Span |
|---|---|---|
|
$\displaystyle\sum_{x \in S} \mathcal{W}(f(x))$ | $\displaystyle\max_{x \in S} \mathcal{S}(f(x))$ |
for_each applies a function f to each element of a sequence,
executing the calls in parallel.
Unlike map, the function f is expected to have side effects,
therefore no result is returned.
par::array_sequence a{1, 2, 3, 4};
par::for_each(a, [](int& x) {
x *= 2;
}); // a == {2, 4, 6, 8}
apply_each |
Work | Span |
|---|---|---|
|
$\displaystyle\sum_{x \in S} \mathcal{W}(f(x))$ | $\displaystyle\max_{x \in S} \mathcal{S}(f(x))$ |
apply_each is similar to for_each, but assumes that the elements
of the sequence are tuple-like types.
Each tuple is unpacked and passed as arguments to the function f.
par::array_sequence a{
std::pair{1, 2},
std::pair{3, 4}
};
par::apply_each(a, [](int x, int y) {
std::cout << (x + y) << "\n";
});
// prints:
// 3
// 7