summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-09-10 10:35:05 +0200
committerEugen Wissner <belka@caraus.de>2017-09-10 10:35:05 +0200
commit3eb8618c32f6b7418945f85b61a03e2f61a4a8cb (patch)
treec03bd6b5b60cd28c3d454780b549e8e525f03f99 /source
parent3567a6608e928245616ad87dd602af2a3081d197 (diff)
downloadtanya-3eb8618c32f6b7418945f85b61a03e2f61a4a8cb.tar.gz
Add range.primitive
Diffstat (limited to 'source')
-rw-r--r--source/tanya/container/array.d2
-rw-r--r--source/tanya/container/list.d3
-rw-r--r--source/tanya/container/set.d2
-rw-r--r--source/tanya/memory/package.d2
-rw-r--r--source/tanya/memory/smartref.d2
-rw-r--r--source/tanya/net/inet.d2
-rw-r--r--source/tanya/range/package.d3
-rw-r--r--source/tanya/range/primitive.d908
8 files changed, 917 insertions, 7 deletions
diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d
index a13ec1e..04fa9a0 100644
--- a/source/tanya/container/array.d
+++ b/source/tanya/container/array.d
@@ -19,11 +19,11 @@ import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
-import std.range.primitives;
import std.meta;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
+import tanya.range.primitive;
/**
* Random-access range for the $(D_PSYMBOL Array).
diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d
index 97228ab..acbd546 100644
--- a/source/tanya/container/list.d
+++ b/source/tanya/container/list.d
@@ -17,10 +17,11 @@ module tanya.container.list;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.algorithm.searching;
-import std.range.primitives;
import tanya.container.entry;
import tanya.memory;
import tanya.meta.trait;
+import tanya.range.array;
+import tanya.range.primitive;
/**
* Forward range for the $(D_PSYMBOL SList).
diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d
index 1c5e559..532d4e5 100644
--- a/source/tanya/container/set.d
+++ b/source/tanya/container/set.d
@@ -702,7 +702,7 @@ private @nogc unittest
// Static checks.
private unittest
{
- import std.range.primitives;
+ import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range));
diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d
index fcb7dfa..1922752 100644
--- a/source/tanya/memory/package.d
+++ b/source/tanya/memory/package.d
@@ -18,9 +18,9 @@ import core.exception;
import std.algorithm.iteration;
import std.algorithm.mutation;
import std.conv;
-import std.range;
public import tanya.memory.allocator;
import tanya.memory.mmappool;
+import tanya.range.primitive;
import tanya.meta.trait;
/**
diff --git a/source/tanya/memory/smartref.d b/source/tanya/memory/smartref.d
index e33abcd..b0392f0 100644
--- a/source/tanya/memory/smartref.d
+++ b/source/tanya/memory/smartref.d
@@ -21,9 +21,9 @@ import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
-import std.range;
import tanya.memory;
import tanya.meta.trait;
+import tanya.range.primitive;
private template Payload(T)
{
diff --git a/source/tanya/net/inet.d b/source/tanya/net/inet.d
index f88eb11..167502a 100644
--- a/source/tanya/net/inet.d
+++ b/source/tanya/net/inet.d
@@ -15,9 +15,9 @@
module tanya.net.inet;
import std.math;
-import std.range.primitives;
import tanya.meta.trait;
import tanya.meta.transform;
+import tanya.range.primitive;
/**
* Represents an unsigned integer as an $(D_KEYWORD ubyte) range.
diff --git a/source/tanya/range/package.d b/source/tanya/range/package.d
index fc59108..98c76a1 100644
--- a/source/tanya/range/package.d
+++ b/source/tanya/range/package.d
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
- * This package contains generic function and templates to be used with D
+ * This package contains generic functions and templates to be used with D
* ranges.
*
* Copyright: Eugene Wissner 2017.
@@ -16,3 +16,4 @@
module tanya.range;
public import tanya.range.array;
+public import tanya.range.primitive;
diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d
new file mode 100644
index 0000000..e0f87e5
--- /dev/null
+++ b/source/tanya/range/primitive.d
@@ -0,0 +1,908 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This module defines primitives for working with ranges.
+ *
+ * Copyright: Eugene Wissner 2017.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/primitive.d,
+ * tanya/range/primitive.d)
+ */
+module tanya.range.primitive;
+
+import tanya.meta.trait;
+
+/**
+ * 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 a string, $(D_PSYMBOL ElementType) doesn't distinguish
+ * between narrow and wide strings, it just returns the base type of the
+ * underlying array ($(D_KEYWORD char), $(D_KEYWORD wchar) or
+ * $(D_KEYWORD dchar)).
+ *
+ * Params:
+ * R = Any 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
+ {
+ alias ElementType = ReturnType!((R r) => r.front());
+ }
+}
+
+/**
+ * Detects whether $(D_PARAM R) has a length property.
+ *
+ * $(D_PARAM R) does not have to be a range to support the length.
+ *
+ * Length mustn't be a $(D_KEYWORD @property) or a function, it can be a member
+ * variable or $(D_KEYWORD enum). But its type (or the type returned by the
+ * appropriate function) should be $(D_KEYWORD size_t), otherwise
+ * $(D_PSYMBOL hasLength) is $(D_KEYWORD false).
+ *
+ * All dynamic arrays except $(D_KEYWORD void)-arrays have length.
+ *
+ * Params:
+ * R = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) has a length property,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isInfinite).
+ */
+template hasLength(R)
+{
+ enum bool hasLength = is(ReturnType!((R r) => r.length) == size_t)
+ && !is(ElementType!R == void);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ static assert(hasLength!(char[]));
+ static assert(hasLength!(int[]));
+ static assert(hasLength!(const(int)[]));
+
+ struct A
+ {
+ enum size_t length = 1;
+ }
+ static assert(hasLength!(A));
+
+ struct B
+ {
+ @property size_t length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(hasLength!(B));
+
+ struct C
+ {
+ @property const(size_t) length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(!hasLength!(C));
+}
+
+/**
+ * Determines whether $(D_PARAM R) is a forward range with slicing support
+ * ($(D_INLINECODE R[i .. j])).
+ *
+ * For finite ranges, the result of `opSlice()` must be of the same type as the
+ * original range. If the range defines opDollar, it must support subtraction.
+ *
+ * For infinite ranges, the result of `opSlice()` must be of the same type as
+ * the original range only if it defines `opDollar()`. Otherwise it can be any
+ * forward range.
+ *
+ * For both finite and infinite ranges, the result of `opSlice()` must have
+ * length.
+ *
+ * Params:
+ * R = The type to be tested.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) supports slicing,
+ * $(D_KEYWORD false) otherwise.
+ */
+template hasSlicing(R)
+{
+ private enum bool hasDollar = is(typeof((R r) => r[0 .. $]));
+ private enum bool subDollar = !hasDollar
+ || isInfinite!R
+ || is(ReturnType!((R r) => r[0 .. $ - 1]) == R);
+
+ static if (isForwardRange!R
+ && is(ReturnType!((R r) => r[0 .. 0]) T)
+ && (!hasDollar || is(ReturnType!((R r) => r[0 .. $]) == R))
+ && subDollar
+ && isForwardRange!(ReturnType!((ref R r) => r[0 .. 0])))
+ {
+ enum bool hasSlicing = (is(T == R) || isInfinite!R)
+ && hasLength!T;
+ }
+ else
+ {
+ enum bool hasSlicing = false;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ static assert(hasSlicing!(int[]));
+ static assert(hasSlicing!(const(int)[]));
+ static assert(hasSlicing!(dstring));
+ static assert(hasSlicing!(string));
+ static assert(!hasSlicing!(const int[]));
+ static assert(!hasSlicing!(void[]));
+
+ struct A
+ {
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return false;
+ }
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ @property size_t length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ typeof(this) opSlice(const size_t i, const size_t j)
+ pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ }
+ static assert(hasSlicing!A);
+
+ struct B
+ {
+ struct Dollar
+ {
+ }
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return false;
+ }
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ @property size_t length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ @property Dollar opDollar() const pure nothrow @safe @nogc
+ {
+ return Dollar();
+ }
+ typeof(this) opSlice(const size_t i, const Dollar j)
+ pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ }
+ static assert(!hasSlicing!B);
+
+ struct C
+ {
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ enum bool empty = false;
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ typeof(this) opSlice(const size_t i, const size_t j)
+ pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ }
+ static assert(!hasSlicing!C);
+
+ struct D
+ {
+ struct Range
+ {
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ @property size_t length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ enum bool empty = false;
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ Range opSlice(const size_t i, const size_t j)
+ pure nothrow @safe @nogc
+ {
+ return Range();
+ }
+ }
+ static assert(hasSlicing!D);
+}
+
+version (TanyaPhobos)
+{
+ public import std.range.primitives : isInputRange,
+ isForwardRange,
+ isBidirectionalRange,
+ isRandomAccessRange,
+ isInfinite;
+}
+else:
+
+import tanya.meta.transform;
+
+version (unittest)
+{
+ mixin template InputRangeStub()
+ {
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ @property bool empty() const pure nothrow @safe @nogc
+ {
+ return false;
+ }
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ }
+ mixin template BidirectionalRangeStub()
+ {
+ @property int back() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ void popBack() pure nothrow @safe @nogc
+ {
+ }
+ }
+}
+
+private template isDynamicArrayRange(R)
+{
+ static if (is(R E : E[]))
+ {
+ enum bool isDynamicArrayRange = !is(E == void);
+ }
+ else
+ {
+ enum bool isDynamicArrayRange = false;
+ }
+}
+
+/**
+ * Determines whether $(D_PARAM R) is an input range.
+ *
+ * An input range should define following primitives:
+ *
+ * $(UL
+ * $(LI front)
+ * $(LI empty)
+ * $(LI popFront)
+ * )
+ *
+ * Params:
+ * R = The type to be tested.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an input range,
+ * $(D_KEYWORD false) otherwise.
+ */
+template isInputRange(R)
+{
+ static if (is(ReturnType!((R r) => r.front()) U)
+ && is(ReturnType!((R r) => r.empty) == bool))
+ {
+ enum bool isInputRange = !is(U == void)
+ && is(typeof(R.popFront()));
+ }
+ else
+ {
+ enum bool isInputRange = isDynamicArrayRange!R;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ static struct Range
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ }
+ static assert(isInputRange!Range);
+ static assert(isInputRange!(int[]));
+ static assert(!isInputRange!(void[]));
+}
+
+private pure nothrow @safe @nogc unittest
+{
+ static struct Range1(T)
+ {
+ void popFront()
+ {
+ }
+ int front()
+ {
+ return 0;
+ }
+ T empty() const
+ {
+ return true;
+ }
+ }
+ static assert(!isInputRange!(Range1!int));
+ static assert(!isInputRange!(Range1!(const bool)));
+
+ static struct Range2
+ {
+ int popFront() pure nothrow @safe @nogc
+ {
+ return 100;
+ }
+ int front() pure nothrow @safe @nogc
+ {
+ return 100;
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ }
+ static assert(isInputRange!Range2);
+
+ static struct Range3
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ void front() pure nothrow @safe @nogc
+ {
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ }
+ static assert(!isInputRange!Range3);
+
+ static struct Range4
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ enum bool empty = false;
+ }
+ static assert(isInputRange!Range4);
+}
+
+/**
+ * Determines whether $(D_PARAM R) is a forward range.
+ *
+ * A forward range is an input range that also defines:
+ *
+ * $(UL
+ * $(LI save)
+ * )
+ *
+ * Params:
+ * R = The type to be tested.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a forward range,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isInputRange).
+ */
+template isForwardRange(R)
+{
+ static if (is(ReturnType!((R r) => r.save()) U))
+ {
+ enum bool isForwardRange = isInputRange!R && is(U == R);
+ }
+ else
+ {
+ enum bool isForwardRange = isDynamicArrayRange!R;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ static struct Range
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ }
+ static assert(isForwardRange!Range);
+ static assert(isForwardRange!(int[]));
+ static assert(!isForwardRange!(void[]));
+}
+
+private pure nothrow @safe @nogc unittest
+{
+ static struct Range1
+ {
+ }
+ static struct Range2
+ {
+ mixin InputRangeStub;
+ Range1 save() pure nothrow @safe @nogc
+ {
+ return Range1();
+ }
+ }
+ static assert(!isForwardRange!Range2);
+
+ static struct Range3
+ {
+ mixin InputRangeStub;
+ const(typeof(this)) save() const pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ }
+ static assert(!isForwardRange!Range3);
+}
+
+/**
+ * Determines whether $(D_PARAM R) is a bidirectional range.
+ *
+ * A bidirectional range is a forward range that also defines:
+ *
+ * $(UL
+ * $(LI back)
+ * $(LI popBack)
+ * )
+ *
+ * Params:
+ * R = The type to be tested.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a bidirectional range,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isForwardRange).
+ */
+template isBidirectionalRange(R)
+{
+ static if (is(ReturnType!((R r) => r.back()) U))
+ {
+ enum bool isBidirectionalRange = isForwardRange!R
+ && is(U == ReturnType!((R r) => r.front()))
+ && is(typeof(R.popBack()));
+ }
+ else
+ {
+ enum bool isBidirectionalRange = isDynamicArrayRange!R;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ static struct Range
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ void popBack() pure nothrow @safe @nogc
+ {
+ }
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ @property int back() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ Range save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ }
+ static assert(isBidirectionalRange!Range);
+ static assert(isBidirectionalRange!(int[]));
+ static assert(!isBidirectionalRange!(void[]));
+}
+
+private nothrow @safe @nogc unittest
+{
+ static struct Range(T, U)
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ void popBack() pure nothrow @safe @nogc
+ {
+ }
+ @property T front() pure nothrow @safe @nogc
+ {
+ return T.init;
+ }
+ @property U back() pure nothrow @safe @nogc
+ {
+ return U.init;
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ Range save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ }
+ static assert(!isBidirectionalRange!(Range!(int, uint)));
+ static assert(!isBidirectionalRange!(Range!(int, const int)));
+}
+
+/**
+ * Determines whether $(D_PARAM R) is a random-access range.
+ *
+ * A random-access range is a range that allows random access to its
+ * elements by index using $(D_INLINECODE [])-operator (defined with
+ * $(D_INLINECODE opIndex())). Further a random access range should be a
+ * bidirectional range that also has a length or an infinite forward range.
+ *
+ * Params:
+ * R = The type to be tested.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isBidirectionalRange),
+ * $(D_PSYMBOL isForwardRange),
+ * $(D_PSYMBOL isInfinite),
+ * $(D_PSYMBOL hasLength).
+ */
+template isRandomAccessRange(R)
+{
+ static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U))
+ {
+ private enum bool isBidirectional = isBidirectionalRange!R
+ && hasLength!R;
+ private enum bool isForward = isInfinite!R && isForwardRange!R;
+ enum bool isRandomAccessRange = (isBidirectional || isForward)
+ && is(U == ReturnType!((R r) => r.front()));
+ }
+ else
+ {
+ enum bool isRandomAccessRange = isDynamicArrayRange!R;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ static struct A
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ void popBack() pure nothrow @safe @nogc
+ {
+ }
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ @property int back() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ bool empty() const pure nothrow @safe @nogc
+ {
+ return true;
+ }
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ int opIndex(const size_t pos) pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ size_t length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(isRandomAccessRange!A);
+ static assert(isRandomAccessRange!(int[]));
+ static assert(!isRandomAccessRange!(void[]));
+
+ static struct B
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ enum bool empty = false;
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ int opIndex(const size_t pos) pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(isRandomAccessRange!B);
+}
+
+private pure nothrow @safe @nogc unittest
+{
+ static struct Range1
+ {
+ mixin InputRangeStub;
+ mixin BidirectionalRangeStub;
+
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ int opIndex(const size_t pos) pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(!isRandomAccessRange!Range1);
+
+ static struct Range2(Args...)
+ {
+ mixin InputRangeStub;
+ mixin BidirectionalRangeStub;
+
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ int opIndex(Args) pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ size_t length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(isRandomAccessRange!(Range2!size_t));
+ static assert(!isRandomAccessRange!(Range2!()));
+ static assert(!isRandomAccessRange!(Range2!(size_t, size_t)));
+
+ static struct Range3
+ {
+ mixin InputRangeStub;
+ mixin BidirectionalRangeStub;
+
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ int opIndex(const size_t pos1, const size_t pos2 = 0)
+ pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ size_t length() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(isRandomAccessRange!Range3);
+
+ static struct Range4
+ {
+ mixin InputRangeStub;
+ mixin BidirectionalRangeStub;
+
+ typeof(this) save() pure nothrow @safe @nogc
+ {
+ return this;
+ }
+ int opIndex(const size_t pos1) pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ size_t opDollar() const pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ }
+ static assert(!isRandomAccessRange!Range4);
+}
+
+/**
+ * Determines whether $(D_PARAM R) is an infinite range.
+ *
+ * An infinite range is an input range whose `empty` member is defined as
+ * $(D_KEYWORD enum) which is always $(D_KEYWORD false).
+ *
+ * Params:
+ * R = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an infinite range,
+ * $(D_KEYWORD false) otherwise.
+ */
+template isInfinite(R)
+{
+ static if (isInputRange!R && is(typeof({enum bool e = R.empty;})))
+ {
+ enum bool isInfinite = R.empty == false;
+ }
+ else
+ {
+ enum bool isInfinite = false;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ static assert(!isInfinite!int);
+
+ static struct NotRange
+ {
+ enum bool empty = false;
+ }
+ static assert(!isInfinite!NotRange);
+
+ static struct InfiniteRange
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ enum bool empty = false;
+ }
+ static assert(isInfinite!InfiniteRange);
+
+ static struct InputRange
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ @property bool empty() const pure nothrow @safe @nogc
+ {
+ return false;
+ }
+ }
+ static assert(!isInfinite!InputRange);
+}
+
+private pure nothrow @safe @nogc unittest
+{
+ static struct StaticConstRange
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ static bool empty = false;
+ }
+ static assert(!isInfinite!StaticConstRange);
+
+ static struct TrueRange
+ {
+ void popFront() pure nothrow @safe @nogc
+ {
+ }
+ @property int front() pure nothrow @safe @nogc
+ {
+ return 0;
+ }
+ static const bool empty = true;
+ }
+ static assert(!isInfinite!TrueRange);
+}