Skip to content

Commit 955c6e3

Browse files
authored
Merge pull request #8727 from ntrel/extrema
[std.algorithm.searching] Add extrema to compute min and max Signed-off-by: Adam Wilson <[email protected]> Merged-on-behalf-of: Nicholas Wilson <[email protected]>
2 parents 35c492c + 347e415 commit 955c6e3

File tree

1 file changed

+129
-2
lines changed

1 file changed

+129
-2
lines changed

std/algorithm/searching.d

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ $(T2 commonPrefix,
3434
`commonPrefix("parakeet", "parachute")` returns `"para"`.)
3535
$(T2 endsWith,
3636
`endsWith("rocks", "ks")` returns `true`.)
37+
$(T2 extrema, `extrema([2, 1, 3, 5, 4])` returns `[1, 5]`.)
3738
$(T2 find,
3839
`find("hello world", "or")` returns `"orld"` using linear search.
3940
(For binary search refer to $(REF SortedRange, std,range).))
@@ -3684,7 +3685,7 @@ Note:
36843685
36853686
See_Also:
36863687
3687-
$(LREF maxElement), $(REF min, std,algorithm,comparison), $(LREF minCount),
3688+
$(LREF extrema), $(LREF maxElement), $(REF min, std,algorithm,comparison), $(LREF minCount),
36883689
$(LREF minIndex), $(LREF minPos)
36893690
*/
36903691
auto minElement(alias map = (a => a), Range)(Range r)
@@ -3865,7 +3866,7 @@ Note:
38653866
38663867
See_Also:
38673868
3868-
$(LREF minElement), $(REF max, std,algorithm,comparison), $(LREF maxCount),
3869+
$(LREF extrema), $(LREF minElement), $(REF max, std,algorithm,comparison), $(LREF maxCount),
38693870
$(LREF maxIndex), $(LREF maxPos)
38703871
*/
38713872
auto maxElement(alias map = (a => a), Range)(Range r)
@@ -4035,6 +4036,132 @@ if (isInputRange!Range && !isInfinite!Range &&
40354036
assert(maxElement(arr) == S(145));
40364037
}
40374038

4039+
/** Returns an array of the minimum and maximum element in `r`.
4040+
* Performs `< 3n/2` comparisons, unlike the naive `< 2n`.
4041+
* Params:
4042+
* r = The range to traverse.
4043+
*/
4044+
// TODO alias map = a => a
4045+
ElementType!Range[2] extrema(Range)(Range r)
4046+
if (isInputRange!Range && !isInfinite!Range)
4047+
in (!r.empty)
4048+
{
4049+
static if (isRandomAccessRange!Range && hasLength!Range)
4050+
{
4051+
if (r.length == 1)
4052+
return [r[0], r[0]];
4053+
4054+
typeof(return) result;
4055+
size_t i;
4056+
if (r.length & 1) // odd
4057+
{
4058+
result = [r[0], r[0]];
4059+
i = 1;
4060+
}
4061+
else
4062+
{
4063+
result = (r[0] < r[1]) ? [r[0], r[1]] : [r[1], r[0]];
4064+
i = 2;
4065+
}
4066+
// iterate pairs
4067+
const imax = r.length;
4068+
for (; i != imax; i += 2)
4069+
{
4070+
// save work
4071+
if (r[i] < r[i+1])
4072+
{
4073+
if (r[i] < result[0])
4074+
result[0] = r[i];
4075+
if (r[i+1] > result[1])
4076+
result[1] = r[i+1];
4077+
}
4078+
else
4079+
{
4080+
if (r[i+1] < result[0])
4081+
result[0] = r[i+1];
4082+
if (r[i] > result[1])
4083+
result[1] = r[i];
4084+
}
4085+
}
4086+
return result;
4087+
}
4088+
else
4089+
{
4090+
auto first = r.front;
4091+
r.popFront;
4092+
if (r.empty)
4093+
return [first, first];
4094+
4095+
typeof(return) result = (first < r.front) ? [first, r.front] : [r.front, first];
4096+
// iterate pairs
4097+
while (true)
4098+
{
4099+
r.popFront;
4100+
if (r.empty)
4101+
return result;
4102+
first = r.front;
4103+
r.popFront;
4104+
if (r.empty)
4105+
{
4106+
if (first < result[0])
4107+
result[0] = first;
4108+
else if (first > result[1])
4109+
result[1] = first;
4110+
return result;
4111+
}
4112+
// save work
4113+
if (first < r.front)
4114+
{
4115+
if (first < result[0])
4116+
result[0] = first;
4117+
if (r.front > result[1])
4118+
result[1] = r.front;
4119+
}
4120+
else
4121+
{
4122+
if (r.front < result[0])
4123+
result[0] = r.front;
4124+
if (first > result[1])
4125+
result[1] = first;
4126+
}
4127+
}
4128+
}
4129+
}
4130+
4131+
///
4132+
@safe unittest
4133+
{
4134+
assert(extrema([5,2,9,4,1]) == [1, 9]);
4135+
}
4136+
4137+
@safe unittest
4138+
{
4139+
assert(extrema([8,3,7,4,9]) == [3, 9]);
4140+
assert(extrema([1,5,3,2]) == [1, 5]);
4141+
assert(extrema([2,3,3,2]) == [2, 3]);
4142+
4143+
import std.range;
4144+
assert(iota(2, 5).extrema == [2, 4]);
4145+
assert(iota(3, 7).retro.extrema == [3, 6]);
4146+
4147+
import std.internal.test.dummyrange;
4148+
foreach (DummyType; AllDummyRanges)
4149+
{
4150+
DummyType d;
4151+
assert(d.extrema == [1, 10]);
4152+
}
4153+
4154+
version (StdRandomTests)
4155+
foreach (i; 0 .. 1000)
4156+
{
4157+
import std.random;
4158+
auto arr = generate!(() => uniform(0, 100)).takeExactly(uniform(1, 10)).array;
4159+
auto result = arr.extrema;
4160+
assert(result[0] == arr.minElement);
4161+
assert(result[1] == arr.maxElement);
4162+
}
4163+
}
4164+
40384165
// minPos
40394166
/**
40404167
Computes a subrange of `range` starting at the first occurrence of `range`'s

0 commit comments

Comments
 (0)