Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 91 additions & 10 deletions std/algorithm/searching.d
Original file line number Diff line number Diff line change
Expand Up @@ -2781,19 +2781,26 @@ Range minPos(alias pred = "a < b", Range)(Range range)
if (isForwardRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
{
if (range.empty) return range;
auto result = range.save;

for (range.popFront(); !range.empty; range.popFront())
static if (isSortedRange!(Range, pred))
{
return range;
}
else
{
//Note: Unlike minCount, we do not care to find equivalence, so a single pred call is enough
if (binaryFun!pred(range.front, result.front))
if (range.empty) return range;
auto result = range.save;

for (range.popFront(); !range.empty; range.popFront())
{
// change the min
result = range.save;
//Note: Unlike minCount, we do not care to find equivalence, so a single pred call is enough
if (binaryFun!pred(range.front, result.front))
{
// change the min
result = range.save;
}
}
return result;
}
return result;
}

///
Expand All @@ -2804,6 +2811,11 @@ Range minPos(alias pred = "a < b", Range)(Range range)
assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]);
// Maximum is 4 and first occurs in position 2
assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]);

// Test SortedRange as input
import std.algorithm.sorting : sort;
import std.algorithm : equal;
assert(equal(minPos(a.sort()), [ 1, 1, 1, 2, 2, 2, 3, 4, 4 ]));
}

@safe unittest
Expand Down Expand Up @@ -2852,6 +2864,76 @@ unittest
assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]);
}

import std.algorithm.comparison : min, max;

/** Returns: Minimum Element in $(D range) or $(D unit) if $(D range) is empty.
*/
auto minElement(alias F = min, R)(R range,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need these? "Compliance with C++ STL" doesn't seem like a good argument. I recommend moving these to a different PR where they can be evaluated separately.

ElementType!R unit = ElementType!R.max)
if (isInputRange!R)
{
import std.range.primitives : isSortedRange;
static if (isSortedRange!(R, "a < b"))
{
import std.range.primitives : front;
return range.empty ? unit : range.front;
}
else static if (isSortedRange!(R, "a > b") &&
isBidirectionalRange!R)
{
import std.range.primitives : back;
return range.empty ? unit : range.back;
}
else
{
import std.algorithm.iteration : reduce;
return reduce!F(unit, range);
}
}

@safe pure nothrow unittest
{
import std.algorithm.sorting : sort, assumeSorted;
auto x = [2, 4, 1, 3];
assert(x.minElement == 1);
assert(x.sort!"a < b".minElement == 1);
assert(x.sort!"a > b".minElement == 1);
}

/** Returns: Maximum Element in $(D range) or $(D unit) if $(D range) is empty.
*/
auto maxElement(alias F = max, R)(R range,
ElementType!R unit = ElementType!R.min)
if (isInputRange!R)
{
import std.range.primitives : isSortedRange;
static if (isSortedRange!(R, "a > b"))
{
import std.range.primitives : front;
return range.empty ? unit : range.front;
}
else static if (isSortedRange!(R, "a < b") &&
isBidirectionalRange!R)
{
import std.range.primitives : back;
return range.empty ? unit : range.back;
}
else
{
import std.algorithm.iteration : reduce;
return reduce!F(unit, range);
}
}

@safe pure nothrow unittest
{
import std.algorithm.sorting : sort, assumeSorted;
auto x = [2, 4, 1, 3];
assert(x.maxElement == 4);
assert(x.sort!"a < b".maxElement == 4);
assert(x.sort!"a > b".maxElement == 4);
}

