[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
This commit is contained in:
parent
faaeaf35a5
commit
460778b9d6
10 changed files with 574 additions and 4 deletions
|
|
@ -165,6 +165,45 @@ template <class _ExecutionPolicy,
|
|||
std::move(__transform));
|
||||
}
|
||||
|
||||
template <class _ExecutionPolicy,
|
||||
class _ForwardIterator1,
|
||||
class _ForwardIterator2,
|
||||
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
|
||||
enable_if_t<is_execution_policy_v<_RawPolicy>, 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 <class _ExecutionPolicy,
|
||||
class _ForwardIterator1,
|
||||
class _ForwardIterator2,
|
||||
class _BinaryOperation,
|
||||
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
|
||||
enable_if_t<is_execution_policy_v<_RawPolicy>, 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
|
||||
|
|
|
|||
|
|
@ -317,6 +317,13 @@ struct __is_sorted;
|
|||
// optional<bool>
|
||||
// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
|
||||
|
||||
template <class _Backend, class _ExecutionPolicy>
|
||||
struct __adjacent_difference;
|
||||
// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
|
||||
// optional<_ForwardIterator2>
|
||||
// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
|
||||
// _ForwardIterator2 __first2, _BinaryOperation &&__op) const noexcept;
|
||||
|
||||
} // namespace __pstl
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <class _ExecutionPolicy>
|
||||
struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
|
||||
template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
|
||||
[[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
|
||||
|
||||
|
|
|
|||
|
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <algorithm>
|
||||
#include <execution>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <class ExecutionPolicy,
|
||||
// class ForwardIterator1,
|
||||
// class ForwardIterator2>
|
||||
// ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
|
||||
// ForwardIterator1 first1,
|
||||
// ForwardIterator1 last1,
|
||||
// ForwardIterator2 first2);
|
||||
|
||||
#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(adjacent_difference);
|
||||
|
||||
static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*>);
|
||||
static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*>);
|
||||
|
||||
// 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 <class Iter1, class Iter2>
|
||||
struct Test {
|
||||
template <class ExecutionPolicy>
|
||||
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 <class Iter1, class Iter2>
|
||||
struct TestCustomTypes {
|
||||
template <class ExecutionPolicy>
|
||||
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<int*>, 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<Test, Iter>::template apply>{});
|
||||
}});
|
||||
types::for_each(
|
||||
types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
|
||||
types::apply_type_identity{[](auto v) {
|
||||
using Iter = typename decltype(v)::type;
|
||||
types::for_each(
|
||||
types::forward_iterator_list<Y*>{},
|
||||
TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
|
||||
}});
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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 <class ExecutionPolicy,
|
||||
// class ForwardIterator1,
|
||||
// class ForwardIterator2,
|
||||
// class BinaryOperation>
|
||||
// ForwardIterator2 adjacent_difference(ExecutionPolicy&& exec,
|
||||
// ForwardIterator1 first1,
|
||||
// ForwardIterator1 last1,
|
||||
// ForwardIterator2 first2,
|
||||
// BinaryOperation op);
|
||||
|
||||
#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(adjacent_difference);
|
||||
|
||||
static_assert(sfinae_test_adjacent_difference<int, int*, int*, int*, int (*)(int, int)>);
|
||||
static_assert(!sfinae_test_adjacent_difference<std::execution::parallel_policy, int*, int*, int*, int (*)(int, int)>);
|
||||
|
||||
// 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 <class Iter1, class Iter2>
|
||||
struct Test {
|
||||
template <class ExecutionPolicy>
|
||||
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 <class Iter1, class Iter2>
|
||||
struct TestCustomTypes {
|
||||
template <class ExecutionPolicy>
|
||||
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<int*>, 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<Test, Iter>::template apply>{});
|
||||
}});
|
||||
types::for_each(
|
||||
types::concatenate_t<types::forward_iterator_list<X*>, types::forward_iterator_list<const X*>>{},
|
||||
types::apply_type_identity{[](auto v) {
|
||||
using Iter = typename decltype(v)::type;
|
||||
types::for_each(
|
||||
types::forward_iterator_list<Y*>{},
|
||||
TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
|
||||
}});
|
||||
return 0;
|
||||
}
|
||||
58
libcxx/test/support/runway_sample.h
Normal file
58
libcxx/test/support/runway_sample.h
Normal file
|
|
@ -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 <cstddef>
|
||||
#include <algorithm>
|
||||
#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:
|
||||
// <no samples>
|
||||
// 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 <class Callable>
|
||||
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
|
||||
75
libcxx/test/support/test.support/runway_sample.pass.cpp
Normal file
75
libcxx/test/support/test.support/runway_sample.pass.cpp
Normal file
|
|
@ -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 <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
#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;
|
||||
}
|
||||
Loading…
Reference in a new issue