Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 54 additions & 14 deletions include/oneapi/dpl/pstl/glue_algorithm_ranges_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ struct __find_fn
std::ranges::borrowed_iterator_t<_R>
operator()(_ExecutionPolicy&& __exec, _R&& __r, const _T& __value, _Proj __proj = {}) const
{
// TODO: make sure std::ranges::equal_to is used for comparison
return oneapi::dpl::ranges::find_if(std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r),
oneapi::dpl::__internal::__equal_value<oneapi::dpl::__internal::__ref_or_copy<_ExecutionPolicy,
const _T>>(__value), __proj);
Expand Down Expand Up @@ -433,6 +434,7 @@ struct __count_fn
operator()(_ExecutionPolicy&& __exec, _R&& __r, const _T& __value, _Proj __proj = {}) const
{
const auto __dispatch_tag = oneapi::dpl::__ranges::__select_backend(__exec);
// TODO: make sure std::ranges::equal_to is used for comparison
return oneapi::dpl::__internal::__ranges::__pattern_count(__dispatch_tag,
std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r), __value, __proj);
}
Expand Down Expand Up @@ -1070,6 +1072,7 @@ struct __replace_fn
operator()(_ExecutionPolicy&& __exec, _R&& __r, const _T1& __old_value, const _T2& __new_value,
_Proj __proj = {}) const
{
// TODO: make sure std::ranges::equal_to is used for comparison
return oneapi::dpl::ranges::replace_if(
std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r),
oneapi::dpl::__internal::__equal_value<oneapi::dpl::__internal::__ref_or_copy<_ExecutionPolicy, const _T1>>(
Expand Down Expand Up @@ -1229,17 +1232,16 @@ struct __mismatch_fn

inline constexpr __internal::__mismatch_fn mismatch;

// [alg.remove_if]
// [alg.remove]

namespace __internal
{

struct __remove_if_fn
{
template<typename _ExecutionPolicy, std::ranges::random_access_range _R, typename _Proj = std::identity,
std::indirect_unary_predicate<std::projected<std::ranges::iterator_t<_R>, _Proj>> _Pred>
template <typename _ExecutionPolicy, std::ranges::random_access_range _R, typename _Proj = std::identity,
std::indirect_unary_predicate<std::projected<std::ranges::iterator_t<_R>, _Proj>> _Pred>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<_ExecutionPolicy>>
&& std::permutable<std::ranges::iterator_t<_R>> && std::ranges::sized_range<_R>
&& std::ranges::sized_range<_R> && std::permutable<std::ranges::iterator_t<_R>>

std::ranges::borrowed_subrange_t<_R>
operator()(_ExecutionPolicy&& __exec, _R&& __r, _Pred __pred, _Proj __proj = {}) const
Expand All @@ -1248,36 +1250,74 @@ struct __remove_if_fn
return oneapi::dpl::__internal::__ranges::__pattern_remove_if(__dispatch_tag,
std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r), __pred, __proj);
}

}; //__remove_if_fn
} //__internal

inline constexpr __internal::__remove_if_fn remove_if;

// [alg.remove]

namespace __internal
{

struct __remove_fn
{
template<typename _ExecutionPolicy, std::ranges::random_access_range _R, typename _Proj = std::identity,
typename _T = oneapi::dpl::projected_value_t<std::ranges::iterator_t<_R>, _Proj>>
template <typename _ExecutionPolicy, std::ranges::random_access_range _R, typename _Proj = std::identity,
typename _T = oneapi::dpl::projected_value_t<std::ranges::iterator_t<_R>, _Proj>>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<_ExecutionPolicy>>
&& std::permutable<std::ranges::iterator_t<_R>> && std::indirect_binary_predicate<std::ranges::equal_to,
std::projected<std::ranges::iterator_t<_R>, _Proj>, const _T*> && std::ranges::sized_range<_R>
&& std::ranges::sized_range<_R> && std::permutable<std::ranges::iterator_t<_R>>
&& std::indirect_binary_predicate<std::ranges::equal_to,
std::projected<std::ranges::iterator_t<_R>, _Proj>, const _T*>

std::ranges::borrowed_subrange_t<_R>
operator()(_ExecutionPolicy&& __exec, _R&& __r, const _T& __value, _Proj __proj = {}) const
{
// TODO: change lambda to a special functor
return oneapi::dpl::ranges::remove_if(std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r),
[__value](auto&& __a) { return std::ranges::equal_to{}(__a, __value);}, __proj);
}

}; //__remove_fn

