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