1 // Written in the D programming language. 2 /** 3 This is a submodule of $(MREF std, algorithm). 4 It contains generic sorting algorithms. 5 6 $(SCRIPT inhibitQuickIndex = 1;) 7 $(BOOKTABLE Cheat Sheet, 8 $(TR $(TH Function Name) $(TH Description)) 9 $(T2 completeSort, 10 If `a = [10, 20, 30]` and `b = [40, 6, 15]`, then 11 `completeSort(a, b)` leaves `a = [6, 10, 15]` and `b = [20, 30, 40]`. 12 The range `a` must be sorted prior to the call, and as a result the 13 combination `$(REF chain, std,range)(a, b)` is sorted.) 14 $(T2 isPartitioned, 15 `isPartitioned!"a < 0"([-1, -2, 1, 0, 2])` returns `true` because 16 the predicate is `true` for a portion of the range and `false` 17 afterwards.) 18 $(T2 isSorted, 19 `isSorted([1, 1, 2, 3])` returns `true`.) 20 $(T2 isStrictlyMonotonic, 21 `isStrictlyMonotonic([1, 1, 2, 3])` returns `false`.) 22 $(T2 ordered, 23 `ordered(1, 1, 2, 3)` returns `true`.) 24 $(T2 strictlyOrdered, 25 `strictlyOrdered(1, 1, 2, 3)` returns `false`.) 26 $(T2 makeIndex, 27 Creates a separate index for a range.) 28 $(T2 merge, 29 Lazily merges two or more sorted ranges.) 30 $(T2 multiSort, 31 Sorts by multiple keys.) 32 $(T2 nextEvenPermutation, 33 Computes the next lexicographically greater even permutation of a range 34 in-place.) 35 $(T2 nextPermutation, 36 Computes the next lexicographically greater permutation of a range 37 in-place.) 38 $(T2 nthPermutation, 39 Computes the nth permutation of a range 40 in-place.) 41 $(T2 partialSort, 42 If `a = [5, 4, 3, 2, 1]`, then `partialSort(a, 3)` leaves 43 `a[0 .. 3] = [1, 2, 3]`. 44 The other elements of `a` are left in an unspecified order.) 45 $(T2 partition, 46 Partitions a range according to a unary predicate.) 47 $(T2 partition3, 48 Partitions a range according to a binary predicate in three parts (less 49 than, equal, greater than the given pivot). Pivot is not given as an 50 index, but instead as an element independent from the range's content.) 51 $(T2 pivotPartition, 52 Partitions a range according to a binary predicate in two parts: less 53 than or equal, and greater than or equal to the given pivot, passed as 54 an index in the range.) 55 $(T2 schwartzSort, 56 Sorts with the help of the $(LINK2 https://en.wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform).) 57 $(T2 sort, 58 Sorts.) 59 $(T2 topN, 60 Separates the top elements in a range, akin to $(LINK2 https://en.wikipedia.org/wiki/Quickselect, Quickselect).) 61 $(T2 topNCopy, 62 Copies out the top elements of a range.) 63 $(T2 topNIndex, 64 Builds an index of the top elements of a range.) 65 ) 66 67 Copyright: Andrei Alexandrescu 2008-. 68 69 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 70 71 Authors: $(HTTP erdani.com, Andrei Alexandrescu) 72 73 Source: $(PHOBOSSRC std/algorithm/sorting.d) 74 75 Macros: 76 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 77 */ 78 module std.algorithm.sorting; 79 80 import std.algorithm.mutation : SwapStrategy; 81 import std.functional : unaryFun, binaryFun; 82 import std.range.primitives; 83 import std.typecons : Flag, No, Yes; 84 import std.meta : allSatisfy; 85 import std.range : SortedRange; 86 import std.traits; 87 88 /** 89 Specifies whether the output of certain algorithm is desired in sorted 90 format. 91 92 If set to `SortOutput.no`, the output should not be sorted. 93 94 Otherwise if set to `SortOutput.yes`, the output should be sorted. 95 */ 96 alias SortOutput = Flag!"sortOutput"; 97 98 // completeSort 99 /** 100 Sorts the random-access range `chain(lhs, rhs)` according to 101 predicate `less`. 102 103 The left-hand side of the range `lhs` is assumed to be already sorted; 104 `rhs` is assumed to be unsorted. 105 The exact strategy chosen depends on the relative sizes of `lhs` and 106 `rhs`. Performs $(BIGOH lhs.length + rhs.length * log(rhs.length)) 107 (best case) to $(BIGOH (lhs.length + rhs.length) * log(lhs.length + 108 rhs.length)) (worst-case) evaluations of $(REF_ALTTEXT swap, swap, std,algorithm,mutation). 109 110 Params: 111 less = The predicate to sort by. 112 ss = The swapping strategy to use. 113 lhs = The sorted, left-hand side of the random access range to be sorted. 114 rhs = The unsorted, right-hand side of the random access range to be 115 sorted. 116 */ 117 void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, 118 Lhs , Rhs)(SortedRange!(Lhs, less) lhs, Rhs rhs) 119 if (hasLength!(Rhs) && hasSlicing!(Rhs) 120 && hasSwappableElements!Lhs && hasSwappableElements!Rhs) 121 { 122 import std.algorithm.mutation : bringToFront; 123 import std.range : chain, assumeSorted; 124 // Probably this algorithm can be optimized by using in-place 125 // merge 126 auto lhsOriginal = lhs.release(); 127 foreach (i; 0 .. rhs.length) 128 { 129 auto sortedSoFar = chain(lhsOriginal, rhs[0 .. i]); 130 auto ub = assumeSorted!less(sortedSoFar).upperBound(rhs[i]); 131 if (!ub.length) continue; 132 bringToFront(ub.release(), rhs[i .. i + 1]); 133 } 134 } 135 136 /// 137 @safe unittest 138 { 139 import std.range : assumeSorted; 140 int[] a = [ 1, 2, 3 ]; 141 int[] b = [ 4, 0, 6, 5 ]; 142 completeSort(assumeSorted(a), b); 143 assert(a == [ 0, 1, 2 ]); 144 assert(b == [ 3, 4, 5, 6 ]); 145 } 146 147 // isSorted 148 /** 149 Checks whether a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) 150 is sorted according to the comparison operation `less`. Performs $(BIGOH r.length) 151 evaluations of `less`. 152 153 Unlike `isSorted`, `isStrictlyMonotonic` does not allow for equal values, 154 i.e. values for which both `less(a, b)` and `less(b, a)` are false. 155 156 With either function, the predicate must be a strict ordering just like with 157 `isSorted`. For example, using `"a <= b"` instead of `"a < b"` is 158 incorrect and will cause failed assertions. 159 160 Params: 161 less = Predicate the range should be sorted by. 162 r = Forward range to check for sortedness. 163 164 Returns: 165 `true` if the range is sorted, false otherwise. `isSorted` allows 166 duplicates, `isStrictlyMonotonic` not. 167 */ 168 bool isSorted(alias less = "a < b", Range)(Range r) 169 if (isForwardRange!(Range)) 170 { 171 if (r.empty) return true; 172 173 static if (isRandomAccessRange!Range && hasLength!Range) 174 { 175 immutable limit = r.length - 1; 176 foreach (i; 0 .. limit) 177 { 178 if (!binaryFun!less(r[i + 1], r[i])) continue; 179 assert( 180 !binaryFun!less(r[i], r[i + 1]), 181 "Predicate for isSorted is not antisymmetric. Both" ~ 182 " pred(a, b) and pred(b, a) are true for certain values."); 183 return false; 184 } 185 } 186 else 187 { 188 auto ahead = r.save; 189 ahead.popFront(); 190 size_t i; 191 192 for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i) 193 { 194 if (!binaryFun!less(ahead.front, r.front)) continue; 195 // Check for antisymmetric predicate 196 assert( 197 !binaryFun!less(r.front, ahead.front), 198 "Predicate for isSorted is not antisymmetric. Both" ~ 199 " pred(a, b) and pred(b, a) are true for certain values."); 200 return false; 201 } 202 } 203 return true; 204 } 205 206 /// 207 @safe unittest 208 { 209 assert([1, 1, 2].isSorted); 210 // strictly monotonic doesn't allow duplicates 211 assert(![1, 1, 2].isStrictlyMonotonic); 212 213 int[] arr = [4, 3, 2, 1]; 214 assert(!isSorted(arr)); 215 assert(!isStrictlyMonotonic(arr)); 216 217 assert(isSorted!"a > b"(arr)); 218 assert(isStrictlyMonotonic!"a > b"(arr)); 219 220 sort(arr); 221 assert(isSorted(arr)); 222 assert(isStrictlyMonotonic(arr)); 223 } 224 225 @safe unittest 226 { 227 import std.conv : to; 228 229 // https://issues.dlang.org/show_bug.cgi?id=9457 230 auto x = "abcd"; 231 assert(isSorted(x)); 232 auto y = "acbd"; 233 assert(!isSorted(y)); 234 235 int[] a = [1, 2, 3]; 236 assert(isSorted(a)); 237 int[] b = [1, 3, 2]; 238 assert(!isSorted(b)); 239 240 // ignores duplicates 241 int[] c = [1, 1, 2]; 242 assert(isSorted(c)); 243 244 dchar[] ds = "コーヒーが好きです"d.dup; 245 sort(ds); 246 string s = to!string(ds); 247 assert(isSorted(ds)); // random-access 248 assert(isSorted(s)); // bidirectional 249 } 250 251 @nogc @safe nothrow pure unittest 252 { 253 static immutable a = [1, 2, 3]; 254 assert(a.isSorted); 255 } 256 257 /// ditto 258 bool isStrictlyMonotonic(alias less = "a < b", Range)(Range r) 259 if (isForwardRange!Range) 260 { 261 import std.algorithm.searching : findAdjacent; 262 return findAdjacent!((a,b) => !binaryFun!less(a,b))(r).empty; 263 } 264 265 @safe unittest 266 { 267 import std.conv : to; 268 269 assert("abcd".isStrictlyMonotonic); 270 assert(!"aacd".isStrictlyMonotonic); 271 assert(!"acb".isStrictlyMonotonic); 272 273 assert([1, 2, 3].isStrictlyMonotonic); 274 assert(![1, 3, 2].isStrictlyMonotonic); 275 assert(![1, 1, 2].isStrictlyMonotonic); 276 277 // ー occurs twice -> can't be strict 278 dchar[] ds = "コーヒーが好きです"d.dup; 279 sort(ds); 280 string s = to!string(ds); 281 assert(!isStrictlyMonotonic(ds)); // random-access 282 assert(!isStrictlyMonotonic(s)); // bidirectional 283 284 dchar[] ds2 = "コーヒが好きです"d.dup; 285 sort(ds2); 286 string s2 = to!string(ds2); 287 assert(isStrictlyMonotonic(ds2)); // random-access 288 assert(isStrictlyMonotonic(s2)); // bidirectional 289 } 290 291 @nogc @safe nothrow pure unittest 292 { 293 static immutable a = [1, 2, 3]; 294 assert(a.isStrictlyMonotonic); 295 } 296 297 /** 298 Like `isSorted`, returns `true` if the given `values` are ordered 299 according to the comparison operation `less`. Unlike `isSorted`, takes values 300 directly instead of structured in a range. 301 302 `ordered` allows repeated values, e.g. `ordered(1, 1, 2)` is `true`. To verify 303 that the values are ordered strictly monotonically, use `strictlyOrdered`; 304 `strictlyOrdered(1, 1, 2)` is `false`. 305 306 With either function, the predicate must be a strict ordering. For example, 307 using `"a <= b"` instead of `"a < b"` is incorrect and will cause failed 308 assertions. 309 310 Params: 311 values = The tested value 312 less = The comparison predicate 313 314 Returns: 315 `true` if the values are ordered; `ordered` allows for duplicates, 316 `strictlyOrdered` does not. 317 */ 318 319 bool ordered(alias less = "a < b", T...)(T values) 320 if ((T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool)) 321 || 322 (T.length > 2 && is(typeof(ordered!less(values[0 .. 1 + $ / 2]))) 323 && is(typeof(ordered!less(values[$ / 2 .. $])))) 324 ) 325 { 326 foreach (i, _; T[0 .. $ - 1]) 327 { 328 if (binaryFun!less(values[i + 1], values[i])) 329 { 330 assert(!binaryFun!less(values[i], values[i + 1]), 331 __FUNCTION__ ~ ": incorrect non-strict predicate."); 332 return false; 333 } 334 } 335 return true; 336 } 337 338 /// ditto 339 bool strictlyOrdered(alias less = "a < b", T...)(T values) 340 if (is(typeof(ordered!less(values)))) 341 { 342 foreach (i, _; T[0 .. $ - 1]) 343 { 344 if (!binaryFun!less(values[i], values[i + 1])) 345 { 346 return false; 347 } 348 assert(!binaryFun!less(values[i + 1], values[i]), 349 __FUNCTION__ ~ ": incorrect non-strict predicate."); 350 } 351 return true; 352 } 353 354 /// 355 @safe unittest 356 { 357 assert(ordered(42, 42, 43)); 358 assert(!strictlyOrdered(43, 42, 45)); 359 assert(ordered(42, 42, 43)); 360 assert(!strictlyOrdered(42, 42, 43)); 361 assert(!ordered(43, 42, 45)); 362 // Ordered lexicographically 363 assert(ordered("Jane", "Jim", "Joe")); 364 assert(strictlyOrdered("Jane", "Jim", "Joe")); 365 // Incidentally also ordered by length decreasing 366 assert(ordered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); 367 // ... but not strictly so: "Jim" and "Joe" have the same length 368 assert(!strictlyOrdered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); 369 } 370 371 // partition 372 /** 373 Partitions a range in two using the given `predicate`. 374 375 Specifically, reorders the range `r = [left, right$(RPAREN)` using $(REF_ALTTEXT swap, swap, std,algorithm,mutation) 376 such that all elements `i` for which `predicate(i)` is `true` come 377 before all elements `j` for which `predicate(j)` returns `false`. 378 379 Performs $(BIGOH r.length) (if unstable or semistable) or $(BIGOH 380 r.length * log(r.length)) (if stable) evaluations of `less` and $(REF_ALTTEXT swap, swap, std,algorithm,mutation). 381 The unstable version computes the minimum possible evaluations of `swap` 382 (roughly half of those performed by the semistable version). 383 384 Params: 385 predicate = The predicate to partition by. 386 ss = The swapping strategy to employ. 387 r = The random-access range to partition. 388 389 Returns: 390 391 The right part of `r` after partitioning. 392 393 If `ss == SwapStrategy.stable`, `partition` preserves the relative 394 ordering of all elements `a`, `b` in `r` for which 395 `predicate(a) == predicate(b)`. 396 If `ss == SwapStrategy.semistable`, `partition` preserves 397 the relative ordering of all elements `a`, `b` in the left part of `r` 398 for which `predicate(a) == predicate(b)`. 399 */ 400 Range partition(alias predicate, SwapStrategy ss, Range)(Range r) 401 if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range && 402 hasSlicing!Range && hasSwappableElements!Range) 403 { 404 import std.algorithm.mutation : bringToFront; 405 406 alias pred = unaryFun!(predicate); 407 if (r.empty) return r; 408 409 if (r.length == 1) 410 { 411 if (pred(r.front)) r.popFront(); 412 return r; 413 } 414 const middle = r.length / 2; 415 alias recurse = .partition!(pred, ss, Range); 416 auto lower = recurse(r[0 .. middle]); 417 auto upper = recurse(r[middle .. r.length]); 418 bringToFront(lower, r[middle .. r.length - upper.length]); 419 return r[r.length - lower.length - upper.length .. r.length]; 420 } 421 422 ///ditto 423 Range partition(alias predicate, SwapStrategy ss = SwapStrategy.unstable, Range)(Range r) 424 if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Range) 425 { 426 import std.algorithm.mutation : swap; 427 alias pred = unaryFun!(predicate); 428 429 static if (ss == SwapStrategy.semistable) 430 { 431 if (r.empty) return r; 432 433 for (; !r.empty; r.popFront()) 434 { 435 // skip the initial portion of "correct" elements 436 if (pred(r.front)) continue; 437 // hit the first "bad" element 438 auto result = r; 439 for (r.popFront(); !r.empty; r.popFront()) 440 { 441 if (!pred(r.front)) continue; 442 swap(result.front, r.front); 443 result.popFront(); 444 } 445 return result; 446 } 447 448 return r; 449 } 450 else 451 { 452 // Inspired from www.stepanovpapers.com/PAM3-partition_notes.pdf, 453 // section "Bidirectional Partition Algorithm (Hoare)" 454 static if (isDynamicArray!Range) 455 { 456 import std.algorithm.mutation : swapAt; 457 // For dynamic arrays prefer index-based manipulation 458 if (!r.length) return r; 459 size_t lo = 0, hi = r.length - 1; 460 for (;;) 461 { 462 for (;;) 463 { 464 if (lo > hi) return r[lo .. r.length]; 465 if (!pred(r[lo])) break; 466 ++lo; 467 } 468 // found the left bound 469 assert(lo <= hi, "lo must be <= hi"); 470 for (;;) 471 { 472 if (lo == hi) return r[lo .. r.length]; 473 if (pred(r[hi])) break; 474 --hi; 475 } 476 // found the right bound, swap & make progress 477 r.swapAt(lo++, hi--); 478 } 479 } 480 else 481 { 482 import std.algorithm.mutation : swap; 483 auto result = r; 484 for (;;) 485 { 486 for (;;) 487 { 488 if (r.empty) return result; 489 if (!pred(r.front)) break; 490 r.popFront(); 491 result.popFront(); 492 } 493 // found the left bound 494 assert(!r.empty, "r must not be empty"); 495 for (;;) 496 { 497 if (pred(r.back)) break; 498 r.popBack(); 499 if (r.empty) return result; 500 } 501 // found the right bound, swap & make progress 502 static if (is(typeof(swap(r.front, r.back)))) 503 { 504 swap(r.front, r.back); 505 } 506 else 507 { 508 auto t1 = r.moveFront(), t2 = r.moveBack(); 509 r.front = t2; 510 r.back = t1; 511 } 512 r.popFront(); 513 result.popFront(); 514 r.popBack(); 515 } 516 } 517 } 518 } 519 520 /// 521 @safe unittest 522 { 523 import std.algorithm.mutation : SwapStrategy; 524 import std.algorithm.searching : count, find; 525 import std.conv : text; 526 import std.range.primitives : empty; 527 528 auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 529 auto arr = Arr.dup; 530 static bool even(int a) { return (a & 1) == 0; } 531 // Partition arr such that even numbers come first 532 auto r = partition!(even)(arr); 533 // Now arr is separated in evens and odds. 534 // Numbers may have become shuffled due to instability 535 assert(r == arr[5 .. $]); 536 assert(count!(even)(arr[0 .. 5]) == 5); 537 assert(find!(even)(r).empty); 538 539 // Can also specify the predicate as a string. 540 // Use 'a' as the predicate argument name 541 arr[] = Arr[]; 542 r = partition!(q{(a & 1) == 0})(arr); 543 assert(r == arr[5 .. $]); 544 545 // Now for a stable partition: 546 arr[] = Arr[]; 547 r = partition!(q{(a & 1) == 0}, SwapStrategy.stable)(arr); 548 // Now arr is [2 4 6 8 10 1 3 5 7 9], and r points to 1 549 assert(arr == [2, 4, 6, 8, 10, 1, 3, 5, 7, 9] && r == arr[5 .. $]); 550 551 // In case the predicate needs to hold its own state, use a delegate: 552 arr[] = Arr[]; 553 int x = 3; 554 // Put stuff greater than 3 on the left 555 bool fun(int a) { return a > x; } 556 r = partition!(fun, SwapStrategy.semistable)(arr); 557 // Now arr is [4 5 6 7 8 9 10 2 3 1] and r points to 2 558 assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1] && r == arr[7 .. $]); 559 } 560 561 @safe unittest 562 { 563 import std.algorithm.internal : rndstuff; 564 static bool even(int a) { return (a & 1) == 0; } 565 566 // test with random data 567 auto a = rndstuff!int(); 568 partition!even(a); 569 assert(isPartitioned!even(a)); 570 auto b = rndstuff!string(); 571 partition!`a.length < 5`(b); 572 assert(isPartitioned!`a.length < 5`(b)); 573 } 574 575 // pivotPartition 576 /** 577 Partitions `r` around `pivot` using comparison function `less`, algorithm akin 578 to $(LINK2 https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme, 579 Hoare partition). 580 581 Specifically, permutes elements of `r` and returns 582 an index `k < r.length` such that: 583 584 $(UL 585 586 $(LI `r[pivot]` is swapped to `r[k]`) 587 588 $(LI All elements `e` in subrange `r[0 .. k]` satisfy `!less(r[k], e)` 589 (i.e. `r[k]` is greater than or equal to each element to its left according to 590 predicate `less`)) 591 592 $(LI All elements `e` in subrange `r[k .. $]` satisfy `!less(e, r[k])` 593 (i.e. `r[k]` is less than or equal to each element to its right 594 according to predicate `less`))) 595 596 If `r` contains equivalent elements, multiple permutations of `r` satisfy these 597 constraints. In such cases, `pivotPartition` attempts to distribute equivalent 598 elements fairly to the left and right of `k` such that `k` stays close to $(D 599 r.length / 2). 600 601 Params: 602 less = The predicate used for comparison, modeled as a 603 $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, 604 strict weak ordering) (irreflexive, antisymmetric, transitive, and implying a transitive 605 equivalence) 606 r = The range being partitioned 607 pivot = The index of the pivot for partitioning, must be less than `r.length` or 608 `0` if `r.length` is `0` 609 610 Returns: 611 The new position of the pivot 612 613 See_Also: 614 $(HTTP jgrcs.info/index.php/jgrcs/article/view/142, Engineering of a Quicksort 615 Partitioning Algorithm), D. Abhyankar, Journal of Global Research in Computer 616 Science, February 2011. $(HTTPS youtube.com/watch?v=AxnotgLql0k, ACCU 2016 617 Keynote), Andrei Alexandrescu. 618 */ 619 size_t pivotPartition(alias less = "a < b", Range) 620 (Range r, size_t pivot) 621 if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && hasAssignableElements!Range) 622 { 623 assert(pivot < r.length || r.length == 0 && pivot == 0, "pivot must be" 624 ~ " less than the length of r or r must be empty and pivot zero"); 625 if (r.length <= 1) return 0; 626 import std.algorithm.mutation : swapAt, move; 627 alias lt = binaryFun!less; 628 629 // Pivot at the front 630 r.swapAt(pivot, 0); 631 632 // Fork implementation depending on nothrow copy, assignment, and 633 // comparison. If all of these are nothrow, use the specialized 634 // implementation discussed at https://youtube.com/watch?v=AxnotgLql0k. 635 static if (is(typeof( 636 () nothrow { auto x = r.front; x = r.front; return lt(x, x); } 637 ))) 638 { 639 auto p = r[0]; 640 // Plant the pivot in the end as well as a sentinel 641 size_t lo = 0, hi = r.length - 1; 642 auto save = r.moveAt(hi); 643 r[hi] = p; // Vacancy is in r[$ - 1] now 644 // Start process 645 for (;;) 646 { 647 // Loop invariant 648 version (StdUnittest) 649 { 650 // this used to import std.algorithm.all, but we want to save 651 // imports when unittests are enabled if possible. 652 foreach (x; r[0 .. lo]) 653 assert(!lt(p, x), "p must not be less than x"); 654 foreach (x; r[hi+1 .. r.length]) 655 assert(!lt(x, p), "x must not be less than p"); 656 } 657 do ++lo; while (lt(r[lo], p)); 658 r[hi] = r[lo]; 659 // Vacancy is now in r[lo] 660 do --hi; while (lt(p, r[hi])); 661 if (lo >= hi) break; 662 r[lo] = r[hi]; 663 // Vacancy is not in r[hi] 664 } 665 // Fixup 666 assert(lo - hi <= 2, "Following compare not possible"); 667 assert(!lt(p, r[hi]), "r[hi] must not be less than p"); 668 if (lo == hi + 2) 669 { 670 assert(!lt(r[hi + 1], p), "r[hi + 1] must not be less than p"); 671 r[lo] = r[hi + 1]; 672 --lo; 673 } 674 r[lo] = save; 675 if (lt(p, save)) --lo; 676 assert(!lt(p, r[lo]), "r[lo] must not be less than p"); 677 } 678 else 679 { 680 size_t lo = 1, hi = r.length - 1; 681 loop: for (;; lo++, hi--) 682 { 683 for (;; ++lo) 684 { 685 if (lo > hi) break loop; 686 if (!lt(r[lo], r[0])) break; 687 } 688 // found the left bound: r[lo] >= r[0] 689 assert(lo <= hi, "lo must be less or equal than hi"); 690 for (;; --hi) 691 { 692 if (lo >= hi) break loop; 693 if (!lt(r[0], r[hi])) break; 694 } 695 // found the right bound: r[hi] <= r[0], swap & make progress 696 assert(!lt(r[lo], r[hi]), "r[lo] must not be less than r[hi]"); 697 r.swapAt(lo, hi); 698 } 699 --lo; 700 } 701 r.swapAt(lo, 0); 702 return lo; 703 } 704 705 /// 706 @safe nothrow unittest 707 { 708 int[] a = [5, 3, 2, 6, 4, 1, 3, 7]; 709 size_t pivot = pivotPartition(a, a.length / 2); 710 import std.algorithm.searching : all; 711 assert(a[0 .. pivot].all!(x => x <= a[pivot])); 712 assert(a[pivot .. $].all!(x => x >= a[pivot])); 713 } 714 715 @safe unittest 716 { 717 void test(alias less)() 718 { 719 int[] a; 720 size_t pivot; 721 722 a = [-9, -4, -2, -2, 9]; 723 pivot = pivotPartition!less(a, a.length / 2); 724 import std.algorithm.searching : all; 725 assert(a[0 .. pivot].all!(x => x <= a[pivot])); 726 assert(a[pivot .. $].all!(x => x >= a[pivot])); 727 728 a = [9, 2, 8, -5, 5, 4, -8, -4, 9]; 729 pivot = pivotPartition!less(a, a.length / 2); 730 assert(a[0 .. pivot].all!(x => x <= a[pivot])); 731 assert(a[pivot .. $].all!(x => x >= a[pivot])); 732 733 a = [ 42 ]; 734 pivot = pivotPartition!less(a, a.length / 2); 735 assert(pivot == 0); 736 assert(a == [ 42 ]); 737 738 a = [ 43, 42 ]; 739 pivot = pivotPartition!less(a, 0); 740 assert(pivot == 1); 741 assert(a == [ 42, 43 ]); 742 743 a = [ 43, 42 ]; 744 pivot = pivotPartition!less(a, 1); 745 assert(pivot == 0); 746 assert(a == [ 42, 43 ]); 747 748 a = [ 42, 42 ]; 749 pivot = pivotPartition!less(a, 0); 750 assert(pivot == 0 || pivot == 1); 751 assert(a == [ 42, 42 ]); 752 pivot = pivotPartition!less(a, 1); 753 assert(pivot == 0 || pivot == 1); 754 assert(a == [ 42, 42 ]); 755 756 import std.algorithm.iteration : map; 757 import std.array : array; 758 import std.format : format; 759 import std.random : Random, uniform, Xorshift; 760 import std.range : iota; 761 auto s = 123_456_789; 762 auto g = Xorshift(s); 763 a = iota(0, uniform(1, 1000, g)) 764 .map!(_ => uniform(-1000, 1000, g)) 765 .array; 766 pivot = pivotPartition!less(a, a.length / 2); 767 assert(a[0 .. pivot].all!(x => x <= a[pivot]), "RNG seed: %d".format(s)); 768 assert(a[pivot .. $].all!(x => x >= a[pivot]), "RNG seed: %d".format(s)); 769 } 770 test!"a < b"; 771 static bool myLess(int a, int b) 772 { 773 static bool bogus; 774 if (bogus) throw new Exception(""); // just to make it no-nothrow 775 return a < b; 776 } 777 test!myLess; 778 } 779 780 /** 781 Params: 782 pred = The predicate that the range should be partitioned by. 783 r = The range to check. 784 Returns: `true` if `r` is partitioned according to predicate `pred`. 785 */ 786 bool isPartitioned(alias pred, Range)(Range r) 787 if (isForwardRange!(Range)) 788 { 789 for (; !r.empty; r.popFront()) 790 { 791 if (unaryFun!(pred)(r.front)) continue; 792 for (r.popFront(); !r.empty; r.popFront()) 793 { 794 if (unaryFun!(pred)(r.front)) return false; 795 } 796 break; 797 } 798 return true; 799 } 800 801 /// 802 @safe unittest 803 { 804 int[] r = [ 1, 3, 5, 7, 8, 2, 4, ]; 805 assert(isPartitioned!"a & 1"(r)); 806 } 807 808 // partition3 809 /** 810 Rearranges elements in `r` in three adjacent ranges and returns 811 them. 812 813 The first and leftmost range only contains elements in `r` 814 less than `pivot`. The second and middle range only contains 815 elements in `r` that are equal to `pivot`. Finally, the third 816 and rightmost range only contains elements in `r` that are greater 817 than `pivot`. The less-than test is defined by the binary function 818 `less`. 819 820 Params: 821 less = The predicate to use for the rearrangement. 822 ss = The swapping strategy to use. 823 r = The random-access range to rearrange. 824 pivot = The pivot element. 825 826 Returns: 827 A $(REF Tuple, std,typecons) of the three resulting ranges. These ranges are 828 slices of the original range. 829 830 BUGS: stable `partition3` has not been implemented yet. 831 */ 832 auto partition3(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, E) 833 (Range r, E pivot) 834 if (ss == SwapStrategy.unstable && isRandomAccessRange!Range 835 && hasSwappableElements!Range && hasLength!Range && hasSlicing!Range 836 && is(typeof(binaryFun!less(r.front, pivot)) == bool) 837 && is(typeof(binaryFun!less(pivot, r.front)) == bool) 838 && is(typeof(binaryFun!less(r.front, r.front)) == bool)) 839 { 840 // The algorithm is described in "Engineering a sort function" by 841 // Jon Bentley et al, pp 1257. 842 843 import std.algorithm.comparison : min; 844 import std.algorithm.mutation : swap, swapAt, swapRanges; 845 import std.typecons : tuple; 846 847 alias lessFun = binaryFun!less; 848 size_t i, j, k = r.length, l = k; 849 850 bigloop: 851 for (;;) 852 { 853 for (;; ++j) 854 { 855 if (j == k) break bigloop; 856 assert(j < r.length, "j must be less than r.length"); 857 if (lessFun(r[j], pivot)) continue; 858 if (lessFun(pivot, r[j])) break; 859 r.swapAt(i++, j); 860 } 861 assert(j < k, "j must be less than k"); 862 for (;;) 863 { 864 assert(k > 0, "k must be positive"); 865 if (!lessFun(pivot, r[--k])) 866 { 867 if (lessFun(r[k], pivot)) break; 868 r.swapAt(k, --l); 869 } 870 if (j == k) break bigloop; 871 } 872 // Here we know r[j] > pivot && r[k] < pivot 873 r.swapAt(j++, k); 874 } 875 876 // Swap the equal ranges from the extremes into the middle 877 auto strictlyLess = j - i, strictlyGreater = l - k; 878 auto swapLen = min(i, strictlyLess); 879 swapRanges(r[0 .. swapLen], r[j - swapLen .. j]); 880 swapLen = min(r.length - l, strictlyGreater); 881 swapRanges(r[k .. k + swapLen], r[r.length - swapLen .. r.length]); 882 return tuple(r[0 .. strictlyLess], 883 r[strictlyLess .. r.length - strictlyGreater], 884 r[r.length - strictlyGreater .. r.length]); 885 } 886 887 /// 888 @safe unittest 889 { 890 auto a = [ 8, 3, 4, 1, 4, 7, 4 ]; 891 auto pieces = partition3(a, 4); 892 assert(pieces[0] == [ 1, 3 ]); 893 assert(pieces[1] == [ 4, 4, 4 ]); 894 assert(pieces[2] == [ 8, 7 ]); 895 } 896 897 @safe unittest 898 { 899 import std.random : Random = Xorshift, uniform; 900 901 immutable uint[] seeds = [3923355730, 1927035882]; 902 foreach (s; seeds) 903 { 904 auto r = Random(s); 905 auto a = new int[](uniform(0, 100, r)); 906 foreach (ref e; a) 907 { 908 e = uniform(0, 50, r); 909 } 910 auto pieces = partition3(a, 25); 911 assert(pieces[0].length + pieces[1].length + pieces[2].length == a.length); 912 foreach (e; pieces[0]) 913 { 914 assert(e < 25); 915 } 916 foreach (e; pieces[1]) 917 { 918 assert(e == 25); 919 } 920 foreach (e; pieces[2]) 921 { 922 assert(e > 25); 923 } 924 } 925 } 926 927 // makeIndex 928 /** 929 Computes an index for `r` based on the comparison `less`. 930 931 The index is a sorted array of pointers or indices into the original 932 range. This technique is similar to sorting, but it is more flexible 933 because (1) it allows "sorting" of immutable collections, (2) allows 934 binary search even if the original collection does not offer random 935 access, (3) allows multiple indexes, each on a different predicate, 936 and (4) may be faster when dealing with large objects. However, using 937 an index may also be slower under certain circumstances due to the 938 extra indirection, and is always larger than a sorting-based solution 939 because it needs space for the index in addition to the original 940 collection. The complexity is the same as `sort`'s. 941 942 The first overload of `makeIndex` writes to a range containing 943 pointers, and the second writes to a range containing offsets. The 944 first overload requires `Range` to be a 945 $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives), and the 946 latter requires it to be a random-access range. 947 948 `makeIndex` overwrites its second argument with the result, but 949 never reallocates it. 950 951 Params: 952 less = The comparison to use. 953 ss = The swapping strategy. 954 r = The range to index. 955 index = The resulting index. 956 957 Returns: The pointer-based version returns a `SortedRange` wrapper 958 over index, of type 959 `SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b))` 960 thus reflecting the ordering of the 961 index. The index-based version returns `void` because the ordering 962 relation involves not only `index` but also `r`. 963 964 Throws: If the second argument's length is less than that of the range 965 indexed, an exception is thrown. 966 */ 967 SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b)) 968 makeIndex( 969 alias less = "a < b", 970 SwapStrategy ss = SwapStrategy.unstable, 971 Range, 972 RangeIndex) 973 (Range r, RangeIndex index) 974 if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex) 975 && is(ElementType!(RangeIndex) : ElementType!(Range)*) && hasAssignableElements!RangeIndex) 976 { 977 import std.algorithm.internal : addressOf; 978 import std.exception : enforce; 979 980 // assume collection already ordered 981 size_t i; 982 for (; !r.empty; r.popFront(), ++i) 983 index[i] = addressOf(r.front); 984 enforce(index.length == i); 985 // sort the index 986 sort!((a, b) => binaryFun!less(*a, *b), ss)(index); 987 return typeof(return)(index); 988 } 989 990 /// Ditto 991 void makeIndex( 992 alias less = "a < b", 993 SwapStrategy ss = SwapStrategy.unstable, 994 Range, 995 RangeIndex) 996 (Range r, RangeIndex index) 997 if (isRandomAccessRange!Range && !isInfinite!Range && 998 isRandomAccessRange!RangeIndex && !isInfinite!RangeIndex && 999 isIntegral!(ElementType!RangeIndex) && hasAssignableElements!RangeIndex) 1000 { 1001 import std.conv : to; 1002 import std.exception : enforce; 1003 1004 alias IndexType = Unqual!(ElementType!RangeIndex); 1005 enforce(r.length == index.length, 1006 "r and index must be same length for makeIndex."); 1007 static if (IndexType.sizeof < size_t.sizeof) 1008 { 1009 enforce(r.length <= size_t(1) + IndexType.max, "Cannot create an index with " ~ 1010 "element type " ~ IndexType.stringof ~ " with length " ~ 1011 to!string(r.length) ~ "."); 1012 } 1013 1014 // Use size_t as loop index to avoid overflow on ++i, 1015 // e.g. when squeezing 256 elements into a ubyte index. 1016 foreach (size_t i; 0 .. r.length) 1017 index[i] = cast(IndexType) i; 1018 1019 // sort the index 1020 sort!((a, b) => binaryFun!less(r[cast(size_t) a], r[cast(size_t) b]), ss) 1021 (index); 1022 } 1023 1024 /// 1025 @system unittest 1026 { 1027 immutable(int[]) arr = [ 2, 3, 1, 5, 0 ]; 1028 // index using pointers 1029 auto index1 = new immutable(int)*[arr.length]; 1030 makeIndex!("a < b")(arr, index1); 1031 assert(isSorted!("*a < *b")(index1)); 1032 // index using offsets 1033 auto index2 = new size_t[arr.length]; 1034 makeIndex!("a < b")(arr, index2); 1035 assert(isSorted! 1036 ((size_t a, size_t b){ return arr[a] < arr[b];}) 1037 (index2)); 1038 } 1039 1040 @system unittest 1041 { 1042 immutable(int)[] arr = [ 2, 3, 1, 5, 0 ]; 1043 // index using pointers 1044 auto index1 = new immutable(int)*[arr.length]; 1045 alias ImmRange = typeof(arr); 1046 alias ImmIndex = typeof(index1); 1047 static assert(isForwardRange!(ImmRange)); 1048 static assert(isRandomAccessRange!(ImmIndex)); 1049 static assert(!isIntegral!(ElementType!(ImmIndex))); 1050 static assert(is(ElementType!(ImmIndex) : ElementType!(ImmRange)*)); 1051 makeIndex!("a < b")(arr, index1); 1052 assert(isSorted!("*a < *b")(index1)); 1053 1054 // index using offsets 1055 auto index2 = new long[arr.length]; 1056 makeIndex(arr, index2); 1057 assert(isSorted! 1058 ((long a, long b){ 1059 return arr[cast(size_t) a] < arr[cast(size_t) b]; 1060 })(index2)); 1061 1062 // index strings using offsets 1063 string[] arr1 = ["I", "have", "no", "chocolate"]; 1064 auto index3 = new byte[arr1.length]; 1065 makeIndex(arr1, index3); 1066 assert(isSorted! 1067 ((byte a, byte b){ return arr1[a] < arr1[b];}) 1068 (index3)); 1069 } 1070 1071 @safe unittest 1072 { 1073 import std.algorithm.comparison : equal; 1074 import std.range : iota; 1075 1076 ubyte[256] index = void; 1077 iota(256).makeIndex(index[]); 1078 assert(index[].equal(iota(256))); 1079 byte[128] sindex = void; 1080 iota(128).makeIndex(sindex[]); 1081 assert(sindex[].equal(iota(128))); 1082 1083 auto index2 = new uint[10]; 1084 10.iota.makeIndex(index2); 1085 assert(index2.equal(10.iota)); 1086 } 1087 1088 struct Merge(alias less = "a < b", Rs...) 1089 if (Rs.length >= 2 && 1090 allSatisfy!(isInputRange, Rs) && 1091 !is(CommonType!(staticMap!(ElementType, Rs)) == void)) 1092 { 1093 public Rs source; 1094 private size_t _lastFrontIndex = size_t.max; 1095 static if (isBidirectional) 1096 { 1097 private size_t _lastBackIndex = size_t.max; // `size_t.max` means uninitialized, 1098 } 1099 1100 import std.functional : binaryFun; 1101 import std.meta : anySatisfy; 1102 import std.traits : isCopyable; 1103 1104 private alias comp = binaryFun!less; 1105 private alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); 1106 private enum isBidirectional = allSatisfy!(isBidirectionalRange, staticMap!(Unqual, Rs)); 1107 1108 debug private enum canCheckSortedness = isCopyable!ElementType && !hasAliasing!ElementType; 1109 1110 this(Rs source) 1111 { 1112 this.source = source; 1113 this._lastFrontIndex = frontIndex; 1114 } 1115 1116 static if (anySatisfy!(isInfinite, Rs)) 1117 { 1118 enum bool empty = false; // propagate infiniteness 1119 } 1120 else 1121 { 1122 @property bool empty() 1123 { 1124 return _lastFrontIndex == size_t.max; 1125 } 1126 } 1127 1128 @property auto ref front() 1129 { 1130 final switch (_lastFrontIndex) 1131 { 1132 foreach (i, _; Rs) 1133 { 1134 case i: 1135 assert(!source[i].empty, "Can not get front of empty Merge"); 1136 return source[i].front; 1137 } 1138 } 1139 } 1140 1141 private size_t frontIndex() 1142 { 1143 size_t bestIndex = size_t.max; // indicate undefined 1144 Unqual!ElementType bestElement; 1145 foreach (i, _; Rs) 1146 { 1147 if (source[i].empty) continue; 1148 if (bestIndex == size_t.max || // either this is the first or 1149 comp(source[i].front, bestElement)) 1150 { 1151 bestIndex = i; 1152 bestElement = source[i].front; 1153 } 1154 } 1155 return bestIndex; 1156 } 1157 1158 void popFront() 1159 { 1160 sw: final switch (_lastFrontIndex) 1161 { 1162 foreach (i, R; Rs) 1163 { 1164 case i: 1165 debug static if (canCheckSortedness) 1166 { 1167 ElementType previousFront = source[i].front(); 1168 } 1169 source[i].popFront(); 1170 debug static if (canCheckSortedness) 1171 { 1172 if (!source[i].empty) 1173 { 1174 assert(!comp(source[i].front, previousFront), 1175 "Input " ~ i.stringof ~ " is unsorted"); // @nogc 1176 } 1177 } 1178 break sw; 1179 } 1180 } 1181 _lastFrontIndex = frontIndex; 1182 } 1183 1184 static if (isBidirectional) 1185 { 1186 @property auto ref back() 1187 { 1188 if (_lastBackIndex == size_t.max) 1189 { 1190 this._lastBackIndex = backIndex; // lazy initialization 1191 } 1192 final switch (_lastBackIndex) 1193 { 1194 foreach (i, _; Rs) 1195 { 1196 case i: 1197 assert(!source[i].empty, "Can not get back of empty Merge"); 1198 return source[i].back; 1199 } 1200 } 1201 } 1202 1203 private size_t backIndex() 1204 { 1205 size_t bestIndex = size_t.max; // indicate undefined 1206 Unqual!ElementType bestElement; 1207 foreach (i, _; Rs) 1208 { 1209 if (source[i].empty) continue; 1210 if (bestIndex == size_t.max || // either this is the first or 1211 comp(bestElement, source[i].back)) 1212 { 1213 bestIndex = i; 1214 bestElement = source[i].back; 1215 } 1216 } 1217 return bestIndex; 1218 } 1219 1220 void popBack() 1221 { 1222 if (_lastBackIndex == size_t.max) 1223 { 1224 this._lastBackIndex = backIndex; // lazy initialization 1225 } 1226 sw: final switch (_lastBackIndex) 1227 { 1228 foreach (i, R; Rs) 1229 { 1230 case i: 1231 debug static if (canCheckSortedness) 1232 { 1233 ElementType previousBack = source[i].back(); 1234 } 1235 source[i].popBack(); 1236 debug static if (canCheckSortedness) 1237 { 1238 if (!source[i].empty) 1239 { 1240 assert(!comp(previousBack, source[i].back), 1241 "Input " ~ i.stringof ~ " is unsorted"); // @nogc 1242 } 1243 } 1244 break sw; 1245 } 1246 } 1247 _lastBackIndex = backIndex; 1248 if (_lastBackIndex == size_t.max) // if emptied 1249 { 1250 _lastFrontIndex = size_t.max; 1251 } 1252 } 1253 } 1254 1255 static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) 1256 { 1257 @property auto save() 1258 { 1259 auto result = this; 1260 foreach (i, _; Rs) 1261 { 1262 result.source[i] = result.source[i].save; 1263 } 1264 return result; 1265 } 1266 } 1267 1268 static if (allSatisfy!(hasLength, Rs)) 1269 { 1270 @property size_t length() 1271 { 1272 size_t result; 1273 foreach (i, _; Rs) 1274 { 1275 result += source[i].length; 1276 } 1277 return result; 1278 } 1279 1280 alias opDollar = length; 1281 } 1282 } 1283 1284 /** 1285 Merge multiple sorted ranges `rs` with less-than predicate function `pred` 1286 into one single sorted output range containing the sorted union of the 1287 elements of inputs. 1288 1289 Duplicates are not eliminated, meaning that the total 1290 number of elements in the output is the sum of all elements in the ranges 1291 passed to it; the `length` member is offered if all inputs also have 1292 `length`. The element types of all the inputs must have a common type 1293 `CommonType`. 1294 1295 Params: 1296 less = Predicate the given ranges are sorted by. 1297 rs = The ranges to compute the union for. 1298 1299 Returns: 1300 A range containing the union of the given ranges. 1301 1302 Details: 1303 1304 All of its inputs are assumed to be sorted. This can mean that inputs are 1305 instances of $(REF SortedRange, std,range). Use the result of $(REF sort, 1306 std,algorithm,sorting), or $(REF assumeSorted, std,range) to merge ranges 1307 known to be sorted (show in the example below). Note that there is currently 1308 no way of ensuring that two or more instances of $(REF SortedRange, 1309 std,range) are sorted using a specific comparison function `pred`. Therefore 1310 no checking is done here to assure that all inputs `rs` are instances of 1311 $(REF SortedRange, std,range). 1312 1313 This algorithm is lazy, doing work progressively as elements are pulled off 1314 the result. 1315 1316 Time complexity is proportional to the sum of element counts over all inputs. 1317 1318 If all inputs have the same element type and offer it by `ref`, output 1319 becomes a range with mutable `front` (and `back` where appropriate) that 1320 reflects in the original inputs. 1321 1322 If any of the inputs `rs` is infinite so is the result (`empty` being always 1323 `false`). 1324 1325 See_Also: $(REF multiwayMerge, std,algorithm,setops) for an analogous function 1326 that merges a dynamic number of ranges. 1327 */ 1328 Merge!(less, Rs) merge(alias less = "a < b", Rs...)(Rs rs) 1329 if (Rs.length >= 2 && 1330 allSatisfy!(isInputRange, Rs) && 1331 !is(CommonType!(staticMap!(ElementType, Rs)) == void)) 1332 { 1333 return typeof(return)(rs); 1334 } 1335 1336 /// 1337 @safe pure nothrow unittest 1338 { 1339 import std.algorithm.comparison : equal; 1340 import std.range : retro; 1341 1342 int[] a = [1, 3, 5]; 1343 int[] b = [2, 3, 4]; 1344 1345 assert(a.merge(b).equal([1, 2, 3, 3, 4, 5])); 1346 assert(a.merge(b).retro.equal([5, 4, 3, 3, 2, 1])); 1347 } 1348 1349 @safe pure nothrow unittest 1350 { 1351 import std.algorithm.comparison : equal; 1352 1353 int[] a = [ 1, 2, 4, 5, 7, 9 ]; 1354 int[] b = [ 0, 1, 2, 4, 7, 8 ]; 1355 double[] c = [ 10.5 ]; 1356 1357 assert(merge(a, b).length == a.length + b.length); 1358 assert(equal(merge(a, b), [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); 1359 assert(equal(merge(a, c, b), 1360 [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9, 10.5][])); 1361 auto u = merge(a, b); 1362 u.front--; 1363 assert(equal(u, [-1, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); 1364 } 1365 1366 @safe pure nothrow unittest 1367 { 1368 // save 1369 import std.range : dropOne; 1370 int[] a = [1, 2]; 1371 int[] b = [0, 3]; 1372 auto arr = a.merge(b); 1373 assert(arr.front == 0); 1374 assert(arr.save.dropOne.front == 1); 1375 assert(arr.front == 0); 1376 } 1377 1378 @safe pure nothrow unittest 1379 { 1380 import std.algorithm.comparison : equal; 1381 import std.internal.test.dummyrange; 1382 1383 auto dummyResult1 = [1, 1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; 1384 auto dummyResult2 = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1385 6, 6, 7, 7, 8, 8, 9, 9, 10, 10]; 1386 foreach (DummyType; AllDummyRanges) 1387 { 1388 DummyType d; 1389 assert(d.merge([1, 1.5, 5.5]).equal(dummyResult1)); 1390 assert(d.merge(d).equal(dummyResult2)); 1391 } 1392 } 1393 1394 @nogc @safe pure nothrow unittest 1395 { 1396 import std.algorithm.comparison : equal; 1397 1398 static immutable a = [1, 3, 5]; 1399 static immutable b = [2, 3, 4]; 1400 static immutable r = [1, 2, 3, 3, 4, 5]; 1401 assert(a.merge(b).equal(r)); 1402 } 1403 1404 /// test bi-directional access and common type 1405 @safe pure nothrow unittest 1406 { 1407 import std.algorithm.comparison : equal; 1408 import std.range : retro; 1409 import std.traits : CommonType; 1410 1411 alias S = short; 1412 alias I = int; 1413 alias D = double; 1414 1415 S[] a = [1, 2, 3]; 1416 I[] b = [50, 60]; 1417 D[] c = [10, 20, 30, 40]; 1418 1419 auto m = merge(a, b, c); 1420 1421 static assert(is(typeof(m.front) == CommonType!(S, I, D))); 1422 1423 assert(equal(m, [1, 2, 3, 10, 20, 30, 40, 50, 60])); 1424 assert(equal(m.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1])); 1425 1426 m.popFront(); 1427 assert(equal(m, [2, 3, 10, 20, 30, 40, 50, 60])); 1428 m.popBack(); 1429 assert(equal(m, [2, 3, 10, 20, 30, 40, 50])); 1430 m.popFront(); 1431 assert(equal(m, [3, 10, 20, 30, 40, 50])); 1432 m.popBack(); 1433 assert(equal(m, [3, 10, 20, 30, 40])); 1434 m.popFront(); 1435 assert(equal(m, [10, 20, 30, 40])); 1436 m.popBack(); 1437 assert(equal(m, [10, 20, 30])); 1438 m.popFront(); 1439 assert(equal(m, [20, 30])); 1440 m.popBack(); 1441 assert(equal(m, [20])); 1442 m.popFront(); 1443 assert(m.empty); 1444 } 1445 1446 // Issue 21810: Check for sortedness must not use `==` 1447 @nogc @safe pure nothrow unittest 1448 { 1449 import std.algorithm.comparison : equal; 1450 import std.typecons : tuple; 1451 1452 static immutable a = [ 1453 tuple(1, 1), 1454 tuple(3, 1), 1455 tuple(3, 2), 1456 tuple(5, 1), 1457 ]; 1458 static immutable b = [ 1459 tuple(2, 1), 1460 tuple(3, 1), 1461 tuple(4, 1), 1462 tuple(4, 2), 1463 ]; 1464 static immutable r = [ 1465 tuple(1, 1), 1466 tuple(2, 1), 1467 tuple(3, 1), 1468 tuple(3, 2), 1469 tuple(3, 1), 1470 tuple(4, 1), 1471 tuple(4, 2), 1472 tuple(5, 1), 1473 ]; 1474 assert(merge!"a[0] < b[0]"(a, b).equal(r)); 1475 } 1476 1477 private template validPredicates(E, less...) 1478 { 1479 static if (less.length == 0) 1480 enum validPredicates = true; 1481 else static if (less.length == 1 && is(typeof(less[0]) == SwapStrategy)) 1482 enum validPredicates = true; 1483 else 1484 enum validPredicates = 1485 is(typeof((E a, E b){ bool r = binaryFun!(less[0])(a, b); })) 1486 && validPredicates!(E, less[1 .. $]); 1487 } 1488 1489 /** 1490 Sorts a range by multiple keys. 1491 1492 The call $(D multiSort!("a.id < b.id", 1493 "a.date > b.date")(r)) sorts the range `r` by `id` ascending, 1494 and sorts elements that have the same `id` by `date` 1495 descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id 1496 < b.id : a.date > b.date"(r)), but `multiSort` is faster because it 1497 does fewer comparisons (in addition to being more convenient). 1498 1499 Returns: 1500 The initial range wrapped as a `SortedRange` with its predicates 1501 converted to an equivalent single predicate. 1502 */ 1503 template multiSort(less...) //if (less.length > 1) 1504 { 1505 auto multiSort(Range)(Range r) 1506 if (validPredicates!(ElementType!Range, less) && hasSwappableElements!Range) 1507 { 1508 import std.meta : AliasSeq; 1509 import std.range : assumeSorted; 1510 static if (is(typeof(less[$ - 1]) == SwapStrategy)) 1511 { 1512 enum ss = less[$ - 1]; 1513 alias funs = less[0 .. $ - 1]; 1514 } 1515 else 1516 { 1517 enum ss = SwapStrategy.unstable; 1518 alias funs = less; 1519 } 1520 1521 static if (funs.length == 0) 1522 static assert(false, "No sorting predicate provided for multiSort"); 1523 else 1524 static if (funs.length == 1) 1525 return sort!(funs[0], ss, Range)(r); 1526 else 1527 { 1528 multiSortImpl!(Range, ss, funs)(r); 1529 return assumeSorted!(multiSortPredFun!(Range, funs))(r); 1530 } 1531 } 1532 } 1533 1534 /// 1535 @safe unittest 1536 { 1537 import std.algorithm.mutation : SwapStrategy; 1538 static struct Point { int x, y; } 1539 auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ]; 1540 auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ]; 1541 multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1); 1542 assert(pts1 == pts2); 1543 } 1544 1545 private bool multiSortPredFun(Range, funs...)(ElementType!Range a, ElementType!Range b) 1546 { 1547 foreach (f; funs) 1548 { 1549 alias lessFun = binaryFun!(f); 1550 if (lessFun(a, b)) return true; 1551 if (lessFun(b, a)) return false; 1552 } 1553 return false; 1554 } 1555 1556 private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r) 1557 { 1558 alias lessFun = binaryFun!(funs[0]); 1559 1560 static if (funs.length > 1) 1561 { 1562 while (r.length > 1) 1563 { 1564 auto p = getPivot!lessFun(r); 1565 auto t = partition3!(funs[0], ss)(r, r[p]); 1566 if (t[0].length <= t[2].length) 1567 { 1568 multiSortImpl!(Range, ss, funs)(t[0]); 1569 multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); 1570 r = t[2]; 1571 } 1572 else 1573 { 1574 multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); 1575 multiSortImpl!(Range, ss, funs)(t[2]); 1576 r = t[0]; 1577 } 1578 } 1579 } 1580 else 1581 { 1582 sort!(lessFun, ss)(r); 1583 } 1584 } 1585 1586 @safe unittest 1587 { 1588 import std.algorithm.comparison : equal; 1589 import std.range; 1590 1591 static struct Point { int x, y; } 1592 auto pts1 = [ Point(5, 6), Point(1, 0), Point(5, 7), Point(1, 1), Point(1, 2), Point(0, 1) ]; 1593 auto pts2 = [ Point(0, 1), Point(1, 0), Point(1, 1), Point(1, 2), Point(5, 6), Point(5, 7) ]; 1594 static assert(validPredicates!(Point, "a.x < b.x", "a.y < b.y")); 1595 multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1); 1596 assert(pts1 == pts2); 1597 1598 auto pts3 = indexed(pts1, iota(pts1.length)); 1599 assert(pts3.multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable).release.equal(pts2)); 1600 1601 auto pts4 = iota(10).array; 1602 assert(pts4.multiSort!("a > b").release.equal(iota(10).retro)); 1603 } 1604 1605 //https://issues.dlang.org/show_bug.cgi?id=9160 (L-value only comparators) 1606 @safe unittest 1607 { 1608 static struct A 1609 { 1610 int x; 1611 int y; 1612 } 1613 1614 static bool byX(const ref A lhs, const ref A rhs) 1615 { 1616 return lhs.x < rhs.x; 1617 } 1618 1619 static bool byY(const ref A lhs, const ref A rhs) 1620 { 1621 return lhs.y < rhs.y; 1622 } 1623 1624 auto points = [ A(4, 1), A(2, 4)]; 1625 multiSort!(byX, byY)(points); 1626 assert(points[0] == A(2, 4)); 1627 assert(points[1] == A(4, 1)); 1628 } 1629 1630 // cannot access frame of function 1631 // https://issues.dlang.org/show_bug.cgi?id=16179 1632 @safe unittest 1633 { 1634 auto arr = [[1, 2], [2, 0], [1, 0], [1, 1]]; 1635 int c = 3; 1636 1637 arr.multiSort!( 1638 (a, b) => a[0] < b[0], 1639 (a, b) => c*a[1] < c*b[1] 1640 ); 1641 assert(arr == [[1, 0], [1, 1], [1, 2], [2, 0]]); 1642 } 1643 1644 // https://issues.dlang.org/show_bug.cgi?id=16413 - @system comparison function 1645 @system unittest 1646 { 1647 static @system bool lt(int a, int b) { return a < b; } 1648 auto a = [2, 1]; 1649 a.multiSort!(lt, lt); 1650 assert(a == [1, 2]); 1651 } 1652 1653 private size_t getPivot(alias less, Range)(Range r) 1654 { 1655 auto mid = r.length / 2; 1656 if (r.length < 512) 1657 { 1658 if (r.length >= 32) 1659 medianOf!less(r, size_t(0), mid, r.length - 1); 1660 return mid; 1661 } 1662 1663 // The plan here is to take the median of five by taking five elements in 1664 // the array, segregate around their median, and return the position of the 1665 // third. We choose first, mid, last, and two more in between those. 1666 1667 auto quarter = r.length / 4; 1668 medianOf!less(r, 1669 size_t(0), mid - quarter, mid, mid + quarter, r.length - 1); 1670 return mid; 1671 } 1672 1673 /* 1674 Sorting routine that is optimized for short ranges. Note: uses insertion sort 1675 going downward. Benchmarked a similar routine that goes upward, for some reason 1676 it's slower. 1677 */ 1678 private void shortSort(alias less, Range)(Range r) 1679 { 1680 import std.algorithm.mutation : swapAt; 1681 alias pred = binaryFun!(less); 1682 1683 switch (r.length) 1684 { 1685 case 0: case 1: 1686 return; 1687 case 2: 1688 if (pred(r[1], r[0])) r.swapAt(0, 1); 1689 return; 1690 case 3: 1691 if (pred(r[2], r[0])) 1692 { 1693 if (pred(r[0], r[1])) 1694 { 1695 r.swapAt(0, 1); 1696 r.swapAt(0, 2); 1697 } 1698 else 1699 { 1700 r.swapAt(0, 2); 1701 if (pred(r[1], r[0])) r.swapAt(0, 1); 1702 } 1703 } 1704 else 1705 { 1706 if (pred(r[1], r[0])) 1707 { 1708 r.swapAt(0, 1); 1709 } 1710 else 1711 { 1712 if (pred(r[2], r[1])) r.swapAt(1, 2); 1713 } 1714 } 1715 return; 1716 case 4: 1717 if (pred(r[1], r[0])) r.swapAt(0, 1); 1718 if (pred(r[3], r[2])) r.swapAt(2, 3); 1719 if (pred(r[2], r[0])) r.swapAt(0, 2); 1720 if (pred(r[3], r[1])) r.swapAt(1, 3); 1721 if (pred(r[2], r[1])) r.swapAt(1, 2); 1722 return; 1723 default: 1724 sort5!pred(r[r.length - 5 .. r.length]); 1725 if (r.length == 5) return; 1726 break; 1727 } 1728 1729 assert(r.length >= 6, "r must have more than 5 elements"); 1730 /* The last 5 elements of the range are sorted. Proceed with expanding the 1731 sorted portion downward. */ 1732 immutable maxJ = r.length - 2; 1733 for (size_t i = r.length - 6; ; --i) 1734 { 1735 static if (is(typeof(() nothrow 1736 { 1737 auto t = r[0]; if (pred(t, r[0])) r[0] = r[0]; 1738 }))) // Can we afford to temporarily invalidate the array? 1739 { 1740 import core.lifetime : move; 1741 1742 size_t j = i + 1; 1743 static if (hasLvalueElements!Range) 1744 auto temp = move(r[i]); 1745 else 1746 auto temp = r[i]; 1747 1748 if (pred(r[j], temp)) 1749 { 1750 do 1751 { 1752 static if (hasLvalueElements!Range) 1753 trustedMoveEmplace(r[j], r[j - 1]); 1754 else 1755 r[j - 1] = r[j]; 1756 ++j; 1757 } 1758 while (j < r.length && pred(r[j], temp)); 1759 1760 static if (hasLvalueElements!Range) 1761 trustedMoveEmplace(temp, r[j - 1]); 1762 else 1763 r[j - 1] = move(temp); 1764 } 1765 } 1766 else 1767 { 1768 size_t j = i; 1769 while (pred(r[j + 1], r[j])) 1770 { 1771 r.swapAt(j, j + 1); 1772 if (j == maxJ) break; 1773 ++j; 1774 } 1775 } 1776 if (i == 0) break; 1777 } 1778 } 1779 1780 /// @trusted wrapper for moveEmplace 1781 private void trustedMoveEmplace(T)(ref T source, ref T target) @trusted 1782 { 1783 import core.lifetime : moveEmplace; 1784 moveEmplace(source, target); 1785 } 1786 1787 @safe unittest 1788 { 1789 import std.random : Random = Xorshift, uniform; 1790 1791 auto rnd = Random(1); 1792 auto a = new int[uniform(100, 200, rnd)]; 1793 foreach (ref e; a) 1794 { 1795 e = uniform(-100, 100, rnd); 1796 } 1797 1798 shortSort!(binaryFun!("a < b"), int[])(a); 1799 assert(isSorted(a)); 1800 } 1801 1802 /* 1803 Sorts the first 5 elements exactly of range r. 1804 */ 1805 private void sort5(alias lt, Range)(Range r) 1806 { 1807 assert(r.length >= 5, "r must have more than 4 elements"); 1808 1809 import std.algorithm.mutation : swapAt; 1810 1811 // 1. Sort first two pairs 1812 if (lt(r[1], r[0])) r.swapAt(0, 1); 1813 if (lt(r[3], r[2])) r.swapAt(2, 3); 1814 1815 // 2. Arrange first two pairs by the largest element 1816 if (lt(r[3], r[1])) 1817 { 1818 r.swapAt(0, 2); 1819 r.swapAt(1, 3); 1820 } 1821 assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[3], r[2]), "unexpected" 1822 ~ " order"); 1823 1824 // 3. Insert 4 into [0, 1, 3] 1825 if (lt(r[4], r[1])) 1826 { 1827 r.swapAt(3, 4); 1828 r.swapAt(1, 3); 1829 if (lt(r[1], r[0])) 1830 { 1831 r.swapAt(0, 1); 1832 } 1833 } 1834 else if (lt(r[4], r[3])) 1835 { 1836 r.swapAt(3, 4); 1837 } 1838 assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[4], r[3]), "unexpected" 1839 ~ " order"); 1840 1841 // 4. Insert 2 into [0, 1, 3, 4] (note: we already know the last is greater) 1842 assert(!lt(r[4], r[2]), "unexpected order"); 1843 if (lt(r[2], r[1])) 1844 { 1845 r.swapAt(1, 2); 1846 if (lt(r[1], r[0])) 1847 { 1848 r.swapAt(0, 1); 1849 } 1850 } 1851 else if (lt(r[3], r[2])) 1852 { 1853 r.swapAt(2, 3); 1854 } 1855 // 7 comparisons, 0-9 swaps 1856 } 1857 1858 @safe unittest 1859 { 1860 import std.algorithm.iteration : permutations; 1861 import std.algorithm.mutation : copy; 1862 import std.range : iota; 1863 1864 int[5] buf; 1865 foreach (per; iota(5).permutations) 1866 { 1867 per.copy(buf[]); 1868 sort5!((a, b) => a < b)(buf[]); 1869 assert(buf[].isSorted); 1870 } 1871 } 1872 1873 // sort 1874 /** 1875 Sorts a random-access range according to the predicate `less`. 1876 1877 Performs $(BIGOH r.length * log(r.length)) evaluations of `less`. If `less` involves 1878 expensive computations on the _sort key, it may be worthwhile to use 1879 $(LREF schwartzSort) instead. 1880 1881 Stable sorting requires `hasAssignableElements!Range` to be true. 1882 1883 `sort` returns a $(REF SortedRange, std,range) over the original range, 1884 allowing functions that can take advantage of sorted data to know that the 1885 range is sorted and adjust accordingly. The $(REF SortedRange, std,range) is a 1886 wrapper around the original range, so both it and the original range are sorted. 1887 Other functions can't know that the original range has been sorted, but 1888 they $(I can) know that $(REF SortedRange, std,range) has been sorted. 1889 1890 Preconditions: 1891 1892 The predicate is expected to satisfy certain rules in order for `sort` to 1893 behave as expected - otherwise, the program may fail on certain inputs (but not 1894 others) when not compiled in release mode, due to the cursory `assumeSorted` 1895 check. Specifically, `sort` expects `less(a,b) && less(b,c)` to imply 1896 `less(a,c)` (transitivity), and, conversely, `!less(a,b) && !less(b,c)` to 1897 imply `!less(a,c)`. Note that the default predicate (`"a < b"`) does not 1898 always satisfy these conditions for floating point types, because the expression 1899 will always be `false` when either `a` or `b` is NaN. 1900 Use $(REF cmp, std,math) instead. 1901 1902 Params: 1903 less = The predicate to sort by. 1904 ss = The swapping strategy to use. 1905 r = The range to sort. 1906 1907 Returns: The initial range wrapped as a `SortedRange` with the predicate 1908 `binaryFun!less`. 1909 1910 Algorithms: $(HTTP en.wikipedia.org/wiki/Introsort, Introsort) is used for unstable sorting and 1911 $(HTTP en.wikipedia.org/wiki/Timsort, Timsort) is used for stable sorting. 1912 Each algorithm has benefits beyond stability. Introsort is generally faster but 1913 Timsort may achieve greater speeds on data with low entropy or if predicate calls 1914 are expensive. Introsort performs no allocations whereas Timsort will perform one 1915 or more allocations per call. Both algorithms have $(BIGOH n log n) worst-case 1916 time complexity. 1917 1918 See_Also: 1919 $(REF assumeSorted, std,range)$(BR) 1920 $(REF SortedRange, std,range)$(BR) 1921 $(REF SwapStrategy, std,algorithm,mutation)$(BR) 1922 $(REF binaryFun, std,functional) 1923 */ 1924 SortedRange!(Range, less) 1925 sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range) 1926 (Range r) 1927 /+ Unstable sorting uses the quicksort algorithm, which uses swapAt, 1928 which either uses swap(...), requiring swappable elements, or just 1929 swaps using assignment. 1930 Stable sorting uses TimSort, which needs to copy elements into a buffer, 1931 requiring assignable elements. +/ 1932 { 1933 import std.range : assumeSorted; 1934 static if (ss == SwapStrategy.unstable) 1935 { 1936 static assert(hasSwappableElements!Range || hasAssignableElements!Range, 1937 "When using SwapStrategy.unstable, the passed Range '" 1938 ~ Range.stringof ~ "' must" 1939 ~ " either fulfill hasSwappableElements, or" 1940 ~ " hasAssignableElements, both were not the case"); 1941 } 1942 else 1943 { 1944 static assert(hasAssignableElements!Range, "When using a SwapStrategy" 1945 ~ " != unstable, the" 1946 ~ " passed Range '" ~ Range.stringof ~ "' must fulfill" 1947 ~ " hasAssignableElements, which it did not"); 1948 } 1949 1950 static assert(isRandomAccessRange!Range, "The passed Range '" 1951 ~ Range.stringof ~ "' must be a Random AccessRange " 1952 ~ "(isRandomAccessRange)"); 1953 1954 static assert(hasSlicing!Range, "The passed Range '" 1955 ~ Range.stringof ~ "' must allow Slicing (hasSlicing)"); 1956 1957 static assert(hasLength!Range, "The passed Range '" 1958 ~ Range.stringof ~ "' must have a length (hasLength)"); 1959 1960 alias lessFun = binaryFun!(less); 1961 alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun 1962 1963 static assert(is(LessRet == bool), "The return type of the template" 1964 ~ " argument 'less' when used with the binaryFun!less template" 1965 ~ " must be a bool. This is not the case, the returned type is '" 1966 ~ LessRet.stringof ~ "'"); 1967 1968 static if (ss == SwapStrategy.unstable) 1969 quickSortImpl!(lessFun)(r, r.length); 1970 else //use Tim Sort for semistable & stable 1971 TimSortImpl!(lessFun, Range).sort(r, null); 1972 1973 assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); 1974 return assumeSorted!less(r); 1975 } 1976 1977 /// 1978 @safe pure nothrow unittest 1979 { 1980 int[] array = [ 1, 2, 3, 4 ]; 1981 1982 // sort in descending order 1983 array.sort!("a > b"); 1984 assert(array == [ 4, 3, 2, 1 ]); 1985 1986 // sort in ascending order 1987 array.sort(); 1988 assert(array == [ 1, 2, 3, 4 ]); 1989 1990 // sort with reusable comparator and chain 1991 alias myComp = (x, y) => x > y; 1992 assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]); 1993 } 1994 1995 /// 1996 @safe unittest 1997 { 1998 // Showcase stable sorting 1999 import std.algorithm.mutation : SwapStrategy; 2000 string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; 2001 sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words); 2002 assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]); 2003 } 2004 2005 /// 2006 @safe unittest 2007 { 2008 // Sorting floating-point numbers in presence of NaN 2009 double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan]; 2010 2011 import std.algorithm.comparison : equal; 2012 import std.math.operations : cmp; 2013 import std.math.traits : isIdentical; 2014 2015 sort!((a, b) => cmp(a, b) < 0)(numbers); 2016 2017 double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan]; 2018 assert(numbers.equal!isIdentical(sorted)); 2019 } 2020 2021 @safe unittest 2022 { 2023 // Simple regression benchmark 2024 import std.algorithm.iteration, std.algorithm.mutation; 2025 import std.array : array; 2026 import std.random : Random, uniform; 2027 import std.range : iota; 2028 Random rng; 2029 int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array; 2030 static uint comps; 2031 static bool less(int a, int b) { ++comps; return a < b; } 2032 sort!less(a); // random numbers 2033 sort!less(a); // sorted ascending 2034 a.reverse(); 2035 sort!less(a); // sorted descending 2036 a[] = 0; 2037 sort!less(a); // all equal 2038 2039 // This should get smaller with time. On occasion it may go larger, but only 2040 // if there's thorough justification. 2041 debug enum uint watermark = 1676220; 2042 else enum uint watermark = 1676220; 2043 2044 import std.conv; 2045 assert(comps <= watermark, text("You seem to have pessimized sort! ", 2046 watermark, " < ", comps)); 2047 assert(comps >= watermark, text("You seem to have improved sort!", 2048 " Please update watermark from ", watermark, " to ", comps)); 2049 } 2050 2051 @safe unittest 2052 { 2053 import std.algorithm.internal : rndstuff; 2054 import std.algorithm.mutation : swapRanges; 2055 import std.random : Random = Xorshift, uniform; 2056 import std.uni : toUpper; 2057 2058 // sort using delegate 2059 auto a = new int[100]; 2060 auto rnd = Random(123_456_789); 2061 foreach (ref e; a) 2062 { 2063 e = uniform(-100, 100, rnd); 2064 } 2065 2066 int i = 0; 2067 bool greater2(int a, int b) @safe { return a + i > b + i; } 2068 auto greater = &greater2; 2069 sort!(greater)(a); 2070 assert(isSorted!(greater)(a)); 2071 2072 // sort using string 2073 sort!("a < b")(a); 2074 assert(isSorted!("a < b")(a)); 2075 2076 // sort using function; all elements equal 2077 foreach (ref e; a) 2078 { 2079 e = 5; 2080 } 2081 static bool less(int a, int b) { return a < b; } 2082 sort!(less)(a); 2083 assert(isSorted!(less)(a)); 2084 2085 string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; 2086 bool lessi(string a, string b) { return toUpper(a) < toUpper(b); } 2087 sort!(lessi, SwapStrategy.stable)(words); 2088 assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]); 2089 2090 // sort using ternary predicate 2091 //sort!("b - a")(a); 2092 //assert(isSorted!(less)(a)); 2093 2094 a = rndstuff!(int)(); 2095 sort(a); 2096 assert(isSorted(a)); 2097 auto b = rndstuff!(string)(); 2098 sort!("toLower(a) < toLower(b)")(b); 2099 assert(isSorted!("toUpper(a) < toUpper(b)")(b)); 2100 2101 { 2102 // https://issues.dlang.org/show_bug.cgi?id=10317 2103 enum E_10317 { a, b } 2104 auto a_10317 = new E_10317[10]; 2105 sort(a_10317); 2106 } 2107 2108 { 2109 // https://issues.dlang.org/show_bug.cgi?id=7767 2110 // Unstable sort should complete without an excessive number of predicate calls 2111 // This would suggest it's running in quadratic time 2112 2113 // Compilation error if predicate is not static, i.e. a nested function 2114 static uint comp; 2115 static bool pred(size_t a, size_t b) 2116 { 2117 ++comp; 2118 return a < b; 2119 } 2120 2121 size_t[] arr; 2122 arr.length = 1024; 2123 2124 foreach (k; 0 .. arr.length) arr[k] = k; 2125 swapRanges(arr[0..$/2], arr[$/2..$]); 2126 2127 sort!(pred, SwapStrategy.unstable)(arr); 2128 assert(comp < 25_000); 2129 } 2130 2131 { 2132 import std.algorithm.mutation : swap; 2133 2134 bool proxySwapCalled; 2135 struct S 2136 { 2137 int i; 2138 alias i this; 2139 void proxySwap(ref S other) { swap(i, other.i); proxySwapCalled = true; } 2140 @disable void opAssign(S value); 2141 } 2142 2143 alias R = S[]; 2144 R r = [S(3), S(2), S(1)]; 2145 static assert(hasSwappableElements!R); 2146 static assert(!hasAssignableElements!R); 2147 r.sort(); 2148 assert(proxySwapCalled); 2149 } 2150 2151 // https://issues.dlang.org/show_bug.cgi?id=20751 2152 { 2153 static bool refPred(ref int a, ref int b) 2154 { 2155 return a < b; 2156 } 2157 2158 auto sortedArr = [5,4,3,2,1].sort!refPred; 2159 sortedArr.equalRange(3); 2160 } 2161 } 2162 2163 private void quickSortImpl(alias less, Range)(Range r, size_t depth) 2164 { 2165 import std.algorithm.comparison : min, max; 2166 import std.algorithm.mutation : swap, swapAt; 2167 2168 alias Elem = ElementType!(Range); 2169 enum int size = Elem.sizeof; 2170 enum size_t shortSortGetsBetter = max(32, 1024 / size); 2171 static assert(shortSortGetsBetter >= 1, Elem.stringof ~ " " 2172 ~ size.stringof); 2173 2174 // partition 2175 while (r.length > shortSortGetsBetter) 2176 { 2177 if (depth == 0) 2178 { 2179 HeapOps!(less, Range).heapSort(r); 2180 return; 2181 } 2182 depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3; 2183 2184 const pivotIdx = getPivot!(less)(r); 2185 auto pivot = r[pivotIdx]; 2186 2187 // partition 2188 r.swapAt(pivotIdx, r.length - 1); 2189 size_t lessI = size_t.max, greaterI = r.length - 1; 2190 2191 outer: for (;;) 2192 { 2193 alias pred = binaryFun!less; 2194 while (pred(r[++lessI], pivot)) {} 2195 assert(lessI <= greaterI, "sort: invalid comparison function."); 2196 for (;;) 2197 { 2198 if (greaterI == lessI) break outer; 2199 if (!pred(pivot, r[--greaterI])) break; 2200 } 2201 assert(lessI <= greaterI, "sort: invalid comparison function."); 2202 if (lessI == greaterI) break; 2203 r.swapAt(lessI, greaterI); 2204 } 2205 2206 r.swapAt(r.length - 1, lessI); 2207 auto left = r[0 .. lessI], right = r[lessI + 1 .. r.length]; 2208 if (right.length > left.length) 2209 { 2210 swap(left, right); 2211 } 2212 .quickSortImpl!(less, Range)(right, depth); 2213 r = left; 2214 } 2215 // residual sort 2216 static if (shortSortGetsBetter > 1) 2217 { 2218 shortSort!(less, Range)(r); 2219 } 2220 } 2221 2222 // Heap operations for random-access ranges 2223 package(std) template HeapOps(alias less, Range) 2224 { 2225 import std.algorithm.mutation : swapAt; 2226 2227 static assert(isRandomAccessRange!Range, Range.stringof ~ " must be a" 2228 ~ " RandomAccessRange"); 2229 static assert(hasLength!Range, Range.stringof ~ " must have length"); 2230 static assert(hasSwappableElements!Range || hasAssignableElements!Range, 2231 Range.stringof ~ " must have swappable or assignable Elements"); 2232 2233 alias lessFun = binaryFun!less; 2234 2235 //template because of https://issues.dlang.org/show_bug.cgi?id=12410 2236 void heapSort()(Range r) 2237 { 2238 // If true, there is nothing to do 2239 if (r.length < 2) return; 2240 // Build Heap 2241 buildHeap(r); 2242 // Sort 2243 for (size_t i = r.length - 1; i > 0; --i) 2244 { 2245 r.swapAt(0, i); 2246 percolate(r, 0, i); 2247 } 2248 } 2249 2250 //template because of https://issues.dlang.org/show_bug.cgi?id=12410 2251 void buildHeap()(Range r) 2252 { 2253 immutable n = r.length; 2254 for (size_t i = n / 2; i-- > 0; ) 2255 { 2256 siftDown(r, i, n); 2257 } 2258 assert(isHeap(r), "r is not a heap"); 2259 } 2260 2261 bool isHeap()(Range r) 2262 { 2263 size_t parent = 0; 2264 foreach (child; 1 .. r.length) 2265 { 2266 if (lessFun(r[parent], r[child])) return false; 2267 // Increment parent every other pass 2268 parent += !(child & 1); 2269 } 2270 return true; 2271 } 2272 2273 // Sifts down r[parent] (which is initially assumed to be messed up) so the 2274 // heap property is restored for r[parent .. end]. 2275 // template because of https://issues.dlang.org/show_bug.cgi?id=12410 2276 void siftDown()(Range r, size_t parent, immutable size_t end) 2277 { 2278 for (;;) 2279 { 2280 auto child = (parent + 1) * 2; 2281 if (child >= end) 2282 { 2283 // Leftover left child? 2284 if (child == end && lessFun(r[parent], r[--child])) 2285 r.swapAt(parent, child); 2286 break; 2287 } 2288 auto leftChild = child - 1; 2289 if (lessFun(r[child], r[leftChild])) child = leftChild; 2290 if (!lessFun(r[parent], r[child])) break; 2291 r.swapAt(parent, child); 2292 parent = child; 2293 } 2294 } 2295 2296 // Alternate version of siftDown that performs fewer comparisons, see 2297 // https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort. The percolate 2298 // process first sifts the parent all the way down (without comparing it 2299 // against the leaves), and then a bit up until the heap property is 2300 // restored. So there are more swaps but fewer comparisons. Gains are made 2301 // when the final position is likely to end toward the bottom of the heap, 2302 // so not a lot of sifts back are performed. 2303 //template because of https://issues.dlang.org/show_bug.cgi?id=12410 2304 void percolate()(Range r, size_t parent, immutable size_t end) 2305 { 2306 immutable root = parent; 2307 2308 // Sift down 2309 for (;;) 2310 { 2311 auto child = (parent + 1) * 2; 2312 2313 if (child >= end) 2314 { 2315 if (child == end) 2316 { 2317 // Leftover left node. 2318 --child; 2319 r.swapAt(parent, child); 2320 parent = child; 2321 } 2322 break; 2323 } 2324 2325 auto leftChild = child - 1; 2326 if (lessFun(r[child], r[leftChild])) child = leftChild; 2327 r.swapAt(parent, child); 2328 parent = child; 2329 } 2330 2331 // Sift up 2332 for (auto child = parent; child > root; child = parent) 2333 { 2334 parent = (child - 1) / 2; 2335 if (!lessFun(r[parent], r[child])) break; 2336 r.swapAt(parent, child); 2337 } 2338 } 2339 } 2340 2341 // Tim Sort implementation 2342 private template TimSortImpl(alias pred, R) 2343 { 2344 import core.bitop : bsr; 2345 import std.array : uninitializedArray; 2346 2347 static assert(isRandomAccessRange!R, R.stringof ~ " must be a" 2348 ~ " RandomAccessRange"); 2349 static assert(hasLength!R, R.stringof ~ " must have a length"); 2350 static assert(hasSlicing!R, R.stringof ~ " must support slicing"); 2351 static assert(hasAssignableElements!R, R.stringof ~ " must have" 2352 ~ " assignable elements"); 2353 2354 alias T = ElementType!R; 2355 2356 alias less = binaryFun!pred; 2357 bool greater()(auto ref T a, auto ref T b) { return less(b, a); } 2358 bool greaterEqual()(auto ref T a, auto ref T b) { return !less(a, b); } 2359 bool lessEqual()(auto ref T a, auto ref T b) { return !less(b, a); } 2360 2361 enum minimalMerge = 128; 2362 enum minimalGallop = 7; 2363 enum minimalStorage = 256; 2364 enum stackSize = 40; 2365 2366 struct Slice{ size_t base, length; } 2367 2368 // Entry point for tim sort 2369 void sort()(R range, T[] temp) 2370 { 2371 import std.algorithm.comparison : min; 2372 import std.format : format; 2373 2374 // Do insertion sort on small range 2375 if (range.length <= minimalMerge) 2376 { 2377 binaryInsertionSort(range); 2378 return; 2379 } 2380 2381 immutable minRun = minRunLength(range.length); 2382 immutable minTemp = min(range.length / 2, minimalStorage); 2383 size_t minGallop = minimalGallop; 2384 Slice[stackSize] stack = void; 2385 size_t stackLen = 0; 2386 2387 // Allocate temporary memory if not provided by user 2388 if (temp.length < minTemp) 2389 { 2390 static if (hasElaborateAssign!T) temp = new T[](minTemp); 2391 else temp = () @trusted { return uninitializedArray!(T[])(minTemp); }(); 2392 } 2393 2394 for (size_t i = 0; i < range.length; ) 2395 { 2396 // Find length of first run in list 2397 size_t runLen = firstRun(range[i .. range.length]); 2398 2399 // If run has less than minRun elements, extend using insertion sort 2400 if (runLen < minRun) 2401 { 2402 // Do not run farther than the length of the range 2403 immutable force = range.length - i > minRun ? minRun : range.length - i; 2404 binaryInsertionSort(range[i .. i + force], runLen); 2405 runLen = force; 2406 } 2407 2408 // Push run onto stack 2409 stack[stackLen++] = Slice(i, runLen); 2410 i += runLen; 2411 2412 // Collapse stack so that (e1 > e2 + e3 && e2 > e3) 2413 // STACK is | ... e1 e2 e3 > 2414 while (stackLen > 1) 2415 { 2416 immutable run4 = stackLen - 1; 2417 immutable run3 = stackLen - 2; 2418 immutable run2 = stackLen - 3; 2419 immutable run1 = stackLen - 4; 2420 2421 if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) || 2422 (stackLen > 3 && stack[run1].length <= stack[run3].length + stack[run2].length) ) 2423 { 2424 immutable at = stack[run2].length < stack[run4].length ? run2 : run3; 2425 mergeAt(range, stack[0 .. stackLen], at, minGallop, temp); 2426 } 2427 else if (stack[run3].length > stack[run4].length) break; 2428 else mergeAt(range, stack[0 .. stackLen], run3, minGallop, temp); 2429 2430 stackLen -= 1; 2431 } 2432 2433 // Assert that the code above established the invariant correctly 2434 version (StdUnittest) 2435 { 2436 if (stackLen == 2) 2437 { 2438 assert(stack[0].length > stack[1].length, format! 2439 "stack[0].length %s > stack[1].length %s"( 2440 stack[0].length, stack[1].length 2441 )); 2442 } 2443 else if (stackLen > 2) 2444 { 2445 foreach (k; 2 .. stackLen) 2446 { 2447 assert(stack[k - 2].length > stack[k - 1].length + stack[k].length, 2448 format!"stack[k - 2].length %s > stack[k - 1].length %s + stack[k].length %s"( 2449 stack[k - 2].length, stack[k - 1].length, stack[k].length 2450 )); 2451 assert(stack[k - 1].length > stack[k].length, 2452 format!"stack[k - 1].length %s > stack[k].length %s"( 2453 stack[k - 1].length, stack[k].length 2454 )); 2455 } 2456 } 2457 } 2458 } 2459 2460 // Force collapse stack until there is only one run left 2461 while (stackLen > 1) 2462 { 2463 immutable run3 = stackLen - 1; 2464 immutable run2 = stackLen - 2; 2465 immutable run1 = stackLen - 3; 2466 immutable at = stackLen >= 3 && stack[run1].length <= stack[run3].length 2467 ? run1 : run2; 2468 mergeAt(range, stack[0 .. stackLen], at, minGallop, temp); 2469 --stackLen; 2470 } 2471 } 2472 2473 // Calculates optimal value for minRun: 2474 // take first 6 bits of n and add 1 if any lower bits are set 2475 size_t minRunLength()(size_t n) 2476 { 2477 immutable shift = bsr(n)-5; 2478 auto result = (n >> shift) + !!(n & ~((1 << shift)-1)); 2479 return result; 2480 } 2481 2482 // Returns length of first run in range 2483 size_t firstRun()(R range) 2484 out(ret) 2485 { 2486 assert(ret <= range.length, "ret must be less or equal than" 2487 ~ " range.length"); 2488 } 2489 do 2490 { 2491 import std.algorithm.mutation : reverse; 2492 2493 if (range.length < 2) return range.length; 2494 2495 size_t i = 2; 2496 if (lessEqual(range[0], range[1])) 2497 { 2498 while (i < range.length && lessEqual(range[i-1], range[i])) ++i; 2499 } 2500 else 2501 { 2502 while (i < range.length && greater(range[i-1], range[i])) ++i; 2503 reverse(range[0 .. i]); 2504 } 2505 return i; 2506 } 2507 2508 // A binary insertion sort for building runs up to minRun length 2509 void binaryInsertionSort()(R range, size_t sortedLen = 1) 2510 out 2511 { 2512 if (!__ctfe) assert(isSorted!pred(range), "range must be sorted"); 2513 } 2514 do 2515 { 2516 import std.algorithm.mutation : move; 2517 2518 for (; sortedLen < range.length; ++sortedLen) 2519 { 2520 T item = range.moveAt(sortedLen); 2521 size_t lower = 0; 2522 size_t upper = sortedLen; 2523 while (upper != lower) 2524 { 2525 size_t center = (lower + upper) / 2; 2526 if (less(item, range[center])) upper = center; 2527 else lower = center + 1; 2528 } 2529 //Currently (DMD 2.061) moveAll+retro is slightly less 2530 //efficient then stright 'for' loop 2531 //11 instructions vs 7 in the innermost loop [checked on Win32] 2532 //moveAll(retro(range[lower .. sortedLen]), 2533 // retro(range[lower+1 .. sortedLen+1])); 2534 for (upper=sortedLen; upper > lower; upper--) 2535 { 2536 static if (hasLvalueElements!R) 2537 move(range[upper -1], range[upper]); 2538 else 2539 range[upper] = range.moveAt(upper - 1); 2540 } 2541 2542 static if (hasLvalueElements!R) 2543 move(item, range[lower]); 2544 else 2545 range[lower] = move(item); 2546 } 2547 } 2548 2549 // Merge two runs in stack (at, at + 1) 2550 void mergeAt()(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp) 2551 in 2552 { 2553 import std.format : format; 2554 assert(stack.length >= 2, "stack be be greater than 1"); 2555 assert(stack.length - at == 2 || stack.length - at == 3, 2556 format!"stack.length - at %s must be 2 or 3"(stack.length - at)); 2557 } 2558 do 2559 { 2560 immutable base = stack[at].base; 2561 immutable mid = stack[at].length; 2562 immutable len = stack[at + 1].length + mid; 2563 2564 // Pop run from stack 2565 stack[at] = Slice(base, len); 2566 if (stack.length - at == 3) stack[$ - 2] = stack[$ - 1]; 2567 2568 // Merge runs (at, at + 1) 2569 return merge(range[base .. base + len], mid, minGallop, temp); 2570 } 2571 2572 // Merge two runs in a range. Mid is the starting index of the second run. 2573 // minGallop and temp are references; The calling function must receive the updated values. 2574 void merge()(R range, size_t mid, ref size_t minGallop, ref T[] temp) 2575 in 2576 { 2577 if (!__ctfe) 2578 { 2579 assert(isSorted!pred(range[0 .. mid]), "range[0 .. mid] must be" 2580 ~ " sorted"); 2581 assert(isSorted!pred(range[mid .. range.length]), "range[mid .." 2582 ~ " range.length] must be sorted"); 2583 } 2584 } 2585 do 2586 { 2587 assert(mid < range.length, "mid must be less than the length of the" 2588 ~ " range"); 2589 2590 // Reduce range of elements 2591 immutable firstElement = gallopForwardUpper(range[0 .. mid], range[mid]); 2592 immutable lastElement = gallopReverseLower(range[mid .. range.length], range[mid - 1]) + mid; 2593 range = range[firstElement .. lastElement]; 2594 mid -= firstElement; 2595 2596 if (mid == 0 || mid == range.length) return; 2597 2598 // Call function which will copy smaller run into temporary memory 2599 if (mid <= range.length / 2) 2600 { 2601 temp = ensureCapacity(mid, temp); 2602 minGallop = mergeLo(range, mid, minGallop, temp); 2603 } 2604 else 2605 { 2606 temp = ensureCapacity(range.length - mid, temp); 2607 minGallop = mergeHi(range, mid, minGallop, temp); 2608 } 2609 } 2610 2611 // Enlarge size of temporary memory if needed 2612 T[] ensureCapacity()(size_t minCapacity, T[] temp) 2613 out(ret) 2614 { 2615 assert(ret.length >= minCapacity, "ensuring the capacity failed"); 2616 } 2617 do 2618 { 2619 if (temp.length < minCapacity) 2620 { 2621 size_t newSize = 1<<(bsr(minCapacity)+1); 2622 //Test for overflow 2623 if (newSize < minCapacity) newSize = minCapacity; 2624 2625 // can't use `temp.length` if there's no default constructor 2626 static if (__traits(compiles, { T defaultConstructed; cast(void) defaultConstructed; })) 2627 { 2628 2629 static if (hasElaborateAssign!T) 2630 temp.length = newSize; 2631 else 2632 { 2633 if (__ctfe) temp.length = newSize; 2634 else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); 2635 } 2636 } 2637 else 2638 { 2639 static assert(!hasElaborateAssign!T, 2640 "Structs which have opAssign but cannot be default-initialized " ~ 2641 "do not currently work with stable sort: " ~ 2642 "https://issues.dlang.org/show_bug.cgi?id=24810"); 2643 temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); 2644 } 2645 } 2646 return temp; 2647 } 2648 2649 // Merge front to back. Returns new value of minGallop. 2650 // temp must be large enough to store range[0 .. mid] 2651 size_t mergeLo()(R range, immutable size_t mid, size_t minGallop, T[] temp) 2652 out 2653 { 2654 if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted"); 2655 } 2656 do 2657 { 2658 import std.algorithm.mutation : copy; 2659 2660 assert(mid <= range.length, "mid must be less than the length of the" 2661 ~ " range"); 2662 assert(temp.length >= mid, "temp.length must be greater or equal to mid"); 2663 2664 // Copy run into temporary memory 2665 temp = temp[0 .. mid]; 2666 copy(range[0 .. mid], temp); 2667 2668 // Move first element into place 2669 moveEntry(range, mid, range, 0); 2670 2671 size_t i = 1, lef = 0, rig = mid + 1; 2672 size_t count_lef, count_rig; 2673 immutable lef_end = temp.length - 1; 2674 2675 if (lef < lef_end && rig < range.length) 2676 outer: while (true) 2677 { 2678 count_lef = 0; 2679 count_rig = 0; 2680 2681 // Linear merge 2682 while ((count_lef | count_rig) < minGallop) 2683 { 2684 if (lessEqual(temp[lef], range[rig])) 2685 { 2686 moveEntry(temp, lef++, range, i++); 2687 if (lef >= lef_end) break outer; 2688 ++count_lef; 2689 count_rig = 0; 2690 } 2691 else 2692 { 2693 moveEntry(range, rig++, range, i++); 2694 if (rig >= range.length) break outer; 2695 count_lef = 0; 2696 ++count_rig; 2697 } 2698 } 2699 2700 // Gallop merge 2701 do 2702 { 2703 count_lef = gallopForwardUpper(temp[lef .. $], range[rig]); 2704 foreach (j; 0 .. count_lef) moveEntry(temp, lef++, range, i++); 2705 if (lef >= temp.length) break outer; 2706 2707 count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]); 2708 foreach (j; 0 .. count_rig) moveEntry(range, rig++, range, i++); 2709 if (rig >= range.length) while (true) 2710 { 2711 moveEntry(temp, lef++, range, i++); 2712 if (lef >= temp.length) break outer; 2713 } 2714 2715 if (minGallop > 0) --minGallop; 2716 } 2717 while (count_lef >= minimalGallop || count_rig >= minimalGallop); 2718 2719 minGallop += 2; 2720 } 2721 2722 // Move remaining elements from right 2723 while (rig < range.length) 2724 moveEntry(range, rig++, range, i++); 2725 2726 // Move remaining elements from left 2727 while (lef < temp.length) 2728 moveEntry(temp, lef++, range, i++); 2729 2730 return minGallop > 0 ? minGallop : 1; 2731 } 2732 2733 // Merge back to front. Returns new value of minGallop. 2734 // temp must be large enough to store range[mid .. range.length] 2735 size_t mergeHi()(R range, immutable size_t mid, size_t minGallop, T[] temp) 2736 out 2737 { 2738 if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted"); 2739 } 2740 do 2741 { 2742 import std.algorithm.mutation : copy; 2743 import std.format : format; 2744 2745 assert(mid <= range.length, "mid must be less or equal to range.length"); 2746 assert(temp.length >= range.length - mid, format! 2747 "temp.length %s >= range.length %s - mid %s"(temp.length, 2748 range.length, mid)); 2749 2750 // Copy run into temporary memory 2751 temp = temp[0 .. range.length - mid]; 2752 copy(range[mid .. range.length], temp); 2753 2754 // Move first element into place 2755 moveEntry(range, mid - 1, range, range.length - 1); 2756 2757 size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1; 2758 size_t count_lef, count_rig; 2759 2760 outer: 2761 while (true) 2762 { 2763 count_lef = 0; 2764 count_rig = 0; 2765 2766 // Linear merge 2767 while ((count_lef | count_rig) < minGallop) 2768 { 2769 if (greaterEqual(temp[rig], range[lef])) 2770 { 2771 moveEntry(temp, rig, range, i--); 2772 if (rig == 1) 2773 { 2774 // Move remaining elements from left 2775 while (true) 2776 { 2777 moveEntry(range, lef, range, i--); 2778 if (lef == 0) break; 2779 --lef; 2780 } 2781 2782 // Move last element into place 2783 moveEntry(temp, 0, range, i); 2784 2785 break outer; 2786 } 2787 --rig; 2788 count_lef = 0; 2789 ++count_rig; 2790 } 2791 else 2792 { 2793 moveEntry(range, lef, range, i--); 2794 if (lef == 0) while (true) 2795 { 2796 moveEntry(temp, rig, range, i--); 2797 if (rig == 0) break outer; 2798 --rig; 2799 } 2800 --lef; 2801 ++count_lef; 2802 count_rig = 0; 2803 } 2804 } 2805 2806 // Gallop merge 2807 do 2808 { 2809 count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]); 2810 foreach (j; 0 .. count_rig) 2811 { 2812 moveEntry(temp, rig, range, i--); 2813 if (rig == 0) break outer; 2814 --rig; 2815 } 2816 2817 count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]); 2818 foreach (j; 0 .. count_lef) 2819 { 2820 moveEntry(range, lef, range, i--); 2821 if (lef == 0) while (true) 2822 { 2823 moveEntry(temp, rig, range, i--); 2824 if (rig == 0) break outer; 2825 --rig; 2826 } 2827 --lef; 2828 } 2829 2830 if (minGallop > 0) --minGallop; 2831 } 2832 while (count_lef >= minimalGallop || count_rig >= minimalGallop); 2833 2834 minGallop += 2; 2835 } 2836 2837 return minGallop > 0 ? minGallop : 1; 2838 } 2839 2840 // false = forward / lower, true = reverse / upper 2841 template gallopSearch(bool forwardReverse, bool lowerUpper) 2842 { 2843 // Gallop search on range according to attributes forwardReverse and lowerUpper 2844 size_t gallopSearch(R)(R range, T value) 2845 out(ret) 2846 { 2847 assert(ret <= range.length, "ret must be less or equal to" 2848 ~ " range.length"); 2849 } 2850 do 2851 { 2852 size_t lower = 0, center = 1, upper = range.length; 2853 alias gap = center; 2854 2855 static if (forwardReverse) 2856 { 2857 static if (!lowerUpper) alias comp = lessEqual; // reverse lower 2858 static if (lowerUpper) alias comp = less; // reverse upper 2859 2860 // Gallop Search Reverse 2861 while (gap <= upper) 2862 { 2863 if (comp(value, range[upper - gap])) 2864 { 2865 upper -= gap; 2866 gap *= 2; 2867 } 2868 else 2869 { 2870 lower = upper - gap; 2871 break; 2872 } 2873 } 2874 2875 // Binary Search Reverse 2876 while (upper != lower) 2877 { 2878 center = lower + (upper - lower) / 2; 2879 if (comp(value, range[center])) upper = center; 2880 else lower = center + 1; 2881 } 2882 } 2883 else 2884 { 2885 static if (!lowerUpper) alias comp = greater; // forward lower 2886 static if (lowerUpper) alias comp = greaterEqual; // forward upper 2887 2888 // Gallop Search Forward 2889 while (lower + gap < upper) 2890 { 2891 if (comp(value, range[lower + gap])) 2892 { 2893 lower += gap; 2894 gap *= 2; 2895 } 2896 else 2897 { 2898 upper = lower + gap; 2899 break; 2900 } 2901 } 2902 2903 // Binary Search Forward 2904 while (lower != upper) 2905 { 2906 center = lower + (upper - lower) / 2; 2907 if (comp(value, range[center])) lower = center + 1; 2908 else upper = center; 2909 } 2910 } 2911 2912 return lower; 2913 } 2914 } 2915 2916 alias gallopForwardLower = gallopSearch!(false, false); 2917 alias gallopForwardUpper = gallopSearch!(false, true); 2918 alias gallopReverseLower = gallopSearch!( true, false); 2919 alias gallopReverseUpper = gallopSearch!( true, true); 2920 2921 /// Helper method that moves from[fIdx] into to[tIdx] if both are lvalues and 2922 /// uses a plain assignment if not (necessary for backwards compatibility) 2923 void moveEntry(X, Y)(ref X from, const size_t fIdx, ref Y to, const size_t tIdx) 2924 { 2925 // This template is instantiated with different combinations of range (R) and temp (T[]). 2926 // T[] obviously has lvalue-elements, so checking R should be enough here 2927 static if (hasLvalueElements!R) 2928 { 2929 import core.lifetime : move; 2930 move(from[fIdx], to[tIdx]); 2931 } 2932 else 2933 to[tIdx] = from[fIdx]; 2934 } 2935 } 2936 2937 @safe unittest 2938 { 2939 import std.random : Random, uniform, randomShuffle; 2940 2941 // Element type with two fields 2942 static struct E 2943 { 2944 size_t value, index; 2945 } 2946 2947 // Generates data especially for testing sorting with Timsort 2948 static E[] genSampleData(uint seed) @safe 2949 { 2950 import std.algorithm.mutation : swap, swapRanges; 2951 2952 auto rnd = Random(seed); 2953 2954 E[] arr; 2955 arr.length = 64 * 64; 2956 2957 // We want duplicate values for testing stability 2958 foreach (i, ref v; arr) v.value = i / 64; 2959 2960 // Swap ranges at random middle point (test large merge operation) 2961 immutable mid = uniform(arr.length / 4, arr.length / 4 * 3, rnd); 2962 swapRanges(arr[0 .. mid], arr[mid .. $]); 2963 2964 // Shuffle last 1/8 of the array (test insertion sort and linear merge) 2965 randomShuffle(arr[$ / 8 * 7 .. $], rnd); 2966 2967 // Swap few random elements (test galloping mode) 2968 foreach (i; 0 .. arr.length / 64) 2969 { 2970 immutable a = uniform(0, arr.length, rnd), b = uniform(0, arr.length, rnd); 2971 swap(arr[a], arr[b]); 2972 } 2973 2974 // Now that our test array is prepped, store original index value 2975 // This will allow us to confirm the array was sorted stably 2976 foreach (i, ref v; arr) v.index = i; 2977 2978 return arr; 2979 } 2980 2981 // Tests the Timsort function for correctness and stability 2982 static bool testSort(uint seed) 2983 { 2984 import std.format : format; 2985 auto arr = genSampleData(seed); 2986 2987 // Now sort the array! 2988 static bool comp(E a, E b) 2989 { 2990 return a.value < b.value; 2991 } 2992 2993 sort!(comp, SwapStrategy.stable)(arr); 2994 2995 // Test that the array was sorted correctly 2996 assert(isSorted!comp(arr), "arr must be sorted"); 2997 2998 // Test that the array was sorted stably 2999 foreach (i; 0 .. arr.length - 1) 3000 { 3001 if (arr[i].value == arr[i + 1].value) 3002 assert(arr[i].index < arr[i + 1].index, format! 3003 "arr[i %s].index %s < arr[i + 1].index %s"( 3004 i, arr[i].index, arr[i + 1].index)); 3005 } 3006 3007 return true; 3008 } 3009 3010 enum seed = 310614065; 3011 testSort(seed); 3012 3013 enum result = testSort(seed); 3014 assert(result == true, "invalid result"); 3015 } 3016 3017 // https://issues.dlang.org/show_bug.cgi?id=4584 3018 @safe unittest 3019 { 3020 assert(isSorted!"a < b"(sort!("a < b", SwapStrategy.stable)( 3021 [83, 42, 85, 86, 87, 22, 89, 30, 91, 46, 93, 94, 95, 6, 3022 97, 14, 33, 10, 101, 102, 103, 26, 105, 106, 107, 6] 3023 ))); 3024 3025 } 3026 3027 @safe unittest 3028 { 3029 //test stable sort + zip 3030 import std.range; 3031 auto x = [10, 50, 60, 60, 20]; 3032 dchar[] y = "abcde"d.dup; 3033 3034 sort!("a[0] < b[0]", SwapStrategy.stable)(zip(x, y)); 3035 assert(x == [10, 20, 50, 60, 60]); 3036 assert(y == "aebcd"d); 3037 } 3038 3039 // https://issues.dlang.org/show_bug.cgi?id=14223 3040 @safe unittest 3041 { 3042 import std.array, std.range; 3043 auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array; 3044 sort!("a < b", SwapStrategy.stable)(arr); 3045 } 3046 3047 @safe unittest 3048 { 3049 static struct NoCopy 3050 { 3051 pure nothrow @nogc @safe: 3052 3053 int key; 3054 this(scope const ref NoCopy) 3055 { 3056 assert(false, "Tried to copy struct!"); 3057 } 3058 ref opAssign()(scope const auto ref NoCopy other) 3059 { 3060 assert(false, "Tried to copy struct!"); 3061 } 3062 this(this) {} 3063 } 3064 3065 static NoCopy[] makeArray(const size_t size) 3066 { 3067 NoCopy[] array = new NoCopy[](size); 3068 foreach (const i, ref t; array[0..$/2]) t.key = cast(int) (size - i); 3069 foreach (const i, ref t; array[$/2..$]) t.key = cast(int) i; 3070 return array; 3071 } 3072 3073 alias cmp = (ref NoCopy a, ref NoCopy b) => a.key < b.key; 3074 enum minMerge = TimSortImpl!(cmp, NoCopy[]).minimalMerge; 3075 3076 sort!(cmp, SwapStrategy.unstable)(makeArray(20)); 3077 sort!(cmp, SwapStrategy.stable)(makeArray(minMerge - 5)); 3078 sort!(cmp, SwapStrategy.stable)(makeArray(minMerge + 5)); 3079 } 3080 3081 // https://issues.dlang.org/show_bug.cgi?id=23668 3082 @safe unittest 3083 { 3084 static struct S 3085 { 3086 int opCmp(const S) const { return 1; } 3087 @disable this(); 3088 } 3089 S[] array; 3090 array.sort!("a < b", SwapStrategy.stable); 3091 } 3092 3093 // https://issues.dlang.org/show_bug.cgi?id=24773 3094 @safe unittest 3095 { 3096 static struct S 3097 { 3098 int i = 42; 3099 ~this() { assert(i == 42); } 3100 } 3101 3102 auto array = new S[](400); 3103 array.sort!((a, b) => false, SwapStrategy.stable); 3104 } 3105 3106 // https://issues.dlang.org/show_bug.cgi?id=24809 3107 @safe unittest 3108 { 3109 static struct E 3110 { 3111 int value; 3112 int valid = 42; 3113 3114 ~this() 3115 { 3116 assert(valid == 42); 3117 } 3118 } 3119 3120 import std.array : array; 3121 import std.range : chain, only, repeat; 3122 auto arr = chain(repeat(E(41), 18), 3123 only(E(39)), 3124 repeat(E(41), 16), 3125 only(E(1)), 3126 repeat(E(42), 33), 3127 only(E(33)), 3128 repeat(E(42), 16), 3129 repeat(E(43), 27), 3130 only(E(33)), 3131 repeat(E(43), 34), 3132 only(E(34)), 3133 only(E(43)), 3134 only(E(63)), 3135 repeat(E(44), 42), 3136 only(E(27)), 3137 repeat(E(44), 11), 3138 repeat(E(45), 64), 3139 repeat(E(46), 3), 3140 only(E(11)), 3141 repeat(E(46), 7), 3142 only(E(4)), 3143 repeat(E(46), 34), 3144 only(E(36)), 3145 repeat(E(46), 17), 3146 repeat(E(47), 36), 3147 only(E(39)), 3148 repeat(E(47), 26), 3149 repeat(E(48), 17), 3150 only(E(21)), 3151 repeat(E(48), 5), 3152 only(E(39)), 3153 repeat(E(48), 14), 3154 only(E(58)), 3155 repeat(E(48), 24), 3156 repeat(E(49), 13), 3157 only(E(40)), 3158 repeat(E(49), 38), 3159 only(E(18)), 3160 repeat(E(49), 11), 3161 repeat(E(50), 6)).array(); 3162 3163 arr.sort!((a, b) => a.value < b.value, SwapStrategy.stable)(); 3164 } 3165 3166 // schwartzSort 3167 /** 3168 Alternative sorting method that should be used when comparing keys involves an 3169 expensive computation. 3170 3171 Instead of using `less(a, b)` for comparing elements, 3172 `schwartzSort` uses `less(transform(a), transform(b))`. The values of the 3173 `transform` function are precomputed in a temporary array, thus saving on 3174 repeatedly computing it. Conversely, if the cost of `transform` is small 3175 compared to the cost of allocating and filling the precomputed array, `sort` 3176 may be faster and therefore preferable. 3177 3178 This approach to sorting is akin to the $(HTTP 3179 wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform), also known as 3180 the decorate-sort-undecorate pattern in Python and Lisp. The complexity is the 3181 same as that of the corresponding `sort`, but `schwartzSort` evaluates 3182 `transform` only `r.length` times (less than half when compared to regular 3183 sorting). The usage can be best illustrated with an example. 3184 3185 Example: 3186 ---- 3187 uint hashFun(string) { ... expensive computation ... } 3188 string[] array = ...; 3189 // Sort strings by hash, slow 3190 sort!((a, b) => hashFun(a) < hashFun(b))(array); 3191 // Sort strings by hash, fast (only computes arr.length hashes): 3192 schwartzSort!(hashFun, "a < b")(array); 3193 ---- 3194 3195 The `schwartzSort` function might require less temporary data and 3196 be faster than the Perl idiom or the decorate-sort-undecorate idiom 3197 present in Python and Lisp. This is because sorting is done in-place 3198 and only minimal extra data (one array of transformed elements) is 3199 created. 3200 3201 To check whether an array was sorted and benefit of the speedup of 3202 Schwartz sorting, a function `schwartzIsSorted` is not provided 3203 because the effect can be achieved by calling $(D 3204 isSorted!less(map!transform(r))). 3205 3206 Params: 3207 transform = The transformation to apply. Either a unary function 3208 (`unaryFun!transform(element)`), or a binary function 3209 (`binaryFun!transform(element, index)`). 3210 less = The predicate to sort the transformed elements by. 3211 ss = The swapping strategy to use. 3212 r = The range to sort. 3213 3214 Returns: The initial range wrapped as a `SortedRange` with the 3215 predicate `(a, b) => binaryFun!less(transform(a), transform(b))`. 3216 */ 3217 SortedRange!(R, ((a, b) => binaryFun!less(unaryFun!transform(a), 3218 unaryFun!transform(b)))) 3219 schwartzSort(alias transform, alias less = "a < b", 3220 SwapStrategy ss = SwapStrategy.unstable, R)(R r) 3221 if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R && 3222 !is(typeof(binaryFun!less) == SwapStrategy)) 3223 { 3224 import core.lifetime : emplace; 3225 import std.range : zip, SortedRange; 3226 import std.string : representation; 3227 3228 static if (is(typeof(unaryFun!transform(r.front)))) 3229 { 3230 alias transformFun = unaryFun!transform; 3231 alias TB = typeof(transformFun(r.front)); 3232 enum isBinary = false; 3233 } 3234 else static if (is(typeof(binaryFun!transform(r.front, 0)))) 3235 { 3236 alias transformFun = binaryFun!transform; 3237 alias TB = typeof(transformFun(r.front, 0)); 3238 enum isBinary = true; 3239 } 3240 else 3241 static assert(false, "unsupported `transform` alias"); 3242 3243 // The `transform` function might return a qualified type, e.g. const(int). 3244 // Strip qualifiers if possible s.t. the temporary array is sortable. 3245 static if (is(TB : Unqual!TB)) 3246 alias T = Unqual!TB; 3247 else 3248 static assert(false, "`transform` returns an unsortable qualified type: " ~ TB.stringof); 3249 3250 static trustedMalloc()(size_t len) @trusted 3251 { 3252 import core.checkedint : mulu; 3253 import core.memory : pureMalloc; 3254 bool overflow; 3255 const nbytes = mulu(len, T.sizeof, overflow); 3256 if (overflow) assert(false, "multiplication overflowed"); 3257 T[] result = (cast(T*) pureMalloc(nbytes))[0 .. len]; 3258 static if (hasIndirections!T) 3259 { 3260 import core.memory : GC; 3261 GC.addRange(result.ptr, nbytes); 3262 } 3263 return result; 3264 } 3265 auto xform1 = trustedMalloc(r.length); 3266 3267 size_t length; 3268 scope(exit) 3269 { 3270 static if (hasElaborateDestructor!T) 3271 { 3272 foreach (i; 0 .. length) collectException(destroy(xform1[i])); 3273 } 3274 static void trustedFree()(T[] p) @trusted 3275 { 3276 import core.memory : pureFree; 3277 static if (hasIndirections!T) 3278 { 3279 import core.memory : GC; 3280 GC.removeRange(p.ptr); 3281 } 3282 pureFree(p.ptr); 3283 } 3284 trustedFree(xform1); 3285 } 3286 for (; length != r.length; ++length) 3287 { 3288 static if (isBinary) 3289 emplace(&xform1[length], transformFun(r[length], length)); 3290 else 3291 emplace(&xform1[length], transformFun(r[length])); 3292 } 3293 // Make sure we use ubyte[] and ushort[], not char[] and wchar[] 3294 // for the intermediate array, lest zip gets confused. 3295 static if (isNarrowString!(typeof(xform1))) 3296 { 3297 auto xform = xform1.representation(); 3298 } 3299 else 3300 { 3301 alias xform = xform1; 3302 } 3303 zip(xform, r).sort!((a, b) => binaryFun!less(a[0], b[0]), ss)(); 3304 return typeof(return)(r); 3305 } 3306 3307 /// ditto 3308 auto schwartzSort(alias transform, SwapStrategy ss, R)(R r) 3309 if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R) 3310 { 3311 return schwartzSort!(transform, "a < b", ss, R)(r); 3312 } 3313 3314 /// 3315 @safe pure unittest 3316 { 3317 import std.algorithm.iteration : map; 3318 import std.numeric : entropy; 3319 3320 auto lowEnt = [ 1.0, 0, 0 ], 3321 midEnt = [ 0.1, 0.1, 0.8 ], 3322 highEnt = [ 0.31, 0.29, 0.4 ]; 3323 auto arr = new double[][3]; 3324 arr[0] = midEnt; 3325 arr[1] = lowEnt; 3326 arr[2] = highEnt; 3327 3328 schwartzSort!(entropy, "a > b")(arr); 3329 3330 assert(arr[0] == highEnt); 3331 assert(arr[1] == midEnt); 3332 assert(arr[2] == lowEnt); 3333 assert(isSorted!("a > b")(map!(entropy)(arr))); 3334 } 3335 3336 @safe pure unittest 3337 { 3338 import std.algorithm.iteration : map; 3339 import std.numeric : entropy; 3340 3341 auto lowEnt = [ 1.0, 0, 0 ], 3342 midEnt = [ 0.1, 0.1, 0.8 ], 3343 highEnt = [ 0.31, 0.29, 0.4 ]; 3344 auto arr = new double[][3]; 3345 arr[0] = midEnt; 3346 arr[1] = lowEnt; 3347 arr[2] = highEnt; 3348 3349 schwartzSort!(entropy, "a < b")(arr); 3350 3351 assert(arr[0] == lowEnt); 3352 assert(arr[1] == midEnt); 3353 assert(arr[2] == highEnt); 3354 assert(isSorted!("a < b")(map!(entropy)(arr))); 3355 } 3356 3357 @safe pure unittest 3358 { 3359 // binary transform function 3360 string[] strings = [ "one", "two", "three" ]; 3361 schwartzSort!((element, index) => size_t.max - index)(strings); 3362 assert(strings == [ "three", "two", "one" ]); 3363 } 3364 3365 // https://issues.dlang.org/show_bug.cgi?id=4909 3366 @safe pure unittest 3367 { 3368 import std.typecons : Tuple; 3369 Tuple!(char)[] chars; 3370 schwartzSort!"a[0]"(chars); 3371 } 3372 3373 // https://issues.dlang.org/show_bug.cgi?id=5924 3374 @safe pure unittest 3375 { 3376 import std.typecons : Tuple; 3377 Tuple!(char)[] chars; 3378 schwartzSort!((Tuple!(char) c){ return c[0]; })(chars); 3379 } 3380 3381 // https://issues.dlang.org/show_bug.cgi?id=13965 3382 @safe pure unittest 3383 { 3384 import std.typecons : Tuple; 3385 Tuple!(char)[] chars; 3386 schwartzSort!("a[0]", SwapStrategy.stable)(chars); 3387 } 3388 3389 // https://issues.dlang.org/show_bug.cgi?id=13965 3390 @safe pure unittest 3391 { 3392 import std.algorithm.iteration : map; 3393 import std.numeric : entropy; 3394 3395 auto lowEnt = [ 1.0, 0, 0 ], 3396 midEnt = [ 0.1, 0.1, 0.8 ], 3397 highEnt = [ 0.31, 0.29, 0.4 ]; 3398 auto arr = new double[][3]; 3399 arr[0] = midEnt; 3400 arr[1] = lowEnt; 3401 arr[2] = highEnt; 3402 3403 schwartzSort!(entropy, SwapStrategy.stable)(arr); 3404 3405 assert(arr[0] == lowEnt); 3406 assert(arr[1] == midEnt); 3407 assert(arr[2] == highEnt); 3408 assert(isSorted!("a < b")(map!(entropy)(arr))); 3409 } 3410 3411 // https://issues.dlang.org/show_bug.cgi?id=20799 3412 @safe unittest 3413 { 3414 import std.range : iota, retro; 3415 import std.array : array; 3416 3417 auto arr = 1_000_000.iota.retro.array; 3418 arr.schwartzSort!( 3419 n => new int(n), 3420 (a, b) => *a < *b 3421 ); 3422 assert(arr.isSorted()); 3423 } 3424 3425 // https://issues.dlang.org/show_bug.cgi?id=21183 3426 @safe unittest 3427 { 3428 static T get(T)(int) { return T.init; } 3429 3430 // There's no need to actually sort, just checking type interference 3431 if (false) 3432 { 3433 int[] arr; 3434 3435 // Fine because there are no indirections 3436 arr.schwartzSort!(get!(const int)); 3437 3438 // Fine because it decays to immutable(int)* 3439 arr.schwartzSort!(get!(immutable int*)); 3440 3441 // Disallowed because it would require a non-const reference 3442 static assert(!__traits(compiles, arr.schwartzSort!(get!(const Object)))); 3443 3444 static struct Wrapper 3445 { 3446 int* ptr; 3447 } 3448 3449 // Disallowed because Wrapper.ptr would become mutable 3450 static assert(!__traits(compiles, arr.schwartzSort!(get!(const Wrapper)))); 3451 } 3452 } 3453 3454 // partialSort 3455 /** 3456 Reorders the random-access range `r` such that the range `r[0 .. mid]` 3457 is the same as if the entire `r` were sorted, and leaves 3458 the range `r[mid .. r.length]` in no particular order. 3459 3460 Performs $(BIGOH r.length * log(mid)) evaluations of `pred`. The 3461 implementation simply calls `topN!(less, ss)(r, n)` and then $(D 3462 sort!(less, ss)(r[0 .. n])). 3463 3464 Params: 3465 less = The predicate to sort by. 3466 ss = The swapping strategy to use. 3467 r = The random-access range to reorder. 3468 n = The length of the initial segment of `r` to sort. 3469 */ 3470 void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, 3471 Range)(Range r, size_t n) 3472 if (isRandomAccessRange!(Range) && hasLength!(Range) && hasSlicing!(Range)) 3473 { 3474 partialSort!(less, ss)(r[0 .. n], r[n .. $]); 3475 } 3476 3477 /// 3478 @system unittest 3479 { 3480 int[] a = [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]; 3481 partialSort(a, 5); 3482 assert(a[0 .. 5] == [ 0, 1, 2, 3, 4 ]); 3483 } 3484 3485 /** 3486 Stores the smallest elements of the two ranges in the left-hand range in sorted order. 3487 3488 Params: 3489 less = The predicate to sort by. 3490 ss = The swapping strategy to use. 3491 r1 = The first range. 3492 r2 = The second range. 3493 */ 3494 3495 void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, 3496 Range1, Range2)(Range1 r1, Range2 r2) 3497 if (isRandomAccessRange!(Range1) && hasLength!Range1 && 3498 isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) && 3499 hasLvalueElements!Range1 && hasLvalueElements!Range2) 3500 { 3501 topN!(less, ss)(r1, r2); 3502 sort!(less, ss)(r1); 3503 } 3504 /// 3505 @system unittest 3506 { 3507 int[] a = [5, 7, 2, 6, 7]; 3508 int[] b = [2, 1, 5, 6, 7, 3, 0]; 3509 3510 partialSort(a, b); 3511 assert(a == [0, 1, 2, 2, 3]); 3512 } 3513 3514 // topN 3515 /** 3516 Reorders the range `r` using $(REF_ALTTEXT swap, swap, std,algorithm,mutation) 3517 such that `r[nth]` refers to the element that would fall there if the range 3518 were fully sorted. 3519 3520 It is akin to $(LINK2 https://en.wikipedia.org/wiki/Quickselect, Quickselect), 3521 and partitions `r` such that all elements 3522 `e1` from `r[0]` to `r[nth]` satisfy `!less(r[nth], e1)`, 3523 and all elements `e2` from `r[nth]` to `r[r.length]` satisfy 3524 `!less(e2, r[nth])`. Effectively, it finds the `nth + 1` smallest 3525 (according to `less`) elements in `r`. Performs an expected 3526 $(BIGOH r.length) (if unstable) or $(BIGOH r.length * log(r.length)) 3527 (if stable) evaluations of `less` and $(REF_ALTTEXT swap, swap, std,algorithm,mutation). 3528 3529 If `n >= r.length`, the algorithm has no effect and returns 3530 `r[0 .. r.length]`. 3531 3532 Params: 3533 less = The predicate to sort by. 3534 ss = The swapping strategy to use. 3535 r = The random-access range to reorder. 3536 nth = The index of the element that should be in sorted position after the 3537 function is done. 3538 3539 Returns: a slice from `r[0]` to `r[nth]`, excluding `r[nth]` itself. 3540 3541 See_Also: 3542 $(LREF topNIndex), 3543 3544 BUGS: 3545 3546 Stable topN has not been implemented yet. 3547 */ 3548 auto topN(alias less = "a < b", 3549 SwapStrategy ss = SwapStrategy.unstable, 3550 Range)(Range r, size_t nth) 3551 if (isRandomAccessRange!(Range) && hasLength!Range && 3552 hasSlicing!Range && hasAssignableElements!Range) 3553 { 3554 static assert(ss == SwapStrategy.unstable, 3555 "Stable topN not yet implemented"); 3556 if (nth >= r.length) return r[0 .. r.length]; 3557 auto ret = r[0 .. nth]; 3558 if (false) 3559 { 3560 // Workaround for https://issues.dlang.org/show_bug.cgi?id=16528 3561 // Safety checks: enumerate all potentially unsafe generic primitives 3562 // then use a @trusted implementation. 3563 cast(void) binaryFun!less(r[0], r[r.length - 1]); 3564 import std.algorithm.mutation : swapAt; 3565 r.swapAt(size_t(0), size_t(0)); 3566 static assert(is(typeof(r.length) == size_t), 3567 typeof(r.length).stringof ~ " must be of type size_t"); 3568 pivotPartition!less(r, 0); 3569 } 3570 bool useSampling = true; 3571 topNImpl!(binaryFun!less)(r, nth, useSampling); 3572 return ret; 3573 } 3574 3575 /// 3576 @safe unittest 3577 { 3578 int[] v = [ 25, 7, 9, 2, 0, 5, 21 ]; 3579 topN!"a < b"(v, 100); 3580 assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]); 3581 auto n = 4; 3582 topN!((a, b) => a < b)(v, n); 3583 assert(v[n] == 9); 3584 } 3585 3586 // https://issues.dlang.org/show_bug.cgi?id=8341 3587 @safe unittest 3588 { 3589 import std.algorithm.comparison : equal; 3590 import std.range : zip; 3591 import std.typecons : tuple; 3592 auto a = [10, 30, 20]; 3593 auto b = ["c", "b", "a"]; 3594 assert(topN!"a[0] > b[0]"(zip(a, b), 2).equal([tuple(20, "a"), tuple(30, "b")])); 3595 } 3596 3597 private @trusted 3598 void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling) 3599 { 3600 for (;;) 3601 { 3602 import std.algorithm.mutation : swapAt; 3603 assert(n < r.length); 3604 size_t pivot = void; 3605 3606 // Decide strategy for partitioning 3607 if (n == 0) 3608 { 3609 pivot = 0; 3610 foreach (i; 1 .. r.length) 3611 if (less(r[i], r[pivot])) pivot = i; 3612 r.swapAt(n, pivot); 3613 return; 3614 } 3615 if (n + 1 == r.length) 3616 { 3617 pivot = 0; 3618 foreach (i; 1 .. r.length) 3619 if (less(r[pivot], r[i])) pivot = i; 3620 r.swapAt(n, pivot); 3621 return; 3622 } 3623 if (r.length <= 12) 3624 { 3625 pivot = pivotPartition!less(r, r.length / 2); 3626 } 3627 else if (n * 16 <= (r.length - 1) * 7) 3628 { 3629 pivot = topNPartitionOffMedian!(less, No.leanRight) 3630 (r, n, useSampling); 3631 // Quality check 3632 if (useSampling) 3633 { 3634 if (pivot < n) 3635 { 3636 if (pivot * 4 < r.length) 3637 { 3638 useSampling = false; 3639 } 3640 } 3641 else if ((r.length - pivot) * 8 < r.length * 3) 3642 { 3643 useSampling = false; 3644 } 3645 } 3646 } 3647 else if (n * 16 >= (r.length - 1) * 9) 3648 { 3649 pivot = topNPartitionOffMedian!(less, Yes.leanRight) 3650 (r, n, useSampling); 3651 // Quality check 3652 if (useSampling) 3653 { 3654 if (pivot < n) 3655 { 3656 if (pivot * 8 < r.length * 3) 3657 { 3658 useSampling = false; 3659 } 3660 } 3661 else if ((r.length - pivot) * 4 < r.length) 3662 { 3663 useSampling = false; 3664 } 3665 } 3666 } 3667 else 3668 { 3669 pivot = topNPartition!less(r, n, useSampling); 3670 // Quality check 3671 if (useSampling && 3672 (pivot * 9 < r.length * 2 || pivot * 9 > r.length * 7)) 3673 { 3674 // Failed - abort sampling going forward 3675 useSampling = false; 3676 } 3677 } 3678 3679 assert(pivot != size_t.max, "pivot must be not equal to size_t.max"); 3680 // See how the pivot fares 3681 if (pivot == n) 3682 { 3683 return; 3684 } 3685 if (pivot > n) 3686 { 3687 r = r[0 .. pivot]; 3688 } 3689 else 3690 { 3691 n -= pivot + 1; 3692 r = r[pivot + 1 .. r.length]; 3693 } 3694 } 3695 } 3696 3697 private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling) 3698 { 3699 import std.format : format; 3700 assert(r.length >= 9 && n < r.length, "length must be longer than 8" 3701 ~ " and n must be less than r.length"); 3702 immutable ninth = r.length / 9; 3703 auto pivot = ninth / 2; 3704 // Position subrange r[lo .. hi] to have length equal to ninth and its upper 3705 // median r[lo .. hi][$ / 2] in exactly the same place as the upper median 3706 // of the entire range r[$ / 2]. This is to improve behavior for searching 3707 // the median in already sorted ranges. 3708 immutable lo = r.length / 2 - pivot, hi = lo + ninth; 3709 // We have either one straggler on the left, one on the right, or none. 3710 assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1, 3711 format!"straggler check failed lo %s, r.length %s, hi %s"(lo, r.length, hi)); 3712 assert(lo >= ninth * 4, format!"lo %s >= ninth * 4 %s"(lo, ninth * 4)); 3713 assert(r.length - hi >= ninth * 4, 3714 format!"r.length %s - hi %s >= ninth * 4 %s"(r.length, hi, ninth * 4)); 3715 3716 // Partition in groups of 3, and the mid tertile again in groups of 3 3717 if (!useSampling) 3718 p3!lp(r, lo - ninth, hi + ninth); 3719 p3!lp(r, lo, hi); 3720 3721 // Get the median of medians of medians 3722 // Map the full interval of n to the full interval of the ninth 3723 pivot = (n * (ninth - 1)) / (r.length - 1); 3724 topNImpl!lp(r[lo .. hi], pivot, useSampling); 3725 return expandPartition!lp(r, lo, pivot + lo, hi); 3726 } 3727 3728 private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi) 3729 { 3730 import std.format : format; 3731 assert(lo <= hi && hi < r.length, 3732 format!"lo %s <= hi %s && hi < r.length %s"(lo, hi, r.length)); 3733 immutable ln = hi - lo; 3734 for (; lo < hi; ++lo) 3735 { 3736 assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln)); 3737 assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"( 3738 lo, ln, r.length)); 3739 medianOf!less(r, lo - ln, lo, lo + ln); 3740 } 3741 } 3742 3743 private void p4(alias less, Flag!"leanRight" f, Range) 3744 (Range r, size_t lo, immutable size_t hi) 3745 { 3746 import std.format : format; 3747 assert(lo <= hi && hi < r.length, format!"lo %s <= hi %s && hi < r.length %s"( 3748 lo, hi, r.length)); 3749 immutable ln = hi - lo, _2ln = ln * 2; 3750 for (; lo < hi; ++lo) 3751 { 3752 assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln)); 3753 assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"( 3754 lo, ln, r.length)); 3755 static if (f == Yes.leanRight) 3756 medianOf!(less, f)(r, lo - _2ln, lo - ln, lo, lo + ln); 3757 else 3758 medianOf!(less, f)(r, lo - ln, lo, lo + ln, lo + _2ln); 3759 } 3760 } 3761 3762 private size_t topNPartitionOffMedian(alias lp, Flag!"leanRight" f, R) 3763 (R r, size_t n, bool useSampling) 3764 { 3765 assert(r.length >= 12, "The length of r must be greater than 11"); 3766 assert(n < r.length, "n must be less than the length of r"); 3767 immutable _4 = r.length / 4; 3768 static if (f == Yes.leanRight) 3769 immutable leftLimit = 2 * _4; 3770 else 3771 immutable leftLimit = _4; 3772 // Partition in groups of 4, and the left quartile again in groups of 3 3773 if (!useSampling) 3774 { 3775 p4!(lp, f)(r, leftLimit, leftLimit + _4); 3776 } 3777 immutable _12 = _4 / 3; 3778 immutable lo = leftLimit + _12, hi = lo + _12; 3779 p3!lp(r, lo, hi); 3780 3781 // Get the median of medians of medians 3782 // Map the full interval of n to the full interval of the ninth 3783 immutable pivot = (n * (_12 - 1)) / (r.length - 1); 3784 topNImpl!lp(r[lo .. hi], pivot, useSampling); 3785 return expandPartition!lp(r, lo, pivot + lo, hi); 3786 } 3787 3788 /* 3789 Params: 3790 less = predicate 3791 r = range to partition 3792 pivot = pivot to partition around 3793 lo = value such that r[lo .. pivot] already less than r[pivot] 3794 hi = value such that r[pivot .. hi] already greater than r[pivot] 3795 3796 Returns: new position of pivot 3797 */ 3798 private 3799 size_t expandPartition(alias lp, R)(R r, size_t lo, size_t pivot, size_t hi) 3800 in 3801 { 3802 import std.algorithm.searching : all; 3803 assert(lo <= pivot, "lo must be less than or equal pivot"); 3804 assert(pivot < hi, "pivot must be less than hi"); 3805 assert(hi <= r.length, "hi must be less than or equal to the length of r"); 3806 assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)), 3807 "r[lo .. pivot + 1] failed less than test"); 3808 assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot])), 3809 "r[pivot + 1 .. hi] failed less than test"); 3810 } 3811 out 3812 { 3813 import std.algorithm.searching : all; 3814 assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x)), 3815 "r[0 .. pivot + 1] failed less than test"); 3816 assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot])), 3817 "r[pivot + 1 .. r.length] failed less than test"); 3818 } 3819 do 3820 { 3821 import std.algorithm.mutation : swapAt; 3822 import std.algorithm.searching : all; 3823 // We work with closed intervals! 3824 --hi; 3825 3826 size_t left = 0, rite = r.length - 1; 3827 loop: for (;; ++left, --rite) 3828 { 3829 for (;; ++left) 3830 { 3831 if (left == lo) break loop; 3832 if (!lp(r[left], r[pivot])) break; 3833 } 3834 for (;; --rite) 3835 { 3836 if (rite == hi) break loop; 3837 if (!lp(r[pivot], r[rite])) break; 3838 } 3839 r.swapAt(left, rite); 3840 } 3841 3842 assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)), 3843 "r[lo .. pivot + 1] failed less than test"); 3844 assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot])), 3845 "r[pivot + 1 .. hi + 1] failed less than test"); 3846 assert(r[0 .. left].all!(x => !lp(r[pivot], x)), 3847 "r[0 .. left] failed less than test"); 3848 assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot])), 3849 "r[rite + 1 .. r.length] failed less than test"); 3850 3851 immutable oldPivot = pivot; 3852 3853 if (left < lo) 3854 { 3855 // First loop: spend r[lo .. pivot] 3856 for (; lo < pivot; ++left) 3857 { 3858 if (left == lo) goto done; 3859 if (!lp(r[oldPivot], r[left])) continue; 3860 --pivot; 3861 assert(!lp(r[oldPivot], r[pivot]), "less check failed"); 3862 r.swapAt(left, pivot); 3863 } 3864 // Second loop: make left and pivot meet 3865 for (;; ++left) 3866 { 3867 if (left == pivot) goto done; 3868 if (!lp(r[oldPivot], r[left])) continue; 3869 for (;;) 3870 { 3871 if (left == pivot) goto done; 3872 --pivot; 3873 if (lp(r[pivot], r[oldPivot])) 3874 { 3875 r.swapAt(left, pivot); 3876 break; 3877 } 3878 } 3879 } 3880 } 3881 3882 // First loop: spend r[lo .. pivot] 3883 for (; hi != pivot; --rite) 3884 { 3885 if (rite == hi) goto done; 3886 if (!lp(r[rite], r[oldPivot])) continue; 3887 ++pivot; 3888 assert(!lp(r[pivot], r[oldPivot]), "less check failed"); 3889 r.swapAt(rite, pivot); 3890 } 3891 // Second loop: make left and pivot meet 3892 for (; rite > pivot; --rite) 3893 { 3894 if (!lp(r[rite], r[oldPivot])) continue; 3895 while (rite > pivot) 3896 { 3897 ++pivot; 3898 if (lp(r[oldPivot], r[pivot])) 3899 { 3900 r.swapAt(rite, pivot); 3901 break; 3902 } 3903 } 3904 } 3905 3906 done: 3907 r.swapAt(oldPivot, pivot); 3908 return pivot; 3909 } 3910 3911 @safe unittest 3912 { 3913 auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ]; 3914 assert(expandPartition!((a, b) => a < b)(a, 4, 5, 6) == 9); 3915 3916 import std.algorithm.iteration : map; 3917 import std.array : array; 3918 import std.random : uniform; 3919 import std.range : iota; 3920 auto size = uniform(1, 1000); 3921 a = iota(0, size).map!(_ => uniform(0, 1000)).array; 3922 if (a.length == 0) return; 3923 expandPartition!((a, b) => a < b)(a, a.length / 2, a.length / 2, 3924 a.length / 2 + 1); 3925 } 3926 3927 @safe unittest 3928 { 3929 import std.algorithm.comparison : max, min; 3930 import std.algorithm.iteration : reduce; 3931 3932 int[] v = [ 7, 6, 5, 4, 3, 2, 1, 0 ]; 3933 ptrdiff_t n = 3; 3934 topN!("a < b")(v, n); 3935 assert(reduce!max(v[0 .. n]) <= v[n]); 3936 assert(reduce!min(v[n + 1 .. $]) >= v[n]); 3937 // 3938 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; 3939 n = 3; 3940 topN(v, n); 3941 assert(reduce!max(v[0 .. n]) <= v[n]); 3942 assert(reduce!min(v[n + 1 .. $]) >= v[n]); 3943 // 3944 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; 3945 n = 1; 3946 topN(v, n); 3947 assert(reduce!max(v[0 .. n]) <= v[n]); 3948 assert(reduce!min(v[n + 1 .. $]) >= v[n]); 3949 // 3950 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; 3951 n = v.length - 1; 3952 topN(v, n); 3953 assert(v[n] == 7); 3954 // 3955 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; 3956 n = 0; 3957 topN(v, n); 3958 assert(v[n] == 1); 3959 3960 double[][] v1 = [[-10, -5], [-10, -3], [-10, -5], [-10, -4], 3961 [-10, -5], [-9, -5], [-9, -3], [-9, -5],]; 3962 3963 // double[][] v1 = [ [-10, -5], [-10, -4], [-9, -5], [-9, -5], 3964 // [-10, -5], [-10, -3], [-10, -5], [-9, -3],]; 3965 double[]*[] idx = [ &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5], &v1[6], 3966 &v1[7], ]; 3967 3968 auto mid = v1.length / 2; 3969 topN!((a, b){ return (*a)[1] < (*b)[1]; })(idx, mid); 3970 foreach (e; idx[0 .. mid]) assert((*e)[1] <= (*idx[mid])[1]); 3971 foreach (e; idx[mid .. $]) assert((*e)[1] >= (*idx[mid])[1]); 3972 } 3973 3974 @safe unittest 3975 { 3976 import std.algorithm.comparison : max, min; 3977 import std.algorithm.iteration : reduce; 3978 import std.random : Random = Xorshift, uniform; 3979 3980 immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953]; 3981 foreach (s; seeds) 3982 { 3983 auto r = Random(s); 3984 3985 int[] a = new int[uniform(1, 10000, r)]; 3986 foreach (ref e; a) e = uniform(-1000, 1000, r); 3987 3988 auto k = uniform(0, a.length, r); 3989 topN(a, k); 3990 if (k > 0) 3991 { 3992 auto left = reduce!max(a[0 .. k]); 3993 assert(left <= a[k]); 3994 } 3995 if (k + 1 < a.length) 3996 { 3997 auto right = reduce!min(a[k + 1 .. $]); 3998 assert(right >= a[k]); 3999 } 4000 } 4001 } 4002 4003 // https://issues.dlang.org/show_bug.cgi?id=12987 4004 @safe unittest 4005 { 4006 int[] a = [ 25, 7, 9, 2, 0, 5, 21 ]; 4007 auto n = 4; 4008 auto t = topN(a, n); 4009 sort(t); 4010 assert(t == [0, 2, 5, 7]); 4011 } 4012 4013 /** 4014 Stores the smallest elements of the two ranges in the left-hand range. 4015 4016 Params: 4017 less = The predicate to sort by. 4018 ss = The swapping strategy to use. 4019 r1 = The first range. 4020 r2 = The second range. 4021 */ 4022 auto topN(alias less = "a < b", 4023 SwapStrategy ss = SwapStrategy.unstable, 4024 Range1, Range2)(Range1 r1, Range2 r2) 4025 if (isRandomAccessRange!(Range1) && hasLength!Range1 && 4026 isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) && 4027 hasLvalueElements!Range1 && hasLvalueElements!Range2) 4028 { 4029 import std.container : BinaryHeap; 4030 4031 static assert(ss == SwapStrategy.unstable, 4032 "Stable topN not yet implemented"); 4033 4034 auto heap = BinaryHeap!(Range1, less)(r1); 4035 foreach (ref e; r2) 4036 { 4037 heap.conditionalSwap(e); 4038 } 4039 4040 return r1; 4041 } 4042 4043 /// 4044 @system unittest 4045 { 4046 int[] a = [ 5, 7, 2, 6, 7 ]; 4047 int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; 4048 topN(a, b); 4049 sort(a); 4050 assert(a == [0, 1, 2, 2, 3]); 4051 } 4052 4053 // https://issues.dlang.org/show_bug.cgi?id=15421 4054 @system unittest 4055 { 4056 import std.algorithm.comparison : equal; 4057 import std.internal.test.dummyrange; 4058 import std.meta : AliasSeq; 4059 4060 alias RandomRanges = AliasSeq!( 4061 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) 4062 ); 4063 4064 alias ReferenceRanges = AliasSeq!( 4065 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), 4066 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), 4067 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), 4068 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), 4069 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional)); 4070 4071 foreach (T1; RandomRanges) 4072 { 4073 foreach (T2; ReferenceRanges) 4074 { 4075 import std.array : array; 4076 4077 T1 A; 4078 T2 B; 4079 4080 A.reinit(); 4081 B.reinit(); 4082 4083 topN(A, B); 4084 4085 // BUG(?): sort doesn't accept DummyRanges (needs Slicing and Length) 4086 auto a = array(A); 4087 auto b = array(B); 4088 sort(a); 4089 sort(b); 4090 4091 assert(equal(a, [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ])); 4092 assert(equal(b, [ 6, 6, 7, 7, 8, 8, 9, 9, 10, 10 ])); 4093 } 4094 } 4095 } 4096 4097 // https://issues.dlang.org/show_bug.cgi?id=15421 4098 @system unittest 4099 { 4100 auto a = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ]; 4101 auto b = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ]; 4102 4103 topN(a, 4); 4104 topN(b[0 .. 4], b[4 .. $]); 4105 4106 sort(a[0 .. 4]); 4107 sort(a[4 .. $]); 4108 sort(b[0 .. 4]); 4109 sort(b[4 .. $]); 4110 4111 assert(a[0 .. 4] == b[0 .. 4]); 4112 assert(a[4 .. $] == b[4 .. $]); 4113 assert(a == b); 4114 } 4115 4116 // https://issues.dlang.org/show_bug.cgi?id=12987 4117 @system unittest 4118 { 4119 int[] a = [ 5, 7, 2, 6, 7 ]; 4120 int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; 4121 auto t = topN(a, b); 4122 sort(t); 4123 assert(t == [ 0, 1, 2, 2, 3 ]); 4124 } 4125 4126 // https://issues.dlang.org/show_bug.cgi?id=15420 4127 @system unittest 4128 { 4129 int[] a = [ 5, 7, 2, 6, 7 ]; 4130 int[] b = [ 2, 1, 5, 6, 7, 3, 0 ]; 4131 topN!"a > b"(a, b); 4132 sort!"a > b"(a); 4133 assert(a == [ 7, 7, 7, 6, 6 ]); 4134 } 4135 4136 /** 4137 Copies the top `n` elements of the 4138 $(REF_ALTTEXT input range, isInputRange, std,range,primitives) `source` into the 4139 random-access range `target`, where `n = target.length`. 4140 4141 Elements of `source` are not touched. If $(D 4142 sorted) is `true`, the target is sorted. Otherwise, the target 4143 respects the $(HTTP en.wikipedia.org/wiki/Binary_heap, heap property). 4144 4145 Params: 4146 less = The predicate to sort by. 4147 source = The source range. 4148 target = The target range. 4149 sorted = Whether to sort the elements copied into `target`. 4150 4151 Returns: The slice of `target` containing the copied elements. 4152 */ 4153 TRange topNCopy(alias less = "a < b", SRange, TRange) 4154 (SRange source, TRange target, SortOutput sorted = No.sortOutput) 4155 if (isInputRange!(SRange) && isRandomAccessRange!(TRange) 4156 && hasLength!(TRange) && hasSlicing!(TRange)) 4157 { 4158 import std.container : BinaryHeap; 4159 4160 if (target.empty) return target; 4161 auto heap = BinaryHeap!(TRange, less)(target, 0); 4162 foreach (e; source) heap.conditionalInsert(e); 4163 auto result = target[0 .. heap.length]; 4164 if (sorted == Yes.sortOutput) 4165 { 4166 while (!heap.empty) heap.removeFront(); 4167 } 4168 return result; 4169 } 4170 4171 /// 4172 @system unittest 4173 { 4174 import std.typecons : Yes; 4175 4176 int[] a = [ 10, 16, 2, 3, 1, 5, 0 ]; 4177 int[] b = new int[3]; 4178 topNCopy(a, b, Yes.sortOutput); 4179 assert(b == [ 0, 1, 2 ]); 4180 } 4181 4182 @system unittest 4183 { 4184 import std.random : Random = Xorshift, uniform, randomShuffle; 4185 import std.typecons : Yes; 4186 4187 auto r = Random(123_456_789); 4188 ptrdiff_t[] a = new ptrdiff_t[uniform(1, 1000, r)]; 4189 foreach (i, ref e; a) e = i; 4190 randomShuffle(a, r); 4191 auto n = uniform(0, a.length, r); 4192 ptrdiff_t[] b = new ptrdiff_t[n]; 4193 topNCopy!(binaryFun!("a < b"))(a, b, Yes.sortOutput); 4194 assert(isSorted!(binaryFun!("a < b"))(b)); 4195 } 4196 4197 /** 4198 Given a range of elements, constructs an index of its top $(I n) elements 4199 (i.e., the first $(I n) elements if the range were sorted). 4200 4201 Similar to $(LREF topN), except that the range is not modified. 4202 4203 Params: 4204 less = A binary predicate that defines the ordering of range elements. 4205 Defaults to `a < b`. 4206 ss = $(RED (Not implemented yet.)) Specify the swapping strategy. 4207 r = A 4208 $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) 4209 of elements to make an index for. 4210 index = A 4211 $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) 4212 with assignable elements to build the index in. The length of this range 4213 determines how many top elements to index in `r`. 4214 4215 This index range can either have integral elements, in which case the 4216 constructed index will consist of zero-based numerical indices into 4217 `r`; or it can have pointers to the element type of `r`, in which 4218 case the constructed index will be pointers to the top elements in 4219 `r`. 4220 sorted = Determines whether to sort the index by the elements they refer 4221 to. 4222 4223 See_also: $(LREF topN), $(LREF topNCopy). 4224 4225 BUGS: 4226 The swapping strategy parameter is not implemented yet; currently it is 4227 ignored. 4228 */ 4229 void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, 4230 Range, RangeIndex) 4231 (Range r, RangeIndex index, SortOutput sorted = No.sortOutput) 4232 if (isRandomAccessRange!Range && 4233 isRandomAccessRange!RangeIndex && 4234 hasAssignableElements!RangeIndex) 4235 { 4236 static assert(ss == SwapStrategy.unstable, 4237 "Stable swap strategy not implemented yet."); 4238 4239 import std.container.binaryheap : BinaryHeap; 4240 if (index.empty) return; 4241 4242 static if (isIntegral!(ElementType!(RangeIndex))) 4243 { 4244 import std.exception : enforce; 4245 4246 enforce(ElementType!(RangeIndex).max >= index.length, 4247 "Index type too small"); 4248 bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b) 4249 { 4250 return binaryFun!(less)(r[a], r[b]); 4251 } 4252 auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); 4253 foreach (i; 0 .. r.length) 4254 { 4255 heap.conditionalInsert(cast(ElementType!RangeIndex) i); 4256 } 4257 4258 } 4259 else static if (is(ElementType!(RangeIndex) == ElementType!(Range)*)) 4260 { 4261 static bool indirectLess(const ElementType!(RangeIndex) a, 4262 const ElementType!(RangeIndex) b) 4263 { 4264 return binaryFun!less(*a, *b); 4265 } 4266 auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); 4267 foreach (i; 0 .. r.length) 4268 { 4269 heap.conditionalInsert(&r[i]); 4270 } 4271 } 4272 else static assert(0, "Invalid ElementType"); 4273 4274 if (sorted == Yes.sortOutput) 4275 { 4276 while (!heap.empty) heap.removeFront(); 4277 } 4278 } 4279 4280 /// 4281 @system unittest 4282 { 4283 import std.typecons : Yes; 4284 4285 // Construct index to top 3 elements using numerical indices: 4286 int[] a = [ 10, 2, 7, 5, 8, 1 ]; 4287 int[] index = new int[3]; 4288 topNIndex(a, index, Yes.sortOutput); 4289 assert(index == [5, 1, 3]); // because a[5]==1, a[1]==2, a[3]==5 4290 4291 // Construct index to top 3 elements using pointer indices: 4292 int*[] ptrIndex = new int*[3]; 4293 topNIndex(a, ptrIndex, Yes.sortOutput); 4294 assert(ptrIndex == [ &a[5], &a[1], &a[3] ]); 4295 } 4296 4297 @system unittest 4298 { 4299 import std.conv : text; 4300 4301 { 4302 int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; 4303 int*[] b = new int*[5]; 4304 topNIndex!("a > b")(a, b, Yes.sortOutput); 4305 assert(b == [ &a[0], &a[2], &a[1], &a[6], &a[5]]); 4306 } 4307 { 4308 int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; 4309 auto b = new ubyte[5]; 4310 topNIndex!("a > b")(a, b, Yes.sortOutput); 4311 assert(b == [ cast(ubyte) 0, cast(ubyte) 2, cast(ubyte) 1, cast(ubyte) 6, cast(ubyte) 5], text(b)); 4312 } 4313 } 4314 4315 // medianOf 4316 /* 4317 Private for the time being. 4318 4319 Computes the median of 2 to 5 arbitrary indexes in random-access range `r` 4320 using hand-written specialized algorithms. The indexes must be distinct (if not, 4321 behavior is implementation-defined). The function also partitions the elements 4322 involved around the median, e.g. `medianOf(r, a, b, c)` not only fills `r[b]` 4323 with the median of `r[a]`, `r[b]`, and `r[c]`, but also puts the minimum in 4324 `r[a]` and the maximum in `r[c]`. 4325 4326 Params: 4327 less = The comparison predicate used, modeled as a 4328 $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, strict weak ordering) 4329 (irreflexive, antisymmetric, transitive, and implying a transitive equivalence). 4330 flag = Used only for even values of `T.length`. If `No.leanRight`, the median 4331 "leans left", meaning `medianOf(r, a, b, c, d)` puts the lower median of the 4332 four in `r[b]`, the minimum in `r[a]`, and the two others in `r[c]` and `r[d]`. 4333 Conversely, `median!("a < b", Yes.leanRight)(r, a, b, c, d)` puts the upper 4334 median of the four in `r[c]`, the maximum in `r[d]`, and the two others in 4335 `r[a]` and `r[b]`. 4336 r = The range containing the indexes. 4337 i = Two to five indexes inside `r`. 4338 */ 4339 private void medianOf( 4340 alias less = "a < b", 4341 Flag!"leanRight" flag = No.leanRight, 4342 Range, 4343 Indexes...) 4344 (Range r, Indexes i) 4345 if (isRandomAccessRange!Range && hasLength!Range && 4346 Indexes.length >= 2 && Indexes.length <= 5 && 4347 allSatisfy!(isUnsigned, Indexes)) 4348 { 4349 assert(r.length >= Indexes.length, "r.length must be greater than or" 4350 ~ " equal to Indexes.length"); 4351 import std.functional : binaryFun; 4352 alias lt = binaryFun!less; 4353 enum k = Indexes.length; 4354 import std.algorithm.mutation : swapAt; 4355 import std.format : format; 4356 4357 alias a = i[0]; 4358 static assert(is(typeof(a) == size_t), typeof(a).stringof ~ " must be" 4359 ~ " of type size_t"); 4360 static if (k >= 2) 4361 { 4362 alias b = i[1]; 4363 static assert(is(typeof(b) == size_t), typeof(b).stringof ~ " must be" 4364 ~ " of type size_t"); 4365 assert(a != b, "a != b "); 4366 } 4367 static if (k >= 3) 4368 { 4369 alias c = i[2]; 4370 static assert(is(typeof(c) == size_t), typeof(c).stringof ~ " must be" 4371 ~ " of type size_t"); 4372 assert(a != c && b != c, "a != c && b != c"); 4373 } 4374 static if (k >= 4) 4375 { 4376 alias d = i[3]; 4377 static assert(is(typeof(d) == size_t), typeof(d).stringof ~ " must be" 4378 ~ " of type size_t"); 4379 assert(a != d && b != d && c != d, "a != d && b != d && c != d failed"); 4380 } 4381 static if (k >= 5) 4382 { 4383 alias e = i[4]; 4384 static assert(is(typeof(e) == size_t), typeof(e).stringof ~ " must be" 4385 ~ " of type size_t"); 4386 assert(a != e && b != e && c != e && d != e, 4387 "a != e && b != e && c != e && d != e failed"); 4388 } 4389 4390 static if (k == 2) 4391 { 4392 if (lt(r[b], r[a])) r.swapAt(a, b); 4393 } 4394 else static if (k == 3) 4395 { 4396 if (lt(r[c], r[a])) // c < a 4397 { 4398 if (lt(r[a], r[b])) // c < a < b 4399 { 4400 r.swapAt(a, b); 4401 r.swapAt(a, c); 4402 } 4403 else // c < a, b <= a 4404 { 4405 r.swapAt(a, c); 4406 if (lt(r[b], r[a])) r.swapAt(a, b); 4407 } 4408 } 4409 else // a <= c 4410 { 4411 if (lt(r[b], r[a])) // b < a <= c 4412 { 4413 r.swapAt(a, b); 4414 } 4415 else // a <= c, a <= b 4416 { 4417 if (lt(r[c], r[b])) r.swapAt(b, c); 4418 } 4419 } 4420 assert(!lt(r[b], r[a]), "less than check failed"); 4421 assert(!lt(r[c], r[b]), "less than check failed"); 4422 } 4423 else static if (k == 4) 4424 { 4425 static if (flag == No.leanRight) 4426 { 4427 // Eliminate the rightmost from the competition 4428 if (lt(r[d], r[c])) r.swapAt(c, d); // c <= d 4429 if (lt(r[d], r[b])) r.swapAt(b, d); // b <= d 4430 medianOf!lt(r, a, b, c); 4431 } 4432 else 4433 { 4434 // Eliminate the leftmost from the competition 4435 if (lt(r[b], r[a])) r.swapAt(a, b); // a <= b 4436 if (lt(r[c], r[a])) r.swapAt(a, c); // a <= c 4437 medianOf!lt(r, b, c, d); 4438 } 4439 } 4440 else static if (k == 5) 4441 { 4442 // Credit: Teppo Niinimäki 4443 version (StdUnittest) scope(success) 4444 { 4445 assert(!lt(r[c], r[a]), "less than check failed"); 4446 assert(!lt(r[c], r[b]), "less than check failed"); 4447 assert(!lt(r[d], r[c]), "less than check failed"); 4448 assert(!lt(r[e], r[c]), "less than check failed"); 4449 } 4450 4451 if (lt(r[c], r[a])) r.swapAt(a, c); 4452 if (lt(r[d], r[b])) r.swapAt(b, d); 4453 if (lt(r[d], r[c])) 4454 { 4455 r.swapAt(c, d); 4456 r.swapAt(a, b); 4457 } 4458 if (lt(r[e], r[b])) r.swapAt(b, e); 4459 if (lt(r[e], r[c])) 4460 { 4461 r.swapAt(c, e); 4462 if (lt(r[c], r[a])) r.swapAt(a, c); 4463 } 4464 else 4465 { 4466 if (lt(r[c], r[b])) r.swapAt(b, c); 4467 } 4468 } 4469 } 4470 4471 @safe unittest 4472 { 4473 // Verify medianOf for all permutations of [1, 2, 2, 3, 4]. 4474 int[5] data = [1, 2, 2, 3, 4]; 4475 do 4476 { 4477 int[5] a = data; 4478 medianOf(a[], size_t(0), size_t(1)); 4479 assert(a[0] <= a[1]); 4480 4481 a[] = data[]; 4482 medianOf(a[], size_t(0), size_t(1), size_t(2)); 4483 assert(ordered(a[0], a[1], a[2])); 4484 4485 a[] = data[]; 4486 medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3)); 4487 assert(a[0] <= a[1] && a[1] <= a[2] && a[1] <= a[3]); 4488 4489 a[] = data[]; 4490 medianOf!("a < b", Yes.leanRight)(a[], size_t(0), size_t(1), 4491 size_t(2), size_t(3)); 4492 assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3]); 4493 4494 a[] = data[]; 4495 medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3), size_t(4)); 4496 assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3] && a[2] <= a[4]); 4497 } 4498 while (nextPermutation(data[])); 4499 } 4500 4501 // nextPermutation 4502 /** 4503 * Permutes `range` in-place to the next lexicographically greater 4504 * permutation. 4505 * 4506 * The predicate `less` defines the lexicographical ordering to be used on 4507 * the range. 4508 * 4509 * If the range is currently the lexicographically greatest permutation, it is 4510 * permuted back to the least permutation and false is returned. Otherwise, 4511 * true is returned. One can thus generate all permutations of a range by 4512 * sorting it according to `less`, which produces the lexicographically 4513 * least permutation, and then calling nextPermutation until it returns false. 4514 * This is guaranteed to generate all distinct permutations of the range 4515 * exactly once. If there are $(I N) elements in the range and all of them are 4516 * unique, then $(I N)! permutations will be generated. Otherwise, if there are 4517 * some duplicated elements, fewer permutations will be produced. 4518 ---- 4519 // Enumerate all permutations 4520 int[] a = [1,2,3,4,5]; 4521 do 4522 { 4523 // use the current permutation and 4524 // proceed to the next permutation of the array. 4525 } while (nextPermutation(a)); 4526 ---- 4527 * Params: 4528 * less = The ordering to be used to determine lexicographical ordering of the 4529 * permutations. 4530 * range = The range to permute. 4531 * 4532 * Returns: false if the range was lexicographically the greatest, in which 4533 * case the range is reversed back to the lexicographically smallest 4534 * permutation; otherwise returns true. 4535 * See_Also: 4536 * $(REF permutations, std,algorithm,iteration). 4537 */ 4538 bool nextPermutation(alias less="a < b", BidirectionalRange) 4539 (BidirectionalRange range) 4540 if (isBidirectionalRange!BidirectionalRange && 4541 hasSwappableElements!BidirectionalRange) 4542 { 4543 import std.algorithm.mutation : reverse, swap; 4544 import std.algorithm.searching : find; 4545 import std.range : retro, takeExactly; 4546 // Ranges of 0 or 1 element have no distinct permutations. 4547 if (range.empty) return false; 4548 4549 auto i = retro(range); 4550 auto last = i.save; 4551 4552 // Find last occurring increasing pair of elements 4553 size_t n = 1; 4554 for (i.popFront(); !i.empty; i.popFront(), last.popFront(), n++) 4555 { 4556 if (binaryFun!less(i.front, last.front)) 4557 break; 4558 } 4559 4560 if (i.empty) 4561 { 4562 // Entire range is decreasing: it's lexicographically the greatest. So 4563 // wrap it around. 4564 range.reverse(); 4565 return false; 4566 } 4567 4568 // Find last element greater than i.front. 4569 auto j = find!((a) => binaryFun!less(i.front, a))( 4570 takeExactly(retro(range), n)); 4571 4572 assert(!j.empty, "j must not be empty"); // shouldn't happen since i.front < last.front 4573 swap(i.front, j.front); 4574 reverse(takeExactly(retro(range), n)); 4575 4576 return true; 4577 } 4578 4579 /// 4580 @safe unittest 4581 { 4582 // Step through all permutations of a sorted array in lexicographic order 4583 int[] a = [1,2,3]; 4584 assert(nextPermutation(a) == true); 4585 assert(a == [1,3,2]); 4586 assert(nextPermutation(a) == true); 4587 assert(a == [2,1,3]); 4588 assert(nextPermutation(a) == true); 4589 assert(a == [2,3,1]); 4590 assert(nextPermutation(a) == true); 4591 assert(a == [3,1,2]); 4592 assert(nextPermutation(a) == true); 4593 assert(a == [3,2,1]); 4594 assert(nextPermutation(a) == false); 4595 assert(a == [1,2,3]); 4596 } 4597 4598 /// 4599 @safe unittest 4600 { 4601 // Step through permutations of an array containing duplicate elements: 4602 int[] a = [1,1,2]; 4603 assert(nextPermutation(a) == true); 4604 assert(a == [1,2,1]); 4605 assert(nextPermutation(a) == true); 4606 assert(a == [2,1,1]); 4607 assert(nextPermutation(a) == false); 4608 assert(a == [1,1,2]); 4609 } 4610 4611 @safe unittest 4612 { 4613 // Boundary cases: arrays of 0 or 1 element. 4614 int[] a1 = []; 4615 assert(!nextPermutation(a1)); 4616 assert(a1 == []); 4617 4618 int[] a2 = [1]; 4619 assert(!nextPermutation(a2)); 4620 assert(a2 == [1]); 4621 } 4622 4623 @safe unittest 4624 { 4625 import std.algorithm.comparison : equal; 4626 4627 auto a1 = [1, 2, 3, 4]; 4628 4629 assert(nextPermutation(a1)); 4630 assert(equal(a1, [1, 2, 4, 3])); 4631 4632 assert(nextPermutation(a1)); 4633 assert(equal(a1, [1, 3, 2, 4])); 4634 4635 assert(nextPermutation(a1)); 4636 assert(equal(a1, [1, 3, 4, 2])); 4637 4638 assert(nextPermutation(a1)); 4639 assert(equal(a1, [1, 4, 2, 3])); 4640 4641 assert(nextPermutation(a1)); 4642 assert(equal(a1, [1, 4, 3, 2])); 4643 4644 assert(nextPermutation(a1)); 4645 assert(equal(a1, [2, 1, 3, 4])); 4646 4647 assert(nextPermutation(a1)); 4648 assert(equal(a1, [2, 1, 4, 3])); 4649 4650 assert(nextPermutation(a1)); 4651 assert(equal(a1, [2, 3, 1, 4])); 4652 4653 assert(nextPermutation(a1)); 4654 assert(equal(a1, [2, 3, 4, 1])); 4655 4656 assert(nextPermutation(a1)); 4657 assert(equal(a1, [2, 4, 1, 3])); 4658 4659 assert(nextPermutation(a1)); 4660 assert(equal(a1, [2, 4, 3, 1])); 4661 4662 assert(nextPermutation(a1)); 4663 assert(equal(a1, [3, 1, 2, 4])); 4664 4665 assert(nextPermutation(a1)); 4666 assert(equal(a1, [3, 1, 4, 2])); 4667 4668 assert(nextPermutation(a1)); 4669 assert(equal(a1, [3, 2, 1, 4])); 4670 4671 assert(nextPermutation(a1)); 4672 assert(equal(a1, [3, 2, 4, 1])); 4673 4674 assert(nextPermutation(a1)); 4675 assert(equal(a1, [3, 4, 1, 2])); 4676 4677 assert(nextPermutation(a1)); 4678 assert(equal(a1, [3, 4, 2, 1])); 4679 4680 assert(nextPermutation(a1)); 4681 assert(equal(a1, [4, 1, 2, 3])); 4682 4683 assert(nextPermutation(a1)); 4684 assert(equal(a1, [4, 1, 3, 2])); 4685 4686 assert(nextPermutation(a1)); 4687 assert(equal(a1, [4, 2, 1, 3])); 4688 4689 assert(nextPermutation(a1)); 4690 assert(equal(a1, [4, 2, 3, 1])); 4691 4692 assert(nextPermutation(a1)); 4693 assert(equal(a1, [4, 3, 1, 2])); 4694 4695 assert(nextPermutation(a1)); 4696 assert(equal(a1, [4, 3, 2, 1])); 4697 4698 assert(!nextPermutation(a1)); 4699 assert(equal(a1, [1, 2, 3, 4])); 4700 } 4701 4702 @safe unittest 4703 { 4704 // Test with non-default sorting order 4705 int[] a = [3,2,1]; 4706 assert(nextPermutation!"a > b"(a) == true); 4707 assert(a == [3,1,2]); 4708 assert(nextPermutation!"a > b"(a) == true); 4709 assert(a == [2,3,1]); 4710 assert(nextPermutation!"a > b"(a) == true); 4711 assert(a == [2,1,3]); 4712 assert(nextPermutation!"a > b"(a) == true); 4713 assert(a == [1,3,2]); 4714 assert(nextPermutation!"a > b"(a) == true); 4715 assert(a == [1,2,3]); 4716 assert(nextPermutation!"a > b"(a) == false); 4717 assert(a == [3,2,1]); 4718 } 4719 4720 // https://issues.dlang.org/show_bug.cgi?id=13594 4721 @safe unittest 4722 { 4723 int[3] a = [1,2,3]; 4724 assert(nextPermutation(a[])); 4725 assert(a == [1,3,2]); 4726 } 4727 4728 // nextEvenPermutation 4729 /** 4730 * Permutes `range` in-place to the next lexicographically greater $(I even) 4731 * permutation. 4732 * 4733 * The predicate `less` defines the lexicographical ordering to be used on 4734 * the range. 4735 * 4736 * An even permutation is one which is produced by swapping an even number of 4737 * pairs of elements in the original range. The set of $(I even) permutations 4738 * is distinct from the set of $(I all) permutations only when there are no 4739 * duplicate elements in the range. If the range has $(I N) unique elements, 4740 * then there are exactly $(I N)!/2 even permutations. 4741 * 4742 * If the range is already the lexicographically greatest even permutation, it 4743 * is permuted back to the least even permutation and false is returned. 4744 * Otherwise, true is returned, and the range is modified in-place to be the 4745 * lexicographically next even permutation. 4746 * 4747 * One can thus generate the even permutations of a range with unique elements 4748 * by starting with the lexicographically smallest permutation, and repeatedly 4749 * calling nextEvenPermutation until it returns false. 4750 ---- 4751 // Enumerate even permutations 4752 int[] a = [1,2,3,4,5]; 4753 do 4754 { 4755 // use the current permutation and 4756 // proceed to the next even permutation of the array. 4757 } while (nextEvenPermutation(a)); 4758 ---- 4759 * One can also generate the $(I odd) permutations of a range by noting that 4760 * permutations obey the rule that even + even = even, and odd + even = odd. 4761 * Thus, by swapping the last two elements of a lexicographically least range, 4762 * it is turned into the first odd permutation. Then calling 4763 * nextEvenPermutation on this first odd permutation will generate the next 4764 * even permutation relative to this odd permutation, which is actually the 4765 * next odd permutation of the original range. Thus, by repeatedly calling 4766 * nextEvenPermutation until it returns false, one enumerates the odd 4767 * permutations of the original range. 4768 ---- 4769 // Enumerate odd permutations 4770 int[] a = [1,2,3,4,5]; 4771 swap(a[$-2], a[$-1]); // a is now the first odd permutation of [1,2,3,4,5] 4772 do 4773 { 4774 // use the current permutation and 4775 // proceed to the next odd permutation of the original array 4776 // (which is an even permutation of the first odd permutation). 4777 } while (nextEvenPermutation(a)); 4778 ---- 4779 * 4780 * Warning: Since even permutations are only distinct from all permutations 4781 * when the range elements are unique, this function assumes that there are no 4782 * duplicate elements under the specified ordering. If this is not _true, some 4783 * permutations may fail to be generated. When the range has non-unique 4784 * elements, you should use $(MYREF nextPermutation) instead. 4785 * 4786 * Params: 4787 * less = The ordering to be used to determine lexicographical ordering of the 4788 * permutations. 4789 * range = The range to permute. 4790 * 4791 * Returns: false if the range was lexicographically the greatest, in which 4792 * case the range is reversed back to the lexicographically smallest 4793 * permutation; otherwise returns true. 4794 */ 4795 bool nextEvenPermutation(alias less="a < b", BidirectionalRange) 4796 (BidirectionalRange range) 4797 if (isBidirectionalRange!BidirectionalRange && 4798 hasSwappableElements!BidirectionalRange) 4799 { 4800 import std.algorithm.mutation : reverse, swap; 4801 import std.algorithm.searching : find; 4802 import std.range : retro, takeExactly; 4803 // Ranges of 0 or 1 element have no distinct permutations. 4804 if (range.empty) return false; 4805 4806 bool oddParity = false; 4807 bool ret = true; 4808 do 4809 { 4810 auto i = retro(range); 4811 auto last = i.save; 4812 4813 // Find last occurring increasing pair of elements 4814 size_t n = 1; 4815 for (i.popFront(); !i.empty; 4816 i.popFront(), last.popFront(), n++) 4817 { 4818 if (binaryFun!less(i.front, last.front)) 4819 break; 4820 } 4821 4822 if (!i.empty) 4823 { 4824 // Find last element greater than i.front. 4825 auto j = find!((a) => binaryFun!less(i.front, a))( 4826 takeExactly(retro(range), n)); 4827 4828 // shouldn't happen since i.front < last.front 4829 assert(!j.empty, "j must not be empty"); 4830 4831 swap(i.front, j.front); 4832 oddParity = !oddParity; 4833 } 4834 else 4835 { 4836 // Entire range is decreasing: it's lexicographically 4837 // the greatest. 4838 ret = false; 4839 } 4840 4841 reverse(takeExactly(retro(range), n)); 4842 if ((n / 2) % 2 == 1) 4843 oddParity = !oddParity; 4844 } while (oddParity); 4845 4846 return ret; 4847 } 4848 4849 /// 4850 @safe unittest 4851 { 4852 // Step through even permutations of a sorted array in lexicographic order 4853 int[] a = [1,2,3]; 4854 assert(nextEvenPermutation(a) == true); 4855 assert(a == [2,3,1]); 4856 assert(nextEvenPermutation(a) == true); 4857 assert(a == [3,1,2]); 4858 assert(nextEvenPermutation(a) == false); 4859 assert(a == [1,2,3]); 4860 } 4861 4862 @safe unittest 4863 { 4864 auto a3 = [ 1, 2, 3, 4 ]; 4865 int count = 1; 4866 while (nextEvenPermutation(a3)) count++; 4867 assert(count == 12); 4868 } 4869 4870 @safe unittest 4871 { 4872 // Test with non-default sorting order 4873 auto a = [ 3, 2, 1 ]; 4874 4875 assert(nextEvenPermutation!"a > b"(a) == true); 4876 assert(a == [ 2, 1, 3 ]); 4877 assert(nextEvenPermutation!"a > b"(a) == true); 4878 assert(a == [ 1, 3, 2 ]); 4879 assert(nextEvenPermutation!"a > b"(a) == false); 4880 assert(a == [ 3, 2, 1 ]); 4881 } 4882 4883 @safe unittest 4884 { 4885 // Test various cases of rollover 4886 auto a = [ 3, 1, 2 ]; 4887 assert(nextEvenPermutation(a) == false); 4888 assert(a == [ 1, 2, 3 ]); 4889 4890 auto b = [ 3, 2, 1 ]; 4891 assert(nextEvenPermutation(b) == false); 4892 assert(b == [ 1, 3, 2 ]); 4893 } 4894 4895 // https://issues.dlang.org/show_bug.cgi?id=13594 4896 @safe unittest 4897 { 4898 int[3] a = [1,2,3]; 4899 assert(nextEvenPermutation(a[])); 4900 assert(a == [2,3,1]); 4901 } 4902 4903 /** 4904 Even permutations are useful for generating coordinates of certain geometric 4905 shapes. Here's a non-trivial example: 4906 */ 4907 @safe unittest 4908 { 4909 import std.math.algebraic : sqrt; 4910 4911 // Print the 60 vertices of a uniform truncated icosahedron (soccer ball) 4912 enum real Phi = (1.0 + sqrt(5.0)) / 2.0; // Golden ratio 4913 real[][] seeds = [ 4914 [0.0, 1.0, 3.0*Phi], 4915 [1.0, 2.0+Phi, 2.0*Phi], 4916 [Phi, 2.0, Phi^^3] 4917 ]; 4918 size_t n; 4919 foreach (seed; seeds) 4920 { 4921 // Loop over even permutations of each seed 4922 do 4923 { 4924 // Loop over all sign changes of each permutation 4925 size_t i; 4926 do 4927 { 4928 // Generate all possible sign changes 4929 for (i=0; i < seed.length; i++) 4930 { 4931 if (seed[i] != 0.0) 4932 { 4933 seed[i] = -seed[i]; 4934 if (seed[i] < 0.0) 4935 break; 4936 } 4937 } 4938 n++; 4939 } while (i < seed.length); 4940 } while (nextEvenPermutation(seed)); 4941 } 4942 assert(n == 60); 4943 } 4944 4945 /** 4946 Permutes `range` into the `perm` permutation. 4947 4948 The algorithm has a constant runtime complexity with respect to the number of 4949 permutations created. 4950 Due to the number of unique values of `ulong` only the first 21 elements of 4951 `range` can be permuted. The rest of the range will therefore not be 4952 permuted. 4953 This algorithm uses the $(HTTP en.wikipedia.org/wiki/Lehmer_code, Lehmer 4954 Code). 4955 4956 The algorithm works as follows: 4957 $(D_CODE 4958 auto pem = [4,0,4,1,0,0,0]; // permutation 2982 in factorial 4959 auto src = [0,1,2,3,4,5,6]; // the range to permutate 4960 4961 auto i = 0; // range index 4962 // range index iterates pem and src in sync 4963 // pem[i] + i is used as index into src 4964 // first src[pem[i] + i] is stored in t 4965 auto t = 4; // tmp value 4966 src = [0,1,2,3,n,5,6]; 4967 4968 // then the values between i and pem[i] + i are moved one 4969 // to the right 4970 src = [n,0,1,2,3,5,6]; 4971 // at last t is inserted into position i 4972 src = [4,0,1,2,3,5,6]; 4973 // finally i is incremented 4974 ++i; 4975 4976 // this process is repeated while i < pem.length 4977 4978 t = 0; 4979 src = [4,n,1,2,3,5,6]; 4980 src = [4,0,1,2,3,5,6]; 4981 ++i; 4982 t = 6; 4983 src = [4,0,1,2,3,5,n]; 4984 src = [4,0,n,1,2,3,5]; 4985 src = [4,0,6,1,2,3,5]; 4986 ) 4987 4988 Returns: 4989 The permuted range. 4990 4991 Params: 4992 range = The Range to permute. The original ordering will be lost. 4993 perm = The permutation to permutate `range` to. 4994 */ 4995 auto ref Range nthPermutation(Range) 4996 (auto ref Range range, const ulong perm) 4997 if (isRandomAccessRange!Range && hasLength!Range) 4998 { 4999 if (!nthPermutationImpl(range, perm)) 5000 { 5001 throw new Exception( 5002 "The range to permutate must not have less" 5003 ~ " elements than the factorial number has digits"); 5004 } 5005 5006 return range; 5007 } 5008 5009 /// 5010 pure @safe unittest 5011 { 5012 auto src = [0, 1, 2, 3, 4, 5, 6]; 5013 auto rslt = [4, 0, 6, 2, 1, 3, 5]; 5014 5015 src = nthPermutation(src, 2982); 5016 assert(src == rslt); 5017 } 5018 5019 /** 5020 Returns: `true` in case the permutation worked, `false` in case `perm` had 5021 more digits in the factorial number system than range had elements. 5022 This case must not occur as this would lead to out of range accesses. 5023 */ 5024 bool nthPermutationImpl(Range) 5025 (auto ref Range range, ulong perm) 5026 if (isRandomAccessRange!Range && hasLength!Range) 5027 { 5028 import std.range.primitives : ElementType; 5029 import std.numeric : decimalToFactorial; 5030 5031 // ulong.max has 21 digits in the factorial number system 5032 ubyte[21] fac; 5033 size_t idx = decimalToFactorial(perm, fac); 5034 5035 if (idx > range.length) 5036 { 5037 return false; 5038 } 5039 5040 ElementType!Range tmp; 5041 size_t i = 0; 5042 5043 for (; i < idx; ++i) 5044 { 5045 size_t re = fac[i]; 5046 tmp = range[re + i]; 5047 for (size_t j = re + i; j > i; --j) 5048 { 5049 range[j] = range[j - 1]; 5050 } 5051 range[i] = tmp; 5052 } 5053 5054 return true; 5055 } 5056 5057 /// 5058 pure @safe unittest 5059 { 5060 auto src = [0, 1, 2, 3, 4, 5, 6]; 5061 auto rslt = [4, 0, 6, 2, 1, 3, 5]; 5062 5063 bool worked = nthPermutationImpl(src, 2982); 5064 assert(worked); 5065 assert(src == rslt); 5066 } 5067 5068 pure @safe unittest 5069 { 5070 auto rslt = [4, 0, 6, 2, 1, 3, 5]; 5071 5072 auto src = nthPermutation([0, 1, 2, 3, 4, 5, 6], 2982); 5073 assert(src == rslt); 5074 } 5075 5076 pure @safe unittest 5077 { 5078 auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 5079 auto rslt = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10]; 5080 5081 src = nthPermutation(src, 2982); 5082 assert(src == rslt); 5083 } 5084 5085 pure @safe unittest 5086 { 5087 import std.exception : assertThrown; 5088 5089 auto src = [0, 1, 2, 3]; 5090 5091 assertThrown(nthPermutation(src, 2982)); 5092 } 5093 5094 pure @safe unittest 5095 { 5096 import std.internal.test.dummyrange; 5097 import std.meta : AliasSeq; 5098 5099 auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 5100 auto rsl = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10]; 5101 5102 foreach (T; AliasSeq!( 5103 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, int[]), 5104 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, int[]))) 5105 { 5106 static assert(isRandomAccessRange!(T)); 5107 static assert(hasLength!(T)); 5108 auto dr = T(src.dup); 5109 dr = nthPermutation(dr, 2982); 5110 5111 int idx; 5112 foreach (it; dr) 5113 { 5114 assert(it == rsl[idx++]); 5115 } 5116 } 5117 }