|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- /// \file
- // Range v3 library
- //
- // Copyright Eric Niebler 2014-present
- //
- // Use, modification and distribution is subject to the
- // Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- // Project home: https://github.com/ericniebler/range-v3
- //
-
- //===----------------------------------------------------------------------===//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is dual licensed under the MIT and the University of Illinois Open
- // Source Licenses. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
-
- #ifndef RANGES_V3_ALGORITHM_NTH_ELEMENT_HPP
- #define RANGES_V3_ALGORITHM_NTH_ELEMENT_HPP
-
- #include <utility>
-
- #include <range/v3/range_fwd.hpp>
-
- #include <range/v3/algorithm/min_element.hpp>
- #include <range/v3/functional/comparisons.hpp>
- #include <range/v3/functional/identity.hpp>
- #include <range/v3/functional/invoke.hpp>
- #include <range/v3/iterator/concepts.hpp>
- #include <range/v3/iterator/operations.hpp>
- #include <range/v3/iterator/traits.hpp>
- #include <range/v3/range/access.hpp>
- #include <range/v3/range/concepts.hpp>
- #include <range/v3/range/dangling.hpp>
- #include <range/v3/range/traits.hpp>
- #include <range/v3/utility/static_const.hpp>
- #include <range/v3/utility/swap.hpp>
-
- namespace ranges
- {
- /// \cond
- namespace detail
- {
- // stable, 2-3 compares, 0-2 swaps
-
- template<typename I, typename C, typename P>
- auto sort3(I x, I y, I z, C & pred, P & proj) -> CPP_ret(unsigned)( //
- requires forward_iterator<I> && indirect_relation<C, projected<I, P>>)
- {
- unsigned r = 0;
- if(!invoke(pred, invoke(proj, *y), invoke(proj, *x))) // if x <= y
- {
- if(!invoke(pred, invoke(proj, *z), invoke(proj, *y))) // if y <= z
- return r; // x <= y && y <= z
- // x <= y && y > z
- ranges::iter_swap(y, z); // x <= z && y < z
- r = 1;
- if(invoke(pred, invoke(proj, *y), invoke(proj, *x))) // if x > y
- {
- ranges::iter_swap(x, y); // x < y && y <= z
- r = 2;
- }
- return r; // x <= y && y < z
- }
- if(invoke(pred, invoke(proj, *z), invoke(proj, *y))) // x > y, if y > z
- {
- ranges::iter_swap(x, z); // x < y && y < z
- r = 1;
- return r;
- }
- ranges::iter_swap(x, y); // x > y && y <= z
- r = 1; // x < y && x <= z
- if(invoke(pred, invoke(proj, *z), invoke(proj, *y))) // if y > z
- {
- ranges::iter_swap(y, z); // x <= y && y < z
- r = 2;
- }
- return r;
- } // x <= y && y <= z
-
- template<typename I, typename C, typename P>
- auto selection_sort(I first, I last, C & pred, P & proj) -> CPP_ret(void)( //
- requires bidirectional_iterator<I> && indirect_relation<C, projected<I, P>>)
- {
- RANGES_EXPECT(first != last);
- for(I lm1 = ranges::prev(last); first != lm1; ++first)
- {
- I i = ranges::min_element(first, last, std::ref(pred), std::ref(proj));
- if(i != first)
- ranges::iter_swap(first, i);
- }
- }
- } // namespace detail
- /// \endcond
-
- /// \addtogroup group-algorithms
- /// @{
- RANGES_BEGIN_NIEBLOID(nth_element)
-
- /// \brief function template \c nth_element
- template<typename I, typename S, typename C = less, typename P = identity>
- auto RANGES_FUN_NIEBLOID(nth_element)(
- I first, I nth, S end_, C pred = C{}, P proj = P{}) //
- ->CPP_ret(I)( //
- requires random_access_iterator<I> && sortable<I, C, P>)
- {
- I last = ranges::next(nth, end_), end_orig = last;
- // C is known to be a reference type
- using difference_type = iter_difference_t<I>;
- difference_type const limit = 7;
- while(true)
- {
- restart:
- if(nth == last)
- return end_orig;
- difference_type len = last - first;
- switch(len)
- {
- case 0:
- case 1:
- return end_orig;
- case 2:
- if(invoke(pred, invoke(proj, *--last), invoke(proj, *first)))
- ranges::iter_swap(first, last);
- return end_orig;
- case 3:
- {
- I m = first;
- detail::sort3(first, ++m, --last, pred, proj);
- return end_orig;
- }
- }
- if(len <= limit)
- {
- detail::selection_sort(first, last, pred, proj);
- return end_orig;
- }
- // len > limit >= 3
- I m = first + len / 2;
- I lm1 = last;
- unsigned n_swaps = detail::sort3(first, m, --lm1, pred, proj);
- // *m is median
- // partition [first, m) < *m and *m <= [m, last)
- //(this inhibits tossing elements equivalent to m around unnecessarily)
- I i = first;
- I j = lm1;
- // j points beyond range to be tested, *lm1 is known to be <= *m
- // The search going up is known to be guarded but the search coming down
- // isn't. Prime the downward search with a guard.
- if(!invoke(pred, invoke(proj, *i), invoke(proj, *m))) // if *first == *m
- {
- // *first == *m, *first doesn't go in first part
- // manually guard downward moving j against i
- while(true)
- {
- if(i == --j)
- {
- // *first == *m, *m <= all other elements
- // Parition instead into [first, i) == *first and *first < [i,
- // last)
- ++i; // first + 1
- j = last;
- if(!invoke(
- pred,
- invoke(proj, *first),
- invoke(
- proj,
- *--j))) // we need a guard if *first == *(last-1)
- {
- while(true)
- {
- if(i == j)
- return end_orig; // [first, last) all equivalent
- // elements
- if(invoke(
- pred, invoke(proj, *first), invoke(proj, *i)))
- {
- ranges::iter_swap(i, j);
- ++n_swaps;
- ++i;
- break;
- }
- ++i;
- }
- }
- // [first, i) == *first and *first < [j, last) and j == last -
- // 1
- if(i == j)
- return end_orig;
- while(true)
- {
- while(
- !invoke(pred, invoke(proj, *first), invoke(proj, *i)))
- ++i;
- while(invoke(
- pred, invoke(proj, *first), invoke(proj, *--j)))
- ;
- if(i >= j)
- break;
- ranges::iter_swap(i, j);
- ++n_swaps;
- ++i;
- }
- // [first, i) == *first and *first < [i, last)
- // The first part is sorted,
- if(nth < i)
- return end_orig;
- // nth_element the second part
- // nth_element<C>(i, nth, last, pred);
- first = i;
- goto restart;
- }
- if(invoke(pred, invoke(proj, *j), invoke(proj, *m)))
- {
- ranges::iter_swap(i, j);
- ++n_swaps;
- break; // found guard for downward moving j, now use unguarded
- // partition
- }
- }
- }
- ++i;
- // j points beyond range to be tested, *lm1 is known to be <= *m
- // if not yet partitioned...
- if(i < j)
- {
- // known that *(i - 1) < *m
- while(true)
- {
- // m still guards upward moving i
- while(invoke(pred, invoke(proj, *i), invoke(proj, *m)))
- ++i;
- // It is now known that a guard exists for downward moving j
- while(!invoke(pred, invoke(proj, *--j), invoke(proj, *m)))
- ;
- if(i >= j)
- break;
- ranges::iter_swap(i, j);
- ++n_swaps;
- // It is known that m != j
- // If m just moved, follow it
- if(m == i)
- m = j;
- ++i;
- }
- }
- // [first, i) < *m and *m <= [i, last)
- if(i != m && invoke(pred, invoke(proj, *m), invoke(proj, *i)))
- {
- ranges::iter_swap(i, m);
- ++n_swaps;
- }
- // [first, i) < *i and *i <= [i+1, last)
- if(nth == i)
- return end_orig;
- if(n_swaps == 0)
- {
- // We were given a perfectly partitioned sequence. Coincidence?
- if(nth < i)
- {
- // Check for [first, i) already sorted
- j = m = first;
- while(++j != i)
- {
- if(invoke(pred, invoke(proj, *j), invoke(proj, *m)))
- // not yet sorted, so sort
- goto not_sorted;
- m = j;
- }
- // [first, i) sorted
- return end_orig;
- }
- else
- {
- // Check for [i, last) already sorted
- j = m = i;
- while(++j != last)
- {
- if(invoke(pred, invoke(proj, *j), invoke(proj, *m)))
- // not yet sorted, so sort
- goto not_sorted;
- m = j;
- }
- // [i, last) sorted
- return end_orig;
- }
- }
- not_sorted:
- // nth_element on range containing nth
- if(nth < i)
- {
- // nth_element<C>(first, nth, i, pred);
- last = i;
- }
- else
- {
- // nth_element<C>(i+1, nth, last, pred);
- first = ++i;
- }
- }
- return end_orig;
- }
-
- /// \overload
- template<typename Rng, typename C = less, typename P = identity>
- auto RANGES_FUN_NIEBLOID(nth_element)(
- Rng && rng, iterator_t<Rng> nth, C pred = C{}, P proj = P{}) //
- ->CPP_ret(safe_iterator_t<Rng>)( //
- requires random_access_range<Rng> && sortable<iterator_t<Rng>, C, P>)
- {
- return (*this)(
- begin(rng), std::move(nth), end(rng), std::move(pred), std::move(proj));
- }
-
- RANGES_END_NIEBLOID(nth_element)
-
- namespace cpp20
- {
- using ranges::nth_element;
- }
- /// @}
- } // namespace ranges
-
- #endif // include guard
|