xref: /llvm-project/libcxx/include/__algorithm/nth_element.h (revision 9783f28cbb155e4a8d49c12e1c60ce14dcfaf0c7)
1134723edSLouis Dionne //===----------------------------------------------------------------------===//
2134723edSLouis Dionne //
3134723edSLouis Dionne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4134723edSLouis Dionne // See https://llvm.org/LICENSE.txt for license information.
5134723edSLouis Dionne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6134723edSLouis Dionne //
7134723edSLouis Dionne //===----------------------------------------------------------------------===//
8134723edSLouis Dionne 
9134723edSLouis Dionne #ifndef _LIBCPP___ALGORITHM_NTH_ELEMENT_H
10134723edSLouis Dionne #define _LIBCPP___ALGORITHM_NTH_ELEMENT_H
11134723edSLouis Dionne 
12134723edSLouis Dionne #include <__algorithm/comp.h>
13134723edSLouis Dionne #include <__algorithm/comp_ref_type.h>
14a7c3379cSKonstantin Varlamov #include <__algorithm/iterator_operations.h>
15134723edSLouis Dionne #include <__algorithm/sort.h>
16ea9af5e7SDaniel Kutenin #include <__assert>
174d81a46fSArthur O'Dwyer #include <__config>
182aea8af2SNikolas Klauser #include <__debug_utils/randomize_range.h>
19134723edSLouis Dionne #include <__iterator/iterator_traits.h>
2023c7328bSKonstantin Varlamov #include <__utility/move.h>
21134723edSLouis Dionne 
22134723edSLouis Dionne #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
23134723edSLouis Dionne #  pragma GCC system_header
24134723edSLouis Dionne #endif
25134723edSLouis Dionne 
26134723edSLouis Dionne _LIBCPP_BEGIN_NAMESPACE_STD
27134723edSLouis Dionne 
28134723edSLouis Dionne template <class _Compare, class _RandomAccessIterator>
29*9783f28cSLouis Dionne _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __nth_element_find_guard(
30*9783f28cSLouis Dionne     _RandomAccessIterator& __i, _RandomAccessIterator& __j, _RandomAccessIterator __m, _Compare __comp) {
31134723edSLouis Dionne   // manually guard downward moving __j against __i
32134723edSLouis Dionne   while (true) {
33134723edSLouis Dionne     if (__i == --__j) {
34134723edSLouis Dionne       return false;
35134723edSLouis Dionne     }
36134723edSLouis Dionne     if (__comp(*__j, *__m)) {
37134723edSLouis Dionne       return true; // found guard for downward moving __j, now use unguarded partition
38134723edSLouis Dionne     }
39134723edSLouis Dionne   }
40134723edSLouis Dionne }
41134723edSLouis Dionne 
42a7c3379cSKonstantin Varlamov template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
435146b57bSNikolas Klauser _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
44ea9af5e7SDaniel Kutenin // NOLINTNEXTLINE(readability-function-cognitive-complexity)
45*9783f28cSLouis Dionne __nth_element(
46*9783f28cSLouis Dionne     _RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp) {
47a7c3379cSKonstantin Varlamov   using _Ops = _IterOps<_AlgPolicy>;
48a7c3379cSKonstantin Varlamov 
49134723edSLouis Dionne   // _Compare is known to be a reference type
50134723edSLouis Dionne   typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
51134723edSLouis Dionne   const difference_type __limit = 7;
52*9783f28cSLouis Dionne   while (true) {
53134723edSLouis Dionne     if (__nth == __last)
54134723edSLouis Dionne       return;
55134723edSLouis Dionne     difference_type __len = __last - __first;
56*9783f28cSLouis Dionne     switch (__len) {
57134723edSLouis Dionne     case 0:
58134723edSLouis Dionne     case 1:
59134723edSLouis Dionne       return;
60134723edSLouis Dionne     case 2:
61134723edSLouis Dionne       if (__comp(*--__last, *__first))
62a7c3379cSKonstantin Varlamov         _Ops::iter_swap(__first, __last);
63134723edSLouis Dionne       return;
64*9783f28cSLouis Dionne     case 3: {
65134723edSLouis Dionne       _RandomAccessIterator __m = __first;
66a7c3379cSKonstantin Varlamov       std::__sort3<_AlgPolicy, _Compare>(__first, ++__m, --__last, __comp);
67134723edSLouis Dionne       return;
68134723edSLouis Dionne     }
69134723edSLouis Dionne     }
70*9783f28cSLouis Dionne     if (__len <= __limit) {
71a7c3379cSKonstantin Varlamov       std::__selection_sort<_AlgPolicy, _Compare>(__first, __last, __comp);
72134723edSLouis Dionne       return;
73134723edSLouis Dionne     }
74134723edSLouis Dionne     // __len > __limit >= 3
75134723edSLouis Dionne     _RandomAccessIterator __m   = __first + __len / 2;
76134723edSLouis Dionne     _RandomAccessIterator __lm1 = __last;
77a7c3379cSKonstantin Varlamov     unsigned __n_swaps          = std::__sort3<_AlgPolicy, _Compare>(__first, __m, --__lm1, __comp);
78134723edSLouis Dionne     // *__m is median
79134723edSLouis Dionne     // partition [__first, __m) < *__m and *__m <= [__m, __last)
80134723edSLouis Dionne     // (this inhibits tossing elements equivalent to __m around unnecessarily)
81134723edSLouis Dionne     _RandomAccessIterator __i = __first;
82134723edSLouis Dionne     _RandomAccessIterator __j = __lm1;
83134723edSLouis Dionne     // j points beyond range to be tested, *__lm1 is known to be <= *__m
84134723edSLouis Dionne     // The search going up is known to be guarded but the search coming down isn't.
85134723edSLouis Dionne     // Prime the downward search with a guard.
86134723edSLouis Dionne     if (!__comp(*__i, *__m)) // if *__first == *__m
87134723edSLouis Dionne     {
88134723edSLouis Dionne       // *__first == *__m, *__first doesn't go in first part
8977a00c0dSLouis Dionne       if (std::__nth_element_find_guard<_Compare>(__i, __j, __m, __comp)) {
90a7c3379cSKonstantin Varlamov         _Ops::iter_swap(__i, __j);
91134723edSLouis Dionne         ++__n_swaps;
92134723edSLouis Dionne       } else {
93134723edSLouis Dionne         // *__first == *__m, *__m <= all other elements
94134723edSLouis Dionne         // Partition instead into [__first, __i) == *__first and *__first < [__i, __last)
95134723edSLouis Dionne         ++__i; // __first + 1
96134723edSLouis Dionne         __j = __last;
97134723edSLouis Dionne         if (!__comp(*__first, *--__j)) { // we need a guard if *__first == *(__last-1)
98134723edSLouis Dionne           while (true) {
99134723edSLouis Dionne             if (__i == __j) {
100134723edSLouis Dionne               return; // [__first, __last) all equivalent elements
101134723edSLouis Dionne             } else if (__comp(*__first, *__i)) {
102a7c3379cSKonstantin Varlamov               _Ops::iter_swap(__i, __j);
103134723edSLouis Dionne               ++__n_swaps;
104134723edSLouis Dionne               ++__i;
105134723edSLouis Dionne               break;
106134723edSLouis Dionne             }
107134723edSLouis Dionne             ++__i;
108134723edSLouis Dionne           }
109134723edSLouis Dionne         }
110134723edSLouis Dionne         // [__first, __i) == *__first and *__first < [__j, __last) and __j == __last - 1
111134723edSLouis Dionne         if (__i == __j) {
112134723edSLouis Dionne           return;
113134723edSLouis Dionne         }
114134723edSLouis Dionne         while (true) {
115ea9af5e7SDaniel Kutenin           while (!__comp(*__first, *__i)) {
116134723edSLouis Dionne             ++__i;
117ea9af5e7SDaniel Kutenin             _LIBCPP_ASSERT_UNCATEGORIZED(
118ea9af5e7SDaniel Kutenin                 __i != __last,
119ea9af5e7SDaniel Kutenin                 "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
120ea9af5e7SDaniel Kutenin           }
121ea9af5e7SDaniel Kutenin           do {
122ea9af5e7SDaniel Kutenin             _LIBCPP_ASSERT_UNCATEGORIZED(
123ea9af5e7SDaniel Kutenin                 __j != __first,
124ea9af5e7SDaniel Kutenin                 "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
125ea9af5e7SDaniel Kutenin             --__j;
126ea9af5e7SDaniel Kutenin           } while (__comp(*__first, *__j));
127134723edSLouis Dionne           if (__i >= __j)
128134723edSLouis Dionne             break;
129a7c3379cSKonstantin Varlamov           _Ops::iter_swap(__i, __j);
130134723edSLouis Dionne           ++__n_swaps;
131134723edSLouis Dionne           ++__i;
132134723edSLouis Dionne         }
133134723edSLouis Dionne         // [__first, __i) == *__first and *__first < [__i, __last)
134134723edSLouis Dionne         // The first part is sorted,
135134723edSLouis Dionne         if (__nth < __i) {
136134723edSLouis Dionne           return;
137134723edSLouis Dionne         }
138134723edSLouis Dionne         // __nth_element the second part
13977a00c0dSLouis Dionne         // std::__nth_element<_Compare>(__i, __nth, __last, __comp);
140134723edSLouis Dionne         __first = __i;
141134723edSLouis Dionne         continue;
142134723edSLouis Dionne       }
143134723edSLouis Dionne     }
144134723edSLouis Dionne     ++__i;
145134723edSLouis Dionne     // j points beyond range to be tested, *__lm1 is known to be <= *__m
146134723edSLouis Dionne     // if not yet partitioned...
147*9783f28cSLouis Dionne     if (__i < __j) {
148134723edSLouis Dionne       // known that *(__i - 1) < *__m
149*9783f28cSLouis Dionne       while (true) {
150134723edSLouis Dionne         // __m still guards upward moving __i
151ea9af5e7SDaniel Kutenin         while (__comp(*__i, *__m)) {
152134723edSLouis Dionne           ++__i;
153ea9af5e7SDaniel Kutenin           _LIBCPP_ASSERT_UNCATEGORIZED(
154ea9af5e7SDaniel Kutenin               __i != __last,
155ea9af5e7SDaniel Kutenin               "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
156ea9af5e7SDaniel Kutenin         }
157134723edSLouis Dionne         // It is now known that a guard exists for downward moving __j
158ea9af5e7SDaniel Kutenin         do {
159ea9af5e7SDaniel Kutenin           _LIBCPP_ASSERT_UNCATEGORIZED(
160ea9af5e7SDaniel Kutenin               __j != __first,
161ea9af5e7SDaniel Kutenin               "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
162ea9af5e7SDaniel Kutenin           --__j;
163ea9af5e7SDaniel Kutenin         } while (!__comp(*__j, *__m));
164134723edSLouis Dionne         if (__i >= __j)
165134723edSLouis Dionne           break;
166a7c3379cSKonstantin Varlamov         _Ops::iter_swap(__i, __j);
167134723edSLouis Dionne         ++__n_swaps;
168134723edSLouis Dionne         // It is known that __m != __j
169134723edSLouis Dionne         // If __m just moved, follow it
170134723edSLouis Dionne         if (__m == __i)
171134723edSLouis Dionne           __m = __j;
172134723edSLouis Dionne         ++__i;
173134723edSLouis Dionne       }
174134723edSLouis Dionne     }
175134723edSLouis Dionne     // [__first, __i) < *__m and *__m <= [__i, __last)
176*9783f28cSLouis Dionne     if (__i != __m && __comp(*__m, *__i)) {
177a7c3379cSKonstantin Varlamov       _Ops::iter_swap(__i, __m);
178134723edSLouis Dionne       ++__n_swaps;
179134723edSLouis Dionne     }
180134723edSLouis Dionne     // [__first, __i) < *__i and *__i <= [__i+1, __last)
181134723edSLouis Dionne     if (__nth == __i)
182134723edSLouis Dionne       return;
183*9783f28cSLouis Dionne     if (__n_swaps == 0) {
184134723edSLouis Dionne       // We were given a perfectly partitioned sequence.  Coincidence?
185*9783f28cSLouis Dionne       if (__nth < __i) {
186134723edSLouis Dionne         // Check for [__first, __i) already sorted
187134723edSLouis Dionne         __j = __m = __first;
188134723edSLouis Dionne         while (true) {
189134723edSLouis Dionne           if (++__j == __i) {
190134723edSLouis Dionne             // [__first, __i) sorted
191134723edSLouis Dionne             return;
192134723edSLouis Dionne           }
193134723edSLouis Dionne           if (__comp(*__j, *__m)) {
194134723edSLouis Dionne             // not yet sorted, so sort
195134723edSLouis Dionne             break;
196134723edSLouis Dionne           }
197134723edSLouis Dionne           __m = __j;
198134723edSLouis Dionne         }
199*9783f28cSLouis Dionne       } else {
200134723edSLouis Dionne         // Check for [__i, __last) already sorted
201134723edSLouis Dionne         __j = __m = __i;
202134723edSLouis Dionne         while (true) {
203134723edSLouis Dionne           if (++__j == __last) {
204134723edSLouis Dionne             // [__i, __last) sorted
205134723edSLouis Dionne             return;
206134723edSLouis Dionne           }
207134723edSLouis Dionne           if (__comp(*__j, *__m)) {
208134723edSLouis Dionne             // not yet sorted, so sort
209134723edSLouis Dionne             break;
210134723edSLouis Dionne           }
211134723edSLouis Dionne           __m = __j;
212134723edSLouis Dionne         }
213134723edSLouis Dionne       }
214134723edSLouis Dionne     }
215134723edSLouis Dionne     // __nth_element on range containing __nth
216*9783f28cSLouis Dionne     if (__nth < __i) {
21777a00c0dSLouis Dionne       // std::__nth_element<_Compare>(__first, __nth, __i, __comp);
218134723edSLouis Dionne       __last = __i;
219*9783f28cSLouis Dionne     } else {
22077a00c0dSLouis Dionne       // std::__nth_element<_Compare>(__i+1, __nth, __last, __comp);
221134723edSLouis Dionne       __first = ++__i;
222134723edSLouis Dionne     }
223134723edSLouis Dionne   }
224134723edSLouis Dionne }
225134723edSLouis Dionne 
226a7c3379cSKonstantin Varlamov template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
227*9783f28cSLouis Dionne inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __nth_element_impl(
228*9783f28cSLouis Dionne     _RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare& __comp) {
22923c7328bSKonstantin Varlamov   if (__nth == __last)
23023c7328bSKonstantin Varlamov     return;
23123c7328bSKonstantin Varlamov 
232a7c3379cSKonstantin Varlamov   std::__debug_randomize_range<_AlgPolicy>(__first, __last);
23323c7328bSKonstantin Varlamov 
234ed2d3644SNikolas Klauser   std::__nth_element<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
23523c7328bSKonstantin Varlamov 
236a7c3379cSKonstantin Varlamov   std::__debug_randomize_range<_AlgPolicy>(__first, __nth);
237a45d2287SDanila Kutenin   if (__nth != __last) {
238a7c3379cSKonstantin Varlamov     std::__debug_randomize_range<_AlgPolicy>(++__nth, __last);
239a45d2287SDanila Kutenin   }
240134723edSLouis Dionne }
241134723edSLouis Dionne 
24223c7328bSKonstantin Varlamov template <class _RandomAccessIterator, class _Compare>
243*9783f28cSLouis Dionne inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
244*9783f28cSLouis Dionne nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp) {
245a7c3379cSKonstantin Varlamov   std::__nth_element_impl<_ClassicAlgPolicy>(std::move(__first), std::move(__nth), std::move(__last), __comp);
24623c7328bSKonstantin Varlamov }
24723c7328bSKonstantin Varlamov 
248134723edSLouis Dionne template <class _RandomAccessIterator>
249*9783f28cSLouis Dionne inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
250*9783f28cSLouis Dionne nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last) {
25188632e48SNikolas Klauser   std::nth_element(std::move(__first), std::move(__nth), std::move(__last), __less<>());
252134723edSLouis Dionne }
253134723edSLouis Dionne 
254134723edSLouis Dionne _LIBCPP_END_NAMESPACE_STD
255134723edSLouis Dionne 
256134723edSLouis Dionne #endif // _LIBCPP___ALGORITHM_NTH_ELEMENT_H
257