struct __remove_copy_if_fn
{
template <typename _ExecutionPolicy, std::ranges::random_access_range _R, std::ranges::random_access_range _OutR,
typename _Proj = std::identity,
std::indirect_unary_predicate<std::projected<std::ranges::iterator_t<_R>, _Proj>> _Pred>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<_ExecutionPolicy>>
&& std::ranges::sized_range<_R> && std::ranges::sized_range<_OutR>
&& std::indirectly_copyable<std::ranges::iterator_t<_R>, std::ranges::iterator_t<_OutR>>

std::ranges::remove_copy_if_result<std::ranges::borrowed_iterator_t<_R>, std::ranges::borrowed_iterator_t<_OutR>>
operator()(_ExecutionPolicy&& __exec, _R&& __r, _OutR&& __out_r, _Pred __pred, _Proj __proj = {}) const
{
return oneapi::dpl::ranges::copy_if(std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r),
std::forward<_OutR>(__out_r), oneapi::dpl::__internal::__not_pred<
oneapi::dpl::__internal::__ref_or_copy<_ExecutionPolicy, _Pred>>(__pred), __proj);
}
}; //__remove_copy_if_fn

struct __remove_copy_fn
{
template <typename _ExecutionPolicy, std::ranges::random_access_range _R, std::ranges::random_access_range _OutR,
typename _Proj = std::identity,
typename _T = oneapi::dpl::projected_value_t<std::ranges::iterator_t<_R>, _Proj>>
requires oneapi::dpl::is_execution_policy_v<std::remove_cvref_t<_ExecutionPolicy>>
&& std::ranges::sized_range<_R> && std::ranges::sized_range<_OutR>
&& std::indirectly_copyable<std::ranges::iterator_t<_R>, std::ranges::iterator_t<_OutR>>
&& std::indirect_binary_predicate<std::ranges::equal_to,
std::projected<std::ranges::iterator_t<_R>, _Proj>, const _T*>

std::ranges::remove_copy_result<std::ranges::borrowed_iterator_t<_R>, std::ranges::borrowed_iterator_t<_OutR>>
operator()(_ExecutionPolicy&& __exec, _R&& __r, _OutR&& __out_r, const _T& __value, _Proj __proj = {}) const
{
// TODO: make sure std::ranges::equal_to is used for comparison
return oneapi::dpl::ranges::copy_if(std::forward<_ExecutionPolicy>(__exec), std::forward<_R>(__r),
std::forward<_OutR>(__out_r), oneapi::dpl::__internal::__not_equal_value<
oneapi::dpl::__internal::__ref_or_copy<_ExecutionPolicy, const _T>>(__value), __proj);
}
}; //__remove_copy_fn
} //__internal

inline constexpr __internal::__remove_fn remove;
inline constexpr __internal::__remove_copy_if_fn remove_copy_if;
inline constexpr __internal::__remove_copy_fn remove_copy;

// [alg.unique]

Expand Down
120 changes: 120 additions & 0 deletions test/parallel_api/ranges/std_ranges_remove_copy.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Copyright (C) UXL Foundation Contributors
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "std_ranges_test.h"

#if _ENABLE_STD_RANGES_TESTING
#include <initializer_list>

