diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index c971b60bb74..d041c5917e2 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -1954,7 +1954,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || // This should get smaller with time. On occasion it may go larger, but only // if there's thorough justification. - debug enum uint watermark = 1676280; + debug enum uint watermark = 1676220; else enum uint watermark = 1676220; import std.conv; diff --git a/std/range/package.d b/std/range/package.d index 18cea01cf5e..96c4d2e5a54 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -10564,16 +10564,56 @@ enum SearchPolicy } /** -Represents a sorted range. In addition to the regular range -primitives, supports additional operations that take advantage of the -ordering, such as merge and binary search. To obtain a $(D -SortedRange) from an unsorted range `r`, use -$(REF sort, std,algorithm,sorting) which sorts `r` in place and returns the -corresponding `SortedRange`. To construct a `SortedRange` from a range -`r` that is known to be already sorted, use $(LREF assumeSorted) described -below. + Options for $(LREF SortedRange) ranges (below). */ -struct SortedRange(Range, alias pred = "a < b") +enum SortedRangeOptions +{ + /** + Assume, that the range is sorted without checking. + */ + assumeSorted, + + /** + All elements of the range are checked to be sorted. + The check is performed in O(n) time. + */ + checkStrictly, + + /** + Some elements of the range are checked to be sorted. + For ranges with random order, this will almost surely + detect, that it is not sorted. For almost sorted ranges + it's more likely to fail. The checked elements are choosen + in a deterministic manner, which makes this check reproducable. + The check is performed in O(log(n)) time. + */ + checkRoughly, +} + +/// +@safe pure unittest +{ + // create a SortedRange, that's checked strictly + SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]); +} + +/** + Represents a sorted range. In addition to the regular range + primitives, supports additional operations that take advantage of the + ordering, such as merge and binary search. To obtain a $(D + SortedRange) from an unsorted range `r`, use + $(REF sort, std,algorithm,sorting) which sorts `r` in place and returns the + corresponding `SortedRange`. To construct a `SortedRange` from a range + `r` that is known to be already sorted, use $(LREF assumeSorted). + + Params: + pred: The predicate used to define the sortedness + opt: Controls how strongly the range is checked for sortedness. + Will only be used for `RandomAccessRanges`. + Will not be used in CTFE. +*/ +struct SortedRange(Range, alias pred = "a < b", + SortedRangeOptions opt = SortedRangeOptions.assumeSorted) if (isInputRange!Range && !isInstanceOf!(SortedRange, Range)) { import std.functional : binaryFun; @@ -10592,36 +10632,53 @@ if (isInputRange!Range && !isInstanceOf!(SortedRange, Range)) // Undocummented because a clearer way to invoke is by calling // assumeSorted. this(Range input) - out - { - // moved out of the body as a workaround for Issue 12661 - dbgVerifySorted(); - } - do { + static if (opt == SortedRangeOptions.checkRoughly) + { + roughlyVerifySorted(input); + } + static if (opt == SortedRangeOptions.checkStrictly) + { + strictlyVerifySorted(input); + } this._input = input; } // Assertion only. - private void dbgVerifySorted() + private void roughlyVerifySorted(Range r) { if (!__ctfe) - debug { static if (isRandomAccessRange!Range && hasLength!Range) { import core.bitop : bsr; import std.algorithm.sorting : isSorted; + import std.exception : enforce; // Check the sortedness of the input - if (this._input.length < 2) return; + if (r.length < 2) return; + + immutable size_t msb = bsr(r.length) + 1; + assert(msb > 0 && msb <= r.length); + immutable step = r.length / msb; + auto st = stride(r, step); + + enforce(isSorted!pred(st), "Range is not sorted"); + } + } + } - immutable size_t msb = bsr(this._input.length) + 1; - assert(msb > 0 && msb <= this._input.length); - immutable step = this._input.length / msb; - auto st = stride(this._input, step); + // Assertion only. + private void strictlyVerifySorted(Range r) + { + if (!__ctfe) + { + static if (isRandomAccessRange!Range && hasLength!Range) + { + import std.algorithm.sorting : isSorted; + import std.exception : enforce; - assert(isSorted!pred(st), "Range is not sorted"); + enforce(isSorted!pred(r), "Range is not sorted"); } } } @@ -11025,11 +11082,12 @@ sorting relation. } /// ditto -template SortedRange(Range, alias pred = "a < b") +template SortedRange(Range, alias pred = "a < b", + SortedRangeOptions opt = SortedRangeOptions.assumeSorted) if (isInstanceOf!(SortedRange, Range)) { // Avoid nesting SortedRange types (see Issue 18933); - alias SortedRange = SortedRange!(Unqual!(typeof(Range._input)), pred); + alias SortedRange = SortedRange!(Unqual!(typeof(Range._input)), pred, opt); } /// @@ -11065,6 +11123,18 @@ that break its sorted-ness, `SortedRange` will work erratically. assert(!r.contains(42)); // passes although it shouldn't } +@safe unittest +{ + import std.exception : assertThrown, assertNotThrown; + + assertNotThrown(SortedRange!(int[])([ 1, 3, 10, 5, 7 ])); + assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 10, 5, 7 ])); + + // these two checks are implementation depended + assertNotThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 12, 2 ])); + assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 2, 12 ])); +} + @safe unittest { import std.algorithm.comparison : equal; @@ -11199,15 +11269,8 @@ that break its sorted-ness, `SortedRange` will work erratically. /** Assumes `r` is sorted by predicate `pred` and returns the -corresponding $(D SortedRange!(pred, R)) having `r` as support. To -keep the checking costs low, the cost is $(BIGOH 1) in release mode -(no checks for sorted-ness are performed). In debug mode, a few random -elements of `r` are checked for sorted-ness. The size of the sample -is proportional $(BIGOH log(r.length)). That way, checking has no -effect on the complexity of subsequent operations specific to sorted -ranges (such as binary search). The probability of an arbitrary -unsorted range failing the test is very high (however, an -almost-sorted range is likely to pass it). To check for sorted-ness at +corresponding $(D SortedRange!(pred, R)) having `r` as support. +To check for sorted-ness at cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting). */ auto assumeSorted(alias pred = "a < b", R)(R r) @@ -11299,20 +11362,6 @@ if (isInputRange!(Unqual!R)) r = assumeSorted(a); } -@system unittest -{ - bool ok = true; - try - { - auto r2 = assumeSorted([ 677, 345, 34, 7, 5 ]); - debug ok = false; - } - catch (Throwable) - { - } - assert(ok); -} - // issue 15003 @nogc @safe unittest {