Skip to content
Merged
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
131 changes: 129 additions & 2 deletions std/algorithm/searching.d
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ $(T2 commonPrefix,
`commonPrefix("parakeet", "parachute")` returns `"para"`.)
$(T2 endsWith,
`endsWith("rocks", "ks")` returns `true`.)
$(T2 extrema, `extrema([2, 1, 3, 5, 4])` returns `[1, 5]`.)
$(T2 find,
`find("hello world", "or")` returns `"orld"` using linear search.
(For binary search refer to $(REF SortedRange, std,range).))
Expand Down Expand Up @@ -3684,7 +3685,7 @@ Note:

See_Also:

$(LREF maxElement), $(REF min, std,algorithm,comparison), $(LREF minCount),
$(LREF extrema), $(LREF maxElement), $(REF min, std,algorithm,comparison), $(LREF minCount),
$(LREF minIndex), $(LREF minPos)
*/
auto minElement(alias map = (a => a), Range)(Range r)
Expand Down Expand Up @@ -3865,7 +3866,7 @@ Note:

See_Also:

$(LREF minElement), $(REF max, std,algorithm,comparison), $(LREF maxCount),
$(LREF extrema), $(LREF minElement), $(REF max, std,algorithm,comparison), $(LREF maxCount),
$(LREF maxIndex), $(LREF maxPos)
*/
auto maxElement(alias map = (a => a), Range)(Range r)
Expand Down Expand Up @@ -4035,6 +4036,132 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(maxElement(arr) == S(145));
}

/** Returns an array of the minimum and maximum element in `r`.
Copy link
Member

Choose a reason for hiding this comment

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

I think this function should rather return a named tuple:

Tuple!(ElementType!Range, "min", ElementType!Range "max")

Copy link
Contributor

Choose a reason for hiding this comment

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

PR please!

* Performs `< 3n/2` comparisons, unlike the naive `< 2n`.
* Params:
* r = The range to traverse.
*/
// TODO alias map = a => a
ElementType!Range[2] extrema(Range)(Range r)
Copy link
Contributor

Choose a reason for hiding this comment

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

The explicit ElementType!Range makes me wonder what happens when the elements are const, immutable, or shared.

if (isInputRange!Range && !isInfinite!Range)
in (!r.empty)
{
static if (isRandomAccessRange!Range && hasLength!Range)
{
if (r.length == 1)
return [r[0], r[0]];

typeof(return) result;
size_t i;
if (r.length & 1) // odd
{
result = [r[0], r[0]];
i = 1;
}
else
{
result = (r[0] < r[1]) ? [r[0], r[1]] : [r[1], r[0]];
i = 2;
}
// iterate pairs
const imax = r.length;
for (; i != imax; i += 2)
{
// save work
if (r[i] < r[i+1])
{
if (r[i] < result[0])
result[0] = r[i];
if (r[i+1] > result[1])
result[1] = r[i+1];
}
else
{
if (r[i+1] < result[0])
result[0] = r[i+1];
if (r[i] > result[1])
result[1] = r[i];
}
}
return result;
}
else
{
auto first = r.front;
r.popFront;
if (r.empty)
return [first, first];

typeof(return) result = (first < r.front) ? [first, r.front] : [r.front, first];
// iterate pairs
while (true)
{
r.popFront;
if (r.empty)
return result;
first = r.front;
r.popFront;
if (r.empty)
{
if (first < result[0])
result[0] = first;
else if (first > result[1])
result[1] = first;
return result;
}
// save work
if (first < r.front)
{
if (first < result[0])
result[0] = first;
if (r.front > result[1])
result[1] = r.front;
}
else
{
if (r.front < result[0])
result[0] = r.front;
if (first > result[1])
result[1] = first;
}
}
}
}

///
@safe unittest
{
assert(extrema([5,2,9,4,1]) == [1, 9]);
}

@safe unittest
{
assert(extrema([8,3,7,4,9]) == [3, 9]);
assert(extrema([1,5,3,2]) == [1, 5]);
assert(extrema([2,3,3,2]) == [2, 3]);

import std.range;
assert(iota(2, 5).extrema == [2, 4]);
assert(iota(3, 7).retro.extrema == [3, 6]);

import std.internal.test.dummyrange;
foreach (DummyType; AllDummyRanges)
{
DummyType d;
assert(d.extrema == [1, 10]);
}

version (StdRandomTests)
foreach (i; 0 .. 1000)
{
import std.random;
auto arr = generate!(() => uniform(0, 100)).takeExactly(uniform(1, 10)).array;
auto result = arr.extrema;
assert(result[0] == arr.minElement);
assert(result[1] == arr.maxElement);
}
}

// minPos
/**
Computes a subrange of `range` starting at the first occurrence of `range`'s
Expand Down
Loading