[libcxx-commits] [libcxx] [libc++][pstl] Add serial implementations of std::inclusive_scan and std::transform_inclusive_scan (PR #236219)

Michael G. Kazakov via libcxx-commits libcxx-commits at lists.llvm.org
Fri Oct 30 05:36:32 PDT 2026


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/236219

>From 8f7d46a88b369d5a5736b53aa1087b6edb0c07e4 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 27 Oct 2026 18:35:59 +0000
Subject: [PATCH] Added inclusive_scan and transform_inclusive_scan to PSTL

---
 libcxx/include/__numeric/pstl.h               | 121 ++++++++++
 libcxx/include/__pstl/backend_fwd.h           |  40 ++++
 libcxx/include/__pstl/backends/default.h      |  82 +++++++
 libcxx/include/__pstl/backends/serial.h       |  27 +++
 .../pstl.iterator-requirements.verify.cpp     |  47 ++++
 .../pstl.exception_handling.pass.cpp          |  32 +++
 .../pstl.inclusive_scan.pass.cpp              | 120 ++++++++++
 .../pstl.inclusive_scan_op.pass.cpp           | 130 +++++++++++
 .../pstl.inclusive_scan_op_init.pass.cpp      | 156 +++++++++++++
 ....transform_inclusive_scan_bop_uop.pass.cpp | 159 +++++++++++++
 ...sform_inclusive_scan_bop_uop_init.pass.cpp | 214 ++++++++++++++++++
 11 files changed, 1128 insertions(+)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op_init.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop_init.pass.cpp

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 fbc6f9920bdedd..a0d6c0fc5a012e 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -222,6 +222,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>
@@ -462,6 +481,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..a3bc637cefd49c
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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);
+      });
+    }
+  }
+};
+
+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>{});
+                  }});
+  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..ee9e303cca7f53
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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);
+      });
+    }
+  }
+};
+
+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>{});
+                  }});
+  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..558aa465bab17c
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/inclusive.scan/pstl.inclusive_scan_op_init.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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));
+    });
+  }
+};
+
+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>{});
+      }});
+  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..6a86c3c9109ef3
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop.pass.cpp
@@ -0,0 +1,159 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <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);
+      });
+    }
+  }
+};
+
+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>{});
+                  }});
+  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..bc0364bed883a3
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/transform.inclusive.scan/pstl.transform_inclusive_scan_bop_uop_init.pass.cpp
@@ -0,0 +1,214 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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);
+      });
+    }
+  }
+};
+
+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));
+    });
+  }
+};
+
+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>{});
+      }});
+  return 0;
+}



More information about the libcxx-commits mailing list