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