Skip to content

Commit ed27a4e

Browse files
philnik777ldionne
andauthored
[libc++][PSTL] Implement std::equal (#72448)
Differential Revision: https://reviews.llvm.org/D157131 Co-authored-by: Louis Dionne <[email protected]>
1 parent e1f59ad commit ed27a4e

File tree

8 files changed

+383
-1
lines changed

8 files changed

+383
-1
lines changed

libcxx/docs/Status/PSTLPaper.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Section,Description,Assignee,Complete
88
| `[alg.copy] <https://wg21.link/alg.copy>`_,std::copy_n,Nikolas Klauser,|Complete|
99
| `[alg.count] <https://wg21.link/alg.count>`_,std::count,Nikolas Klauser,|Complete|
1010
| `[alg.count] <https://wg21.link/alg.count>`_,std::count_if,Nikolas Klauser,|Complete|
11-
| `[alg.equal] <https://wg21.link/alg.equal>`_,std::equal,Nikolas Klauser,|Not Started|
11+
| `[alg.equal] <https://wg21.link/alg.equal>`_,std::equal,Nikolas Klauser,|Complete|
1212
| `[alg.exclusive.scan] <https://wg21.link/alg.exclusive.scan>`_,std::exclusive_scan,Nikolas Klauser,|Not Started|
1313
| `[alg.exclusive.scan] <https://wg21.link/alg.exclusive.scan>`_,std::exclusive_scan,Nikolas Klauser,|Not Started|
1414
| `[alg.fill] <https://wg21.link/alg.fill>`_,std::fill,Nikolas Klauser,|Complete|

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ set(files
8787
__algorithm/pstl_backends/cpu_backends/transform_reduce.h
8888
__algorithm/pstl_copy.h
8989
__algorithm/pstl_count.h
90+
__algorithm/pstl_equal.h
9091
__algorithm/pstl_fill.h
9192
__algorithm/pstl_find.h
9293
__algorithm/pstl_for_each.h

libcxx/include/__algorithm/pstl_backend.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ implemented, all the algorithms will eventually forward to the basis algorithms
177177
template <class _ExecutionPolicy, class _Iterator, class _Comp>
178178
optional<__empty> __pstl_sort(_Backend, _Iterator __first, _Iterator __last, _Comp __comp);
179179
180+
template <class _ExecutionPolicy, class _Iterator1, class _Iterator2, class _Comp>
181+
optional<bool> __pstl_equal(_Backend, _Iterator1 first1, _Iterator1 last1, _Iterator2 first2, _Comp __comp);
182+
180183
// TODO: Complete this list
181184
182185
Exception handling
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___ALGORITHM_PSTL_EQUAL_H
10+
#define _LIBCPP___ALGORITHM_PSTL_EQUAL_H
11+
12+
#include <__algorithm/equal.h>
13+
#include <__algorithm/pstl_frontend_dispatch.h>
14+
#include <__config>
15+
#include <__functional/operations.h>
16+
#include <__iterator/iterator_traits.h>
17+
#include <__numeric/pstl_transform_reduce.h>
18+
#include <__utility/move.h>
19+
20+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
21+
# pragma GCC system_header
22+
#endif
23+
24+
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
25+
26+
_LIBCPP_BEGIN_NAMESPACE_STD
27+
28+
template <class>
29+
void __pstl_equal();
30+
31+
template <class _ExecutionPolicy,
32+
class _ForwardIterator1,
33+
class _ForwardIterator2,
34+
class _Pred,
35+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
36+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
37+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<bool>
38+
__equal(_ExecutionPolicy&& __policy,
39+
_ForwardIterator1&& __first1,
40+
_ForwardIterator1&& __last1,
41+
_ForwardIterator2&& __first2,
42+
_Pred&& __pred) noexcept {
43+
return std::__pstl_frontend_dispatch(
44+
_LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_equal, _RawPolicy),
45+
[&__policy](
46+
_ForwardIterator1 __g_first1, _ForwardIterator1 __g_last1, _ForwardIterator2 __g_first2, _Pred __g_pred) {
47+
return std::__transform_reduce(
48+
__policy,
49+
std::move(__g_first1),
50+
std::move(__g_last1),
51+
std::move(__g_first2),
52+
true,
53+
std::logical_and{},
54+
std::move(__g_pred));
55+
},
56+
std::move(__first1),
57+
std::move(__last1),
58+
std::move(__first2),
59+
std::move(__pred));
60+
}
61+
62+
template <class _ExecutionPolicy,
63+
class _ForwardIterator1,
64+
class _ForwardIterator2,
65+
class _Pred,
66+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
67+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
68+
_LIBCPP_HIDE_FROM_ABI bool
69+
equal(_ExecutionPolicy&& __policy,
70+
_ForwardIterator1 __first1,
71+
_ForwardIterator1 __last1,
72+
_ForwardIterator2 __first2,
73+
_Pred __pred) {
74+
auto __res = std::__equal(__policy, std::move(__first1), std::move(__last1), std::move(__first2), std::move(__pred));
75+
if (!__res)
76+
std::__throw_bad_alloc();
77+
return *__res;
78+
}
79+
80+
template <class _ExecutionPolicy,
81+
class _ForwardIterator1,
82+
class _ForwardIterator2,
83+
enable_if_t<is_execution_policy_v<__remove_cvref_t<_ExecutionPolicy>>, int> = 0>
84+
_LIBCPP_HIDE_FROM_ABI bool
85+
equal(_ExecutionPolicy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
86+
return std::equal(__policy, std::move(__first1), std::move(__last1), std::move(__first2), std::equal_to{});
87+
}
88+
89+
template <class _ExecutionPolicy,
90+
class _ForwardIterator1,
91+
class _ForwardIterator2,
92+
class _Pred,
93+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
94+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
95+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<bool>
96+
__equal(_ExecutionPolicy&& __policy,
97+
_ForwardIterator1&& __first1,
98+
_ForwardIterator1&& __last1,
99+
_ForwardIterator2&& __first2,
100+
_ForwardIterator2&& __last2,
101+
_Pred&& __pred) noexcept {
102+
return std::__pstl_frontend_dispatch(
103+
_LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_equal, _RawPolicy),
104+
[&__policy](_ForwardIterator1 __g_first1,
105+
_ForwardIterator1 __g_last1,
106+
_ForwardIterator2 __g_first2,
107+
_ForwardIterator2 __g_last2,
108+
_Pred __g_pred) -> optional<bool> {
109+
if constexpr (__has_random_access_iterator_category<_ForwardIterator1>::value &&
110+
__has_random_access_iterator_category<_ForwardIterator2>::value) {
111+
if (__g_last1 - __g_first1 != __g_last2 - __g_first2)
112+
return false;
113+
return std::__equal(
114+
__policy, std::move(__g_first1), std::move(__g_last1), std::move(__g_first2), std::move(__g_pred));
115+
} else {
116+
(void)__policy; // Avoid unused lambda capture warning
117+
return std::equal(
118+
std::move(__g_first1),
119+
std::move(__g_last1),
120+
std::move(__g_first2),
121+
std::move(__g_last2),
122+
std::move(__g_pred));
123+
}
124+
},
125+
std::move(__first1),
126+
std::move(__last1),
127+
std::move(__first2),
128+
std::move(__last2),
129+
std::move(__pred));
130+
}
131+
132+
template <class _ExecutionPolicy,
133+
class _ForwardIterator1,
134+
class _ForwardIterator2,
135+
class _Pred,
136+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
137+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
138+
_LIBCPP_HIDE_FROM_ABI bool
139+
equal(_ExecutionPolicy&& __policy,
140+
_ForwardIterator1 __first1,
141+
_ForwardIterator1 __last1,
142+
_ForwardIterator2 __first2,
143+
_ForwardIterator2 __last2,
144+
_Pred __pred) {
145+
auto __res = std::__equal(
146+
__policy, std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
147+
if (!__res)
148+
std::__throw_bad_alloc();
149+
return *__res;
150+
}
151+
152+
template <class _ExecutionPolicy,
153+
class _ForwardIterator1,
154+
class _ForwardIterator2,
155+
enable_if_t<is_execution_policy_v<__remove_cvref_t<_ExecutionPolicy>>, int> = 0>
156+
_LIBCPP_HIDE_FROM_ABI bool
157+
equal(_ExecutionPolicy&& __policy,
158+
_ForwardIterator1 __first1,
159+
_ForwardIterator1 __last1,
160+
_ForwardIterator2 __first2,
161+
_ForwardIterator2 __last2) {
162+
return std::equal(
163+
__policy, std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::equal_to{});
164+
}
165+
166+
_LIBCPP_END_NAMESPACE_STD
167+
168+
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
169+
170+
#endif // _LIBCPP___ALGORITHM_PSTL_EQUAL_H

libcxx/include/algorithm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,6 +1826,7 @@ template <class BidirectionalIterator, class Compare>
18261826
#include <__algorithm/pstl_any_all_none_of.h>
18271827
#include <__algorithm/pstl_copy.h>
18281828
#include <__algorithm/pstl_count.h>
1829+
#include <__algorithm/pstl_equal.h>
18291830
#include <__algorithm/pstl_fill.h>
18301831
#include <__algorithm/pstl_find.h>
18311832
#include <__algorithm/pstl_for_each.h>

libcxx/include/module.modulemap.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ module std_private_algorithm_pstl_backends_cpu_backends_transform [system
748748
module std_private_algorithm_pstl_backends_cpu_backends_transform_reduce [system] { header "__algorithm/pstl_backends/cpu_backends/transform_reduce.h" }
749749
module std_private_algorithm_pstl_copy [system] { header "__algorithm/pstl_copy.h" }
750750
module std_private_algorithm_pstl_count [system] { header "__algorithm/pstl_count.h" }
751+
module std_private_algorithm_pstl_equal [system] { header "__algorithm/pstl_equal.h" }
751752
module std_private_algorithm_pstl_fill [system] { header "__algorithm/pstl_fill.h" }
752753
module std_private_algorithm_pstl_find [system] {
753754
header "__algorithm/pstl_find.h"
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14
10+
11+
// UNSUPPORTED: libcpp-has-no-incomplete-pstl
12+
13+
// <algorithm>
14+
15+
// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
16+
// bool equal(ExecutionPolicy&& exec,
17+
// ForwardIterator1 first1, ForwardIterator1 last1,
18+
// ForwardIterator2 first2);
19+
//
20+
// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
21+
// class BinaryPredicate>
22+
// bool equal(ExecutionPolicy&& exec,
23+
// ForwardIterator1 first1, ForwardIterator1 last1,
24+
// ForwardIterator2 first2, BinaryPredicate pred);
25+
//
26+
// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
27+
// bool equal(ExecutionPolicy&& exec,
28+
// ForwardIterator1 first1, ForwardIterator1 last1,
29+
// ForwardIterator2 first2, ForwardIterator2 last2);
30+
//
31+
// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
32+
// class BinaryPredicate>
33+
// bool equal(ExecutionPolicy&& exec,
34+
// ForwardIterator1 first1, ForwardIterator1 last1,
35+
// ForwardIterator2 first2, ForwardIterator2 last2,
36+
// BinaryPredicate pred);
37+
38+
#include <algorithm>
39+
#include <cassert>
40+
#include <iterator>
41+
42+
#include "test_execution_policies.h"
43+
#include "test_iterators.h"
44+
#include "test_macros.h"
45+
46+
template <class It1, class It2>
47+
struct Test {
48+
template <class Policy>
49+
void operator()(Policy&& policy) {
50+
{ // 3 iter overloads
51+
// check with equal ranges
52+
{
53+
int a[] = {1, 2, 3, 4};
54+
int b[] = {1, 2, 3, 4};
55+
assert(std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b))));
56+
}
57+
58+
// check with an empty range
59+
{
60+
int a[] = {999};
61+
int b[] = {1, 2, 3};
62+
assert(std::equal(policy, It1(std::begin(a)), It1(std::begin(a)), It2(std::begin(b))));
63+
}
64+
65+
// check with different ranges
66+
{
67+
int a[] = {1, 2, 3};
68+
int b[] = {3, 2, 1};
69+
assert(!std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b))));
70+
}
71+
72+
// check that the predicate is used
73+
{
74+
int a[] = {2, 4, 6, 8, 10};
75+
int b[] = {12, 14, 16, 18, 20};
76+
assert(std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b)), [](int lhs, int rhs) {
77+
return lhs % 2 == rhs % 2;
78+
}));
79+
}
80+
}
81+
82+
{ // 4 iter overloads
83+
// check with equal ranges of equal size
84+
{
85+
int a[] = {1, 2, 3, 4};
86+
int b[] = {1, 2, 3, 4};
87+
assert(std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b)), It2(std::end(b))));
88+
}
89+
90+
// check with unequal ranges of equal size
91+
{
92+
int a[] = {1, 2, 3, 4};
93+
int b[] = {4, 3, 2, 1};
94+
assert(!std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b)), It2(std::end(b))));
95+
}
96+
97+
// check with equal ranges of unequal size
98+
{
99+
{
100+
int a[] = {1, 2, 3, 4};
101+
int b[] = {1, 2, 3, 4, 5};
102+
assert(!std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b)), It2(std::end(b))));
103+
}
104+
{
105+
int a[] = {1, 2, 3, 4, 5};
106+
int b[] = {1, 2, 3, 4};
107+
assert(!std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b)), It2(std::end(b))));
108+
}
109+
}
110+
111+
// check empty ranges
112+
{
113+
// empty/empty
114+
{
115+
int a[] = {888};
116+
int b[] = {999};
117+
assert(std::equal(policy, It1(std::begin(a)), It1(std::begin(a)), It2(std::begin(b)), It2(std::begin(b))));
118+
}
119+
// empty/non-empty
120+
{
121+
int a[] = {999};
122+
int b[] = {999};
123+
assert(!std::equal(policy, It1(std::begin(a)), It1(std::begin(a)), It2(std::begin(b)), It2(std::end(b))));
124+
}
125+
// non-empty/empty
126+
{
127+
int a[] = {999};
128+
int b[] = {999};
129+
assert(!std::equal(policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b)), It2(std::begin(b))));
130+
}
131+
}
132+
133+
// check that the predicate is used
134+
{
135+
int a[] = {2, 4, 6, 8, 10};
136+
int b[] = {12, 14, 16, 18, 20};
137+
assert(std::equal(
138+
policy, It1(std::begin(a)), It1(std::end(a)), It2(std::begin(b)), It2(std::end(b)), [](int lhs, int rhs) {
139+
return lhs % 2 == rhs % 2;
140+
}));
141+
}
142+
}
143+
}
144+
};
145+
146+
int main(int, char**) {
147+
types::for_each(types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
148+
using It1 = typename decltype(v)::type;
149+
types::for_each(
150+
types::forward_iterator_list<int*>{},
151+
TestIteratorWithPolicies<types::partial_instantiation<Test, It1>::template apply>{});
152+
}});
153+
return 0;
154+
}

0 commit comments

Comments
 (0)