summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/tanya/range/array.d18
-rw-r--r--source/tanya/range/primitive.d688
2 files changed, 582 insertions, 124 deletions
diff --git a/source/tanya/range/array.d b/source/tanya/range/array.d
index f348402..3dcc331 100644
--- a/source/tanya/range/array.d
+++ b/source/tanya/range/array.d
@@ -59,13 +59,13 @@ in
{
assert(array.length > 0);
}
-body
+do
{
return array[0];
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
string s = "Wenn die Wunde nicht mehr wehtut, schmerzt die Narbe";
static assert(is(typeof(s.front) == immutable char));
@@ -99,13 +99,13 @@ in
{
assert(array.length > 0);
}
-body
+do
{
return array[$ - 1];
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
string s = "Brecht";
static assert(is(typeof(s.back) == immutable char));
@@ -138,7 +138,7 @@ in
{
assert(array.length > 0);
}
-body
+do
{
array = array[1 .. $];
}
@@ -149,13 +149,13 @@ in
{
assert(array.length > 0);
}
-body
+do
{
array = array[0 .. $ - 1];
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
wstring array = "Der finstere Ozean der Metaphysik. Nietzsche";
@@ -184,7 +184,7 @@ pure nothrow @safe @nogc unittest
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
int[1] array;
assert(!array.empty);
@@ -209,7 +209,7 @@ pure nothrow @safe @nogc unittest
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
ubyte[8] array;
auto slice = array.save;
diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d
index 33efbce..542ee69 100644
--- a/source/tanya/range/primitive.d
+++ b/source/tanya/range/primitive.d
@@ -14,14 +14,17 @@
*/
module tanya.range.primitive;
+import tanya.math;
import tanya.meta.trait;
import tanya.meta.transform;
+import tanya.range.array;
/**
* Returns the element type of the range $(D_PARAM R).
*
* Element type is the return type of such primitives like
* $(D_INLINECODE R.front) and (D_INLINECODE R.back) or the array base type.
+ * If $(D_PARAM R) is not a range, its element type is $(D_KEYWORD void).
*
* If $(D_PARAM R) is a string, $(D_PSYMBOL ElementType) doesn't distinguish
* between narrow and wide strings, it just returns the base type of the
@@ -29,21 +32,24 @@ import tanya.meta.transform;
* $(D_KEYWORD dchar)).
*
* Params:
- * R = Any range type.
+ * R = Range type.
*
* Returns: Element type of the range $(D_PARAM R).
*/
template ElementType(R)
-if (isInputRange!R)
{
static if (is(R U : U[]))
{
alias ElementType = U;
}
- else
+ else static if (isInputRange!R)
{
alias ElementType = ReturnType!((R r) => r.front());
}
+ else
+ {
+ alias ElementType = void;
+ }
}
/**
@@ -68,12 +74,11 @@ if (isInputRange!R)
*/
template hasLength(R)
{
- enum bool hasLength = is(ReturnType!((R r) => r.length) == size_t)
- && !is(ElementType!R == void);
+ enum bool hasLength = is(ReturnType!((R r) => r.length) == size_t);
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static assert(hasLength!(char[]));
static assert(hasLength!(int[]));
@@ -87,7 +92,7 @@ pure nothrow @safe @nogc unittest
struct B
{
- @property size_t length() const pure nothrow @safe @nogc
+ @property size_t length() const @nogc nothrow pure @safe
{
return 0;
}
@@ -96,12 +101,12 @@ pure nothrow @safe @nogc unittest
struct C
{
- @property const(size_t) length() const pure nothrow @safe @nogc
+ @property const(size_t) length() const @nogc nothrow pure @safe
{
return 0;
}
}
- static assert(!hasLength!(C));
+ static assert(!hasLength!C);
}
/**
@@ -147,7 +152,7 @@ template hasSlicing(R)
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static assert(hasSlicing!(int[]));
static assert(hasSlicing!(const(int)[]));
@@ -158,22 +163,22 @@ pure nothrow @safe @nogc unittest
struct A
{
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return false;
}
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- @property size_t length() const pure nothrow @safe @nogc
+ @property size_t length() const @nogc nothrow pure @safe
{
return 0;
}
@@ -190,26 +195,26 @@ pure nothrow @safe @nogc unittest
struct Dollar
{
}
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return false;
}
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- @property size_t length() const pure nothrow @safe @nogc
+ @property size_t length() const @nogc nothrow pure @safe
{
return 0;
}
- @property Dollar opDollar() const pure nothrow @safe @nogc
+ @property Dollar opDollar() const @nogc nothrow pure @safe
{
return Dollar();
}
@@ -223,15 +228,15 @@ pure nothrow @safe @nogc unittest
struct C
{
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
enum bool empty = false;
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
@@ -247,35 +252,35 @@ pure nothrow @safe @nogc unittest
{
struct Range
{
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- @property size_t length() const pure nothrow @safe @nogc
+ @property size_t length() const @nogc nothrow pure @safe
{
return 0;
}
}
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
enum bool empty = false;
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
@@ -292,25 +297,25 @@ version (unittest)
{
mixin template InputRangeStub()
{
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
- @property bool empty() const pure nothrow @safe @nogc
+ @property bool empty() const @nogc nothrow pure @safe
{
return false;
}
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
}
mixin template BidirectionalRangeStub()
{
- @property int back() pure nothrow @safe @nogc
+ @property int back() @nogc nothrow pure @safe
{
return 0;
}
- void popBack() pure nothrow @safe @nogc
+ void popBack() @nogc nothrow pure @safe
{
}
}
@@ -348,10 +353,10 @@ private template isDynamicArrayRange(R)
template isInputRange(R)
{
static if (is(ReturnType!((R r) => r.front()) U)
- && is(ReturnType!((R r) => r.empty) == bool))
+ && is(ReturnType!((R r) => r.empty) == bool)
+ && is(typeof(R.popFront())))
{
- enum bool isInputRange = !is(U == void)
- && is(typeof(R.popFront()));
+ enum bool isInputRange = !is(U == void);
}
else
{
@@ -360,18 +365,18 @@ template isInputRange(R)
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct Range
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
@@ -381,7 +386,7 @@ pure nothrow @safe @nogc unittest
static assert(!isInputRange!(void[]));
}
-private pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct Range1(T)
{
@@ -402,15 +407,15 @@ private pure nothrow @safe @nogc unittest
static struct Range2
{
- int popFront() pure nothrow @safe @nogc
+ int popFront() @nogc nothrow pure @safe
{
return 100;
}
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 100;
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
@@ -419,13 +424,13 @@ private pure nothrow @safe @nogc unittest
static struct Range3
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- void front() pure nothrow @safe @nogc
+ void front() @nogc nothrow pure @safe
{
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
@@ -434,10 +439,10 @@ private pure nothrow @safe @nogc unittest
static struct Range4
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
@@ -471,27 +476,27 @@ template isForwardRange(R)
}
else
{
- enum bool isForwardRange = isDynamicArrayRange!R;
+ enum bool isForwardRange = false;
}
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct Range
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- int front() pure nothrow @safe @nogc
+ int front() @nogc nothrow pure @safe
{
return 0;
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
@@ -501,7 +506,7 @@ pure nothrow @safe @nogc unittest
static assert(!isForwardRange!(void[]));
}
-private pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct Range1
{
@@ -509,7 +514,7 @@ private pure nothrow @safe @nogc unittest
static struct Range2
{
mixin InputRangeStub;
- Range1 save() pure nothrow @safe @nogc
+ Range1 save() @nogc nothrow pure @safe
{
return Range1();
}
@@ -519,7 +524,7 @@ private pure nothrow @safe @nogc unittest
static struct Range3
{
mixin InputRangeStub;
- const(typeof(this)) save() const pure nothrow @safe @nogc
+ const(typeof(this)) save() const @nogc nothrow pure @safe
{
return this;
}
@@ -547,11 +552,11 @@ private pure nothrow @safe @nogc unittest
*/
template isBidirectionalRange(R)
{
- static if (is(ReturnType!((R r) => r.back()) U))
+ static if (is(ReturnType!((R r) => r.back()) U)
+ && is(typeof(R.popBack())))
{
enum bool isBidirectionalRange = isForwardRange!R
- && is(U == ReturnType!((R r) => r.front()))
- && is(typeof(R.popBack()));
+ && is(U == ReturnType!((R r) => r.front()));
}
else
{
@@ -560,29 +565,29 @@ template isBidirectionalRange(R)
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct Range
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- void popBack() pure nothrow @safe @nogc
+ void popBack() @nogc nothrow pure @safe
{
}
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
- @property int back() pure nothrow @safe @nogc
+ @property int back() @nogc nothrow pure @safe
{
return 0;
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
- Range save() pure nothrow @safe @nogc
+ Range save() @nogc nothrow pure @safe
{
return this;
}
@@ -592,29 +597,29 @@ pure nothrow @safe @nogc unittest
static assert(!isBidirectionalRange!(void[]));
}
-private nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct Range(T, U)
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- void popBack() pure nothrow @safe @nogc
+ void popBack() @nogc nothrow pure @safe
{
}
- @property T front() pure nothrow @safe @nogc
+ @property T front() @nogc nothrow pure @safe
{
return T.init;
}
- @property U back() pure nothrow @safe @nogc
+ @property U back() @nogc nothrow pure @safe
{
return U.init;
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
- Range save() pure nothrow @safe @nogc
+ Range save() @nogc nothrow pure @safe
{
return this;
}
@@ -659,37 +664,37 @@ template isRandomAccessRange(R)
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct A
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- void popBack() pure nothrow @safe @nogc
+ void popBack() @nogc nothrow pure @safe
{
}
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
- @property int back() pure nothrow @safe @nogc
+ @property int back() @nogc nothrow pure @safe
{
return 0;
}
- bool empty() const pure nothrow @safe @nogc
+ bool empty() const @nogc nothrow pure @safe
{
return true;
}
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- int opIndex(const size_t pos) pure nothrow @safe @nogc
+ int opIndex(const size_t pos) @nogc nothrow pure @safe
{
return 0;
}
- size_t length() const pure nothrow @safe @nogc
+ size_t length() const @nogc nothrow pure @safe
{
return 0;
}
@@ -700,19 +705,19 @@ pure nothrow @safe @nogc unittest
static struct B
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
enum bool empty = false;
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- int opIndex(const size_t pos) pure nothrow @safe @nogc
+ int opIndex(const size_t pos) @nogc nothrow pure @safe
{
return 0;
}
@@ -720,18 +725,18 @@ pure nothrow @safe @nogc unittest
static assert(isRandomAccessRange!B);
}
-private pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct Range1
{
mixin InputRangeStub;
mixin BidirectionalRangeStub;
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- int opIndex(const size_t pos) pure nothrow @safe @nogc
+ int opIndex(const size_t pos) @nogc nothrow pure @safe
{
return 0;
}
@@ -743,15 +748,15 @@ private pure nothrow @safe @nogc unittest
mixin InputRangeStub;
mixin BidirectionalRangeStub;
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- int opIndex(Args) pure nothrow @safe @nogc
+ int opIndex(Args) @nogc nothrow pure @safe
{
return 0;
}
- size_t length() const pure nothrow @safe @nogc
+ size_t length() const @nogc nothrow pure @safe
{
return 0;
}
@@ -765,16 +770,16 @@ private pure nothrow @safe @nogc unittest
mixin InputRangeStub;
mixin BidirectionalRangeStub;
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
int opIndex(const size_t pos1, const size_t pos2 = 0)
- pure nothrow @safe @nogc
+ @nogc nothrow pure @safe
{
return 0;
}
- size_t length() const pure nothrow @safe @nogc
+ size_t length() const @nogc nothrow pure @safe
{
return 0;
}
@@ -786,15 +791,15 @@ private pure nothrow @safe @nogc unittest
mixin InputRangeStub;
mixin BidirectionalRangeStub;
- typeof(this) save() pure nothrow @safe @nogc
+ typeof(this) save() @nogc nothrow pure @safe
{
return this;
}
- int opIndex(const size_t pos1) pure nothrow @safe @nogc
+ int opIndex(const size_t pos1) @nogc nothrow pure @safe
{
return 0;
}
- size_t opDollar() const pure nothrow @safe @nogc
+ size_t opDollar() const @nogc nothrow pure @safe
{
return 0;
}
@@ -803,6 +808,129 @@ private pure nothrow @safe @nogc unittest
}
/**
+ * Determines whether $(D_PARAM R) is an output range for the elemens of type
+ * $(D_PARAM E).
+ *
+ * If $(D_PARAM R) is an output range for the elements of type $(D_PARAM E)
+ * if an element `e` of type $(D_PARAM E) can be put into the range instance
+ * `r` in one of the following ways:
+ *
+ * $(TABLE
+ * $(TR
+ * $(TH Code)
+ * $(TH Scenario)
+ * )
+ * $(TR
+ * $(TD r.put(e))
+ * $(TD $(D_PARAM R) defines `put` for $(D_PARAM E).)
+ * )
+ * $(TR
+ * $(TD r.front = e)
+ * $(TD $(D_PARAM R) is an input range, whose element type is
+ * $(D_PARAM E) and `front` is an lvalue.)
+ * )
+ * $(TR
+ * $(TD r(e))
+ * $(TD $(D_PARAM R) defines `opCall` for $(D_PARAM E).)
+ * )
+ * $(TR
+ * $(TD for (; !e.empty; e.popFront()) r.put(e.front) $(BR)
+ * for (; !e.empty; e.popFront(), r.popFront())
+ * r.front = e.front $(BR)
+ * for (; !e.empty; e.popFront()) r(e.front)
+ * )
+ * $(TD $(D_PARAM E) is input range, whose elements can be put into
+ * $(D_PARAM R) according to the rules described above in this table.
+ * )
+ * )
+ * )
+ *
+ * Params:
+ * R = The type to be tested.
+ * E = Element type should be tested for.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the
+ * elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise.
+ */
+template isOutputRange(R, E)
+{
+ private enum bool doPut(E) = is(typeof((R r, E e) => r.put(e)))
+ || (isInputRange!R
+ && is(typeof((R r, E e) => r.front = e)))
+ || is(typeof((R r, E e) => r(e)));
+
+ static if (doPut!E)
+ {
+ enum bool isOutputRange = true;
+ }
+ else static if (isInputRange!E)
+ {
+ enum bool isOutputRange = doPut!(ElementType!E);
+ }
+ else
+ {
+ enum bool isOutputRange = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct R1
+ {
+ void put(int) @nogc nothrow pure @safe
+ {
+ }
+ }
+ static assert(isOutputRange!(R1, int));
+
+ static struct R2
+ {
+ int value;
+ void popFront() @nogc nothrow pure @safe
+ {
+ }
+ ref int front() @nogc nothrow pure @safe
+ {
+ return value;
+ }
+ bool empty() const @nogc nothrow pure @safe
+ {
+ return true;
+ }
+ }
+ static assert(isOutputRange!(R2, int));
+
+ static struct R3
+ {
+ void popFront() @nogc nothrow pure @safe
+ {
+ }
+ int front() @nogc nothrow pure @safe
+ {
+ return 0;
+ }
+ bool empty() const @nogc nothrow pure @safe
+ {
+ return true;
+ }
+ }
+ static assert(!isOutputRange!(R3, int));
+
+ static struct R4
+ {
+ void opCall(int) @nogc nothrow pure @safe
+ {
+ }
+ }
+ static assert(isOutputRange!(R4, int));
+
+ static assert(isOutputRange!(R1, R3));
+ static assert(isOutputRange!(R2, R3));
+ static assert(isOutputRange!(R4, R3));
+}
+
+/**
* Determines whether $(D_PARAM R) is an infinite range.
*
* An infinite range is an input range whose `empty` member is defined as
@@ -827,7 +955,7 @@ template isInfinite(R)
}
///
-pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static assert(!isInfinite!int);
@@ -839,10 +967,10 @@ pure nothrow @safe @nogc unittest
static struct InfiniteRange
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
@@ -852,14 +980,14 @@ pure nothrow @safe @nogc unittest
static struct InputRange
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
- @property bool empty() const pure nothrow @safe @nogc
+ @property bool empty() const @nogc nothrow pure @safe
{
return false;
}
@@ -867,14 +995,14 @@ pure nothrow @safe @nogc unittest
static assert(!isInfinite!InputRange);
}
-private pure nothrow @safe @nogc unittest
+@nogc nothrow pure @safe unittest
{
static struct StaticConstRange
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
@@ -884,10 +1012,10 @@ private pure nothrow @safe @nogc unittest
static struct TrueRange
{
- void popFront() pure nothrow @safe @nogc
+ void popFront() @nogc nothrow pure @safe
{
}
- @property int front() pure nothrow @safe @nogc
+ @property int front() @nogc nothrow pure @safe
{
return 0;
}
@@ -895,3 +1023,333 @@ private pure nothrow @safe @nogc unittest
}
static assert(!isInfinite!TrueRange);
}
+
+/**
+ * Removes exactly $(D_PARAM count) first elements from the input range
+ * $(D_PARAM range).
+ *
+ * $(D_PARAM R) must have length or be infinite.
+ *
+ * Params:
+ * R = Range type.
+ * range = Some input range.
+ * count = Number of elements to remove.
+ *
+ * See_Also: $(D_PSYMBOL popBackExactly),
+ * $(D_PSYMBOL popFrontN),
+ * $(D_PSYMBOL isInputRange),
+ * $(D_PSYMBOL hasLength),
+ * $(D_PSYMBOL isInfinite).
+ *
+ * Precondition: If $(D_PARAM R) has length, it must be less than or equal to
+ * $(D_PARAM count).
+ */
+void popFrontExactly(R)(ref R range, size_t count)
+if (isInputRange!R && (hasLength!R || isInfinite!R))
+in
+{
+ static if (hasLength!R)
+ {
+ assert(count <= range.length);
+ }
+}
+do
+{
+ static if (hasSlicing!R)
+ {
+ range = range[count .. $];
+ }
+ else
+ {
+ while (count-- != 0)
+ {
+ range.popFront();
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int[5] a = [1, 2, 3, 4, 5];
+ auto slice = a[];
+
+ popFrontExactly(slice, 3);
+ assert(slice.length == 2);
+ assert(slice[0] == 4);
+ assert(slice[$ - 1] == 5);
+
+ popFrontExactly(slice, 2);
+ assert(slice.length == 0);
+}
+
+/**
+ * Removes exactly $(D_PARAM count) last elements from the bidirectional range
+ * $(D_PARAM range).
+ *
+ * $(D_PARAM R) must have length or be infinite.
+ *
+ * Params:
+ * R = Range type.
+ * range = Some bidirectional range.
+ * count = Number of elements to remove.
+ *
+ * See_Also: $(D_PSYMBOL popFrontExactly),
+ * $(D_PSYMBOL popBackN),
+ * $(D_PSYMBOL isBidirectionalRange),
+ * $(D_PSYMBOL hasLength),
+ * $(D_PSYMBOL isInfinite).
+ *
+ * Precondition: If $(D_PARAM R) has length, it must be less than or equal to
+ * $(D_PARAM count).
+ */
+void popBackExactly(R)(ref R range, size_t count)
+if (isBidirectionalRange!R && (hasLength!R || isInfinite!R))
+in
+{
+ static if (hasLength!R)
+ {
+ assert(count <= range.length);
+ }
+}
+do
+{
+ static if (hasSlicing!R)
+ {
+ range = range[0 .. $ - count];
+ }
+ else
+ {
+ while (count-- != 0)
+ {
+ range.popBack();
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int[5] a = [1, 2, 3, 4, 5];
+ auto slice = a[];
+
+ popBackExactly(slice, 3);
+ assert(slice.length == 2);
+ assert(slice[0] == 1);
+ assert(slice[$ - 1] == 2);
+
+ popBackExactly(slice, 2);
+ assert(slice.length == 0);
+}
+
+/**
+ * Removes maximum $(D_PARAM count) first elements from the input range
+ * $(D_PARAM range).
+ *
+ * Params:
+ * R = Range type.
+ * range = Some input range.
+ * count = Number of elements to remove.
+ *
+ * See_Also: $(D_PSYMBOL popBackN),
+ * $(D_PSYMBOL popFrontExactly),
+ * $(D_PSYMBOL isInputRange).
+ */
+void popFrontN(R)(ref R range, size_t count)
+if (isInputRange!R)
+{
+ static if (hasLength!R && hasSlicing!R)
+ {
+ range = range[min(count, range.length) .. $];
+ }
+ else static if (hasLength!R)
+ {
+ size_t length = min(count, range.length);
+ while (length--)
+ {
+ range.popFront();
+ }
+ }
+ else
+ {
+ while (count-- != 0 && !range.empty)
+ {
+ range.popFront();
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int[5] a = [1, 2, 3, 4, 5];
+ auto slice = a[];
+
+ popFrontN(slice, 3);
+ assert(slice.length == 2);
+ assert(slice[0] == 4);
+ assert(slice[$ - 1] == 5);
+
+ popFrontN(slice, 20);
+ assert(slice.length == 0);
+}
+
+/**
+ * Removes maximum $(D_PARAM count) last elements from the bidirectional range
+ * $(D_PARAM range).
+ *
+ * Params:
+ * R = Range type.
+ * range = Some bidirectional range.
+ * count = Number of elements to remove.
+ *
+ * See_Also: $(D_PSYMBOL popFrontN),
+ * $(D_PSYMBOL popBackExactly),
+ * $(D_PSYMBOL isBidirectionalRange).
+ */
+void popBackN(R)(ref R range, size_t count)
+if (isBidirectionalRange!R)
+{
+ static if (hasLength!R && hasSlicing!R)
+ {
+ range = range[0 .. $ - min(count, range.length)];
+ }
+ else static if (hasLength!R)
+ {
+ size_t length = min(count, range.length);
+ while (length--)
+ {
+ range.popBack();
+ }
+ }
+ else
+ {
+ while (count-- != 0 && !range.empty)
+ {
+ range.popBack();
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int[5] a = [1, 2, 3, 4, 5];
+ auto slice = a[];
+
+ popBackN(slice, 3);
+ assert(slice.length == 2);
+ assert(slice[0] == 1);
+ assert(slice[$ - 1] == 2);
+
+ popBackN(slice, 20);
+ assert(slice.length == 0);
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static struct InfiniteRange
+ {
+ private int i;
+
+ InfiniteRange save() @nogc nothrow pure @safe
+ {
+ return this;
+ }
+
+ void popFront() @nogc nothrow pure @safe
+ {
+ ++this.i;
+ }
+
+ void popBack() @nogc nothrow pure @safe
+ {
+ --this.i;
+ }
+
+ @property int front() const @nogc nothrow pure @safe
+ {
+ return this.i;
+ }
+
+ @property int back() const @nogc nothrow pure @safe
+ {
+ return this.i;
+ }
+
+ enum bool empty = false;
+ }
+ {
+ InfiniteRange range;
+ popFrontExactly(range, 2);
+ assert(range.front == 2);
+ popFrontN(range, 2);
+ assert(range.front == 4);
+ }
+ {
+ InfiniteRange range;
+ popBackExactly(range, 2);
+ assert(range.back == -2);
+ popBackN(range, 2);
+ assert(range.back == -4);
+ }
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static struct Range
+ {
+ private int[5] a = [1, 2, 3, 4, 5];
+ private size_t begin = 0, end = 5;
+
+ Range save() @nogc nothrow pure @safe
+ {
+ return this;
+ }
+
+ void popFront() @nogc nothrow pure @safe
+ {
+ ++this.begin;
+ }
+
+ void popBack() @nogc nothrow pure @safe
+ {
+ --this.end;
+ }
+
+ @property int front() const @nogc nothrow pure @safe
+ {
+ return this.a[this.begin];
+ }
+
+ @property int back() const @nogc nothrow pure @safe
+ {
+ return this.a[this.end - 1];
+ }
+
+ @property bool empty() const @nogc nothrow pure @safe
+ {
+ return this.begin >= this.end;
+ }
+ }
+ {
+ Range range;
+
+ popFrontN(range, 3);
+ assert(range.front == 4);
+ assert(range.back == 5);
+
+ popFrontN(range, 20);
+ assert(range.empty);
+ }
+ {
+ Range range;
+
+ popBackN(range, 3);
+ assert(range.front == 1);
+ assert(range.back == 2);
+
+ popBackN(range, 20);
+ assert(range.empty);
+ }
+}