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