You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

nth_element.hpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /// \file
  2. // Range v3 library
  3. //
  4. // Copyright Eric Niebler 2014-present
  5. //
  6. // Use, modification and distribution is subject to the
  7. // Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. // Project home: https://github.com/ericniebler/range-v3
  12. //
  13. //===----------------------------------------------------------------------===//
  14. //
  15. // The LLVM Compiler Infrastructure
  16. //
  17. // This file is dual licensed under the MIT and the University of Illinois Open
  18. // Source Licenses. See LICENSE.TXT for details.
  19. //
  20. //===----------------------------------------------------------------------===//
  21. #ifndef RANGES_V3_ALGORITHM_NTH_ELEMENT_HPP
  22. #define RANGES_V3_ALGORITHM_NTH_ELEMENT_HPP
  23. #include <utility>
  24. #include <range/v3/range_fwd.hpp>
  25. #include <range/v3/algorithm/min_element.hpp>
  26. #include <range/v3/functional/comparisons.hpp>
  27. #include <range/v3/functional/identity.hpp>
  28. #include <range/v3/functional/invoke.hpp>
  29. #include <range/v3/iterator/concepts.hpp>
  30. #include <range/v3/iterator/operations.hpp>
  31. #include <range/v3/iterator/traits.hpp>
  32. #include <range/v3/range/access.hpp>
  33. #include <range/v3/range/concepts.hpp>
  34. #include <range/v3/range/dangling.hpp>
  35. #include <range/v3/range/traits.hpp>
  36. #include <range/v3/utility/static_const.hpp>
  37. #include <range/v3/utility/swap.hpp>
  38. namespace ranges
  39. {
  40. /// \cond
  41. namespace detail
  42. {
  43. // stable, 2-3 compares, 0-2 swaps
  44. template<typename I, typename C, typename P>
  45. auto sort3(I x, I y, I z, C & pred, P & proj) -> CPP_ret(unsigned)( //
  46. requires forward_iterator<I> && indirect_relation<C, projected<I, P>>)
  47. {
  48. unsigned r = 0;
  49. if(!invoke(pred, invoke(proj, *y), invoke(proj, *x))) // if x <= y
  50. {
  51. if(!invoke(pred, invoke(proj, *z), invoke(proj, *y))) // if y <= z
  52. return r; // x <= y && y <= z
  53. // x <= y && y > z
  54. ranges::iter_swap(y, z); // x <= z && y < z
  55. r = 1;
  56. if(invoke(pred, invoke(proj, *y), invoke(proj, *x))) // if x > y
  57. {
  58. ranges::iter_swap(x, y); // x < y && y <= z
  59. r = 2;
  60. }
  61. return r; // x <= y && y < z
  62. }
  63. if(invoke(pred, invoke(proj, *z), invoke(proj, *y))) // x > y, if y > z
  64. {
  65. ranges::iter_swap(x, z); // x < y && y < z
  66. r = 1;
  67. return r;
  68. }
  69. ranges::iter_swap(x, y); // x > y && y <= z
  70. r = 1; // x < y && x <= z
  71. if(invoke(pred, invoke(proj, *z), invoke(proj, *y))) // if y > z
  72. {
  73. ranges::iter_swap(y, z); // x <= y && y < z
  74. r = 2;
  75. }
  76. return r;
  77. } // x <= y && y <= z
  78. template<typename I, typename C, typename P>
  79. auto selection_sort(I first, I last, C & pred, P & proj) -> CPP_ret(void)( //
  80. requires bidirectional_iterator<I> && indirect_relation<C, projected<I, P>>)
  81. {
  82. RANGES_EXPECT(first != last);
  83. for(I lm1 = ranges::prev(last); first != lm1; ++first)
  84. {
  85. I i = ranges::min_element(first, last, std::ref(pred), std::ref(proj));
  86. if(i != first)
  87. ranges::iter_swap(first, i);
  88. }
  89. }
  90. } // namespace detail
  91. /// \endcond
  92. /// \addtogroup group-algorithms
  93. /// @{
  94. RANGES_BEGIN_NIEBLOID(nth_element)
  95. /// \brief function template \c nth_element
  96. template<typename I, typename S, typename C = less, typename P = identity>
  97. auto RANGES_FUN_NIEBLOID(nth_element)(
  98. I first, I nth, S end_, C pred = C{}, P proj = P{}) //
  99. ->CPP_ret(I)( //
  100. requires random_access_iterator<I> && sortable<I, C, P>)
  101. {
  102. I last = ranges::next(nth, end_), end_orig = last;
  103. // C is known to be a reference type
  104. using difference_type = iter_difference_t<I>;
  105. difference_type const limit = 7;
  106. while(true)
  107. {
  108. restart:
  109. if(nth == last)
  110. return end_orig;
  111. difference_type len = last - first;
  112. switch(len)
  113. {
  114. case 0:
  115. case 1:
  116. return end_orig;
  117. case 2:
  118. if(invoke(pred, invoke(proj, *--last), invoke(proj, *first)))
  119. ranges::iter_swap(first, last);
  120. return end_orig;
  121. case 3:
  122. {
  123. I m = first;
  124. detail::sort3(first, ++m, --last, pred, proj);
  125. return end_orig;
  126. }
  127. }
  128. if(len <= limit)
  129. {
  130. detail::selection_sort(first, last, pred, proj);
  131. return end_orig;
  132. }
  133. // len > limit >= 3
  134. I m = first + len / 2;
  135. I lm1 = last;
  136. unsigned n_swaps = detail::sort3(first, m, --lm1, pred, proj);
  137. // *m is median
  138. // partition [first, m) < *m and *m <= [m, last)
  139. //(this inhibits tossing elements equivalent to m around unnecessarily)
  140. I i = first;
  141. I j = lm1;
  142. // j points beyond range to be tested, *lm1 is known to be <= *m
  143. // The search going up is known to be guarded but the search coming down
  144. // isn't. Prime the downward search with a guard.
  145. if(!invoke(pred, invoke(proj, *i), invoke(proj, *m))) // if *first == *m
  146. {
  147. // *first == *m, *first doesn't go in first part
  148. // manually guard downward moving j against i
  149. while(true)
  150. {
  151. if(i == --j)
  152. {
  153. // *first == *m, *m <= all other elements
  154. // Parition instead into [first, i) == *first and *first < [i,
  155. // last)
  156. ++i; // first + 1
  157. j = last;
  158. if(!invoke(
  159. pred,
  160. invoke(proj, *first),
  161. invoke(
  162. proj,
  163. *--j))) // we need a guard if *first == *(last-1)
  164. {
  165. while(true)
  166. {
  167. if(i == j)
  168. return end_orig; // [first, last) all equivalent
  169. // elements
  170. if(invoke(
  171. pred, invoke(proj, *first), invoke(proj, *i)))
  172. {
  173. ranges::iter_swap(i, j);
  174. ++n_swaps;
  175. ++i;
  176. break;
  177. }
  178. ++i;
  179. }
  180. }
  181. // [first, i) == *first and *first < [j, last) and j == last -
  182. // 1
  183. if(i == j)
  184. return end_orig;
  185. while(true)
  186. {
  187. while(
  188. !invoke(pred, invoke(proj, *first), invoke(proj, *i)))
  189. ++i;
  190. while(invoke(
  191. pred, invoke(proj, *first), invoke(proj, *--j)))
  192. ;
  193. if(i >= j)
  194. break;
  195. ranges::iter_swap(i, j);
  196. ++n_swaps;
  197. ++i;
  198. }
  199. // [first, i) == *first and *first < [i, last)
  200. // The first part is sorted,
  201. if(nth < i)
  202. return end_orig;
  203. // nth_element the second part
  204. // nth_element<C>(i, nth, last, pred);
  205. first = i;
  206. goto restart;
  207. }
  208. if(invoke(pred, invoke(proj, *j), invoke(proj, *m)))
  209. {
  210. ranges::iter_swap(i, j);
  211. ++n_swaps;
  212. break; // found guard for downward moving j, now use unguarded
  213. // partition
  214. }
  215. }
  216. }
  217. ++i;
  218. // j points beyond range to be tested, *lm1 is known to be <= *m
  219. // if not yet partitioned...
  220. if(i < j)
  221. {
  222. // known that *(i - 1) < *m
  223. while(true)
  224. {
  225. // m still guards upward moving i
  226. while(invoke(pred, invoke(proj, *i), invoke(proj, *m)))
  227. ++i;
  228. // It is now known that a guard exists for downward moving j
  229. while(!invoke(pred, invoke(proj, *--j), invoke(proj, *m)))
  230. ;
  231. if(i >= j)
  232. break;
  233. ranges::iter_swap(i, j);
  234. ++n_swaps;
  235. // It is known that m != j
  236. // If m just moved, follow it
  237. if(m == i)
  238. m = j;
  239. ++i;
  240. }
  241. }
  242. // [first, i) < *m and *m <= [i, last)
  243. if(i != m && invoke(pred, invoke(proj, *m), invoke(proj, *i)))
  244. {
  245. ranges::iter_swap(i, m);
  246. ++n_swaps;
  247. }
  248. // [first, i) < *i and *i <= [i+1, last)
  249. if(nth == i)
  250. return end_orig;
  251. if(n_swaps == 0)
  252. {
  253. // We were given a perfectly partitioned sequence. Coincidence?
  254. if(nth < i)
  255. {
  256. // Check for [first, i) already sorted
  257. j = m = first;
  258. while(++j != i)
  259. {
  260. if(invoke(pred, invoke(proj, *j), invoke(proj, *m)))
  261. // not yet sorted, so sort
  262. goto not_sorted;
  263. m = j;
  264. }
  265. // [first, i) sorted
  266. return end_orig;
  267. }
  268. else
  269. {
  270. // Check for [i, last) already sorted
  271. j = m = i;
  272. while(++j != last)
  273. {
  274. if(invoke(pred, invoke(proj, *j), invoke(proj, *m)))
  275. // not yet sorted, so sort
  276. goto not_sorted;
  277. m = j;
  278. }
  279. // [i, last) sorted
  280. return end_orig;
  281. }
  282. }
  283. not_sorted:
  284. // nth_element on range containing nth
  285. if(nth < i)
  286. {
  287. // nth_element<C>(first, nth, i, pred);
  288. last = i;
  289. }
  290. else
  291. {
  292. // nth_element<C>(i+1, nth, last, pred);
  293. first = ++i;
  294. }
  295. }
  296. return end_orig;
  297. }
  298. /// \overload
  299. template<typename Rng, typename C = less, typename P = identity>
  300. auto RANGES_FUN_NIEBLOID(nth_element)(
  301. Rng && rng, iterator_t<Rng> nth, C pred = C{}, P proj = P{}) //
  302. ->CPP_ret(safe_iterator_t<Rng>)( //
  303. requires random_access_range<Rng> && sortable<iterator_t<Rng>, C, P>)
  304. {
  305. return (*this)(
  306. begin(rng), std::move(nth), end(rng), std::move(pred), std::move(proj));
  307. }
  308. RANGES_END_NIEBLOID(nth_element)
  309. namespace cpp20
  310. {
  311. using ranges::nth_element;
  312. }
  313. /// @}
  314. } // namespace ranges
  315. #endif // include guard