/**
Skip over the initial portion of the first given range that matches the second
range, or do nothing if there is no match.
Expand Down Expand Up @@ -3494,4 +3576,3 @@ unittest // Issue 13124
auto s = "hello how\nare you";
s.until!(c => c.among!('\n', '\r'));
}

97 changes: 55 additions & 42 deletions std/algorithm/sorting.d
Original file line number Diff line number Diff line change
Expand Up @@ -131,39 +131,45 @@ less).
*/
bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range))
{
if (r.empty) return true;

static if (isRandomAccessRange!Range && hasLength!Range)
static if (isSortedRange!(Range, less))
{
immutable limit = r.length - 1;
foreach (i; 0 .. limit)
{
if (!binaryFun!less(r[i + 1], r[i])) continue;
assert(
!binaryFun!less(r[i], r[i + 1]),
"Predicate for isSorted is not antisymmetric. Both" ~
" pred(a, b) and pred(b, a) are true for certain values.");
return false;
}
return true;
}
else
{
auto ahead = r;
ahead.popFront();
size_t i;

for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i)
if (r.empty) return true;
static if (isRandomAccessRange!Range && hasLength!Range)
{
if (!binaryFun!less(ahead.front, r.front)) continue;
// Check for antisymmetric predicate
assert(
!binaryFun!less(r.front, ahead.front),
"Predicate for isSorted is not antisymmetric. Both" ~
" pred(a, b) and pred(b, a) are true for certain values.");
return false;
immutable limit = r.length - 1;
foreach (i; 0 .. limit)
{
if (!binaryFun!less(r[i + 1], r[i])) continue;
assert(
!binaryFun!less(r[i], r[i + 1]),
"Predicate for isSorted is not antisymmetric. Both" ~
" pred(a, b) and pred(b, a) are true for certain values.");
return false;
}
}
else
{
auto ahead = r;
ahead.popFront();
size_t i;

for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i)
{
if (!binaryFun!less(ahead.front, r.front)) continue;
// Check for antisymmetric predicate
assert(
!binaryFun!less(r.front, ahead.front),
"Predicate for isSorted is not antisymmetric. Both" ~
" pred(a, b) and pred(b, a) are true for certain values.");
return false;
}
}
return true;
}
return true;
}

///
Expand All @@ -175,6 +181,7 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range))
assert(isSorted(arr));
sort!("a > b")(arr);
assert(isSorted!("a > b")(arr));
assert(isSorted(sort(arr)));
}

@safe unittest
Expand Down Expand Up @@ -954,7 +961,7 @@ See_Also:
*/
SortedRange!(Range, less)
sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
Range)(Range r)
Range)(Range r)
if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
hasAssignableElements!Range)) ||
(ss != SwapStrategy.unstable && hasAssignableElements!Range)) &&
Expand All @@ -966,25 +973,32 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
swaps using assignment.
Stable sorting uses TimSort, which needs to copy elements into a buffer,
requiring assignable elements. +/
{
import std.range : assumeSorted;
alias lessFun = binaryFun!(less);
alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun
static if (is(LessRet == bool))
{
static if (ss == SwapStrategy.unstable)
quickSortImpl!(lessFun)(r, r.length);
else //use Tim Sort for semistable & stable
TimSortImpl!(lessFun, Range).sort(r, null);

enum maxLen = 8;
assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof);
static if (isSortedRange!(Range, less))
{
return r; // already sorted, so just return $(D r) as is
}
else
{
static assert(false, "Invalid predicate passed to sort: " ~ less.stringof);
import std.range : assumeSorted;
alias lessFun = binaryFun!(less);
alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun
static if (is(LessRet == bool))
{
static if (ss == SwapStrategy.unstable)
quickSortImpl!(lessFun)(r, r.length);
else //use Tim Sort for semistable & stable
TimSortImpl!(lessFun, Range).sort(r, null);

enum maxLen = 8;
assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof);
}
else
{
static assert(false, "Invalid predicate passed to sort: " ~ less.stringof);
}
return assumeSorted!less(r);
}
return assumeSorted!less(r);
}

///
Expand Down Expand Up @@ -2859,4 +2873,3 @@ shapes. Here's a non-trivial example:
}
assert(n == 60);
}

Loading