@@ -534,3 +534,52 @@ template transitionIndex(alias test = "a < b")
534534 assert (j == 3 );
535535 auto upperBound = a[j .. $];
536536}
537+
538+ /+ +
539+ Computes an index for `r` based on the comparison `less`. The
540+ index is a sorted array of indices into the original
541+ range. This technique is similar to sorting, but it is more flexible
542+ because (1) it allows "sorting" of immutable collections, (2) allows
543+ binary search even if the original collection does not offer random
544+ access, (3) allows multiple indexes, each on a different predicate,
545+ and (4) may be faster when dealing with large objects. However, using
546+ an index may also be slower under certain circumstances due to the
547+ extra indirection, and is always larger than a sorting-based solution
548+ because it needs space for the index in addition to the original
549+ collection. The complexity is the same as `sort`'s.
550+ Params:
551+ less = The comparison to use.
552+ ss = The swapping strategy.
553+ r = The slice/array to index.
554+ index = The resulting index.
555+ Returns: Index array.
556+ +/
557+ Slice! (I* ) makeIndex (I = size_t , alias less = " a < b" , Iterator, SliceKind kind)(scope Slice! (Iterator, 1 , kind) r)
558+ {
559+ import mir.functional: naryFun;
560+ import mir.ndslice.allocation: slice;
561+ import mir.ndslice.topology: iota;
562+ return r
563+ .length
564+ .iota! I
565+ .slice
566+ .sort! ((a, b) => naryFun! less(r[a], r[b]));
567+ }
568+
569+ // /
570+ I[] makeIndex (I = size_t , alias less = " a < b" , T)(scope T[] r)
571+ {
572+ return .makeIndex! (I, less)(r.sliced).field;
573+ }
574+
575+ // /
576+ @system unittest
577+ {
578+ import mir.algorithm.iteration: all;
579+ import mir.ndslice.topology: indexed, pairwise;
580+
581+ immutable arr = [ 2 , 3 , 1 , 5 , 0 ];
582+ auto index = arr.makeIndex;
583+
584+ assert (arr.indexed(index).pairwise! " a < b" .all);
585+ }
0 commit comments