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_NTH_ELEMENT_H |
10 | #define _LIBCPP___ALGORITHM_NTH_ELEMENT_H |
11 | |
12 | #include <__algorithm/comp.h> |
13 | #include <__algorithm/comp_ref_type.h> |
14 | #include <__algorithm/iterator_operations.h> |
15 | #include <__algorithm/sort.h> |
16 | #include <__assert> |
17 | #include <__config> |
18 | #include <__debug_utils/randomize_range.h> |
19 | #include <__iterator/iterator_traits.h> |
20 | #include <__utility/move.h> |
21 | |
22 | #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
23 | # pragma GCC system_header |
24 | #endif |
25 | |
26 | _LIBCPP_PUSH_MACROS |
27 | #include <__undef_macros> |
28 | |
29 | _LIBCPP_BEGIN_NAMESPACE_STD |
30 | |
31 | template <class _Compare, class _RandomAccessIterator> |
32 | _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __nth_element_find_guard( |
33 | _RandomAccessIterator& __i, _RandomAccessIterator& __j, _RandomAccessIterator __m, _Compare __comp) { |
34 | // manually guard downward moving __j against __i |
35 | while (true) { |
36 | if (__i == --__j) { |
37 | return false; |
38 | } |
39 | if (__comp(*__j, *__m)) { |
40 | return true; // found guard for downward moving __j, now use unguarded partition |
41 | } |
42 | } |
43 | } |
44 | |
45 | template <class _AlgPolicy, class _Compare, class _RandomAccessIterator> |
46 | _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void |
47 | // NOLINTNEXTLINE(readability-function-cognitive-complexity) |
48 | __nth_element( |
49 | _RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp) { |
50 | using _Ops = _IterOps<_AlgPolicy>; |
51 | |
52 | // _Compare is known to be a reference type |
53 | typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type; |
54 | const difference_type __limit = 7; |
55 | while (true) { |
56 | if (__nth == __last) |
57 | return; |
58 | difference_type __len = __last - __first; |
59 | switch (__len) { |
60 | case 0: |
61 | case 1: |
62 | return; |
63 | case 2: |
64 | if (__comp(*--__last, *__first)) |
65 | _Ops::iter_swap(__first, __last); |
66 | return; |
67 | case 3: { |
68 | _RandomAccessIterator __m = __first; |
69 | std::__sort3<_AlgPolicy, _Compare>(__first, ++__m, --__last, __comp); |
70 | return; |
71 | } |
72 | } |
73 | if (__len <= __limit) { |
74 | std::__selection_sort<_AlgPolicy, _Compare>(__first, __last, __comp); |
75 | return; |
76 | } |
77 | // __len > __limit >= 3 |
78 | _RandomAccessIterator __m = __first + __len / 2; |
79 | _RandomAccessIterator __lm1 = __last; |
80 | unsigned __n_swaps = std::__sort3<_AlgPolicy, _Compare>(__first, __m, --__lm1, __comp); |
81 | // *__m is median |
82 | // partition [__first, __m) < *__m and *__m <= [__m, __last) |
83 | // (this inhibits tossing elements equivalent to __m around unnecessarily) |
84 | _RandomAccessIterator __i = __first; |
85 | _RandomAccessIterator __j = __lm1; |
86 | // j points beyond range to be tested, *__lm1 is known to be <= *__m |
87 | // The search going up is known to be guarded but the search coming down isn't. |
88 | // Prime the downward search with a guard. |
89 | if (!__comp(*__i, *__m)) // if *__first == *__m |
90 | { |
91 | // *__first == *__m, *__first doesn't go in first part |
92 | if (std::__nth_element_find_guard<_Compare>(__i, __j, __m, __comp)) { |
93 | _Ops::iter_swap(__i, __j); |
94 | ++__n_swaps; |
95 | } else { |
96 | // *__first == *__m, *__m <= all other elements |
97 | // Partition instead into [__first, __i) == *__first and *__first < [__i, __last) |
98 | ++__i; // __first + 1 |
99 | __j = __last; |
100 | if (!__comp(*__first, *--__j)) { // we need a guard if *__first == *(__last-1) |
101 | while (true) { |
102 | if (__i == __j) { |
103 | return; // [__first, __last) all equivalent elements |
104 | } else if (__comp(*__first, *__i)) { |
105 | _Ops::iter_swap(__i, __j); |
106 | ++__n_swaps; |
107 | ++__i; |
108 | break; |
109 | } |
110 | ++__i; |
111 | } |
112 | } |
113 | // [__first, __i) == *__first and *__first < [__j, __last) and __j == __last - 1 |
114 | if (__i == __j) { |
115 | return; |
116 | } |
117 | while (true) { |
118 | while (!__comp(*__first, *__i)) { |
119 | ++__i; |
120 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
121 | __i != __last, |
122 | "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?" ); |
123 | } |
124 | do { |
125 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
126 | __j != __first, |
127 | "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?" ); |
128 | --__j; |
129 | } while (__comp(*__first, *__j)); |
130 | if (__i >= __j) |
131 | break; |
132 | _Ops::iter_swap(__i, __j); |
133 | ++__n_swaps; |
134 | ++__i; |
135 | } |
136 | // [__first, __i) == *__first and *__first < [__i, __last) |
137 | // The first part is sorted, |
138 | if (__nth < __i) { |
139 | return; |
140 | } |
141 | // __nth_element the second part |
142 | // std::__nth_element<_Compare>(__i, __nth, __last, __comp); |
143 | __first = __i; |
144 | continue; |
145 | } |
146 | } |
147 | ++__i; |
148 | // j points beyond range to be tested, *__lm1 is known to be <= *__m |
149 | // if not yet partitioned... |
150 | if (__i < __j) { |
151 | // known that *(__i - 1) < *__m |
152 | while (true) { |
153 | // __m still guards upward moving __i |
154 | while (__comp(*__i, *__m)) { |
155 | ++__i; |
156 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
157 | __i != __last, |
158 | "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?" ); |
159 | } |
160 | // It is now known that a guard exists for downward moving __j |
161 | do { |
162 | _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
163 | __j != __first, |
164 | "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?" ); |
165 | --__j; |
166 | } while (!__comp(*__j, *__m)); |
167 | if (__i >= __j) |
168 | break; |
169 | _Ops::iter_swap(__i, __j); |
170 | ++__n_swaps; |
171 | // It is known that __m != __j |
172 | // If __m just moved, follow it |
173 | if (__m == __i) |
174 | __m = __j; |
175 | ++__i; |
176 | } |
177 | } |
178 | // [__first, __i) < *__m and *__m <= [__i, __last) |
179 | if (__i != __m && __comp(*__m, *__i)) { |
180 | _Ops::iter_swap(__i, __m); |
181 | ++__n_swaps; |
182 | } |
183 | // [__first, __i) < *__i and *__i <= [__i+1, __last) |
184 | if (__nth == __i) |
185 | return; |
186 | if (__n_swaps == 0) { |
187 | // We were given a perfectly partitioned sequence. Coincidence? |
188 | if (__nth < __i) { |
189 | // Check for [__first, __i) already sorted |
190 | __j = __m = __first; |
191 | while (true) { |
192 | if (++__j == __i) { |
193 | // [__first, __i) sorted |
194 | return; |
195 | } |
196 | if (__comp(*__j, *__m)) { |
197 | // not yet sorted, so sort |
198 | break; |
199 | } |
200 | __m = __j; |
201 | } |
202 | } else { |
203 | // Check for [__i, __last) already sorted |
204 | __j = __m = __i; |
205 | while (true) { |
206 | if (++__j == __last) { |
207 | // [__i, __last) sorted |
208 | return; |
209 | } |
210 | if (__comp(*__j, *__m)) { |
211 | // not yet sorted, so sort |
212 | break; |
213 | } |
214 | __m = __j; |
215 | } |
216 | } |
217 | } |
218 | // __nth_element on range containing __nth |
219 | if (__nth < __i) { |
220 | // std::__nth_element<_Compare>(__first, __nth, __i, __comp); |
221 | __last = __i; |
222 | } else { |
223 | // std::__nth_element<_Compare>(__i+1, __nth, __last, __comp); |
224 | __first = ++__i; |
225 | } |
226 | } |
227 | } |
228 | |
229 | template <class _AlgPolicy, class _RandomAccessIterator, class _Compare> |
230 | inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __nth_element_impl( |
231 | _RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare& __comp) { |
232 | if (__nth == __last) |
233 | return; |
234 | |
235 | std::__debug_randomize_range<_AlgPolicy>(__first, __last); |
236 | |
237 | std::__nth_element<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp); |
238 | |
239 | std::__debug_randomize_range<_AlgPolicy>(__first, __nth); |
240 | if (__nth != __last) { |
241 | std::__debug_randomize_range<_AlgPolicy>(++__nth, __last); |
242 | } |
243 | } |
244 | |
245 | template <class _RandomAccessIterator, class _Compare> |
246 | inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void |
247 | nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp) { |
248 | std::__nth_element_impl<_ClassicAlgPolicy>(std::move(__first), std::move(__nth), std::move(__last), __comp); |
249 | } |
250 | |
251 | template <class _RandomAccessIterator> |
252 | inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void |
253 | nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last) { |
254 | std::nth_element(std::move(__first), std::move(__nth), std::move(__last), __less<>()); |
255 | } |
256 | |
257 | _LIBCPP_END_NAMESPACE_STD |
258 | |
259 | _LIBCPP_POP_MACROS |
260 | |
261 | #endif // _LIBCPP___ALGORITHM_NTH_ELEMENT_H |
262 | |