diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d index e4a8c86c773..3d5583733b1 100644 --- a/std/algorithm/comparison.d +++ b/std/algorithm/comparison.d @@ -221,6 +221,7 @@ Params: handlers that accept one argument. There can also be a choice that accepts zero arguments. That choice will be invoked if the $(D switchObject) is null. + switchObject = the object against which the tests are being made. Returns: The value of the selected choice. @@ -500,7 +501,7 @@ Params: Returns: Returns $(D val), if it is between $(D lower) and $(D upper). - Otherwise returns the nearest of the two. + Otherwise returns the nearest of the two. */ auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) @@ -708,14 +709,14 @@ template equal(alias pred = "a == b") /++ This function compares to ranges for equality. The ranges may have different element types, as long as $(D pred(a, b)) evaluates to $(D bool) - for $(D a) in $(D r1) and $(D b) in $(D r2). + for $(D a) in $(D r1) and $(D b) in $(D r2). Performs $(BIGOH min(r1.length, r2.length)) evaluations of $(D pred). Params: - r1 = The first range to be compared. + r1 = The first range to be compared. r2 = The second range to be compared. - Returns: + Returns: $(D true) if and only if the two ranges compare equal element for element, according to binary predicate $(D pred). @@ -799,7 +800,7 @@ range of range (of range...) comparisons. { import std.algorithm.iteration : map; import std.math : approxEqual; - import std.internal.test.dummyrange : ReferenceForwardRange, + import std.internal.test.dummyrange : ReferenceForwardRange, ReferenceInputRange, ReferenceInfiniteForwardRange; debug(std_algorithm) scope(success) diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 9e7f2ce474d..e8ab9e53837 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -193,6 +193,80 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) assert(isSorted(s)); // bidirectional } +/** +Like $(D isSorted), returns $(D true) if the given $(D values) are ordered +according to the comparison operation $(D less). Unlike $(D isSorted), takes values +directly instead of structured in a range. + +$(D ordered) allows repeated values, e.g. $(D ordered(1, 1, 2)) is $(D true). To verify +that the values are ordered strictly monotonically, use $(D strictlyOrdered); +$(D strictlyOrdered(1, 1, 2)) is $(D false). + +With either function, the predicate must be a strict ordering just like with $(D isSorted). For +example, using $(D "a <= b") instead of $(D "a < b") is incorrect and will cause failed +assertions. + +Params: + values = The tested value + less = The comparison predicate + +Returns: + $(D true) if the values are ordered; $(D ordered) allows for duplicates, + $(D strictlyOrdered) does not. +*/ + +bool ordered(alias less = "a < b", T...)(T values) +if ((T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool)) + || + (T.length > 2 && is(typeof(ordered!less(values[0 .. 1 + $ / 2]))) + && is(typeof(ordered!less(values[$ / 2 .. $])))) + ) +{ + foreach (i, _; T[0 .. $ - 1]) + { + if (binaryFun!less(values[i + 1], values[i])) + { + assert(!binaryFun!less(values[i], values[i + 1]), + __FUNCTION__ ~ ": incorrect non-strict predicate."); + return false; + } + } + return true; +} + +/// ditto +bool strictlyOrdered(alias less = "a < b", T...)(T values) +if (is(typeof(ordered!less(values)))) +{ + foreach (i, _; T[0 .. $ - 1]) + { + if (!binaryFun!less(values[i], values[i + 1])) + { + return false; + } + assert(!binaryFun!less(values[i + 1], values[i]), + __FUNCTION__ ~ ": incorrect non-strict predicate."); + } + return true; +} + +/// +unittest +{ + assert(ordered(42, 42, 43)); + assert(!strictlyOrdered(43, 42, 45)); + assert(ordered(42, 42, 43)); + assert(!strictlyOrdered(42, 42, 43)); + assert(!ordered(43, 42, 45)); + // Ordered lexicographically + assert(ordered("Jane", "Jim", "Joe")); + assert(strictlyOrdered("Jane", "Jim", "Joe")); + // Incidentally also ordered by length decreasing + assert(ordered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); + // ... but not strictly so: "Jim" and "Joe" have the same length + assert(!strictlyOrdered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); +} + // partition /** Partitions a range in two using $(D pred) as a @@ -1229,8 +1303,8 @@ private template TimSortImpl(alias pred, R) immutable run3 = stackLen - 2; immutable run2 = stackLen - 3; immutable run1 = stackLen - 4; - - if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) || + + if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) || (stackLen > 3 && stack[run1].length <= stack[run3].length + stack[run2].length) ) { immutable at = stack[run2].length < stack[run4].length ? run2 : run3; @@ -1238,10 +1312,10 @@ private template TimSortImpl(alias pred, R) } else if (stack[run3].length > stack[run4].length) break; else mergeAt(range, stack[0 .. stackLen], run3, minGallop, temp); - + stackLen -= 1; } - + // Assert that the code above established the invariant correctly version (assert) {