[libcxx-commits] [libcxx] [libc++][PSTL] Implement adjacent_difference (PR #102431)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Aug 8 07:41:48 PDT 2024


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/102431

>From 522dd60df4ee5d958abfdb178cf68f9e3960d0ab Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 8 Aug 2024 09:49:55 +0200
Subject: [PATCH] [libc++][PSTL] Implement adjacent_difference

---
 libcxx/docs/Status/PSTLPaper.csv              |   2 +-
 libcxx/include/__numeric/pstl.h               |  35 ++++++
 libcxx/include/__pstl/backend_fwd.h           |   8 ++
 libcxx/include/__pstl/backends/default.h      |  19 ++++
 .../pstl.adjacent_difference.pass.cpp         | 104 ++++++++++++++++++
 5 files changed, 167 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp

diff --git a/libcxx/docs/Status/PSTLPaper.csv b/libcxx/docs/Status/PSTLPaper.csv
index 3fb5adfe3ec46a..cb0e2423e42a36 100644
--- a/libcxx/docs/Status/PSTLPaper.csv
+++ b/libcxx/docs/Status/PSTLPaper.csv
@@ -1,5 +1,5 @@
 Section,Description,Assignee,Complete
-| `[adjacent.difference] <https://wg21.link/adjacent.difference>`_,std::adjacent_difference,Nikolas Klauser,|Not Started|
+| `[adjacent.difference] <https://wg21.link/adjacent.difference>`_,std::adjacent_difference,Nikolas Klauser,|Complete|
 | `[alg.adjacent.find] <https://wg21.link/alg.adjacent.find>`_,std::adjacent_find,Nikolas Klauser,|Not Started|
 | `[alg.all.of] <https://wg21.link/alg.all.of>`_,std::all_of,Nikolas Klauser,|Complete|
 | `[alg.any.of] <https://wg21.link/alg.any.of>`_,std::any_of,Nikolas Klauser,|Complete|
diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h
index 7557686a3663db..08d7fa9cbebd63 100644
--- a/libcxx/include/__numeric/pstl.h
+++ b/libcxx/include/__numeric/pstl.h
@@ -165,6 +165,41 @@ _LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
       std::move(__transform));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _ForwardOutIterator,
+          class _BinaryOperation,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator adjacent_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator __first,
+    _ForwardIterator __last,
+    _ForwardOutIterator __out_it,
+    _BinaryOperation __bin_op) {
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first),
+      std::move(__last),
+      std::move(__out_it),
+      std::move(__bin_op));
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _ForwardOutIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator adjacent_difference(
+    _ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _ForwardOutIterator __out_it) {
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__out_it), minus{});
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 32c5da576fb3c0..92cbb6dbbc2093 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -63,6 +63,14 @@ using __current_configuration = __backend_configuration<__libdispatch_backend_ta
 #  error "Invalid PSTL backend configuration"
 #endif
 
+template <class _Backend, class _ExecutionPolicy>
+struct __adjacent_difference;
+// template <class _Policy, class _ForwardIterator, class _ForwardOutIterator, class _BinaryOp>
+// optional<_ForwardOutIterator>
+// operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last,
+//            _ForwardOutIterator __out_it,
+//            _BinaryOp __bin_op) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __find_if;
 // template <class _Policy, class _ForwardIterator, class _Predicate>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 61a128805f8549..195a0b08e95450 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -19,6 +19,7 @@
 #include <__functional/operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
 #include <__utility/empty.h>
@@ -88,6 +89,7 @@ namespace __pstl {
 // - copy
 // - copy_n
 // - rotate_copy
+// - adjacent_difference
 //
 
 //////////////////////////////////////////////////////////////
@@ -495,6 +497,23 @@ struct __rotate_copy<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _ForwardOutIterator, class _BinaryOp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardOutIterator>
+  operator()(_Policy&& __policy,
+             _ForwardIterator __first,
+             _ForwardIterator __last,
+             _ForwardOutIterator __out_it,
+             _BinaryOp __bin_op) const noexcept {
+    if (__first == __last)
+      return __out_it;
+
+    using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>;
+    return _TransformBinary()(__policy, std::next(__first), __last, __first, __out_it, __bin_op);
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
new file mode 100644
index 00000000000000..b5e3902a4f0d0c
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <numeric>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
+//   ForwardIterator2
+//     adjacent_difference(ExecutionPolicy&& exec,
+//                         ForwardIterator1 first, ForwardIterator1 last, ForwardIterator2 result);
+//
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
+//          class BinaryOperation>
+//   ForwardIterator2
+//     adjacent_difference(ExecutionPolicy&& exec,
+//                         ForwardIterator1 first, ForwardIterator1 last,
+//                         ForwardIterator2 result, BinaryOperation binary_op);
+
+#include <algorithm>
+#include <array>
+#include <numeric>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <int N, int OutN = N - 1, class Policy>
+  void test(Policy&& policy,
+            std::array<int, N> input,
+            std::array<int, OutN> plus_expected,
+            std::array<int, OutN> minus_expected) {
+    {
+      std::array<int, OutN> out;
+      auto ret =
+          std::adjacent_difference(policy, Iter1(input.data()), Iter1(input.data() + input.size()), Iter2(out.data()));
+      assert(base(ret) == out.data() + out.size());
+      assert(out == minus_expected);
+    }
+    {
+      std::array<int, OutN> out;
+      auto ret = std::adjacent_difference(
+          policy, Iter1(input.data()), Iter1(input.data() + input.size()), Iter2(out.data()), std::plus{});
+      assert(base(ret) == out.data() + out.size());
+      assert(out == plus_expected);
+    }
+  }
+
+  template <class Policy>
+  void operator()(Policy&& policy) {
+    // simple test
+    test<4>(policy, {1, 2, 3, 4}, {3, 5, 7}, {1, 1, 1});
+    // empty range
+    test<0, 0>(policy, {}, {}, {});
+    // single element range
+    test<1>(policy, {1}, {}, {});
+    // two element range
+    test<2>(policy, {10, 5}, {15}, {-5});
+
+    // Large inputs with generated data
+    for (auto e : {100, 322, 497, 2048}) {
+      std::vector<int> input(e);
+      std::iota(input.begin(), input.end(), 0);
+      std::vector<int> expected(e - 1);
+      auto binop = [](int lhs, int rhs) { return lhs + rhs * 3; };
+      std::adjacent_difference(input.begin(), input.end(), expected.begin(), binop);
+      std::vector<int> output(e - 1);
+      std::adjacent_difference(input.begin(), input.end(), output.begin(), binop);
+      assert(output == expected);
+    }
+
+    { // ensure that all values are used exactly once
+      std::array input = {0, 1, 2, 3, 4, 5, 6, 7};
+      std::array<bool, input.size() - 1> called{};
+      std::array<int, input.size() - 1> output;
+      std::adjacent_difference(input.data(), input.data() + input.size(), output.data(), [&](int lhs, int rhs) {
+        assert(!called[rhs]);
+        called[rhs] = true;
+        return rhs - lhs;
+      });
+      assert(std::all_of(called.begin(), called.end(), [](bool b) { return b; }));
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v1) {
+                    using Iter1 = typename decltype(v1)::type;
+                    types::for_each(
+                        types::forward_iterator_list<int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter1>::template apply>{});
+                  }});
+
+  return 0;
+}



More information about the libcxx-commits mailing list