File indexing completed on 2025-07-11 08:16:05
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #ifndef BOOST_MOVE_ADAPTIVE_SORT_HPP
0013 #define BOOST_MOVE_ADAPTIVE_SORT_HPP
0014
0015 #include <boost/move/detail/config_begin.hpp>
0016
0017 #include <boost/move/algo/detail/adaptive_sort_merge.hpp>
0018 #include <cassert>
0019
0020 #if defined(BOOST_CLANG) || (defined(BOOST_GCC) && (BOOST_GCC >= 40600))
0021 #pragma GCC diagnostic push
0022 #pragma GCC diagnostic ignored "-Wsign-conversion"
0023 #pragma GCC diagnostic ignored "-Wconversion"
0024 #endif
0025
0026 namespace boost {
0027 namespace movelib {
0028
0029
0030 namespace detail_adaptive {
0031
0032 template<class RandIt>
0033 void move_data_backward( RandIt cur_pos
0034 , typename iter_size<RandIt>::type const l_data
0035 , RandIt new_pos
0036 , bool const xbuf_used)
0037 {
0038
0039 if(xbuf_used){
0040 boost::move_backward(cur_pos, cur_pos+l_data, new_pos+l_data);
0041 }
0042 else{
0043 boost::adl_move_swap_ranges_backward(cur_pos, cur_pos+l_data, new_pos+l_data);
0044
0045
0046 }
0047 }
0048
0049 template<class RandIt>
0050 void move_data_forward( RandIt cur_pos
0051 , typename iter_size<RandIt>::type const l_data
0052 , RandIt new_pos
0053 , bool const xbuf_used)
0054 {
0055
0056 if(xbuf_used){
0057 boost::move(cur_pos, cur_pos+l_data, new_pos);
0058 }
0059 else{
0060 boost::adl_move_swap_ranges(cur_pos, cur_pos+l_data, new_pos);
0061
0062
0063 }
0064 }
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086 template<class RandIt, class Compare, class XBuf>
0087 typename iter_size<RandIt>::type
0088 adaptive_sort_build_blocks
0089 ( RandIt const first
0090 , typename iter_size<RandIt>::type const len
0091 , typename iter_size<RandIt>::type const l_base
0092 , typename iter_size<RandIt>::type const l_build_buf
0093 , XBuf & xbuf
0094 , Compare comp)
0095 {
0096 typedef typename iter_size<RandIt>::type size_type;
0097 assert(l_build_buf <= len);
0098 assert(0 == ((l_build_buf / l_base)&(l_build_buf/l_base-1)));
0099
0100
0101 RandIt first_block = first + l_build_buf;
0102 size_type const elements_in_blocks = size_type(len - l_build_buf);
0103
0104
0105
0106
0107 size_type l_merged = 0u;
0108
0109 assert(l_build_buf);
0110
0111 size_type kbuf = min_value<size_type>(l_build_buf, size_type(xbuf.capacity()));
0112 kbuf = kbuf < l_base ? 0 : kbuf;
0113
0114 if(kbuf){
0115
0116 xbuf.move_assign(first+l_build_buf-kbuf, kbuf);
0117 l_merged = op_insertion_sort_step_left(first_block, elements_in_blocks, l_base, comp, move_op());
0118
0119
0120
0121 l_merged = op_merge_left_step_multiple
0122 ( first_block - l_merged, elements_in_blocks, l_merged, l_build_buf, size_type(kbuf - l_merged), comp, move_op());
0123
0124
0125
0126 if(kbuf != l_build_buf){
0127 boost::move(xbuf.data()+kbuf-l_merged, xbuf.data() + kbuf, first_block-l_merged+elements_in_blocks);
0128 }
0129 }
0130 else{
0131 l_merged = insertion_sort_step(first_block, elements_in_blocks, l_base, comp);
0132 rotate_gcd(first_block-l_merged, first_block, first_block+elements_in_blocks);
0133 }
0134
0135
0136
0137 l_merged = op_merge_left_step_multiple
0138 (first_block-l_merged, elements_in_blocks, l_merged, l_build_buf, size_type(l_build_buf - l_merged), comp, swap_op());
0139
0140 assert(l_merged == l_build_buf);
0141
0142
0143
0144
0145
0146
0147
0148 if(kbuf && kbuf == l_build_buf){
0149 op_merge_right_step_once(first, elements_in_blocks, l_build_buf, comp, move_op());
0150
0151
0152 boost::move(xbuf.data(), xbuf.data() + kbuf, first);
0153 }
0154 else{
0155 op_merge_right_step_once(first, elements_in_blocks, l_build_buf, comp, swap_op());
0156 }
0157 xbuf.clear();
0158
0159 return min_value<size_type>(elements_in_blocks, size_type(2u*l_build_buf));
0160 }
0161
0162 template<class RandItKeys, class KeyCompare, class RandIt, class Compare, class XBuf>
0163 void adaptive_sort_combine_blocks
0164 ( RandItKeys const keys
0165 , KeyCompare key_comp
0166 , RandIt const first
0167 , typename iter_size<RandIt>::type const len
0168 , typename iter_size<RandIt>::type const l_prev_merged
0169 , typename iter_size<RandIt>::type const l_block
0170 , bool const use_buf
0171 , bool const xbuf_used
0172 , XBuf & xbuf
0173 , Compare comp
0174 , bool merge_left)
0175 {
0176 boost::movelib::ignore(xbuf);
0177 typedef typename iter_size<RandIt>::type size_type;
0178
0179 size_type const l_reg_combined = size_type(2u*l_prev_merged);
0180 size_type l_irreg_combined = 0;
0181 size_type const l_total_combined = calculate_total_combined(len, l_prev_merged, &l_irreg_combined);
0182 size_type const n_reg_combined = len/l_reg_combined;
0183 RandIt combined_first = first;
0184
0185 boost::movelib::ignore(l_total_combined);
0186 assert(l_total_combined <= len);
0187
0188 size_type const max_i = size_type(n_reg_combined + (l_irreg_combined != 0));
0189
0190 if(merge_left || !use_buf) {
0191 for( size_type combined_i = 0; combined_i != max_i; ) {
0192
0193 bool const is_last = combined_i==n_reg_combined;
0194 size_type const l_cur_combined = is_last ? l_irreg_combined : l_reg_combined;
0195
0196 range_xbuf<RandIt, size_type, move_op> rbuf( (use_buf && xbuf_used) ? (combined_first-l_block) : combined_first, combined_first);
0197 size_type n_block_a, n_block_b, l_irreg1, l_irreg2;
0198 combine_params( keys, key_comp, l_cur_combined
0199 , l_prev_merged, l_block, rbuf
0200 , n_block_a, n_block_b, l_irreg1, l_irreg2);
0201 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" A combpar: ", len + l_block);
0202 BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(combined_first, combined_first + n_block_a*l_block+l_irreg1, comp));
0203 BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(combined_first + n_block_a*l_block+l_irreg1, combined_first + n_block_a*l_block+l_irreg1+n_block_b*l_block+l_irreg2, comp));
0204 if(!use_buf){
0205 merge_blocks_bufferless
0206 (keys, key_comp, combined_first, l_block, 0u, n_block_a, n_block_b, l_irreg2, comp);
0207 }
0208 else{
0209 merge_blocks_left
0210 (keys, key_comp, combined_first, l_block, 0u, n_block_a, n_block_b, l_irreg2, comp, xbuf_used);
0211 }
0212 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" After merge_blocks_L: ", len + l_block);
0213 ++combined_i;
0214 if(combined_i != max_i)
0215 combined_first += l_reg_combined;
0216 }
0217 }
0218 else{
0219 combined_first += size_type(l_reg_combined*(max_i-1u));
0220 for( size_type combined_i = max_i; combined_i; ) {
0221 --combined_i;
0222 bool const is_last = combined_i==n_reg_combined;
0223 size_type const l_cur_combined = is_last ? l_irreg_combined : l_reg_combined;
0224
0225 RandIt const combined_last(combined_first+l_cur_combined);
0226 range_xbuf<RandIt, size_type, move_op> rbuf(combined_last, xbuf_used ? (combined_last+l_block) : combined_last);
0227 size_type n_block_a, n_block_b, l_irreg1, l_irreg2;
0228 combine_params( keys, key_comp, l_cur_combined
0229 , l_prev_merged, l_block, rbuf
0230 , n_block_a, n_block_b, l_irreg1, l_irreg2);
0231 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" A combpar: ", len + l_block);
0232 BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(combined_first, combined_first + n_block_a*l_block+l_irreg1, comp));
0233 BOOST_MOVE_ADAPTIVE_SORT_INVARIANT(boost::movelib::is_sorted(combined_first + n_block_a*l_block+l_irreg1, combined_first + n_block_a*l_block+l_irreg1+n_block_b*l_block+l_irreg2, comp));
0234 merge_blocks_right
0235 (keys, key_comp, combined_first, l_block, n_block_a, n_block_b, l_irreg2, comp, xbuf_used);
0236 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" After merge_blocks_R: ", len + l_block);
0237 if(combined_i)
0238 combined_first -= l_reg_combined;
0239 }
0240 }
0241 }
0242
0243
0244
0245
0246 template<class RandIt, class Compare, class XBuf>
0247 bool adaptive_sort_combine_all_blocks
0248 ( RandIt keys
0249 , typename iter_size<RandIt>::type &n_keys
0250 , RandIt const buffer
0251 , typename iter_size<RandIt>::type const l_buf_plus_data
0252 , typename iter_size<RandIt>::type l_merged
0253 , typename iter_size<RandIt>::type &l_intbuf
0254 , XBuf & xbuf
0255 , Compare comp)
0256 {
0257 typedef typename iter_size<RandIt>::type size_type;
0258
0259 RandIt const first = buffer + l_intbuf;
0260 size_type const l_data = size_type(l_buf_plus_data - l_intbuf);
0261 size_type const l_unique = size_type(l_intbuf + n_keys);
0262
0263 bool const common_xbuf = l_data > l_merged && l_intbuf && l_intbuf <= xbuf.capacity();
0264 if(common_xbuf){
0265 xbuf.move_assign(buffer, l_intbuf);
0266 }
0267
0268 bool prev_merge_left = true;
0269 size_type l_prev_total_combined = l_merged, l_prev_block = 0;
0270 bool prev_use_internal_buf = true;
0271
0272 for( size_type n = 0; l_data > l_merged
0273 ; l_merged = size_type(2u*l_merged)
0274 , ++n){
0275
0276
0277
0278
0279
0280 bool use_internal_buf = false;
0281 size_type const l_block = lblock_for_combine(l_intbuf, n_keys, size_type(2*l_merged), use_internal_buf);
0282 assert(!l_intbuf || (l_block == l_intbuf));
0283 assert(n == 0 || (!use_internal_buf || prev_use_internal_buf) );
0284 assert(n == 0 || (!use_internal_buf || l_prev_block == l_block) );
0285
0286 bool const is_merge_left = (n&1) == 0;
0287 size_type const l_total_combined = calculate_total_combined(l_data, l_merged);
0288 if(n && prev_use_internal_buf && prev_merge_left){
0289 if(is_merge_left || !use_internal_buf){
0290 move_data_backward(first-l_prev_block, l_prev_total_combined, first, common_xbuf);
0291 }
0292 else{
0293
0294 RandIt const buf_end = first+l_prev_total_combined;
0295 RandIt const buf_beg = buf_end-l_block;
0296 if(l_prev_total_combined > l_total_combined){
0297 size_type const l_diff = size_type(l_prev_total_combined - l_total_combined);
0298 move_data_backward(buf_beg-l_diff, l_diff, buf_end-l_diff, common_xbuf);
0299 }
0300 else if(l_prev_total_combined < l_total_combined){
0301 size_type const l_diff = size_type(l_total_combined - l_prev_total_combined);
0302 move_data_forward(buf_end, l_diff, buf_beg, common_xbuf);
0303 }
0304 }
0305 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L2(" After move_data : ", l_data + l_intbuf);
0306 }
0307
0308
0309 if(n_keys){
0310 size_type upper_n_keys_this_iter = size_type(2u*l_merged/l_block);
0311 if(upper_n_keys_this_iter > 256){
0312 adaptive_sort_combine_blocks
0313 ( keys, comp, !use_internal_buf || is_merge_left ? first : first-l_block
0314 , l_data, l_merged, l_block, use_internal_buf, common_xbuf, xbuf, comp, is_merge_left);
0315 }
0316 else{
0317 unsigned char uint_keys[256];
0318 adaptive_sort_combine_blocks
0319 ( uint_keys, less(), !use_internal_buf || is_merge_left ? first : first-l_block
0320 , l_data, l_merged, l_block, use_internal_buf, common_xbuf, xbuf, comp, is_merge_left);
0321 }
0322 }
0323 else{
0324 size_type *const uint_keys = xbuf.template aligned_trailing<size_type>();
0325 adaptive_sort_combine_blocks
0326 ( uint_keys, less(), !use_internal_buf || is_merge_left ? first : first-l_block
0327 , l_data, l_merged, l_block, use_internal_buf, common_xbuf, xbuf, comp, is_merge_left);
0328 }
0329
0330 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(is_merge_left ? " After comb blocks L: " : " After comb blocks R: ", l_data + l_intbuf);
0331 prev_merge_left = is_merge_left;
0332 l_prev_total_combined = l_total_combined;
0333 l_prev_block = l_block;
0334 prev_use_internal_buf = use_internal_buf;
0335 }
0336 assert(l_prev_total_combined == l_data);
0337 bool const buffer_right = prev_use_internal_buf && prev_merge_left;
0338
0339 l_intbuf = prev_use_internal_buf ? l_prev_block : 0u;
0340 n_keys = size_type(l_unique - l_intbuf);
0341
0342 if(common_xbuf){
0343 if(buffer_right){
0344 boost::move(xbuf.data(), xbuf.data() + l_intbuf, buffer+l_data);
0345 }
0346 else{
0347 boost::move(xbuf.data(), xbuf.data() + l_intbuf, buffer);
0348 }
0349 }
0350 return buffer_right;
0351 }
0352
0353
0354 template<class RandIt, class Compare, class XBuf>
0355 void adaptive_sort_final_merge( bool buffer_right
0356 , RandIt const first
0357 , typename iter_size<RandIt>::type const l_intbuf
0358 , typename iter_size<RandIt>::type const n_keys
0359 , typename iter_size<RandIt>::type const len
0360 , XBuf & xbuf
0361 , Compare comp)
0362 {
0363
0364 xbuf.clear();
0365
0366 typedef typename iter_size<RandIt>::type size_type;
0367
0368 size_type const n_key_plus_buf = size_type(l_intbuf+n_keys);
0369 if(buffer_right){
0370
0371 stable_sort(first+len-l_intbuf, first+len, comp, xbuf);
0372 stable_merge( first+n_keys, first+len-l_intbuf, first+len, antistable<Compare>(comp), xbuf);
0373 unstable_sort(first, first+n_keys, comp, xbuf);
0374 stable_merge(first, first+n_keys, first+len, comp, xbuf);
0375 }
0376 else{
0377
0378 stable_sort(first, first+n_key_plus_buf, comp, xbuf);
0379 if(xbuf.capacity() >= n_key_plus_buf){
0380 buffered_merge(first, first+n_key_plus_buf, first+len, comp, xbuf);
0381 }
0382 else if(xbuf.capacity() >= min_value<size_type>(l_intbuf, n_keys)){
0383 stable_merge( first+n_keys, first+n_key_plus_buf
0384 , first+len, comp, xbuf);
0385 stable_merge(first, first+n_keys, first+len, comp, xbuf);
0386 }
0387 else{
0388 stable_merge(first, first+n_key_plus_buf, first+len, comp, xbuf);
0389 }
0390 }
0391 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(" After final_merge : ", len);
0392 }
0393
0394 template<class RandIt, class Compare, class Unsigned, class XBuf>
0395 bool adaptive_sort_build_params
0396 (RandIt first, Unsigned const len, Compare comp
0397 , Unsigned &n_keys, Unsigned &l_intbuf, Unsigned &l_base, Unsigned &l_build_buf
0398 , XBuf & xbuf
0399 )
0400 {
0401 typedef typename iter_size<RandIt>::type size_type;
0402
0403
0404 l_base = 0u;
0405
0406
0407
0408
0409
0410
0411
0412 l_intbuf = size_type(ceil_sqrt_multiple(len, &l_base));
0413
0414
0415 while(xbuf.capacity() >= l_intbuf*2){
0416 l_intbuf = size_type(2u*l_intbuf);
0417 }
0418
0419
0420
0421
0422 size_type n_min_ideal_keys = size_type(l_intbuf-1u);
0423 while(n_min_ideal_keys >= (len-l_intbuf-n_min_ideal_keys)/l_intbuf){
0424 --n_min_ideal_keys;
0425 }
0426 ++n_min_ideal_keys;
0427 assert(n_min_ideal_keys <= l_intbuf);
0428
0429 if(xbuf.template supports_aligned_trailing<size_type>
0430 (l_intbuf, size_type((size_type(len-l_intbuf)-1u)/l_intbuf+1u))){
0431 n_keys = 0u;
0432 l_build_buf = l_intbuf;
0433 }
0434 else{
0435
0436
0437
0438
0439
0440
0441
0442 bool const non_unique_buf = xbuf.capacity() >= l_intbuf;
0443 size_type const to_collect = non_unique_buf ? n_min_ideal_keys : size_type(l_intbuf*2u);
0444 size_type collected = collect_unique(first, first+len, to_collect, comp, xbuf);
0445
0446
0447
0448 if(non_unique_buf && collected == n_min_ideal_keys){
0449 l_build_buf = l_intbuf;
0450 n_keys = n_min_ideal_keys;
0451 }
0452 else if(collected == 2*l_intbuf){
0453
0454 l_build_buf = size_type(l_intbuf*2);
0455 n_keys = l_intbuf;
0456 }
0457 else if(collected >= (n_min_ideal_keys+l_intbuf)){
0458 l_build_buf = l_intbuf;
0459 n_keys = size_type(collected - l_intbuf);
0460 }
0461
0462
0463 else{
0464 assert(collected < (n_min_ideal_keys+l_intbuf));
0465 if(collected < 4){
0466 return false;
0467 }
0468 n_keys = l_intbuf;
0469 while(n_keys & (n_keys-1u)){
0470 n_keys &= size_type(n_keys-1u);
0471 }
0472 while(n_keys > collected){
0473 n_keys/=2;
0474 }
0475
0476 l_base = min_value<Unsigned>(n_keys, AdaptiveSortInsertionSortThreshold);
0477 l_intbuf = 0;
0478 l_build_buf = n_keys;
0479 }
0480 assert((n_keys+l_intbuf) >= l_build_buf);
0481 }
0482
0483 return true;
0484 }
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509
0510
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539
0540
0541
0542 template<class RandIt, class Compare, class XBuf>
0543 void adaptive_sort_impl
0544 ( RandIt first
0545 , typename iter_size<RandIt>::type const len
0546 , Compare comp
0547 , XBuf & xbuf
0548 )
0549 {
0550 typedef typename iter_size<RandIt>::type size_type;
0551
0552
0553 if(len <= size_type(AdaptiveSortInsertionSortThreshold)){
0554 insertion_sort(first, first + len, comp);
0555 }
0556 else if((len-len/2) <= xbuf.capacity()){
0557 merge_sort(first, first+len, comp, xbuf.data());
0558 }
0559 else{
0560
0561 BOOST_MOVE_STATIC_ASSERT(AdaptiveSortInsertionSortThreshold >= 4);
0562
0563 size_type l_base = 0;
0564 size_type l_intbuf = 0;
0565 size_type n_keys = 0;
0566 size_type l_build_buf = 0;
0567
0568
0569
0570 if(!adaptive_sort_build_params(first, len, comp, n_keys, l_intbuf, l_base, l_build_buf, xbuf)){
0571 stable_sort(first, first+len, comp, xbuf);
0572 }
0573 else{
0574 assert(l_build_buf);
0575
0576 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1("\n After collect_unique: ", len);
0577 size_type const n_key_plus_buf = size_type(l_intbuf+n_keys);
0578
0579 assert(l_intbuf || (0 == (l_build_buf & (l_build_buf-1))));
0580
0581
0582 size_type const l_merged = adaptive_sort_build_blocks
0583 ( first + n_key_plus_buf-l_build_buf
0584 , size_type(len-n_key_plus_buf+l_build_buf)
0585 , l_base, l_build_buf, xbuf, comp);
0586 BOOST_MOVE_ADAPTIVE_SORT_PRINT_L1(" After build_blocks: ", len);
0587
0588
0589 bool const buffer_right = adaptive_sort_combine_all_blocks
0590 (first, n_keys, first+n_keys, size_type(len-n_keys), l_merged, l_intbuf, xbuf, comp);
0591
0592
0593 adaptive_sort_final_merge(buffer_right, first, l_intbuf, n_keys, len, xbuf, comp);
0594 }
0595 }
0596 }
0597
0598 }
0599
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624
0625
0626
0627 template<class RandIt, class RandRawIt, class Compare>
0628 void adaptive_sort( RandIt first, RandIt last, Compare comp
0629 , RandRawIt uninitialized
0630 , typename iter_size<RandIt>::type uninitialized_len)
0631 {
0632 typedef typename iter_size<RandIt>::type size_type;
0633 typedef typename iterator_traits<RandIt>::value_type value_type;
0634
0635 ::boost::movelib::adaptive_xbuf<value_type, RandRawIt, size_type> xbuf(uninitialized, uninitialized_len);
0636 ::boost::movelib::detail_adaptive::adaptive_sort_impl(first, size_type(last - first), comp, xbuf);
0637 }
0638
0639 template<class RandIt, class Compare>
0640 void adaptive_sort( RandIt first, RandIt last, Compare comp)
0641 {
0642 typedef typename iterator_traits<RandIt>::value_type value_type;
0643 adaptive_sort(first, last, comp, (value_type*)0, 0u);
0644 }
0645
0646 }
0647 }
0648
0649 #include <boost/move/detail/config_end.hpp>
0650
0651 #if defined(BOOST_CLANG) || (defined(BOOST_GCC) && (BOOST_GCC >= 40600))
0652 #pragma GCC diagnostic pop
0653 #endif
0654
0655 #endif