xref: /llvm-project/libcxx/include/__algorithm/nth_element.h (revision 77a00c0d546cd4aa8311b5b9031ae9ea8cdb050c)
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>
295146b57bSNikolas Klauser _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
30134723edSLouis Dionne __nth_element_find_guard(_RandomAccessIterator& __i, _RandomAccessIterator& __j,
31134723edSLouis Dionne                          _RandomAccessIterator __m, _Compare __comp)
32134723edSLouis Dionne {
33134723edSLouis Dionne     // manually guard downward moving __j against __i
34134723edSLouis Dionne     while (true) {
35134723edSLouis Dionne         if (__i == --__j) {
36134723edSLouis Dionne             return false;
37134723edSLouis Dionne         }
38134723edSLouis Dionne         if (__comp(*__j, *__m)) {
39134723edSLouis Dionne             return true;  // found guard for downward moving __j, now use unguarded partition
40134723edSLouis Dionne         }
41134723edSLouis Dionne     }
42134723edSLouis Dionne }
43134723edSLouis Dionne 
44a7c3379cSKonstantin Varlamov template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
455146b57bSNikolas Klauser _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
46ea9af5e7SDaniel Kutenin // NOLINTNEXTLINE(readability-function-cognitive-complexity)
47134723edSLouis Dionne __nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last, _Compare __comp)
48134723edSLouis Dionne {
49a7c3379cSKonstantin Varlamov     using _Ops = _IterOps<_AlgPolicy>;
50a7c3379cSKonstantin Varlamov 
51134723edSLouis Dionne     // _Compare is known to be a reference type
52134723edSLouis Dionne     typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
53134723edSLouis Dionne     const difference_type __limit = 7;
54134723edSLouis Dionne     while (true)
55134723edSLouis Dionne     {
56134723edSLouis Dionne         if (__nth == __last)
57134723edSLouis Dionne             return;
58134723edSLouis Dionne         difference_type __len = __last - __first;
59134723edSLouis Dionne         switch (__len)
60134723edSLouis Dionne         {
61134723edSLouis Dionne         case 0:
62134723edSLouis Dionne         case 1:
63134723edSLouis Dionne             return;
64134723edSLouis Dionne         case 2:
65134723edSLouis Dionne             if (__comp(*--__last, *__first))
66a7c3379cSKonstantin Varlamov                 _Ops::iter_swap(__first, __last);
67134723edSLouis Dionne             return;
68134723edSLouis Dionne         case 3:
69134723edSLouis Dionne             {
70134723edSLouis Dionne             _RandomAccessIterator __m = __first;
71a7c3379cSKonstantin Varlamov             std::__sort3<_AlgPolicy, _Compare>(__first, ++__m, --__last, __comp);
72134723edSLouis Dionne             return;
73134723edSLouis Dionne             }
74134723edSLouis Dionne         }
75134723edSLouis Dionne         if (__len <= __limit)
76134723edSLouis Dionne         {
77a7c3379cSKonstantin Varlamov             std::__selection_sort<_AlgPolicy, _Compare>(__first, __last, __comp);
78134723edSLouis Dionne             return;
79134723edSLouis Dionne         }
80134723edSLouis Dionne         // __len > __limit >= 3
81134723edSLouis Dionne         _RandomAccessIterator __m = __first + __len/2;
82134723edSLouis Dionne         _RandomAccessIterator __lm1 = __last;
83a7c3379cSKonstantin Varlamov         unsigned __n_swaps = std::__sort3<_AlgPolicy, _Compare>(__first, __m, --__lm1, __comp);
84134723edSLouis Dionne         // *__m is median
85134723edSLouis Dionne         // partition [__first, __m) < *__m and *__m <= [__m, __last)
86134723edSLouis Dionne         // (this inhibits tossing elements equivalent to __m around unnecessarily)
87134723edSLouis Dionne         _RandomAccessIterator __i = __first;
88134723edSLouis Dionne         _RandomAccessIterator __j = __lm1;
89134723edSLouis Dionne         // j points beyond range to be tested, *__lm1 is known to be <= *__m
90134723edSLouis Dionne         // The search going up is known to be guarded but the search coming down isn't.
91134723edSLouis Dionne         // Prime the downward search with a guard.
92134723edSLouis Dionne         if (!__comp(*__i, *__m))  // if *__first == *__m
93134723edSLouis Dionne         {
94134723edSLouis Dionne             // *__first == *__m, *__first doesn't go in first part
95*77a00c0dSLouis Dionne             if (std::__nth_element_find_guard<_Compare>(__i, __j, __m, __comp)) {
96a7c3379cSKonstantin Varlamov                 _Ops::iter_swap(__i, __j);
97134723edSLouis Dionne                 ++__n_swaps;
98134723edSLouis Dionne             } else {
99134723edSLouis Dionne                 // *__first == *__m, *__m <= all other elements
100134723edSLouis Dionne                 // Partition instead into [__first, __i) == *__first and *__first < [__i, __last)
101134723edSLouis Dionne                 ++__i;  // __first + 1
102134723edSLouis Dionne                 __j = __last;
103134723edSLouis Dionne                 if (!__comp(*__first, *--__j)) {  // we need a guard if *__first == *(__last-1)
104134723edSLouis Dionne                     while (true) {
105134723edSLouis Dionne                         if (__i == __j) {
106134723edSLouis Dionne                             return;  // [__first, __last) all equivalent elements
107134723edSLouis Dionne                         } else if (__comp(*__first, *__i)) {
108a7c3379cSKonstantin Varlamov                             _Ops::iter_swap(__i, __j);
109134723edSLouis Dionne                             ++__n_swaps;
110134723edSLouis Dionne                             ++__i;
111134723edSLouis Dionne                             break;
112134723edSLouis Dionne                         }
113134723edSLouis Dionne                         ++__i;
114134723edSLouis Dionne                     }
115134723edSLouis Dionne                 }
116134723edSLouis Dionne                 // [__first, __i) == *__first and *__first < [__j, __last) and __j == __last - 1
117134723edSLouis Dionne                 if (__i == __j) {
118134723edSLouis Dionne                     return;
119134723edSLouis Dionne                 }
120134723edSLouis Dionne                 while (true) {
121ea9af5e7SDaniel Kutenin                     while (!__comp(*__first, *__i)) {
122134723edSLouis Dionne                         ++__i;
123ea9af5e7SDaniel Kutenin                         _LIBCPP_ASSERT_UNCATEGORIZED(
124ea9af5e7SDaniel Kutenin                             __i != __last,
125ea9af5e7SDaniel Kutenin                             "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
126ea9af5e7SDaniel Kutenin                     }
127ea9af5e7SDaniel Kutenin                     do {
128ea9af5e7SDaniel Kutenin                         _LIBCPP_ASSERT_UNCATEGORIZED(
129ea9af5e7SDaniel Kutenin                             __j != __first,
130ea9af5e7SDaniel Kutenin                             "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
131ea9af5e7SDaniel Kutenin                         --__j;
132ea9af5e7SDaniel Kutenin                     } while (__comp(*__first, *__j));
133134723edSLouis Dionne                     if (__i >= __j)
134134723edSLouis Dionne                         break;
135a7c3379cSKonstantin Varlamov                     _Ops::iter_swap(__i, __j);
136134723edSLouis Dionne                     ++__n_swaps;
137134723edSLouis Dionne                     ++__i;
138134723edSLouis Dionne                 }
139134723edSLouis Dionne                 // [__first, __i) == *__first and *__first < [__i, __last)
140134723edSLouis Dionne                 // The first part is sorted,
141134723edSLouis Dionne                 if (__nth < __i) {
142134723edSLouis Dionne                     return;
143134723edSLouis Dionne                 }
144134723edSLouis Dionne                 // __nth_element the second part
145*77a00c0dSLouis Dionne                 // std::__nth_element<_Compare>(__i, __nth, __last, __comp);
146134723edSLouis Dionne                 __first = __i;
147134723edSLouis Dionne                 continue;
148134723edSLouis Dionne             }
149134723edSLouis Dionne         }
150134723edSLouis Dionne         ++__i;
151134723edSLouis Dionne         // j points beyond range to be tested, *__lm1 is known to be <= *__m
152134723edSLouis Dionne         // if not yet partitioned...
153134723edSLouis Dionne         if (__i < __j)
154134723edSLouis Dionne         {
155134723edSLouis Dionne             // known that *(__i - 1) < *__m
156134723edSLouis Dionne             while (true)
157134723edSLouis Dionne             {
158134723edSLouis Dionne                 // __m still guards upward moving __i
159ea9af5e7SDaniel Kutenin                 while (__comp(*__i, *__m)) {
160134723edSLouis Dionne                     ++__i;
161ea9af5e7SDaniel Kutenin                     _LIBCPP_ASSERT_UNCATEGORIZED(
162ea9af5e7SDaniel Kutenin                         __i != __last,
163ea9af5e7SDaniel Kutenin                         "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
164ea9af5e7SDaniel Kutenin                 }
165134723edSLouis Dionne                 // It is now known that a guard exists for downward moving __j
166ea9af5e7SDaniel Kutenin                 do {
167ea9af5e7SDaniel Kutenin                     _LIBCPP_ASSERT_UNCATEGORIZED(
168ea9af5e7SDaniel Kutenin                         __j != __first,
169ea9af5e7SDaniel Kutenin                         "Would read out of bounds, does your comparator satisfy the strict-weak ordering requirement?");
170ea9af5e7SDaniel Kutenin                     --__j;
171ea9af5e7SDaniel Kutenin                 } while (!__comp(*__j, *__m));
172134723edSLouis Dionne                 if (__i >= __j)
173134723edSLouis Dionne                     break;
174a7c3379cSKonstantin Varlamov                 _Ops::iter_swap(__i, __j);
175134723edSLouis Dionne                 ++__n_swaps;
176134723edSLouis Dionne                 // It is known that __m != __j
177134723edSLouis Dionne                 // If __m just moved, follow it
178134723edSLouis Dionne                 if (__m == __i)
179134723edSLouis Dionne                     __m = __j;
180134723edSLouis Dionne                 ++__i;
181134723edSLouis Dionne             }
182134723edSLouis Dionne         }
183134723edSLouis Dionne         // [__first, __i) < *__m and *__m <= [__i, __last)
184134723edSLouis Dionne         if (__i != __m && __comp(*__m, *__i))
185134723edSLouis Dionne         {
186a7c3379cSKonstantin Varlamov             _Ops::iter_swap(__i, __m);
187134723edSLouis Dionne             ++__n_swaps;
188134723edSLouis Dionne         }
189134723edSLouis Dionne         // [__first, __i) < *__i and *__i <= [__i+1, __last)
190134723edSLouis Dionne         if (__nth == __i)
191134723edSLouis Dionne             return;
192134723edSLouis Dionne         if (__n_swaps == 0)
193134723edSLouis Dionne         {
194134723edSLouis Dionne             // We were given a perfectly partitioned sequence.  Coincidence?
195134723edSLouis Dionne             if (__nth < __i)
196134723edSLouis Dionne             {
197134723edSLouis Dionne                 // Check for [__first, __i) already sorted
198134723edSLouis Dionne                 __j = __m = __first;
199134723edSLouis Dionne                 while (true) {
200134723edSLouis Dionne                     if (++__j == __i) {
201134723edSLouis Dionne                         // [__first, __i) sorted
202134723edSLouis Dionne                         return;
203134723edSLouis Dionne                     }
204134723edSLouis Dionne                     if (__comp(*__j, *__m)) {
205134723edSLouis Dionne                         // not yet sorted, so sort
206134723edSLouis Dionne                         break;
207134723edSLouis Dionne                     }
208134723edSLouis Dionne                     __m = __j;
209134723edSLouis Dionne                 }
210134723edSLouis Dionne             }
211134723edSLouis Dionne             else
212134723edSLouis Dionne             {
213134723edSLouis Dionne                 // Check for [__i, __last) already sorted
214134723edSLouis Dionne                 __j = __m = __i;
215134723edSLouis Dionne                 while (true) {
216134723edSLouis Dionne                     if (++__j == __last) {
217134723edSLouis Dionne                         // [__i, __last) sorted
218134723edSLouis Dionne                         return;
219134723edSLouis Dionne                     }
220134723edSLouis Dionne                     if (__comp(*__j, *__m)) {
221134723edSLouis Dionne                         // not yet sorted, so sort
222134723edSLouis Dionne                         break;
223134723edSLouis Dionne                     }
224134723edSLouis Dionne                     __m = __j;
225134723edSLouis Dionne                 }
226134723edSLouis Dionne             }
227134723edSLouis Dionne         }
228134723edSLouis Dionne         // __nth_element on range containing __nth
229134723edSLouis Dionne         if (__nth < __i)
230134723edSLouis Dionne         {
231*77a00c0dSLouis Dionne             // std::__nth_element<_Compare>(__first, __nth, __i, __comp);
232134723edSLouis Dionne             __last = __i;
233134723edSLouis Dionne         }
234134723edSLouis Dionne         else
235134723edSLouis Dionne         {
236*77a00c0dSLouis Dionne             // std::__nth_element<_Compare>(__i+1, __nth, __last, __comp);
237134723edSLouis Dionne             __first = ++__i;
238134723edSLouis Dionne         }
239134723edSLouis Dionne     }
240134723edSLouis Dionne }
241134723edSLouis Dionne 
242a7c3379cSKonstantin Varlamov template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
2435146b57bSNikolas Klauser inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
24423c7328bSKonstantin Varlamov void __nth_element_impl(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last,
24523c7328bSKonstantin Varlamov                         _Compare& __comp) {
24623c7328bSKonstantin Varlamov   if (__nth == __last)
24723c7328bSKonstantin Varlamov     return;
24823c7328bSKonstantin Varlamov 
249a7c3379cSKonstantin Varlamov   std::__debug_randomize_range<_AlgPolicy>(__first, __last);
25023c7328bSKonstantin Varlamov 
251ed2d3644SNikolas Klauser   std::__nth_element<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __nth, __last, __comp);
25223c7328bSKonstantin Varlamov 
253a7c3379cSKonstantin Varlamov   std::__debug_randomize_range<_AlgPolicy>(__first, __nth);
254a45d2287SDanila Kutenin   if (__nth != __last) {
255a7c3379cSKonstantin Varlamov     std::__debug_randomize_range<_AlgPolicy>(++__nth, __last);
256a45d2287SDanila Kutenin   }
257134723edSLouis Dionne }
258134723edSLouis Dionne 
25923c7328bSKonstantin Varlamov template <class _RandomAccessIterator, class _Compare>
2605146b57bSNikolas Klauser inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
26123c7328bSKonstantin Varlamov void nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last,
26223c7328bSKonstantin Varlamov                  _Compare __comp) {
263a7c3379cSKonstantin Varlamov   std::__nth_element_impl<_ClassicAlgPolicy>(std::move(__first), std::move(__nth), std::move(__last), __comp);
26423c7328bSKonstantin Varlamov }
26523c7328bSKonstantin Varlamov 
266134723edSLouis Dionne template <class _RandomAccessIterator>
2675146b57bSNikolas Klauser inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
26823c7328bSKonstantin Varlamov void nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth, _RandomAccessIterator __last) {
26988632e48SNikolas Klauser   std::nth_element(std::move(__first), std::move(__nth), std::move(__last), __less<>());
270134723edSLouis Dionne }
271134723edSLouis Dionne 
272134723edSLouis Dionne _LIBCPP_END_NAMESPACE_STD
273134723edSLouis Dionne 
274134723edSLouis Dionne #endif // _LIBCPP___ALGORITHM_NTH_ELEMENT_H
275