struct
{
template <std::ranges::random_access_range InRange, std::ranges::random_access_range OutRange,
typename V, typename Proj = std::identity>
auto operator()(InRange&& r_in, OutRange&& r_out, const V& value, Proj proj = {})
{
using ret_type = std::ranges::remove_copy_result<std::ranges::borrowed_iterator_t<InRange>,
std::ranges::borrowed_iterator_t<OutRange>>;
auto in = std::ranges::begin(r_in);
auto out = std::ranges::begin(r_out);
std::size_t i = 0, j = 0;
for(; i < std::ranges::size(r_in); ++i)
{
if (!std::ranges::equal_to{}(std::invoke(proj, in[i]), value))
{
if (j < std::ranges::size(r_out))
out[j++] = in[i];
else
break;
}
}
return ret_type{in + i, out + j};
}

void test_self()
{
#if TEST_CPP20_SPAN_PRESENT
int input[10] = {0,0, 1, 2,2, 8, 1,1,1, 8};
int output[9] = {-9, -8, -7, -6, -5, -4, -3, -2, -1};

// Define test cases with expected outputs and expected end positions
struct TestCase {
int in_size;
int out_size;
std::initializer_list<int> expected_output;
int expected_in_end;
int expected_out_end;
} test_cases[] = {
// insz, outsz, expected, instop, outstop
{0, 0, {}, 0, 0}, // Empty ranges
{10, 0, {}, 0, 0}, // Empty output range
{1, 1, {0}, 1, 1}, // One element ranges
{10, 1, {0}, 1, 1}, // One element output range
{10, 5, {0, 0, 2, 2, 8}, 9, 5}, // Output range is not big enough
{10, 6, {0, 0, 2, 2, 8, 8}, 10, 6}, // Output range is just enough
{10, 7, {0, 0, 2, 2, 8, 8}, 10, 6}, // Output range is bigger than needed
};

auto& self = *this;
for (const TestCase& test_case : test_cases) {
constexpr int shift = 1;
std::span<int> in_span(input, test_case.in_size);
std::span<int> out_span(output + shift, test_case.out_size);

auto result = self(in_span, out_span, 1);

// Verify the returned iterators point to the correct end positions
EXPECT_EQ(in_span.begin() + test_case.expected_in_end, result.in, "Checker problem: wrong input stop");
EXPECT_EQ(out_span.begin() + test_case.expected_out_end, result.out, "Checker problem: wrong output stop");

// Verify the output matches the expected result and nothing is overwritten
for (int i = 0; i < 9; ++i)
{
if (i < shift || i >= shift + test_case.expected_out_end)
{
EXPECT_EQ(i - 9, output[i], "Checker problem: out of range modification");
}
else
{
EXPECT_EQ(test_case.expected_output.begin()[i - shift], output[i], "Checker problem: wrong output");
output[i] = i - 9; // Restore the original output data
}
}
}
#endif // TEST_CPP20_SPAN_PRESENT
}
} remove_copy_checker;
#endif // _ENABLE_STD_RANGES_TESTING

std::int32_t
main()
{
#if _ENABLE_STD_RANGES_TESTING
using namespace test_std_ranges;
namespace dpl_ranges = oneapi::dpl::ranges;

auto almost_always_two = [](auto i) {
if (i%7 > 0 && (i - 1)%3 == 0)
return i;
return 2;
};
using many_twos = decltype(almost_always_two);

remove_copy_checker.test_self();

test_range_algo<0, int, data_in_out_lim>{179}(dpl_ranges::remove_copy, remove_copy_checker, 0);
test_range_algo<1, int, data_in_out_lim, many_twos>{1127}(dpl_ranges::remove_copy, remove_copy_checker, 2);
test_range_algo<2, int, data_in_out_lim>{}(dpl_ranges::remove_copy, remove_copy_checker, 1, proj);
test_range_algo<3, P2, data_in_out_lim, many_twos>{}(dpl_ranges::remove_copy, remove_copy_checker, 2, &P2::x);
test_range_algo<4, P2, data_in_out_lim>{}(dpl_ranges::remove_copy, remove_copy_checker, 0, &P2::proj);
test_range_algo<5, int, data_in_out_lim>{big_sz}(dpl_ranges::remove_copy, remove_copy_checker, 1);
test_range_algo<6, int, data_in_out_lim, many_twos>{big_sz}(dpl_ranges::remove_copy, remove_copy_checker, 2);
#endif // _ENABLE_STD_RANGES_TESTING

return TestUtils::done(_ENABLE_STD_RANGES_TESTING);
}
125 changes: 125 additions & 0 deletions test/parallel_api/ranges/std_ranges_remove_copy_if.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Copyright (C) UXL Foundation Contributors
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "std_ranges_test.h"

#if _ENABLE_STD_RANGES_TESTING
#include <initializer_list>

