mirror of
https://github.com/dlang/phobos.git
synced 2025-05-06 19:16:13 +03:00
Fix Issue 15230 - Inconsistent std.range.SortedRange predicate checks
This commit is contained in:
parent
4b125d87c0
commit
58c3a75738
2 changed files with 98 additions and 49 deletions
|
@ -1954,7 +1954,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
|
||||||
|
|
||||||
// This should get smaller with time. On occasion it may go larger, but only
|
// This should get smaller with time. On occasion it may go larger, but only
|
||||||
// if there's thorough justification.
|
// if there's thorough justification.
|
||||||
debug enum uint watermark = 1676280;
|
debug enum uint watermark = 1676220;
|
||||||
else enum uint watermark = 1676220;
|
else enum uint watermark = 1676220;
|
||||||
|
|
||||||
import std.conv;
|
import std.conv;
|
||||||
|
|
|
@ -10564,16 +10564,56 @@ enum SearchPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Represents a sorted range. In addition to the regular range
|
Options for $(LREF SortedRange) ranges (below).
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
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))
|
if (isInputRange!Range && !isInstanceOf!(SortedRange, Range))
|
||||||
{
|
{
|
||||||
import std.functional : binaryFun;
|
import std.functional : binaryFun;
|
||||||
|
@ -10592,36 +10632,53 @@ if (isInputRange!Range && !isInstanceOf!(SortedRange, Range))
|
||||||
// Undocummented because a clearer way to invoke is by calling
|
// Undocummented because a clearer way to invoke is by calling
|
||||||
// assumeSorted.
|
// assumeSorted.
|
||||||
this(Range input)
|
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;
|
this._input = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assertion only.
|
// Assertion only.
|
||||||
private void dbgVerifySorted()
|
private void roughlyVerifySorted(Range r)
|
||||||
{
|
{
|
||||||
if (!__ctfe)
|
if (!__ctfe)
|
||||||
debug
|
|
||||||
{
|
{
|
||||||
static if (isRandomAccessRange!Range && hasLength!Range)
|
static if (isRandomAccessRange!Range && hasLength!Range)
|
||||||
{
|
{
|
||||||
import core.bitop : bsr;
|
import core.bitop : bsr;
|
||||||
import std.algorithm.sorting : isSorted;
|
import std.algorithm.sorting : isSorted;
|
||||||
|
import std.exception : enforce;
|
||||||
|
|
||||||
// Check the sortedness of the input
|
// Check the sortedness of the input
|
||||||
if (this._input.length < 2) return;
|
if (r.length < 2) return;
|
||||||
|
|
||||||
immutable size_t msb = bsr(this._input.length) + 1;
|
immutable size_t msb = bsr(r.length) + 1;
|
||||||
assert(msb > 0 && msb <= this._input.length);
|
assert(msb > 0 && msb <= r.length);
|
||||||
immutable step = this._input.length / msb;
|
immutable step = r.length / msb;
|
||||||
auto st = stride(this._input, step);
|
auto st = stride(r, step);
|
||||||
|
|
||||||
assert(isSorted!pred(st), "Range is not sorted");
|
enforce(isSorted!pred(st), "Range is not sorted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assertion only.
|
||||||
|
private void strictlyVerifySorted(Range r)
|
||||||
|
{
|
||||||
|
if (!__ctfe)
|
||||||
|
{
|
||||||
|
static if (isRandomAccessRange!Range && hasLength!Range)
|
||||||
|
{
|
||||||
|
import std.algorithm.sorting : isSorted;
|
||||||
|
import std.exception : enforce;
|
||||||
|
|
||||||
|
enforce(isSorted!pred(r), "Range is not sorted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11025,11 +11082,12 @@ sorting relation.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
template SortedRange(Range, alias pred = "a < b")
|
template SortedRange(Range, alias pred = "a < b",
|
||||||
|
SortedRangeOptions opt = SortedRangeOptions.assumeSorted)
|
||||||
if (isInstanceOf!(SortedRange, Range))
|
if (isInstanceOf!(SortedRange, Range))
|
||||||
{
|
{
|
||||||
// Avoid nesting SortedRange types (see Issue 18933);
|
// 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
|
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
|
@safe unittest
|
||||||
{
|
{
|
||||||
import std.algorithm.comparison : equal;
|
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
|
Assumes `r` is sorted by predicate `pred` and returns the
|
||||||
corresponding $(D SortedRange!(pred, R)) having `r` as support. To
|
corresponding $(D SortedRange!(pred, R)) having `r` as support.
|
||||||
keep the checking costs low, the cost is $(BIGOH 1) in release mode
|
To check for sorted-ness at
|
||||||
(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
|
|
||||||
cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting).
|
cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting).
|
||||||
*/
|
*/
|
||||||
auto assumeSorted(alias pred = "a < b", R)(R r)
|
auto assumeSorted(alias pred = "a < b", R)(R r)
|
||||||
|
@ -11299,20 +11362,6 @@ if (isInputRange!(Unqual!R))
|
||||||
r = assumeSorted(a);
|
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
|
// issue 15003
|
||||||
@nogc @safe unittest
|
@nogc @safe unittest
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue