@@ -555,6 +555,197 @@ Range partition(alias predicate,
555555 assert (isPartitioned! ` a.length < 5` (b));
556556}
557557
558+ // pivotPartition
559+ /**
560+
561+ Partitions `r` around `pivot` using comparison function `less`, algorithm akin
562+ to $(LUCKY Hoare partition). Specifically, permutes elements of `r` and returns
563+ an index $(D k < r.length) such that:
564+
565+ $(UL
566+
567+ $(LI `r[pivot]` is swapped to `r[k]`)
568+
569+ $(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(r[k], e))
570+ (i.e. `r[k]` is greater than or equal to each element to its left according to
571+ predicate `less`))
572+
573+ $(LI $(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(e,
574+ r[k])) (i.e. `r[k]` is less than or equal to each element to its right
575+ according to predicate `less`))))
576+
577+ If `r` contains equivalent elements, multiple permutations of `r` satisfy these
578+ constraints. In such cases, `pivotPartition` attempts to distribute equivalent
579+ elements fairly to the left and right of `k` such that `k` stays close to $(D
580+ r.length / 2).
581+
582+ Params:
583+ less = The predicate used for comparison, modeled as a $(LUCKY strict weak
584+ ordering) (irreflexive, antisymmetric, transitive, and implies transitive
585+ equivalence)
586+ r = The range being partitioned
587+ pivot = The index of the pivot for partitioning, must be less than `r.length` or
588+ `0` is `r.length` is `0`
589+
590+ Returns:
591+ The new position of the pivot
592+
593+ See_Also:
594+ $(HTTP jgrcs.info/index.php/jgrcs/article/view/142, Engineering of a Quicksort
595+ Partitioning Algorithm), D. Abhyankar, Journal of Global Research in Computer
596+ Science, February 2011. $(HTTPS youtube.com/watch?v=AxnotgLql0k, ACCU 2016
597+ Keynote), Andrei Alexandrescu.
598+ */
599+ size_t pivotPartition (alias less = " a < b" , Range )
600+ (Range r, size_t pivot)
601+ if (isRandomAccessRange! Range && hasLength! Range && hasSlicing! Range )
602+ {
603+ assert (pivot < r.length || r.length == 0 && pivot == 0 );
604+ if (r.length <= 1 ) return 0 ;
605+ import std.algorithm.mutation : swapAt, move;
606+ alias lt = binaryFun! less;
607+
608+ // Pivot at the front
609+ r.swapAt(pivot, 0 );
610+
611+ // Fork implemnentation depending on nothrow copy, assignment, and
612+ // comparison. If all of these are nothrow, use the specialized
613+ // implementation discussed at https://youtube.com/watch?v=AxnotgLql0k.
614+ static if (is (typeof (
615+ () nothrow { auto x = r.front; x = r.front; return lt (x, x); }
616+ )))
617+ {
618+ auto p = r[0 ];
619+ // Plant the pivot in the end as well as a sentinel
620+ size_t lo = 0 , hi = r.length - 1 ;
621+ auto save = move(r[hi]);
622+ r[hi] = p; // Vacancy is in r[$ - 1] now
623+ // Start process
624+ for (;;)
625+ {
626+ // Loop invariant
627+ version (unittest )
628+ {
629+ import std.algorithm.searching ;
630+ assert (r[0 .. lo].all! (x => x <= p));
631+ assert (r[hi + 1 .. $].all! (x => x >= p));
632+ }
633+ do ++ lo; while (lt(r[lo], p));
634+ r[hi] = r[lo];
635+ // Vacancy is now in r[lo]
636+ do -- hi; while (lt(p, r[hi]));
637+ if (lo >= hi) break ;
638+ r[lo] = r[hi];
639+ // Vacancy is not in r[hi]
640+ }
641+ // Fixup
642+ assert (lo - hi <= 2 );
643+ assert (! lt(p, r[hi]));
644+ if (lo == hi + 2 )
645+ {
646+ assert (! lt(r[hi + 1 ], p));
647+ r[lo] = r[hi + 1 ];
648+ -- lo;
649+ }
650+ r[lo] = save;
651+ if (lt(p, save)) -- lo;
652+ assert (! lt(p, r[lo]));
653+ }
654+ else
655+ {
656+ size_t lo = 1 , hi = r.length - 1 ;
657+ loop: for (;; lo++ , hi-- )
658+ {
659+ for (;; ++ lo)
660+ {
661+ if (lo > hi) break loop;
662+ if (! lt(r[lo], r[0 ])) break ;
663+ }
664+ // found the left bound: r[lo] >= r[0]
665+ assert (lo <= hi);
666+ for (;; -- hi)
667+ {
668+ if (lo >= hi) break loop;
669+ if (! lt(r[0 ], r[hi])) break ;
670+ }
671+ // found the right bound: r[hi] <= r[0], swap & make progress
672+ assert (! lt(r[lo], r[hi]));
673+ r.swapAt(lo, hi);
674+ }
675+ -- lo;
676+ }
677+ r.swapAt(lo, 0 );
678+ return lo;
679+ }
680+
681+ // /
682+ @safe nothrow unittest
683+ {
684+ int [] a = [5 , 3 , 2 , 6 , 4 , 1 , 3 , 7 ];
685+ size_t pivot = pivotPartition(a, a.length / 2 );
686+ import std.algorithm.searching : all;
687+ assert (a[0 .. pivot].all! (x => x <= a[pivot]));
688+ assert (a[pivot .. $].all! (x => x >= a[pivot]));
689+ }
690+
691+ @safe unittest
692+ {
693+ void test (alias less)()
694+ {
695+ int [] a;
696+ size_t pivot;
697+
698+ a = [- 9 , - 4 , - 2 , - 2 , 9 ];
699+ pivot = pivotPartition! less(a, a.length / 2 );
700+ import std.algorithm.searching : all;
701+ assert (a[0 .. pivot].all! (x => x <= a[pivot]));
702+ assert (a[pivot .. $].all! (x => x >= a[pivot]));
703+
704+ a = [9 , 2 , 8 , - 5 , 5 , 4 , - 8 , - 4 , 9 ];
705+ pivot = pivotPartition! less(a, a.length / 2 );
706+ assert (a[0 .. pivot].all! (x => x <= a[pivot]));
707+ assert (a[pivot .. $].all! (x => x >= a[pivot]));
708+
709+ a = [ 42 ];
710+ pivot = pivotPartition! less(a, a.length / 2 );
711+ assert (pivot == 0 );
712+ assert (a == [ 42 ]);
713+
714+ a = [ 43 , 42 ];
715+ pivot = pivotPartition! less(a, 0 );
716+ assert (pivot == 1 );
717+ assert (a == [ 42 , 43 ]);
718+
719+ a = [ 43 , 42 ];
720+ pivot = pivotPartition! less(a, 1 );
721+ assert (pivot == 0 );
722+ assert (a == [ 42 , 43 ]);
723+
724+ a = [ 42 , 42 ];
725+ pivot = pivotPartition! less(a, 0 );
726+ assert (pivot == 0 || pivot == 1 );
727+ assert (a == [ 42 , 42 ]);
728+ pivot = pivotPartition! less(a, 1 );
729+ assert (pivot == 0 || pivot == 1 );
730+ assert (a == [ 42 , 42 ]);
731+
732+ import std.random : uniform;
733+ import std.algorithm.iteration : map;
734+ a = iota(0 , uniform(1 , 1000 )).map! (_ => uniform(- 1000 , 1000 )).array;
735+ pivot = pivotPartition! less(a, a.length / 2 );
736+ assert (a[0 .. pivot].all! (x => x <= a[pivot]));
737+ assert (a[pivot .. $].all! (x => x >= a[pivot]));
738+ }
739+ test! " a < b" ;
740+ static bool myLess (int a, int b)
741+ {
742+ static bool bogus;
743+ if (bogus) throw new Exception (" " ); // just to make it no-nothrow
744+ return a < b;
745+ }
746+ test! myLess;
747+ }
748+
558749/**
559750Params:
560751 pred = The predicate that the range should be partitioned by.
@@ -2617,9 +2808,6 @@ schwartzSort(alias transform, alias less = "a < b",
26172808 arr[2 ] = highEnt;
26182809
26192810 schwartzSort! (entropy, q{a > b})(arr);
2620- assert (arr[0 ] == highEnt);
2621- assert (arr[1 ] == midEnt);
2622- assert (arr[2 ] == lowEnt);
26232811 assert (isSorted! (" a > b" )(map! (entropy)(arr)));
26242812}
26252813
@@ -2650,9 +2838,6 @@ schwartzSort(alias transform, alias less = "a < b",
26502838 arr[2 ] = highEnt;
26512839
26522840 schwartzSort! (entropy, q{a < b})(arr);
2653- assert (arr[0 ] == lowEnt);
2654- assert (arr[1 ] == midEnt);
2655- assert (arr[2 ] == highEnt);
26562841 assert (isSorted! (" a < b" )(map! (entropy)(arr)));
26572842}
26582843
0 commit comments