/// \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_INPLACE_MERGE_HPP #define RANGES_V3_ALGORITHM_INPLACE_MERGE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \cond namespace detail { struct merge_adaptive_fn { private: template static void impl(I first, I middle, I last, iter_difference_t len1, iter_difference_t len2, iter_value_t * const buf, C & pred, P & proj) { auto tmpbuf = make_raw_buffer(buf); if(len1 <= len2) { auto p = ranges::move(first, middle, tmpbuf.begin()).out; merge(make_move_iterator(buf), make_move_iterator(p.base().base()), make_move_iterator(std::move(middle)), make_move_iterator(std::move(last)), std::move(first), std::ref(pred), std::ref(proj), std::ref(proj)); } else { auto p = ranges::move(middle, last, tmpbuf.begin()).out; using RBi = ranges::reverse_iterator; using Rv = ranges::reverse_iterator *>; merge(make_move_iterator(RBi{std::move(middle)}), make_move_iterator(RBi{std::move(first)}), make_move_iterator(Rv{p.base().base()}), make_move_iterator(Rv{buf}), RBi{std::move(last)}, not_fn(std::ref(pred)), std::ref(proj), std::ref(proj)); } } public: template auto operator()(I first, I middle, I last, iter_difference_t len1, iter_difference_t len2, iter_value_t * buf, std::ptrdiff_t buf_size, C pred = C{}, P proj = P{}) const -> CPP_ret(void)( // requires bidirectional_iterator && sortable) { using D = iter_difference_t; while(true) { // if middle == last, we're done if(len2 == 0) return; // shrink [first, middle) as much as possible (with no moves), // returning if it shrinks to 0 for(; true; ++first, --len1) { if(len1 == 0) return; if(invoke(pred, invoke(proj, *middle), invoke(proj, *first))) break; } if(len1 <= buf_size || len2 <= buf_size) { merge_adaptive_fn::impl(std::move(first), std::move(middle), std::move(last), len1, len2, buf, pred, proj); return; } // first < middle < end // *first > *middle // partition [first, m1) [m1, middle) [middle, m2) [m2, last) such // that // all elements in: // [first, m1) <= [middle, m2) // [middle, m2) < [m1, middle) // [m1, middle) <= [m2, last) // and m1 or m2 is in the middle of its range I m1; // "median" of [first, middle) I m2; // "median" of [middle, last) D len11; // distance(first, m1) D len21; // distance(middle, m2) // binary search smaller range if(len1 < len2) { // len >= 1, len2 >= 2 len21 = len2 / 2; m2 = next(middle, len21); m1 = upper_bound(first, middle, invoke(proj, *m2), std::ref(pred), std::ref(proj)); len11 = distance(first, m1); } else { if(len1 == 1) { // len1 >= len2 && len2 > 0, therefore len2 == 1 // It is known *first > *middle ranges::iter_swap(first, middle); return; } // len1 >= 2, len2 >= 1 len11 = len1 / 2; m1 = next(first, len11); m2 = lower_bound(middle, last, invoke(proj, *m1), std::ref(pred), std::ref(proj)); len21 = distance(middle, m2); } D len12 = len1 - len11; // distance(m1, middle) D len22 = len2 - len21; // distance(m2, last) // [first, m1) [m1, middle) [middle, m2) [m2, last) // swap middle two partitions middle = rotate(m1, std::move(middle), m2).begin(); // len12 and len21 now have swapped meanings // merge smaller range with recursive call and larger with tail // recursion elimination if(len11 + len21 < len12 + len22) { (*this)(std::move(first), std::move(m1), middle, len11, len21, buf, buf_size, std::ref(pred), std::ref(proj)); first = std::move(middle); middle = std::move(m2); len1 = len12; len2 = len22; } else { (*this)(middle, std::move(m2), std::move(last), len12, len22, buf, buf_size, std::ref(pred), std::ref(proj)); last = std::move(middle); middle = std::move(m1); len1 = len11; len2 = len21; } } } }; RANGES_INLINE_VARIABLE(merge_adaptive_fn, merge_adaptive) struct inplace_merge_no_buffer_fn { template auto operator()(I first, I middle, I last, iter_difference_t len1, iter_difference_t len2, C pred = C{}, P proj = P{}) const -> CPP_ret(void)( // requires bidirectional_iterator && sortable) { merge_adaptive(std::move(first), std::move(middle), std::move(last), len1, len2, static_cast *>(nullptr), 0, std::move(pred), std::move(proj)); } }; RANGES_INLINE_VARIABLE(inplace_merge_no_buffer_fn, inplace_merge_no_buffer) } // namespace detail /// \endcond /// \addtogroup group-algorithms /// @{ RANGES_BEGIN_NIEBLOID(inplace_merge) // TODO reimplement to only need forward iterators /// \brief function template \c inplace_merge template auto RANGES_FUN_NIEBLOID(inplace_merge)( I first, I middle, S last, C pred = C{}, P proj = P{}) ->CPP_ret(I)( // requires bidirectional_iterator && sortable) { using value_type = iter_value_t; auto len1 = distance(first, middle); auto len2_and_end = enumerate(middle, last); auto buf_size = ranges::min(len1, len2_and_end.first); std::pair buf{nullptr, 0}; std::unique_ptr h; if(detail::is_trivially_copy_assignable::value && 8 < buf_size) { buf = detail::get_temporary_buffer(buf_size); h.reset(buf.first); } detail::merge_adaptive(std::move(first), std::move(middle), len2_and_end.second, len1, len2_and_end.first, buf.first, buf.second, std::move(pred), std::move(proj)); return len2_and_end.second; } /// \overload template auto RANGES_FUN_NIEBLOID(inplace_merge)( Rng && rng, iterator_t middle, C pred = C{}, P proj = P{}) ->CPP_ret(safe_iterator_t)( // requires bidirectional_range && sortable, C, P>) { return (*this)(begin(rng), std::move(middle), end(rng), std::move(pred), std::move(proj)); } RANGES_END_NIEBLOID(inplace_merge) namespace cpp20 { using ranges::inplace_merge; } /// @} } // namespace ranges #endif // include guard