xref: /freebsd/contrib/llvm-project/libcxx/include/__algorithm/nth_element.h (revision b3edf4467982447620505a28fc82e38a414c07dc)
1fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric 
9fe6060f1SDimitry Andric #ifndef _LIBCPP___ALGORITHM_NTH_ELEMENT_H
10fe6060f1SDimitry Andric #define _LIBCPP___ALGORITHM_NTH_ELEMENT_H
11fe6060f1SDimitry Andric 
12fe6060f1SDimitry Andric #include <__algorithm/comp.h>
13fe6060f1SDimitry Andric #include <__algorithm/comp_ref_type.h>
14fcaf7f86SDimitry Andric #include <__algorithm/iterator_operations.h>
15fe6060f1SDimitry Andric #include <__algorithm/sort.h>
165f757f3fSDimitry Andric #include <__assert>
1704eeddc0SDimitry Andric #include <__config>
18753f127fSDimitry Andric #include <__debug_utils/randomize_range.h>
19fe6060f1SDimitry Andric #include <__iterator/iterator_traits.h>
20753f127fSDimitry Andric #include <__utility/move.h>
21fe6060f1SDimitry Andric 
22fe6060f1SDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
23fe6060f1SDimitry Andric #  pragma GCC system_header
24fe6060f1SDimitry Andric #endif
25fe6060f1SDimitry Andric 
26*b3edf446SDimitry Andric _LIBCPP_PUSH_MACROS
27*b3edf446SDimitry Andric #include <__undef_macros>
28*b3edf446SDimitry Andric 
29fe6060f1SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
30fe6060f1SDimitry Andric 
31fe6060f1SDimitry Andric template <class _Compare, class _RandomAccessIterator>
32cb14a3feSDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __nth_element_find_guard(
33cb14a3feSDimitry Andric     _RandomAccessIterator& __i, _RandomAccessIterator& __j, _RandomAccessIterator __m, _Compare __comp) {
34fe6060f1SDimitry Andric   // manually guard downward moving __j against __i
35fe6060f1SDimitry Andric   while (true) {
36fe6060f1SDimitry Andric     if (__i == --__j) {
37fe6060f1SDimitry Andric       return false;
38fe6060f1SDimitry Andric     }
39fe6060f1SDimitry Andric     if (__comp(*__j, *__m)) {
40fe6060f1SDimitry Andric       return true; // found guard for downward moving __j, now use unguarded partition
41fe6060f1SDimitry Andric     }
42fe6060f1SDimitry Andric   }
43fe6060f1SDimitry Andric }
44fe6060f1SDimitry Andric 
45fcaf7f86SDimitry Andric template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
46bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
475f757f3fSDimitry Andric // NOLINTNEXTLINE(readability-function-cognitive-complexity)
48cb14a3feSDimitry Andric __nth_element(
49cb14a3feSDimitry Andric     _RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp) {
50fcaf7f86SDimitry Andric   using _Ops = _IterOps<_AlgPolicy>;
51fcaf7f86SDimitry Andric 
52fe6060f1SDimitry Andric   // _Compare is known to be a reference type
53fe6060f1SDimitry Andric   typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
54fe6060f1SDimitry Andric   const difference_type __limit = 7;
55cb14a3feSDimitry Andric   while (true) {
56fe6060f1SDimitry Andric     if (__nth == __last)
57fe6060f1SDimitry Andric       return;
58fe6060f1SDimitry Andric     difference_type __len = __last - __first;
59cb14a3feSDimitry Andric     switch (__len) {
60fe6060f1SDimitry Andric     case 0:
61fe6060f1SDimitry Andric     case 1:
62fe6060f1SDimitry Andric       return;
63fe6060f1SDimitry Andric     case 2:
64fe6060f1SDimitry Andric       if (__comp(*--__last, *__first))
65fcaf7f86SDimitry Andric         _Ops::iter_swap(__first, __last);
66fe6060f1SDimitry Andric       return;
67cb14a3feSDimitry Andric     case 3: {
68fe6060f1SDimitry Andric       _RandomAccessIterator __m = __first;
69fcaf7f86SDimitry Andric       std::__sort3<_AlgPolicy, _Compare>(__first, ++__m, --__last, __comp);
70fe6060f1SDimitry Andric       return;
71fe6060f1SDimitry Andric     }
72fe6060f1SDimitry Andric     }
73cb14a3feSDimitry Andric     if (__len <= __limit) {
74fcaf7f86SDimitry Andric       std::__selection_sort<_AlgPolicy, _Compare>(__first, __last, __comp);
75fe6060f1SDimitry Andric       return;
76fe6060f1SDimitry Andric     }
77fe6060f1SDimitry Andric     // __len > __limit >= 3
78fe6060f1SDimitry Andric     _RandomAccessIterator __m   = __first + __len / 2;
79fe6060f1SDimitry Andric     _RandomAccessIterator __lm1 = __last;
80fcaf7f86SDimitry Andric     unsigned __n_swaps          = std::__sort3<_AlgPolicy, _Compare>(__first, __m, --__lm1, __comp);
81fe6060f1SDimitry Andric     // *__m is median
82fe6060f1SDimitry Andric     // partition [__first, __m) < *__m and *__m <= [__m, __last)
83fe6060f1SDimitry Andric     // (this inhibits tossing elements equivalent to __m around unnecessarily)
84fe6060f1SDimitry Andric     _RandomAccessIterator __i = __first;
85fe6060f1SDimitry Andric     _RandomAccessIterator __j = __lm1;
86fe6060f1SDimitry Andric     // j points beyond range to be tested, *__lm1 is known to be <= *__m
87fe6060f1SDimitry Andric     // The search going up is known to be guarded but the search coming down isn't.
88fe6060f1SDimitry Andric     // Prime the downward search with a guard.
89fe6060f1SDimitry Andric     if (!__comp(*__i, *__m)) // if *__first == *__m
90fe6060f1SDimitry Andric     {
91fe6060f1SDimitry Andric       // *__first == *__m, *__first doesn't go in first part
925f757f3fSDimitry Andric       if (std::__nth_element_find_guard<_Compare>(__i, __j, __m, __comp)) {
93fcaf7f86SDimitry Andric         _Ops::iter_swap(__i, __j);
94fe6060f1SDimitry Andric         ++__n_swaps;
95fe6060f1SDimitry Andric       } else {
96fe6060f1SDimitry Andric         // *__first == *__m, *__m <= all other elements
97fe6060f1SDimitry Andric         // Partition instead into [__first, __i) == *__first and *__first < [__i, __last)
98fe6060f1SDimitry Andric         ++__i; // __first + 1
99fe6060f1SDimitry Andric         __j = __last;
100fe6060f1SDimitry Andric         if (!__comp(*__first, *--__j)) { // we need a guard if *__first == *(__last-1)
101fe6060f1SDimitry Andric           while (true) {
102fe6060f1SDimitry Andric             if (__i == __j) {
103fe6060f1SDimitry Andric               return; // [__first, __last) all equivalent elements
104fe6060f1SDimitry Andric             } else if (__comp(*__first, *__i)) {
105fcaf7f86SDimitry Andric               _Ops::iter_swap(__i, __j);
106fe6060f1SDimitry Andric               ++__n_swaps;
107fe6060f1SDimitry Andric               ++__i;
108fe6060f1SDimitry Andric               break;
109fe6060f1SDimitry Andric             }
110fe6060f1SDimitry Andric             ++__i;
111fe6060f1SDimitry Andric           }
112fe6060f1SDimitry Andric         }
113fe6060f1SDimitry Andric         // [__first, __i) == *__first and *__first < [__j, __last) and __j == __last - 1
114fe6060f1SDimitry Andric         if (__i == __j) {
115fe6060f1SDimitry Andric           return;
116fe6060f1SDimitry Andric         }
117fe6060f1SDimitry Andric         while (true) {
1185f757f3fSDimitry Andric           while (!__comp(*__first, *__i)) {
119fe6060f1SDimitry Andric             ++__i;
1207a6dacacSDimitry Andric             _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1215f757f3fSDimitry Andric                 __i != __last,
1225f757f3fSDimitry Andric                 "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
1235f757f3fSDimitry Andric           }
1245f757f3fSDimitry Andric           do {
1257a6dacacSDimitry Andric             _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1265f757f3fSDimitry Andric                 __j != __first,
1275f757f3fSDimitry Andric                 "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
1285f757f3fSDimitry Andric             --__j;
1295f757f3fSDimitry Andric           } while (__comp(*__first, *__j));
130fe6060f1SDimitry Andric           if (__i >= __j)
131fe6060f1SDimitry Andric             break;
132fcaf7f86SDimitry Andric           _Ops::iter_swap(__i, __j);
133fe6060f1SDimitry Andric           ++__n_swaps;
134fe6060f1SDimitry Andric           ++__i;
135fe6060f1SDimitry Andric         }
136fe6060f1SDimitry Andric         // [__first, __i) == *__first and *__first < [__i, __last)
137fe6060f1SDimitry Andric         // The first part is sorted,
138fe6060f1SDimitry Andric         if (__nth < __i) {
139fe6060f1SDimitry Andric           return;
140fe6060f1SDimitry Andric         }
141fe6060f1SDimitry Andric         // __nth_element the second part
1425f757f3fSDimitry Andric         // std::__nth_element<_Compare>(__i, __nth, __last, __comp);
143fe6060f1SDimitry Andric         __first = __i;
144fe6060f1SDimitry Andric         continue;
145fe6060f1SDimitry Andric       }
146fe6060f1SDimitry Andric     }
147fe6060f1SDimitry Andric     ++__i;
148fe6060f1SDimitry Andric     // j points beyond range to be tested, *__lm1 is known to be <= *__m
149fe6060f1SDimitry Andric     // if not yet partitioned...
150cb14a3feSDimitry Andric     if (__i < __j) {
151fe6060f1SDimitry Andric       // known that *(__i - 1) < *__m
152cb14a3feSDimitry Andric       while (true) {
153fe6060f1SDimitry Andric         // __m still guards upward moving __i
1545f757f3fSDimitry Andric         while (__comp(*__i, *__m)) {
155fe6060f1SDimitry Andric           ++__i;
1567a6dacacSDimitry Andric           _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1575f757f3fSDimitry Andric               __i != __last,
1585f757f3fSDimitry Andric               "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
1595f757f3fSDimitry Andric         }
160fe6060f1SDimitry Andric         // It is now known that a guard exists for downward moving __j
1615f757f3fSDimitry Andric         do {
1627a6dacacSDimitry Andric           _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1635f757f3fSDimitry Andric               __j != __first,
1645f757f3fSDimitry Andric               "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
1655f757f3fSDimitry Andric           --__j;
1665f757f3fSDimitry Andric         } while (!__comp(*__j, *__m));
167fe6060f1SDimitry Andric         if (__i >= __j)
168fe6060f1SDimitry Andric           break;
169fcaf7f86SDimitry Andric         _Ops::iter_swap(__i, __j);
170fe6060f1SDimitry Andric         ++__n_swaps;
171fe6060f1SDimitry Andric         // It is known that __m != __j
172fe6060f1SDimitry Andric         // If __m just moved, follow it
173fe6060f1SDimitry Andric         if (__m == __i)
174fe6060f1SDimitry Andric           __m = __j;
175fe6060f1SDimitry Andric         ++__i;
176fe6060f1SDimitry Andric       }
177fe6060f1SDimitry Andric     }
178fe6060f1SDimitry Andric     // [__first, __i) < *__m and *__m <= [__i, __last)
179cb14a3feSDimitry Andric     if (__i != __m && __comp(*__m, *__i)) {
180fcaf7f86SDimitry Andric       _Ops::iter_swap(__i, __m);
181fe6060f1SDimitry Andric       ++__n_swaps;
182fe6060f1SDimitry Andric     }
183fe6060f1SDimitry Andric     // [__first, __i) < *__i and *__i <= [__i+1, __last)
184fe6060f1SDimitry Andric     if (__nth == __i)
185fe6060f1SDimitry Andric       return;
186cb14a3feSDimitry Andric     if (__n_swaps == 0) {
187fe6060f1SDimitry Andric       // We were given a perfectly partitioned sequence.  Coincidence?
188cb14a3feSDimitry Andric       if (__nth < __i) {
189fe6060f1SDimitry Andric         // Check for [__first, __i) already sorted
190fe6060f1SDimitry Andric         __j = __m = __first;
191fe6060f1SDimitry Andric         while (true) {
192fe6060f1SDimitry Andric           if (++__j == __i) {
193fe6060f1SDimitry Andric             // [__first, __i) sorted
194fe6060f1SDimitry Andric             return;
195fe6060f1SDimitry Andric           }
196fe6060f1SDimitry Andric           if (__comp(*__j, *__m)) {
197fe6060f1SDimitry Andric             // not yet sorted, so sort
198fe6060f1SDimitry Andric             break;
199fe6060f1SDimitry Andric           }
200fe6060f1SDimitry Andric           __m = __j;
201fe6060f1SDimitry Andric         }
202cb14a3feSDimitry Andric       } else {
203fe6060f1SDimitry Andric         // Check for [__i, __last) already sorted
204fe6060f1SDimitry Andric         __j = __m = __i;
205fe6060f1SDimitry Andric         while (true) {
206fe6060f1SDimitry Andric           if (++__j == __last) {
207fe6060f1SDimitry Andric             // [__i, __last) sorted
208fe6060f1SDimitry Andric             return;
209fe6060f1SDimitry Andric           }
210fe6060f1SDimitry Andric           if (__comp(*__j, *__m)) {
211fe6060f1SDimitry Andric             // not yet sorted, so sort
212fe6060f1SDimitry Andric             break;
213fe6060f1SDimitry Andric           }
214fe6060f1SDimitry Andric           __m = __j;
215fe6060f1SDimitry Andric         }
216fe6060f1SDimitry Andric       }
217fe6060f1SDimitry Andric     }
218fe6060f1SDimitry Andric     // __nth_element on range containing __nth
219cb14a3feSDimitry Andric     if (__nth < __i) {
2205f757f3fSDimitry Andric       // std::__nth_element<_Compare>(__first, __nth, __i, __comp);
221fe6060f1SDimitry Andric       __last = __i;
222cb14a3feSDimitry Andric     } else {
2235f757f3fSDimitry Andric       // std::__nth_element<_Compare>(__i+1, __nth, __last, __comp);
224fe6060f1SDimitry Andric       __first = ++__i;
225fe6060f1SDimitry Andric     }
226fe6060f1SDimitry Andric   }
227fe6060f1SDimitry Andric }
228fe6060f1SDimitry Andric 
229fcaf7f86SDimitry Andric template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
230cb14a3feSDimitry Andric inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __nth_element_impl(
231cb14a3feSDimitry Andric     _RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare& __comp) {
232753f127fSDimitry Andric   if (__nth == __last)
233753f127fSDimitry Andric     return;
234753f127fSDimitry Andric 
235fcaf7f86SDimitry Andric   std::__debug_randomize_range<_AlgPolicy>(__first, __last);
236753f127fSDimitry Andric 
237bdd1243dSDimitry Andric   std::__nth_element<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
238753f127fSDimitry Andric 
239fcaf7f86SDimitry Andric   std::__debug_randomize_range<_AlgPolicy>(__first, __nth);
240349cc55cSDimitry Andric   if (__nth != __last) {
241fcaf7f86SDimitry Andric     std::__debug_randomize_range<_AlgPolicy>(++__nth, __last);
242349cc55cSDimitry Andric   }
243fe6060f1SDimitry Andric }
244fe6060f1SDimitry Andric 
245753f127fSDimitry Andric template <class _RandomAccessIterator, class _Compare>
246cb14a3feSDimitry Andric inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
247cb14a3feSDimitry Andric nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp) {
248fcaf7f86SDimitry Andric   std::__nth_element_impl<_ClassicAlgPolicy>(std::move(__first), std::move(__nth), std::move(__last), __comp);
249753f127fSDimitry Andric }
250753f127fSDimitry Andric 
251fe6060f1SDimitry Andric template <class _RandomAccessIterator>
252cb14a3feSDimitry Andric inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
253cb14a3feSDimitry Andric nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last) {
25406c3fb27SDimitry Andric   std::nth_element(std::move(__first), std::move(__nth), std::move(__last), __less<>());
255fe6060f1SDimitry Andric }
256fe6060f1SDimitry Andric 
257fe6060f1SDimitry Andric _LIBCPP_END_NAMESPACE_STD
258fe6060f1SDimitry Andric 
259*b3edf446SDimitry Andric _LIBCPP_POP_MACROS
260*b3edf446SDimitry Andric 
261fe6060f1SDimitry Andric #endif // _LIBCPP___ALGORITHM_NTH_ELEMENT_H
262