From 460778b9d64ed36fd7e80a4cc73ea552aebf016b Mon Sep 17 00:00:00 2001 From: "Michael G. Kazakov" Date: Tue, 21 Jul 2026 12:42:26 +0100 Subject: [PATCH] [libc++][pstl] Default implementation of parallel std::adjacent_difference (#207585) This PR adds a "one-liner" default implementation of parallel `std::adjacent_difference` on top of parallel binary `std::transform`. The implementation builds two iterator ranges out of the input one, so that a zip of these ranges yields adjacent pairs. Then performs a binary transform to calculate and output the adjacent differences. Part of https://github.com/llvm/llvm-project/issues/99938 --- libcxx/include/__numeric/pstl.h | 39 ++++ libcxx/include/__pstl/backend_fwd.h | 7 + libcxx/include/__pstl/backends/default.h | 35 +++- .../pstl.iterator-requirements.verify.cpp | 11 ++ .../algorithms/pstl.nodiscard.verify.cpp | 5 + .../pstl.exception_handling.pass.cpp | 16 +- .../pstl.adjacent_difference.pass.cpp | 161 +++++++++++++++++ .../pstl.adjacent_difference_op.pass.cpp | 171 ++++++++++++++++++ libcxx/test/support/runway_sample.h | 58 ++++++ .../test.support/runway_sample.pass.cpp | 75 ++++++++ 10 files changed, 574 insertions(+), 4 deletions(-) create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp create mode 100644 libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp create mode 100644 libcxx/test/support/runway_sample.h create mode 100644 libcxx/test/support/test.support/runway_sample.pass.cpp diff --git a/libcxx/include/__numeric/pstl.h b/libcxx/include/__numeric/pstl.h index ecbc9f534833..0b85cfb8c232 100644 --- a/libcxx/include/__numeric/pstl.h +++ b/libcxx/include/__numeric/pstl.h @@ -165,6 +165,45 @@ template , + enable_if_t, int> = 0> +[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference( + _ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) { + _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators"); + _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators"); + using _Implementation = + __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>; + return __pstl::__handle_exception<_Implementation>( + std::forward<_ExecutionPolicy>(__policy), std::move(__first1), std::move(__last1), std::move(__first2), minus{}); +} + +template , + enable_if_t, int> = 0> +[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator2 adjacent_difference( + _ExecutionPolicy&& __policy, + _ForwardIterator1 __first1, + _ForwardIterator1 __last1, + _ForwardIterator2 __first2, + _BinaryOperation __op) { + _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "adjacent_difference requires ForwardIterators"); + _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "adjacent_difference requires ForwardIterators"); + using _Implementation = + __pstl::__dispatch<__pstl::__adjacent_difference, __pstl::__current_configuration, _RawPolicy>; + return __pstl::__handle_exception<_Implementation>( + std::forward<_ExecutionPolicy>(__policy), + std::move(__first1), + std::move(__last1), + std::move(__first2), + std::move(__op)); +} + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h index a84a768cad57..2ba5b4434fc3 100644 --- a/libcxx/include/__pstl/backend_fwd.h +++ b/libcxx/include/__pstl/backend_fwd.h @@ -317,6 +317,13 @@ struct __is_sorted; // optional // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept; +template +struct __adjacent_difference; +// template +// optional<_ForwardIterator2> +// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, +// _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept; + } // namespace __pstl _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h index 5c78d4efa412..28bc75be147a 100644 --- a/libcxx/include/__pstl/backends/default.h +++ b/libcxx/include/__pstl/backends/default.h @@ -92,11 +92,12 @@ namespace __pstl { // // transform and transform_binary family // ------------------------------------- -// - replace_copy_if -// - replace_copy -// - move +// - adjacent_difference // - copy // - copy_n +// - move +// - replace_copy +// - replace_copy_if // - reverse_copy // - rotate_copy // @@ -575,6 +576,34 @@ struct __reverse_copy<__default_backend_tag, _ExecutionPolicy> { } }; +template +struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator2> + operator()(_Policy&& __policy, + _ForwardIterator1 __first1, + _ForwardIterator1 __last1, + _ForwardIterator2 __result, + _BinaryOperation&& __op) const noexcept { + using _TransformBinary = __dispatch<__transform_binary, __current_configuration, _ExecutionPolicy>; + if (__first1 == __last1) + return __result; // edge case: empty input range, just return the output iterator + *__result = *__first1; + ++__result; + _ForwardIterator1 __first2 = std::next(__first1); + if (__first2 == __last1) + return __result; // edge case: not enough elements to perform adjacent difference, just return the output iterator + // Process as a binary transform of two iterator ranges: [__first1 + 1, __last1) and [__first1, __last1 - 1) + return _TransformBinary()( + __policy, + std::move(__first2), + std::move(__last1), + std::move(__first1), + std::move(__result), + std::forward<_BinaryOperation>(__op)); + } +}; + } // namespace __pstl _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp index fbe3460b67f1..402d8fbf9131 100644 --- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp +++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp @@ -211,4 +211,15 @@ void f(non_forward_iterator non_fwd, (void)std::transform_reduce( pol, non_fwd, non_fwd, val, func, func); // expected-error@*:* {{static assertion failed: transform_reduce}} } + + { + (void)std::adjacent_difference( + pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: adjacent_difference}} + (void)std::adjacent_difference( + pol, it, it, non_fwd, func); // expected-error@*:* {{static assertion failed: adjacent_difference}} + (void)std::adjacent_difference( + pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: adjacent_difference}} + (void)std::adjacent_difference( + pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}} + } } diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp index 049c8dc88c2d..f4fffd456a33 100644 --- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp +++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp @@ -15,6 +15,7 @@ #include #include #include +#include void test() { int a[] = {1}; @@ -58,4 +59,8 @@ void test() { std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::reverse_copy(std::execution::par, std::begin(a), std::end(a), std::begin(b)); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b)); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2); } diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp index 8af2a0a15fd5..e00046fdb1dd 100644 --- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp +++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp @@ -85,7 +85,7 @@ int main(int, char**) { auto last1 = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1); auto first2 = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1); auto last2 = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1); - auto dest = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1); + auto dest = util::throw_on_move_iterator(std::begin(storage), tokens[4].active() ? 1 : -1); auto maybe_throw = [](ThrowToken const& token, auto f) { return [&token, f](auto... args) { if (token.active()) @@ -365,6 +365,20 @@ int main(int, char**) { (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction); }); } + + { + auto op = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; }); + + // adjacent_difference(first, last, dest) + assert_non_throwing([=, &policy] { + (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest)); + }); + + // adjacent_difference(first, last, dest, op) + assert_non_throwing([=, &policy] { + (void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op); + }); + } } }); } 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 000000000000..d06f5b64e2f9 --- /dev/null +++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference.pass.cpp @@ -0,0 +1,161 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec, +// ForwardIterator1 first1, +// ForwardIterator1 last1, +// ForwardIterator2 first2); + +#include +#include +#include +#include +#include +#include + +#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(adjacent_difference); + +static_assert(sfinae_test_adjacent_difference); +static_assert(!sfinae_test_adjacent_difference); + +// Types X and Y are provided to test adjacent_difference() against custom types. +// X is a source type that supports subtraction. +// Y is a destination type that can be assigned from X and compared for equality. + +class X { + int i_; + + X& operator=(const X&); + +public: + explicit X(int i) : i_(i) {} + X(const X& x) : i_(x.i_) {} + X& operator=(X&& x) { + i_ = x.i_; + x.i_ = -1; + return *this; + } + + friend X operator-(const X& x, const X& y) { return X(x.i_ - y.i_); } + + friend class Y; +}; + +class Y { + int i_; + + Y& operator=(const Y&); + +public: + explicit Y(int i) : i_(i) {} + Y(const Y& y) : i_(y.i_) {} + void operator=(const X& x) { i_ = x.i_; } + bool operator==(const Y& y) const { return i_ == y.i_; } +}; + +template +struct Test { + template + void operator()(ExecutionPolicy&& policy) { + { + int ia[] = {10}; + int ir[] = {42}; + int ib[] = {42}; + Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib))); + assert(r == Iter2(std::begin(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[] = {10}; + int ir[] = {10}; + int ib[] = {42}; + Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib))); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[] = {10, 30}; + int ir[] = {10, 20}; + int ib[std::size(ia)] = {0}; + Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib))); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[] = {15, 10, 6, 3, 1}; + int ir[] = {15, -5, -4, -3, -2}; + int ib[std::size(ia)] = {0}; + Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib))); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[1073]; + int ib[1073]; + std::iota(std::begin(ia), std::end(ia), 1); + runway_sample(std::size(ia), [&](size_t i) { + Iter2 r = + std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib))); + assert(r == Iter2(std::begin(ib) + i)); + assert(std::all_of(std::begin(ib), std::begin(ib) + i, [](int x) { return x == 1; })); + }); + } + } +}; + +template +struct TestCustomTypes { + template + void operator()(ExecutionPolicy&& policy) { + { + X ia[3] = {X(1), X(5), X(7)}; + Y ir[3] = {Y(1), Y(4), Y(2)}; + Y ib[3] = {Y(0), Y(0), Y(0)}; + Iter2 r = std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib))); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + } +}; + +int main(int, char**) { + types::for_each( + types::concatenate_t, types::forward_iterator_list>{}, + types::apply_type_identity{[](auto v) { + using Iter = typename decltype(v)::type; + types::for_each(types::forward_iterator_list{}, + TestIteratorWithPolicies::template apply>{}); + }}); + types::for_each( + types::concatenate_t, types::forward_iterator_list>{}, + types::apply_type_identity{[](auto v) { + using Iter = typename decltype(v)::type; + types::for_each( + types::forward_iterator_list{}, + TestIteratorWithPolicies::template apply>{}); + }}); + return 0; +} diff --git a/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp new file mode 100644 index 000000000000..d623cda058d0 --- /dev/null +++ b/libcxx/test/std/numerics/numeric.ops/adjacent.difference/pstl.adjacent_difference_op.pass.cpp @@ -0,0 +1,171 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec, +// ForwardIterator1 first1, +// ForwardIterator1 last1, +// ForwardIterator2 first2, +// BinaryOperation op); + +#include +#include +#include +#include +#include +#include + +#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(adjacent_difference); + +static_assert(sfinae_test_adjacent_difference); +static_assert(!sfinae_test_adjacent_difference); + +// Types X and Y are provided to test adjacent_difference() against custom types. +// X is a source type that supports subtraction. +// Y is a destination type that can be assigned from X and compared for equality. + +class X { + int i_; + X& operator=(const X&); + +public: + explicit X(int i) : i_(i) {} + X(const X& x) : i_(x.i_) {} + X& operator=(X&& x) { + i_ = x.i_; + x.i_ = -1; + return *this; + } + friend class Y; + friend struct Op; +}; + +class Y { + int i_; + Y& operator=(const Y&); + +public: + explicit Y(int i) : i_(i) {} + Y(const Y& y) : i_(y.i_) {} + void operator=(const X& x) { i_ = x.i_; } + bool operator==(const Y& y) const { return i_ == y.i_; } +}; + +// ensures that the predicate is not equivalent to std::minus +struct Op { + int operator()(int l, int r) const { return l * 2 - r; } + X operator()(const X& l, const X& r) const { return X(l.i_ * 2 - r.i_); } +}; + +template +struct Test { + template + void operator()(ExecutionPolicy&& policy) { + { + int ia[] = {10}; + int ir[] = {42}; + int ib[] = {42}; + Iter2 r = + std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::begin(ia)), Iter2(std::begin(ib)), Op{}); + assert(r == Iter2(std::begin(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[] = {10}; + int ir[] = {10}; + int ib[] = {42}; + Iter2 r = + std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{}); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[] = {10, 30}; + int ir[] = {10, 50}; + int ib[std::size(ia)] = {0}; + Iter2 r = + std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{}); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[] = {15, 10, 6, 3, 1}; + int ir[] = {15, 5, 2, 0, -1}; + int ib[std::size(ia)] = {0}; + Iter2 r = + std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{}); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + { + int ia[1073]; + int ib[1073]; + std::iota(std::begin(ia), std::end(ia), 1); + runway_sample(std::size(ia), [&](size_t i) { + Iter2 r = std::adjacent_difference( + policy, Iter1(std::begin(ia)), Iter1(std::begin(ia) + i), Iter2(std::begin(ib)), Op{}); + assert(r == Iter2(std::begin(ib) + i)); + for (size_t j = 0; j < i; ++j) + assert(ib[j] == (int)(j == 0 ? 1 : j + 2)); + }); + } + } +}; + +template +struct TestCustomTypes { + template + void operator()(ExecutionPolicy&& policy) { + { + X ia[3] = {X(1), X(5), X(8)}; + Y ir[3] = {Y(1), Y(9), Y(11)}; + Y ib[3] = {Y(0), Y(0), Y(0)}; + Iter2 r = + std::adjacent_difference(policy, Iter1(std::begin(ia)), Iter1(std::end(ia)), Iter2(std::begin(ib)), Op{}); + assert(r == Iter2(std::end(ib))); + for (size_t i = 0; i < std::size(ia); ++i) + assert(ib[i] == ir[i]); + } + } +}; + +int main(int, char**) { + types::for_each( + types::concatenate_t, types::forward_iterator_list>{}, + types::apply_type_identity{[](auto v) { + using Iter = typename decltype(v)::type; + types::for_each(types::forward_iterator_list{}, + TestIteratorWithPolicies::template apply>{}); + }}); + types::for_each( + types::concatenate_t, types::forward_iterator_list>{}, + types::apply_type_identity{[](auto v) { + using Iter = typename decltype(v)::type; + types::for_each( + types::forward_iterator_list{}, + TestIteratorWithPolicies::template apply>{}); + }}); + return 0; +} diff --git a/libcxx/test/support/runway_sample.h b/libcxx/test/support/runway_sample.h new file mode 100644 index 000000000000..16ecd58cef74 --- /dev/null +++ b/libcxx/test/support/runway_sample.h @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#ifndef TEST_SUPPORT_RUNWAY_SAMPLE_H +#define TEST_SUPPORT_RUNWAY_SAMPLE_H + +#include +#include +#include "test_macros.h" + +// runway_sample(size, callable) calls callable(i) for a set of sample indices i in the range [0, size). +// they are chosen in 3 phases: +// - prefix, sampled densely with increments of 1 +// - middle, sampled sparsely with uncanny numbers +// - suffix, sampled densely with increments of 1 +// +// Examples of sample indices for various sizes: +// size = 0: +// +// size = 1: +// 0 +// size = 10: +// 0 1 2 3 4 5 6 7 8 9 +// size = 100: +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 +// size = 1000: +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 50 157 493 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 +template +TEST_CONSTEXPR_CXX14 void runway_sample(std::size_t size, Callable callable) { + constexpr std::size_t affix = 16; + std::size_t i = 0; + + // 0, 1, 2, ..., 15 + for (; i < std::min(size, affix); ++i) { + callable(i); + } + + if (size <= affix) { + return; + } + + // 16, 50, 157, 493, 1549, 4868, ... + for (std::size_t j = i; j + affix < size; i = j, j = j * 3 + j / 7) { + callable(j); + } + + // size - 16, size - 15, ..., size - 1 + i = std::max(i, size - affix); + for (; i < size; ++i) { + callable(i); + } +} + +#endif // TEST_SUPPORT_RUNWAY_SAMPLE_H diff --git a/libcxx/test/support/test.support/runway_sample.pass.cpp b/libcxx/test/support/test.support/runway_sample.pass.cpp new file mode 100644 index 000000000000..6c6eaafc3439 --- /dev/null +++ b/libcxx/test/support/test.support/runway_sample.pass.cpp @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// "support/runway_sample.h" + +#include +#include +#include +#include + +#include "test_macros.h" +#include "runway_sample.h" + +constexpr bool test() { + { + runway_sample(0, [&](std::size_t /*i*/) { assert(false); }); + } + { + std::size_t expected[] = {0}; + std::size_t n = 0; + runway_sample(1, [&](std::size_t i) { + assert(expected[n] == i); + n++; + }); + } + { + std::size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::size_t n = 0; + runway_sample(10, [&](std::size_t i) { + assert(expected[n] == i); + n++; + }); + } + { + std::size_t expected[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; + std::size_t n = 0; + runway_sample(30, [&](std::size_t i) { + assert(expected[n] == i); + n++; + }); + } + { + std::size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 50, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99}; + std::size_t n = 0; + runway_sample(100, [&](std::size_t i) { + assert(expected[n] == i); + n++; + }); + } + { + std::size_t expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 50, + 157, 493, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999}; + std::size_t n = 0; + runway_sample(1000, [&](std::size_t i) { + assert(expected[n] == i); + n++; + }); + } + return true; +} + +int main() { + test(); + static_assert(test()); + return 0; +}