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.

stable_partition.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. //===-------------------------- algorithm ---------------------------------===//
  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_STABLE_PARTITION_HPP
  22. #define RANGES_V3_ALGORITHM_STABLE_PARTITION_HPP
  23. #include <functional>
  24. #include <memory>
  25. #include <type_traits>
  26. #include <meta/meta.hpp>
  27. #include <range/v3/range_fwd.hpp>
  28. #include <range/v3/algorithm/move.hpp>
  29. #include <range/v3/algorithm/partition_copy.hpp>
  30. #include <range/v3/algorithm/rotate.hpp>
  31. #include <range/v3/functional/identity.hpp>
  32. #include <range/v3/functional/invoke.hpp>
  33. #include <range/v3/iterator/concepts.hpp>
  34. #include <range/v3/iterator/move_iterators.hpp>
  35. #include <range/v3/iterator/operations.hpp>
  36. #include <range/v3/iterator/traits.hpp>
  37. #include <range/v3/range/access.hpp>
  38. #include <range/v3/range/concepts.hpp>
  39. #include <range/v3/range/dangling.hpp>
  40. #include <range/v3/range/traits.hpp>
  41. #include <range/v3/utility/memory.hpp>
  42. #include <range/v3/utility/static_const.hpp>
  43. #include <range/v3/utility/swap.hpp>
  44. namespace ranges
  45. {
  46. /// \addtogroup group-algorithms
  47. /// @{
  48. /// \cond
  49. namespace detail
  50. {
  51. template<typename I, typename C, typename P, typename D, typename Pair>
  52. I stable_partition_impl(I first, I last, C pred, P proj, D len, Pair const p,
  53. detail::forward_iterator_tag_ fi)
  54. {
  55. // *first is known to be false
  56. // len >= 1
  57. if(len == 1)
  58. return first;
  59. if(len == 2)
  60. {
  61. I tmp = first;
  62. if(invoke(pred, invoke(proj, *++tmp)))
  63. {
  64. ranges::iter_swap(first, tmp);
  65. return tmp;
  66. }
  67. return first;
  68. }
  69. if(len <= p.second)
  70. { // The buffer is big enough to use
  71. // Move the falses into the temporary buffer, and the trues to the front
  72. // of the line Update first to always point to the last of the trues
  73. auto tmpbuf = make_raw_buffer(p.first);
  74. auto buf = tmpbuf.begin();
  75. *buf = iter_move(first);
  76. ++buf;
  77. auto res = partition_copy(make_move_iterator(next(first)),
  78. make_move_sentinel(last),
  79. first,
  80. buf,
  81. std::ref(pred),
  82. std::ref(proj));
  83. // All trues now at start of range, all falses in buffer
  84. // Move falses back into range, but don't mess up first which points to
  85. // first false
  86. ranges::move(p.first, res.out2.base().base(), res.out1);
  87. // h destructs moved-from values out of the temp buffer, but doesn't
  88. // deallocate buffer
  89. return res.out1;
  90. }
  91. // Else not enough buffer, do in place
  92. // len >= 3
  93. D half = len / 2; // half >= 2
  94. I middle = next(first, half);
  95. // recurse on [first, middle), *first know to be false
  96. // F?????????????????
  97. // f m l
  98. I begin_false =
  99. detail::stable_partition_impl(first, middle, pred, proj, half, p, fi);
  100. // TTTFFFFF??????????
  101. // f ff m l
  102. // recurse on [middle, last], except increase middle until *(middle) is false,
  103. // *last know to be true
  104. I m1 = middle;
  105. D len_half = len - half;
  106. while(invoke(pred, invoke(proj, *m1)))
  107. {
  108. if(++m1 == last)
  109. return ranges::rotate(begin_false, middle, last).begin();
  110. --len_half;
  111. }
  112. // TTTFFFFFTTTF??????
  113. // f ff m m1 l
  114. I end_false =
  115. detail::stable_partition_impl(m1, last, pred, proj, len_half, p, fi);
  116. // TTTFFFFFTTTTTFFFFF
  117. // f ff m sf l
  118. return ranges::rotate(begin_false, middle, end_false).begin();
  119. // TTTTTTTTFFFFFFFFFF
  120. // |
  121. }
  122. template<typename I, typename S, typename C, typename P>
  123. I stable_partition_impl(I first, S last, C pred, P proj,
  124. detail::forward_iterator_tag_ fi)
  125. {
  126. using difference_type = iter_difference_t<I>;
  127. difference_type const alloc_limit = 3; // might want to make this a function
  128. // of trivial assignment.
  129. // Either prove all true and return first or point to first false
  130. while(true)
  131. {
  132. if(first == last)
  133. return first;
  134. if(!invoke(pred, invoke(proj, *first)))
  135. break;
  136. ++first;
  137. }
  138. // We now have a reduced range [first, last)
  139. // *first is known to be false
  140. using value_type = iter_value_t<I>;
  141. auto len_end = enumerate(first, last);
  142. auto p = len_end.first >= alloc_limit
  143. ? detail::get_temporary_buffer<value_type>(len_end.first)
  144. : detail::value_init{};
  145. std::unique_ptr<value_type, detail::return_temporary_buffer> const h{p.first};
  146. return detail::stable_partition_impl(
  147. first, len_end.second, pred, proj, len_end.first, p, fi);
  148. }
  149. template<typename I, typename C, typename P, typename D, typename Pair>
  150. I stable_partition_impl(I first, I last, C pred, P proj, D len, Pair p,
  151. detail::bidirectional_iterator_tag_ bi)
  152. {
  153. // *first is known to be false
  154. // *last is known to be true
  155. // len >= 2
  156. if(len == 2)
  157. {
  158. ranges::iter_swap(first, last);
  159. return last;
  160. }
  161. if(len == 3)
  162. {
  163. I tmp = first;
  164. if(invoke(pred, invoke(proj, *++tmp)))
  165. {
  166. ranges::iter_swap(first, tmp);
  167. ranges::iter_swap(tmp, last);
  168. return last;
  169. }
  170. ranges::iter_swap(tmp, last);
  171. ranges::iter_swap(first, tmp);
  172. return tmp;
  173. }
  174. if(len <= p.second)
  175. { // The buffer is big enough to use
  176. // Move the falses into the temporary buffer, and the trues to the front
  177. // of the line Update first to always point to the last of the trues
  178. auto tmpbuf = ranges::make_raw_buffer(p.first);
  179. auto buf = tmpbuf.begin();
  180. *buf = iter_move(first);
  181. ++buf;
  182. auto res = partition_copy(make_move_iterator(next(first)),
  183. make_move_sentinel(last),
  184. first,
  185. buf,
  186. std::ref(pred),
  187. std::ref(proj));
  188. first = res.out1;
  189. // move *last, known to be true
  190. *first = iter_move(res.in);
  191. ++first;
  192. // All trues now at start of range, all falses in buffer
  193. // Move falses back into range, but don't mess up first which points to
  194. // first false
  195. ranges::move(p.first, res.out2.base().base(), first);
  196. // h destructs moved-from values out of the temp buffer, but doesn't
  197. // deallocate buffer
  198. return first;
  199. }
  200. // Else not enough buffer, do in place
  201. // len >= 4
  202. I middle = first;
  203. D half = len / 2; // half >= 2
  204. advance(middle, half);
  205. // recurse on [first, middle-1], except reduce middle-1 until *(middle-1) is
  206. // true, *first know to be false F????????????????T f m l
  207. I m1 = middle;
  208. I begin_false = first;
  209. D len_half = half;
  210. while(!invoke(pred, invoke(proj, *--m1)))
  211. {
  212. if(m1 == first)
  213. goto first_half_done;
  214. --len_half;
  215. }
  216. // F???TFFF?????????T
  217. // f m1 m l
  218. begin_false =
  219. detail::stable_partition_impl(first, m1, pred, proj, len_half, p, bi);
  220. first_half_done:
  221. // TTTFFFFF?????????T
  222. // f ff m l
  223. // recurse on [middle, last], except increase middle until *(middle) is false,
  224. // *last know to be true
  225. m1 = middle;
  226. len_half = len - half;
  227. while(invoke(pred, invoke(proj, *m1)))
  228. {
  229. if(++m1 == last)
  230. return ranges::rotate(begin_false, middle, ++last).begin();
  231. --len_half;
  232. }
  233. // TTTFFFFFTTTF?????T
  234. // f ff m m1 l
  235. I end_false =
  236. detail::stable_partition_impl(m1, last, pred, proj, len_half, p, bi);
  237. // TTTFFFFFTTTTTFFFFF
  238. // f ff m sf l
  239. return ranges::rotate(begin_false, middle, end_false).begin();
  240. // TTTTTTTTFFFFFFFFFF
  241. // |
  242. }
  243. template<typename I, typename S, typename C, typename P>
  244. I stable_partition_impl(I first, S end_, C pred, P proj,
  245. detail::bidirectional_iterator_tag_ bi)
  246. {
  247. using difference_type = iter_difference_t<I>;
  248. using value_type = iter_value_t<I>;
  249. difference_type const alloc_limit =
  250. 4; // might want to make this a function of trivial assignment
  251. // Either prove all true and return first or point to first false
  252. while(true)
  253. {
  254. if(first == end_)
  255. return first;
  256. if(!invoke(pred, invoke(proj, *first)))
  257. break;
  258. ++first;
  259. }
  260. // first points to first false, everything prior to first is already set.
  261. // Either prove [first, last) is all false and return first, or point last to
  262. // last true
  263. I last = ranges::next(first, end_);
  264. do
  265. {
  266. if(first == --last)
  267. return first;
  268. } while(!invoke(pred, invoke(proj, *last)));
  269. // We now have a reduced range [first, last]
  270. // *first is known to be false
  271. // *last is known to be true
  272. // len >= 2
  273. auto len = distance(first, last) + 1;
  274. auto p = len >= alloc_limit ? detail::get_temporary_buffer<value_type>(len)
  275. : detail::value_init{};
  276. std::unique_ptr<value_type, detail::return_temporary_buffer> const h{p.first};
  277. return detail::stable_partition_impl(first, last, pred, proj, len, p, bi);
  278. }
  279. } // namespace detail
  280. /// endcond
  281. RANGES_BEGIN_NIEBLOID(stable_partition)
  282. /// \brief function template \c stable_partition
  283. template<typename I, typename S, typename C, typename P = identity>
  284. auto RANGES_FUN_NIEBLOID(stable_partition)(
  285. I first, S last, C pred, P proj = P{}) //
  286. ->CPP_ret(I)( //
  287. requires bidirectional_iterator<I> && sentinel_for<S, I> &&
  288. indirect_unary_predicate<C, projected<I, P>> && permutable<I>)
  289. {
  290. return detail::stable_partition_impl(std::move(first),
  291. std::move(last),
  292. std::ref(pred),
  293. std::ref(proj),
  294. iterator_tag_of<I>());
  295. }
  296. // BUGBUG Can this be optimized if Rng has O1 size?
  297. /// \overload
  298. template<typename Rng, typename C, typename P = identity>
  299. auto RANGES_FUN_NIEBLOID(stable_partition)(Rng && rng, C pred, P proj = P{}) //
  300. ->CPP_ret(safe_iterator_t<Rng>)( //
  301. requires bidirectional_range<Rng> &&
  302. indirect_unary_predicate<C, projected<iterator_t<Rng>, P>> &&
  303. permutable<iterator_t<Rng>>)
  304. {
  305. return (*this)(begin(rng), end(rng), std::move(pred), std::move(proj));
  306. }
  307. RANGES_END_NIEBLOID(stable_partition)
  308. namespace cpp20
  309. {
  310. using ranges::stable_partition;
  311. }
  312. /// @}
  313. } // namespace ranges
  314. #endif // include guard