[libcxx-commits] [libcxx] 7efb163 - [libc++][pstl] Add serial implementations of std::inclusive_scan and std::transform_inclusive_scan (#236219)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Oct 31 10:09:21 PDT 2026
Author: Michael G. Kazakov
Date: 2026-10-31T17:09:17Z
New Revision: 7efb1638f7cb94196057b1f8fa7726d43d84475a
URL: https://github.com/llvm/llvm-project/commit/7efb1638f7cb94196057b1f8fa7726d43d84475a
DIFF: https://github.com/llvm/llvm-project/commit/7efb1638f7cb94196057b1f8fa7726d43d84475a.diff
LOG: [libc++][pstl] Add serial implementations of std::inclusive_scan and std::transform_inclusive_scan (#236219)
This PR adds serial implementations of the inclusive scan operations to
PSTL.
Within this patch they are added only to the serial backend, such that
any other backend can fall back to these implementations.
A real parallel implementation is intended to land within the `statpart`
backend later, which will be a purely additive change, it will not touch
the code in this PR.
These are the added scan operations:
- `std::inclusive_scan` (3 variants)
- `std::transform_inclusive_scan` (2 variants)
Part of #99938.
---------
Co-authored-by: PaulXiCao <paulxicao7 at gmail.com>
Added:
libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan.pass.cpp
libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op.pass.cpp
libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op_init.pass.cpp
libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop.pass.cpp
libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop_init.pass.cpp
Modified:
libcxx/include/__numeric/pstl.h
libcxx/include/__pstl/backend_fwd.h
libcxx/include/__pstl/backends/default.h
libcxx/include/__pstl/backends/serial.h
libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index 84eab701e2282d..26c638799c64e6 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -35,6 +35,70 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
+template <class _ExecutionPolicy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _BinaryOperation,
+ class _Tp,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator2 inclusive_scan(
+ _ExecutionPolicy&& __policy,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce,
+ _Tp __init) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "inclusive_scan requires ForwardIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "inclusive_scan requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__inclusive_scan_init, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy),
+ std::move(__first),
+ std::move(__last),
+ std::move(__result),
+ std::move(__reduce),
+ std::move(__init));
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _BinaryOperation,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator2 inclusive_scan(
+ _ExecutionPolicy&& __policy,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "inclusive_scan requires ForwardIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "inclusive_scan requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__inclusive_scan, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy),
+ std::move(__first),
+ std::move(__last),
+ std::move(__result),
+ std::move(__reduce));
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator2 inclusive_scan(
+ _ExecutionPolicy&& __policy, _ForwardIterator1 __first, _ForwardIterator1 __last, _ForwardIterator2 __result) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "inclusive_scan requires ForwardIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "inclusive_scan requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__inclusive_scan, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result), plus{});
+}
+
template <class _ExecutionPolicy,
class _ForwardIterator,
class _Tp,
@@ -82,6 +146,63 @@ reduce(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator _
plus{});
}
+template <class _ExecutionPolicy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _BinaryOperation,
+ class _UnaryOperation,
+ class _Tp,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator2 transform_inclusive_scan(
+ _ExecutionPolicy&& __policy,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce,
+ _UnaryOperation __transform,
+ _Tp __init) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "transform_inclusive_scan requires ForwardIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "transform_inclusive_scan requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__transform_inclusive_scan_init, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy),
+ std::move(__first),
+ std::move(__last),
+ std::move(__result),
+ std::move(__reduce),
+ std::move(__transform),
+ std::move(__init));
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _BinaryOperation,
+ class _UnaryOperation,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator2 transform_inclusive_scan(
+ _ExecutionPolicy&& __policy,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce,
+ _UnaryOperation __transform) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "transform_inclusive_scan requires ForwardIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "transform_inclusive_scan requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__transform_inclusive_scan, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy),
+ std::move(__first),
+ std::move(__last),
+ std::move(__result),
+ std::move(__reduce),
+ std::move(__transform));
+}
+
template <class _ExecutionPolicy,
class _ForwardIterator1,
class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 184d2b6df1dd1d..ae797a9fa1fb52 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -226,6 +226,25 @@ struct __includes;
// _ForwardIterator2 __first2, _ForwardIterator2 __last2,
// _Compare __comp) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __inclusive_scan;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2,
+// class _BinaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&&, _ForwardIterator1 __first, _ForwardIterator1 __last,
+// _ForwardIterator2 __result,
+// _BinaryOperation __reduce) const noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __inclusive_scan_init;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2,
+// class _BinaryOperation, class _Tp>
+// optional<_ForwardIterator2>
+// operator()(_Policy&&, _ForwardIterator1 __first, _ForwardIterator1 __last,
+// _ForwardIterator2 __result,
+// _BinaryOperation __reduce,
+// _Tp __value) const noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __is_heap;
// template <class _Policy, class _RandomAccessIterator, class _Comp>
@@ -466,6 +485,27 @@ struct __transform_binary;
// _ForwardOutIterator __result,
// _BinaryOperation __op) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __transform_inclusive_scan;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2,
+// class _BinaryOperation, class _UnaryOperation>
+// optional<_ForwardIterator2>
+// operator()(_Policy&&, _ForwardIterator1 __first, _ForwardIterator1 __last,
+// _ForwardIterator2 __result,
+// _BinaryOperation __reduce,
+// _UnaryOperation __transform) const noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __transform_inclusive_scan_init;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2,
+// class _BinaryOperation, class _UnaryOperation, class _Tp>
+// optional<_ForwardIterator2>
+// operator()(_Policy&&, _ForwardIterator1 __first, _ForwardIterator1 __last,
+// _ForwardIterator2 __result,
+// _BinaryOperation __reduce,
+// _UnaryOperation __transform,
+// _Tp __init) const noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __transform_reduce;
// template <class _Policy, class _ForwardIterator, class _Tp, class _BinaryOperation, class _UnaryOperation>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 769344f34249fc..ba101734eafea0 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -164,6 +164,12 @@ namespace __pstl {
// -------------------------------------
// - uninitialized_move_n
//
+// transform_inclusive_scan_init family
+// -------------------------------------
+// - __inclusive_scan
+// - __inclusive_scan_init
+// - __transform_inclusive_scan
+//
//////////////////////////////////////////////////////////////
// find_if family
@@ -964,6 +970,82 @@ struct __uninitialized_move_n<__default_backend_tag, _ExecutionPolicy> {
}
};
+//////////////////////////////////////////////////////////////
+// transform_inclusive_scan_init family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __inclusive_scan<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+ operator()(_Policy&& __policy,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce) const noexcept {
+ using _TransformInclusiveScan = __dispatch<__transform_inclusive_scan, __current_configuration, _ExecutionPolicy>;
+ return _TransformInclusiveScan()(
+ __policy, std::move(__first), std::move(__last), std::move(__result), std::move(__reduce), __identity{});
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __inclusive_scan_init<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation, class _Tp>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+ operator()(_Policy&& __policy,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce,
+ _Tp __init) const noexcept {
+ using _TransformInclusiveScanInit =
+ __dispatch<__transform_inclusive_scan_init, __current_configuration, _ExecutionPolicy>;
+ return _TransformInclusiveScanInit()(
+ __policy,
+ std::move(__first),
+ std::move(__last),
+ std::move(__result),
+ std::move(__reduce),
+ __identity{},
+ std::move(__init));
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __transform_inclusive_scan<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _BinaryOperation,
+ class _UnaryOperation>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2>
+ operator()(_Policy&& __policy,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce,
+ _UnaryOperation __transform) const noexcept {
+ if (__first != __last) {
+ using _TransformInclusiveScanInit =
+ __dispatch<__transform_inclusive_scan_init, __current_configuration, _ExecutionPolicy>;
+ using _ValueType = __iterator_value_type<_ForwardIterator1>;
+ _ValueType __init = __transform(*__first);
+ *__result++ = __init;
+ if (++__first != __last)
+ return _TransformInclusiveScanInit()(
+ __policy,
+ std::move(__first),
+ std::move(__last),
+ std::move(__result),
+ std::move(__reduce),
+ std::move(__transform),
+ std::move(__init));
+ }
+ return __result;
+ }
+};
+
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index 294927cbb70276..ecb3f91b159110 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -31,6 +31,7 @@
#include <__algorithm/transform.h>
#include <__config>
#include <__memory/uninitialized_algorithms.h>
+#include <__numeric/transform_inclusive_scan.h>
#include <__numeric/transform_reduce.h>
#include <__optional/optional.h>
#include <__pstl/backend_fwd.h>
@@ -255,6 +256,32 @@ struct __transform_binary<__serial_backend_tag, _ExecutionPolicy> {
}
};
+template <class _ExecutionPolicy>
+struct __transform_inclusive_scan_init<__serial_backend_tag, _ExecutionPolicy> {
+ template <class _Policy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _BinaryOperation,
+ class _UnaryOperation,
+ class _Tp>
+ _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2> operator()(
+ _Policy&&,
+ _ForwardIterator1 __first,
+ _ForwardIterator1 __last,
+ _ForwardIterator2 __result,
+ _BinaryOperation __reduce,
+ _UnaryOperation __transform,
+ _Tp __init) const noexcept {
+ return std::transform_inclusive_scan(
+ std::move(__first),
+ std::move(__last),
+ std::move(__result),
+ std::move(__reduce),
+ std::move(__transform),
+ std::move(__init));
+ }
+};
+
template <class _ExecutionPolicy>
struct __transform_reduce<__serial_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator, class _Tp, class _BinaryOperation, class _UnaryOperation>
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 7a006be5af72b1..2a874f487d5ecb 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -158,6 +158,20 @@ void f(non_forward_iterator non_fwd,
pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: includes}}
}
+ {
+ (void)std::inclusive_scan(
+ pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: inclusive_scan}}
+ (void)std::inclusive_scan(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: inclusive_scan}}
+ (void)std::inclusive_scan(
+ pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: inclusive_scan}}
+ (void)std::inclusive_scan(
+ pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: inclusive_scan}}
+ (void)std::inclusive_scan(
+ pol, non_fwd, non_fwd, it, func, val); // expected-error@*:* {{static assertion failed: inclusive_scan}}
+ (void)std::inclusive_scan(
+ pol, it, it, non_fwd, func, val); // expected-error@*:* {{static assertion failed: inclusive_scan}}
+ }
+
{
(void)std::is_partitioned(
pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
@@ -366,6 +380,39 @@ void f(non_forward_iterator non_fwd,
pol, it, it, it, non_output, func); // expected-error@*:* {{static assertion failed: transform}}
}
+ {
+ (void)std::transform_inclusive_scan(
+ pol,
+ non_fwd,
+ non_fwd,
+ it,
+ func,
+ func); // expected-error@*:* {{static assertion failed: transform_inclusive_scan}}
+ (void)std::transform_inclusive_scan(
+ pol,
+ it,
+ it,
+ non_fwd,
+ func,
+ func); // expected-error@*:* {{static assertion failed: transform_inclusive_scan}}
+ (void)std::transform_inclusive_scan(
+ pol,
+ non_fwd,
+ non_fwd,
+ it,
+ func,
+ func,
+ val); // expected-error@*:* {{static assertion failed: transform_inclusive_scan}}
+ (void)std::transform_inclusive_scan(
+ pol,
+ it,
+ it,
+ non_fwd,
+ func,
+ func,
+ val); // expected-error@*:* {{static assertion failed: transform_inclusive_scan}}
+ }
+
{
(void)std::reduce(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: reduce}}
(void)std::reduce(pol, non_fwd, non_fwd, val); // expected-error@*:* {{static assertion failed: reduce}}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index ec8b60c2759d52..75ae0404d51d43 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -261,6 +261,38 @@ int main(int, char**) {
});
}
+ {
+ auto unary = maybe_throw(tokens[5], [](int x) -> int { return x * 2; });
+ auto binary = maybe_throw(tokens[5], [](int x, int y) -> int { return x * y; });
+
+ // inclusive_scan(first, last, result)
+ assert_non_throwing([=, &policy] {
+ (void)std::inclusive_scan(policy, std::move(first1), std::move(last1), std::move(dest));
+ });
+
+ // inclusive_scan(first, last, result, binary)
+ assert_non_throwing([=, &policy] {
+ (void)std::inclusive_scan(policy, std::move(first1), std::move(last1), std::move(dest), binary);
+ });
+
+ // inclusive_scan(first, last, result, binary, init)
+ assert_non_throwing([=, &policy] {
+ (void)std::inclusive_scan(policy, std::move(first1), std::move(last1), std::move(dest), binary, val);
+ });
+
+ // transform_inclusive_scan(first, last, result, binary, unary)
+ assert_non_throwing([=, &policy] {
+ (void)std::transform_inclusive_scan(
+ policy, std::move(first1), std::move(last1), std::move(dest), binary, unary);
+ });
+
+ // transform_inclusive_scan(first, last, result, binary, unary, init)
+ assert_non_throwing([=, &policy] {
+ (void)std::transform_inclusive_scan(
+ policy, std::move(first1), std::move(last1), std::move(dest), binary, unary, val);
+ });
+ }
+
{
auto comp = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
diff --git a/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan.pass.cpp b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan.pass.cpp
new file mode 100644
index 00000000000000..bb930e2914e314
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan.pass.cpp
@@ -0,0 +1,157 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class ForwardIterator1,
+// class ForwardIterator2>
+// ForwardIterator2 inclusive_scan(ExecutionPolicy&& exec,
+// ForwardIterator1 first,
+// ForwardIterator1 last,
+// ForwardIterator2 result);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <limits>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(inclusive_scan);
+
+static_assert(sfinae_test_inclusive_scan<int, int*, int*, int*>);
+static_assert(!sfinae_test_inclusive_scan<std::execution::parallel_policy, int*, int*, int*>);
+
+template <typename T>
+constexpr auto triangle(T n) {
+ return n * (n + 1) / 2;
+}
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ { // Check the return type
+ int a[] = {0};
+ auto res = std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)));
+ static_assert(std::is_same_v<decltype(res), Iter2>);
+ }
+ { // Empty
+ int a[] = {-1};
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)));
+ assert(a[0] == -1);
+ }
+ { // Scan a uniform array
+ int a[10];
+ std::fill(std::begin(a), std::end(a), 3);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)));
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == (static_cast<int>(i) + 1) * 3);
+ }
+ { // Scan an increasing array starting from 0
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 0);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)));
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(triangle(i)));
+ }
+ { // Scan an increasing array starting from 1
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 1);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)));
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(triangle(i + 1)));
+ }
+ { // Test against pre-computed expected results
+ auto test = [&](auto first, auto last, auto exp_first, auto exp_last) {
+ int out[5];
+ // Not in place
+ auto end = std::inclusive_scan(policy, first, last, Iter2(std::begin(out)));
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ // In place
+ std::copy(first, last, out);
+ end = std::inclusive_scan(policy, Iter2(std::begin(out)), end, Iter2(std::begin(out)));
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ };
+ const int a[] = {1, 3, 5, 7, 9};
+ const int exp_plus[] = {1, 4, 9, 16, 25}; // plus
+ static_assert(std::size(a) == std::size(exp_plus)); // just to be sure
+ for (unsigned int i = 0; i < std::size(a); ++i) {
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), exp_plus, exp_plus + i);
+ }
+ }
+ { // Construct iota
+ int a[1073];
+ int b[1073];
+ std::fill(std::begin(a), std::end(a), 1);
+ runway_sample(std::size(a) + 1, [&](size_t i) {
+ Iter2 r = std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::begin(a) + i), Iter2(std::begin(b)));
+ assert(r == Iter2(std::begin(b) + i));
+ for (size_t j = 0; j < i; ++j) {
+ assert(b[j] == static_cast<int>(j) + 1);
+ }
+ std::fill(std::begin(b), std::begin(b) + i, -1);
+ });
+ }
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestString {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ for (std::size_t size : {0, 1, 2, 3, 10, 100, 1000}) {
+ std::vector<std::string> data(size);
+ for (std::size_t i = 0; i < size; ++i)
+ data[i] = std::string(1, static_cast<char>('a' + i % 26));
+
+ std::vector<std::string> expected(size);
+ std::inclusive_scan(data.begin(), data.end(), expected.begin());
+
+ // Not in place
+ std::vector<std::string> result(size);
+ auto ret =
+ std::inclusive_scan(policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(result.data()));
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+
+ // In place
+ ret = std::inclusive_scan(policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(data.data()));
+ assert(ret == Iter2(data.data() + size));
+ assert(data == expected);
+ }
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ types::for_each(types::forward_iterator_list<std::string*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<std::string*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestString, Iter>::template apply>{});
+ }});
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op.pass.cpp
new file mode 100644
index 00000000000000..f0b1be18a9a85a
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op.pass.cpp
@@ -0,0 +1,168 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class ForwardIterator1,
+// class ForwardIterator2,
+// class BinaryOperation>
+// ForwardIterator2 inclusive_scan(ExecutionPolicy&& exec,
+// ForwardIterator1 first,
+// ForwardIterator1 last,
+// ForwardIterator2 result,
+// BinaryOperation reduce);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(inclusive_scan);
+
+static_assert(sfinae_test_inclusive_scan<int, int*, int*, int*, int (*)(int, int)>);
+static_assert(!sfinae_test_inclusive_scan<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
+
+template <typename T>
+constexpr auto triangle(T n) {
+ return n * (n + 1) / 2;
+}
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ { // Check the return type
+ int a[] = {0};
+ auto res =
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{});
+ static_assert(std::is_same_v<decltype(res), Iter2>);
+ }
+ { // Empty
+ int a[] = {-1};
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), std::plus<>{});
+ assert(a[0] == -1);
+ }
+ { // Scan a uniform array
+ int a[10];
+ std::fill(std::begin(a), std::end(a), 3);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{});
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == (static_cast<int>(i) + 1) * 3);
+ }
+ { // Scan an increasing array starting from 0
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 0);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{});
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(triangle(i)));
+ }
+ { // Scan an increasing array starting from 1
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 1);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{});
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(triangle(i + 1)));
+ }
+ { // Test against pre-computed expected results
+ auto test = [&](auto first, auto last, auto reduce, auto exp_first, auto exp_last) {
+ int out[5];
+ // Not in place
+ auto end = std::inclusive_scan(policy, first, last, Iter2(std::begin(out)), reduce);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ // In place
+ std::copy(first, last, out);
+ end = std::inclusive_scan(policy, Iter2(std::begin(out)), end, Iter2(std::begin(out)), reduce);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ };
+ const int a[] = {1, 3, 5, 7, 9};
+ const int exp_plus[] = {1, 4, 9, 16, 25}; // plus
+ const int exp_mul[] = {1, 3, 15, 105, 945}; // multiplies
+ static_assert(std::size(a) == std::size(exp_plus)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul)); // just to be sure
+ std::plus<> plus;
+ std::multiplies<> mult;
+ for (unsigned int i = 0; i < std::size(a); ++i) {
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, exp_plus, exp_plus + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, exp_mul, exp_mul + i);
+ }
+ }
+ { // Construct iota from std::plus<>
+ int a[1073];
+ int b[1073];
+ std::fill(std::begin(a), std::end(a), 1);
+ runway_sample(std::size(a) + 1, [&](size_t i) {
+ Iter2 r = std::inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::begin(a) + i), Iter2(std::begin(b)), std::plus<>{});
+ assert(r == Iter2(std::begin(b) + i));
+ for (size_t j = 0; j < i; ++j) {
+ assert(b[j] == static_cast<int>(j) + 1);
+ }
+ std::fill(std::begin(b), std::begin(b) + i, -1);
+ });
+ }
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestString {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ auto op = [](const std::string& a, const std::string& b) { return a + b; };
+ for (std::size_t size : {0, 1, 2, 3, 10, 100, 1000}) {
+ std::vector<std::string> data(size);
+ for (std::size_t i = 0; i < size; ++i)
+ data[i] = std::string(1, static_cast<char>('a' + i % 26));
+
+ std::vector<std::string> expected(size);
+ std::inclusive_scan(data.begin(), data.end(), expected.begin(), op);
+
+ // Not in place
+ std::vector<std::string> result(size);
+ auto ret =
+ std::inclusive_scan(policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(result.data()), op);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+
+ // In place
+ ret = std::inclusive_scan(policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(data.data()), op);
+ assert(ret == Iter2(data.data() + size));
+ assert(data == expected);
+ }
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ types::for_each(types::forward_iterator_list<std::string*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<std::string*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestString, Iter>::template apply>{});
+ }});
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op_init.pass.cpp b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op_init.pass.cpp
new file mode 100644
index 00000000000000..0878b7bfae57db
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op_init.pass.cpp
@@ -0,0 +1,196 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class ForwardIterator1,
+// class ForwardIterator2,
+// class BinaryOperation,
+// class T>
+// ForwardIterator2 inclusive_scan(ExecutionPolicy&& exec,
+// ForwardIterator1 first,
+// ForwardIterator1 last,
+// ForwardIterator2 result,
+// BinaryOperation reduce,
+// T init);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(inclusive_scan);
+
+static_assert(sfinae_test_inclusive_scan<int, int*, int*, int*, int (*)(int, int), int>);
+static_assert(!sfinae_test_inclusive_scan<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int), int>);
+
+template <typename T>
+constexpr auto triangle(T n) {
+ return n * (n + 1) / 2;
+}
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ { // Check the return type
+ int a[] = {0};
+ auto res =
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, 0);
+ static_assert(std::is_same_v<decltype(res), Iter2>);
+ }
+ { // Empty
+ int a[] = {-1};
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), std::plus<>{}, 10);
+ assert(a[0] == -1);
+ }
+ { // Scan a uniform array
+ int a[10];
+ std::fill(std::begin(a), std::end(a), 3);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, 50);
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == 50 + (static_cast<int>(i) + 1) * 3);
+ }
+ { // Scan an increasing array starting from 0
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 0);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, 40);
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(40 + triangle(i)));
+ }
+ { // Scan an increasing array starting from 1
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 1);
+ std::inclusive_scan(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, 30);
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(30 + triangle(i + 1)));
+ }
+ { // Test against pre-computed expected results
+ auto test = [&](auto first, auto last, auto reduce, auto init, auto exp_first, auto exp_last) {
+ int out[5];
+ // Not in place
+ auto end = std::inclusive_scan(policy, first, last, Iter2(std::begin(out)), reduce, init);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ // In place
+ std::copy(first, last, out);
+ end = std::inclusive_scan(policy, Iter2(std::begin(out)), end, Iter2(std::begin(out)), reduce, init);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ };
+ const int a[] = {1, 3, 5, 7, 9};
+ const int exp_plus_0[] = {1, 4, 9, 16, 25}; // plus, init = 0
+ const int exp_mul_1[] = {1, 3, 15, 105, 945}; // multiplies, init = 1
+ static_assert(std::size(a) == std::size(exp_plus_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul_1)); // just to be sure
+ std::plus<> plus;
+ std::multiplies<> mult;
+ for (unsigned int i = 0; i < std::size(a); ++i) {
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, 0, exp_plus_0, exp_plus_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, 1, exp_mul_1, exp_mul_1 + i);
+ }
+ }
+ { // Construct iota from std::plus<>
+ int a[1073];
+ int b[1073];
+ std::fill(std::begin(a), std::end(a), 1);
+ runway_sample(std::size(a) + 1, [&](size_t i) {
+ Iter2 r = std::inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::begin(a) + i), Iter2(std::begin(b)), std::plus<>{}, 1);
+ assert(r == Iter2(std::begin(b) + i));
+ for (size_t j = 0; j < i; ++j) {
+ assert(b[j] == static_cast<int>(j) + 2);
+ }
+ std::fill(std::begin(b), std::begin(b) + i, -1);
+ });
+ }
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestUnsignedCharToSize_t {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ // Make sure that the calculations are done using the init typedef
+ unsigned char a[1073];
+ std::size_t b[1073];
+ std::fill(std::begin(a), std::end(a), static_cast<unsigned char>(255));
+ runway_sample(std::size(a) + 1, [&](size_t i) {
+ std::inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::begin(a) + i), Iter2(std::begin(b)), std::plus<>{}, std::size_t{0});
+ for (std::size_t k = 0; k < i; ++k)
+ assert(b[k] == 255 * (k + 1));
+ });
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestString {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ auto op = [](const std::string& a, const std::string& b) { return a + b; };
+ std::string init = "0";
+ for (std::size_t size : {0, 1, 2, 3, 10, 100, 1000}) {
+ std::vector<std::string> data(size);
+ for (std::size_t i = 0; i < size; ++i)
+ data[i] = std::string(1, static_cast<char>('a' + i % 26));
+
+ std::vector<std::string> expected(size);
+ std::inclusive_scan(data.begin(), data.end(), expected.begin(), op, init);
+
+ // Not in place
+ std::vector<std::string> result(size);
+ auto ret = std::inclusive_scan(
+ policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(result.data()), op, init);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+
+ // In place
+ ret = std::inclusive_scan(
+ policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(data.data()), op, init);
+ assert(ret == Iter2(data.data() + size));
+ assert(data == expected);
+ }
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ types::for_each(
+ types::forward_iterator_list<const unsigned char*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<std::size_t*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestUnsignedCharToSize_t, Iter>::template apply>{});
+ }});
+ types::for_each(types::forward_iterator_list<std::string*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<std::string*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestString, Iter>::template apply>{});
+ }});
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop.pass.cpp b/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop.pass.cpp
new file mode 100644
index 00000000000000..3e1aed96457845
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class ForwardIterator1,
+// class ForwardIterator2,
+// class BinaryOperation,
+// class UnaryOperation,
+// class T>
+// ForwardIterator2 transform_inclusive_scan(ExecutionPolicy&& exec,
+// ForwardIterator1 first,
+// ForwardIterator1 last,
+// ForwardIterator2 result,
+// BinaryOperation reduce,
+// UnaryOperation transform);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <vector>
+#include <string>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(transform_inclusive_scan);
+
+static_assert(sfinae_test_transform_inclusive_scan<int, int*, int*, int*, int (*)(int, int), int (*)(int)>);
+static_assert(!sfinae_test_transform_inclusive_scan< std::execution::parallel_policy,
+ int*,
+ int*,
+ int*,
+ int (*)(int, int),
+ int (*)(int)>);
+
+struct add_one {
+ template <typename T>
+ constexpr T operator()(T x) const {
+ return x + 1;
+ }
+};
+
+template <typename T>
+constexpr auto triangle(T n) {
+ return n * (n + 1) / 2;
+}
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ { // Check the return type
+ int a[] = {0};
+ auto res = std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<int>{}, [](int x) {
+ return x;
+ });
+ static_assert(std::is_same_v<decltype(res), Iter2>);
+ }
+ { // Empty
+ int a[] = {-1};
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{});
+ assert(a[0] == -1);
+ }
+ { // Scan a uniform array
+ int a[10];
+ std::fill(std::begin(a), std::end(a), 3);
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{});
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == (static_cast<int>(i) + 1) * 4);
+ }
+ { // Scan an increasing array starting from 0
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 0);
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{});
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(triangle(i) + i + 1));
+ }
+ { // Scan an increasing array starting from 1
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 1);
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{});
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(triangle(i + 1) + i + 1));
+ }
+ { // Test against pre-computed expected results
+ auto test = [&](auto first, auto last, auto reduce, auto transform, auto exp_first, auto exp_last) {
+ int out[5];
+ // Not in place
+ auto end = std::transform_inclusive_scan(policy, first, last, Iter2(std::begin(out)), reduce, transform);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ // In place
+ std::copy(first, last, out);
+ end = std::transform_inclusive_scan(
+ policy, Iter2(std::begin(out)), end, Iter2(std::begin(out)), reduce, transform);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ };
+ const int a[] = {1, 3, 5, 7, 9};
+ const int exp_plus_inc_0[] = {2, 6, 12, 20, 30}; // plus, add_one, init = 0
+ const int exp_mul_inc_0[] = {2, 8, 48, 384, 3840}; // multiplies, add_one, init = 0
+ const int exp_plus_neg_0[] = {-1, -4, -9, -16, -25}; // plus, negate, init = 0
+ const int exp_mul_neg_0[] = {-1, 3, -15, 105, -945}; // multiplies, negate, init = 0
+ static_assert(std::size(a) == std::size(exp_plus_inc_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul_inc_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_plus_neg_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul_neg_0)); // just to be sure
+ std::plus<> plus;
+ std::multiplies<> mult;
+ std::negate<> negate;
+ for (unsigned int i = 0; i < std::size(a); ++i) {
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, add_one{}, exp_plus_inc_0, exp_plus_inc_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, add_one{}, exp_mul_inc_0, exp_mul_inc_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, negate, exp_plus_neg_0, exp_plus_neg_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, negate, exp_mul_neg_0, exp_mul_neg_0 + i);
+ }
+ }
+ { // Construct iota from std::plus<> and add_one
+ int ia[1073];
+ int ib[1073];
+ std::fill(std::begin(ia), std::end(ia), 0);
+ runway_sample(std::size(ia) + 1, [&](size_t i) {
+ Iter2 r = std::transform_inclusive_scan(
+ policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), std::plus<>{}, add_one{});
+ assert(r == Iter2(std::begin(ib) + i));
+ for (size_t j = 0; j < i; ++j) {
+ assert(ib[j] == static_cast<int>(j) + 1);
+ }
+ std::fill(std::begin(ib), std::begin(ib) + i, -1);
+ });
+ }
+ { // Check against the non-pstl version
+ for (int size : {0, 1, 2, 100, 350, 10'000}) {
+ std::vector<int> data(size);
+ std::iota(data.begin(), data.end(), 7);
+
+ std::vector<int> expected(size);
+ auto binary_op = [check = std::string("Banane")](int i, int j) {
+ assert(check == "Banane"); // ensure that no double-moves happen
+ return i + j;
+ };
+ auto unary_op = [check = std::string("Banane")](int i) {
+ assert(check == "Banane"); // ensure that no double-moves happen
+ return i + 1;
+ };
+
+ std::transform_inclusive_scan(data.begin(), data.end(), expected.begin(), binary_op, unary_op);
+
+ std::vector<int> result(size);
+ auto ret = std::transform_inclusive_scan(
+ policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(result.data()), binary_op, unary_op);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+ }
+ }
+ { // binary_op whose identity element is not int{}
+ for (int size : {0, 1, 2, 100, 350, 10'000}) {
+ std::vector<int> data(size);
+ std::iota(data.begin(), data.end(), 7);
+
+ auto binary_op = std::multiplies{};
+ auto unary_op = [](int x) { return (x % 1'000 == 0) ? 2 : 1; }; // keeps product below 2^10
+
+ std::vector<int> expected(size);
+ std::transform_inclusive_scan(data.begin(), data.end(), expected.begin(), binary_op, unary_op);
+
+ std::vector<int> result(size);
+ auto ret = std::transform_inclusive_scan(
+ policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(result.data()), binary_op, unary_op);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+ }
+ }
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestString {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ auto binary_op = [](const std::string& a, const std::string& b) { return a + b; };
+ auto unary_op = [](const std::string& s) { return s + "!"; };
+ for (std::size_t size : {0, 1, 2, 3, 10, 100, 1000}) {
+ std::vector<std::string> data(size);
+ for (std::size_t i = 0; i < size; ++i)
+ data[i] = std::string(1, static_cast<char>('a' + i % 26));
+
+ std::vector<std::string> expected(size);
+ std::transform_inclusive_scan(data.begin(), data.end(), expected.begin(), binary_op, unary_op);
+
+ // Not in place
+ std::vector<std::string> result(size);
+ auto ret = std::transform_inclusive_scan(
+ policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(result.data()), binary_op, unary_op);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+
+ // In place
+ ret = std::transform_inclusive_scan(
+ policy, Iter1(data.data()), Iter1(data.data() + data.size()), Iter2(data.data()), binary_op, unary_op);
+ assert(ret == Iter2(data.data() + size));
+ assert(data == expected);
+ }
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ types::for_each(types::forward_iterator_list<std::string*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<std::string*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestString, Iter>::template apply>{});
+ }});
+ return 0;
+}
diff --git a/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop_init.pass.cpp b/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop_init.pass.cpp
new file mode 100644
index 00000000000000..6c3717e6baf53f
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop_init.pass.cpp
@@ -0,0 +1,323 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class ForwardIterator1,
+// class ForwardIterator2,
+// class BinaryOperation,
+// class UnaryOperation,
+// class T>
+// ForwardIterator2 transform_inclusive_scan(ExecutionPolicy&& exec,
+// ForwardIterator1 first,
+// ForwardIterator1 last,
+// ForwardIterator2 result,
+// BinaryOperation reduce,
+// UnaryOperation transform,
+// T init);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <numeric>
+#include <vector>
+#include <string>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(transform_inclusive_scan);
+
+static_assert(sfinae_test_transform_inclusive_scan<int, int*, int*, int*, int (*)(int, int), int (*)(int), int>);
+static_assert(!sfinae_test_transform_inclusive_scan<
+ std::execution::parallel_policy,
+ int*,
+ int*,
+ int*,
+ int (*)(int, int),
+ int (*)(int),
+ int>);
+
+struct add_one {
+ template <typename T>
+ constexpr T operator()(T x) const {
+ return x + 1;
+ }
+};
+
+template <typename T>
+constexpr auto triangle(T n) {
+ return n * (n + 1) / 2;
+}
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ { // Check the return type
+ int a[] = {0};
+ auto res = std::transform_inclusive_scan(
+ policy,
+ Iter1(std::begin(a)),
+ Iter1(std::end(a)),
+ Iter2(std::begin(a)),
+ std::plus<int>{},
+ [](int x) { return x; },
+ 0);
+ static_assert(std::is_same_v<decltype(res), Iter2>);
+ }
+ { // Empty
+ int a[] = {-1};
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{}, 10);
+ assert(a[0] == -1);
+ }
+ { // Scan a uniform array
+ int a[10];
+ std::fill(std::begin(a), std::end(a), 3);
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{}, 50);
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == 50 + (static_cast<int>(i) + 1) * 4);
+ }
+ { // Scan an increasing array starting from 0
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 0);
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{}, 30);
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(30 + triangle(i) + i + 1));
+ }
+ { // Scan an increasing array starting from 1
+ int a[10];
+ std::iota(std::begin(a), std::end(a), 1);
+ std::transform_inclusive_scan(
+ policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), std::plus<>{}, add_one{}, 40);
+ for (std::size_t i = 0; i < std::size(a); ++i)
+ assert(a[i] == static_cast<int>(40 + triangle(i + 1) + i + 1));
+ }
+ { // Test against pre-computed expected results
+ auto test = [&](auto first, auto last, auto reduce, auto transform, auto init, auto exp_first, auto exp_last) {
+ int out[5];
+ // Not in place
+ auto end = std::transform_inclusive_scan(policy, first, last, Iter2(std::begin(out)), reduce, transform, init);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ // In place
+ std::copy(first, last, out);
+ end = std::transform_inclusive_scan(
+ policy, Iter2(std::begin(out)), end, Iter2(std::begin(out)), reduce, transform, init);
+ assert(std::equal(Iter2(std::begin(out)), end, exp_first, exp_last));
+ };
+ const int a[] = {1, 3, 5, 7, 9};
+ const int exp_plus_inc_0[] = {2, 6, 12, 20, 30}; // plus, add_one, init = 0
+ const int exp_mul_inc_0[] = {0, 0, 0, 0, 0}; // multiplies, add_one, init = 0
+ const int exp_plus_neg_0[] = {-1, -4, -9, -16, -25}; // plus, negate, init = 0
+ const int exp_mul_neg_0[] = {0, 0, 0, 0, 0}; // multiplies, negate, init = 0
+ const int exp_plus_inc_2[] = {4, 8, 14, 22, 32}; // plus, add_one, init = 2
+ const int exp_mul_inc_2[] = {4, 16, 96, 768, 7680}; // multiplies, add_one, init = 2
+ const int exp_plus_neg_2[] = {1, -2, -7, -14, -23}; // plus, negate, init = 2
+ const int exp_mul_neg_2[] = {-2, 6, -30, 210, -1890}; // multiplies, negate, init = 2
+ static_assert(std::size(a) == std::size(exp_plus_inc_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul_inc_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_plus_neg_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul_neg_0)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_plus_inc_2)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul_inc_2)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_plus_neg_2)); // just to be sure
+ static_assert(std::size(a) == std::size(exp_mul_neg_2)); // just to be sure
+ std::plus<> plus;
+ std::multiplies<> mult;
+ std::negate<> negate;
+ for (unsigned int i = 0; i < std::size(a); ++i) {
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, add_one{}, 0, exp_plus_inc_0, exp_plus_inc_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, add_one{}, 0, exp_mul_inc_0, exp_mul_inc_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, negate, 0, exp_plus_neg_0, exp_plus_neg_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, negate, 0, exp_mul_neg_0, exp_mul_neg_0 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, add_one{}, 2, exp_plus_inc_2, exp_plus_inc_2 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, add_one{}, 2, exp_mul_inc_2, exp_mul_inc_2 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), plus, negate, 2, exp_plus_neg_2, exp_plus_neg_2 + i);
+ test(Iter1(std::begin(a)), Iter1(std::begin(a) + i), mult, negate, 2, exp_mul_neg_2, exp_mul_neg_2 + i);
+ }
+ }
+ { // Construct iota from std::plus<> and add_one
+ int ia[1073];
+ int ib[1073];
+ std::fill(std::begin(ia), std::end(ia), 0);
+ runway_sample(std::size(ia) + 1, [&](size_t i) {
+ Iter2 r = std::transform_inclusive_scan(
+ policy,
+ Iter1(std::begin(ia)),
+ Iter1(std::begin(ia) + i),
+ Iter2(std::begin(ib)),
+ std::plus<>{},
+ add_one{},
+ 1);
+ assert(r == Iter2(std::begin(ib) + i));
+ for (size_t j = 0; j < i; ++j) {
+ assert(ib[j] == static_cast<int>(j) + 2);
+ }
+ std::fill(std::begin(ib), std::begin(ib) + i, -1);
+ });
+ }
+ { // Check against the non-pstl version
+ for (int size : {0, 1, 2, 100, 350, 10'000}) {
+ std::vector<int> data(size);
+ std::iota(data.begin(), data.end(), 7);
+ int init = 42;
+
+ std::vector<int> expected(size);
+ auto binary_op = [check = std::string("Banane")](int i, int j) {
+ assert(check == "Banane"); // ensure that no double-moves happen
+ return i + j;
+ };
+ auto unary_op = [check = std::string("Banane")](int i) {
+ assert(check == "Banane"); // ensure that no double-moves happen
+ return i + 1;
+ };
+
+ std::transform_inclusive_scan(data.begin(), data.end(), expected.begin(), binary_op, unary_op, init);
+
+ std::vector<int> result(size);
+ auto ret = std::transform_inclusive_scan(
+ policy,
+ Iter1(data.data()),
+ Iter1(data.data() + data.size()),
+ Iter2(result.data()),
+ binary_op,
+ unary_op,
+ init);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+ }
+ }
+ { // binary_op whose identity element is not int{}
+ for (int size : {0, 1, 2, 100, 350, 10'000}) {
+ std::vector<int> data(size);
+ std::iota(data.begin(), data.end(), 7);
+ int init = 42;
+
+ auto binary_op = std::multiplies{};
+ auto unary_op = [](int x) { return (x % 1'000 == 0) ? 2 : 1; }; // keeps product below 2^10
+
+ std::vector<int> expected(size);
+ std::transform_inclusive_scan(data.begin(), data.end(), expected.begin(), binary_op, unary_op, init);
+
+ std::vector<int> result(size);
+ auto ret = std::transform_inclusive_scan(
+ policy,
+ Iter1(data.data()),
+ Iter1(data.data() + data.size()),
+ Iter2(result.data()),
+ binary_op,
+ unary_op,
+ init);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+ }
+ }
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestUnsignedCharToSize_t {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ // Make sure that the calculations are done using the init typedef
+ unsigned char a[1073];
+ std::size_t b[1073];
+ std::fill(std::begin(a), std::end(a), static_cast<unsigned char>(254));
+ runway_sample(std::size(a) + 1, [&](size_t i) {
+ std::transform_inclusive_scan(
+ policy,
+ Iter1(std::begin(a)),
+ Iter1(std::begin(a) + i),
+ Iter2(std::begin(b)),
+ std::plus<>{},
+ add_one{},
+ std::size_t{0});
+ for (std::size_t k = 0; k < i; ++k)
+ assert(b[k] == 255 * (k + 1));
+ });
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestString {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ auto binary_op = [](const std::string& a, const std::string& b) { return a + b; };
+ auto unary_op = [](const std::string& s) { return s + "!"; };
+ std::string init = "0";
+ for (std::size_t size : {0, 1, 2, 3, 10, 100, 1000}) {
+ std::vector<std::string> data(size);
+ for (std::size_t i = 0; i < size; ++i)
+ data[i] = std::string(1, static_cast<char>('a' + i % 26));
+
+ std::vector<std::string> expected(size);
+ std::transform_inclusive_scan(data.begin(), data.end(), expected.begin(), binary_op, unary_op, init);
+
+ // Not in place
+ std::vector<std::string> result(size);
+ auto ret = std::transform_inclusive_scan(
+ policy,
+ Iter1(data.data()),
+ Iter1(data.data() + data.size()),
+ Iter2(result.data()),
+ binary_op,
+ unary_op,
+ init);
+ assert(ret == Iter2(result.data() + size));
+ assert(result == expected);
+
+ // In place
+ ret = std::transform_inclusive_scan(
+ policy,
+ Iter1(data.data()),
+ Iter1(data.data() + data.size()),
+ Iter2(data.data()),
+ binary_op,
+ unary_op,
+ init);
+ assert(ret == Iter2(data.data() + size));
+ assert(data == expected);
+ }
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ types::for_each(
+ types::forward_iterator_list<const unsigned char*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<std::size_t*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestUnsignedCharToSize_t, Iter>::template apply>{});
+ }});
+ types::for_each(types::forward_iterator_list<std::string*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<std::string*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestString, Iter>::template apply>{});
+ }});
+ return 0;
+}
More information about the libcxx-commits
mailing list