File indexing completed on 2024-04-28 04:36:21
0001 /* 0002 * C++ implementation of timsort 0003 * 0004 * ported from Python's and OpenJDK's: 0005 * - http://svn.python.org/projects/python/trunk/Objects/listobject.c 0006 * - http://cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/raw_files/new/src/share/classes/java/util/TimSort.java 0007 * 0008 * Copyright (c) 2011 Fuji, Goro (gfx) <gfuji@cpan.org>. 0009 * Copyright (c) 2019-2021 Morwenn. 0010 * Copyright (c) 2021 Igor Kushnir <igorkuo@gmail.com>. 0011 * 0012 * Permission is hereby granted, free of charge, to any person obtaining a copy 0013 * of this software and associated documentation files (the "Software"), to 0014 * deal in the Software without restriction, including without limitation the 0015 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 0016 * sell copies of the Software, and to permit persons to whom the Software is 0017 * furnished to do so, subject to the following conditions: 0018 * 0019 * The above copyright notice and this permission notice shall be included in 0020 * all copies or substantial portions of the Software. 0021 * 0022 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0023 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0024 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0025 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 0026 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 0027 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 0028 * IN THE SOFTWARE. 0029 */ 0030 0031 #ifndef GFX_TIMSORT_HPP 0032 #define GFX_TIMSORT_HPP 0033 0034 #include <algorithm> 0035 #include <functional> 0036 #include <iterator> 0037 #include <utility> 0038 #include <vector> 0039 0040 // Semantic versioning macros 0041 0042 #define GFX_TIMSORT_VERSION_MAJOR 2 0043 #define GFX_TIMSORT_VERSION_MINOR 1 0044 #define GFX_TIMSORT_VERSION_PATCH 0 0045 0046 // Diagnostic selection macros 0047 0048 #if defined(GFX_TIMSORT_ENABLE_ASSERT) || defined(GFX_TIMSORT_ENABLE_AUDIT) 0049 # include <cassert> 0050 #endif 0051 0052 #ifdef GFX_TIMSORT_ENABLE_ASSERT 0053 # define GFX_TIMSORT_ASSERT(expr) assert(expr) 0054 #else 0055 # define GFX_TIMSORT_ASSERT(expr) ((void)0) 0056 #endif 0057 0058 #ifdef GFX_TIMSORT_ENABLE_AUDIT 0059 # define GFX_TIMSORT_AUDIT(expr) assert(expr) 0060 #else 0061 # define GFX_TIMSORT_AUDIT(expr) ((void)0) 0062 #endif 0063 0064 #ifdef GFX_TIMSORT_ENABLE_LOG 0065 # include <iostream> 0066 # define GFX_TIMSORT_LOG(expr) (std::clog << "# " << __func__ << ": " << expr << std::endl) 0067 #else 0068 # define GFX_TIMSORT_LOG(expr) ((void)0) 0069 #endif 0070 0071 0072 namespace gfx { 0073 0074 // --------------------------------------- 0075 // Implementation details 0076 // --------------------------------------- 0077 0078 namespace detail { 0079 0080 // Equivalent to C++20 std::identity 0081 struct identity { 0082 template <typename T> 0083 constexpr T&& operator()(T&& value) const noexcept 0084 { 0085 return std::forward<T>(value); 0086 } 0087 }; 0088 0089 // Merge a predicate and a projection function 0090 template <typename Compare, typename Projection> 0091 struct projection_compare { 0092 projection_compare(Compare comp, Projection proj) : compare(comp), projection(proj) { 0093 } 0094 0095 template <typename T, typename U> 0096 bool operator()(T &&lhs, U &&rhs) { 0097 #ifdef __cpp_lib_invoke 0098 return static_cast<bool>(std::invoke(compare, 0099 std::invoke(projection, std::forward<T>(lhs)), 0100 std::invoke(projection, std::forward<U>(rhs)) 0101 )); 0102 #else 0103 return static_cast<bool>(compare( 0104 projection(std::forward<T>(lhs)), 0105 projection(std::forward<U>(rhs)) 0106 )); 0107 #endif 0108 } 0109 0110 Compare compare; 0111 Projection projection; 0112 }; 0113 0114 template <typename Iterator> struct run { 0115 typedef typename std::iterator_traits<Iterator>::difference_type diff_t; 0116 0117 Iterator base; 0118 diff_t len; 0119 0120 run(Iterator b, diff_t l) : base(b), len(l) { 0121 } 0122 }; 0123 0124 template <typename RandomAccessIterator, typename Compare> class TimSort { 0125 typedef RandomAccessIterator iter_t; 0126 typedef typename std::iterator_traits<iter_t>::value_type value_t; 0127 typedef typename std::iterator_traits<iter_t>::reference ref_t; 0128 typedef typename std::iterator_traits<iter_t>::difference_type diff_t; 0129 0130 static const int MIN_MERGE = 32; 0131 static const int MIN_GALLOP = 7; 0132 0133 int minGallop_; // default to MIN_GALLOP 0134 0135 std::vector<value_t> tmp_; // temp storage for merges 0136 typedef typename std::vector<value_t>::iterator tmp_iter_t; 0137 0138 std::vector<run<RandomAccessIterator> > pending_; 0139 0140 static void binarySort(iter_t const lo, iter_t const hi, iter_t start, Compare compare) { 0141 GFX_TIMSORT_ASSERT(lo <= start); 0142 GFX_TIMSORT_ASSERT(start <= hi); 0143 if (start == lo) { 0144 ++start; 0145 } 0146 for (; start < hi; ++start) { 0147 GFX_TIMSORT_ASSERT(lo <= start); 0148 value_t pivot = std::move(*start); 0149 0150 iter_t const pos = std::upper_bound(lo, start, pivot, compare); 0151 for (iter_t p = start; p > pos; --p) { 0152 *p = std::move(*(p - 1)); 0153 } 0154 *pos = std::move(pivot); 0155 } 0156 } 0157 0158 static diff_t countRunAndMakeAscending(iter_t const lo, iter_t const hi, Compare compare) { 0159 GFX_TIMSORT_ASSERT(lo < hi); 0160 0161 iter_t runHi = lo + 1; 0162 if (runHi == hi) { 0163 return 1; 0164 } 0165 0166 if (compare(*runHi, *lo)) { // decreasing 0167 do { 0168 ++runHi; 0169 } while (runHi < hi && compare(*runHi, *(runHi - 1))); 0170 std::reverse(lo, runHi); 0171 } else { // non-decreasing 0172 do { 0173 ++runHi; 0174 } while (runHi < hi && !compare(*runHi, *(runHi - 1))); 0175 } 0176 0177 return runHi - lo; 0178 } 0179 0180 static diff_t minRunLength(diff_t n) { 0181 GFX_TIMSORT_ASSERT(n >= 0); 0182 0183 diff_t r = 0; 0184 while (n >= 2 * MIN_MERGE) { 0185 r |= (n & 1); 0186 n >>= 1; 0187 } 0188 return n + r; 0189 } 0190 0191 TimSort() : minGallop_(MIN_GALLOP) { 0192 } 0193 0194 // Silence GCC -Winline warning 0195 ~TimSort() {} 0196 0197 void pushRun(iter_t const runBase, diff_t const runLen) { 0198 pending_.push_back(run<iter_t>(runBase, runLen)); 0199 } 0200 0201 void mergeCollapse(Compare compare) { 0202 while (pending_.size() > 1) { 0203 diff_t n = pending_.size() - 2; 0204 0205 if ((n > 0 && pending_[n - 1].len <= pending_[n].len + pending_[n + 1].len) || 0206 (n > 1 && pending_[n - 2].len <= pending_[n - 1].len + pending_[n].len)) { 0207 if (pending_[n - 1].len < pending_[n + 1].len) { 0208 --n; 0209 } 0210 mergeAt(n, compare); 0211 } else if (pending_[n].len <= pending_[n + 1].len) { 0212 mergeAt(n, compare); 0213 } else { 0214 break; 0215 } 0216 } 0217 } 0218 0219 void mergeForceCollapse(Compare compare) { 0220 while (pending_.size() > 1) { 0221 diff_t n = pending_.size() - 2; 0222 0223 if (n > 0 && pending_[n - 1].len < pending_[n + 1].len) { 0224 --n; 0225 } 0226 mergeAt(n, compare); 0227 } 0228 } 0229 0230 void mergeAt(diff_t const i, Compare compare) { 0231 diff_t const stackSize = pending_.size(); 0232 GFX_TIMSORT_ASSERT(stackSize >= 2); 0233 GFX_TIMSORT_ASSERT(i >= 0); 0234 GFX_TIMSORT_ASSERT(i == stackSize - 2 || i == stackSize - 3); 0235 0236 iter_t base1 = pending_[i].base; 0237 diff_t len1 = pending_[i].len; 0238 iter_t base2 = pending_[i + 1].base; 0239 diff_t len2 = pending_[i + 1].len; 0240 0241 pending_[i].len = len1 + len2; 0242 0243 if (i == stackSize - 3) { 0244 pending_[i + 1] = pending_[i + 2]; 0245 } 0246 0247 pending_.pop_back(); 0248 0249 mergeConsecutiveRuns(base1, len1, base2, len2, std::move(compare)); 0250 } 0251 0252 void mergeConsecutiveRuns(iter_t base1, diff_t len1, iter_t base2, diff_t len2, Compare compare) { 0253 GFX_TIMSORT_ASSERT(len1 > 0); 0254 GFX_TIMSORT_ASSERT(len2 > 0); 0255 GFX_TIMSORT_ASSERT(base1 + len1 == base2); 0256 0257 diff_t const k = gallopRight(*base2, base1, len1, 0, compare); 0258 GFX_TIMSORT_ASSERT(k >= 0); 0259 0260 base1 += k; 0261 len1 -= k; 0262 0263 if (len1 == 0) { 0264 return; 0265 } 0266 0267 len2 = gallopLeft(*(base1 + (len1 - 1)), base2, len2, len2 - 1, compare); 0268 GFX_TIMSORT_ASSERT(len2 >= 0); 0269 if (len2 == 0) { 0270 return; 0271 } 0272 0273 if (len1 <= len2) { 0274 mergeLo(base1, len1, base2, len2, compare); 0275 } else { 0276 mergeHi(base1, len1, base2, len2, compare); 0277 } 0278 } 0279 0280 template <typename Iter> 0281 static diff_t gallopLeft(ref_t key, Iter const base, diff_t const len, 0282 diff_t const hint, Compare compare) { 0283 GFX_TIMSORT_ASSERT(len > 0); 0284 GFX_TIMSORT_ASSERT(hint >= 0); 0285 GFX_TIMSORT_ASSERT(hint < len); 0286 0287 diff_t lastOfs = 0; 0288 diff_t ofs = 1; 0289 0290 if (compare(*(base + hint), key)) { 0291 diff_t const maxOfs = len - hint; 0292 while (ofs < maxOfs && compare(*(base + (hint + ofs)), key)) { 0293 lastOfs = ofs; 0294 ofs = (ofs << 1) + 1; 0295 0296 if (ofs <= 0) { // int overflow 0297 ofs = maxOfs; 0298 } 0299 } 0300 if (ofs > maxOfs) { 0301 ofs = maxOfs; 0302 } 0303 0304 lastOfs += hint; 0305 ofs += hint; 0306 } else { 0307 diff_t const maxOfs = hint + 1; 0308 while (ofs < maxOfs && !compare(*(base + (hint - ofs)), key)) { 0309 lastOfs = ofs; 0310 ofs = (ofs << 1) + 1; 0311 0312 if (ofs <= 0) { 0313 ofs = maxOfs; 0314 } 0315 } 0316 if (ofs > maxOfs) { 0317 ofs = maxOfs; 0318 } 0319 0320 diff_t const tmp = lastOfs; 0321 lastOfs = hint - ofs; 0322 ofs = hint - tmp; 0323 } 0324 GFX_TIMSORT_ASSERT(-1 <= lastOfs); 0325 GFX_TIMSORT_ASSERT(lastOfs < ofs); 0326 GFX_TIMSORT_ASSERT(ofs <= len); 0327 0328 return std::lower_bound(base + (lastOfs + 1), base + ofs, key, compare) - base; 0329 } 0330 0331 template <typename Iter> 0332 static diff_t gallopRight(ref_t key, Iter const base, diff_t const len, 0333 diff_t const hint, Compare compare) { 0334 GFX_TIMSORT_ASSERT(len > 0); 0335 GFX_TIMSORT_ASSERT(hint >= 0); 0336 GFX_TIMSORT_ASSERT(hint < len); 0337 0338 diff_t ofs = 1; 0339 diff_t lastOfs = 0; 0340 0341 if (compare(key, *(base + hint))) { 0342 diff_t const maxOfs = hint + 1; 0343 while (ofs < maxOfs && compare(key, *(base + (hint - ofs)))) { 0344 lastOfs = ofs; 0345 ofs = (ofs << 1) + 1; 0346 0347 if (ofs <= 0) { 0348 ofs = maxOfs; 0349 } 0350 } 0351 if (ofs > maxOfs) { 0352 ofs = maxOfs; 0353 } 0354 0355 diff_t const tmp = lastOfs; 0356 lastOfs = hint - ofs; 0357 ofs = hint - tmp; 0358 } else { 0359 diff_t const maxOfs = len - hint; 0360 while (ofs < maxOfs && !compare(key, *(base + (hint + ofs)))) { 0361 lastOfs = ofs; 0362 ofs = (ofs << 1) + 1; 0363 0364 if (ofs <= 0) { // int overflow 0365 ofs = maxOfs; 0366 } 0367 } 0368 if (ofs > maxOfs) { 0369 ofs = maxOfs; 0370 } 0371 0372 lastOfs += hint; 0373 ofs += hint; 0374 } 0375 GFX_TIMSORT_ASSERT(-1 <= lastOfs); 0376 GFX_TIMSORT_ASSERT(lastOfs < ofs); 0377 GFX_TIMSORT_ASSERT(ofs <= len); 0378 0379 return std::upper_bound(base + (lastOfs + 1), base + ofs, key, compare) - base; 0380 } 0381 0382 static void rotateLeft(iter_t first, iter_t last) 0383 { 0384 value_t tmp = std::move(*first); 0385 iter_t last_1 = std::move(first + 1, last, first); 0386 *last_1 = std::move(tmp); 0387 } 0388 0389 static void rotateRight(iter_t first, iter_t last) 0390 { 0391 iter_t last_1 = last - 1; 0392 value_t tmp = std::move(*last_1); 0393 std::move_backward(first, last_1, last); 0394 *first = std::move(tmp); 0395 } 0396 0397 0398 void mergeLo(iter_t const base1, diff_t len1, iter_t const base2, diff_t len2, Compare compare) { 0399 GFX_TIMSORT_ASSERT(len1 > 0); 0400 GFX_TIMSORT_ASSERT(len2 > 0); 0401 GFX_TIMSORT_ASSERT(base1 + len1 == base2); 0402 0403 if (len1 == 1) { 0404 return rotateLeft(base1, base2 + len2); 0405 } 0406 if (len2 == 1) { 0407 return rotateRight(base1, base2 + len2); 0408 } 0409 0410 copy_to_tmp(base1, len1); 0411 0412 tmp_iter_t cursor1 = tmp_.begin(); 0413 iter_t cursor2 = base2; 0414 iter_t dest = base1; 0415 0416 *dest = std::move(*cursor2); 0417 ++cursor2; 0418 ++dest; 0419 --len2; 0420 0421 int minGallop(minGallop_); 0422 0423 // outer: 0424 while (true) { 0425 diff_t count1 = 0; 0426 diff_t count2 = 0; 0427 0428 do { 0429 GFX_TIMSORT_ASSERT(len1 > 1); 0430 GFX_TIMSORT_ASSERT(len2 > 0); 0431 0432 if (compare(*cursor2, *cursor1)) { 0433 *dest = std::move(*cursor2); 0434 ++cursor2; 0435 ++dest; 0436 ++count2; 0437 count1 = 0; 0438 if (--len2 == 0) { 0439 goto epilogue; 0440 } 0441 } else { 0442 *dest = std::move(*cursor1); 0443 ++cursor1; 0444 ++dest; 0445 ++count1; 0446 count2 = 0; 0447 if (--len1 == 1) { 0448 goto epilogue; 0449 } 0450 } 0451 } while ((count1 | count2) < minGallop); 0452 0453 do { 0454 GFX_TIMSORT_ASSERT(len1 > 1); 0455 GFX_TIMSORT_ASSERT(len2 > 0); 0456 0457 count1 = gallopRight(*cursor2, cursor1, len1, 0, compare); 0458 if (count1 != 0) { 0459 std::move_backward(cursor1, cursor1 + count1, dest + count1); 0460 dest += count1; 0461 cursor1 += count1; 0462 len1 -= count1; 0463 0464 if (len1 <= 1) { 0465 goto epilogue; 0466 } 0467 } 0468 *dest = std::move(*cursor2); 0469 ++cursor2; 0470 ++dest; 0471 if (--len2 == 0) { 0472 goto epilogue; 0473 } 0474 0475 count2 = gallopLeft(*cursor1, cursor2, len2, 0, compare); 0476 if (count2 != 0) { 0477 std::move(cursor2, cursor2 + count2, dest); 0478 dest += count2; 0479 cursor2 += count2; 0480 len2 -= count2; 0481 if (len2 == 0) { 0482 goto epilogue; 0483 } 0484 } 0485 *dest = std::move(*cursor1); 0486 ++cursor1; 0487 ++dest; 0488 if (--len1 == 1) { 0489 goto epilogue; 0490 } 0491 0492 --minGallop; 0493 } while ((count1 >= MIN_GALLOP) | (count2 >= MIN_GALLOP)); 0494 0495 if (minGallop < 0) { 0496 minGallop = 0; 0497 } 0498 minGallop += 2; 0499 } // end of "outer" loop 0500 0501 epilogue: // merge what is left from either cursor1 or cursor2 0502 0503 minGallop_ = (std::min)(minGallop, 1); 0504 0505 if (len1 == 1) { 0506 GFX_TIMSORT_ASSERT(len2 > 0); 0507 std::move(cursor2, cursor2 + len2, dest); 0508 *(dest + len2) = std::move(*cursor1); 0509 } else { 0510 GFX_TIMSORT_ASSERT(len1 != 0 && "Comparison function violates its general contract"); 0511 GFX_TIMSORT_ASSERT(len2 == 0); 0512 GFX_TIMSORT_ASSERT(len1 > 1); 0513 std::move(cursor1, cursor1 + len1, dest); 0514 } 0515 } 0516 0517 void mergeHi(iter_t const base1, diff_t len1, iter_t const base2, diff_t len2, Compare compare) { 0518 GFX_TIMSORT_ASSERT(len1 > 0); 0519 GFX_TIMSORT_ASSERT(len2 > 0); 0520 GFX_TIMSORT_ASSERT(base1 + len1 == base2); 0521 0522 if (len1 == 1) { 0523 return rotateLeft(base1, base2 + len2); 0524 } 0525 if (len2 == 1) { 0526 return rotateRight(base1, base2 + len2); 0527 } 0528 0529 copy_to_tmp(base2, len2); 0530 0531 iter_t cursor1 = base1 + len1; 0532 tmp_iter_t cursor2 = tmp_.begin() + (len2 - 1); 0533 iter_t dest = base2 + (len2 - 1); 0534 0535 *dest = std::move(*(--cursor1)); 0536 --dest; 0537 --len1; 0538 0539 int minGallop(minGallop_); 0540 0541 // outer: 0542 while (true) { 0543 diff_t count1 = 0; 0544 diff_t count2 = 0; 0545 0546 // The next loop is a hot path of the algorithm, so we decrement 0547 // eagerly the cursor so that it always points directly to the value 0548 // to compare, but we have to implement some trickier logic to make 0549 // sure that it points to the next value again by the end of said loop 0550 --cursor1; 0551 0552 do { 0553 GFX_TIMSORT_ASSERT(len1 > 0); 0554 GFX_TIMSORT_ASSERT(len2 > 1); 0555 0556 if (compare(*cursor2, *cursor1)) { 0557 *dest = std::move(*cursor1); 0558 --dest; 0559 ++count1; 0560 count2 = 0; 0561 if (--len1 == 0) { 0562 goto epilogue; 0563 } 0564 --cursor1; 0565 } else { 0566 *dest = std::move(*cursor2); 0567 --cursor2; 0568 --dest; 0569 ++count2; 0570 count1 = 0; 0571 if (--len2 == 1) { 0572 ++cursor1; // See comment before the loop 0573 goto epilogue; 0574 } 0575 } 0576 } while ((count1 | count2) < minGallop); 0577 ++cursor1; // See comment before the loop 0578 0579 do { 0580 GFX_TIMSORT_ASSERT(len1 > 0); 0581 GFX_TIMSORT_ASSERT(len2 > 1); 0582 0583 count1 = len1 - gallopRight(*cursor2, base1, len1, len1 - 1, compare); 0584 if (count1 != 0) { 0585 dest -= count1; 0586 cursor1 -= count1; 0587 len1 -= count1; 0588 std::move_backward(cursor1, cursor1 + count1, dest + (1 + count1)); 0589 0590 if (len1 == 0) { 0591 goto epilogue; 0592 } 0593 } 0594 *dest = std::move(*cursor2); 0595 --cursor2; 0596 --dest; 0597 if (--len2 == 1) { 0598 goto epilogue; 0599 } 0600 0601 count2 = len2 - gallopLeft(*(cursor1 - 1), tmp_.begin(), len2, len2 - 1, compare); 0602 if (count2 != 0) { 0603 dest -= count2; 0604 cursor2 -= count2; 0605 len2 -= count2; 0606 std::move(cursor2 + 1, cursor2 + (1 + count2), dest + 1); 0607 if (len2 <= 1) { 0608 goto epilogue; 0609 } 0610 } 0611 *dest = std::move(*(--cursor1)); 0612 --dest; 0613 if (--len1 == 0) { 0614 goto epilogue; 0615 } 0616 0617 --minGallop; 0618 } while ((count1 >= MIN_GALLOP) | (count2 >= MIN_GALLOP)); 0619 0620 if (minGallop < 0) { 0621 minGallop = 0; 0622 } 0623 minGallop += 2; 0624 } // end of "outer" loop 0625 0626 epilogue: // merge what is left from either cursor1 or cursor2 0627 0628 minGallop_ = (std::min)(minGallop, 1); 0629 0630 if (len2 == 1) { 0631 GFX_TIMSORT_ASSERT(len1 > 0); 0632 dest -= len1; 0633 std::move_backward(cursor1 - len1, cursor1, dest + (1 + len1)); 0634 *dest = std::move(*cursor2); 0635 } else { 0636 GFX_TIMSORT_ASSERT(len2 != 0 && "Comparison function violates its general contract"); 0637 GFX_TIMSORT_ASSERT(len1 == 0); 0638 GFX_TIMSORT_ASSERT(len2 > 1); 0639 std::move(tmp_.begin(), tmp_.begin() + len2, dest - (len2 - 1)); 0640 } 0641 } 0642 0643 void copy_to_tmp(iter_t const begin, diff_t len) { 0644 tmp_.assign(std::make_move_iterator(begin), 0645 std::make_move_iterator(begin + len)); 0646 } 0647 0648 public: 0649 0650 static void merge(iter_t const lo, iter_t const mid, iter_t const hi, Compare compare) { 0651 GFX_TIMSORT_ASSERT(lo <= mid); 0652 GFX_TIMSORT_ASSERT(mid <= hi); 0653 0654 if (lo == mid || mid == hi) { 0655 return; // nothing to do 0656 } 0657 0658 TimSort ts; 0659 ts.mergeConsecutiveRuns(lo, mid - lo, mid, hi - mid, std::move(compare)); 0660 0661 GFX_TIMSORT_LOG("1st size: " << (mid - lo) << "; 2nd size: " << (hi - mid) 0662 << "; tmp_.size(): " << ts.tmp_.size()); 0663 } 0664 0665 static void sort(iter_t const lo, iter_t const hi, Compare compare) { 0666 GFX_TIMSORT_ASSERT(lo <= hi); 0667 0668 diff_t nRemaining = (hi - lo); 0669 if (nRemaining < 2) { 0670 return; // nothing to do 0671 } 0672 0673 if (nRemaining < MIN_MERGE) { 0674 diff_t const initRunLen = countRunAndMakeAscending(lo, hi, compare); 0675 GFX_TIMSORT_LOG("initRunLen: " << initRunLen); 0676 binarySort(lo, hi, lo + initRunLen, compare); 0677 return; 0678 } 0679 0680 TimSort ts; 0681 diff_t const minRun = minRunLength(nRemaining); 0682 iter_t cur = lo; 0683 do { 0684 diff_t runLen = countRunAndMakeAscending(cur, hi, compare); 0685 0686 if (runLen < minRun) { 0687 diff_t const force = (std::min)(nRemaining, minRun); 0688 binarySort(cur, cur + force, cur + runLen, compare); 0689 runLen = force; 0690 } 0691 0692 ts.pushRun(cur, runLen); 0693 ts.mergeCollapse(compare); 0694 0695 cur += runLen; 0696 nRemaining -= runLen; 0697 } while (nRemaining != 0); 0698 0699 GFX_TIMSORT_ASSERT(cur == hi); 0700 ts.mergeForceCollapse(compare); 0701 GFX_TIMSORT_ASSERT(ts.pending_.size() == 1); 0702 0703 GFX_TIMSORT_LOG("size: " << (hi - lo) << " tmp_.size(): " << ts.tmp_.size() 0704 << " pending_.size(): " << ts.pending_.size()); 0705 } 0706 }; 0707 0708 } // namespace detail 0709 0710 0711 // --------------------------------------- 0712 // Public interface implementation 0713 // --------------------------------------- 0714 0715 /** 0716 * Stably merges two consecutive sorted ranges [first, middle) and [middle, last) into one 0717 * sorted range [first, last) with a comparison function and a projection function. 0718 */ 0719 template <typename RandomAccessIterator, typename Compare, typename Projection> 0720 void timmerge(RandomAccessIterator first, RandomAccessIterator middle, 0721 RandomAccessIterator last, Compare compare, Projection projection) { 0722 typedef detail::projection_compare<Compare, Projection> compare_t; 0723 compare_t comp(std::move(compare), std::move(projection)); 0724 GFX_TIMSORT_AUDIT(std::is_sorted(first, middle, comp) && "Precondition"); 0725 GFX_TIMSORT_AUDIT(std::is_sorted(middle, last, comp) && "Precondition"); 0726 detail::TimSort<RandomAccessIterator, compare_t>::merge(first, middle, last, comp); 0727 GFX_TIMSORT_AUDIT(std::is_sorted(first, last, comp) && "Postcondition"); 0728 } 0729 0730 /** 0731 * Same as std::inplace_merge(first, middle, last, compare). 0732 */ 0733 template <typename RandomAccessIterator, typename Compare> 0734 void timmerge(RandomAccessIterator first, RandomAccessIterator middle, 0735 RandomAccessIterator last, Compare compare) { 0736 gfx::timmerge(first, middle, last, compare, detail::identity()); 0737 } 0738 0739 /** 0740 * Same as std::inplace_merge(first, middle, last). 0741 */ 0742 template <typename RandomAccessIterator> 0743 void timmerge(RandomAccessIterator first, RandomAccessIterator middle, 0744 RandomAccessIterator last) { 0745 typedef typename std::iterator_traits<RandomAccessIterator>::value_type value_type; 0746 gfx::timmerge(first, middle, last, std::less<value_type>(), detail::identity()); 0747 } 0748 0749 /** 0750 * Stably sorts a range with a comparison function and a projection function. 0751 */ 0752 template <typename RandomAccessIterator, typename Compare, typename Projection> 0753 void timsort(RandomAccessIterator const first, RandomAccessIterator const last, 0754 Compare compare, Projection projection) { 0755 typedef detail::projection_compare<Compare, Projection> compare_t; 0756 compare_t comp(std::move(compare), std::move(projection)); 0757 detail::TimSort<RandomAccessIterator, compare_t>::sort(first, last, comp); 0758 GFX_TIMSORT_AUDIT(std::is_sorted(first, last, comp) && "Postcondition"); 0759 } 0760 0761 /** 0762 * Same as std::stable_sort(first, last, compare). 0763 */ 0764 template <typename RandomAccessIterator, typename Compare> 0765 void timsort(RandomAccessIterator const first, RandomAccessIterator const last, Compare compare) { 0766 gfx::timsort(first, last, compare, detail::identity()); 0767 } 0768 0769 /** 0770 * Same as std::stable_sort(first, last). 0771 */ 0772 template <typename RandomAccessIterator> 0773 void timsort(RandomAccessIterator const first, RandomAccessIterator const last) { 0774 typedef typename std::iterator_traits<RandomAccessIterator>::value_type value_type; 0775 gfx::timsort(first, last, std::less<value_type>(), detail::identity()); 0776 } 0777 0778 /** 0779 * Stably sorts a range with a comparison function and a projection function. 0780 */ 0781 template <typename RandomAccessRange, typename Compare, typename Projection> 0782 void timsort(RandomAccessRange &range, Compare compare, Projection projection) { 0783 gfx::timsort(std::begin(range), std::end(range), compare, projection); 0784 } 0785 0786 /** 0787 * Same as std::stable_sort(std::begin(range), std::end(range), compare). 0788 */ 0789 template <typename RandomAccessRange, typename Compare> 0790 void timsort(RandomAccessRange &range, Compare compare) { 0791 gfx::timsort(std::begin(range), std::end(range), compare); 0792 } 0793 0794 /** 0795 * Same as std::stable_sort(std::begin(range), std::end(range)). 0796 */ 0797 template <typename RandomAccessRange> 0798 void timsort(RandomAccessRange &range) { 0799 gfx::timsort(std::begin(range), std::end(range)); 0800 } 0801 0802 } // namespace gfx 0803 0804 #undef GFX_TIMSORT_ENABLE_ASSERT 0805 #undef GFX_TIMSORT_ASSERT 0806 #undef GFX_TIMSORT_ENABLE_AUDIT 0807 #undef GFX_TIMSORT_AUDIT 0808 #undef GFX_TIMSORT_ENABLE_LOG 0809 #undef GFX_TIMSORT_LOG 0810 0811 #endif // GFX_TIMSORT_HPP