@@ -465,14 +465,108 @@ void medianOf(alias less, Iterator)
465465 }
466466}
467467
468+ /+ +
469+ Returns: `true` if a sorted array contains the value.
470+
471+ Params:
472+ test = strict ordering symmetric predicate
473+
474+ For non-symmetric predicates please use a structure with two `opCall`s or an alias of two global functions,
475+ that correponds to `(array[i], value)` and `(value, array[i])` cases.
468476
477+ See_also: $(LREF transitionIndex).
478+ +/
479+ template assumeSortedContains (alias test = " a < b" )
480+ {
481+ import mir.functional: naryFun;
482+ static if (__traits(isSame, naryFun! test, test))
483+ {
484+ @optmath:
485+ /+ +
486+ Params:
487+ slice = sorted one-dimensional slice or array.
488+ v = value to test with. It is passed to second argument.
489+ +/
490+ bool assumeSortedContains (Iterator, SliceKind kind, V)
491+ (auto ref Slice! (Iterator, 1 , kind) slice, auto ref scope const V v)
492+ {
493+ auto ti = transitionIndex! test(slice, v);
494+ return ti < slice.length && ! test(v, slice[ti]);
495+ }
469496
497+ // / ditto
498+ bool assumeSortedContains (T, V)(scope T[] ar, auto ref scope const V v)
499+ {
500+ return .assumeSortedContains! test(ar.sliced, v);
501+ }
502+ }
503+ else
504+ alias assumeSortedContains = .assumeSortedContains! (naryFun! test);
505+ }
506+
507+ /+ +
508+ Returns: the smallest index of a sorted array such
509+ that the index corresponds to the arrays element at the index according to the predicate
510+ and array length if the array doesn't contain corresponding element.
511+
512+ Params:
513+ test = strict ordering symmetric predicate.
514+
515+ For non-symmetric predicates please use a structure with two `opCall`s or an alias of two global functions,
516+ that correponds to `(array[i], value)` and `(value, array[i])` cases.
517+
518+ See_also: $(LREF transitionIndex).
519+ +/
520+ template assumeSortedEqualIndex (alias test = " a < b" )
521+ {
522+ import mir.functional: naryFun;
523+ static if (__traits(isSame, naryFun! test, test))
524+ {
525+ @optmath:
526+ /+ +
527+ Params:
528+ slice = sorted one-dimensional slice or array.
529+ v = value to test with. It is passed to second argument.
530+ +/
531+ size_t assumeSortedEqualIndex (Iterator, SliceKind kind, V)
532+ (auto ref Slice! (Iterator, 1 , kind) slice, auto ref scope const V v)
533+ {
534+ auto ti = transitionIndex! test(slice, v);
535+ return ti < slice.length && ! test(v, slice[ti]) ? ti : slice.length;
536+ }
537+
538+ // / ditto
539+ size_t assumeSortedEqualIndex (T, V)(scope T[] ar, auto ref scope const V v)
540+ {
541+ return .assumeSortedEqualIndex! test(ar.sliced, v);
542+ }
543+ }
544+ else
545+ alias assumeSortedEqualIndex = .assumeSortedEqualIndex! (naryFun! test);
546+ }
547+
548+ // /
549+ version (mir_test)
550+ @safe pure unittest
551+ {
552+ // sorted: a < b
553+ auto a = [0 , 1 , 2 , 3 , 4 , 6 ];
554+
555+ assert (a.assumeSortedEqualIndex(2 ) == 2 );
556+ assert (a.assumeSortedEqualIndex(5 ) == a.length);
557+
558+ // <= non strict predicates doesn't work
559+ assert (a.assumeSortedEqualIndex! " a <= b" (2 ) == a.length);
560+ }
470561
471562/+ +
472563Computes transition index using binary search.
473564It is low-level API for lower and upper bounds of a sorted array.
474565
475- See_also: $(SUBREF topology, flattened).
566+ Params:
567+ test = ordering predicate for (`(array[i], value)`) pairs.
568+
569+ See_also: $(SUBREF topology, assumeSortedEqualIndex).
476570+/
477571template transitionIndex (alias test = " a < b" )
478572{
@@ -517,6 +611,7 @@ template transitionIndex(alias test = "a < b")
517611}
518612
519613// /
614+ version (mir_test)
520615@safe pure unittest
521616{
522617 // sorted: a < b
@@ -570,6 +665,7 @@ I[] makeIndex(I = size_t, alias less = "a < b", T)(scope T[] r)
570665}
571666
572667// /
668+ version (mir_test)
573669@system unittest
574670{
575671 import mir.algorithm.iteration: all;
0 commit comments