1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) Copyright Ion Gaztanaga 2015-2016.
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // See http://www.boost.org/libs/move for documentation.
9 //
10 //////////////////////////////////////////////////////////////////////////////
11 //
12 // Stable sorting that works in O(N*log(N)) worst time
13 // and uses O(1) extra memory
14 //
15 //////////////////////////////////////////////////////////////////////////////
16 //
17 // The main idea of the adaptive_sort algorithm was developed by Andrey Astrelin
18 // and explained in the article from the russian collaborative blog
19 // Habrahabr (http://habrahabr.ru/post/205290/). The algorithm is based on
20 // ideas from B-C. Huang and M. A. Langston explained in their article
21 // "Fast Stable Merging and Sorting in Constant Extra Space (1989-1992)"
22 // (http://comjnl.oxfordjournals.org/content/35/6/643.full.pdf).
23 //
24 // This implementation by Ion Gaztanaga uses previous ideas with additional changes:
25 //
26 // - Use of GCD-based rotation.
27 // - Non power of two buffer-sizes.
28 // - Tries to find sqrt(len)*2 unique keys, so that the merge sort
29 //   phase can form up to sqrt(len)*4 segments if enough keys are found.
30 // - The merge-sort phase can take advantage of external memory to
31 //   save some additional combination steps.
32 // - Combination phase: Blocks are selection sorted and merged in parallel.
33 // - The combination phase is performed alternating merge to left and merge
34 //   to right phases minimizing swaps due to internal buffer repositioning.
35 // - When merging blocks special optimizations are made to avoid moving some
36 //   elements twice.
37 //
38 // The adaptive_merge algorithm was developed by Ion Gaztanaga reusing some parts
39 // from the sorting algorithm and implementing an additional block merge algorithm
40 // without moving elements to left or right.
41 //////////////////////////////////////////////////////////////////////////////
42 #ifndef BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
43 #define BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
44 
45 #include <boost/move/detail/config_begin.hpp>
46 #include <boost/move/detail/reverse_iterator.hpp>
47 #include <boost/move/algo/move.hpp>
48 #include <boost/move/algo/detail/merge.hpp>
49 #include <boost/move/adl_move_swap.hpp>
50 #include <boost/move/algo/detail/insertion_sort.hpp>
51 #include <boost/move/algo/detail/merge_sort.hpp>
52 #include <boost/move/algo/detail/heap_sort.hpp>
53 #include <boost/move/algo/detail/merge.hpp>
54 #include <boost/move/algo/detail/is_sorted.hpp>
55 #include <boost/core/ignore_unused.hpp>
56 #include <boost/assert.hpp>
57 #include <boost/cstdint.hpp>
58 
59 #ifndef BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL
60    #define BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL 1
61 #endif
62 
63 #ifdef BOOST_MOVE_ADAPTIVE_SORT_STATS
64    #if BOOST_MOVE_ADAPTIVE_SORT_STATS_LEVEL == 2
65       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L) \
66          print_stats(STR, L)\
67       //
68 
69       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L) \
70          print_stats(STR, L)\
71       //
72    #else
73       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L) \
74          print_stats(STR, L)\
75       //
76 
77       #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L)
78    #endif
79 #else
80    #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(STR, L)
81    #define BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(STR, L)
82 #endif
83 
84 #ifdef BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS
85    #define BOOST_MOVE_ADAPTIVE_SORT_INVARIANT  BOOST_ASSERT
86 #else
87    #define BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(L)
88 #endif
89 
90 namespace boost {
91 namespace movelib {
92 
93 #if defined(BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS)
94 
is_sorted(::order_perf_type * first,::order_perf_type * last,::order_type_less)95 bool is_sorted(::order_perf_type *first, ::order_perf_type *last, ::order_type_less)
96 {
97    if (first != last) {
98       const order_perf_type *next = first, *cur(first);
99       while (++next != last) {
100          if (!(cur->key < next->key || (cur->key == next->key && cur->val < next->val)))
101             return false;
102          cur = next;
103       }
104    }
105    return true;
106 }
107 
108 #endif   //BOOST_MOVE_ADAPTIVE_SORT_INVARIANTS
109 
110 namespace detail_adaptive {
111 
112 static const std::size_t AdaptiveSortInsertionSortThreshold = 16;
113 //static const std::size_t AdaptiveSortInsertionSortThreshold = 4;
114 BOOST_STATIC_ASSERT((AdaptiveSortInsertionSortThreshold&(AdaptiveSortInsertionSortThreshold-1)) == 0);
115 
116 #if defined BOOST_HAS_INTPTR_T
117    typedef ::boost::uintptr_t uintptr_t;
118 #else
119    typedef std::size_t uintptr_t;
120 #endif
121 
122 template<class T>
min_value(const T & a,const T & b)123 const T &min_value(const T &a, const T &b)
124 {
125    return a < b ? a : b;
126 }
127 
128 template<class T>
max_value(const T & a,const T & b)129 const T &max_value(const T &a, const T &b)
130 {
131    return a > b ? a : b;
132 }
133 
134 template<class ForwardIt, class Pred, class V>
135 typename iterator_traits<ForwardIt>::size_type
count_if_with(ForwardIt first,ForwardIt last,Pred pred,const V & v)136    count_if_with(ForwardIt first, ForwardIt last, Pred pred, const V &v)
137 {
138    typedef typename iterator_traits<ForwardIt>::size_type size_type;
139    size_type count = 0;
140    while(first != last) {
141       count += static_cast<size_type>(0 != pred(*first, v));
142       ++first;
143    }
144    return count;
145 }
146 
147 
148 template<class RandIt, class Compare>
skip_until_merge(RandIt first1,RandIt const last1,const typename iterator_traits<RandIt>::value_type & next_key,Compare comp)149 RandIt skip_until_merge
150    ( RandIt first1, RandIt const last1
151    , const typename iterator_traits<RandIt>::value_type &next_key, Compare comp)
152 {
153    while(first1 != last1 && !comp(next_key, *first1)){
154       ++first1;
155    }
156    return first1;
157 }
158 
159 
160 template<class RandItKeys, class RandIt>
swap_and_update_key(RandItKeys const key_next,RandItKeys const key_range2,RandItKeys & key_mid,RandIt const begin,RandIt const end,RandIt const with)161 void swap_and_update_key
162    ( RandItKeys const key_next
163    , RandItKeys const key_range2
164    , RandItKeys &key_mid
165    , RandIt const begin
166    , RandIt const end
167    , RandIt const with)
168 {
169    if(begin != with){
170       ::boost::adl_move_swap_ranges(begin, end, with);
171       ::boost::adl_move_swap(*key_next, *key_range2);
172       if(key_next == key_mid){
173          key_mid = key_range2;
174       }
175       else if(key_mid == key_range2){
176          key_mid = key_next;
177       }
178    }
179 }
180 
181 template<class RandItKeys>
update_key(RandItKeys const key_next,RandItKeys const key_range2,RandItKeys & key_mid)182 void update_key
183 (RandItKeys const key_next
184    , RandItKeys const key_range2
185    , RandItKeys &key_mid)
186 {
187    if (key_next != key_range2) {
188       ::boost::adl_move_swap(*key_next, *key_range2);
189       if (key_next == key_mid) {
190          key_mid = key_range2;
191       }
192       else if (key_mid == key_range2) {
193          key_mid = key_next;
194       }
195    }
196 }
197 
198 template<class RandItKeys, class RandIt, class RandIt2, class Op>
buffer_and_update_key(RandItKeys const key_next,RandItKeys const key_range2,RandItKeys & key_mid,RandIt begin,RandIt end,RandIt with,RandIt2 buffer,Op op)199 RandIt2 buffer_and_update_key
200 (RandItKeys const key_next
201    , RandItKeys const key_range2
202    , RandItKeys &key_mid
203    , RandIt begin
204    , RandIt end
205    , RandIt with
206    , RandIt2 buffer
207    , Op op)
208 {
209    if (begin != with) {
210       while(begin != end) {
211          op(three_way_t(), begin++, with++, buffer++);
212       }
213       ::boost::adl_move_swap(*key_next, *key_range2);
214       if (key_next == key_mid) {
215          key_mid = key_range2;
216       }
217       else if (key_mid == key_range2) {
218          key_mid = key_next;
219       }
220    }
221    return buffer;
222 }
223 
224 ///////////////////////////////////////////////////////////////////////////////
225 //
226 //                         MERGE BUFFERLESS
227 //
228 ///////////////////////////////////////////////////////////////////////////////
229 
230 // [first1, last1) merge [last1,last2) -> [first1,last2)
231 template<class RandIt, class Compare>
partial_merge_bufferless_impl(RandIt first1,RandIt last1,RandIt const last2,bool * const pis_range1_A,Compare comp)232 RandIt partial_merge_bufferless_impl
233    (RandIt first1, RandIt last1, RandIt const last2, bool *const pis_range1_A, Compare comp)
234 {
235    if(last1 == last2){
236       return first1;
237    }
238    bool const is_range1_A = *pis_range1_A;
239    if(first1 != last1 && comp(*last1, last1[-1])){
240       do{
241          RandIt const old_last1 = last1;
242          last1  = boost::movelib::lower_bound(last1, last2, *first1, comp);
243          first1 = rotate_gcd(first1, old_last1, last1);//old_last1 == last1 supported
244          if(last1 == last2){
245             return first1;
246          }
247          do{
248             ++first1;
249          } while(last1 != first1 && !comp(*last1, *first1) );
250       } while(first1 != last1);
251    }
252    *pis_range1_A = !is_range1_A;
253    return last1;
254 }
255 
256 // [first1, last1) merge [last1,last2) -> [first1,last2)
257 template<class RandIt, class Compare>
partial_merge_bufferless(RandIt first1,RandIt last1,RandIt const last2,bool * const pis_range1_A,Compare comp)258 RandIt partial_merge_bufferless
259    (RandIt first1, RandIt last1, RandIt const last2, bool *const pis_range1_A, Compare comp)
260 {
261    return *pis_range1_A ? partial_merge_bufferless_impl(first1, last1, last2, pis_range1_A, comp)
262                         : partial_merge_bufferless_impl(first1, last1, last2, pis_range1_A, antistable<Compare>(comp));
263 }
264 
265 template<class SizeType>
needed_keys_count(SizeType n_block_a,SizeType n_block_b)266 static SizeType needed_keys_count(SizeType n_block_a, SizeType n_block_b)
267 {
268    return n_block_a + n_block_b;
269 }
270 
271 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
272 typename iterator_traits<RandIt>::size_type
find_next_block(RandItKeys const key_first,KeyCompare key_comp,RandIt const first,typename iterator_traits<RandIt>::size_type const l_block,typename iterator_traits<RandIt>::size_type const ix_first_block,typename iterator_traits<RandIt>::size_type const ix_last_block,Compare comp)273    find_next_block
274       ( RandItKeys const key_first
275       , KeyCompare key_comp
276       , RandIt const first
277       , typename iterator_traits<RandIt>::size_type const l_block
278       , typename iterator_traits<RandIt>::size_type const ix_first_block
279       , typename iterator_traits<RandIt>::size_type const ix_last_block
280       , Compare comp)
281 {
282    typedef typename iterator_traits<RandIt>::size_type      size_type;
283    typedef typename iterator_traits<RandIt>::value_type     value_type;
284    typedef typename iterator_traits<RandItKeys>::value_type key_type;
285    BOOST_ASSERT(ix_first_block <= ix_last_block);
286    size_type ix_min_block = 0u;
287    for (size_type szt_i = ix_first_block; szt_i < ix_last_block; ++szt_i) {
288       const value_type &min_val = first[ix_min_block*l_block];
289       const value_type &cur_val = first[szt_i*l_block];
290       const key_type   &min_key = key_first[ix_min_block];
291       const key_type   &cur_key = key_first[szt_i];
292 
293       bool const less_than_minimum = comp(cur_val, min_val) ||
294          (!comp(min_val, cur_val) && key_comp(cur_key, min_key));
295 
296       if (less_than_minimum) {
297          ix_min_block = szt_i;
298       }
299    }
300    return ix_min_block;
301 }
302 
303 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
merge_blocks_bufferless(RandItKeys const key_first,KeyCompare key_comp,RandIt const first,typename iterator_traits<RandIt>::size_type const l_block,typename iterator_traits<RandIt>::size_type const l_irreg1,typename iterator_traits<RandIt>::size_type const n_block_a,typename iterator_traits<RandIt>::size_type const n_block_b,typename iterator_traits<RandIt>::size_type const l_irreg2,Compare comp)304 void merge_blocks_bufferless
305    ( RandItKeys const key_first
306    , KeyCompare key_comp
307    , RandIt const first
308    , typename iterator_traits<RandIt>::size_type const l_block
309    , typename iterator_traits<RandIt>::size_type const l_irreg1
310    , typename iterator_traits<RandIt>::size_type const n_block_a
311    , typename iterator_traits<RandIt>::size_type const n_block_b
312    , typename iterator_traits<RandIt>::size_type const l_irreg2
313    , Compare comp)
314 {
315    typedef typename iterator_traits<RandIt>::size_type size_type;
316    size_type const key_count = needed_keys_count(n_block_a, n_block_b);
317    ::boost::ignore_unused(key_count);
318    //BOOST_ASSERT(n_block_a || n_block_b);
319    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
320    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]));
321 
322    size_type n_bef_irreg2 = 0;
323    bool l_irreg_pos_count = true;
324    RandItKeys key_mid(key_first + n_block_a);
325    RandIt const first_irr2 = first + l_irreg1 + (n_block_a+n_block_b)*l_block;
326    RandIt const last_irr2  = first_irr2 + l_irreg2;
327 
328    {  //Selection sort blocks
329       size_type n_block_left = n_block_b + n_block_a;
330       RandItKeys key_range2(key_first);
331 
332       size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
333       size_type max_check = min_value<size_type>(min_check+1, n_block_left);
334       for (RandIt f = first+l_irreg1; n_block_left; --n_block_left, ++key_range2, f += l_block, min_check -= min_check != 0, max_check -= max_check != 0) {
335          size_type const next_key_idx = find_next_block(key_range2, key_comp, f, l_block, min_check, max_check, comp);
336          RandItKeys const key_next(key_range2 + next_key_idx);
337          max_check = min_value<size_type>(max_value<size_type>(max_check, next_key_idx+size_type(2)), n_block_left);
338 
339          RandIt const first_min = f + next_key_idx*l_block;
340 
341          //Check if irregular b block should go here.
342          //If so, break to the special code handling the irregular block
343          if (l_irreg_pos_count && l_irreg2 && comp(*first_irr2, *first_min)){
344             l_irreg_pos_count = false;
345          }
346          n_bef_irreg2 += l_irreg_pos_count;
347 
348          swap_and_update_key(key_next, key_range2, key_mid, f, f + l_block, first_min);
349          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(f, f+l_block, comp));
350          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_min, first_min + l_block, comp));
351          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((f == (first+l_irreg1)) || !comp(*f, *(f-l_block)));
352       }
353    }
354    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first+l_irreg1+n_bef_irreg2*l_block, first_irr2, comp));
355 
356    RandIt first1 = first;
357    RandIt last1  = first+l_irreg1;
358    RandItKeys const key_end (key_first+n_bef_irreg2);
359    bool is_range1_A = true;
360 
361    for(RandItKeys key_next = key_first; key_next != key_end; ++key_next){
362       bool is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
363       first1 = is_range1_A == is_range2_A
364          ? last1 : partial_merge_bufferless(first1, last1, last1 + l_block, &is_range1_A, comp);
365       last1 += l_block;
366       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, first1, comp));
367    }
368 
369    merge_bufferless(is_range1_A ? first1 : last1, first_irr2, last_irr2, comp);
370    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, last_irr2, comp));
371 }
372 
373 // Complexity: 2*distance(first, last)+max_collected^2/2
374 //
375 // Tries to collect at most n_keys unique elements from [first, last),
376 // in the begining of the range, and ordered according to comp
377 //
378 // Returns the number of collected keys
379 template<class RandIt, class Compare, class XBuf>
380 typename iterator_traits<RandIt>::size_type
collect_unique(RandIt const first,RandIt const last,typename iterator_traits<RandIt>::size_type const max_collected,Compare comp,XBuf & xbuf)381    collect_unique
382       ( RandIt const first, RandIt const last
383       , typename iterator_traits<RandIt>::size_type const max_collected, Compare comp
384       , XBuf & xbuf)
385 {
386    typedef typename iterator_traits<RandIt>::size_type size_type;
387    size_type h = 0;
388    if(max_collected){
389       ++h;  // first key is always here
390       RandIt h0 = first;
391       RandIt u = first; ++u;
392       RandIt search_end = u;
393 
394       if(xbuf.capacity() >= max_collected){
395          typename XBuf::iterator const ph0 = xbuf.add(first);
396          while(u != last && h < max_collected){
397             typename XBuf::iterator const r = boost::movelib::lower_bound(ph0, xbuf.end(), *u, comp);
398             //If key not found add it to [h, h+h0)
399             if(r == xbuf.end() || comp(*u, *r) ){
400                RandIt const new_h0 = boost::move(search_end, u, h0);
401                search_end = u;
402                ++search_end;
403                ++h;
404                xbuf.insert(r, u);
405                h0 = new_h0;
406             }
407             ++u;
408          }
409          boost::move_backward(first, h0, h0+h);
410          boost::move(xbuf.data(), xbuf.end(), first);
411       }
412       else{
413          while(u != last && h < max_collected){
414             RandIt const r = boost::movelib::lower_bound(h0, search_end, *u, comp);
415             //If key not found add it to [h, h+h0)
416             if(r == search_end || comp(*u, *r) ){
417                RandIt const new_h0 = rotate_gcd(h0, search_end, u);
418                search_end = u;
419                ++search_end;
420                ++h;
421                rotate_gcd(r+(new_h0-h0), u, search_end);
422                h0 = new_h0;
423             }
424             ++u;
425          }
426          rotate_gcd(first, h0, h0+h);
427       }
428    }
429    return h;
430 }
431 
432 template<class Unsigned>
floor_sqrt(Unsigned const n)433 Unsigned floor_sqrt(Unsigned const n)
434 {
435    Unsigned x = n;
436    Unsigned y = x/2 + (x&1);
437    while (y < x){
438       x = y;
439       y = (x + n / x)/2;
440    }
441    return x;
442 }
443 
444 template<class Unsigned>
ceil_sqrt(Unsigned const n)445 Unsigned ceil_sqrt(Unsigned const n)
446 {
447    Unsigned r = floor_sqrt(n);
448    return r + Unsigned((n%r) != 0);
449 }
450 
451 template<class Unsigned>
floor_merge_multiple(Unsigned const n,Unsigned & base,Unsigned & pow)452 Unsigned floor_merge_multiple(Unsigned const n, Unsigned &base, Unsigned &pow)
453 {
454    Unsigned s = n;
455    Unsigned p = 0;
456    while(s > AdaptiveSortInsertionSortThreshold){
457       s /= 2;
458       ++p;
459    }
460    base = s;
461    pow = p;
462    return s << p;
463 }
464 
465 template<class Unsigned>
ceil_merge_multiple(Unsigned const n,Unsigned & base,Unsigned & pow)466 Unsigned ceil_merge_multiple(Unsigned const n, Unsigned &base, Unsigned &pow)
467 {
468    Unsigned fm = floor_merge_multiple(n, base, pow);
469 
470    if(fm != n){
471       if(base < AdaptiveSortInsertionSortThreshold){
472          ++base;
473       }
474       else{
475          base = AdaptiveSortInsertionSortThreshold/2 + 1;
476          ++pow;
477       }
478    }
479    return base << pow;
480 }
481 
482 template<class Unsigned>
ceil_sqrt_multiple(Unsigned const n,Unsigned * pbase=0)483 Unsigned ceil_sqrt_multiple(Unsigned const n, Unsigned *pbase = 0)
484 {
485    Unsigned const r = ceil_sqrt(n);
486    Unsigned pow = 0;
487    Unsigned base = 0;
488    Unsigned const res = ceil_merge_multiple(r, base, pow);
489    if(pbase) *pbase = base;
490    return res;
491 }
492 
493 struct less
494 {
495    template<class T>
operator ()boost::movelib::detail_adaptive::less496    bool operator()(const T &l, const T &r)
497    {  return l < r;  }
498 };
499 
500 ///////////////////////////////////////////////////////////////////////////////
501 //
502 //                            MERGE BLOCKS
503 //
504 ///////////////////////////////////////////////////////////////////////////////
505 
506 //#define ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
507 
508 #if defined ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
509 template<class RandIt, class Compare>
slow_stable_sort(RandIt const first,RandIt const last,Compare comp)510 void slow_stable_sort
511    ( RandIt const first, RandIt const last, Compare comp)
512 {
513    boost::movelib::inplace_stable_sort(first, last, comp);
514 }
515 
516 #else //ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
517 
518 template<class RandIt, class Compare>
slow_stable_sort(RandIt const first,RandIt const last,Compare comp)519 void slow_stable_sort
520    ( RandIt const first, RandIt const last, Compare comp)
521 {
522    typedef typename iterator_traits<RandIt>::size_type size_type;
523    size_type L = size_type(last - first);
524    {  //Use insertion sort to merge first elements
525       size_type m = 0;
526       while((L - m) > size_type(AdaptiveSortInsertionSortThreshold)){
527          insertion_sort(first+m, first+m+size_type(AdaptiveSortInsertionSortThreshold), comp);
528          m += AdaptiveSortInsertionSortThreshold;
529       }
530       insertion_sort(first+m, last, comp);
531    }
532 
533    size_type h = AdaptiveSortInsertionSortThreshold;
534    for(bool do_merge = L > h; do_merge; h*=2){
535       do_merge = (L - h) > h;
536       size_type p0 = 0;
537       if(do_merge){
538          size_type const h_2 = 2*h;
539          while((L-p0) > h_2){
540             merge_bufferless(first+p0, first+p0+h, first+p0+h_2, comp);
541             p0 += h_2;
542          }
543       }
544       if((L-p0) > h){
545          merge_bufferless(first+p0, first+p0+h, last, comp);
546       }
547    }
548 }
549 
550 #endif   //ADAPTIVE_SORT_MERGE_SLOW_STABLE_SORT_IS_NLOGN
551 
552 //Returns new l_block and updates use_buf
553 template<class Unsigned>
lblock_for_combine(Unsigned const l_block,Unsigned const n_keys,Unsigned const l_data,bool & use_buf)554 Unsigned lblock_for_combine
555    (Unsigned const l_block, Unsigned const n_keys, Unsigned const l_data, bool &use_buf)
556 {
557    BOOST_ASSERT(l_data > 1);
558 
559    //We need to guarantee lblock >= l_merged/(n_keys/2) keys for the combination.
560    //We have at least 4 keys guaranteed (which are the minimum to merge 2 ranges)
561    //If l_block != 0, then n_keys is already enough to merge all blocks in all
562    //phases as we've found all needed keys for that buffer and length before.
563    //If l_block == 0 then see if half keys can be used as buffer and the rest
564    //as keys guaranteeing that n_keys >= (2*l_merged)/lblock =
565    if(!l_block){
566       //If l_block == 0 then n_keys is power of two
567       //(guaranteed by build_params(...))
568       BOOST_ASSERT(n_keys >= 4);
569       //BOOST_ASSERT(0 == (n_keys &(n_keys-1)));
570 
571       //See if half keys are at least 4 and if half keys fulfill
572       Unsigned const new_buf  = n_keys/2;
573       Unsigned const new_keys = n_keys-new_buf;
574       use_buf = new_keys >= 4 && new_keys >= l_data/new_buf;
575       if(use_buf){
576          return new_buf;
577       }
578       else{
579          return l_data/n_keys;
580       }
581    }
582    else{
583       use_buf = true;
584       return l_block;
585    }
586 }
587 
588 template<class RandIt, class Compare, class XBuf>
stable_sort(RandIt first,RandIt last,Compare comp,XBuf & xbuf)589 void stable_sort( RandIt first, RandIt last, Compare comp, XBuf & xbuf)
590 {
591    typedef typename iterator_traits<RandIt>::size_type size_type;
592    size_type const len = size_type(last - first);
593    size_type const half_len = len/2 + (len&1);
594    if(std::size_t(xbuf.capacity() - xbuf.size()) >= half_len) {
595       merge_sort(first, last, comp, xbuf.data()+xbuf.size());
596    }
597    else{
598       slow_stable_sort(first, last, comp);
599    }
600 }
601 
602 template<class RandIt, class Comp, class XBuf>
unstable_sort(RandIt first,RandIt last,Comp comp,XBuf & xbuf)603 void unstable_sort( RandIt first, RandIt last
604                     , Comp comp
605                     , XBuf & xbuf)
606 {
607    heap_sort(first, last, comp);
608    ::boost::ignore_unused(xbuf);
609 }
610 
611 template<class RandIt, class Compare, class XBuf>
stable_merge(RandIt first,RandIt const middle,RandIt last,Compare comp,XBuf & xbuf)612 void stable_merge
613       ( RandIt first, RandIt const middle, RandIt last
614       , Compare comp
615       , XBuf &xbuf)
616 {
617    BOOST_ASSERT(xbuf.empty());
618    typedef typename iterator_traits<RandIt>::size_type   size_type;
619    size_type const len1  = size_type(middle-first);
620    size_type const len2  = size_type(last-middle);
621    size_type const l_min = min_value<size_type>(len1, len2);
622    if(xbuf.capacity() >= l_min){
623       buffered_merge(first, middle, last, comp, xbuf);
624       xbuf.clear();
625    }
626    else{
627       //merge_bufferless(first, middle, last, comp);
628       merge_adaptive_ONlogN(first, middle, last, comp, xbuf.begin(), xbuf.capacity());
629    }
630    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, last, boost::movelib::unantistable(comp)));
631 }
632 
633 template<class RandIt, class Comp, class XBuf>
initialize_keys(RandIt first,RandIt last,Comp comp,XBuf & xbuf)634 void initialize_keys( RandIt first, RandIt last
635                     , Comp comp
636                     , XBuf & xbuf)
637 {
638    unstable_sort(first, last, comp, xbuf);
639    BOOST_ASSERT(boost::movelib::is_sorted_and_unique(first, last, comp));
640 }
641 
642 template<class RandIt, class U>
initialize_keys(RandIt first,RandIt last,less,U &)643 void initialize_keys( RandIt first, RandIt last
644                     , less
645                     , U &)
646 {
647    typedef typename iterator_traits<RandIt>::value_type value_type;
648    std::size_t count = std::size_t(last - first);
649    for(std::size_t i = 0; i != count; ++i){
650       *first = static_cast<value_type>(i);
651       ++first;
652    }
653 }
654 
655 template <class Unsigned>
calculate_total_combined(Unsigned const len,Unsigned const l_prev_merged,Unsigned * pl_irreg_combined=0)656 Unsigned calculate_total_combined(Unsigned const len, Unsigned const l_prev_merged, Unsigned *pl_irreg_combined = 0)
657 {
658    typedef Unsigned size_type;
659 
660    size_type const l_combined = 2*l_prev_merged;
661    size_type l_irreg_combined = len%l_combined;
662    size_type l_total_combined = len;
663    if(l_irreg_combined <= l_prev_merged){
664       l_total_combined -= l_irreg_combined;
665       l_irreg_combined = 0;
666    }
667    if(pl_irreg_combined)
668       *pl_irreg_combined = l_irreg_combined;
669    return l_total_combined;
670 }
671 
672 template<class RandItKeys, class KeyCompare, class SizeType, class XBuf>
combine_params(RandItKeys const keys,KeyCompare key_comp,SizeType l_combined,SizeType const l_prev_merged,SizeType const l_block,XBuf & xbuf,SizeType & n_block_a,SizeType & n_block_b,SizeType & l_irreg1,SizeType & l_irreg2,bool do_initialize_keys=true)673 void combine_params
674    ( RandItKeys const keys
675    , KeyCompare key_comp
676    , SizeType l_combined
677    , SizeType const l_prev_merged
678    , SizeType const l_block
679    , XBuf & xbuf
680    //Output
681    , SizeType &n_block_a
682    , SizeType &n_block_b
683    , SizeType &l_irreg1
684    , SizeType &l_irreg2
685    //Options
686    , bool do_initialize_keys = true)
687 {
688    typedef SizeType   size_type;
689 
690    //Initial parameters for selection sort blocks
691    l_irreg1 = l_prev_merged%l_block;
692    l_irreg2 = (l_combined-l_irreg1)%l_block;
693    BOOST_ASSERT(((l_combined-l_irreg1-l_irreg2)%l_block) == 0);
694    size_type const n_reg_block = (l_combined-l_irreg1-l_irreg2)/l_block;
695    n_block_a = l_prev_merged/l_block;
696    n_block_b = n_reg_block - n_block_a;
697    BOOST_ASSERT(n_reg_block>=n_block_a);
698 
699    //Key initialization
700    if (do_initialize_keys) {
701       initialize_keys(keys, keys + needed_keys_count(n_block_a, n_block_b), key_comp, xbuf);
702    }
703 }
704 
705 
706 
707 //////////////////////////////////
708 //
709 //          partial_merge
710 //
711 //////////////////////////////////
712 template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
op_partial_merge_impl(InputIt1 & r_first1,InputIt1 const last1,InputIt2 & r_first2,InputIt2 const last2,OutputIt d_first,Compare comp,Op op)713 OutputIt op_partial_merge_impl
714    (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, OutputIt d_first, Compare comp, Op op)
715 {
716    InputIt1 first1(r_first1);
717    InputIt2 first2(r_first2);
718    if(first2 != last2 && last1 != first1)
719    while(1){
720       if(comp(*first2, *first1)) {
721          op(first2++, d_first++);
722          if(first2 == last2){
723             break;
724          }
725       }
726       else{
727          op(first1++, d_first++);
728          if(first1 == last1){
729             break;
730          }
731       }
732    }
733    r_first1 = first1;
734    r_first2 = first2;
735    return d_first;
736 }
737 
738 template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
op_partial_merge(InputIt1 & r_first1,InputIt1 const last1,InputIt2 & r_first2,InputIt2 const last2,OutputIt d_first,Compare comp,Op op,bool is_stable)739 OutputIt op_partial_merge
740    (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, OutputIt d_first, Compare comp, Op op, bool is_stable)
741 {
742    return is_stable ? op_partial_merge_impl(r_first1, last1, r_first2, last2, d_first, comp, op)
743                     : op_partial_merge_impl(r_first1, last1, r_first2, last2, d_first, antistable<Compare>(comp), op);
744 }
745 
746 //////////////////////////////////
747 //////////////////////////////////
748 //////////////////////////////////
749 //
750 //    op_partial_merge_and_save
751 //
752 //////////////////////////////////
753 //////////////////////////////////
754 //////////////////////////////////
755 template<class InputIt1, class InputIt2, class OutputIt, class Compare, class Op>
op_partial_merge_and_swap_impl(InputIt1 & r_first1,InputIt1 const last1,InputIt2 & r_first2,InputIt2 const last2,InputIt2 & r_first_min,OutputIt d_first,Compare comp,Op op)756 OutputIt op_partial_merge_and_swap_impl
757    (InputIt1 &r_first1, InputIt1 const last1, InputIt2 &r_first2, InputIt2 const last2, InputIt2 &r_first_min, OutputIt d_first, Compare comp, Op op)
758 {
759    InputIt1 first1(r_first1);
760    InputIt2 first2(r_first2);
761 
762    if(first2 != last2 && last1 != first1) {
763       InputIt2 first_min(r_first_min);
764       bool non_empty_ranges = true;
765       do{
766          if(comp(*first_min, *first1)) {
767             op(three_way_t(), first2++, first_min++, d_first++);
768             non_empty_ranges = first2 != last2;
769          }
770          else{
771             op(first1++, d_first++);
772             non_empty_ranges = first1 != last1;
773          }
774       } while(non_empty_ranges);
775       r_first_min = first_min;
776       r_first1 = first1;
777       r_first2 = first2;
778    }
779    return d_first;
780 }
781 
782 template<class RandIt, class InputIt2, class OutputIt, class Compare, class Op>
op_partial_merge_and_swap(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)783 OutputIt op_partial_merge_and_swap
784    (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)
785 {
786    return is_stable ? op_partial_merge_and_swap_impl(r_first1, last1, r_first2, last2, r_first_min, d_first, comp, op)
787                     : op_partial_merge_and_swap_impl(r_first1, last1, r_first2, last2, r_first_min, d_first, antistable<Compare>(comp), op);
788 }
789 
790 template<class RandIt1, class RandIt2, class RandItB, class Compare, class Op>
op_buffered_partial_merge_and_swap_to_range1_and_buffer(RandIt1 first1,RandIt1 const last1,RandIt2 & rfirst2,RandIt2 const last2,RandIt2 & rfirst_min,RandItB & rfirstb,Compare comp,Op op)791 RandItB op_buffered_partial_merge_and_swap_to_range1_and_buffer
792    ( RandIt1 first1, RandIt1 const last1
793    , RandIt2 &rfirst2, RandIt2 const last2, RandIt2 &rfirst_min
794    , RandItB &rfirstb, Compare comp, Op op )
795 {
796    RandItB firstb = rfirstb;
797    RandItB lastb  = firstb;
798    RandIt2 first2 = rfirst2;
799 
800    //Move to buffer while merging
801    //Three way moves need less moves when op is swap_op so use it
802    //when merging elements from range2 to the destination occupied by range1
803    if(first1 != last1 && first2 != last2){
804       RandIt2 first_min = rfirst_min;
805       op(four_way_t(), first2++, first_min++, first1++, lastb++);
806 
807       while(first1 != last1){
808          if(first2 == last2){
809             lastb = op(forward_t(), first1, last1, firstb);
810             break;
811          }
812 
813          if(comp(*first_min, *firstb)){
814             op( four_way_t(), first2++, first_min++, first1++, lastb++);
815          }
816          else{
817             op(three_way_t(), firstb++, first1++, lastb++);
818          }
819       }
820       rfirst2 = first2;
821       rfirstb = firstb;
822       rfirst_min = first_min;
823    }
824 
825    return lastb;
826 }
827 
828 template<class RandIt1, class RandIt2, class RandItB, class Compare, class Op>
op_buffered_partial_merge_to_range1_and_buffer(RandIt1 first1,RandIt1 const last1,RandIt2 & rfirst2,RandIt2 const last2,RandItB & rfirstb,Compare comp,Op op)829 RandItB op_buffered_partial_merge_to_range1_and_buffer
830    ( RandIt1 first1, RandIt1 const last1
831    , RandIt2 &rfirst2, RandIt2 const last2
832    , RandItB &rfirstb, Compare comp, Op op )
833 {
834    RandItB firstb = rfirstb;
835    RandItB lastb  = firstb;
836    RandIt2 first2 = rfirst2;
837 
838    //Move to buffer while merging
839    //Three way moves need less moves when op is swap_op so use it
840    //when merging elements from range2 to the destination occupied by range1
841    if(first1 != last1 && first2 != last2){
842       op(three_way_t(), first2++, first1++, lastb++);
843 
844       while(true){
845          if(first1 == last1){
846             break;
847          }
848          if(first2 == last2){
849             lastb = op(forward_t(), first1, last1, firstb);
850             break;
851          }
852          if (comp(*first2, *firstb)) {
853             op(three_way_t(), first2++, first1++, lastb++);
854          }
855          else {
856             op(three_way_t(), firstb++, first1++, lastb++);
857          }
858       }
859       rfirst2 = first2;
860       rfirstb = firstb;
861    }
862 
863    return lastb;
864 }
865 
866 template<class RandIt, class RandItBuf, class Compare, class Op>
op_partial_merge_and_save_impl(RandIt first1,RandIt const last1,RandIt & rfirst2,RandIt last2,RandIt first_min,RandItBuf & buf_first1_in_out,RandItBuf & buf_last1_in_out,Compare comp,Op op)867 RandIt op_partial_merge_and_save_impl
868    ( RandIt first1, RandIt const last1, RandIt &rfirst2, RandIt last2, RandIt first_min
869    , RandItBuf &buf_first1_in_out, RandItBuf &buf_last1_in_out
870    , Compare comp, Op op
871    )
872 {
873    RandItBuf buf_first1 = buf_first1_in_out;
874    RandItBuf buf_last1  = buf_last1_in_out;
875    RandIt first2(rfirst2);
876 
877    bool const do_swap = first2 != first_min;
878    if(buf_first1 == buf_last1){
879       //Skip any element that does not need to be moved
880       RandIt new_first1 = skip_until_merge(first1, last1, *first_min, comp);
881       buf_first1 += (new_first1-first1);
882       first1 = new_first1;
883       buf_last1  = do_swap ? op_buffered_partial_merge_and_swap_to_range1_and_buffer(first1, last1, first2, last2, first_min, buf_first1, comp, op)
884                            : op_buffered_partial_merge_to_range1_and_buffer    (first1, last1, first2, last2, buf_first1, comp, op);
885       first1 = last1;
886    }
887    else{
888       BOOST_ASSERT((last1-first1) == (buf_last1 - buf_first1));
889    }
890 
891    //Now merge from buffer
892    first1 = do_swap ? op_partial_merge_and_swap_impl(buf_first1, buf_last1, first2, last2, first_min, first1, comp, op)
893                     : op_partial_merge_impl    (buf_first1, buf_last1, first2, last2, first1, comp, op);
894    buf_first1_in_out = buf_first1;
895    buf_last1_in_out  = buf_last1;
896    rfirst2 = first2;
897    return first1;
898 }
899 
900 template<class RandIt, class RandItBuf, class Compare, class Op>
op_partial_merge_and_save(RandIt first1,RandIt const last1,RandIt & rfirst2,RandIt last2,RandIt first_min,RandItBuf & buf_first1_in_out,RandItBuf & buf_last1_in_out,Compare comp,Op op,bool is_stable)901 RandIt op_partial_merge_and_save
902    ( RandIt first1, RandIt const last1, RandIt &rfirst2, RandIt last2, RandIt first_min
903    , RandItBuf &buf_first1_in_out
904    , RandItBuf &buf_last1_in_out
905    , Compare comp
906    , Op op
907    , bool is_stable)
908 {
909    return is_stable
910       ? op_partial_merge_and_save_impl
911          (first1, last1, rfirst2, last2, first_min, buf_first1_in_out, buf_last1_in_out, comp, op)
912       : op_partial_merge_and_save_impl
913          (first1, last1, rfirst2, last2, first_min, buf_first1_in_out, buf_last1_in_out, antistable<Compare>(comp), op)
914       ;
915 }
916 
917 //////////////////////////////////
918 //////////////////////////////////
919 //////////////////////////////////
920 //
921 //    op_merge_blocks_with_irreg
922 //
923 //////////////////////////////////
924 //////////////////////////////////
925 //////////////////////////////////
926 
927 template<class RandItKeys, class KeyCompare, class RandIt, class RandIt2, class OutputIt, class Compare, class Op>
op_merge_blocks_with_irreg(RandItKeys key_first,RandItKeys key_mid,KeyCompare key_comp,RandIt first_reg,RandIt2 & first_irr,RandIt2 const last_irr,OutputIt dest,typename iterator_traits<RandIt>::size_type const l_block,typename iterator_traits<RandIt>::size_type n_block_left,typename iterator_traits<RandIt>::size_type min_check,typename iterator_traits<RandIt>::size_type max_check,Compare comp,bool const is_stable,Op op)928 OutputIt op_merge_blocks_with_irreg
929    ( RandItKeys key_first
930    , RandItKeys key_mid
931    , KeyCompare key_comp
932    , RandIt first_reg
933    , RandIt2 &first_irr
934    , RandIt2 const last_irr
935    , OutputIt dest
936    , typename iterator_traits<RandIt>::size_type const l_block
937    , typename iterator_traits<RandIt>::size_type n_block_left
938    , typename iterator_traits<RandIt>::size_type min_check
939    , typename iterator_traits<RandIt>::size_type max_check
940    , Compare comp, bool const is_stable, Op op)
941 {
942    typedef typename iterator_traits<RandIt>::size_type size_type;
943 
944    for(; n_block_left; --n_block_left, ++key_first, min_check -= min_check != 0, max_check -= max_check != 0){
945       size_type next_key_idx = find_next_block(key_first, key_comp, first_reg, l_block, min_check, max_check, comp);
946       max_check = min_value<size_type>(max_value<size_type>(max_check, next_key_idx+size_type(2)), n_block_left);
947       RandIt const last_reg  = first_reg + l_block;
948       RandIt first_min = first_reg + next_key_idx*l_block;
949       RandIt const last_min  = first_min + l_block;
950       boost::ignore_unused(last_min);
951 
952       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_reg, last_reg, comp));
953       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!next_key_idx || boost::movelib::is_sorted(first_min, last_min, comp));
954       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((!next_key_idx || !comp(*first_reg, *first_min )));
955 
956       OutputIt orig_dest = dest;
957       boost::ignore_unused(orig_dest);
958       dest = next_key_idx ? op_partial_merge_and_swap(first_irr, last_irr, first_reg, last_reg, first_min, dest, comp, op, is_stable)
959                           : op_partial_merge         (first_irr, last_irr, first_reg, last_reg, dest, comp, op, is_stable);
960       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(orig_dest, dest, comp));
961 
962       if(first_reg == dest){
963          dest = next_key_idx ? ::boost::adl_move_swap_ranges(first_min, last_min, first_reg)
964                              : last_reg;
965       }
966       else{
967          dest = next_key_idx ? op(three_way_forward_t(), first_reg, last_reg, first_min, dest)
968                              : op(forward_t(), first_reg, last_reg, dest);
969       }
970 
971       RandItKeys const key_next(key_first + next_key_idx);
972       swap_and_update_key(key_next, key_first, key_mid, last_reg, last_reg, first_min);
973 
974       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(orig_dest, dest, comp));
975       first_reg = last_reg;
976    }
977    return dest;
978 }
979 
980 //////////////////////////////////
981 //////////////////////////////////
982 //////////////////////////////////
983 //
984 //    op_merge_blocks_left/right
985 //
986 //////////////////////////////////
987 //////////////////////////////////
988 //////////////////////////////////
989 
990 template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class Op>
op_merge_blocks_left(RandItKeys const key_first,KeyCompare key_comp,RandIt const first,typename iterator_traits<RandIt>::size_type const l_block,typename iterator_traits<RandIt>::size_type const l_irreg1,typename iterator_traits<RandIt>::size_type const n_block_a,typename iterator_traits<RandIt>::size_type const n_block_b,typename iterator_traits<RandIt>::size_type const l_irreg2,Compare comp,Op op)991 void op_merge_blocks_left
992    ( RandItKeys const key_first
993    , KeyCompare key_comp
994    , RandIt const first
995    , typename iterator_traits<RandIt>::size_type const l_block
996    , typename iterator_traits<RandIt>::size_type const l_irreg1
997    , typename iterator_traits<RandIt>::size_type const n_block_a
998    , typename iterator_traits<RandIt>::size_type const n_block_b
999    , typename iterator_traits<RandIt>::size_type const l_irreg2
1000    , Compare comp, Op op)
1001 {
1002    typedef typename iterator_traits<RandIt>::size_type size_type;
1003    size_type const key_count = needed_keys_count(n_block_a, n_block_b);
1004    boost::ignore_unused(key_count);
1005 
1006 //   BOOST_ASSERT(n_block_a || n_block_b);
1007    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
1008    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]));
1009 
1010    size_type n_block_b_left = n_block_b;
1011    size_type n_block_a_left = n_block_a;
1012    size_type n_block_left = n_block_b + n_block_a;
1013    RandItKeys key_mid(key_first + n_block_a);
1014 
1015    RandIt buffer = first - l_block;
1016    RandIt first1 = first;
1017    RandIt last1  = first1 + l_irreg1;
1018    RandIt first2 = last1;
1019    RandIt const irreg2 = first2 + n_block_left*l_block;
1020    bool is_range1_A = true;
1021 
1022    RandItKeys key_range2(key_first);
1023 
1024    ////////////////////////////////////////////////////////////////////////////
1025    //Process all regular blocks before the irregular B block
1026    ////////////////////////////////////////////////////////////////////////////
1027    size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
1028    size_type max_check = min_value<size_type>(min_check+size_type(1), n_block_left);
1029    for (; n_block_left; --n_block_left, ++key_range2, min_check -= min_check != 0, max_check -= max_check != 0) {
1030       size_type const next_key_idx = find_next_block(key_range2, key_comp, first2, l_block, min_check, max_check, comp);
1031       max_check = min_value<size_type>(max_value<size_type>(max_check, next_key_idx+size_type(2)), n_block_left);
1032       RandIt const first_min = first2 + next_key_idx*l_block;
1033       RandIt const last_min  = first_min + l_block;
1034 
1035       boost::ignore_unused(last_min);
1036       RandIt const last2  = first2 + l_block;
1037 
1038       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first1, last1, comp));
1039       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first2, last2, comp));
1040       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_left || boost::movelib::is_sorted(first_min, last_min, comp));
1041 
1042       //Check if irregular b block should go here.
1043       //If so, break to the special code handling the irregular block
1044       if (!n_block_b_left &&
1045             ( (l_irreg2 && comp(*irreg2, *first_min)) || (!l_irreg2 && is_range1_A)) ){
1046          break;
1047       }
1048 
1049       RandItKeys const key_next(key_range2 + next_key_idx);
1050       bool const is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
1051 
1052       bool const is_buffer_middle = last1 == buffer;
1053       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT( ( is_buffer_middle && size_type(first2-buffer) == l_block && buffer == last1) ||
1054                                           (!is_buffer_middle && size_type(first1-buffer) == l_block && first2 == last1));
1055 
1056       if(is_range1_A == is_range2_A){
1057          BOOST_ASSERT((first1 == last1) || !comp(*first_min, last1[-1]));
1058          if(!is_buffer_middle){
1059             buffer = op(forward_t(), first1, last1, buffer);
1060          }
1061          swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1062          first1 = first2;
1063          last1  = last2;
1064       }
1065       else {
1066          RandIt unmerged;
1067          RandIt buf_beg;
1068          RandIt buf_end;
1069          if(is_buffer_middle){
1070             buf_end = buf_beg = first2 - (last1-first1);
1071             unmerged = op_partial_merge_and_save( first1, last1, first2, last2, first_min
1072                                                 , buf_beg, buf_end, comp, op, is_range1_A);
1073          }
1074          else{
1075             buf_beg = first1;
1076             buf_end = last1;
1077             unmerged = op_partial_merge_and_save
1078                (buffer, buffer+(last1-first1), first2, last2, first_min, buf_beg, buf_end, comp, op, is_range1_A);
1079          }
1080 
1081          boost::ignore_unused(unmerged);
1082          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first-l_block, unmerged, comp));
1083 
1084          swap_and_update_key( key_next, key_range2, key_mid, first2, last2
1085                             , last_min - size_type(last2 - first2));
1086 
1087          if(buf_beg != buf_end){  //range2 exhausted: is_buffer_middle for the next iteration
1088             first1 = buf_beg;
1089             last1  = buf_end;
1090             BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(buf_end == (last2-l_block));
1091             buffer = last1;
1092          }
1093          else{ //range1 exhausted: !is_buffer_middle for the next iteration
1094             first1 = first2;
1095             last1  = last2;
1096             buffer = first2 - l_block;
1097             is_range1_A = is_range2_A;
1098          }
1099       }
1100       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT( (is_range2_A && n_block_a_left) || (!is_range2_A && n_block_b_left));
1101       is_range2_A ? --n_block_a_left : --n_block_b_left;
1102       first2 = last2;
1103    }
1104 
1105    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));
1106    BOOST_ASSERT(!n_block_b_left);
1107 
1108    ////////////////////////////////////////////////////////////////////////////
1109    //Process remaining range 1 left before the irregular B block
1110    ////////////////////////////////////////////////////////////////////////////
1111    bool const is_buffer_middle = last1 == buffer;
1112    RandIt first_irr2 = irreg2;
1113    RandIt const last_irr2  = first_irr2 + l_irreg2;
1114    if(l_irreg2 && is_range1_A){
1115       if(is_buffer_middle){
1116          first1 = skip_until_merge(first1, last1, *first_irr2, comp);
1117          //Even if we copy backward, no overlapping occurs so use forward copy
1118          //that can be faster specially with trivial types
1119          RandIt const new_first1 = first2 - (last1 - first1);
1120          op(forward_t(), first1, last1, new_first1);
1121          first1 = new_first1;
1122          last1 = first2;
1123          buffer = first1 - l_block;
1124       }
1125       buffer = op_partial_merge_impl(first1, last1, first_irr2, last_irr2, buffer, comp, op);
1126       buffer = op(forward_t(), first1, last1, buffer);
1127    }
1128    else if(!is_buffer_middle){
1129       buffer = op(forward_t(), first1, last1, buffer);
1130    }
1131    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first-l_block, buffer, comp));
1132 
1133    ////////////////////////////////////////////////////////////////////////////
1134    //Process irregular B block and remaining A blocks
1135    ////////////////////////////////////////////////////////////////////////////
1136    buffer = op_merge_blocks_with_irreg
1137       ( key_range2, key_mid, key_comp, first2, first_irr2, last_irr2
1138       , buffer, l_block, n_block_left, min_check, max_check, comp, false, op);
1139    buffer = op(forward_t(), first_irr2, last_irr2, buffer);
1140    boost::ignore_unused(buffer);
1141    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first-l_block, buffer, comp));
1142 }
1143 
1144 // first - first element to merge.
1145 // first[-l_block, 0) - buffer (if use_buf == true)
1146 // l_block - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded
1147 // keys - sequence of keys, in same order as blocks. key<midkey means stream A
1148 // n_bef_irreg2/n_aft_irreg2 are regular blocks
1149 // l_irreg2 is a irregular block, that is to be combined after n_bef_irreg2 blocks and before n_aft_irreg2 blocks
1150 // If l_irreg2==0 then n_aft_irreg2==0 (no irregular blocks).
1151 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
merge_blocks_left(RandItKeys const key_first,KeyCompare key_comp,RandIt const first,typename iterator_traits<RandIt>::size_type const l_block,typename iterator_traits<RandIt>::size_type const l_irreg1,typename iterator_traits<RandIt>::size_type const n_block_a,typename iterator_traits<RandIt>::size_type const n_block_b,typename iterator_traits<RandIt>::size_type const l_irreg2,Compare comp,bool const xbuf_used)1152 void merge_blocks_left
1153    ( RandItKeys const key_first
1154    , KeyCompare key_comp
1155    , RandIt const first
1156    , typename iterator_traits<RandIt>::size_type const l_block
1157    , typename iterator_traits<RandIt>::size_type const l_irreg1
1158    , typename iterator_traits<RandIt>::size_type const n_block_a
1159    , typename iterator_traits<RandIt>::size_type const n_block_b
1160    , typename iterator_traits<RandIt>::size_type const l_irreg2
1161    , Compare comp
1162    , bool const xbuf_used)
1163 {
1164    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]));
1165    if(xbuf_used){
1166       op_merge_blocks_left
1167          (key_first, key_comp, first, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, move_op());
1168    }
1169    else{
1170       op_merge_blocks_left
1171          (key_first, key_comp, first, l_block, l_irreg1, n_block_a, n_block_b, l_irreg2, comp, swap_op());
1172    }
1173 }
1174 
1175 // first - first element to merge.
1176 // [first+l_block*(n_bef_irreg2+n_aft_irreg2)+l_irreg2, first+l_block*(n_bef_irreg2+n_aft_irreg2+1)+l_irreg2) - buffer
1177 // l_block - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded
1178 // keys - sequence of keys, in same order as blocks. key<midkey means stream A
1179 // n_bef_irreg2/n_aft_irreg2 are regular blocks
1180 // l_irreg2 is a irregular block, that is to be combined after n_bef_irreg2 blocks and before n_aft_irreg2 blocks
1181 // If l_irreg2==0 then n_aft_irreg2==0 (no irregular blocks).
1182 template<class RandItKeys, class KeyCompare, class RandIt, class Compare>
merge_blocks_right(RandItKeys const key_first,KeyCompare key_comp,RandIt const first,typename iterator_traits<RandIt>::size_type const l_block,typename iterator_traits<RandIt>::size_type const n_block_a,typename iterator_traits<RandIt>::size_type const n_block_b,typename iterator_traits<RandIt>::size_type const l_irreg2,Compare comp,bool const xbuf_used)1183 void merge_blocks_right
1184    ( RandItKeys const key_first
1185    , KeyCompare key_comp
1186    , RandIt const first
1187    , typename iterator_traits<RandIt>::size_type const l_block
1188    , typename iterator_traits<RandIt>::size_type const n_block_a
1189    , typename iterator_traits<RandIt>::size_type const n_block_b
1190    , typename iterator_traits<RandIt>::size_type const l_irreg2
1191    , Compare comp
1192    , bool const xbuf_used)
1193 {
1194    merge_blocks_left
1195       ( (make_reverse_iterator)(key_first + needed_keys_count(n_block_a, n_block_b))
1196       , inverse<KeyCompare>(key_comp)
1197       , (make_reverse_iterator)(first + ((n_block_a+n_block_b)*l_block+l_irreg2))
1198       , l_block
1199       , l_irreg2
1200       , n_block_b
1201       , n_block_a
1202       , 0
1203       , inverse<Compare>(comp), xbuf_used);
1204 }
1205 
1206 //////////////////////////////////
1207 //////////////////////////////////
1208 //////////////////////////////////
1209 //
1210 //    op_merge_blocks_with_buf
1211 //
1212 //////////////////////////////////
1213 //////////////////////////////////
1214 //////////////////////////////////
1215 template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class Op, class RandItBuf>
op_merge_blocks_with_buf(RandItKeys key_first,KeyCompare key_comp,RandIt const first,typename iterator_traits<RandIt>::size_type const l_block,typename iterator_traits<RandIt>::size_type const l_irreg1,typename iterator_traits<RandIt>::size_type const n_block_a,typename iterator_traits<RandIt>::size_type const n_block_b,typename iterator_traits<RandIt>::size_type const l_irreg2,Compare comp,Op op,RandItBuf const buf_first)1216 void op_merge_blocks_with_buf
1217    ( RandItKeys key_first
1218    , KeyCompare key_comp
1219    , RandIt const first
1220    , typename iterator_traits<RandIt>::size_type const l_block
1221    , typename iterator_traits<RandIt>::size_type const l_irreg1
1222    , typename iterator_traits<RandIt>::size_type const n_block_a
1223    , typename iterator_traits<RandIt>::size_type const n_block_b
1224    , typename iterator_traits<RandIt>::size_type const l_irreg2
1225    , Compare comp
1226    , Op op
1227    , RandItBuf const buf_first)
1228 {
1229    typedef typename iterator_traits<RandIt>::size_type size_type;
1230    size_type const key_count = needed_keys_count(n_block_a, n_block_b);
1231    boost::ignore_unused(key_count);
1232    //BOOST_ASSERT(n_block_a || n_block_b);
1233    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted_and_unique(key_first, key_first + key_count, key_comp));
1234    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]));
1235 
1236    size_type n_block_b_left = n_block_b;
1237    size_type n_block_a_left = n_block_a;
1238    size_type n_block_left = n_block_b + n_block_a;
1239    RandItKeys key_mid(key_first + n_block_a);
1240 
1241    RandItBuf buffer = buf_first;
1242    RandItBuf buffer_end = buffer;
1243    RandIt first1 = first;
1244    RandIt last1  = first1 + l_irreg1;
1245    RandIt first2 = last1;
1246    RandIt const first_irr2 = first2 + n_block_left*l_block;
1247    bool is_range1_A = true;
1248    const size_type len = l_block * n_block_a + l_block * n_block_b + l_irreg1 + l_irreg2;
1249    boost::ignore_unused(len);
1250 
1251    RandItKeys key_range2(key_first);
1252 
1253    ////////////////////////////////////////////////////////////////////////////
1254    //Process all regular blocks before the irregular B block
1255    ////////////////////////////////////////////////////////////////////////////
1256    size_type min_check = n_block_a == n_block_left ? 0u : n_block_a;
1257    size_type max_check = min_value<size_type>(min_check+size_type(1), n_block_left);
1258    for (; n_block_left; --n_block_left, ++key_range2, min_check -= min_check != 0, max_check -= max_check != 0) {
1259       size_type const next_key_idx = find_next_block(key_range2, key_comp, first2, l_block, min_check, max_check, comp);
1260       max_check = min_value<size_type>(max_value<size_type>(max_check, next_key_idx+size_type(2)), n_block_left);
1261       RandIt       first_min = first2 + next_key_idx*l_block;
1262       RandIt const last_min  = first_min + l_block;
1263       boost::ignore_unused(last_min);
1264       RandIt const last2  = first2 + l_block;
1265 
1266       bool const buffer_empty = buffer == buffer_end;
1267       boost::ignore_unused(buffer_empty);
1268       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(buffer_empty ? boost::movelib::is_sorted(first1, last1, comp) : boost::movelib::is_sorted(buffer, buffer_end, comp));
1269       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first2, last2, comp));
1270       BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!n_block_left || boost::movelib::is_sorted(first_min, last_min, comp));
1271 
1272       //Check if irregular b block should go here.
1273       //If so, break to the special code handling the irregular block
1274       if (!n_block_b_left &&
1275             ( (l_irreg2 && comp(*first_irr2, *first_min)) || (!l_irreg2 && is_range1_A)) ){
1276          break;
1277       }
1278 
1279       RandItKeys const key_next(key_range2 + next_key_idx);
1280       bool const is_range2_A = key_mid == (key_first+key_count) || key_comp(*key_next, *key_mid);
1281 
1282       if(is_range1_A == is_range2_A){
1283          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT((first1 == last1) || (buffer_empty ? !comp(*first_min, last1[-1]) : !comp(*first_min, buffer_end[-1])));
1284          //If buffered, put those elements in place
1285          RandIt res = op(forward_t(), buffer, buffer_end, first1);
1286          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_fwd: ", len);
1287          buffer    = buffer_end = buf_first;
1288          BOOST_ASSERT(buffer_empty || res == last1);
1289          boost::ignore_unused(res);
1290          //swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1291          buffer_end = buffer_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min, buffer = buf_first, op);
1292          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_swp: ", len);
1293          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first2, last2, comp));
1294          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_min, last_min, comp));
1295          first1 = first2;
1296          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, first1, comp));
1297       }
1298       else {
1299          RandIt const unmerged = op_partial_merge_and_save(first1, last1, first2, last2, first_min, buffer, buffer_end, comp, op, is_range1_A);
1300          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_mrs: ", len);
1301          bool const is_range_1_empty = buffer == buffer_end;
1302          BOOST_ASSERT(is_range_1_empty || (buffer_end-buffer) == (last1+l_block-unmerged));
1303          if(is_range_1_empty){
1304             buffer    = buffer_end = buf_first;
1305             first_min = last_min - (last2 - first2);
1306             //swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1307             buffer_end = buffer_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min, buf_first, op);
1308          }
1309          else{
1310             first_min = last_min;
1311             //swap_and_update_key(key_next, key_range2, key_mid, first2, last2, first_min);
1312             update_key(key_next, key_range2, key_mid);
1313          }
1314          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(!is_range_1_empty || (last_min-first_min) == (last2-unmerged));
1315          BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_swp: ", len);
1316          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first_min, last_min, comp));
1317          is_range1_A ^= is_range_1_empty;
1318          first1 = unmerged;
1319          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, unmerged, comp));
1320       }
1321       BOOST_ASSERT( (is_range2_A && n_block_a_left) || (!is_range2_A && n_block_b_left));
1322       is_range2_A ? --n_block_a_left : --n_block_b_left;
1323       last1 += l_block;
1324       first2 = last2;
1325    }
1326    RandIt res = op(forward_t(), buffer, buffer_end, first1);
1327    boost::ignore_unused(res);
1328    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, res, comp));
1329    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_fwd: ", len);
1330 
1331    ////////////////////////////////////////////////////////////////////////////
1332    //Process irregular B block and remaining A blocks
1333    ////////////////////////////////////////////////////////////////////////////
1334    RandIt const last_irr2 = first_irr2 + l_irreg2;
1335    op(forward_t(), first_irr2, first_irr2+l_irreg2, buf_first);
1336    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_fwir:", len);
1337    buffer = buf_first;
1338    buffer_end = buffer+l_irreg2;
1339 
1340    reverse_iterator<RandItBuf> rbuf_beg(buffer_end);
1341    RandIt dest = op_merge_blocks_with_irreg
1342       ((make_reverse_iterator)(key_first + n_block_b + n_block_a), (make_reverse_iterator)(key_mid), inverse<KeyCompare>(key_comp)
1343       , (make_reverse_iterator)(first_irr2), rbuf_beg, (make_reverse_iterator)(buffer), (make_reverse_iterator)(last_irr2)
1344       , l_block, n_block_left, 0, n_block_left
1345       , inverse<Compare>(comp), true, op).base();
1346    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(dest, last_irr2, comp));
1347    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_blocks_w_irg: ", len);
1348 
1349    buffer_end = rbuf_beg.base();
1350    BOOST_ASSERT((dest-last1) == (buffer_end-buffer));
1351    op_merge_with_left_placed(is_range1_A ? first1 : last1, last1, dest, buffer, buffer_end, comp, op);
1352    BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2("   merge_with_left_plc:", len);
1353    BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(first, last_irr2, comp));
1354 }
1355 
1356 //////////////////////////////////
1357 //////////////////////////////////
1358 //////////////////////////////////
1359 //
1360 //  op_insertion_sort_step_left/right
1361 //
1362 //////////////////////////////////
1363 //////////////////////////////////
1364 //////////////////////////////////
1365 
1366 template<class RandIt, class Compare, class Op>
1367 typename iterator_traits<RandIt>::size_type
op_insertion_sort_step_left(RandIt const first,typename iterator_traits<RandIt>::size_type const length,typename iterator_traits<RandIt>::size_type const step,Compare comp,Op op)1368    op_insertion_sort_step_left
1369       ( RandIt const first
1370       , typename iterator_traits<RandIt>::size_type const length
1371       , typename iterator_traits<RandIt>::size_type const step
1372       , Compare comp, Op op)
1373 {
1374    typedef typename iterator_traits<RandIt>::size_type size_type;
1375    size_type const s = min_value<size_type>(step, AdaptiveSortInsertionSortThreshold);
1376    size_type m = 0;
1377 
1378    while((length - m) > s){
1379       insertion_sort_op(first+m, first+m+s, first+m-s, comp, op);
1380       m += s;
1381    }
1382    insertion_sort_op(first+m, first+length, first+m-s, comp, op);
1383    return s;
1384 }
1385 
1386 template<class RandIt, class Compare, class Op>
op_merge_right_step_once(RandIt first_block,typename iterator_traits<RandIt>::size_type const elements_in_blocks,typename iterator_traits<RandIt>::size_type const l_build_buf,Compare comp,Op op)1387 void op_merge_right_step_once
1388       ( RandIt first_block
1389       , typename iterator_traits<RandIt>::size_type const elements_in_blocks
1390       , typename iterator_traits<RandIt>::size_type const l_build_buf
1391       , Compare comp
1392       , Op op)
1393 {
1394    typedef typename iterator_traits<RandIt>::size_type size_type;
1395    size_type restk = elements_in_blocks%(2*l_build_buf);
1396    size_type p = elements_in_blocks - restk;
1397    BOOST_ASSERT(0 == (p%(2*l_build_buf)));
1398 
1399    if(restk <= l_build_buf){
1400       op(backward_t(),first_block+p, first_block+p+restk, first_block+p+restk+l_build_buf);
1401    }
1402    else{
1403       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);
1404    }
1405    while(p>0){
1406       p -= 2*l_build_buf;
1407       op_merge_right(first_block+p, first_block+p+l_build_buf, first_block+p+2*l_build_buf, first_block+p+3*l_build_buf, comp, op);
1408    }
1409 }
1410 
1411 
1412 //////////////////////////////////
1413 //////////////////////////////////
1414 //////////////////////////////////
1415 //
1416 //    insertion_sort_step
1417 //
1418 //////////////////////////////////
1419 //////////////////////////////////
1420 //////////////////////////////////
1421 template<class RandIt, class Compare>
1422 typename iterator_traits<RandIt>::size_type
insertion_sort_step(RandIt const first,typename iterator_traits<RandIt>::size_type const length,typename iterator_traits<RandIt>::size_type const step,Compare comp)1423    insertion_sort_step
1424       ( RandIt const first
1425       , typename iterator_traits<RandIt>::size_type const length
1426       , typename iterator_traits<RandIt>::size_type const step
1427       , Compare comp)
1428 {
1429    typedef typename iterator_traits<RandIt>::size_type size_type;
1430    size_type const s = min_value<size_type>(step, AdaptiveSortInsertionSortThreshold);
1431    size_type m = 0;
1432 
1433    while((length - m) > s){
1434       insertion_sort(first+m, first+m+s, comp);
1435       m += s;
1436    }
1437    insertion_sort(first+m, first+length, comp);
1438    return s;
1439 }
1440 
1441 //////////////////////////////////
1442 //////////////////////////////////
1443 //////////////////////////////////
1444 //
1445 //    op_merge_left_step_multiple
1446 //
1447 //////////////////////////////////
1448 //////////////////////////////////
1449 //////////////////////////////////
1450 template<class RandIt, class Compare, class Op>
1451 typename iterator_traits<RandIt>::size_type
op_merge_left_step_multiple(RandIt first_block,typename iterator_traits<RandIt>::size_type const elements_in_blocks,typename iterator_traits<RandIt>::size_type l_merged,typename iterator_traits<RandIt>::size_type const l_build_buf,typename iterator_traits<RandIt>::size_type l_left_space,Compare comp,Op op)1452    op_merge_left_step_multiple
1453       ( RandIt first_block
1454       , typename iterator_traits<RandIt>::size_type const elements_in_blocks
1455       , typename iterator_traits<RandIt>::size_type l_merged
1456       , typename iterator_traits<RandIt>::size_type const l_build_buf
1457       , typename iterator_traits<RandIt>::size_type l_left_space
1458       , Compare comp
1459       , Op op)
1460 {
1461    typedef typename iterator_traits<RandIt>::size_type size_type;
1462    for(; l_merged < l_build_buf && l_left_space >= l_merged; l_merged*=2){
1463       size_type p0=0;
1464       RandIt pos = first_block;
1465       while((elements_in_blocks - p0) > 2*l_merged) {
1466          op_merge_left(pos-l_merged, pos, pos+l_merged, pos+2*l_merged, comp, op);
1467          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(pos-l_merged, pos+l_merged, comp));
1468          p0 += 2*l_merged;
1469          pos = first_block+p0;
1470       }
1471       if((elements_in_blocks-p0) > l_merged) {
1472          op_merge_left(pos-l_merged, pos, pos+l_merged, first_block+elements_in_blocks, comp, op);
1473          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(pos-l_merged, pos-l_merged+(first_block+elements_in_blocks-pos), comp));
1474       }
1475       else {
1476          op(forward_t(), pos, first_block+elements_in_blocks, pos-l_merged);
1477          BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(pos-l_merged, first_block+elements_in_blocks-l_merged, comp));
1478       }
1479       first_block -= l_merged;
1480       l_left_space -= l_merged;
1481    }
1482    return l_merged;
1483 }
1484 
1485 
1486 }  //namespace detail_adaptive {
1487 }  //namespace movelib {
1488 }  //namespace boost {
1489 
1490 #include <boost/move/detail/config_end.hpp>
1491 
1492 #endif   //#define BOOST_MOVE_ADAPTIVE_SORT_MERGE_HPP
1493