struct
{
template <std::ranges::random_access_range InRange, std::ranges::random_access_range OutRange,
typename Pred, typename Proj = std::identity>
auto operator()(InRange&& r_in, OutRange&& r_out, Pred pred, Proj proj = {})
{
using ret_type = std::ranges::remove_copy_if_result<std::ranges::borrowed_iterator_t<InRange>,
std::ranges::borrowed_iterator_t<OutRange>>;
auto in = std::ranges::begin(r_in);
auto out = std::ranges::begin(r_out);
std::size_t i = 0, j = 0;
for(; i < std::ranges::size(r_in); ++i)
{
if (!std::invoke(pred, std::invoke(proj, in[i])))
{
if (j < std::ranges::size(r_out))
out[j++] = in[i];
else
break;
}
}
return ret_type{in + i, out + j};
}

void test_self()
{
#if TEST_CPP20_SPAN_PRESENT
int input[10] = {0,0, 1, 2,2, 8, 1,1,1, 8};
int output[9] = {-9, -8, -7, -6, -5, -4, -3, -2, -1};

// Define test cases with expected outputs and expected end positions
struct TestCase {
int in_size;
int out_size;
std::initializer_list<int> expected_output;
int expected_in_end;
int expected_out_end;
} test_cases[] = {
// insz, outsz, expected, instop, outstop
{0, 0, {}, 0, 0}, // Empty ranges
{10, 0, {}, 0, 0}, // Empty output range
{1, 1, {0}, 1, 1}, // One element ranges
{10, 1, {0}, 1, 1}, // One element output range
{10, 5, {0, 0, 2, 2, 8}, 9, 5}, // Output range is not big enough
{10, 6, {0, 0, 2, 2, 8, 8}, 10, 6}, // Output range is just enough
{10, 7, {0, 0, 2, 2, 8, 8}, 10, 6}, // Output range is bigger than needed
};

auto& self = *this;
for (const TestCase& test_case : test_cases) {
constexpr int shift = 1;
std::span<int> in_span(input, test_case.in_size);
std::span<int> out_span(output + shift, test_case.out_size);

auto result = self(in_span, out_span, [](int v){ return v == 1; });

// Verify the returned iterators point to the correct end positions
EXPECT_EQ(in_span.begin() + test_case.expected_in_end, result.in, "Checker problem: wrong input stop");
EXPECT_EQ(out_span.begin() + test_case.expected_out_end, result.out, "Checker problem: wrong output stop");

// Verify the output matches the expected result and nothing is overwritten
for (int i = 0; i < 9; ++i)
{
if (i < shift || i >= shift + test_case.expected_out_end)
{
EXPECT_EQ(i - 9, output[i], "Checker problem: out of range modification");
}
else
{
EXPECT_EQ(test_case.expected_output.begin()[i - shift], output[i], "Checker problem: wrong output");
output[i] = i - 9; // Restore the original output data
}
}
}
#endif // TEST_CPP20_SPAN_PRESENT
}
} remove_copy_if_checker;
#endif // _ENABLE_STD_RANGES_TESTING

std::int32_t
main()
{
#if _ENABLE_STD_RANGES_TESTING
using namespace test_std_ranges;
namespace dpl_ranges = oneapi::dpl::ranges;

// input generator with a fair chance of repeating the previous value
auto repeat_sometimes = [](auto i) {
static decltype(i) last = 0;
if (i == 0)
last = 0; // reset
else if (i%7 > 0 && (last + i - 1)%3 == 0)
last = i;
return last;
};
using repeating_gen = decltype(repeat_sometimes);
auto modulo_3_is_1 = [](int val) { return (val % 3) == 1; };

remove_copy_if_checker.test_self();

test_range_algo<0, int, data_in_out_lim>{239}(dpl_ranges::remove_copy_if, remove_copy_if_checker, pred);
test_range_algo<1, int, data_in_out_lim>{1471}(dpl_ranges::remove_copy_if, remove_copy_if_checker, select_many);
test_range_algo<2, int, data_in_out_lim>{}(dpl_ranges::remove_copy_if, remove_copy_if_checker, select_many, proj);
test_range_algo<3, P2, data_in_out_lim, repeating_gen>{}(dpl_ranges::remove_copy_if, remove_copy_if_checker, modulo_3_is_1, &P2::x);
test_range_algo<4, P2, data_in_out_lim>{}(dpl_ranges::remove_copy_if, remove_copy_if_checker, pred, &P2::proj);
test_range_algo<5, int, data_in_out_lim>{big_sz}(dpl_ranges::remove_copy_if, remove_copy_if_checker, pred);
test_range_algo<6, int, data_in_out_lim, repeating_gen>{big_sz}(dpl_ranges::remove_copy_if, remove_copy_if_checker, select_many);
#endif // _ENABLE_STD_RANGES_TESTING

return TestUtils::done(_ENABLE_STD_RANGES_TESTING);
}
Loading
Loading