Skip to content

Commit 428217b

Browse files
authored
Merge pull request #4961 from edi33416/uniq_consistency
Fix issue 16588 - uniqs BidirectionalRange behavior is inconsistent w…
2 parents 7c2dc1c + e1c5362 commit 428217b

File tree

1 file changed

+88
-8
lines changed

1 file changed

+88
-8
lines changed

std/algorithm/iteration.d

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4836,9 +4836,48 @@ if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool))
48364836
assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
48374837
}
48384838

4839+
///
4840+
@trusted unittest
4841+
{
4842+
import std.algorithm.comparison : equal;
4843+
4844+
struct S
4845+
{
4846+
int i;
4847+
string s;
4848+
}
4849+
4850+
auto arr = [ S(1, "a"), S(1, "b"), S(2, "c"), S(2, "d") ];
4851+
4852+
// Let's consider just the 'i' member for equality
4853+
auto r = arr.uniq!((a, b) => a.i == b.i);
4854+
assert(r.equal([S(1, "a"), S(2, "c")]));
4855+
assert(r.front == S(1, "a"));
4856+
assert(r.back == S(2, "c"));
4857+
4858+
r.popBack;
4859+
assert(r.back == S(1, "a"));
4860+
assert(r.front == r.back);
4861+
4862+
r.popBack;
4863+
assert(r.empty);
4864+
4865+
import std.exception : assertThrown;
4866+
4867+
assertThrown!Error(r.front);
4868+
assertThrown!Error(r.back);
4869+
assertThrown!Error(r.popFront);
4870+
assertThrown!Error(r.popBack);
4871+
}
4872+
48394873
private struct UniqResult(alias pred, Range)
48404874
{
4841-
Range _input;
4875+
private Range _input;
4876+
static if (isBidirectionalRange!Range)
4877+
{
4878+
private ElementType!Range _back;
4879+
private bool _isInBack;
4880+
}
48424881

48434882
this(Range input)
48444883
{
@@ -4853,17 +4892,33 @@ private struct UniqResult(alias pred, Range)
48534892
void popFront()
48544893
{
48554894
assert(!empty, "Attempting to popFront an empty uniq.");
4895+
static if (isBidirectionalRange!Range)
4896+
{
4897+
if (_input.empty)
4898+
{
4899+
_isInBack = false;
4900+
return;
4901+
}
4902+
}
4903+
48564904
auto last = _input.front;
48574905
do
48584906
{
4859-
_input.popFront();
4907+
_input.popFront;
48604908
}
48614909
while (!_input.empty && pred(last, _input.front));
48624910
}
48634911

48644912
@property ElementType!Range front()
48654913
{
48664914
assert(!empty, "Attempting to fetch the front of an empty uniq.");
4915+
static if (isBidirectionalRange!Range)
4916+
{
4917+
if (_input.empty && _isInBack)
4918+
{
4919+
return _back;
4920+
}
4921+
}
48674922
return _input.front;
48684923
}
48694924

@@ -4872,18 +4927,33 @@ private struct UniqResult(alias pred, Range)
48724927
void popBack()
48734928
{
48744929
assert(!empty, "Attempting to popBack an empty uniq.");
4875-
auto last = _input.back;
4876-
do
4930+
if (_input.empty)
4931+
{
4932+
_isInBack = false;
4933+
}
4934+
else
48774935
{
4878-
_input.popBack();
4936+
_isInBack = true;
4937+
_back = _input.back;
4938+
ElementType!Range last;
4939+
do
4940+
{
4941+
last = _input.back;
4942+
_input.popBack;
4943+
}
4944+
while (!_input.empty && pred(_back, _input.back));
4945+
_back = last;
48794946
}
4880-
while (!_input.empty && pred(last, _input.back));
48814947
}
48824948

48834949
@property ElementType!Range back()
48844950
{
48854951
assert(!empty, "Attempting to fetch the back of an empty uniq.");
4886-
return _input.back;
4952+
if (!_isInBack)
4953+
{
4954+
popBack;
4955+
}
4956+
return _back;
48874957
}
48884958
}
48894959

@@ -4893,7 +4963,17 @@ private struct UniqResult(alias pred, Range)
48934963
}
48944964
else
48954965
{
4896-
@property bool empty() { return _input.empty; }
4966+
@property bool empty()
4967+
{
4968+
static if (isBidirectionalRange!Range)
4969+
{
4970+
return _input.empty && !_isInBack;
4971+
}
4972+
else
4973+
{
4974+
return _input.empty;
4975+
}
4976+
}
48974977
}
48984978

48994979
static if (isForwardRange!Range)

0 commit comments

Comments
 (0)