Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:40:51

0001 //////////////////////////////////////////////////////////////////////////////
0002 //
0003 // (C) Copyright Ion Gaztanaga 2015-2016.
0004 // Distributed under the Boost Software License, Version 1.0.
0005 // (See accompanying file LICENSE_1_0.txt or copy at
0006 // http://www.boost.org/LICENSE_1_0.txt)
0007 //
0008 // See http://www.boost.org/libs/move for documentation.
0009 //
0010 //////////////////////////////////////////////////////////////////////////////
0011 //
0012 // Stable sorting that works in O(N*log(N)) worst time
0013 // and uses O(1) extra memory
0014 //
0015 //////////////////////////////////////////////////////////////////////////////
0016 //
0017 // The main idea of the adaptive_sort algorithm was developed by Andrey Astrelin
0018 // and explained in the article from the russian collaborative blog
0019 // Habrahabr (http://habrahabr.ru/post/205290/). The algorithm is based on
0020 // ideas from B-C. Huang and M. A. Langston explained in their article
0021 // "Fast Stable Merging and Sorting in Constant Extra Space (1989-1992)"
0022 // (http://comjnl.oxfordjournals.org/content/35/6/643.full.pdf).
0023 //
0024 // This implementation by Ion Gaztanaga uses previous ideas with additional changes:
0025 // 
0026 // - Use of GCD-based rotation.
0027 // - Non power of two buffer-sizes.
0028 // - Tries to find sqrt(len)*2 unique keys, so that the merge sort
0029 //   phase can form up to sqrt(len)*4 segments if enough keys are found.
0030 // - The merge-sort phase can take advantage of external memory to
0031 //   save some additional combination steps.
0032 // - Combination phase: Blocks are selection sorted and merged in parallel.
0033 // - The combination phase is performed alternating merge to left and merge
0034 //   to right phases minimizing swaps due to internal buffer repositioning.
0035 // - When merging blocks special optimizations are made to avoid moving some
0036 //   elements twice.
0037 //
0038 // The adaptive_merge algorithm was developed by Ion Gaztanaga reusing some parts
0039 // from the sorting algorithm and implementing an additional block merge algorithm
0040 // without moving elements to left or right.
0041 //////////////////////////////////////////////////////////////////////////////
0042 #ifndef BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
0043 #define BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
0044 
0045 #include <boost/move/detail/config_begin.hpp>
0046 
0047 #include <boost/move/detail/reverse_iterator.hpp>
0048 #include <boost/move/algo/move.hpp>
0049 #include <boost/move/algo/detail/merge.hpp>
0050 #include <boost/move/adl_move_swap.hpp>
0051 #include <boost/move/algo/detail/insertion_sort.hpp>
0052 #include <boost/move/algo/detail/merge_sort.hpp>
0053 #include <boost/move/algo/detail/heap_sort.hpp>
0054 #include <boost/move/algo/detail/merge.hpp>
0055 #include <boost/move/algo/detail/is_sorted.hpp>
0056 #include <cassert>
0057 #include <boost/cstdint.hpp>
0058 #include <limits.h>
0059 
0060 #if defined(BOOST_CLANG) || (defined(BOOST_GCC) && (BOOST_GCC >= 40600))
0061 #pragma GCC diagnostic push
0062 #pragma GCC diagnostic ignored "-Wsign-conversion"
0063 #endif
0064 
0065 #ifndef BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL
0066    #define BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL 1
0067 #endif
0068 
0069 #ifdef BOOST_MOVE_ADAPTIVE_SORT_STATS
0070    #if BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL == 2
0071       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L) \
0072          print_stats(STR, L)\
0073       //
0074 
0075       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L) \
0076          print_stats(STR, L)\
0077       //
0078    #else
0079       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L) \
0080          print_stats(STR, L)\
0081       //
0082 
0083       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L)
0084    #endif
0085 #else
0086    #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L)
0087    #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L)
0088 #endif
0089 
0090 #ifdef BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS
0091    #define BOOST_MOVE_ADAPTIVE_SORT_INVARIANT  assert
0092 #else
0093    #define BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(L)
0094 #endif
0095 
0096 namespace boost {
0097 namespace movelib {
0098 
0099 #if defined(BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS)
0100 
0101 bool is_sorted(::order_perf_type *first, ::order_perf_type *last, ::order_type_less)
0102 {
0103    if (first != last) {
0104       const order_perf_type *next = first, *cur(first);
0105       while (++next != last) {
0106          if (!(cur->key < next->key || (cur->key == next->key && cur->val < next->val)))
0107             return false;
0108          cur = next;
0109       }
0110    }
0111    return true;
0112 }
0113 
0114 #endif   //BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS
0115 
0116 namespace detail_adaptive {
0117 
0118 static const std::size_t AdaptiveSortInsertionSortThreshold = 16;
0119 //static const std::size_t AdaptiveSortInsertionSortThreshold = 4;
0120 BOOST_MOVE_STATIC_ASSERT((AdaptiveSortInsertionSortThreshold&(AdaptiveSortInsertionSortThreshold-1)) == 0);
0121 
0122 #if defined BOOST_HAS_INTPTR_T
0123    typedef ::boost::uintptr_t uintptr_t;
0124 #else
0125    typedef std::size_t uintptr_t;
0126 #endif
0127 
0128 template<class T>
0129 const T &min_value(const T &a, const T &b)
0130 {
0131    return a < b ? a : b;
0132 }
0133 
0134 template<class T>
0135 const T &max_value(const T &a, const T &b)
0136 {
0137    return a > b ? a : b;
0138 }
0139 
0140 template<class ForwardIt, class Pred, class V>
0141 typename iter_size<ForwardIt>::type
0142    count_if_with(ForwardIt first, ForwardIt last, Pred pred, const V &v)
0143 {
0144    typedef typename iter_size<ForwardIt>::type size_type;
0145    size_type count = 0;
0146    while(first != last) {
0147       count = size_type(count + static_cast<size_type>(0 != pred(*first, v)));
0148       ++first;
0149    }
0150    return count;
0151 }
0152 
0153 
0154 template<class RandIt, class Compare>
0155 RandIt skip_until_merge
0156    ( RandIt first1, RandIt const last1
0157    , const typename iterator_traits<RandIt>::value_type &next_key, Compare comp)
0158 {
0159    while(first1 != last1 && !comp(next_key, *first1)){
0160       ++first1;
0161    }
0162    return first1;
0163 }
0164 
0165 
0166 template<class RandItKeys, class RandIt>
0167 void swap_and_update_key
0168    ( RandItKeys const key_next
0169    , RandItKeys const key_range2
0170    , RandItKeys &key_mid
0171    , RandIt const begin
0172    , RandIt const end
0173    , RandIt const with)
0174 {
0175    if(begin != with){
0176       ::boost::adl_move_swap_ranges(begin, end, with);
0177       if(key_next != key_range2)  //Avoid potential self-swapping
0178          ::boost::adl_move_swap(*key_next, *key_range2);
0179       if(key_next == key_mid){
0180          key_mid = key_range2;
0181       }
0182       else if(key_mid == key_range2){
0183          key_mid = key_next;
0184       }
0185    }
0186 }
0187 
0188 template<class RandItKeys>
0189 void update_key
0190 (RandItKeys const key_next
0191    , RandItKeys const key_range2
0192    , RandItKeys &key_mid)
0193 {
0194    if (key_next != key_range2) {
0195       ::boost::adl_move_swap(*key_next, *key_range2);
0196       if (key_next == key_mid) {
0197          key_mid = key_range2;
0198       }
0199       else if (key_mid == key_range2) {
0200          key_mid = key_next;
0201       }
0202    }
0203 }
0204 
0205 template<class RandItKeys, class RandIt, class RandIt2, class Op>
0206 RandIt2 buffer_and_update_key
0207 (RandItKeys const key_next
0208    , RandItKeys const key_range2
0209    , RandItKeys &key_mid
0210    , RandIt begin
0211    , RandIt end
0212    , RandIt with
0213    , RandIt2 buffer
0214    , Op op)
0215 {
0216    if (begin != with) {
0217       while(begin != end) {
0218          op(three_way_t(), begin++, with++, buffer++);
0219       }
0220       if (key_next != key_range2)   //Avoid potential self-swapping
0221          ::boost::adl_move_swap(*key_next, *key_range2);
0222       if (key_next == key_mid) {
0223          key_mid = key_range2;
0224       }
0225       else if (key_mid == key_range2) {
0226          key_mid = key_next;
0227       }
0228    }
0229    return buffer;
0230 }
0231 
0232 ///////////////////////////////////////////////////////////////////////////////
0233 //
0234 //                         MERGE BUFFERLESS
0235 //
0236 ///////////////////////////////////////////////////////////////////////////////
0237 
0238 // [first1, last1) merge [last1,last2) -> [first1,last2)
0239 template<class RandIt, class Compare>
0240 RandIt partial_merge_bufferless_impl
0241    (RandIt first1, RandIt last1, RandIt const last2, bool *const pis_range1_A, Compare comp)
0242 {
0243    if(last1 == last2){
0244       return first1;
0245    }
0246    bool const is_range1_A = *pis_range1_A;
0247    if(first1 != last1 && comp(*last1, last1[-1])){
0248       do{
0249          RandIt const old_last1 = last1;
0250          last1  = boost::movelib::lower_bound(last1, last2, *first1, comp);
0251          first1 = rotate_gcd(first1, old_last1, last1);//old_last1 == last1 supported
0252          if(last1 == last2){
0253             return first1;
0254          }
0255          do{
0256             ++first1;
0257          } while(last1 != first1 && !comp(*last1, *first1) );
0258       } while(first1 != last1);
0259    }
0260    *pis_range1_A = !is_range1_A;
0261    return last1;
0262 }
0263 
0264 // [first1, last1) merge [last1,last2) -> [first1,last2)
0265 template<class RandIt, class Compare>
0266 RandIt partial_merge_bufferless
0267    (RandIt first1, RandIt last1, RandIt const last2, bool *const pis_range1_A, Compare comp)
0268 {
0269    return *pis_range1_A ? partial_merge_bufferless_impl(first1, last1, last2, pis_range1_A, comp)
0270                         : partial_merge_bufferless_impl(first1, last1, last2, pis_range1_A, antistable<Compare>(comp));
0271 }
0272 
0273 template<class SizeType>
0274 static SizeType needed_keys_count(SizeType n_block_a, SizeType n_block_b)
0275 {
0276    return SizeType(n_block_a + n_block_b);
0277 }
0278 
0279 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
0280 typename iter_size<RandIt>::type
0281    find_next_block
0282       ( RandItKeys const key_first
0283       , KeyCompare key_comp
0284       , RandIt const first
0285       , typename iter_size<RandIt>::type const l_block
0286       , typename iter_size<RandIt>::type const ix_first_block
0287       , typename iter_size<RandIt>::type const ix_last_block
0288       , Compare comp)
0289 {
0290    typedef typename iter_size<RandIt>::type      size_type;
0291    typedef typename iterator_traits<RandIt>::value_type     value_type;
0292    typedef typename iterator_traits<RandItKeys>::value_type key_type;
0293    assert(ix_first_block <= ix_last_block);
0294    size_type ix_min_block = 0u;
0295    for (size_type szt_i = ix_first_block; szt_i < ix_last_block; ++szt_i) {
0296       const value_type &min_val = first[size_type(ix_min_block*l_block)];
0297       const value_type &cur_val = first[size_type(szt_i*l_block)];
0298       const key_type   &min_key = key_first[ix_min_block];
0299       const key_type   &cur_key = key_first[szt_i];
0300 
0301       bool const less_than_minimum = comp(cur_val, min_val) ||
0302          (!comp(min_val, cur_val) && key_comp(cur_key, min_key));
0303 
0304       if (less_than_minimum) {
0305          ix_min_block = szt_i;
0306       }
0307    }
0308    return ix_min_block;
0309 }
0310 
0311 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
0312 void merge_blocks_bufferless
0313    ( RandItKeys const key_first
0314    , KeyCompare key_comp
0315    , RandIt const first
0316    , typename iter_size<RandIt>::type const l_block
0317    , typename iter_size<RandIt>::type const l_irreg1
0318    , typename iter_size<RandIt>::type const n_block_a
0319    , typename iter_size<RandIt>::type const n_block_b
0320    , typename iter_size<RandIt>::type const l_irreg2
0321    , Compare comp)
0322 {
0323    typedef typename iter_size<RandIt>::type size_type;
0324    size_type const key_count = needed_keys_count(n_block_a, n_block_b);
0325    ::boost::movelib::ignore(key_count);
0326    //assert(n_block_a || n_block_b);
0327    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
0328    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + key_count, key_comp, key_first[n_block_a]));
0329 
0330    size_type n_bef_irreg2 = 0;
0331    bool l_irreg_pos_count = true;
0332    RandItKeys key_mid(key_first + n_block_a);
0333    RandIt const first_irr2 = first + size_type(l_irreg1 + (n_block_a+n_block_b)*l_block);
0334    RandIt const last_irr2  = first_irr2 + l_irreg2;
0335 
0336    {  //Selection sort blocks
0337       size_type n_block_left = size_type(n_block_b + n_block_a);
0338       RandItKeys key_range2(key_first);
0339 
0340       size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
0341       size_type max_check = min_value<size_type>(size_type(min_check+1), n_block_left);
0342       for ( RandIt f = first+l_irreg1; n_block_left; --n_block_left) {
0343          size_type const next_key_idx = find_next_block(key_range2, key_comp, f, l_block, min_check, max_check, comp);
0344          RandItKeys const key_next(key_range2 + next_key_idx);
0345          max_check = min_value<size_type>(max_value<size_type>(max_check, size_type(next_key_idx+2)), n_block_left);
0346 
0347          RandIt const first_min = f + size_type(next_key_idx*l_block);
0348 
0349          //Check if irregular b block should go here.
0350          //If so, break to the special code handling the irregular block
0351          if (l_irreg_pos_count && l_irreg2 && comp(*first_irr2, *first_min)){
0352             l_irreg_pos_count = false;
0353          }
0354          n_bef_irreg2 = size_type(n_bef_irreg2+l_irreg_pos_count);
0355 
0356          swap_and_update_key(key_next, key_range2, key_mid, f, f + l_block, first_min);
0357          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(f, f+l_block, comp));
0358          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_min, first_min + l_block, comp));
0359          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((f == (first+l_irreg1)) || !comp(*f, *(f-l_block)));
0360          //Update context
0361          ++key_range2;
0362          f += l_block;
0363          min_check = size_type(min_check - (min_check != 0));
0364          max_check = size_type(max_check - (max_check != 0));
0365       }
0366    }
0367    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first+l_irreg1+n_bef_irreg2*l_block, first_irr2, comp));
0368 
0369    RandIt first1 = first;
0370    RandIt last1  = first+l_irreg1;
0371    RandItKeys const key_end (key_first+n_bef_irreg2);
0372    bool is_range1_A = true;
0373 
0374    for(RandItKeys key_next = key_first; key_next != key_end; ++key_next){
0375       bool is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
0376       first1 = is_range1_A == is_range2_A
0377          ? last1 : partial_merge_bufferless(first1, last1, last1 + l_block, &is_range1_A, comp);
0378       last1 += l_block;
0379       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, first1, comp));
0380    }
0381 
0382    merge_bufferless(is_range1_A ? first1 : last1, first_irr2, last_irr2, comp);
0383    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, last_irr2, comp));
0384 }
0385 
0386 // Complexity: 2*distance(first, last)+max_collected^2/2
0387 //
0388 // Tries to collect at most n_keys unique elements from [first, last),
0389 // in the begining of the range, and ordered according to comp
0390 // 
0391 // Returns the number of collected keys
0392 template<class RandIt, class Compare, class XBuf>
0393 typename iter_size<RandIt>::type
0394    collect_unique
0395       ( RandIt const first, RandIt const last
0396       , typename iter_size<RandIt>::type const max_collected, Compare comp
0397       , XBuf & xbuf)
0398 {
0399    typedef typename iter_size<RandIt>::type       size_type;
0400    size_type h = 0;
0401 
0402    if(max_collected){
0403       ++h;  // first key is always here
0404       RandIt h0 = first;
0405       RandIt u = first; ++u;
0406       RandIt search_end = u;
0407 
0408       if(xbuf.capacity() >= max_collected){
0409          typename XBuf::iterator const ph0 = xbuf.add(first);
0410          while(u != last && h < max_collected){
0411             typename XBuf::iterator const r = boost::movelib::lower_bound(ph0, xbuf.end(), *u, comp);
0412             //If key not found add it to [h, h+h0)
0413             if(r == xbuf.end() || comp(*u, *r) ){
0414                RandIt const new_h0 = boost::move(search_end, u, h0);
0415                search_end = u;
0416                ++search_end;
0417                ++h;
0418                xbuf.insert(r, u);
0419                h0 = new_h0;
0420             }
0421             ++u;
0422          }
0423          boost::move_backward(first, h0, h0+h);
0424          boost::move(xbuf.data(), xbuf.end(), first);
0425       }
0426       else{
0427          while(u != last && h < max_collected){
0428             RandIt const r = boost::movelib::lower_bound(h0, search_end, *u, comp);
0429             //If key not found add it to [h, h+h0)
0430             if(r == search_end || comp(*u, *r) ){
0431                RandIt const new_h0 = rotate_gcd(h0, search_end, u);
0432                search_end = u;
0433                ++search_end;
0434                ++h;
0435                rotate_gcd(r+(new_h0-h0), u, search_end);
0436                h0 = new_h0;
0437             }
0438             ++u;
0439          }
0440          rotate_gcd(first, h0, h0+h);
0441       }
0442    }
0443    return h;
0444 }
0445 
0446 template<class Unsigned>
0447 Unsigned floor_sqrt(Unsigned n)
0448 {
0449    Unsigned rem = 0, root = 0;
0450    const unsigned bits = sizeof(Unsigned)*CHAR_BIT;
0451 
0452    for (unsigned i = bits / 2; i > 0; i--) {
0453       root = Unsigned(root << 1u);
0454       rem = Unsigned(Unsigned(rem << 2u) | Unsigned(n >> (bits - 2u)));
0455       n = Unsigned(n << 2u);
0456       if (root < rem) {
0457          rem  = Unsigned(rem - Unsigned(root | 1u));
0458          root = Unsigned(root + 2u);
0459       }
0460    }
0461    return Unsigned(root >> 1u);
0462 }
0463 
0464 template<class Unsigned>
0465 Unsigned ceil_sqrt(Unsigned const n)
0466 {
0467    Unsigned r = floor_sqrt(n);
0468    return Unsigned(r + Unsigned((n%r) != 0));
0469 }
0470 
0471 template<class Unsigned>
0472 Unsigned floor_merge_multiple(Unsigned const n, Unsigned &base, Unsigned &pow)
0473 {
0474    Unsigned s = n;
0475    Unsigned p = 0;
0476    while(s > AdaptiveSortInsertionSortThreshold){
0477       s /= 2;
0478       ++p;
0479    }
0480    base = s;
0481    pow = p;
0482    return Unsigned(s << p);
0483 }
0484 
0485 template<class Unsigned>
0486 Unsigned ceil_merge_multiple(Unsigned const n, Unsigned &base, Unsigned &pow)
0487 {
0488    Unsigned fm = floor_merge_multiple(n, base, pow);
0489 
0490    if(fm != n){
0491       if(base < AdaptiveSortInsertionSortThreshold){
0492          ++base;
0493       }
0494       else{
0495          base = AdaptiveSortInsertionSortThreshold/2 + 1;
0496          ++pow;
0497       }
0498    }
0499    return Unsigned(base << pow);
0500 }
0501 
0502 template<class Unsigned>
0503 Unsigned ceil_sqrt_multiple(Unsigned const n, Unsigned *pbase = 0)
0504 {
0505    Unsigned const r = ceil_sqrt(n);
0506    Unsigned pow = 0;
0507    Unsigned base = 0;
0508    Unsigned const res = ceil_merge_multiple(r, base, pow);
0509    if(pbase) *pbase = base;
0510    return res;
0511 }
0512 
0513 struct less
0514 {
0515    template<class T>
0516    bool operator()(const T &l, const T &r)
0517    {  return l < r;  }
0518 };
0519 
0520 ///////////////////////////////////////////////////////////////////////////////
0521 //
0522 //                            MERGE BLOCKS
0523 //
0524 ///////////////////////////////////////////////////////////////////////////////
0525 
0526 //#define ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
0527 
0528 #if defined ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
0529 template<class RandIt, class Compare>
0530 void slow_stable_sort
0531    ( RandIt const first, RandIt const last, Compare comp)
0532 {
0533    boost::movelib::inplace_stable_sort(first, last, comp);
0534 }
0535 
0536 #else //ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
0537 
0538 template<class RandIt, class Compare>
0539 void slow_stable_sort
0540    ( RandIt const first, RandIt const last, Compare comp)
0541 {
0542    typedef typename iter_size<RandIt>::type       size_type;
0543 
0544    size_type L = size_type(last - first);
0545    {  //Use insertion sort to merge first elements
0546       size_type m = 0;
0547       while((L - m) > size_type(AdaptiveSortInsertionSortThreshold)){
0548          insertion_sort(first+m, first+m+size_type(AdaptiveSortInsertionSortThreshold), comp);
0549          m = size_type(m + AdaptiveSortInsertionSortThreshold);
0550       }
0551       insertion_sort(first+m, last, comp);
0552    }
0553 
0554    size_type h = AdaptiveSortInsertionSortThreshold;
0555    for(bool do_merge = L > h; do_merge; h = size_type(h*2)){
0556       do_merge = (L - h) > h;
0557       size_type p0 = 0;
0558       if(do_merge){
0559          size_type const h_2 = size_type(2*h);
0560          while((L-p0) > h_2){
0561             merge_bufferless(first+p0, first+p0+h, first+p0+h_2, comp);
0562             p0 = size_type(p0 + h_2);
0563          }
0564       }
0565       if((L-p0) > h){
0566          merge_bufferless(first+p0, first+p0+h, last, comp);
0567       }
0568    }
0569 }
0570 
0571 #endif   //ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
0572 
0573 //Returns new l_block and updates use_buf
0574 template<class Unsigned>
0575 Unsigned lblock_for_combine
0576    (Unsigned const l_block, Unsigned const n_keys, Unsigned const l_data, bool &use_buf)
0577 {
0578    assert(l_data > 1);
0579 
0580    //We need to guarantee lblock >= l_merged/(n_keys/2) keys for the combination.
0581    //We have at least 4 keys guaranteed (which are the minimum to merge 2 ranges)
0582    //If l_block != 0, then n_keys is already enough to merge all blocks in all
0583    //phases as we've found all needed keys for that buffer and length before.
0584    //If l_block == 0 then see if half keys can be used as buffer and the rest
0585    //as keys guaranteeing that n_keys >= (2*l_merged)/lblock = 
0586    if(!l_block){
0587       //If l_block == 0 then n_keys is power of two
0588       //(guaranteed by build_params(...))
0589       assert(n_keys >= 4);
0590       //assert(0 == (n_keys &(n_keys-1)));
0591 
0592       //See if half keys are at least 4 and if half keys fulfill
0593       Unsigned const new_buf  = n_keys/2;
0594       Unsigned const new_keys = Unsigned(n_keys-new_buf);
0595       use_buf = new_keys >= 4 && new_keys >= l_data/new_buf;
0596       if(use_buf){
0597          return new_buf;
0598       }
0599       else{
0600          return l_data/n_keys;
0601       }
0602    }
0603    else{
0604       use_buf = true;
0605       return l_block;
0606    }
0607 }
0608 
0609 template<class RandIt, class Compare, class XBuf>
0610 void stable_sort( RandIt first, RandIt last, Compare comp, XBuf & xbuf)
0611 {
0612    typedef typename iter_size<RandIt>::type size_type;
0613    size_type const len = size_type(last - first);
0614    size_type const half_len = size_type(len/2u + (len&1u));
0615    if(std::size_t(xbuf.capacity() - xbuf.size()) >= half_len) {
0616       merge_sort(first, last, comp, xbuf.data()+xbuf.size());
0617    }
0618    else{
0619       slow_stable_sort(first, last, comp);
0620    }
0621 }
0622 
0623 template<class RandIt, class Comp, class XBuf>
0624 void unstable_sort( RandIt first, RandIt last
0625                     , Comp comp
0626                     , XBuf & xbuf)
0627 {
0628    heap_sort(first, last, comp);
0629    ::boost::movelib::ignore(xbuf);
0630 }
0631 
0632 template<class RandIt, class Compare, class XBuf>
0633 void stable_merge
0634       ( RandIt first, RandIt const middle, RandIt last
0635       , Compare comp
0636       , XBuf &xbuf)
0637 {
0638    assert(xbuf.empty());
0639    typedef typename iter_size<RandIt>::type   size_type;
0640    size_type const len1  = size_type(middle-first);
0641    size_type const len2  = size_type(last-middle);
0642    size_type const l_min = min_value<size_type>(len1, len2);
0643    if(xbuf.capacity() >= l_min){
0644       buffered_merge(first, middle, last, comp, xbuf);
0645       xbuf.clear();
0646    }
0647    else{
0648       //merge_bufferless(first, middle, last, comp);
0649       merge_adaptive_ONlogN(first, middle, last, comp, xbuf.begin(), xbuf.capacity());
0650    }
0651    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, last, boost::movelib::unantistable(comp)));
0652 }
0653 
0654 template<class RandIt, class Comp, class XBuf>
0655 void initialize_keys( RandIt first, RandIt last
0656                     , Comp comp
0657                     , XBuf & xbuf)
0658 {
0659    unstable_sort(first, last, comp, xbuf);
0660    assert(boost::movelib::is_sorted_and_unique(first, last, comp));
0661 }
0662 
0663 template<class RandIt, class U>
0664 void initialize_keys( RandIt first, RandIt last
0665                     , less
0666                     , U &)
0667 {
0668    typedef typename iterator_traits<RandIt>::value_type value_type;
0669    std::size_t count = std::size_t(last - first);
0670    for(std::size_t i = 0; i != count; ++i){
0671       *first = static_cast<value_type>(i);
0672       ++first;
0673    }
0674 }
0675 
0676 template <class Unsigned>
0677 Unsigned calculate_total_combined(Unsigned const len, Unsigned const l_prev_merged, Unsigned *pl_irreg_combined = 0)
0678 {
0679    typedef Unsigned size_type;
0680 
0681    size_type const l_combined = size_type(2*l_prev_merged);
0682    size_type l_irreg_combined = size_type(len%l_combined);
0683    size_type l_total_combined = len;
0684    if(l_irreg_combined <= l_prev_merged){
0685       l_total_combined = size_type(l_total_combined - l_irreg_combined);
0686       l_irreg_combined = 0;
0687    }
0688    if(pl_irreg_combined)
0689       *pl_irreg_combined = l_irreg_combined;
0690    return l_total_combined;
0691 }
0692 
0693 template<class RandItKeys, class KeyCompare, class SizeType, class XBuf>
0694 void combine_params
0695    ( RandItKeys const keys
0696    , KeyCompare key_comp
0697    , SizeType l_combined
0698    , SizeType const l_prev_merged
0699    , SizeType const l_block
0700    , XBuf & xbuf
0701    //Output
0702    , SizeType &n_block_a
0703    , SizeType &n_block_b
0704    , SizeType &l_irreg1
0705    , SizeType &l_irreg2
0706    //Options
0707    , bool do_initialize_keys = true)
0708 {
0709    typedef SizeType   size_type;
0710 
0711    //Initial parameters for selection sort blocks
0712    l_irreg1 = size_type(l_prev_merged%l_block);
0713    l_irreg2 = size_type((l_combined-l_irreg1)%l_block);
0714    assert(((l_combined-l_irreg1-l_irreg2)%l_block) == 0);
0715    size_type const n_reg_block = size_type((l_combined-l_irreg1-l_irreg2)/l_block);
0716    n_block_a = l_prev_merged/l_block;
0717    n_block_b = size_type(n_reg_block - n_block_a);
0718    assert(n_reg_block>=n_block_a);
0719 
0720    //Key initialization
0721    if (do_initialize_keys) {
0722       initialize_keys(keys, keys + needed_keys_count(n_block_a, n_block_b), key_comp, xbuf);
0723    }
0724 }
0725 
0726 
0727 
0728 //////////////////////////////////
0729 //
0730 //          partial_merge
0731 //
0732 //////////////////////////////////
0733 template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
0734 OutputIt op_partial_merge_impl
0735    (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, OutputIt d_first, Compare comp, Op op)
0736 {
0737    InputIt1 first1(r_first1);
0738    InputIt2 first2(r_first2);
0739    if(first2 != last2 && last1 != first1)
0740    while(1){
0741       if(comp(*first2, *first1)) {
0742          op(first2++, d_first++);
0743          if(first2 == last2){
0744             break;
0745          }
0746       }
0747       else{
0748          op(first1++, d_first++);
0749          if(first1 == last1){
0750             break;
0751          }
0752       }
0753    }
0754    r_first1 = first1;
0755    r_first2 = first2;
0756    return d_first;
0757 }
0758 
0759 template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
0760 OutputIt op_partial_merge
0761    (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, OutputIt d_first, Compare comp, Op op, bool is_stable)
0762 {
0763    return is_stable ? op_partial_merge_impl(r_first1, last1, r_first2, last2, d_first, comp, op)
0764                     : op_partial_merge_impl(r_first1, last1, r_first2, last2, d_first, antistable<Compare>(comp), op);
0765 }
0766 
0767 //////////////////////////////////
0768 //////////////////////////////////
0769 //////////////////////////////////
0770 //
0771 //    op_partial_merge_and_save
0772 //
0773 //////////////////////////////////
0774 //////////////////////////////////
0775 //////////////////////////////////
0776 template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
0777 OutputIt op_partial_merge_and_swap_impl
0778    (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, InputIt2 &r_first_min, OutputIt d_first, Compare comp, Op op)
0779 {
0780    InputIt1 first1(r_first1);
0781    InputIt2 first2(r_first2);
0782    
0783    if(first2 != last2 && last1 != first1) {
0784       InputIt2 first_min(r_first_min);
0785       bool non_empty_ranges = true;
0786       do{
0787          if(comp(*first_min, *first1)) {
0788             op(three_way_t(), first2++, first_min++, d_first++);
0789             non_empty_ranges = first2 != last2;
0790          }
0791          else{
0792             op(first1++, d_first++);
0793             non_empty_ranges = first1 != last1;
0794          }
0795       } while(non_empty_ranges);
0796       r_first_min = first_min;
0797       r_first1 = first1;
0798       r_first2 = first2;
0799    }
0800    return d_first;
0801 }
0802 
0803 template<class RandIt, class InputIt2, class OutputIt, class Compare, class Op>
0804 OutputIt op_partial_merge_and_swap
0805    (RandIt &r_first1, RandIt const last1, InputIt2 &r_first2, InputIt2 const last2, InputIt2 &r_first_min, OutputIt d_first, Compare comp, Op op, bool is_stable)
0806 {
0807    return is_stable ? op_partial_merge_and_swap_impl(r_first1, last1, r_first2, last2, r_first_min, d_first, comp, op)
0808                     : op_partial_merge_and_swap_impl(r_first1, last1, r_first2, last2, r_first_min, d_first, antistable<Compare>(comp), op);
0809 }
0810 
0811 template<class RandIt1, class RandIt2, class RandItB, class Compare, class Op>
0812 RandItB op_buffered_partial_merge_and_swap_to_range1_and_buffer
0813    ( RandIt1 first1, RandIt1 const last1
0814    , RandIt2 &rfirst2, RandIt2 const last2, RandIt2 &rfirst_min
0815    , RandItB &rfirstb, Compare comp, Op op )
0816 {
0817    RandItB firstb = rfirstb;
0818    RandItB lastb  = firstb;
0819    RandIt2 first2 = rfirst2;
0820 
0821    //Move to buffer while merging
0822    //Three way moves need less moves when op is swap_op so use it
0823    //when merging elements from range2 to the destination occupied by range1
0824    if(first1 != last1 && first2 != last2){
0825       RandIt2 first_min = rfirst_min;
0826       op(four_way_t(), first2++, first_min++, first1++, lastb++);
0827 
0828       while(first1 != last1){
0829          if(first2 == last2){
0830             lastb = op(forward_t(), first1, last1, firstb);
0831             break;
0832          }
0833 
0834          if(comp(*first_min, *firstb)){
0835             op( four_way_t(), first2++, first_min++, first1++, lastb++);
0836          }
0837          else{
0838             op(three_way_t(), firstb++, first1++, lastb++);
0839          }
0840       }
0841       rfirst2 = first2;
0842       rfirstb = firstb;
0843       rfirst_min = first_min;
0844    }
0845 
0846    return lastb;
0847 }
0848 
0849 template<class RandIt1, class RandIt2, class RandItB, class Compare, class Op>
0850 RandItB op_buffered_partial_merge_to_range1_and_buffer
0851    ( RandIt1 first1, RandIt1 const last1
0852    , RandIt2 &rfirst2, RandIt2 const last2
0853    , RandItB &rfirstb, Compare comp, Op op )
0854 {
0855    RandItB firstb = rfirstb;
0856    RandItB lastb  = firstb;
0857    RandIt2 first2 = rfirst2;
0858 
0859    //Move to buffer while merging
0860    //Three way moves need less moves when op is swap_op so use it
0861    //when merging elements from range2 to the destination occupied by range1
0862    if(first1 != last1 && first2 != last2){
0863       op(three_way_t(), first2++, first1++, lastb++);
0864 
0865       while(true){
0866          if(first1 == last1){
0867             break;
0868          }
0869          if(first2 == last2){
0870             lastb = op(forward_t(), first1, last1, firstb);
0871             break;
0872          }
0873          if (comp(*first2, *firstb)) {
0874             op(three_way_t(), first2++, first1++, lastb++);
0875          }
0876          else {
0877             op(three_way_t(), firstb++, first1++, lastb++);
0878          }
0879       }
0880       rfirst2 = first2;
0881       rfirstb = firstb;
0882    }
0883 
0884    return lastb;
0885 }
0886 
0887 template<class RandIt, class RandItBuf, class Compare, class Op>
0888 RandIt op_partial_merge_and_save_impl
0889    ( RandIt first1, RandIt const last1, RandIt &rfirst2, RandIt last2, RandIt first_min
0890    , RandItBuf &buf_first1_in_out, RandItBuf &buf_last1_in_out
0891    , Compare comp, Op op
0892    )
0893 {
0894    RandItBuf buf_first1 = buf_first1_in_out;
0895    RandItBuf buf_last1  = buf_last1_in_out;
0896    RandIt first2(rfirst2);
0897 
0898    bool const do_swap = first2 != first_min;
0899    if(buf_first1 == buf_last1){
0900       //Skip any element that does not need to be moved
0901       RandIt new_first1 = skip_until_merge(first1, last1, *first_min, comp);
0902       buf_first1 += (new_first1-first1);
0903       first1 = new_first1;
0904       buf_last1  = do_swap ? op_buffered_partial_merge_and_swap_to_range1_and_buffer(first1, last1, first2, last2, first_min, buf_first1, comp, op)
0905                            : op_buffered_partial_merge_to_range1_and_buffer    (first1, last1, first2, last2, buf_first1, comp, op);
0906       first1 = last1;
0907    }
0908    else{
0909       assert((last1-first1) == (buf_last1 - buf_first1));
0910    }
0911 
0912    //Now merge from buffer
0913    first1 = do_swap ? op_partial_merge_and_swap_impl(buf_first1, buf_last1, first2, last2, first_min, first1, comp, op)
0914                     : op_partial_merge_impl    (buf_first1, buf_last1, first2, last2, first1, comp, op);
0915    buf_first1_in_out = buf_first1;
0916    buf_last1_in_out  = buf_last1;
0917    rfirst2 = first2;
0918    return first1;
0919 }
0920 
0921 template<class RandIt, class RandItBuf, class Compare, class Op>
0922 RandIt op_partial_merge_and_save
0923    ( RandIt first1, RandIt const last1, RandIt &rfirst2, RandIt last2, RandIt first_min
0924    , RandItBuf &buf_first1_in_out
0925    , RandItBuf &buf_last1_in_out
0926    , Compare comp
0927    , Op op
0928    , bool is_stable)
0929 {
0930    return is_stable
0931       ? op_partial_merge_and_save_impl
0932          (first1, last1, rfirst2, last2, first_min, buf_first1_in_out, buf_last1_in_out, comp, op)
0933       : op_partial_merge_and_save_impl
0934          (first1, last1, rfirst2, last2, first_min, buf_first1_in_out, buf_last1_in_out, antistable<Compare>(comp), op)
0935       ;
0936 }
0937 
0938 //////////////////////////////////
0939 //////////////////////////////////
0940 //////////////////////////////////
0941 //
0942 //    op_merge_blocks_with_irreg
0943 //
0944 //////////////////////////////////
0945 //////////////////////////////////
0946 //////////////////////////////////
0947 
0948 template<class RandItKeys, class KeyCompare, class RandIt, class RandIt2, class OutputIt, class Compare, class Op>
0949 OutputIt op_merge_blocks_with_irreg
0950    ( RandItKeys key_first
0951    , RandItKeys key_mid
0952    , KeyCompare key_comp
0953    , RandIt first_reg
0954    , RandIt2 &first_irr
0955    , RandIt2 const last_irr
0956    , OutputIt dest
0957    , typename iter_size<RandIt>::type const l_block
0958    , typename iter_size<RandIt>::type n_block_left
0959    , typename iter_size<RandIt>::type min_check
0960    , typename iter_size<RandIt>::type max_check
0961    , Compare comp, bool const is_stable, Op op)
0962 {
0963    typedef typename iter_size<RandIt>::type size_type;
0964 
0965    for(; n_block_left; --n_block_left){
0966       size_type next_key_idx = find_next_block(key_first, key_comp, first_reg, l_block, min_check, max_check, comp);  
0967       max_check = min_value(max_value(max_check, size_type(next_key_idx+2u)), n_block_left);
0968       RandIt const last_reg  = first_reg + l_block;
0969       RandIt first_min = first_reg + size_type(next_key_idx*l_block);
0970       RandIt const last_min  = first_min + l_block;
0971       boost::movelib::ignore(last_min);
0972 
0973       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_reg, last_reg, comp));
0974       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!next_key_idx || boost::movelib::is_sorted(first_min, last_min, comp));
0975       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((!next_key_idx || !comp(*first_reg, *first_min )));
0976 
0977       OutputIt orig_dest = dest;
0978       boost::movelib::ignore(orig_dest);
0979       dest = next_key_idx ? op_partial_merge_and_swap(first_irr, last_irr, first_reg, last_reg, first_min, dest, comp, op, is_stable)
0980                           : op_partial_merge         (first_irr, last_irr, first_reg, last_reg, dest, comp, op, is_stable);
0981       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(orig_dest, dest, comp));
0982 
0983       if(first_reg == dest){
0984          dest = next_key_idx ? ::boost::adl_move_swap_ranges(first_min, last_min, first_reg)
0985                              : last_reg;
0986       }
0987       else{
0988          dest = next_key_idx ? op(three_way_forward_t(), first_reg, last_reg, first_min, dest)
0989                              : op(forward_t(), first_reg, last_reg, dest);
0990       }
0991 
0992       RandItKeys const key_next(key_first + next_key_idx);
0993       swap_and_update_key(key_next, key_first, key_mid, last_reg, last_reg, first_min);
0994 
0995       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(orig_dest, dest, comp));
0996       first_reg = last_reg;
0997       ++key_first;
0998       min_check = size_type(min_check - (min_check != 0));
0999       max_check = size_type(max_check - (max_check != 0));
1000    }
1001    return dest;
1002 }
1003 
1004 //////////////////////////////////
1005 //////////////////////////////////
1006 //////////////////////////////////
1007 //
1008 //    op_merge_blocks_left/right
1009 //
1010 //////////////////////////////////
1011 //////////////////////////////////
1012 //////////////////////////////////
1013 
1014 template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class Op>
1015 void op_merge_blocks_left
1016    ( RandItKeys const key_first
1017    , KeyCompare key_comp
1018    , RandIt const first
1019    , typename iter_size<RandIt>::type const l_block
1020    , typename iter_size<RandIt>::type const l_irreg1
1021    , typename iter_size<RandIt>::type const n_block_a
1022    , typename iter_size<RandIt>::type const n_block_b
1023    , typename iter_size<RandIt>::type const l_irreg2
1024    , Compare comp, Op op)
1025 {
1026    typedef typename iter_size<RandIt>::type       size_type;
1027 
1028    size_type const key_count = needed_keys_count(n_block_a, n_block_b);
1029    boost::movelib::ignore(key_count);
1030 
1031 //   assert(n_block_a || n_block_b);
1032    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
1033    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + key_count, key_comp, key_first[n_block_a]));
1034 
1035    size_type n_block_b_left = n_block_b;
1036    size_type n_block_a_left = n_block_a;
1037    size_type n_block_left = size_type(n_block_b + n_block_a);
1038    RandItKeys key_mid(key_first + n_block_a);
1039 
1040    RandIt buffer = first - l_block;
1041    RandIt first1 = first;
1042    RandIt last1  = first1 + l_irreg1;
1043    RandIt first2 = last1;
1044    RandIt const irreg2 = first2 + size_type(n_block_left*l_block);
1045    bool is_range1_A = true;
1046 
1047    RandItKeys key_range2(key_first);
1048 
1049    ////////////////////////////////////////////////////////////////////////////
1050    //Process all regular blocks before the irregular B block
1051    ////////////////////////////////////////////////////////////////////////////
1052    size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
1053    size_type max_check = min_value<size_type>(size_type(min_check+1u), n_block_left);
1054    for (; n_block_left; --n_block_left) {
1055       size_type const next_key_idx = find_next_block(key_range2, key_comp, first2, l_block, min_check, max_check, comp);
1056       max_check = min_value<size_type>(max_value<size_type>(max_check, size_type(next_key_idx+2u)), n_block_left);
1057       RandIt const first_min = first2 + size_type(next_key_idx*l_block);
1058       RandIt const last_min  = first_min + l_block;
1059 
1060       boost::movelib::ignore(last_min);
1061       RandIt const last2  = first2 + l_block;
1062 
1063       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first1, last1, comp));
1064       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first2, last2, comp));
1065       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_left || boost::movelib::is_sorted(first_min, last_min, comp));
1066 
1067       //Check if irregular b block should go here.
1068       //If so, break to the special code handling the irregular block
1069       if (!n_block_b_left &&
1070             ( (l_irreg2 && comp(*irreg2, *first_min)) || (!l_irreg2 && is_range1_A)) ){
1071          break;
1072       }
1073 
1074       RandItKeys const key_next(key_range2 + next_key_idx);
1075       bool const is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
1076 
1077       bool const is_buffer_middle = last1 == buffer;
1078       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT( ( is_buffer_middle && size_type(first2-buffer) == l_block && buffer == last1) ||
1079                                           (!is_buffer_middle && size_type(first1-buffer) == l_block && first2 == last1));
1080 
1081       if(is_range1_A == is_range2_A){
1082          assert((first1 == last1) || !comp(*first_min, last1[typename iterator_traits<RandIt>::difference_type(-1)]));
1083          if(!is_buffer_middle){
1084             buffer = op(forward_t(), first1, last1, buffer);
1085          }
1086          swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1087          first1 = first2;
1088          last1  = last2;
1089       }
1090       else {
1091          RandIt unmerged;
1092          RandIt buf_beg;
1093          RandIt buf_end;
1094          if(is_buffer_middle){
1095             buf_end = buf_beg = first2 - (last1-first1);
1096             unmerged = op_partial_merge_and_save( first1, last1, first2, last2, first_min
1097                                                 , buf_beg, buf_end, comp, op, is_range1_A);
1098          }  
1099          else{
1100             buf_beg = first1;
1101             buf_end = last1;
1102             unmerged = op_partial_merge_and_save
1103                (buffer, buffer+(last1-first1), first2, last2, first_min, buf_beg, buf_end, comp, op, is_range1_A);
1104          }
1105 
1106          boost::movelib::ignore(unmerged);
1107          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first-l_block, unmerged, comp));
1108 
1109          swap_and_update_key( key_next, key_range2, key_mid, first2, last2
1110                             , last_min - size_type(last2 - first2));
1111 
1112          if(buf_beg != buf_end){  //range2 exhausted: is_buffer_middle for the next iteration
1113             first1 = buf_beg;
1114             last1  = buf_end;
1115             BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(buf_end == (last2-l_block));
1116             buffer = last1;
1117          }
1118          else{ //range1 exhausted: !is_buffer_middle for the next iteration
1119             first1 = first2;
1120             last1  = last2;
1121             buffer = first2 - l_block;
1122             is_range1_A = is_range2_A;
1123          }
1124       }
1125       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT( (is_range2_A && n_block_a_left) || (!is_range2_A && n_block_b_left));
1126       is_range2_A ? --n_block_a_left : --n_block_b_left;
1127       first2 = last2;
1128       //Update context
1129       ++key_range2;
1130       min_check = size_type(min_check - (min_check != 0));
1131       max_check = size_type(max_check - (max_check != 0));
1132    }
1133 
1134    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_range2 + n_block_left, key_comp, *key_mid));
1135    assert(!n_block_b_left);
1136 
1137    ////////////////////////////////////////////////////////////////////////////
1138    //Process remaining range 1 left before the irregular B block
1139    ////////////////////////////////////////////////////////////////////////////
1140    bool const is_buffer_middle = last1 == buffer;
1141    RandIt first_irr2 = irreg2;
1142    RandIt const last_irr2  = first_irr2 + l_irreg2;
1143    if(l_irreg2 && is_range1_A){
1144       if(is_buffer_middle){
1145          first1 = skip_until_merge(first1, last1, *first_irr2, comp);
1146          //Even if we copy backward, no overlapping occurs so use forward copy
1147          //that can be faster specially with trivial types
1148          RandIt const new_first1 = first2 - (last1 - first1);
1149          op(forward_t(), first1, last1, new_first1);
1150          first1 = new_first1;
1151          last1 = first2;
1152          buffer = first1 - l_block;
1153       }
1154       buffer = op_partial_merge_impl(first1, last1, first_irr2, last_irr2, buffer, comp, op);
1155       buffer = op(forward_t(), first1, last1, buffer);
1156    }
1157    else if(!is_buffer_middle){
1158       buffer = op(forward_t(), first1, last1, buffer);
1159    }
1160    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first-l_block, buffer, comp));
1161 
1162    ////////////////////////////////////////////////////////////////////////////
1163    //Process irregular B block and remaining A blocks
1164    ////////////////////////////////////////////////////////////////////////////
1165    buffer = op_merge_blocks_with_irreg
1166       ( key_range2, key_mid, key_comp, first2, first_irr2, last_irr2
1167       , buffer, l_block, n_block_left, min_check, max_check, comp, false, op);
1168    buffer = op(forward_t(), first_irr2, last_irr2, buffer);
1169    boost::movelib::ignore(buffer);
1170    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first-l_block, buffer, comp));
1171 }
1172 
1173 // first - first element to merge.
1174 // first[-l_block, 0) - buffer (if use_buf == true)
1175 // l_block - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded
1176 // keys - sequence of keys, in same order as blocks. key<midkey means stream A
1177 // n_bef_irreg2/n_aft_irreg2 are regular blocks
1178 // l_irreg2 is a irregular block, that is to be combined after n_bef_irreg2 blocks and before n_aft_irreg2 blocks
1179 // If l_irreg2==0 then n_aft_irreg2==0 (no irregular blocks).
1180 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
1181 void merge_blocks_left
1182    ( RandItKeys const key_first
1183    , KeyCompare key_comp
1184    , RandIt const first
1185    , typename iter_size<RandIt>::type const l_block
1186    , typename iter_size<RandIt>::type const l_irreg1
1187    , typename iter_size<RandIt>::type const n_block_a
1188    , typename iter_size<RandIt>::type const n_block_b
1189    , typename iter_size<RandIt>::type const l_irreg2
1190    , Compare comp
1191    , bool const xbuf_used)
1192 {
1193    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + needed_keys_count(n_block_a, n_block_b), key_comp, key_first[n_block_a]));
1194    if(xbuf_used){
1195       op_merge_blocks_left
1196          (key_first, key_comp, first, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, move_op());
1197    }
1198    else{
1199       op_merge_blocks_left
1200          (key_first, key_comp, first, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, swap_op());
1201    }
1202 }
1203 
1204 // first - first element to merge.
1205 // [first+l_block*(n_bef_irreg2+n_aft_irreg2)+l_irreg2, first+l_block*(n_bef_irreg2+n_aft_irreg2+1)+l_irreg2) - buffer
1206 // l_block - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded
1207 // keys - sequence of keys, in same order as blocks. key<midkey means stream A
1208 // n_bef_irreg2/n_aft_irreg2 are regular blocks
1209 // l_irreg2 is a irregular block, that is to be combined after n_bef_irreg2 blocks and before n_aft_irreg2 blocks
1210 // If l_irreg2==0 then n_aft_irreg2==0 (no irregular blocks).
1211 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
1212 void merge_blocks_right
1213    ( RandItKeys const key_first
1214    , KeyCompare key_comp
1215    , RandIt const first
1216    , typename iter_size<RandIt>::type const l_block
1217    , typename iter_size<RandIt>::type const n_block_a
1218    , typename iter_size<RandIt>::type const n_block_b
1219    , typename iter_size<RandIt>::type const l_irreg2
1220    , Compare comp
1221    , bool const xbuf_used)
1222 {
1223    typedef typename iter_size<RandIt>::type size_type;
1224    merge_blocks_left
1225       ( (make_reverse_iterator)(key_first + needed_keys_count(n_block_a, n_block_b))
1226       , inverse<KeyCompare>(key_comp)
1227       , (make_reverse_iterator)(first + size_type((n_block_a+n_block_b)*l_block+l_irreg2))
1228       , l_block
1229       , l_irreg2
1230       , n_block_b
1231       , n_block_a
1232       , 0
1233       , inverse<Compare>(comp), xbuf_used);
1234 }
1235 
1236 //////////////////////////////////
1237 //////////////////////////////////
1238 //////////////////////////////////
1239 //
1240 //    op_merge_blocks_with_buf
1241 //
1242 //////////////////////////////////
1243 //////////////////////////////////
1244 //////////////////////////////////
1245 template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class Op, class RandItBuf>
1246 void op_merge_blocks_with_buf
1247    ( RandItKeys key_first
1248    , KeyCompare key_comp
1249    , RandIt const first
1250    , typename iter_size<RandIt>::type const l_block
1251    , typename iter_size<RandIt>::type const l_irreg1
1252    , typename iter_size<RandIt>::type const n_block_a
1253    , typename iter_size<RandIt>::type const n_block_b
1254    , typename iter_size<RandIt>::type const l_irreg2
1255    , Compare comp
1256    , Op op
1257    , RandItBuf const buf_first)
1258 {
1259    typedef typename iter_size<RandIt>::type size_type;
1260    size_type const key_count = needed_keys_count(n_block_a, n_block_b);
1261    boost::movelib::ignore(key_count);
1262    //assert(n_block_a || n_block_b);
1263    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
1264    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_b || n_block_a == count_if_with(key_first, key_first + key_count, key_comp, key_first[n_block_a]));
1265 
1266    size_type n_block_b_left = n_block_b;
1267    size_type n_block_a_left = n_block_a;
1268    size_type n_block_left = size_type(n_block_b + n_block_a);
1269    RandItKeys key_mid(key_first + n_block_a);
1270 
1271    RandItBuf buffer = buf_first;
1272    RandItBuf buffer_end = buffer;
1273    RandIt first1 = first;
1274    RandIt last1  = first1 + l_irreg1;
1275    RandIt first2 = last1;
1276    RandIt const first_irr2 = first2 + size_type(n_block_left*l_block);
1277    bool is_range1_A = true;
1278    const size_type len = size_type(l_block * n_block_a + l_block * n_block_b + l_irreg1 + l_irreg2);
1279    boost::movelib::ignore(len);
1280 
1281    RandItKeys key_range2(key_first);
1282 
1283    ////////////////////////////////////////////////////////////////////////////
1284    //Process all regular blocks before the irregular B block
1285    ////////////////////////////////////////////////////////////////////////////
1286    size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
1287    size_type max_check = min_value(size_type(min_check+1), n_block_left);
1288    for (; n_block_left; --n_block_left) {
1289       size_type const next_key_idx = find_next_block(key_range2, key_comp, first2, l_block, min_check, max_check, comp);
1290       max_check = min_value(max_value(max_check, size_type(next_key_idx+2)), n_block_left);
1291       RandIt       first_min = first2 + size_type(next_key_idx*l_block);
1292       RandIt const last_min  = first_min + l_block;
1293       boost::movelib::ignore(last_min);
1294       RandIt const last2  = first2 + l_block;
1295 
1296       bool const buffer_empty = buffer == buffer_end;
1297       boost::movelib::ignore(buffer_empty);
1298       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(buffer_empty ? boost::movelib::is_sorted(first1, last1, comp) : boost::movelib::is_sorted(buffer, buffer_end, comp));
1299       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first2, last2, comp));
1300       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_left || boost::movelib::is_sorted(first_min, last_min, comp));
1301 
1302       //Check if irregular b block should go here.
1303       //If so, break to the special code handling the irregular block
1304       if (!n_block_b_left &&
1305             ( (l_irreg2 && comp(*first_irr2, *first_min)) || (!l_irreg2 && is_range1_A)) ){
1306          break;
1307       }
1308 
1309       RandItKeys const key_next(key_range2 + next_key_idx);
1310       bool const is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
1311 
1312       if(is_range1_A == is_range2_A){
1313          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((first1 == last1) || (buffer_empty ? !comp(*first_min, last1[-1]) : !comp(*first_min, buffer_end[-1])));
1314          //If buffered, put those elements in place
1315          RandIt res = op(forward_t(), buffer, buffer_end, first1);
1316          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_fwd: ", len);
1317          buffer    = buffer_end = buf_first;
1318          assert(buffer_empty || res == last1);
1319          boost::movelib::ignore(res);
1320          //swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1321          buffer_end = buffer_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min, buffer = buf_first, op);
1322          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_swp: ", len);
1323          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first2, last2, comp));
1324          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_min, last_min, comp));
1325          first1 = first2;
1326          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, first1, comp));
1327       }
1328       else {
1329          RandIt const unmerged = op_partial_merge_and_save(first1, last1, first2, last2, first_min, buffer, buffer_end, comp, op, is_range1_A);
1330          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_mrs: ", len);
1331          bool const is_range_1_empty = buffer == buffer_end;
1332          assert(is_range_1_empty || (buffer_end-buffer) == (last1+l_block-unmerged));
1333          if(is_range_1_empty){
1334             buffer    = buffer_end = buf_first;
1335             first_min = last_min - (last2 - first2);
1336             //swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1337             buffer_end = buffer_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min, buf_first, op);
1338          }
1339          else{
1340             first_min = last_min;
1341             //swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1342             update_key(key_next, key_range2, key_mid);
1343          }
1344          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!is_range_1_empty || (last_min-first_min) == (last2-unmerged));
1345          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_swp: ", len);
1346          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_min, last_min, comp));
1347          is_range1_A ^= is_range_1_empty;
1348          first1 = unmerged;
1349          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, unmerged, comp));
1350       }
1351       assert( (is_range2_A && n_block_a_left) || (!is_range2_A && n_block_b_left));
1352       is_range2_A ? --n_block_a_left : --n_block_b_left;
1353       last1 += l_block;
1354       first2 = last2;
1355       //Update context
1356       ++key_range2;
1357       min_check = size_type(min_check - (min_check != 0));
1358       max_check = size_type(max_check - (max_check != 0));
1359    }
1360    RandIt res = op(forward_t(), buffer, buffer_end, first1);
1361    boost::movelib::ignore(res);
1362    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, res, comp));
1363    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_fwd: ", len);
1364 
1365    ////////////////////////////////////////////////////////////////////////////
1366    //Process irregular B block and remaining A blocks
1367    ////////////////////////////////////////////////////////////////////////////
1368    RandIt const last_irr2 = first_irr2 + l_irreg2;
1369    op(forward_t(), first_irr2, first_irr2+l_irreg2, buf_first);
1370    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_fwir:", len);
1371    buffer = buf_first;
1372    buffer_end = buffer+l_irreg2;
1373 
1374    reverse_iterator<RandItBuf> rbuf_beg(buffer_end);
1375    RandIt dest = op_merge_blocks_with_irreg
1376       ((make_reverse_iterator)(key_first + n_block_b + n_block_a), (make_reverse_iterator)(key_mid), inverse<KeyCompare>(key_comp)
1377       , (make_reverse_iterator)(first_irr2), rbuf_beg, (make_reverse_iterator)(buffer), (make_reverse_iterator)(last_irr2)
1378       , l_block, n_block_left, 0, n_block_left
1379       , inverse<Compare>(comp), true, op).base();
1380    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(dest, last_irr2, comp));
1381    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_irg: ", len);
1382 
1383    buffer_end = rbuf_beg.base();
1384    assert((dest-last1) == (buffer_end-buffer));
1385    op_merge_with_left_placed(is_range1_A ? first1 : last1, last1, dest, buffer, buffer_end, comp, op);
1386    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_with_left_plc:", len);
1387    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, last_irr2, comp));
1388 }
1389 
1390 //////////////////////////////////
1391 //////////////////////////////////
1392 //////////////////////////////////
1393 //
1394 //  op_insertion_sort_step_left/right
1395 //
1396 //////////////////////////////////
1397 //////////////////////////////////
1398 //////////////////////////////////
1399 
1400 template<class RandIt, class Compare, class Op>
1401 typename iter_size<RandIt>::type
1402    op_insertion_sort_step_left
1403       ( RandIt const first
1404       , typename iter_size<RandIt>::type const length
1405       , typename iter_size<RandIt>::type const step
1406       , Compare comp, Op op)
1407 {
1408    typedef typename iter_size<RandIt>::type       size_type;
1409 
1410    size_type const s = min_value<size_type>(step, AdaptiveSortInsertionSortThreshold);
1411    size_type m = 0;
1412 
1413    while(size_type(length - m) > s){
1414       insertion_sort_op(first+m, first+m+s, first+m-s, comp, op);
1415       m = size_type(m + s);
1416    }
1417    insertion_sort_op(first+m, first+length, first+m-s, comp, op);
1418    return s;
1419 }
1420 
1421 template<class RandIt, class Compare, class Op>
1422 void op_merge_right_step_once
1423       ( RandIt first_block
1424       , typename iter_size<RandIt>::type const elements_in_blocks
1425       , typename iter_size<RandIt>::type const l_build_buf
1426       , Compare comp
1427       , Op op)
1428 {
1429    typedef typename iter_size<RandIt>::type size_type;
1430    size_type restk = size_type(elements_in_blocks%(2*l_build_buf));
1431    size_type p = size_type(elements_in_blocks - restk);
1432    assert(0 == (p%(2*l_build_buf)));
1433 
1434    if(restk <= l_build_buf){
1435       op(backward_t(),first_block+p, first_block+p+restk, first_block+p+restk+l_build_buf);
1436    }
1437    else{
1438       op_merge_right(first_block+p, first_block+p+l_build_buf, first_block+p+restk, first_block+p+restk+l_build_buf, comp, op);
1439    }
1440    while(p>0){
1441       p = size_type(p - 2u*l_build_buf);
1442       op_merge_right( first_block+p, first_block+size_type(p+l_build_buf)
1443                     , first_block+size_type(p+2*l_build_buf)
1444                     , first_block+size_type(p+3*l_build_buf), comp, op);
1445    }
1446 }
1447 
1448 
1449 //////////////////////////////////
1450 //////////////////////////////////
1451 //////////////////////////////////
1452 //
1453 //    insertion_sort_step
1454 //
1455 //////////////////////////////////
1456 //////////////////////////////////
1457 //////////////////////////////////
1458 template<class RandIt, class Compare>
1459 typename iter_size<RandIt>::type
1460    insertion_sort_step
1461       ( RandIt const first
1462       , typename iter_size<RandIt>::type const length
1463       , typename iter_size<RandIt>::type const step
1464       , Compare comp)
1465 {
1466    typedef typename iter_size<RandIt>::type size_type;
1467    size_type const s = min_value<size_type>(step, AdaptiveSortInsertionSortThreshold);
1468    size_type m = 0;
1469 
1470    while((length - m) > s){
1471       insertion_sort(first+m, first+m+s, comp);
1472       m = size_type(m + s);
1473    }
1474    insertion_sort(first+m, first+length, comp);
1475    return s;
1476 }
1477 
1478 //////////////////////////////////
1479 //////////////////////////////////
1480 //////////////////////////////////
1481 //
1482 //    op_merge_left_step_multiple
1483 //
1484 //////////////////////////////////
1485 //////////////////////////////////
1486 //////////////////////////////////
1487 template<class RandIt, class Compare, class Op>
1488 typename iter_size<RandIt>::type  
1489    op_merge_left_step_multiple
1490       ( RandIt first_block
1491       , typename iter_size<RandIt>::type const elements_in_blocks
1492       , typename iter_size<RandIt>::type l_merged
1493       , typename iter_size<RandIt>::type const l_build_buf
1494       , typename iter_size<RandIt>::type l_left_space
1495       , Compare comp
1496       , Op op)
1497 {
1498    typedef typename iter_size<RandIt>::type size_type;
1499    for(; l_merged < l_build_buf && l_left_space >= l_merged; l_merged = size_type(l_merged*2u)){
1500       size_type p0=0;
1501       RandIt pos = first_block;
1502       while((elements_in_blocks - p0) > 2*l_merged) {
1503          op_merge_left(pos-l_merged, pos, pos+l_merged, pos+size_type(2*l_merged), comp, op);
1504          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(pos-l_merged, pos+l_merged, comp));
1505          p0 = size_type(p0 + 2u*l_merged);
1506          pos = first_block+p0;
1507       }
1508       if((elements_in_blocks-p0) > l_merged) {
1509          op_merge_left(pos-l_merged, pos, pos+l_merged, first_block+elements_in_blocks, comp, op);
1510          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT
1511             (boost::movelib::is_sorted
1512                (pos-l_merged, pos+size_type((first_block+elements_in_blocks-pos))-l_merged, comp));
1513       }
1514       else {
1515          op(forward_t(), pos, first_block+elements_in_blocks, pos-l_merged);
1516          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT
1517             (boost::movelib::is_sorted
1518                (pos-l_merged, first_block+size_type(elements_in_blocks-l_merged), comp));
1519       }
1520       first_block  -= l_merged;
1521       l_left_space = size_type(l_left_space - l_merged);
1522    }
1523    return l_merged;
1524 }
1525 
1526 
1527 }  //namespace detail_adaptive {
1528 }  //namespace movelib {
1529 }  //namespace boost {
1530 
1531 #if defined(BOOST_CLANG) || (defined(BOOST_GCC) && (BOOST_GCC >= 40600))
1532 #pragma GCC diagnostic pop
1533 #endif
1534 
1535 #include <boost/move/detail/config_end.hpp>
1536 
1537 #endif   //#define BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP