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 {
foreach(i,_;T[0..$-1])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 {
foreach(i,_;T[0..$-1])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;
even(int a)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
fun(int a)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;
even(int a)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
version(StdUnittest)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 {
test(alias less)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";
myLess(int a,int 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];
foreach(s;seeds)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
thisMerge1110 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 {
emptyMerge1122 @property bool empty()
1123 {
1124 return _lastFrontIndex == size_t.max;
1125 }
1126 }
1127
frontMerge1128 @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
frontIndexMerge1141 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
popFrontMerge1158 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 {
backMerge1186 @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
backIndexMerge1203 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
popBackMerge1220 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 {
saveMerge1257 @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 {
lengthMerge1270 @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];
foreach(DummyType;AllDummyRanges)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
validPredicates(E,less...)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 */
multiSort(less...)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
multiSortPredFun(Range,funs...)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
multiSortImpl(Range,SwapStrategy ss,funs...)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
byX(const ref A lhs,const ref A rhs)1614 static bool byX(const ref A lhs, const ref A rhs)
1615 {
1616 return lhs.x < rhs.x;
1617 }
1618
byY(const ref A lhs,const ref A rhs)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 @safe unittest
1646 {
lt(int a,int b)1647 bool lt(int a, int b) { return a < b; } static @system
1648 auto a = [2, 1];
1649 a.multiSort!(lt, lt);
1650 assert(a == [1, 2]);
1651 }
1652
getPivot(alias less,Range)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 */
shortSort(alias less,Range)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
trustedMoveEmplace(T)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)];
foreach(ref e;a)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 */
sort5(alias lt,Range)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,
1926 Range)(Range r)
1927 if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
1928 hasAssignableElements!Range)) ||
1929 (ss != SwapStrategy.unstable && hasAssignableElements!Range)) &&
1930 isRandomAccessRange!Range &&
1931 hasSlicing!Range &&
1932 hasLength!Range)
1933 /+ Unstable sorting uses the quicksort algorithm, which uses swapAt,
1934 which either uses swap(...), requiring swappable elements, or just
1935 swaps using assignment.
1936 Stable sorting uses TimSort, which needs to copy elements into a buffer,
1937 requiring assignable elements. +/
1938 {
1939 import std.range : assumeSorted;
1940 alias lessFun = binaryFun!(less);
1941 alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun
1942 static if (is(LessRet == bool))
1943 {
1944 static if (ss == SwapStrategy.unstable)
1945 quickSortImpl!(lessFun)(r, r.length);
1946 else //use Tim Sort for semistable & stable
1947 TimSortImpl!(lessFun, Range).sort(r, null);
1948
1949 assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof);
1950 }
1951 else
1952 {
1953 static assert(false, "Invalid predicate passed to sort: " ~ less.stringof);
1954 }
1955 return assumeSorted!less(r);
1956 }
1957
1958 ///
1959 @safe pure nothrow unittest
1960 {
1961 int[] array = [ 1, 2, 3, 4 ];
1962
1963 // sort in descending order
1964 array.sort!("a > b");
1965 assert(array == [ 4, 3, 2, 1 ]);
1966
1967 // sort in ascending order
1968 array.sort();
1969 assert(array == [ 1, 2, 3, 4 ]);
1970
1971 // sort with reusable comparator and chain
1972 alias myComp = (x, y) => x > y;
1973 assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]);
1974 }
1975
1976 ///
1977 @safe unittest
1978 {
1979 // Showcase stable sorting
1980 import std.algorithm.mutation : SwapStrategy;
1981 string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ];
1982 sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words);
1983 assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
1984 }
1985
1986 ///
1987 @safe unittest
1988 {
1989 // Sorting floating-point numbers in presence of NaN
1990 double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan];
1991
1992 import std.algorithm.comparison : equal;
1993 import std.math.operations : cmp;
1994 import std.math.traits : isIdentical;
1995
1996 sort!((a, b) => cmp(a, b) < 0)(numbers);
1997
1998 double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan];
1999 assert(numbers.equal!isIdentical(sorted));
2000 }
2001
2002 @safe unittest
2003 {
2004 // Simple regression benchmark
2005 import std.algorithm.iteration, std.algorithm.mutation;
2006 import std.array : array;
2007 import std.random : Random, uniform;
2008 import std.range : iota;
2009 Random rng;
2010 int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array;
2011 static uint comps;
less(int a,int b)2012 static bool less(int a, int b) { ++comps; return a < b; }
2013 sort!less(a); // random numbers
2014 sort!less(a); // sorted ascending
2015 a.reverse();
2016 sort!less(a); // sorted descending
2017 a[] = 0;
2018 sort!less(a); // all equal
2019
2020 // This should get smaller with time. On occasion it may go larger, but only
2021 // if there's thorough justification.
2022 debug enum uint watermark = 1676220;
2023 else enum uint watermark = 1676220;
2024
2025 import std.conv;
2026 assert(comps <= watermark, text("You seem to have pessimized sort! ",
2027 watermark, " < ", comps));
2028 assert(comps >= watermark, text("You seem to have improved sort!",
2029 " Please update watermark from ", watermark, " to ", comps));
2030 }
2031
2032 @safe unittest
2033 {
2034 import std.algorithm.internal : rndstuff;
2035 import std.algorithm.mutation : swapRanges;
2036 import std.random : Random = Xorshift, uniform;
2037 import std.uni : toUpper;
2038
2039 // sort using delegate
2040 auto a = new int[100];
2041 auto rnd = Random(123_456_789);
foreach(ref e;a)2042 foreach (ref e; a)
2043 {
2044 e = uniform(-100, 100, rnd);
2045 }
2046
2047 int i = 0;
greater2(int a,int b)2048 bool greater2(int a, int b) @safe { return a + i > b + i; }
2049 auto greater = &greater2;
2050 sort!(greater)(a);
2051 assert(isSorted!(greater)(a));
2052
2053 // sort using string
2054 sort!("a < b")(a);
2055 assert(isSorted!("a < b")(a));
2056
2057 // sort using function; all elements equal
foreach(ref e;a)2058 foreach (ref e; a)
2059 {
2060 e = 5;
2061 }
less(int a,int b)2062 static bool less(int a, int b) { return a < b; }
2063 sort!(less)(a);
2064 assert(isSorted!(less)(a));
2065
2066 string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ];
lessi(string a,string b)2067 bool lessi(string a, string b) { return toUpper(a) < toUpper(b); }
2068 sort!(lessi, SwapStrategy.stable)(words);
2069 assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
2070
2071 // sort using ternary predicate
2072 //sort!("b - a")(a);
2073 //assert(isSorted!(less)(a));
2074
2075 a = rndstuff!(int)();
2076 sort(a);
2077 assert(isSorted(a));
2078 auto b = rndstuff!(string)();
2079 sort!("toLower(a) < toLower(b)")(b);
2080 assert(isSorted!("toUpper(a) < toUpper(b)")(b));
2081
2082 {
2083 // https://issues.dlang.org/show_bug.cgi?id=10317
2084 enum E_10317 { a, b }
2085 auto a_10317 = new E_10317[10];
2086 sort(a_10317);
2087 }
2088
2089 {
2090 // https://issues.dlang.org/show_bug.cgi?id=7767
2091 // Unstable sort should complete without an excessive number of predicate calls
2092 // This would suggest it's running in quadratic time
2093
2094 // Compilation error if predicate is not static, i.e. a nested function
2095 static uint comp;
pred(size_t a,size_t b)2096 static bool pred(size_t a, size_t b)
2097 {
2098 ++comp;
2099 return a < b;
2100 }
2101
2102 size_t[] arr;
2103 arr.length = 1024;
2104
2105 foreach (k; 0 .. arr.length) arr[k] = k;
2106 swapRanges(arr[0..$/2], arr[$/2..$]);
2107
2108 sort!(pred, SwapStrategy.unstable)(arr);
2109 assert(comp < 25_000);
2110 }
2111
2112 {
2113 import std.algorithm.mutation : swap;
2114
2115 bool proxySwapCalled;
2116 struct S
2117 {
2118 int i;
2119 alias i this;
proxySwapS2120 void proxySwap(ref S other) { swap(i, other.i); proxySwapCalled = true; }
2121 @disable void opAssign(S value);
2122 }
2123
2124 alias R = S[];
2125 R r = [S(3), S(2), S(1)];
2126 static assert(hasSwappableElements!R);
2127 static assert(!hasAssignableElements!R);
2128 r.sort();
2129 assert(proxySwapCalled);
2130 }
2131
2132 // https://issues.dlang.org/show_bug.cgi?id=20751
2133 {
refPred(ref int a,ref int b)2134 static bool refPred(ref int a, ref int b)
2135 {
2136 return a < b;
2137 }
2138
2139 auto sortedArr = [5,4,3,2,1].sort!refPred;
2140 sortedArr.equalRange(3);
2141 }
2142 }
2143
quickSortImpl(alias less,Range)2144 private void quickSortImpl(alias less, Range)(Range r, size_t depth)
2145 {
2146 import std.algorithm.comparison : min, max;
2147 import std.algorithm.mutation : swap, swapAt;
2148 import std.conv : to;
2149
2150 alias Elem = ElementType!(Range);
2151 enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof);
2152 static assert(shortSortGetsBetter >= 1, Elem.stringof ~ " "
2153 ~ to!string(Elem.sizeof));
2154
2155 // partition
2156 while (r.length > shortSortGetsBetter)
2157 {
2158 if (depth == 0)
2159 {
2160 HeapOps!(less, Range).heapSort(r);
2161 return;
2162 }
2163 depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3;
2164
2165 const pivotIdx = getPivot!(less)(r);
2166 auto pivot = r[pivotIdx];
2167
2168 // partition
2169 r.swapAt(pivotIdx, r.length - 1);
2170 size_t lessI = size_t.max, greaterI = r.length - 1;
2171
2172 outer: for (;;)
2173 {
2174 alias pred = binaryFun!less;
2175 while (pred(r[++lessI], pivot)) {}
2176 assert(lessI <= greaterI, "sort: invalid comparison function.");
2177 for (;;)
2178 {
2179 if (greaterI == lessI) break outer;
2180 if (!pred(pivot, r[--greaterI])) break;
2181 }
2182 assert(lessI <= greaterI, "sort: invalid comparison function.");
2183 if (lessI == greaterI) break;
2184 r.swapAt(lessI, greaterI);
2185 }
2186
2187 r.swapAt(r.length - 1, lessI);
2188 auto left = r[0 .. lessI], right = r[lessI + 1 .. r.length];
2189 if (right.length > left.length)
2190 {
2191 swap(left, right);
2192 }
2193 .quickSortImpl!(less, Range)(right, depth);
2194 r = left;
2195 }
2196 // residual sort
2197 static if (shortSortGetsBetter > 1)
2198 {
2199 shortSort!(less, Range)(r);
2200 }
2201 }
2202
2203 // Heap operations for random-access ranges
package(std)2204 package(std) template HeapOps(alias less, Range)
2205 {
2206 import std.algorithm.mutation : swapAt;
2207
2208 static assert(isRandomAccessRange!Range, Range.stringof ~ " must be a"
2209 ~ " RandomAccessRange");
2210 static assert(hasLength!Range, Range.stringof ~ " must have length");
2211 static assert(hasSwappableElements!Range || hasAssignableElements!Range,
2212 Range.stringof ~ " must have swappable or assignable Elements");
2213
2214 alias lessFun = binaryFun!less;
2215
2216 //template because of https://issues.dlang.org/show_bug.cgi?id=12410
2217 void heapSort()(Range r)
2218 {
2219 // If true, there is nothing to do
2220 if (r.length < 2) return;
2221 // Build Heap
2222 buildHeap(r);
2223 // Sort
2224 for (size_t i = r.length - 1; i > 0; --i)
2225 {
2226 r.swapAt(0, i);
2227 percolate(r, 0, i);
2228 }
2229 }
2230
2231 //template because of https://issues.dlang.org/show_bug.cgi?id=12410
2232 void buildHeap()(Range r)
2233 {
2234 immutable n = r.length;
2235 for (size_t i = n / 2; i-- > 0; )
2236 {
2237 siftDown(r, i, n);
2238 }
2239 assert(isHeap(r), "r is not a heap");
2240 }
2241
2242 bool isHeap()(Range r)
2243 {
2244 size_t parent = 0;
2245 foreach (child; 1 .. r.length)
2246 {
2247 if (lessFun(r[parent], r[child])) return false;
2248 // Increment parent every other pass
2249 parent += !(child & 1);
2250 }
2251 return true;
2252 }
2253
2254 // Sifts down r[parent] (which is initially assumed to be messed up) so the
2255 // heap property is restored for r[parent .. end].
2256 // template because of https://issues.dlang.org/show_bug.cgi?id=12410
2257 void siftDown()(Range r, size_t parent, immutable size_t end)
2258 {
2259 for (;;)
2260 {
2261 auto child = (parent + 1) * 2;
2262 if (child >= end)
2263 {
2264 // Leftover left child?
2265 if (child == end && lessFun(r[parent], r[--child]))
2266 r.swapAt(parent, child);
2267 break;
2268 }
2269 auto leftChild = child - 1;
2270 if (lessFun(r[child], r[leftChild])) child = leftChild;
2271 if (!lessFun(r[parent], r[child])) break;
2272 r.swapAt(parent, child);
2273 parent = child;
2274 }
2275 }
2276
2277 // Alternate version of siftDown that performs fewer comparisons, see
2278 // https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort. The percolate
2279 // process first sifts the parent all the way down (without comparing it
2280 // against the leaves), and then a bit up until the heap property is
2281 // restored. So there are more swaps but fewer comparisons. Gains are made
2282 // when the final position is likely to end toward the bottom of the heap,
2283 // so not a lot of sifts back are performed.
2284 //template because of https://issues.dlang.org/show_bug.cgi?id=12410
2285 void percolate()(Range r, size_t parent, immutable size_t end)
2286 {
2287 immutable root = parent;
2288
2289 // Sift down
2290 for (;;)
2291 {
2292 auto child = (parent + 1) * 2;
2293
2294 if (child >= end)
2295 {
2296 if (child == end)
2297 {
2298 // Leftover left node.
2299 --child;
2300 r.swapAt(parent, child);
2301 parent = child;
2302 }
2303 break;
2304 }
2305
2306 auto leftChild = child - 1;
2307 if (lessFun(r[child], r[leftChild])) child = leftChild;
2308 r.swapAt(parent, child);
2309 parent = child;
2310 }
2311
2312 // Sift up
2313 for (auto child = parent; child > root; child = parent)
2314 {
2315 parent = (child - 1) / 2;
2316 if (!lessFun(r[parent], r[child])) break;
2317 r.swapAt(parent, child);
2318 }
2319 }
2320 }
2321
2322 // Tim Sort implementation
TimSortImpl(alias pred,R)2323 private template TimSortImpl(alias pred, R)
2324 {
2325 import core.bitop : bsr;
2326 import std.array : uninitializedArray;
2327
2328 static assert(isRandomAccessRange!R, R.stringof ~ " must be a"
2329 ~ " RandomAccessRange");
2330 static assert(hasLength!R, R.stringof ~ " must have a length");
2331 static assert(hasSlicing!R, R.stringof ~ " must support slicing");
2332 static assert(hasAssignableElements!R, R.stringof ~ " must have"
2333 ~ " assignable elements");
2334
2335 alias T = ElementType!R;
2336
2337 alias less = binaryFun!pred;
2338 bool greater()(auto ref T a, auto ref T b) { return less(b, a); }
2339 bool greaterEqual()(auto ref T a, auto ref T b) { return !less(a, b); }
2340 bool lessEqual()(auto ref T a, auto ref T b) { return !less(b, a); }
2341
2342 enum minimalMerge = 128;
2343 enum minimalGallop = 7;
2344 enum minimalStorage = 256;
2345 enum stackSize = 40;
2346
2347 struct Slice{ size_t base, length; }
2348
2349 // Entry point for tim sort
2350 void sort()(R range, T[] temp)
2351 {
2352 import std.algorithm.comparison : min;
2353 import std.format : format;
2354
2355 // Do insertion sort on small range
2356 if (range.length <= minimalMerge)
2357 {
2358 binaryInsertionSort(range);
2359 return;
2360 }
2361
2362 immutable minRun = minRunLength(range.length);
2363 immutable minTemp = min(range.length / 2, minimalStorage);
2364 size_t minGallop = minimalGallop;
2365 Slice[stackSize] stack = void;
2366 size_t stackLen = 0;
2367
2368 // Allocate temporary memory if not provided by user
2369 if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }();
2370
2371 for (size_t i = 0; i < range.length; )
2372 {
2373 // Find length of first run in list
2374 size_t runLen = firstRun(range[i .. range.length]);
2375
2376 // If run has less than minRun elements, extend using insertion sort
2377 if (runLen < minRun)
2378 {
2379 // Do not run farther than the length of the range
2380 immutable force = range.length - i > minRun ? minRun : range.length - i;
2381 binaryInsertionSort(range[i .. i + force], runLen);
2382 runLen = force;
2383 }
2384
2385 // Push run onto stack
2386 stack[stackLen++] = Slice(i, runLen);
2387 i += runLen;
2388
2389 // Collapse stack so that (e1 > e2 + e3 && e2 > e3)
2390 // STACK is | ... e1 e2 e3 >
2391 while (stackLen > 1)
2392 {
2393 immutable run4 = stackLen - 1;
2394 immutable run3 = stackLen - 2;
2395 immutable run2 = stackLen - 3;
2396 immutable run1 = stackLen - 4;
2397
2398 if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) ||
2399 (stackLen > 3 && stack[run1].length <= stack[run3].length + stack[run2].length) )
2400 {
2401 immutable at = stack[run2].length < stack[run4].length ? run2 : run3;
2402 mergeAt(range, stack[0 .. stackLen], at, minGallop, temp);
2403 }
2404 else if (stack[run3].length > stack[run4].length) break;
2405 else mergeAt(range, stack[0 .. stackLen], run3, minGallop, temp);
2406
2407 stackLen -= 1;
2408 }
2409
2410 // Assert that the code above established the invariant correctly
2411 version (StdUnittest)
2412 {
2413 if (stackLen == 2)
2414 {
2415 assert(stack[0].length > stack[1].length, format!
2416 "stack[0].length %s > stack[1].length %s"(
2417 stack[0].length, stack[1].length
2418 ));
2419 }
2420 else if (stackLen > 2)
2421 {
2422 foreach (k; 2 .. stackLen)
2423 {
2424 assert(stack[k - 2].length > stack[k - 1].length + stack[k].length,
2425 format!"stack[k - 2].length %s > stack[k - 1].length %s + stack[k].length %s"(
2426 stack[k - 2].length, stack[k - 1].length, stack[k].length
2427 ));
2428 assert(stack[k - 1].length > stack[k].length,
2429 format!"stack[k - 1].length %s > stack[k].length %s"(
2430 stack[k - 1].length, stack[k].length
2431 ));
2432 }
2433 }
2434 }
2435 }
2436
2437 // Force collapse stack until there is only one run left
2438 while (stackLen > 1)
2439 {
2440 immutable run3 = stackLen - 1;
2441 immutable run2 = stackLen - 2;
2442 immutable run1 = stackLen - 3;
2443 immutable at = stackLen >= 3 && stack[run1].length <= stack[run3].length
2444 ? run1 : run2;
2445 mergeAt(range, stack[0 .. stackLen], at, minGallop, temp);
2446 --stackLen;
2447 }
2448 }
2449
2450 // Calculates optimal value for minRun:
2451 // take first 6 bits of n and add 1 if any lower bits are set
2452 size_t minRunLength()(size_t n)
2453 {
2454 immutable shift = bsr(n)-5;
2455 auto result = (n >> shift) + !!(n & ~((1 << shift)-1));
2456 return result;
2457 }
2458
2459 // Returns length of first run in range
2460 size_t firstRun()(R range)
2461 out(ret)
2462 {
2463 assert(ret <= range.length, "ret must be less or equal than"
2464 ~ " range.length");
2465 }
2466 do
2467 {
2468 import std.algorithm.mutation : reverse;
2469
2470 if (range.length < 2) return range.length;
2471
2472 size_t i = 2;
2473 if (lessEqual(range[0], range[1]))
2474 {
2475 while (i < range.length && lessEqual(range[i-1], range[i])) ++i;
2476 }
2477 else
2478 {
2479 while (i < range.length && greater(range[i-1], range[i])) ++i;
2480 reverse(range[0 .. i]);
2481 }
2482 return i;
2483 }
2484
2485 // A binary insertion sort for building runs up to minRun length
2486 void binaryInsertionSort()(R range, size_t sortedLen = 1)
2487 out
2488 {
2489 if (!__ctfe) assert(isSorted!pred(range), "range must be sorted");
2490 }
2491 do
2492 {
2493 import std.algorithm.mutation : move;
2494
2495 for (; sortedLen < range.length; ++sortedLen)
2496 {
2497 T item = range.moveAt(sortedLen);
2498 size_t lower = 0;
2499 size_t upper = sortedLen;
2500 while (upper != lower)
2501 {
2502 size_t center = (lower + upper) / 2;
2503 if (less(item, range[center])) upper = center;
2504 else lower = center + 1;
2505 }
2506 //Currently (DMD 2.061) moveAll+retro is slightly less
2507 //efficient then stright 'for' loop
2508 //11 instructions vs 7 in the innermost loop [checked on Win32]
2509 //moveAll(retro(range[lower .. sortedLen]),
2510 // retro(range[lower+1 .. sortedLen+1]));
2511 for (upper=sortedLen; upper > lower; upper--)
2512 {
2513 static if (hasLvalueElements!R)
2514 move(range[upper -1], range[upper]);
2515 else
2516 range[upper] = range.moveAt(upper - 1);
2517 }
2518
2519 static if (hasLvalueElements!R)
2520 move(item, range[lower]);
2521 else
2522 range[lower] = move(item);
2523 }
2524 }
2525
2526 // Merge two runs in stack (at, at + 1)
2527 void mergeAt()(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp)
2528 in
2529 {
2530 import std.format : format;
2531 assert(stack.length >= 2, "stack be be greater than 1");
2532 assert(stack.length - at == 2 || stack.length - at == 3,
2533 format!"stack.length - at %s must be 2 or 3"(stack.length - at));
2534 }
2535 do
2536 {
2537 immutable base = stack[at].base;
2538 immutable mid = stack[at].length;
2539 immutable len = stack[at + 1].length + mid;
2540
2541 // Pop run from stack
2542 stack[at] = Slice(base, len);
2543 if (stack.length - at == 3) stack[$ - 2] = stack[$ - 1];
2544
2545 // Merge runs (at, at + 1)
2546 return merge(range[base .. base + len], mid, minGallop, temp);
2547 }
2548
2549 // Merge two runs in a range. Mid is the starting index of the second run.
2550 // minGallop and temp are references; The calling function must receive the updated values.
2551 void merge()(R range, size_t mid, ref size_t minGallop, ref T[] temp)
2552 in
2553 {
2554 if (!__ctfe)
2555 {
2556 assert(isSorted!pred(range[0 .. mid]), "range[0 .. mid] must be"
2557 ~ " sorted");
2558 assert(isSorted!pred(range[mid .. range.length]), "range[mid .."
2559 ~ " range.length] must be sorted");
2560 }
2561 }
2562 do
2563 {
2564 assert(mid < range.length, "mid must be less than the length of the"
2565 ~ " range");
2566
2567 // Reduce range of elements
2568 immutable firstElement = gallopForwardUpper(range[0 .. mid], range[mid]);
2569 immutable lastElement = gallopReverseLower(range[mid .. range.length], range[mid - 1]) + mid;
2570 range = range[firstElement .. lastElement];
2571 mid -= firstElement;
2572
2573 if (mid == 0 || mid == range.length) return;
2574
2575 // Call function which will copy smaller run into temporary memory
2576 if (mid <= range.length / 2)
2577 {
2578 temp = ensureCapacity(mid, temp);
2579 minGallop = mergeLo(range, mid, minGallop, temp);
2580 }
2581 else
2582 {
2583 temp = ensureCapacity(range.length - mid, temp);
2584 minGallop = mergeHi(range, mid, minGallop, temp);
2585 }
2586 }
2587
2588 // Enlarge size of temporary memory if needed
2589 T[] ensureCapacity()(size_t minCapacity, T[] temp)
2590 out(ret)
2591 {
2592 assert(ret.length >= minCapacity, "ensuring the capacity failed");
2593 }
2594 do
2595 {
2596 if (temp.length < minCapacity)
2597 {
2598 size_t newSize = 1<<(bsr(minCapacity)+1);
2599 //Test for overflow
2600 if (newSize < minCapacity) newSize = minCapacity;
2601
2602 if (__ctfe) temp.length = newSize;
2603 else temp = () @trusted { return uninitializedArray!(T[])(newSize); }();
2604 }
2605 return temp;
2606 }
2607
2608 // Merge front to back. Returns new value of minGallop.
2609 // temp must be large enough to store range[0 .. mid]
2610 size_t mergeLo()(R range, immutable size_t mid, size_t minGallop, T[] temp)
2611 out
2612 {
2613 if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted");
2614 }
2615 do
2616 {
2617 import std.algorithm.mutation : copy;
2618
2619 assert(mid <= range.length, "mid must be less than the length of the"
2620 ~ " range");
2621 assert(temp.length >= mid, "temp.length must be greater or equal to mid");
2622
2623 // Copy run into temporary memory
2624 temp = temp[0 .. mid];
2625 copy(range[0 .. mid], temp);
2626
2627 // Move first element into place
2628 moveEntry(range, mid, range, 0);
2629
2630 size_t i = 1, lef = 0, rig = mid + 1;
2631 size_t count_lef, count_rig;
2632 immutable lef_end = temp.length - 1;
2633
2634 if (lef < lef_end && rig < range.length)
2635 outer: while (true)
2636 {
2637 count_lef = 0;
2638 count_rig = 0;
2639
2640 // Linear merge
2641 while ((count_lef | count_rig) < minGallop)
2642 {
2643 if (lessEqual(temp[lef], range[rig]))
2644 {
2645 moveEntry(temp, lef++, range, i++);
2646 if (lef >= lef_end) break outer;
2647 ++count_lef;
2648 count_rig = 0;
2649 }
2650 else
2651 {
2652 moveEntry(range, rig++, range, i++);
2653 if (rig >= range.length) break outer;
2654 count_lef = 0;
2655 ++count_rig;
2656 }
2657 }
2658
2659 // Gallop merge
2660 do
2661 {
2662 count_lef = gallopForwardUpper(temp[lef .. $], range[rig]);
2663 foreach (j; 0 .. count_lef) moveEntry(temp, lef++, range, i++);
2664 if (lef >= temp.length) break outer;
2665
2666 count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]);
2667 foreach (j; 0 .. count_rig) moveEntry(range, rig++, range, i++);
2668 if (rig >= range.length) while (true)
2669 {
2670 moveEntry(temp, lef++, range, i++);
2671 if (lef >= temp.length) break outer;
2672 }
2673
2674 if (minGallop > 0) --minGallop;
2675 }
2676 while (count_lef >= minimalGallop || count_rig >= minimalGallop);
2677
2678 minGallop += 2;
2679 }
2680
2681 // Move remaining elements from right
2682 while (rig < range.length)
2683 moveEntry(range, rig++, range, i++);
2684
2685 // Move remaining elements from left
2686 while (lef < temp.length)
2687 moveEntry(temp, lef++, range, i++);
2688
2689 return minGallop > 0 ? minGallop : 1;
2690 }
2691
2692 // Merge back to front. Returns new value of minGallop.
2693 // temp must be large enough to store range[mid .. range.length]
2694 size_t mergeHi()(R range, immutable size_t mid, size_t minGallop, T[] temp)
2695 out
2696 {
2697 if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted");
2698 }
2699 do
2700 {
2701 import std.algorithm.mutation : copy;
2702 import std.format : format;
2703
2704 assert(mid <= range.length, "mid must be less or equal to range.length");
2705 assert(temp.length >= range.length - mid, format!
2706 "temp.length %s >= range.length %s - mid %s"(temp.length,
2707 range.length, mid));
2708
2709 // Copy run into temporary memory
2710 temp = temp[0 .. range.length - mid];
2711 copy(range[mid .. range.length], temp);
2712
2713 // Move first element into place
2714 moveEntry(range, mid - 1, range, range.length - 1);
2715
2716 size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1;
2717 size_t count_lef, count_rig;
2718
2719 outer:
2720 while (true)
2721 {
2722 count_lef = 0;
2723 count_rig = 0;
2724
2725 // Linear merge
2726 while ((count_lef | count_rig) < minGallop)
2727 {
2728 if (greaterEqual(temp[rig], range[lef]))
2729 {
2730 moveEntry(temp, rig, range, i--);
2731 if (rig == 1)
2732 {
2733 // Move remaining elements from left
2734 while (true)
2735 {
2736 moveEntry(range, lef, range, i--);
2737 if (lef == 0) break;
2738 --lef;
2739 }
2740
2741 // Move last element into place
2742 moveEntry(temp, 0, range, i);
2743
2744 break outer;
2745 }
2746 --rig;
2747 count_lef = 0;
2748 ++count_rig;
2749 }
2750 else
2751 {
2752 moveEntry(range, lef, range, i--);
2753 if (lef == 0) while (true)
2754 {
2755 moveEntry(temp, rig, range, i--);
2756 if (rig == 0) break outer;
2757 --rig;
2758 }
2759 --lef;
2760 ++count_lef;
2761 count_rig = 0;
2762 }
2763 }
2764
2765 // Gallop merge
2766 do
2767 {
2768 count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]);
2769 foreach (j; 0 .. count_rig)
2770 {
2771 moveEntry(temp, rig, range, i--);
2772 if (rig == 0) break outer;
2773 --rig;
2774 }
2775
2776 count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]);
2777 foreach (j; 0 .. count_lef)
2778 {
2779 moveEntry(range, lef, range, i--);
2780 if (lef == 0) while (true)
2781 {
2782 moveEntry(temp, rig, range, i--);
2783 if (rig == 0) break outer;
2784 --rig;
2785 }
2786 --lef;
2787 }
2788
2789 if (minGallop > 0) --minGallop;
2790 }
2791 while (count_lef >= minimalGallop || count_rig >= minimalGallop);
2792
2793 minGallop += 2;
2794 }
2795
2796 return minGallop > 0 ? minGallop : 1;
2797 }
2798
2799 // false = forward / lower, true = reverse / upper
2800 template gallopSearch(bool forwardReverse, bool lowerUpper)
2801 {
2802 // Gallop search on range according to attributes forwardReverse and lowerUpper
2803 size_t gallopSearch(R)(R range, T value)
2804 out(ret)
2805 {
2806 assert(ret <= range.length, "ret must be less or equal to"
2807 ~ " range.length");
2808 }
2809 do
2810 {
2811 size_t lower = 0, center = 1, upper = range.length;
2812 alias gap = center;
2813
2814 static if (forwardReverse)
2815 {
2816 static if (!lowerUpper) alias comp = lessEqual; // reverse lower
2817 static if (lowerUpper) alias comp = less; // reverse upper
2818
2819 // Gallop Search Reverse
2820 while (gap <= upper)
2821 {
2822 if (comp(value, range[upper - gap]))
2823 {
2824 upper -= gap;
2825 gap *= 2;
2826 }
2827 else
2828 {
2829 lower = upper - gap;
2830 break;
2831 }
2832 }
2833
2834 // Binary Search Reverse
2835 while (upper != lower)
2836 {
2837 center = lower + (upper - lower) / 2;
2838 if (comp(value, range[center])) upper = center;
2839 else lower = center + 1;
2840 }
2841 }
2842 else
2843 {
2844 static if (!lowerUpper) alias comp = greater; // forward lower
2845 static if (lowerUpper) alias comp = greaterEqual; // forward upper
2846
2847 // Gallop Search Forward
2848 while (lower + gap < upper)
2849 {
2850 if (comp(value, range[lower + gap]))
2851 {
2852 lower += gap;
2853 gap *= 2;
2854 }
2855 else
2856 {
2857 upper = lower + gap;
2858 break;
2859 }
2860 }
2861
2862 // Binary Search Forward
2863 while (lower != upper)
2864 {
2865 center = lower + (upper - lower) / 2;
2866 if (comp(value, range[center])) lower = center + 1;
2867 else upper = center;
2868 }
2869 }
2870
2871 return lower;
2872 }
2873 }
2874
2875 alias gallopForwardLower = gallopSearch!(false, false);
2876 alias gallopForwardUpper = gallopSearch!(false, true);
2877 alias gallopReverseLower = gallopSearch!( true, false);
2878 alias gallopReverseUpper = gallopSearch!( true, true);
2879
2880 /// Helper method that moves from[fIdx] into to[tIdx] if both are lvalues and
2881 /// uses a plain assignment if not (necessary for backwards compatibility)
2882 void moveEntry(X, Y)(ref X from, const size_t fIdx, ref Y to, const size_t tIdx)
2883 {
2884 // This template is instantiated with different combinations of range (R) and temp (T[]).
2885 // T[] obviously has lvalue-elements, so checking R should be enough here
2886 static if (hasLvalueElements!R)
2887 {
2888 import core.lifetime : move;
2889 move(from[fIdx], to[tIdx]);
2890 }
2891 else
2892 to[tIdx] = from[fIdx];
2893 }
2894 }
2895
2896 @safe unittest
2897 {
2898 import std.random : Random, uniform, randomShuffle;
2899
2900 // Element type with two fields
2901 static struct E
2902 {
2903 size_t value, index;
2904 }
2905
2906 // Generates data especially for testing sorting with Timsort
genSampleData(uint seed)2907 static E[] genSampleData(uint seed) @safe
2908 {
2909 import std.algorithm.mutation : swap, swapRanges;
2910
2911 auto rnd = Random(seed);
2912
2913 E[] arr;
2914 arr.length = 64 * 64;
2915
2916 // We want duplicate values for testing stability
2917 foreach (i, ref v; arr) v.value = i / 64;
2918
2919 // Swap ranges at random middle point (test large merge operation)
2920 immutable mid = uniform(arr.length / 4, arr.length / 4 * 3, rnd);
2921 swapRanges(arr[0 .. mid], arr[mid .. $]);
2922
2923 // Shuffle last 1/8 of the array (test insertion sort and linear merge)
2924 randomShuffle(arr[$ / 8 * 7 .. $], rnd);
2925
2926 // Swap few random elements (test galloping mode)
2927 foreach (i; 0 .. arr.length / 64)
2928 {
2929 immutable a = uniform(0, arr.length, rnd), b = uniform(0, arr.length, rnd);
2930 swap(arr[a], arr[b]);
2931 }
2932
2933 // Now that our test array is prepped, store original index value
2934 // This will allow us to confirm the array was sorted stably
2935 foreach (i, ref v; arr) v.index = i;
2936
2937 return arr;
2938 }
2939
2940 // Tests the Timsort function for correctness and stability
testSort(uint seed)2941 static bool testSort(uint seed)
2942 {
2943 import std.format : format;
2944 auto arr = genSampleData(seed);
2945
2946 // Now sort the array!
2947 static bool comp(E a, E b)
2948 {
2949 return a.value < b.value;
2950 }
2951
2952 sort!(comp, SwapStrategy.stable)(arr);
2953
2954 // Test that the array was sorted correctly
2955 assert(isSorted!comp(arr), "arr must be sorted");
2956
2957 // Test that the array was sorted stably
2958 foreach (i; 0 .. arr.length - 1)
2959 {
2960 if (arr[i].value == arr[i + 1].value)
2961 assert(arr[i].index < arr[i + 1].index, format!
2962 "arr[i %s].index %s < arr[i + 1].index %s"(
2963 i, arr[i].index, arr[i + 1].index));
2964 }
2965
2966 return true;
2967 }
2968
2969 enum seed = 310614065;
2970 testSort(seed);
2971
2972 enum result = testSort(seed);
2973 assert(result == true, "invalid result");
2974 }
2975
2976 // https://issues.dlang.org/show_bug.cgi?id=4584
2977 @safe unittest
2978 {
2979 assert(isSorted!"a < b"(sort!("a < b", SwapStrategy.stable)(
2980 [83, 42, 85, 86, 87, 22, 89, 30, 91, 46, 93, 94, 95, 6,
2981 97, 14, 33, 10, 101, 102, 103, 26, 105, 106, 107, 6]
2982 )));
2983
2984 }
2985
2986 @safe unittest
2987 {
2988 //test stable sort + zip
2989 import std.range;
2990 auto x = [10, 50, 60, 60, 20];
2991 dchar[] y = "abcde"d.dup;
2992
2993 sort!("a[0] < b[0]", SwapStrategy.stable)(zip(x, y));
2994 assert(x == [10, 20, 50, 60, 60]);
2995 assert(y == "aebcd"d);
2996 }
2997
2998 // https://issues.dlang.org/show_bug.cgi?id=14223
2999 @safe unittest
3000 {
3001 import std.array, std.range;
3002 auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array;
3003 sort!("a < b", SwapStrategy.stable)(arr);
3004 }
3005
3006 @safe unittest
3007 {
3008 static struct NoCopy
3009 {
3010 pure nothrow @nogc @safe:
3011
3012 int key;
thisNoCopy3013 this(scope const ref NoCopy)
3014 {
3015 assert(false, "Tried to copy struct!");
3016 }
opAssignNoCopy3017 ref opAssign()(scope const auto ref NoCopy other)
3018 {
3019 assert(false, "Tried to copy struct!");
3020 }
thisNoCopy3021 this(this) {}
3022 }
3023
makeArray(const size_t size)3024 static NoCopy[] makeArray(const size_t size)
3025 {
3026 NoCopy[] array = new NoCopy[](size);
3027 foreach (const i, ref t; array[0..$/2]) t.key = cast(int) (size - i);
3028 foreach (const i, ref t; array[$/2..$]) t.key = cast(int) i;
3029 return array;
3030 }
3031
3032 alias cmp = (ref NoCopy a, ref NoCopy b) => a.key < b.key;
3033 enum minMerge = TimSortImpl!(cmp, NoCopy[]).minimalMerge;
3034
3035 sort!(cmp, SwapStrategy.unstable)(makeArray(20));
3036 sort!(cmp, SwapStrategy.stable)(makeArray(minMerge - 5));
3037 sort!(cmp, SwapStrategy.stable)(makeArray(minMerge + 5));
3038 }
3039
3040 // schwartzSort
3041 /**
3042 Alternative sorting method that should be used when comparing keys involves an
3043 expensive computation.
3044
3045 Instead of using `less(a, b)` for comparing elements,
3046 `schwartzSort` uses `less(transform(a), transform(b))`. The values of the
3047 `transform` function are precomputed in a temporary array, thus saving on
3048 repeatedly computing it. Conversely, if the cost of `transform` is small
3049 compared to the cost of allocating and filling the precomputed array, `sort`
3050 may be faster and therefore preferable.
3051
3052 This approach to sorting is akin to the $(HTTP
3053 wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform), also known as
3054 the decorate-sort-undecorate pattern in Python and Lisp. The complexity is the
3055 same as that of the corresponding `sort`, but `schwartzSort` evaluates
3056 `transform` only `r.length` times (less than half when compared to regular
3057 sorting). The usage can be best illustrated with an example.
3058
3059 Example:
3060 ----
3061 uint hashFun(string) { ... expensive computation ... }
3062 string[] array = ...;
3063 // Sort strings by hash, slow
3064 sort!((a, b) => hashFun(a) < hashFun(b))(array);
3065 // Sort strings by hash, fast (only computes arr.length hashes):
3066 schwartzSort!(hashFun, "a < b")(array);
3067 ----
3068
3069 The `schwartzSort` function might require less temporary data and
3070 be faster than the Perl idiom or the decorate-sort-undecorate idiom
3071 present in Python and Lisp. This is because sorting is done in-place
3072 and only minimal extra data (one array of transformed elements) is
3073 created.
3074
3075 To check whether an array was sorted and benefit of the speedup of
3076 Schwartz sorting, a function `schwartzIsSorted` is not provided
3077 because the effect can be achieved by calling $(D
3078 isSorted!less(map!transform(r))).
3079
3080 Params:
3081 transform = The transformation to apply. Either a unary function
3082 (`unaryFun!transform(element)`), or a binary function
3083 (`binaryFun!transform(element, index)`).
3084 less = The predicate to sort the transformed elements by.
3085 ss = The swapping strategy to use.
3086 r = The range to sort.
3087
3088 Returns: The initial range wrapped as a `SortedRange` with the
3089 predicate `(a, b) => binaryFun!less(transform(a), transform(b))`.
3090 */
3091 SortedRange!(R, ((a, b) => binaryFun!less(unaryFun!transform(a),
3092 unaryFun!transform(b))))
3093 schwartzSort(alias transform, alias less = "a < b",
3094 SwapStrategy ss = SwapStrategy.unstable, R)(R r)
3095 if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R &&
3096 !is(typeof(binaryFun!less) == SwapStrategy))
3097 {
3098 import core.lifetime : emplace;
3099 import std.range : zip, SortedRange;
3100 import std.string : representation;
3101
3102 static if (is(typeof(unaryFun!transform(r.front))))
3103 {
3104 alias transformFun = unaryFun!transform;
3105 alias TB = typeof(transformFun(r.front));
3106 enum isBinary = false;
3107 }
3108 else static if (is(typeof(binaryFun!transform(r.front, 0))))
3109 {
3110 alias transformFun = binaryFun!transform;
3111 alias TB = typeof(transformFun(r.front, 0));
3112 enum isBinary = true;
3113 }
3114 else
3115 static assert(false, "unsupported `transform` alias");
3116
3117 // The `transform` function might return a qualified type, e.g. const(int).
3118 // Strip qualifiers if possible s.t. the temporary array is sortable.
3119 static if (is(TB : Unqual!TB))
3120 alias T = Unqual!TB;
3121 else
3122 static assert(false, "`transform` returns an unsortable qualified type: " ~ TB.stringof);
3123
trustedMalloc()3124 static trustedMalloc()(size_t len) @trusted
3125 {
3126 import core.checkedint : mulu;
3127 import core.memory : pureMalloc;
3128 bool overflow;
3129 const nbytes = mulu(len, T.sizeof, overflow);
3130 if (overflow) assert(false, "multiplication overflowed");
3131 T[] result = (cast(T*) pureMalloc(nbytes))[0 .. len];
3132 static if (hasIndirections!T)
3133 {
3134 import core.memory : GC;
3135 GC.addRange(result.ptr, nbytes);
3136 }
3137 return result;
3138 }
3139 auto xform1 = trustedMalloc(r.length);
3140
3141 size_t length;
scope(exit)3142 scope(exit)
3143 {
3144 static if (hasElaborateDestructor!T)
3145 {
3146 foreach (i; 0 .. length) collectException(destroy(xform1[i]));
3147 }
3148 static void trustedFree()(T[] p) @trusted
3149 {
3150 import core.memory : pureFree;
3151 static if (hasIndirections!T)
3152 {
3153 import core.memory : GC;
3154 GC.removeRange(p.ptr);
3155 }
3156 pureFree(p.ptr);
3157 }
3158 trustedFree(xform1);
3159 }
3160 for (; length != r.length; ++length)
3161 {
3162 static if (isBinary)
3163 emplace(&xform1[length], transformFun(r[length], length));
3164 else
3165 emplace(&xform1[length], transformFun(r[length]));
3166 }
3167 // Make sure we use ubyte[] and ushort[], not char[] and wchar[]
3168 // for the intermediate array, lest zip gets confused.
3169 static if (isNarrowString!(typeof(xform1)))
3170 {
3171 auto xform = xform1.representation();
3172 }
3173 else
3174 {
3175 alias xform = xform1;
3176 }
3177 zip(xform, r).sort!((a, b) => binaryFun!less(a[0], b[0]), ss)();
3178 return typeof(return)(r);
3179 }
3180
3181 /// ditto
3182 auto schwartzSort(alias transform, SwapStrategy ss, R)(R r)
3183 if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R)
3184 {
3185 return schwartzSort!(transform, "a < b", ss, R)(r);
3186 }
3187
3188 ///
3189 @safe pure unittest
3190 {
3191 import std.algorithm.iteration : map;
3192 import std.numeric : entropy;
3193
3194 auto lowEnt = [ 1.0, 0, 0 ],
3195 midEnt = [ 0.1, 0.1, 0.8 ],
3196 highEnt = [ 0.31, 0.29, 0.4 ];
3197 auto arr = new double[][3];
3198 arr[0] = midEnt;
3199 arr[1] = lowEnt;
3200 arr[2] = highEnt;
3201
3202 schwartzSort!(entropy, "a > b")(arr);
3203
3204 assert(arr[0] == highEnt);
3205 assert(arr[1] == midEnt);
3206 assert(arr[2] == lowEnt);
3207 assert(isSorted!("a > b")(map!(entropy)(arr)));
3208 }
3209
3210 @safe pure unittest
3211 {
3212 import std.algorithm.iteration : map;
3213 import std.numeric : entropy;
3214
3215 auto lowEnt = [ 1.0, 0, 0 ],
3216 midEnt = [ 0.1, 0.1, 0.8 ],
3217 highEnt = [ 0.31, 0.29, 0.4 ];
3218 auto arr = new double[][3];
3219 arr[0] = midEnt;
3220 arr[1] = lowEnt;
3221 arr[2] = highEnt;
3222
3223 schwartzSort!(entropy, "a < b")(arr);
3224
3225 assert(arr[0] == lowEnt);
3226 assert(arr[1] == midEnt);
3227 assert(arr[2] == highEnt);
3228 assert(isSorted!("a < b")(map!(entropy)(arr)));
3229 }
3230
3231 @safe pure unittest
3232 {
3233 // binary transform function
3234 string[] strings = [ "one", "two", "three" ];
3235 schwartzSort!((element, index) => size_t.max - index)(strings);
3236 assert(strings == [ "three", "two", "one" ]);
3237 }
3238
3239 // https://issues.dlang.org/show_bug.cgi?id=4909
3240 @safe pure unittest
3241 {
3242 import std.typecons : Tuple;
3243 Tuple!(char)[] chars;
3244 schwartzSort!"a[0]"(chars);
3245 }
3246
3247 // https://issues.dlang.org/show_bug.cgi?id=5924
3248 @safe pure unittest
3249 {
3250 import std.typecons : Tuple;
3251 Tuple!(char)[] chars;
3252 schwartzSort!((Tuple!(char) c){ return c[0]; })(chars);
3253 }
3254
3255 // https://issues.dlang.org/show_bug.cgi?id=13965
3256 @safe pure unittest
3257 {
3258 import std.typecons : Tuple;
3259 Tuple!(char)[] chars;
3260 schwartzSort!("a[0]", SwapStrategy.stable)(chars);
3261 }
3262
3263 // https://issues.dlang.org/show_bug.cgi?id=13965
3264 @safe pure unittest
3265 {
3266 import std.algorithm.iteration : map;
3267 import std.numeric : entropy;
3268
3269 auto lowEnt = [ 1.0, 0, 0 ],
3270 midEnt = [ 0.1, 0.1, 0.8 ],
3271 highEnt = [ 0.31, 0.29, 0.4 ];
3272 auto arr = new double[][3];
3273 arr[0] = midEnt;
3274 arr[1] = lowEnt;
3275 arr[2] = highEnt;
3276
3277 schwartzSort!(entropy, SwapStrategy.stable)(arr);
3278
3279 assert(arr[0] == lowEnt);
3280 assert(arr[1] == midEnt);
3281 assert(arr[2] == highEnt);
3282 assert(isSorted!("a < b")(map!(entropy)(arr)));
3283 }
3284
3285 // https://issues.dlang.org/show_bug.cgi?id=20799
3286 @safe unittest
3287 {
3288 import std.range : iota, retro;
3289 import std.array : array;
3290
3291 auto arr = 1_000_000.iota.retro.array;
3292 arr.schwartzSort!(
3293 n => new int(n),
3294 (a, b) => *a < *b
3295 );
3296 assert(arr.isSorted());
3297 }
3298
3299 // https://issues.dlang.org/show_bug.cgi?id=21183
3300 @safe unittest
3301 {
get(T)3302 static T get(T)(int) { return T.init; }
3303
3304 // There's no need to actually sort, just checking type interference
3305 if (false)
3306 {
3307 int[] arr;
3308
3309 // Fine because there are no indirections
3310 arr.schwartzSort!(get!(const int));
3311
3312 // Fine because it decays to immutable(int)*
3313 arr.schwartzSort!(get!(immutable int*));
3314
3315 // Disallowed because it would require a non-const reference
3316 static assert(!__traits(compiles, arr.schwartzSort!(get!(const Object))));
3317
3318 static struct Wrapper
3319 {
3320 int* ptr;
3321 }
3322
3323 // Disallowed because Wrapper.ptr would become mutable
3324 static assert(!__traits(compiles, arr.schwartzSort!(get!(const Wrapper))));
3325 }
3326 }
3327
3328 // partialSort
3329 /**
3330 Reorders the random-access range `r` such that the range `r[0 .. mid]`
3331 is the same as if the entire `r` were sorted, and leaves
3332 the range `r[mid .. r.length]` in no particular order.
3333
3334 Performs $(BIGOH r.length * log(mid)) evaluations of `pred`. The
3335 implementation simply calls `topN!(less, ss)(r, n)` and then $(D
3336 sort!(less, ss)(r[0 .. n])).
3337
3338 Params:
3339 less = The predicate to sort by.
3340 ss = The swapping strategy to use.
3341 r = The random-access range to reorder.
3342 n = The length of the initial segment of `r` to sort.
3343 */
3344 void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
3345 Range)(Range r, size_t n)
3346 if (isRandomAccessRange!(Range) && hasLength!(Range) && hasSlicing!(Range))
3347 {
3348 partialSort!(less, ss)(r[0 .. n], r[n .. $]);
3349 }
3350
3351 ///
3352 @system unittest
3353 {
3354 int[] a = [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ];
3355 partialSort(a, 5);
3356 assert(a[0 .. 5] == [ 0, 1, 2, 3, 4 ]);
3357 }
3358
3359 /**
3360 Stores the smallest elements of the two ranges in the left-hand range in sorted order.
3361
3362 Params:
3363 less = The predicate to sort by.
3364 ss = The swapping strategy to use.
3365 r1 = The first range.
3366 r2 = The second range.
3367 */
3368
3369 void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
3370 Range1, Range2)(Range1 r1, Range2 r2)
3371 if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
3372 isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) &&
3373 hasLvalueElements!Range1 && hasLvalueElements!Range2)
3374 {
3375 topN!(less, ss)(r1, r2);
3376 sort!(less, ss)(r1);
3377 }
3378 ///
3379 @system unittest
3380 {
3381 int[] a = [5, 7, 2, 6, 7];
3382 int[] b = [2, 1, 5, 6, 7, 3, 0];
3383
3384 partialSort(a, b);
3385 assert(a == [0, 1, 2, 2, 3]);
3386 }
3387
3388 // topN
3389 /**
3390 Reorders the range `r` using $(REF_ALTTEXT swap, swap, std,algorithm,mutation)
3391 such that `r[nth]` refers to the element that would fall there if the range
3392 were fully sorted.
3393
3394 It is akin to $(LINK2 https://en.wikipedia.org/wiki/Quickselect, Quickselect),
3395 and partitions `r` such that all elements
3396 `e1` from `r[0]` to `r[nth]` satisfy `!less(r[nth], e1)`,
3397 and all elements `e2` from `r[nth]` to `r[r.length]` satisfy
3398 `!less(e2, r[nth])`. Effectively, it finds the `nth + 1` smallest
3399 (according to `less`) elements in `r`. Performs an expected
3400 $(BIGOH r.length) (if unstable) or $(BIGOH r.length * log(r.length))
3401 (if stable) evaluations of `less` and $(REF_ALTTEXT swap, swap, std,algorithm,mutation).
3402
3403 If `n >= r.length`, the algorithm has no effect and returns
3404 `r[0 .. r.length]`.
3405
3406 Params:
3407 less = The predicate to sort by.
3408 ss = The swapping strategy to use.
3409 r = The random-access range to reorder.
3410 nth = The index of the element that should be in sorted position after the
3411 function is done.
3412
3413 Returns: a slice from `r[0]` to `r[nth]`, excluding `r[nth]` itself.
3414
3415 See_Also:
3416 $(LREF topNIndex),
3417
3418 BUGS:
3419
3420 Stable topN has not been implemented yet.
3421 */
3422 auto topN(alias less = "a < b",
3423 SwapStrategy ss = SwapStrategy.unstable,
3424 Range)(Range r, size_t nth)
3425 if (isRandomAccessRange!(Range) && hasLength!Range &&
3426 hasSlicing!Range && hasAssignableElements!Range)
3427 {
3428 static assert(ss == SwapStrategy.unstable,
3429 "Stable topN not yet implemented");
3430 if (nth >= r.length) return r[0 .. r.length];
3431 auto ret = r[0 .. nth];
3432 if (false)
3433 {
3434 // Workaround for https://issues.dlang.org/show_bug.cgi?id=16528
3435 // Safety checks: enumerate all potentially unsafe generic primitives
3436 // then use a @trusted implementation.
3437 cast(void) binaryFun!less(r[0], r[r.length - 1]);
3438 import std.algorithm.mutation : swapAt;
3439 r.swapAt(size_t(0), size_t(0));
3440 static assert(is(typeof(r.length) == size_t),
3441 typeof(r.length).stringof ~ " must be of type size_t");
3442 pivotPartition!less(r, 0);
3443 }
3444 bool useSampling = true;
3445 topNImpl!(binaryFun!less)(r, nth, useSampling);
3446 return ret;
3447 }
3448
3449 ///
3450 @safe unittest
3451 {
3452 int[] v = [ 25, 7, 9, 2, 0, 5, 21 ];
3453 topN!"a < b"(v, 100);
3454 assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]);
3455 auto n = 4;
3456 topN!((a, b) => a < b)(v, n);
3457 assert(v[n] == 9);
3458 }
3459
3460 // https://issues.dlang.org/show_bug.cgi?id=8341
3461 @safe unittest
3462 {
3463 import std.algorithm.comparison : equal;
3464 import std.range : zip;
3465 import std.typecons : tuple;
3466 auto a = [10, 30, 20];
3467 auto b = ["c", "b", "a"];
3468 assert(topN!"a[0] > b[0]"(zip(a, b), 2).equal([tuple(20, "a"), tuple(30, "b")]));
3469 }
3470
3471 private @trusted
topNImpl(alias less,R)3472 void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling)
3473 {
3474 for (;;)
3475 {
3476 import std.algorithm.mutation : swapAt;
3477 assert(n < r.length);
3478 size_t pivot = void;
3479
3480 // Decide strategy for partitioning
3481 if (n == 0)
3482 {
3483 pivot = 0;
3484 foreach (i; 1 .. r.length)
3485 if (less(r[i], r[pivot])) pivot = i;
3486 r.swapAt(n, pivot);
3487 return;
3488 }
3489 if (n + 1 == r.length)
3490 {
3491 pivot = 0;
3492 foreach (i; 1 .. r.length)
3493 if (less(r[pivot], r[i])) pivot = i;
3494 r.swapAt(n, pivot);
3495 return;
3496 }
3497 if (r.length <= 12)
3498 {
3499 pivot = pivotPartition!less(r, r.length / 2);
3500 }
3501 else if (n * 16 <= (r.length - 1) * 7)
3502 {
3503 pivot = topNPartitionOffMedian!(less, No.leanRight)
3504 (r, n, useSampling);
3505 // Quality check
3506 if (useSampling)
3507 {
3508 if (pivot < n)
3509 {
3510 if (pivot * 4 < r.length)
3511 {
3512 useSampling = false;
3513 }
3514 }
3515 else if ((r.length - pivot) * 8 < r.length * 3)
3516 {
3517 useSampling = false;
3518 }
3519 }
3520 }
3521 else if (n * 16 >= (r.length - 1) * 9)
3522 {
3523 pivot = topNPartitionOffMedian!(less, Yes.leanRight)
3524 (r, n, useSampling);
3525 // Quality check
3526 if (useSampling)
3527 {
3528 if (pivot < n)
3529 {
3530 if (pivot * 8 < r.length * 3)
3531 {
3532 useSampling = false;
3533 }
3534 }
3535 else if ((r.length - pivot) * 4 < r.length)
3536 {
3537 useSampling = false;
3538 }
3539 }
3540 }
3541 else
3542 {
3543 pivot = topNPartition!less(r, n, useSampling);
3544 // Quality check
3545 if (useSampling &&
3546 (pivot * 9 < r.length * 2 || pivot * 9 > r.length * 7))
3547 {
3548 // Failed - abort sampling going forward
3549 useSampling = false;
3550 }
3551 }
3552
3553 assert(pivot != size_t.max, "pivot must be not equal to size_t.max");
3554 // See how the pivot fares
3555 if (pivot == n)
3556 {
3557 return;
3558 }
3559 if (pivot > n)
3560 {
3561 r = r[0 .. pivot];
3562 }
3563 else
3564 {
3565 n -= pivot + 1;
3566 r = r[pivot + 1 .. r.length];
3567 }
3568 }
3569 }
3570
topNPartition(alias lp,R)3571 private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling)
3572 {
3573 import std.format : format;
3574 assert(r.length >= 9 && n < r.length, "length must be longer than 8"
3575 ~ " and n must be less than r.length");
3576 immutable ninth = r.length / 9;
3577 auto pivot = ninth / 2;
3578 // Position subrange r[lo .. hi] to have length equal to ninth and its upper
3579 // median r[lo .. hi][$ / 2] in exactly the same place as the upper median
3580 // of the entire range r[$ / 2]. This is to improve behavior for searching
3581 // the median in already sorted ranges.
3582 immutable lo = r.length / 2 - pivot, hi = lo + ninth;
3583 // We have either one straggler on the left, one on the right, or none.
3584 assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1,
3585 format!"straggler check failed lo %s, r.length %s, hi %s"(lo, r.length, hi));
3586 assert(lo >= ninth * 4, format!"lo %s >= ninth * 4 %s"(lo, ninth * 4));
3587 assert(r.length - hi >= ninth * 4,
3588 format!"r.length %s - hi %s >= ninth * 4 %s"(r.length, hi, ninth * 4));
3589
3590 // Partition in groups of 3, and the mid tertile again in groups of 3
3591 if (!useSampling)
3592 p3!lp(r, lo - ninth, hi + ninth);
3593 p3!lp(r, lo, hi);
3594
3595 // Get the median of medians of medians
3596 // Map the full interval of n to the full interval of the ninth
3597 pivot = (n * (ninth - 1)) / (r.length - 1);
3598 topNImpl!lp(r[lo .. hi], pivot, useSampling);
3599 return expandPartition!lp(r, lo, pivot + lo, hi);
3600 }
3601
p3(alias less,Range)3602 private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi)
3603 {
3604 import std.format : format;
3605 assert(lo <= hi && hi < r.length,
3606 format!"lo %s <= hi %s && hi < r.length %s"(lo, hi, r.length));
3607 immutable ln = hi - lo;
3608 for (; lo < hi; ++lo)
3609 {
3610 assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln));
3611 assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"(
3612 lo, ln, r.length));
3613 medianOf!less(r, lo - ln, lo, lo + ln);
3614 }
3615 }
3616
3617 private void p4(alias less, Flag!"leanRight" f, Range)
3618 (Range r, size_t lo, immutable size_t hi)
3619 {
3620 import std.format : format;
3621 assert(lo <= hi && hi < r.length, format!"lo %s <= hi %s && hi < r.length %s"(
3622 lo, hi, r.length));
3623 immutable ln = hi - lo, _2ln = ln * 2;
3624 for (; lo < hi; ++lo)
3625 {
3626 assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln));
3627 assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"(
3628 lo, ln, r.length));
3629 static if (f == Yes.leanRight)
3630 medianOf!(less, f)(r, lo - _2ln, lo - ln, lo, lo + ln);
3631 else
3632 medianOf!(less, f)(r, lo - ln, lo, lo + ln, lo + _2ln);
3633 }
3634 }
3635
3636 private size_t topNPartitionOffMedian(alias lp, Flag!"leanRight" f, R)
3637 (R r, size_t n, bool useSampling)
3638 {
3639 assert(r.length >= 12, "The length of r must be greater than 11");
3640 assert(n < r.length, "n must be less than the length of r");
3641 immutable _4 = r.length / 4;
3642 static if (f == Yes.leanRight)
3643 immutable leftLimit = 2 * _4;
3644 else
3645 immutable leftLimit = _4;
3646 // Partition in groups of 4, and the left quartile again in groups of 3
3647 if (!useSampling)
3648 {
3649 p4!(lp, f)(r, leftLimit, leftLimit + _4);
3650 }
3651 immutable _12 = _4 / 3;
3652 immutable lo = leftLimit + _12, hi = lo + _12;
3653 p3!lp(r, lo, hi);
3654
3655 // Get the median of medians of medians
3656 // Map the full interval of n to the full interval of the ninth
3657 immutable pivot = (n * (_12 - 1)) / (r.length - 1);
3658 topNImpl!lp(r[lo .. hi], pivot, useSampling);
3659 return expandPartition!lp(r, lo, pivot + lo, hi);
3660 }
3661
3662 /*
3663 Params:
3664 less = predicate
3665 r = range to partition
3666 pivot = pivot to partition around
3667 lo = value such that r[lo .. pivot] already less than r[pivot]
3668 hi = value such that r[pivot .. hi] already greater than r[pivot]
3669
3670 Returns: new position of pivot
3671 */
3672 private
expandPartition(alias lp,R)3673 size_t expandPartition(alias lp, R)(R r, size_t lo, size_t pivot, size_t hi)
3674 in
3675 {
3676 import std.algorithm.searching : all;
3677 assert(lo <= pivot, "lo must be less than or equal pivot");
3678 assert(pivot < hi, "pivot must be less than hi");
3679 assert(hi <= r.length, "hi must be less than or equal to the length of r");
3680 assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)),
3681 "r[lo .. pivot + 1] failed less than test");
3682 assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot])),
3683 "r[pivot + 1 .. hi] failed less than test");
3684 }
3685 out
3686 {
3687 import std.algorithm.searching : all;
3688 assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x)),
3689 "r[0 .. pivot + 1] failed less than test");
3690 assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot])),
3691 "r[pivot + 1 .. r.length] failed less than test");
3692 }
3693 do
3694 {
3695 import std.algorithm.mutation : swapAt;
3696 import std.algorithm.searching : all;
3697 // We work with closed intervals!
3698 --hi;
3699
3700 size_t left = 0, rite = r.length - 1;
3701 loop: for (;; ++left, --rite)
3702 {
3703 for (;; ++left)
3704 {
3705 if (left == lo) break loop;
3706 if (!lp(r[left], r[pivot])) break;
3707 }
3708 for (;; --rite)
3709 {
3710 if (rite == hi) break loop;
3711 if (!lp(r[pivot], r[rite])) break;
3712 }
3713 r.swapAt(left, rite);
3714 }
3715
3716 assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)),
3717 "r[lo .. pivot + 1] failed less than test");
3718 assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot])),
3719 "r[pivot + 1 .. hi + 1] failed less than test");
3720 assert(r[0 .. left].all!(x => !lp(r[pivot], x)),
3721 "r[0 .. left] failed less than test");
3722 assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot])),
3723 "r[rite + 1 .. r.length] failed less than test");
3724
3725 immutable oldPivot = pivot;
3726
3727 if (left < lo)
3728 {
3729 // First loop: spend r[lo .. pivot]
3730 for (; lo < pivot; ++left)
3731 {
3732 if (left == lo) goto done;
3733 if (!lp(r[oldPivot], r[left])) continue;
3734 --pivot;
3735 assert(!lp(r[oldPivot], r[pivot]), "less check failed");
3736 r.swapAt(left, pivot);
3737 }
3738 // Second loop: make left and pivot meet
3739 for (;; ++left)
3740 {
3741 if (left == pivot) goto done;
3742 if (!lp(r[oldPivot], r[left])) continue;
3743 for (;;)
3744 {
3745 if (left == pivot) goto done;
3746 --pivot;
3747 if (lp(r[pivot], r[oldPivot]))
3748 {
3749 r.swapAt(left, pivot);
3750 break;
3751 }
3752 }
3753 }
3754 }
3755
3756 // First loop: spend r[lo .. pivot]
3757 for (; hi != pivot; --rite)
3758 {
3759 if (rite == hi) goto done;
3760 if (!lp(r[rite], r[oldPivot])) continue;
3761 ++pivot;
3762 assert(!lp(r[pivot], r[oldPivot]), "less check failed");
3763 r.swapAt(rite, pivot);
3764 }
3765 // Second loop: make left and pivot meet
3766 for (; rite > pivot; --rite)
3767 {
3768 if (!lp(r[rite], r[oldPivot])) continue;
3769 while (rite > pivot)
3770 {
3771 ++pivot;
3772 if (lp(r[oldPivot], r[pivot]))
3773 {
3774 r.swapAt(rite, pivot);
3775 break;
3776 }
3777 }
3778 }
3779
3780 done:
3781 r.swapAt(oldPivot, pivot);
3782 return pivot;
3783 }
3784
3785 @safe unittest
3786 {
3787 auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ];
3788 assert(expandPartition!((a, b) => a < b)(a, 4, 5, 6) == 9);
3789
3790 import std.algorithm.iteration : map;
3791 import std.array : array;
3792 import std.random : uniform;
3793 import std.range : iota;
3794 auto size = uniform(1, 1000);
3795 a = iota(0, size).map!(_ => uniform(0, 1000)).array;
3796 if (a.length == 0) return;
3797 expandPartition!((a, b) => a < b)(a, a.length / 2, a.length / 2,
3798 a.length / 2 + 1);
3799 }
3800
3801 @safe unittest
3802 {
3803 import std.algorithm.comparison : max, min;
3804 import std.algorithm.iteration : reduce;
3805
3806 int[] v = [ 7, 6, 5, 4, 3, 2, 1, 0 ];
3807 ptrdiff_t n = 3;
3808 topN!("a < b")(v, n);
3809 assert(reduce!max(v[0 .. n]) <= v[n]);
3810 assert(reduce!min(v[n + 1 .. $]) >= v[n]);
3811 //
3812 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
3813 n = 3;
3814 topN(v, n);
3815 assert(reduce!max(v[0 .. n]) <= v[n]);
3816 assert(reduce!min(v[n + 1 .. $]) >= v[n]);
3817 //
3818 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
3819 n = 1;
3820 topN(v, n);
3821 assert(reduce!max(v[0 .. n]) <= v[n]);
3822 assert(reduce!min(v[n + 1 .. $]) >= v[n]);
3823 //
3824 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
3825 n = v.length - 1;
3826 topN(v, n);
3827 assert(v[n] == 7);
3828 //
3829 v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
3830 n = 0;
3831 topN(v, n);
3832 assert(v[n] == 1);
3833
3834 double[][] v1 = [[-10, -5], [-10, -3], [-10, -5], [-10, -4],
3835 [-10, -5], [-9, -5], [-9, -3], [-9, -5],];
3836
3837 // double[][] v1 = [ [-10, -5], [-10, -4], [-9, -5], [-9, -5],
3838 // [-10, -5], [-10, -3], [-10, -5], [-9, -3],];
3839 double[]*[] idx = [ &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5], &v1[6],
3840 &v1[7], ];
3841
3842 auto mid = v1.length / 2;
3843 topN!((a, b){ return (*a)[1] < (*b)[1]; })(idx, mid);
3844 foreach (e; idx[0 .. mid]) assert((*e)[1] <= (*idx[mid])[1]);
3845 foreach (e; idx[mid .. $]) assert((*e)[1] >= (*idx[mid])[1]);
3846 }
3847
3848 @safe unittest
3849 {
3850 import std.algorithm.comparison : max, min;
3851 import std.algorithm.iteration : reduce;
3852 import std.random : Random = Xorshift, uniform;
3853
3854 immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953];
foreach(s;seeds)3855 foreach (s; seeds)
3856 {
3857 auto r = Random(s);
3858
3859 int[] a = new int[uniform(1, 10000, r)];
3860 foreach (ref e; a) e = uniform(-1000, 1000, r);
3861
3862 auto k = uniform(0, a.length, r);
3863 topN(a, k);
3864 if (k > 0)
3865 {
3866 auto left = reduce!max(a[0 .. k]);
3867 assert(left <= a[k]);
3868 }
3869 if (k + 1 < a.length)
3870 {
3871 auto right = reduce!min(a[k + 1 .. $]);
3872 assert(right >= a[k]);
3873 }
3874 }
3875 }
3876
3877 // https://issues.dlang.org/show_bug.cgi?id=12987
3878 @safe unittest
3879 {
3880 int[] a = [ 25, 7, 9, 2, 0, 5, 21 ];
3881 auto n = 4;
3882 auto t = topN(a, n);
3883 sort(t);
3884 assert(t == [0, 2, 5, 7]);
3885 }
3886
3887 /**
3888 Stores the smallest elements of the two ranges in the left-hand range.
3889
3890 Params:
3891 less = The predicate to sort by.
3892 ss = The swapping strategy to use.
3893 r1 = The first range.
3894 r2 = The second range.
3895 */
3896 auto topN(alias less = "a < b",
3897 SwapStrategy ss = SwapStrategy.unstable,
3898 Range1, Range2)(Range1 r1, Range2 r2)
3899 if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
3900 isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) &&
3901 hasLvalueElements!Range1 && hasLvalueElements!Range2)
3902 {
3903 import std.container : BinaryHeap;
3904
3905 static assert(ss == SwapStrategy.unstable,
3906 "Stable topN not yet implemented");
3907
3908 auto heap = BinaryHeap!(Range1, less)(r1);
foreach(ref e;r2)3909 foreach (ref e; r2)
3910 {
3911 heap.conditionalSwap(e);
3912 }
3913
3914 return r1;
3915 }
3916
3917 ///
3918 @system unittest
3919 {
3920 int[] a = [ 5, 7, 2, 6, 7 ];
3921 int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
3922 topN(a, b);
3923 sort(a);
3924 assert(a == [0, 1, 2, 2, 3]);
3925 }
3926
3927 // https://issues.dlang.org/show_bug.cgi?id=15421
3928 @system unittest
3929 {
3930 import std.algorithm.comparison : equal;
3931 import std.internal.test.dummyrange;
3932 import std.meta : AliasSeq;
3933
3934 alias RandomRanges = AliasSeq!(
3935 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random)
3936 );
3937
3938 alias ReferenceRanges = AliasSeq!(
3939 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward),
3940 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional),
3941 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random),
3942 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward),
3943 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional));
3944
foreach(T1;RandomRanges)3945 foreach (T1; RandomRanges)
3946 {
3947 foreach (T2; ReferenceRanges)
3948 {
3949 import std.array;
3950
3951 T1 A;
3952 T2 B;
3953
3954 A.reinit();
3955 B.reinit();
3956
3957 topN(A, B);
3958
3959 // BUG(?): sort doesn't accept DummyRanges (needs Slicing and Length)
3960 auto a = array(A);
3961 auto b = array(B);
3962 sort(a);
3963 sort(b);
3964
3965 assert(equal(a, [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ]));
3966 assert(equal(b, [ 6, 6, 7, 7, 8, 8, 9, 9, 10, 10 ]));
3967 }
3968 }
3969 }
3970
3971 // https://issues.dlang.org/show_bug.cgi?id=15421
3972 @system unittest
3973 {
3974 auto a = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ];
3975 auto b = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ];
3976
3977 topN(a, 4);
3978 topN(b[0 .. 4], b[4 .. $]);
3979
3980 sort(a[0 .. 4]);
3981 sort(a[4 .. $]);
3982 sort(b[0 .. 4]);
3983 sort(b[4 .. $]);
3984
3985 assert(a[0 .. 4] == b[0 .. 4]);
3986 assert(a[4 .. $] == b[4 .. $]);
3987 assert(a == b);
3988 }
3989
3990 // https://issues.dlang.org/show_bug.cgi?id=12987
3991 @system unittest
3992 {
3993 int[] a = [ 5, 7, 2, 6, 7 ];
3994 int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
3995 auto t = topN(a, b);
3996 sort(t);
3997 assert(t == [ 0, 1, 2, 2, 3 ]);
3998 }
3999
4000 // https://issues.dlang.org/show_bug.cgi?id=15420
4001 @system unittest
4002 {
4003 int[] a = [ 5, 7, 2, 6, 7 ];
4004 int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
4005 topN!"a > b"(a, b);
4006 sort!"a > b"(a);
4007 assert(a == [ 7, 7, 7, 6, 6 ]);
4008 }
4009
4010 /**
4011 Copies the top `n` elements of the
4012 $(REF_ALTTEXT input range, isInputRange, std,range,primitives) `source` into the
4013 random-access range `target`, where `n = target.length`.
4014
4015 Elements of `source` are not touched. If $(D
4016 sorted) is `true`, the target is sorted. Otherwise, the target
4017 respects the $(HTTP en.wikipedia.org/wiki/Binary_heap, heap property).
4018
4019 Params:
4020 less = The predicate to sort by.
4021 source = The source range.
4022 target = The target range.
4023 sorted = Whether to sort the elements copied into `target`.
4024
4025 Returns: The slice of `target` containing the copied elements.
4026 */
4027 TRange topNCopy(alias less = "a < b", SRange, TRange)
4028 (SRange source, TRange target, SortOutput sorted = No.sortOutput)
4029 if (isInputRange!(SRange) && isRandomAccessRange!(TRange)
4030 && hasLength!(TRange) && hasSlicing!(TRange))
4031 {
4032 import std.container : BinaryHeap;
4033
4034 if (target.empty) return target;
4035 auto heap = BinaryHeap!(TRange, less)(target, 0);
4036 foreach (e; source) heap.conditionalInsert(e);
4037 auto result = target[0 .. heap.length];
4038 if (sorted == Yes.sortOutput)
4039 {
4040 while (!heap.empty) heap.removeFront();
4041 }
4042 return result;
4043 }
4044
4045 ///
4046 @system unittest
4047 {
4048 import std.typecons : Yes;
4049
4050 int[] a = [ 10, 16, 2, 3, 1, 5, 0 ];
4051 int[] b = new int[3];
4052 topNCopy(a, b, Yes.sortOutput);
4053 assert(b == [ 0, 1, 2 ]);
4054 }
4055
4056 @system unittest
4057 {
4058 import std.random : Random = Xorshift, uniform, randomShuffle;
4059 import std.typecons : Yes;
4060
4061 auto r = Random(123_456_789);
4062 ptrdiff_t[] a = new ptrdiff_t[uniform(1, 1000, r)];
4063 foreach (i, ref e; a) e = i;
4064 randomShuffle(a, r);
4065 auto n = uniform(0, a.length, r);
4066 ptrdiff_t[] b = new ptrdiff_t[n];
4067 topNCopy!(binaryFun!("a < b"))(a, b, Yes.sortOutput);
4068 assert(isSorted!(binaryFun!("a < b"))(b));
4069 }
4070
4071 /**
4072 Given a range of elements, constructs an index of its top $(I n) elements
4073 (i.e., the first $(I n) elements if the range were sorted).
4074
4075 Similar to $(LREF topN), except that the range is not modified.
4076
4077 Params:
4078 less = A binary predicate that defines the ordering of range elements.
4079 Defaults to `a < b`.
4080 ss = $(RED (Not implemented yet.)) Specify the swapping strategy.
4081 r = A
4082 $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives)
4083 of elements to make an index for.
4084 index = A
4085 $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives)
4086 with assignable elements to build the index in. The length of this range
4087 determines how many top elements to index in `r`.
4088
4089 This index range can either have integral elements, in which case the
4090 constructed index will consist of zero-based numerical indices into
4091 `r`; or it can have pointers to the element type of `r`, in which
4092 case the constructed index will be pointers to the top elements in
4093 `r`.
4094 sorted = Determines whether to sort the index by the elements they refer
4095 to.
4096
4097 See_also: $(LREF topN), $(LREF topNCopy).
4098
4099 BUGS:
4100 The swapping strategy parameter is not implemented yet; currently it is
4101 ignored.
4102 */
4103 void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
4104 Range, RangeIndex)
4105 (Range r, RangeIndex index, SortOutput sorted = No.sortOutput)
4106 if (isRandomAccessRange!Range &&
4107 isRandomAccessRange!RangeIndex &&
4108 hasAssignableElements!RangeIndex)
4109 {
4110 static assert(ss == SwapStrategy.unstable,
4111 "Stable swap strategy not implemented yet.");
4112
4113 import std.container.binaryheap : BinaryHeap;
4114 if (index.empty) return;
4115
4116 static if (isIntegral!(ElementType!(RangeIndex)))
4117 {
4118 import std.exception : enforce;
4119
4120 enforce(ElementType!(RangeIndex).max >= index.length,
4121 "Index type too small");
4122 bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b)
4123 {
4124 return binaryFun!(less)(r[a], r[b]);
4125 }
4126 auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
4127 foreach (i; 0 .. r.length)
4128 {
4129 heap.conditionalInsert(cast(ElementType!RangeIndex) i);
4130 }
4131
4132 }
4133 else static if (is(ElementType!(RangeIndex) == ElementType!(Range)*))
4134 {
4135 static bool indirectLess(const ElementType!(RangeIndex) a,
4136 const ElementType!(RangeIndex) b)
4137 {
4138 return binaryFun!less(*a, *b);
4139 }
4140 auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
4141 foreach (i; 0 .. r.length)
4142 {
4143 heap.conditionalInsert(&r[i]);
4144 }
4145 }
4146 else static assert(0, "Invalid ElementType");
4147
4148 if (sorted == Yes.sortOutput)
4149 {
4150 while (!heap.empty) heap.removeFront();
4151 }
4152 }
4153
4154 ///
4155 @system unittest
4156 {
4157 import std.typecons : Yes;
4158
4159 // Construct index to top 3 elements using numerical indices:
4160 int[] a = [ 10, 2, 7, 5, 8, 1 ];
4161 int[] index = new int[3];
4162 topNIndex(a, index, Yes.sortOutput);
4163 assert(index == [5, 1, 3]); // because a[5]==1, a[1]==2, a[3]==5
4164
4165 // Construct index to top 3 elements using pointer indices:
4166 int*[] ptrIndex = new int*[3];
4167 topNIndex(a, ptrIndex, Yes.sortOutput);
4168 assert(ptrIndex == [ &a[5], &a[1], &a[3] ]);
4169 }
4170
4171 @system unittest
4172 {
4173 import std.conv : text;
4174
4175 {
4176 int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ];
4177 int*[] b = new int*[5];
4178 topNIndex!("a > b")(a, b, Yes.sortOutput);
4179 assert(b == [ &a[0], &a[2], &a[1], &a[6], &a[5]]);
4180 }
4181 {
4182 int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ];
4183 auto b = new ubyte[5];
4184 topNIndex!("a > b")(a, b, Yes.sortOutput);
4185 assert(b == [ cast(ubyte) 0, cast(ubyte) 2, cast(ubyte) 1, cast(ubyte) 6, cast(ubyte) 5], text(b));
4186 }
4187 }
4188
4189 // medianOf
4190 /*
4191 Private for the time being.
4192
4193 Computes the median of 2 to 5 arbitrary indexes in random-access range `r`
4194 using hand-written specialized algorithms. The indexes must be distinct (if not,
4195 behavior is implementation-defined). The function also partitions the elements
4196 involved around the median, e.g. `medianOf(r, a, b, c)` not only fills `r[b]`
4197 with the median of `r[a]`, `r[b]`, and `r[c]`, but also puts the minimum in
4198 `r[a]` and the maximum in `r[c]`.
4199
4200 Params:
4201 less = The comparison predicate used, modeled as a
4202 $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, strict weak ordering)
4203 (irreflexive, antisymmetric, transitive, and implying a transitive equivalence).
4204 flag = Used only for even values of `T.length`. If `No.leanRight`, the median
4205 "leans left", meaning `medianOf(r, a, b, c, d)` puts the lower median of the
4206 four in `r[b]`, the minimum in `r[a]`, and the two others in `r[c]` and `r[d]`.
4207 Conversely, `median!("a < b", Yes.leanRight)(r, a, b, c, d)` puts the upper
4208 median of the four in `r[c]`, the maximum in `r[d]`, and the two others in
4209 `r[a]` and `r[b]`.
4210 r = The range containing the indexes.
4211 i = Two to five indexes inside `r`.
4212 */
4213 private void medianOf(
4214 alias less = "a < b",
4215 Flag!"leanRight" flag = No.leanRight,
4216 Range,
4217 Indexes...)
4218 (Range r, Indexes i)
4219 if (isRandomAccessRange!Range && hasLength!Range &&
4220 Indexes.length >= 2 && Indexes.length <= 5 &&
4221 allSatisfy!(isUnsigned, Indexes))
4222 {
4223 assert(r.length >= Indexes.length, "r.length must be greater than or"
4224 ~ " equal to Indexes.length");
4225 import std.functional : binaryFun;
4226 alias lt = binaryFun!less;
4227 enum k = Indexes.length;
4228 import std.algorithm.mutation : swapAt;
4229 import std.format : format;
4230
4231 alias a = i[0];
4232 static assert(is(typeof(a) == size_t), typeof(a).stringof ~ " must be"
4233 ~ " of type size_t");
4234 static if (k >= 2)
4235 {
4236 alias b = i[1];
4237 static assert(is(typeof(b) == size_t), typeof(b).stringof ~ " must be"
4238 ~ " of type size_t");
4239 assert(a != b, "a != b ");
4240 }
4241 static if (k >= 3)
4242 {
4243 alias c = i[2];
4244 static assert(is(typeof(c) == size_t), typeof(c).stringof ~ " must be"
4245 ~ " of type size_t");
4246 assert(a != c && b != c, "a != c && b != c");
4247 }
4248 static if (k >= 4)
4249 {
4250 alias d = i[3];
4251 static assert(is(typeof(d) == size_t), typeof(d).stringof ~ " must be"
4252 ~ " of type size_t");
4253 assert(a != d && b != d && c != d, "a != d && b != d && c != d failed");
4254 }
4255 static if (k >= 5)
4256 {
4257 alias e = i[4];
4258 static assert(is(typeof(e) == size_t), typeof(e).stringof ~ " must be"
4259 ~ " of type size_t");
4260 assert(a != e && b != e && c != e && d != e,
4261 "a != e && b != e && c != e && d != e failed");
4262 }
4263
4264 static if (k == 2)
4265 {
4266 if (lt(r[b], r[a])) r.swapAt(a, b);
4267 }
4268 else static if (k == 3)
4269 {
4270 if (lt(r[c], r[a])) // c < a
4271 {
4272 if (lt(r[a], r[b])) // c < a < b
4273 {
4274 r.swapAt(a, b);
4275 r.swapAt(a, c);
4276 }
4277 else // c < a, b <= a
4278 {
4279 r.swapAt(a, c);
4280 if (lt(r[b], r[a])) r.swapAt(a, b);
4281 }
4282 }
4283 else // a <= c
4284 {
4285 if (lt(r[b], r[a])) // b < a <= c
4286 {
4287 r.swapAt(a, b);
4288 }
4289 else // a <= c, a <= b
4290 {
4291 if (lt(r[c], r[b])) r.swapAt(b, c);
4292 }
4293 }
4294 assert(!lt(r[b], r[a]), "less than check failed");
4295 assert(!lt(r[c], r[b]), "less than check failed");
4296 }
4297 else static if (k == 4)
4298 {
4299 static if (flag == No.leanRight)
4300 {
4301 // Eliminate the rightmost from the competition
4302 if (lt(r[d], r[c])) r.swapAt(c, d); // c <= d
4303 if (lt(r[d], r[b])) r.swapAt(b, d); // b <= d
4304 medianOf!lt(r, a, b, c);
4305 }
4306 else
4307 {
4308 // Eliminate the leftmost from the competition
4309 if (lt(r[b], r[a])) r.swapAt(a, b); // a <= b
4310 if (lt(r[c], r[a])) r.swapAt(a, c); // a <= c
4311 medianOf!lt(r, b, c, d);
4312 }
4313 }
4314 else static if (k == 5)
4315 {
4316 // Credit: Teppo Niinimäki
scope(success)4317 version (StdUnittest) scope(success)
4318 {
4319 assert(!lt(r[c], r[a]), "less than check failed");
4320 assert(!lt(r[c], r[b]), "less than check failed");
4321 assert(!lt(r[d], r[c]), "less than check failed");
4322 assert(!lt(r[e], r[c]), "less than check failed");
4323 }
4324
4325 if (lt(r[c], r[a])) r.swapAt(a, c);
4326 if (lt(r[d], r[b])) r.swapAt(b, d);
4327 if (lt(r[d], r[c]))
4328 {
4329 r.swapAt(c, d);
4330 r.swapAt(a, b);
4331 }
4332 if (lt(r[e], r[b])) r.swapAt(b, e);
4333 if (lt(r[e], r[c]))
4334 {
4335 r.swapAt(c, e);
4336 if (lt(r[c], r[a])) r.swapAt(a, c);
4337 }
4338 else
4339 {
4340 if (lt(r[c], r[b])) r.swapAt(b, c);
4341 }
4342 }
4343 }
4344
4345 @safe unittest
4346 {
4347 // Verify medianOf for all permutations of [1, 2, 2, 3, 4].
4348 int[5] data = [1, 2, 2, 3, 4];
4349 do
4350 {
4351 int[5] a = data;
4352 medianOf(a[], size_t(0), size_t(1));
4353 assert(a[0] <= a[1]);
4354
4355 a[] = data[];
4356 medianOf(a[], size_t(0), size_t(1), size_t(2));
4357 assert(ordered(a[0], a[1], a[2]));
4358
4359 a[] = data[];
4360 medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3));
4361 assert(a[0] <= a[1] && a[1] <= a[2] && a[1] <= a[3]);
4362
4363 a[] = data[];
4364 medianOf!("a < b", Yes.leanRight)(a[], size_t(0), size_t(1),
4365 size_t(2), size_t(3));
4366 assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3]);
4367
4368 a[] = data[];
4369 medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3), size_t(4));
4370 assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3] && a[2] <= a[4]);
4371 }
4372 while (nextPermutation(data[]));
4373 }
4374
4375 // nextPermutation
4376 /**
4377 * Permutes `range` in-place to the next lexicographically greater
4378 * permutation.
4379 *
4380 * The predicate `less` defines the lexicographical ordering to be used on
4381 * the range.
4382 *
4383 * If the range is currently the lexicographically greatest permutation, it is
4384 * permuted back to the least permutation and false is returned. Otherwise,
4385 * true is returned. One can thus generate all permutations of a range by
4386 * sorting it according to `less`, which produces the lexicographically
4387 * least permutation, and then calling nextPermutation until it returns false.
4388 * This is guaranteed to generate all distinct permutations of the range
4389 * exactly once. If there are $(I N) elements in the range and all of them are
4390 * unique, then $(I N)! permutations will be generated. Otherwise, if there are
4391 * some duplicated elements, fewer permutations will be produced.
4392 ----
4393 // Enumerate all permutations
4394 int[] a = [1,2,3,4,5];
4395 do
4396 {
4397 // use the current permutation and
4398 // proceed to the next permutation of the array.
4399 } while (nextPermutation(a));
4400 ----
4401 * Params:
4402 * less = The ordering to be used to determine lexicographical ordering of the
4403 * permutations.
4404 * range = The range to permute.
4405 *
4406 * Returns: false if the range was lexicographically the greatest, in which
4407 * case the range is reversed back to the lexicographically smallest
4408 * permutation; otherwise returns true.
4409 * See_Also:
4410 * $(REF permutations, std,algorithm,iteration).
4411 */
4412 bool nextPermutation(alias less="a < b", BidirectionalRange)
4413 (BidirectionalRange range)
4414 if (isBidirectionalRange!BidirectionalRange &&
4415 hasSwappableElements!BidirectionalRange)
4416 {
4417 import std.algorithm.mutation : reverse, swap;
4418 import std.algorithm.searching : find;
4419 import std.range : retro, takeExactly;
4420 // Ranges of 0 or 1 element have no distinct permutations.
4421 if (range.empty) return false;
4422
4423 auto i = retro(range);
4424 auto last = i.save;
4425
4426 // Find last occurring increasing pair of elements
4427 size_t n = 1;
4428 for (i.popFront(); !i.empty; i.popFront(), last.popFront(), n++)
4429 {
4430 if (binaryFun!less(i.front, last.front))
4431 break;
4432 }
4433
4434 if (i.empty)
4435 {
4436 // Entire range is decreasing: it's lexicographically the greatest. So
4437 // wrap it around.
4438 range.reverse();
4439 return false;
4440 }
4441
4442 // Find last element greater than i.front.
4443 auto j = find!((a) => binaryFun!less(i.front, a))(
4444 takeExactly(retro(range), n));
4445
4446 assert(!j.empty, "j must not be empty"); // shouldn't happen since i.front < last.front
4447 swap(i.front, j.front);
4448 reverse(takeExactly(retro(range), n));
4449
4450 return true;
4451 }
4452
4453 ///
4454 @safe unittest
4455 {
4456 // Step through all permutations of a sorted array in lexicographic order
4457 int[] a = [1,2,3];
4458 assert(nextPermutation(a) == true);
4459 assert(a == [1,3,2]);
4460 assert(nextPermutation(a) == true);
4461 assert(a == [2,1,3]);
4462 assert(nextPermutation(a) == true);
4463 assert(a == [2,3,1]);
4464 assert(nextPermutation(a) == true);
4465 assert(a == [3,1,2]);
4466 assert(nextPermutation(a) == true);
4467 assert(a == [3,2,1]);
4468 assert(nextPermutation(a) == false);
4469 assert(a == [1,2,3]);
4470 }
4471
4472 ///
4473 @safe unittest
4474 {
4475 // Step through permutations of an array containing duplicate elements:
4476 int[] a = [1,1,2];
4477 assert(nextPermutation(a) == true);
4478 assert(a == [1,2,1]);
4479 assert(nextPermutation(a) == true);
4480 assert(a == [2,1,1]);
4481 assert(nextPermutation(a) == false);
4482 assert(a == [1,1,2]);
4483 }
4484
4485 @safe unittest
4486 {
4487 // Boundary cases: arrays of 0 or 1 element.
4488 int[] a1 = [];
4489 assert(!nextPermutation(a1));
4490 assert(a1 == []);
4491
4492 int[] a2 = [1];
4493 assert(!nextPermutation(a2));
4494 assert(a2 == [1]);
4495 }
4496
4497 @safe unittest
4498 {
4499 import std.algorithm.comparison : equal;
4500
4501 auto a1 = [1, 2, 3, 4];
4502
4503 assert(nextPermutation(a1));
4504 assert(equal(a1, [1, 2, 4, 3]));
4505
4506 assert(nextPermutation(a1));
4507 assert(equal(a1, [1, 3, 2, 4]));
4508
4509 assert(nextPermutation(a1));
4510 assert(equal(a1, [1, 3, 4, 2]));
4511
4512 assert(nextPermutation(a1));
4513 assert(equal(a1, [1, 4, 2, 3]));
4514
4515 assert(nextPermutation(a1));
4516 assert(equal(a1, [1, 4, 3, 2]));
4517
4518 assert(nextPermutation(a1));
4519 assert(equal(a1, [2, 1, 3, 4]));
4520
4521 assert(nextPermutation(a1));
4522 assert(equal(a1, [2, 1, 4, 3]));
4523
4524 assert(nextPermutation(a1));
4525 assert(equal(a1, [2, 3, 1, 4]));
4526
4527 assert(nextPermutation(a1));
4528 assert(equal(a1, [2, 3, 4, 1]));
4529
4530 assert(nextPermutation(a1));
4531 assert(equal(a1, [2, 4, 1, 3]));
4532
4533 assert(nextPermutation(a1));
4534 assert(equal(a1, [2, 4, 3, 1]));
4535
4536 assert(nextPermutation(a1));
4537 assert(equal(a1, [3, 1, 2, 4]));
4538
4539 assert(nextPermutation(a1));
4540 assert(equal(a1, [3, 1, 4, 2]));
4541
4542 assert(nextPermutation(a1));
4543 assert(equal(a1, [3, 2, 1, 4]));
4544
4545 assert(nextPermutation(a1));
4546 assert(equal(a1, [3, 2, 4, 1]));
4547
4548 assert(nextPermutation(a1));
4549 assert(equal(a1, [3, 4, 1, 2]));
4550
4551 assert(nextPermutation(a1));
4552 assert(equal(a1, [3, 4, 2, 1]));
4553
4554 assert(nextPermutation(a1));
4555 assert(equal(a1, [4, 1, 2, 3]));
4556
4557 assert(nextPermutation(a1));
4558 assert(equal(a1, [4, 1, 3, 2]));
4559
4560 assert(nextPermutation(a1));
4561 assert(equal(a1, [4, 2, 1, 3]));
4562
4563 assert(nextPermutation(a1));
4564 assert(equal(a1, [4, 2, 3, 1]));
4565
4566 assert(nextPermutation(a1));
4567 assert(equal(a1, [4, 3, 1, 2]));
4568
4569 assert(nextPermutation(a1));
4570 assert(equal(a1, [4, 3, 2, 1]));
4571
4572 assert(!nextPermutation(a1));
4573 assert(equal(a1, [1, 2, 3, 4]));
4574 }
4575
4576 @safe unittest
4577 {
4578 // Test with non-default sorting order
4579 int[] a = [3,2,1];
4580 assert(nextPermutation!"a > b"(a) == true);
4581 assert(a == [3,1,2]);
4582 assert(nextPermutation!"a > b"(a) == true);
4583 assert(a == [2,3,1]);
4584 assert(nextPermutation!"a > b"(a) == true);
4585 assert(a == [2,1,3]);
4586 assert(nextPermutation!"a > b"(a) == true);
4587 assert(a == [1,3,2]);
4588 assert(nextPermutation!"a > b"(a) == true);
4589 assert(a == [1,2,3]);
4590 assert(nextPermutation!"a > b"(a) == false);
4591 assert(a == [3,2,1]);
4592 }
4593
4594 // https://issues.dlang.org/show_bug.cgi?id=13594
4595 @safe unittest
4596 {
4597 int[3] a = [1,2,3];
4598 assert(nextPermutation(a[]));
4599 assert(a == [1,3,2]);
4600 }
4601
4602 // nextEvenPermutation
4603 /**
4604 * Permutes `range` in-place to the next lexicographically greater $(I even)
4605 * permutation.
4606 *
4607 * The predicate `less` defines the lexicographical ordering to be used on
4608 * the range.
4609 *
4610 * An even permutation is one which is produced by swapping an even number of
4611 * pairs of elements in the original range. The set of $(I even) permutations
4612 * is distinct from the set of $(I all) permutations only when there are no
4613 * duplicate elements in the range. If the range has $(I N) unique elements,
4614 * then there are exactly $(I N)!/2 even permutations.
4615 *
4616 * If the range is already the lexicographically greatest even permutation, it
4617 * is permuted back to the least even permutation and false is returned.
4618 * Otherwise, true is returned, and the range is modified in-place to be the
4619 * lexicographically next even permutation.
4620 *
4621 * One can thus generate the even permutations of a range with unique elements
4622 * by starting with the lexicographically smallest permutation, and repeatedly
4623 * calling nextEvenPermutation until it returns false.
4624 ----
4625 // Enumerate even permutations
4626 int[] a = [1,2,3,4,5];
4627 do
4628 {
4629 // use the current permutation and
4630 // proceed to the next even permutation of the array.
4631 } while (nextEvenPermutation(a));
4632 ----
4633 * One can also generate the $(I odd) permutations of a range by noting that
4634 * permutations obey the rule that even + even = even, and odd + even = odd.
4635 * Thus, by swapping the last two elements of a lexicographically least range,
4636 * it is turned into the first odd permutation. Then calling
4637 * nextEvenPermutation on this first odd permutation will generate the next
4638 * even permutation relative to this odd permutation, which is actually the
4639 * next odd permutation of the original range. Thus, by repeatedly calling
4640 * nextEvenPermutation until it returns false, one enumerates the odd
4641 * permutations of the original range.
4642 ----
4643 // Enumerate odd permutations
4644 int[] a = [1,2,3,4,5];
4645 swap(a[$-2], a[$-1]); // a is now the first odd permutation of [1,2,3,4,5]
4646 do
4647 {
4648 // use the current permutation and
4649 // proceed to the next odd permutation of the original array
4650 // (which is an even permutation of the first odd permutation).
4651 } while (nextEvenPermutation(a));
4652 ----
4653 *
4654 * Warning: Since even permutations are only distinct from all permutations
4655 * when the range elements are unique, this function assumes that there are no
4656 * duplicate elements under the specified ordering. If this is not _true, some
4657 * permutations may fail to be generated. When the range has non-unique
4658 * elements, you should use $(MYREF nextPermutation) instead.
4659 *
4660 * Params:
4661 * less = The ordering to be used to determine lexicographical ordering of the
4662 * permutations.
4663 * range = The range to permute.
4664 *
4665 * Returns: false if the range was lexicographically the greatest, in which
4666 * case the range is reversed back to the lexicographically smallest
4667 * permutation; otherwise returns true.
4668 */
4669 bool nextEvenPermutation(alias less="a < b", BidirectionalRange)
4670 (BidirectionalRange range)
4671 if (isBidirectionalRange!BidirectionalRange &&
4672 hasSwappableElements!BidirectionalRange)
4673 {
4674 import std.algorithm.mutation : reverse, swap;
4675 import std.algorithm.searching : find;
4676 import std.range : retro, takeExactly;
4677 // Ranges of 0 or 1 element have no distinct permutations.
4678 if (range.empty) return false;
4679
4680 bool oddParity = false;
4681 bool ret = true;
4682 do
4683 {
4684 auto i = retro(range);
4685 auto last = i.save;
4686
4687 // Find last occurring increasing pair of elements
4688 size_t n = 1;
4689 for (i.popFront(); !i.empty;
4690 i.popFront(), last.popFront(), n++)
4691 {
4692 if (binaryFun!less(i.front, last.front))
4693 break;
4694 }
4695
4696 if (!i.empty)
4697 {
4698 // Find last element greater than i.front.
4699 auto j = find!((a) => binaryFun!less(i.front, a))(
4700 takeExactly(retro(range), n));
4701
4702 // shouldn't happen since i.front < last.front
4703 assert(!j.empty, "j must not be empty");
4704
4705 swap(i.front, j.front);
4706 oddParity = !oddParity;
4707 }
4708 else
4709 {
4710 // Entire range is decreasing: it's lexicographically
4711 // the greatest.
4712 ret = false;
4713 }
4714
4715 reverse(takeExactly(retro(range), n));
4716 if ((n / 2) % 2 == 1)
4717 oddParity = !oddParity;
4718 } while (oddParity);
4719
4720 return ret;
4721 }
4722
4723 ///
4724 @safe unittest
4725 {
4726 // Step through even permutations of a sorted array in lexicographic order
4727 int[] a = [1,2,3];
4728 assert(nextEvenPermutation(a) == true);
4729 assert(a == [2,3,1]);
4730 assert(nextEvenPermutation(a) == true);
4731 assert(a == [3,1,2]);
4732 assert(nextEvenPermutation(a) == false);
4733 assert(a == [1,2,3]);
4734 }
4735
4736 @safe unittest
4737 {
4738 auto a3 = [ 1, 2, 3, 4 ];
4739 int count = 1;
4740 while (nextEvenPermutation(a3)) count++;
4741 assert(count == 12);
4742 }
4743
4744 @safe unittest
4745 {
4746 // Test with non-default sorting order
4747 auto a = [ 3, 2, 1 ];
4748
4749 assert(nextEvenPermutation!"a > b"(a) == true);
4750 assert(a == [ 2, 1, 3 ]);
4751 assert(nextEvenPermutation!"a > b"(a) == true);
4752 assert(a == [ 1, 3, 2 ]);
4753 assert(nextEvenPermutation!"a > b"(a) == false);
4754 assert(a == [ 3, 2, 1 ]);
4755 }
4756
4757 @safe unittest
4758 {
4759 // Test various cases of rollover
4760 auto a = [ 3, 1, 2 ];
4761 assert(nextEvenPermutation(a) == false);
4762 assert(a == [ 1, 2, 3 ]);
4763
4764 auto b = [ 3, 2, 1 ];
4765 assert(nextEvenPermutation(b) == false);
4766 assert(b == [ 1, 3, 2 ]);
4767 }
4768
4769 // https://issues.dlang.org/show_bug.cgi?id=13594
4770 @safe unittest
4771 {
4772 int[3] a = [1,2,3];
4773 assert(nextEvenPermutation(a[]));
4774 assert(a == [2,3,1]);
4775 }
4776
4777 /**
4778 Even permutations are useful for generating coordinates of certain geometric
4779 shapes. Here's a non-trivial example:
4780 */
4781 @safe unittest
4782 {
4783 import std.math.algebraic : sqrt;
4784
4785 // Print the 60 vertices of a uniform truncated icosahedron (soccer ball)
4786 enum real Phi = (1.0 + sqrt(5.0)) / 2.0; // Golden ratio
4787 real[][] seeds = [
4788 [0.0, 1.0, 3.0*Phi],
4789 [1.0, 2.0+Phi, 2.0*Phi],
4790 [Phi, 2.0, Phi^^3]
4791 ];
4792 size_t n;
foreach(seed;seeds)4793 foreach (seed; seeds)
4794 {
4795 // Loop over even permutations of each seed
4796 do
4797 {
4798 // Loop over all sign changes of each permutation
4799 size_t i;
4800 do
4801 {
4802 // Generate all possible sign changes
4803 for (i=0; i < seed.length; i++)
4804 {
4805 if (seed[i] != 0.0)
4806 {
4807 seed[i] = -seed[i];
4808 if (seed[i] < 0.0)
4809 break;
4810 }
4811 }
4812 n++;
4813 } while (i < seed.length);
4814 } while (nextEvenPermutation(seed));
4815 }
4816 assert(n == 60);
4817 }
4818
4819 /**
4820 Permutes `range` into the `perm` permutation.
4821
4822 The algorithm has a constant runtime complexity with respect to the number of
4823 permutations created.
4824 Due to the number of unique values of `ulong` only the first 21 elements of
4825 `range` can be permuted. The rest of the range will therefore not be
4826 permuted.
4827 This algorithm uses the $(HTTP en.wikipedia.org/wiki/Lehmer_code, Lehmer
4828 Code).
4829
4830 The algorithm works as follows:
4831 $(D_CODE
4832 auto pem = [4,0,4,1,0,0,0]; // permutation 2982 in factorial
4833 auto src = [0,1,2,3,4,5,6]; // the range to permutate
4834
4835 auto i = 0; // range index
4836 // range index iterates pem and src in sync
4837 // pem[i] + i is used as index into src
4838 // first src[pem[i] + i] is stored in t
4839 auto t = 4; // tmp value
4840 src = [0,1,2,3,n,5,6];
4841
4842 // then the values between i and pem[i] + i are moved one
4843 // to the right
4844 src = [n,0,1,2,3,5,6];
4845 // at last t is inserted into position i
4846 src = [4,0,1,2,3,5,6];
4847 // finally i is incremented
4848 ++i;
4849
4850 // this process is repeated while i < pem.length
4851
4852 t = 0;
4853 src = [4,n,1,2,3,5,6];
4854 src = [4,0,1,2,3,5,6];
4855 ++i;
4856 t = 6;
4857 src = [4,0,1,2,3,5,n];
4858 src = [4,0,n,1,2,3,5];
4859 src = [4,0,6,1,2,3,5];
4860 )
4861
4862 Returns:
4863 The permuted range.
4864
4865 Params:
4866 range = The Range to permute. The original ordering will be lost.
4867 perm = The permutation to permutate `range` to.
4868 */
4869 auto ref Range nthPermutation(Range)
4870 (auto ref Range range, const ulong perm)
4871 if (isRandomAccessRange!Range && hasLength!Range)
4872 {
4873 if (!nthPermutationImpl(range, perm))
4874 {
4875 throw new Exception(
4876 "The range to permutate must not have less"
4877 ~ " elements than the factorial number has digits");
4878 }
4879
4880 return range;
4881 }
4882
4883 ///
4884 pure @safe unittest
4885 {
4886 auto src = [0, 1, 2, 3, 4, 5, 6];
4887 auto rslt = [4, 0, 6, 2, 1, 3, 5];
4888
4889 src = nthPermutation(src, 2982);
4890 assert(src == rslt);
4891 }
4892
4893 /**
4894 Returns: `true` in case the permutation worked, `false` in case `perm` had
4895 more digits in the factorial number system than range had elements.
4896 This case must not occur as this would lead to out of range accesses.
4897 */
4898 bool nthPermutationImpl(Range)
4899 (auto ref Range range, ulong perm)
4900 if (isRandomAccessRange!Range && hasLength!Range)
4901 {
4902 import std.range.primitives : ElementType;
4903 import std.numeric : decimalToFactorial;
4904
4905 // ulong.max has 21 digits in the factorial number system
4906 ubyte[21] fac;
4907 size_t idx = decimalToFactorial(perm, fac);
4908
4909 if (idx > range.length)
4910 {
4911 return false;
4912 }
4913
4914 ElementType!Range tmp;
4915 size_t i = 0;
4916
4917 for (; i < idx; ++i)
4918 {
4919 size_t re = fac[i];
4920 tmp = range[re + i];
4921 for (size_t j = re + i; j > i; --j)
4922 {
4923 range[j] = range[j - 1];
4924 }
4925 range[i] = tmp;
4926 }
4927
4928 return true;
4929 }
4930
4931 ///
4932 pure @safe unittest
4933 {
4934 auto src = [0, 1, 2, 3, 4, 5, 6];
4935 auto rslt = [4, 0, 6, 2, 1, 3, 5];
4936
4937 bool worked = nthPermutationImpl(src, 2982);
4938 assert(worked);
4939 assert(src == rslt);
4940 }
4941
4942 pure @safe unittest
4943 {
4944 auto rslt = [4, 0, 6, 2, 1, 3, 5];
4945
4946 auto src = nthPermutation([0, 1, 2, 3, 4, 5, 6], 2982);
4947 assert(src == rslt);
4948 }
4949
4950 pure @safe unittest
4951 {
4952 auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
4953 auto rslt = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10];
4954
4955 src = nthPermutation(src, 2982);
4956 assert(src == rslt);
4957 }
4958
4959 pure @safe unittest
4960 {
4961 import std.exception : assertThrown;
4962
4963 auto src = [0, 1, 2, 3];
4964
4965 assertThrown(nthPermutation(src, 2982));
4966 }
4967
4968 pure @safe unittest
4969 {
4970 import std.internal.test.dummyrange;
4971 import std.meta : AliasSeq;
4972
4973 auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
4974 auto rsl = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10];
4975
4976 foreach (T; AliasSeq!(
4977 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, int[]),
4978 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, int[])))
4979 {
4980 static assert(isRandomAccessRange!(T));
4981 static assert(hasLength!(T));
4982 auto dr = T(src.dup);
4983 dr = nthPermutation(dr, 2982);
4984
4985 int idx;
foreach(it;dr)4986 foreach (it; dr)
4987 {
4988 assert(it == rsl[idx++]);
4989 }
4990 }
4991 }
4992