phobos/std/range.d
2010-06-10 15:57:35 +00:00

2615 lines
63 KiB
D

// Written in the D programming language.
/**
This module defines a few useful _range incarnations. Credit for ideas
in building this module go to $(WEB fantascienza.net/leonardo/so/,
Leonardo Maffi).
Macros:
WIKI = Phobos/StdRange
Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
Authors: $(WEB erdani.org, Andrei Alexandrescu)
Copyright Andrei Alexandrescu 2008 - 2009.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/
module std.range;
public import std.array;
import std.contracts;
import std.traits;
import std.typecons;
import std.typetuple;
import std.algorithm;
import std.functional;
import std.conv;
version(unittest)
{
import std.container, std.conv, std.math, std.stdio;
}
/**
Returns $(D true) if $(D R) is an input range. An input range must
define the primitives $(D empty), $(D popFront), and $(D front). The
following code should compile for any input range.
----
R r; // can define a range object
if (r.empty) {} // can test for empty
r.popFront; // can invoke next
auto h = r.front; // can get the front of the range
----
The semantics of an input range (not checkable during compilation) are
assumed to be the following ($(D r) is an object of type $(D R)):
$(UL $(LI $(D r.empty) returns $(D false) iff there is more data
available in the range.) $(LI $(D r.front) returns the current element
in the range. It may return by value or by reference. Calling $(D
r.front) is allowed only if calling $(D r.empty) has, or would have,
returned $(D false).) $(LI $(D r.popFront) advances to the popFront element in
the range. Calling $(D r.popFront) is allowed only if calling $(D r.empty)
has, or would have, returned $(D false).))
*/
template isInputRange(R)
{
enum bool isInputRange = is(typeof(
{
R r; // can define a range object
if (r.empty) {} // can test for empty
r.popFront; // can invoke next
auto h = r.front; // can get the front of the range
}()));
}
unittest
{
struct A {}
static assert(!isInputRange!(A));
struct B
{
void popFront();
bool empty();
int front();
}
static assert(isInputRange!(B));
static assert(isInputRange!(int[]));
static assert(isInputRange!(char[]));
}
/**
Returns $(D true) if $(D R) is an output range. An output range must
define the primitive $(D put) that accepts an object of type $(D
E). The following code should compile for any output range.
----
R r; // can define a range object
E e;
r.put(e); // can write an element to the range
----
The semantics of an output range (not checkable during compilation)
are assumed to be the following ($(D r) is an object of type $(D R)):
$(UL $(LI $(D r.put(e)) puts $(D e) in the range (in a range-dependent
manner) and advances to the popFront position in the range. Successive
calls to $(D r.put) add elements to the range. $(D put) may throw to
signal failure.))
*/
template isOutputRange(R, E)
{
enum bool isOutputRange = is(typeof(
{
R r; // can define a range object
E e;
r.put(e); // can write element to range
}()));
}
unittest
{
struct A {}
static assert(!isInputRange!(A));
struct B
{
void put(int);
}
static assert(isOutputRange!(B, int));
static assert(isOutputRange!(int[], int));
}
/**
Returns $(D true) if $(D R) is a forward range. A forward range is an
input range that can save "checkpoints" by simply copying it to
another value of the same type. Notable examples of input ranges that
are $(I not) forward ranges are file/socket ranges; copying such a
range will not save the position in the stream, and they most likely
reuse an internal buffer as the entire stream does not sit in
memory. Subsequently, advancing either the original or the copy will
advance the stream, so the copies are not independent. The following
code should compile for any forward range.
----
static assert(isInputRange!(R));
R r1;
R r2 = r1; // can copy a range to another
----
The semantics of a forward range (not checkable during compilation)
are the same as for an input range, with the additional requirement
that backtracking must be possible by saving a copy of the range
object.
*/
template isForwardRange(R)
{
enum bool isForwardRange = isInputRange!(R) && is(typeof(
{
R r1;
R r2 = r1.save; // can call "save" against a range
// object
}()));
}
unittest
{
static assert(!isForwardRange!(int));
static assert(isForwardRange!(int[]));
}
/**
Returns $(D true) if $(D R) is a bidirectional range. A bidirectional
range is a forward range that also offers the primitives $(D back) and
$(D popBack). The following code should compile for any bidirectional
range.
----
R r;
static assert(isForwardRange!(R)); // range is an input range
r.popBack; // can invoke popBack
auto t = r.back; // can get the back of the range
----
The semantics of a bidirectional range (not checkable during compilation)
are assumed to be the following ($(D r) is an object of type $(D R)):
$(UL $(LI $(D r.back) returns (possibly a reference to) the last
element in the range. Calling $(D r.back) is allowed only if calling
$(D r.empty) has, or would have, returned $(D false).))
*/
template isBidirectionalRange(R)
{
enum bool isBidirectionalRange = isForwardRange!(R) && is(typeof(
{
R r;
r.popBack; // can invoke popBack
auto h = r.back; // can get the back of the range
}()));
}
unittest
{
struct A {}
static assert(!isBidirectionalRange!(A));
struct B
{
void popFront();
bool empty();
int front();
}
static assert(!isBidirectionalRange!(B));
struct C
{
@property bool empty();
@property C save();
void popFront();
@property int front();
void popBack();
@property int back();
}
static assert(isBidirectionalRange!(C));
static assert(isBidirectionalRange!(int[]));
static assert(isBidirectionalRange!(char[]));
}
/**
Returns $(D true) if $(D R) is a random-access range. A random-access
range is a forward range that also offers the primitive $(D
opIndex), OR an infinite input range that offers $(D opIndex). The
following code should compile for any random-access range.
----
R r;
static assert(isForwardRange!(R)); // range is forward
static assert(isBidirectionalRange!(R) || isInfinite!(R));
// range is bidirectional or infinite
auto e = r[1]; // can index
----
The semantics of a random-access range (not checkable during
compilation) are assumed to be the following ($(D r) is an object of
type $(D R)):
$(UL $(LI $(D r.opIndex(n)) returns a reference to the $(D n)th
element in the range.))
*/
template isRandomAccessRange(R)
{
enum bool isRandomAccessRange =
(isBidirectionalRange!(R) || isInfinite!(R))
&& is(typeof(R.init[1]))
&& !isNarrowString!R;
}
unittest
{
struct A {}
static assert(!isRandomAccessRange!(A));
struct B
{
void popFront();
bool empty();
int front();
}
static assert(!isRandomAccessRange!(B));
struct C
{
void popFront();
bool empty();
int front();
void popBack();
int back();
}
static assert(!isRandomAccessRange!(C));
struct D
{
bool empty();
@property D save();
int front();
void popFront();
int back();
void popBack();
ref int opIndex(uint);
//int opSlice(uint, uint);
}
static assert(isRandomAccessRange!(D));
static assert(isRandomAccessRange!(int[]));
}
/**
The element type of $(D R). $(D R) does not have to be a range. The
element type is determined as the type yielded by $(D r.front) for an
object $(D r) or type $(D R). For example, $(D ElementType!(T[])) is
$(D T).
*/
template ElementType(R)
{
//alias typeof({ R r; return front(r[]); }()) ElementType;
static if (is(typeof(R.front()) T))
alias T ElementType;
else
alias void ElementType;
}
unittest
{
enum XYZ : string { a = "foo" };
auto x = front(XYZ.a);
static assert(is(ElementType!(XYZ) : dchar));
immutable char[3] a = "abc";
static assert(is(ElementType!(typeof(a)) : dchar));
int[] i;
static assert(is(ElementType!(typeof(i)) : int));
}
/**
Returns $(D true) if $(D R) is a forward range and has swappable
elements. The following code should compile for any random-access
range.
----
R r;
static assert(isForwardRange!(R)); // range is forward
swap(r.front, r.front); // can swap elements of the range
----
*/
template hasSwappableElements(R)
{
enum bool hasSwappableElements = isForwardRange!(R) && is(typeof(
{
R r;
swap(r.front, r.front); // can swap elements of the range
}()));
}
unittest
{
static assert(!hasSwappableElements!(const int[]));
static assert(!hasSwappableElements!(const(int)[]));
static assert(hasSwappableElements!(int[]));
//static assert(hasSwappableElements!(char[]));
}
/**
Returns $(D true) if $(D R) is a forward range and has mutable
elements. The following code should compile for any random-access
range.
----
R r;
static assert(isForwardRange!(R)); // range is forward
auto e = r.front;
r.front = e; // can assign elements of the range
----
*/
template hasAssignableElements(R)
{
enum bool hasAssignableElements = isForwardRange!(R) && is(typeof(
{
R r;
static assert(isForwardRange!(R)); // range is forward
auto e = r.front;
r.front = e; // can assign elements of the range
}()));
}
unittest
{
static assert(!hasAssignableElements!(const int[]));
static assert(!hasAssignableElements!(const(int)[]));
static assert(hasAssignableElements!(int[]));
}
/**
Returns $(D true) if $(D R) has a $(D length) member that returns an
integral type. $(D R) does not have to be a range. Note that $(D
length) is an optional primitive as no range must implement it. Some
ranges do not store their length explicitly, some cannot compute it
without actually exhausting the range (e.g. socket streams), and some
other ranges may be infinite.
*/
template hasLength(R)
{
enum bool hasLength = is(typeof(R.init.length) : ulong) &&
!isNarrowString!R;
}
unittest
{
static assert(hasLength!(int[]));
struct A { ulong length; }
static assert(hasLength!(A));
struct B { size_t length() { return 0; } }
static assert(!hasLength!(B));
struct C { @property size_t length() { return 0; } }
static assert(hasLength!(C));
}
/**
Returns $(D true) if $(D Range) is an infinite input range. An
infinite input range is an input range that has a statically-defined
enumerated member called $(D empty) that is always $(D false), for
example:
----
struct InfiniteRange
{
enum bool empty = false;
...
}
----
*/
template isInfinite(Range)
{
static if (isInputRange!Range && is(char[1 + Range.empty]))
enum bool isInfinite = !Range.empty;
else
enum bool isInfinite = false;
}
unittest
{
assert(!isInfinite!(int[]));
assert(isInfinite!(Repeat!(int)));
}
/**
Returns $(D true) if $(D Range) offers a slicing operator with
integral boundaries, that in turn returns an input range type. The
following code should compile for $(D hasSlicing) to be $(D true):
----
Range r;
auto s = r[1 .. 2];
static assert(isInputRange!(typeof(s)));
----
*/
template hasSlicing(Range)
{
enum bool hasSlicing = is(typeof(
{
Range r;
auto s = r[1 .. 2];
static assert(isInputRange!(typeof(s)));
}()));
}
unittest
{
static assert(hasSlicing!(int[]));
struct A { int opSlice(uint, uint); }
static assert(!hasSlicing!(A));
struct B { int[] opSlice(uint, uint); }
static assert(hasSlicing!(B));
}
/**
This is a best-effort implementation of $(D length) for any kind of
range.
If $(D hasLength!(Range)), simply returns $(D range.length) without
checking $(D upTo).
Otherwise, walks the range through its length and returns the number
of elements seen. Performes $(BIGOH n) evaluations of $(D range.empty)
and $(D range.popFront), where $(D n) is the effective length of $(D
range). The $(D upTo) parameter is useful to "cut the losses" in case
the interest is in seeing whether the range has at least some number
of elements. If the parameter $(D upTo) is specified, stops if $(D
upTo) steps have been taken and returns $(D upTo).
*/
size_t walkLength(Range)(Range range, size_t upTo = size_t.max)
if (isInputRange!(Range))
{
static if (isRandomAccessRange!Range && hasLength!Range)
{
return range.length;
}
else
{
size_t result;
for (; result < upTo && !range.empty; range.popFront) ++result;
return result;
}
}
unittest
{
int[] a = [ 1, 2, 3 ];
assert(walkLength(a) == 3);
assert(walkLength(a, 0) == 3);
}
private template isRetro(R)
{
static if (is(R R1 == Retro!R2, R2))
{
enum isRetro = true;
}
else
{
enum isRetro = false;
}
}
/**
Iterates a bidirectional range backwards.
Example:
----
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(retro(a) == [ 5, 4, 3, 2, 1 ][]));
----
*/
struct Retro(R) if (isBidirectionalRange!(R) && !isRetro!R)
{
private:
R _input;
enum bool byRef = is(typeof(&(R.init.front())));
public:
alias R Source;
/**
Forwards to $(D _input.empty).
*/
bool empty()
{
return _input.empty;
}
/**
Returns a copy of $(D this).
*/
@property Retro save()
{
return Retro(_input.save);
}
/**
Forwards to $(D _input.popBack).
*/
void popFront()
{
_input.popBack;
}
/**
Forwards to $(D _input.popFront).
*/
void popBack()
{
_input.popFront;
}
/// @@@UGLY@@@
/**
Forwards to $(D _input.back).
*/
mixin(
(byRef ? "ref " : "")~
q{ElementType!(R) front()
{
return _input.back;
}
});
/**
Forwards to $(D _input.front).
*/
mixin(
(byRef ? "ref " : "")~
q{ElementType!(R) back()
{
return _input.front;
}
});
/**
Forwards to $(D _input[_input.length - n + 1]). Defined only if $(D R)
is a random access range and if $(D R) defines $(D R.length).
*/
static if (isRandomAccessRange!(R) && hasLength!(R))
static if (byRef)
auto ref ElementType!R opIndex(uint n)
{
return _input[_input.length - n - 1];
}
else
ElementType!R opIndex(uint n)
{
return _input[_input.length - n - 1];
}
/**
Range primitive operation that returns the length of the
range. Forwards to $(D _input.length) and is defined only if $(D
hasLength!(R)).
*/
static if (hasLength!R || isNarrowString!R)
@property size_t length()
{
return _input.length;
}
}
template Retro(R) if (isRetro!R)
{
alias R.Source Retro;
}
/// Ditto
Retro!(R) retro(R)(R input) if (isBidirectionalRange!(R))
{
static if (isRetro!R)
return input._input;
else
return Retro!(R)(input);
}
unittest
{
int[] a;
static assert(is(typeof(a) == typeof(retro(retro(a)))));
static assert(isRandomAccessRange!(Retro!(int[])));
void test(int[] input, int[] witness)
{
auto r = retro(input);
assert(r.front == witness.front);
assert(r.back == witness.back);
assert(equal(r, witness));
}
test([ 1 ], [ 1 ]);
test([ 1, 2 ], [ 2, 1 ]);
test([ 1, 2, 3 ], [ 3, 2, 1 ]);
test([ 1, 2, 3, 4 ], [ 4, 3, 2, 1 ]);
test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]);
test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]);
}
/**
Iterates range $(D r) with stride $(D n). If the range is a
random-access range, moves by indexing into the range; otehrwise,
moves by successive calls to $(D popFront).
Example:
----
int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
assert(equal(stride(a, 3) == [ 1, 4, 7, 10 ][]));
----
*/
struct Stride(R) if (isInputRange!(R))
{
private:
R _input;
size_t _n;
public:
/**
Initializes the stride.
*/
this(R input, size_t n)
{
_input = input;
_n = n;
static if (hasLength!(R))
{
auto slack = _input.length % _n;
if (slack) slack--;
if (!slack) return;
static if (isRandomAccessRange!(R) && hasSlicing!(R))
{
_input = _input[0 .. _input.length - slack];
}
else
{
foreach (i; 0 .. slack)
{
if (_input.empty) break;
_input.popBack;
}
}
}
}
/**
Returns $(D this).
*/
@property Stride save()
{
return Stride(_input.save, _n);
}
/**
Forwards to $(D _input.empty).
*/
bool empty()
{
return _input.empty;
}
/**
@@@
*/
void popFront()
{
static if (isRandomAccessRange!(R) && hasLength!(R) && hasSlicing!(R))
{
_input = _input[
_n < _input.length ? _n : _input.length
.. _input.length];
}
else
foreach (i; 0 .. _n)
{
_input.popFront;
if (_input.empty) break;
}
}
/**
Forwards to $(D _input.popFront).
*/
static if (hasLength!(R))
void popBack()
{
enforce(_input.length >= _n);
static if (isRandomAccessRange!(R) && hasSlicing!(R))
{
_input = _input[0 .. _input.length - _n];
}
else
{
foreach (i; 0 .. _n)
{
if (_input.empty) break;
_input.popBack;
}
}
}
/**
Forwards to $(D _input.front).
*/
ref ElementType!(R) front()
{
return _input.front;
}
/**
Forwards to $(D _input.back) after getting rid of any slack items.
*/
ref ElementType!(R) back()
{
return _input.back;
}
/**
Forwards to $(D _input[_input.length - n + 1]). Defined only if $(D R)
is a random access range and if $(D R) defines $(D R.length).
*/
static if (isRandomAccessRange!(R) && hasLength!(R))
ref ElementType!(R) opIndex(uint n)
{
return _input[_n * n];
}
/**
Range primitive operation that returns the length of the
range. Forwards to $(D _input.length) and is defined only if $(D
hasLength!(R)).
*/
static if (hasLength!(R))
@property size_t length()
{
return (_input.length + _n - 1) / _n;
}
}
/// Ditto
Stride!(R) stride(R)(R input, size_t n)
if (isInputRange!(R))
{
enforce(n > 0);
return Stride!(R)(input, n);
}
unittest
{
static assert(isRandomAccessRange!(Stride!(int[])));
void test(size_t n, int[] input, int[] witness)
{
assert(equal(stride(input, n), witness));
}
test(1, [], []);
int[] arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
test(1, arr, arr);
test(2, arr, [1, 3, 5, 7, 9]);
test(3, arr, [1, 4, 7, 10]);
test(4, arr, [1, 5, 9]);
}
/**
Spans multiple ranges in sequence. The function $(D chain) takes any
number of ranges and returns a $(D Chain!(R1, R2,...)) object. The
ranges may be different, but they must have the same element type. The
result is a range that offers the $(D front), $(D popFront), and $(D empty)
primitives. If all input ranges offer random access and $(D length),
$(D Chain) offers them as well.
If only one range is offered to $(D Chain) or $(D chain), the $(D Chain)
type exits the picture by aliasing itself directly to that range's
type.
Example:
----
int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
int[] arr3 = [ 7 ];
auto s = chain(arr1, arr2, arr3);
assert(s.length == 7);
assert(s[5] == 6);
assert(equal(s, [1, 2, 3, 4, 5, 6, 7][]));
----
*/
template Chain(R...) if (allSatisfy!(isInputRange, R))
{
static if (R.length > 1)
alias ChainImpl!(R) Chain;
else
alias R[0] Chain;
}
struct ChainImpl(R...)
{
private:
alias CommonType!(staticMap!(.ElementType, R)) RvalueElementType;
template sameET(A)
{
enum sameET = is(.ElementType!(A) == RvalueElementType);
}
enum bool allSameType = allSatisfy!(sameET, R);
Tuple!(R) _input;
public:
// This doesn't work yet
static if (allSameType)
alias ref RvalueElementType ElementType;
else
alias RvalueElementType ElementType;
this(R input)
{
foreach (i, v; input)
{
_input.field[i] = v;
}
}
bool empty()
{
foreach (i, Unused; R)
{
if (!_input.field[i].empty) return false;
}
return true;
}
static if (allSatisfy!(isForwardRange, R))
ChainImpl save()
{
auto result = ChainImpl();
foreach (i, Unused; R)
{
result._input.field[i] = _input.field[i].save;
}
return result;
}
void popFront()
{
foreach (i, Unused; R)
{
if (_input.field[i].empty) continue;
_input.field[i].popFront;
return;
}
}
@property RvalueElementType front()
{
foreach (i, Unused; R)
{
if (_input.field[i].empty) continue;
return _input.field[i].front;
}
assert(false);
}
static if (allSatisfy!(hasAssignableElements, R))
{
// @@@BUG@@@
//@property void front(T)(T v) if (is(T : RvalueElementType))
@property void front(RvalueElementType v)
{
foreach (i, Unused; R)
{
if (_input.field[i].empty) continue;
_input.field[i].front = v;
return;
}
assert(false);
}
}
RvalueElementType moveFront()
{
foreach (i, Unused; R)
{
if (_input.field[i].empty) continue;
return .moveFront(_input.field[i]);
}
assert(false);
}
static if (allSatisfy!(isBidirectionalRange, R))
{
mixin(
((allSameType && allSatisfy!(hasAssignableElements, R)) ? "ref " : "")~
q{ElementType back()
{
foreach_reverse (i, Unused; R)
{
if (_input.field[i].empty) continue;
return _input.field[i].back;
}
assert(false);
}
});
void popBack()
{
foreach_reverse (i, Unused; R)
{
if (_input.field[i].empty) continue;
_input.field[i].popBack;
return;
}
}
}
static if (allSatisfy!(hasLength, R))
@property size_t length()
{
size_t result;
foreach (i, Unused; R)
{
result += _input.field[i].length;
}
return result;
}
static if (allSatisfy!(isRandomAccessRange, R))
{
mixin(
((allSameType && allSatisfy!(hasAssignableElements, R)) ? "ref " : "")~
q{ElementType opIndex(uint index)
{
foreach (i, Unused; R)
{
immutable length = _input.field[i].length;
if (index < length) return _input.field[i][index];
index -= length;
}
assert(false);
}
});
static if (allSameType && allSatisfy!(hasAssignableElements, R))
void opIndexAssign(ElementType v, uint index)
{
foreach (i, Unused; R)
{
immutable length = _input.field[i].length;
if (index < length)
{
_input.field[i][index] = v;
return;
}
index -= length;
}
assert(false);
}
}
static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R))
ChainImpl opSlice(size_t begin, size_t end)
{
auto result = this;
foreach (i, Unused; R)
{
immutable len = result._input.field[i].length;
if (len < begin)
{
result._input.field[i] = result._input.field[i]
[len .. len];
begin -= len;
}
else
{
result._input.field[i] = result._input.field[i]
[begin .. len];
break;
}
}
auto cut = length;
cut = cut <= end ? 0 : cut - end;
foreach_reverse (i, Unused; R)
{
immutable len = result._input.field[i].length;
if (cut > len)
{
result._input.field[i] = result._input.field[i]
[0 .. 0];
cut -= len;
}
else
{
result._input.field[i] = result._input.field[i]
[0 .. len - cut];
break;
}
}
return result;
}
}
/// Ditto
Chain!(R) chain(R...)(R input)
{
static if (input.length > 1)
return Chain!(R)(input);
else
return input[0];
}
unittest
{
{
int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
int[] arr3 = [ 7 ];
int[] witness = [ 1, 2, 3, 4, 5, 6, 7 ];
auto s1 = chain(arr1);
static assert(isRandomAccessRange!(typeof(s1)));
auto s2 = chain(arr1, arr2);
static assert(isBidirectionalRange!(typeof(s2)));
static assert(isRandomAccessRange!(typeof(s2)));
s2.front = 1;
auto s = chain(arr1, arr2, arr3);
assert(s[5] == 6);
assert(equal(s, witness));
assert(s[5] == 6);
}
{
int[] arr1 = [ 1, 2, 3, 4 ];
int[] witness = [ 1, 2, 3, 4 ];
assert(equal(chain(arr1), witness));
}
{
uint[] foo = [1,2,3,4,5];
uint[] bar = [1,2,3,4,5];
auto c = chain(foo, bar);
c[3] = 42;
assert(c[3] == 42);
}
// Make sure bug 3311 is fixed. ChainImpl should compile even if not all
// elements are mutable.
auto c = chain( iota(0, 10), iota(0, 10) );
}
/**
Iterates a random-access range starting from a given point and
progressively extending left and right from that point. If no initial
point is given, iteration starts from the middle of the
range. Iteration spans the entire range.
Example:
----
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(radial(a) == [ 3, 2, 4, 1, 5 ][]));
a = [ 1, 2, 3, 4 ];
assert(equal(radial(a) == [ 2, 3, 1, 4 ][]));
----
*/
struct Radial(R) if (isRandomAccessRange!(R) && hasLength!(R))
{
private:
R _low, _up;
bool _upIsActive;
public:
/**
Takes a range and starts iterating from its median point. Ranges with
an even length start iterating from the element to the left of the
median. The second iterated element, if any, is the one to the right
of the first iterated element. A convenient way to use this
constructor is by calling the helper function $(D radial(input)).
*/
this(R input)
{
auto mid = (input.length + 1) / 2;
_low = input[0 .. mid];
_up = input[mid .. $];
}
/**
Takes a range and starts iterating from $(D input[mid]). The second
iterated element, if any, is the one to the right of the first
iterated element. If there is no element to the right of $(D
input[mid]), iteration continues downwards with $(D input[mid - 1])
etc. A convenient way to use this constructor is by calling the helper
function $(D radial(input, startingPoint)).
*/
this(R input, size_t startingPoint)
{
_low = input[0 .. startingPoint + 1];
_up = input[startingPoint + 1 .. $];
if (_low.empty) _upIsActive = true;
}
/**
Returns $(D this).
*/
ref Radial opSlice()
{
return this;
}
/**
Range primitive operation that returns $(D true) iff there are no more
elements to be iterated.
*/
bool empty()
{
return _low.empty && _up.empty;
}
/**
Range primitive operation that advances the range to its next
element.
*/
void popFront()
{
assert(!empty);
// We started with low active
if (!_upIsActive)
{
// Consumed the low part, now look in the upper part
if (_up.empty)
{
// no more stuff up, attempt to continue in the low area
_low.popBack;
}
else
{
// more stuff available in the upper area
_upIsActive = true;
}
}
else
{
// we consumed both the lower and the upper area, must
// make real progress up there
if (!_up.empty) _up.popFront;
if (!_low.empty) _low.popBack;
if (!_low.empty) _upIsActive = false;
}
}
/**
Range primitive operation that returns the currently iterated
element. Throws if the range is empty.
*/
ref ElementType!(R) front()
{
enforce(!empty, "Calling front() against an empty "
~typeof(this).stringof);
if (!_upIsActive)
{
// @@@ Damndest thing... removing the enforce below causes
// a segfault in release unittest
enforce(!_low.empty);
return _low.back;
}
enforce(!_up.empty);
return _up.front;
}
}
/// Ditto
Radial!(R) radial(R)(R r)
if (isRandomAccessRange!(R) && hasLength!(R))
{
return Radial!(R)(r);
}
/// Ditto
Radial!(R) radial(R)(R r, size_t startingIndex)
if (isRandomAccessRange!(R) && hasLength!(R))
{
return Radial!(R)(r, startingIndex);
}
unittest
{
void test(int[] input, int[] witness)
{
enforce(equal(radial(input), witness));
}
test([], []);
test([ 1 ], [ 1 ]);
test([ 1, 2 ], [ 1, 2 ]);
test([ 1, 2, 3 ], [ 2, 3, 1 ]);
test([ 1, 2, 3, 4 ], [ 2, 3, 1, 4 ]);
test([ 1, 2, 3, 4, 5 ], [ 3, 4, 2, 5, 1 ]);
test([ 1, 2, 3, 4, 5, 6 ], [ 3, 4, 2, 5, 1, 6 ]);
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(radial(a, 1), [ 2, 3, 1, 4, 5 ][]));
}
/**
Lazily takes only up to $(D n) elements of a range. This is
particulary useful when using with infinite ranges. If the range
offers random access and $(D length), $(D Take) offers them as well.
Example:
----
int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
auto s = take(arr1, 5);
assert(s.length == 5);
assert(s[4] == 5);
assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
----
*/
struct Take(R) if (isInputRange!(R))
{
R original;
private size_t _maxAvailable;
enum bool byRef = is(typeof(&_input.front) == ElementType!(R)*);
public:
alias R Source;
static if (byRef)
alias ref .ElementType!(R) ElementType;
else
alias .ElementType!(R) ElementType;
bool empty()
{
return _maxAvailable == 0 || original.empty;
}
static if (isForwardRange!R)
@property Take save()
{
return Take(original.save, _maxAvailable);
}
void popFront()
{
enforce(_maxAvailable > 0,
"Attempting to popFront() past the end of a "
~ Take.stringof);
original.popFront;
--_maxAvailable;
}
@property ElementType front()
{
enforce(_maxAvailable > 0,
"Attempting to fetch the front of an empty "
~ Take.stringof);
return original.front;
}
static if (hasAssignableElements!R)
@property void front(ElementType v)
{
original.front = v;
}
ElementType moveFront()
{
return .moveFront(original);
}
static if (isInfinite!(R))
{
@property size_t length() const
{
return _maxAvailable;
}
}
else static if (hasLength!(R))
{
@property size_t length()
{
return min(_maxAvailable, original.length);
}
}
static if (isRandomAccessRange!(R) && (hasLength!(R) || isInfinite!(R)))
{
void popBack()
{
enforce(_maxAvailable > 0,
"Attempting to popBack() past the beginning of a "
~ Take.stringof);
--_maxAvailable;
}
mixin(
(byRef ? "ref " : "")~
q{/+auto ref+/ ElementType back()
{
return original[this.length - 1];
}});
}
static if (isRandomAccessRange!(R))
{
mixin(
(byRef ? "ref " : "")~
q{/+auto ref+/ ElementType opIndex(size_t index)
{
enforce(index < this.length,
"Attempting to index out of the bounds of a "
~ Take.stringof);
return original[index];
}});
}
Take opSlice() { return this; }
@property size_t maxLength() const
{
return _maxAvailable;
}
}
/// Ditto
Take!(R) take(R)(R input, size_t n) if (isInputRange!R && !hasSlicing!R)
{
return Take!(R)(input, n);
}
/// Ditto
auto take(R)(R input, size_t n) if (isInputRange!R && hasSlicing!R)
{
static if (hasLength!R)
{
// @@@BUG@@@
//return input[0 .. min(n, @)];
return input[0 .. min(n, input.length)];
}
else
{
static assert(isInfinite!R,
"Nonsensical finite range with slicing but no length");
return input[0 .. n];
}
}
unittest
{
int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
auto s = take(arr1, 5);
assert(s.length == 5);
assert(s[4] == 5);
assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
assert(equal(retro(s), [ 5, 4, 3, 2, 1 ][]));
}
/**
Eagerly advances $(D r) itself (not a copy) $(D n) times (by calling
$(D r.popFront) $(D n) times). The pass of $(D r) into $(D popFrontN)
is by reference, so the original range is affected. Completes in
$(BIGOH 1) steps for ranges that support slicing, and in $(BIGOH n)
time for all other ranges.
Example:
----
int[] a = [ 1, 2, 3, 4, 5 ];
a.popFrontN(2);
assert(a == [ 3, 4, 5 ]);
----
*/
size_t popFrontN(Range)(ref Range r, size_t n) if (isInputRange!(Range))
{
static if (hasSlicing!(Range) && hasLength!(Range))
{
n = min(n, r.length);
r = r[n .. r.length];
}
else
{
foreach (i; 0 .. n)
{
if (r.empty) return i;
r.popFront;
}
}
return n;
}
unittest
{
int[] a = [ 1, 2, 3, 4, 5 ];
a.popFrontN(2);
assert(a == [ 3, 4, 5 ]);
}
/**
Eagerly reduces $(D r) itself (not a copy) $(D n) times from its right
side (by calling $(D r.popBack) $(D n) times). The pass of $(D r) into
$(D popBackN) is by reference, so the original range is
affected. Completes in $(BIGOH 1) steps for ranges that support
slicing, and in $(BIGOH n) time for all other ranges.
Example:
----
int[] a = [ 1, 2, 3, 4, 5 ];
a.popBackN(2);
assert(a == [ 1, 2, 3 ]);
----
*/
size_t popBackN(Range)(ref Range r, size_t n) if (isInputRange!(Range))
{
static if (hasSlicing!(Range) && hasLength!(Range))
{
auto newLen = n < r.length ? r.length - n : 0;
n = r.length - newLen;
r = r[0 .. newLen];
}
else
{
foreach (i; 0 .. n)
{
if (r.empty) return i;
r.popBack;
}
}
return n;
}
unittest
{
int[] a = [ 1, 2, 3, 4, 5 ];
a.popBackN(2);
assert(a == [ 1, 2, 3 ]);
}
/**
Repeats one value forever. Example:
----
enforce(equal(take(repeat(5), 4), [ 5, 5, 5, 5 ][]));
----
*/
struct Repeat(T)
{
private T _value;
/// Range primitive implementations.
@property ref T front() { return _value; }
/// Ditto
@property ref T back() { return _value; }
/// Ditto
enum bool empty = false;
/// Ditto
void popFront() {}
/// Ditto
void popBack() {}
/// Ditto
ref T opIndex(uint) { return _value; }
}
/// Ditto
Repeat!(T) repeat(T)(T value) { return Repeat!(T)(value); }
unittest
{
enforce(equal(take(repeat(5), 4), [ 5, 5, 5, 5 ][]));
}
/**
Replicates $(D value) exactly $(D n) times. Equivalent to $(D
take(repeat(value), n)).
*/
Take!(Repeat!(T)) replicate(T)(T value, size_t n)
{
return take(repeat(value), n);
}
unittest
{
enforce(equal(replicate(5, 4), [ 5, 5, 5, 5 ][]));
}
/**
Repeats the given forward range ad infinitum. If the original range is
infinite (fact that would make $(D Cycle) the identity application),
$(D Cycle) detects that and aliases itself to the range type
itself. If the original range has random access, $(D Cycle) offers
random access and also offers a constructor taking an initial position
$(D index). $(D Cycle) is specialized for statically-sized arrays,
mostly for performance reasons.
Example:
----
assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][]));
----
Tip: This is a great way to implement simple circular buffers.
*/
struct Cycle(R) if (isForwardRange!(R))
{
static if (isInfinite!(R))
{
alias R Cycle;
}
else static if (isRandomAccessRange!(R) && hasLength!(R))
{
R _original;
size_t _index;
this(R input, size_t index = 0) { _original = input; _index = index; }
/// Range primitive implementations.
ref ElementType!(R) front()
{
return _original[_index % _original.length];
}
/// Ditto
enum bool empty = false;
/// Ditto
void popFront() { ++_index; }
ref ElementType!(R) opIndex(size_t n)
{
return _original[(n + _index) % _original.length];
}
}
else
{
R _original, _current;
this(R input) { _original = input; _current = input.save; }
/// Range primitive implementations.
ref ElementType!(R) front() { return _current.front; }
/// Ditto
static if (isBidirectionalRange!(R))
ref ElementType!(R) back() { return _current.back; }
/// Ditto
enum bool empty = false;
/// Ditto
void popFront()
{
_current.popFront;
if (_current.empty) _current = _original;
}
}
}
/// Ditto
struct Cycle(R) if (isStaticArray!(R))
{
private alias typeof(R[0]) ElementType;
private ElementType* _ptr;
private size_t _index;
this(ref R input, size_t index = 0)
{
_ptr = input.ptr;
_index = index;
}
/// Range primitive implementations.
ref ElementType front()
{
return _ptr[_index % R.length];
}
/// Ditto
enum bool empty = false;
/// Ditto
void popFront() { ++_index; }
ref ElementType opIndex(size_t n)
{
return _ptr[(n + _index) % R.length];
}
}
/// Ditto
Cycle!(R) cycle(R)(R input) if (isForwardRange!(R))
{
return Cycle!(R)(input);
}
/// Ditto
Cycle!(R) cycle(R)(R input, size_t index) if (isRandomAccessRange!(R))
{
return Cycle!(R)(input, index);
}
/// Ditto
Cycle!(R) cycle(R)(ref R input, size_t index = 0) if (isStaticArray!R)
{
return Cycle!(R)(input, index);
}
unittest
{
assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][]));
int[3] a = [ 1, 2, 3 ];
static assert(isStaticArray!(typeof(a)));
auto c = cycle(a);
assert(a.ptr == c._ptr);
assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][]));
}
/**
Iterate several ranges in lockstep. The element type is a proxy tuple
that allows accessing the current element in the $(D n)th range by
using $(D e.at!(n)).
Example:
----
int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "b", "c" ];
// prints 1:a 2:b 3:c
foreach (e; zip(a, b))
{
write(e.at!(0), ':', e.at!(1), ' ');
}
----
$(D Zip) offers the lowest range facilities of all components, e.g. it
offers random access iff all ranges offer random access, and also
offers mutation and swapping if all ranges offer it. Due to this, $(D
Zip) is extremely powerful because it allows manipulating several
ranges in lockstep. For example, the following code sorts two arrays
in parallel:
----
int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "b", "c" ];
sort!("a.at!(0) > b.at!(0)")(zip(a, b));
assert(a == [ 3, 2, 1 ]);
assert(b == [ "c", "b", "a" ]);
----
*/
struct Zip(R...) if (R.length && allSatisfy!(isInputRange, R))
{
Tuple!(R) ranges;
StoppingPolicy stoppingPolicy = StoppingPolicy.shortest;
/**
Builds an object. Usually this is invoked indirectly by using the
$(XREF range,zip) function.
*/
this(R rs, StoppingPolicy s = StoppingPolicy.shortest)
{
stoppingPolicy = s;
foreach (i, Unused; R)
{
ranges.field[i] = rs[i];
}
}
/**
Returns $(D true) if the range is at end. The test depends on the
stopping policy.
*/
bool empty()
{
bool firstRangeIsEmpty = ranges.field[0].empty;
if (firstRangeIsEmpty && stoppingPolicy == StoppingPolicy.shortest)
return true;
foreach (i, Unused; R[1 .. $])
{
switch (stoppingPolicy)
{
case StoppingPolicy.shortest:
if (ranges.field[i + 1].empty) return true;
break;
case StoppingPolicy.longest:
if (!ranges.field[i + 1].empty) return false;
break;
default:
assert(stoppingPolicy == StoppingPolicy.requireSameLength);
enforce(firstRangeIsEmpty == ranges.field[i + 1].empty,
"Inequal-length ranges passed to Zip");
break;
}
}
return firstRangeIsEmpty;
}
static if (allSatisfy!(isForwardRange, R))
@property Zip save()
{
Zip result;
result.stoppingPolicy = stoppingPolicy;
foreach (i, Unused; R)
{
result.ranges.field[i] = ranges.field[i].save;
}
return result;
}
/**
Returns a proxy for the current iterated element.
*/
Proxy front()
{
Proxy result;
foreach (i, Unused; R)
{
result.ptrs.field[i] = &ranges.field[i].front;
}
return result;
}
/**
Returns a proxy for the rightmost element.
*/
Proxy back()
{
Proxy result;
foreach (i, Unused; R)
{
result.ptrs.field[i] = &ranges.field[i].back;
}
return result;
}
/**
Advances to the popFront element in all controlled ranges.
*/
void popFront()
{
foreach (i, Unused; R)
{
ranges.field[i].popFront;
}
}
/**
Calls $(D popBack) for all controlled ranges.
*/
void popBack()
{
foreach (i, Unused; R)
{
ranges.field[i].popBack;
}
}
/**
Returns the length of this range. Defined only if all ranges define
$(D length).
*/
static if (allSatisfy!(hasLength, R))
@property size_t length()
{
auto result = ranges.field[0].length;
if (stoppingPolicy == StoppingPolicy.requireSameLength)
return result;
foreach (i, Unused; R[1 .. $])
{
if (stoppingPolicy == StoppingPolicy.shortest)
{
result = min(ranges.field[i + 1].length, result);
}
else
{
assert(stoppingPolicy == StoppingPolicy.longest);
result = max(ranges.field[i + 1].length, result);
}
}
return result;
}
/**
Returns a slice of the range. Defined only if all range define
slicing.
*/
static if (allSatisfy!(hasSlicing, R))
Zip!(R) opSlice(size_t from, size_t to)
{
Zip!(R) result;
foreach (i, Unused; R)
{
result.ranges.field[i] = ranges.field[i][from .. to];
}
return result;
}
/**
Proxy type returned by the access function.
*/
struct Proxy
{
template ElemPtr(Range)
{
alias std.range.ElementType!(Range)* ElemPtr;
}
Tuple!(staticMap!(ElemPtr, R)) ptrs;
/**
Returns the current element in the $(D i)th range.
*/
/*ref*/ std.range.ElementType!(R[i]) at(int i)()
{
return *ptrs.field[i];
}
/**
Returns whether the current element exists in the $(D i)th range. This
function returns $(D false) if e.g. one of the ranges has exhausted in
the $(D StoppingPolicy.longest) policy.
*/
bool hasAt(int i)()
{
return *ptrs.field[i];
}
void proxySwap(Proxy rhs)
{
foreach (i, Unused; R)
{
.swap(*ptrs.field[i], *rhs.ptrs.field[i]);
}
}
}
/**
Returns the $(D n)th element in the composite range. Defined if all
ranges offer random access.
*/
static if (allSatisfy!(isRandomAccessRange, R))
Proxy opIndex(size_t n)
{
Proxy result;
foreach (i, Unused; R)
{
result.ptrs.field[i] = &ranges.field[i][n];
}
return result;
}
}
/// Ditto
Zip!(R) zip(R...)(R ranges)
//if (allSatisfy!(isInputRange, R))
{
return Zip!(R)(ranges);
}
/// Ditto
Zip!(R) zip(R...)(StoppingPolicy sp, R ranges)
//if (allSatisfy!(isInputRange, R))
{
return Zip!(R)(ranges, sp);
}
/**
Dictates how iteration in a $(D Zip) should stop. By default stop at
the end of the shortest of all ranges.
*/
enum StoppingPolicy
{
/// Stop when the shortest range is exhausted
shortest,
/// Stop when the longest range is exhausted
longest,
/// Require that all ranges are equal
requireSameLength,
}
unittest
{
int[] a = [ 1, 2, 3 ];
float[] b = [ 1., 2, 3 ];
foreach (e; zip(a, b))
{
assert(e.at!(0) == e.at!(1));
}
auto z = zip(a, b);
swap(z.front(), z.back());
//@@@BUG@@@
//sort!("a.at!(0) < b.at!(0)")(zip(a, b));
}
/**
Creates a mathematical sequence given the initial values and a
recurrence function that computes the popFront value from the existing
values. The sequence comes in the form of an infinite forward
range. The type $(D Recurrence) itself is seldom used directly; most
often, recurrences are obtained by calling the function $(D
recurrence).
When calling $(D recurrence), the function that computes the next
value is specified as a template argument, and the initial values in
the recurrence are passed as regular arguments. For example, in a
Fibonacci sequence, there are two initial values (and therefore a
state size of 2) because computing the popFront Fibonacci value needs the
past two values.
If the function is passed in string form, the state has name $(D "a")
and the zero-based index in the recurrence has name $(D "n"). The
given string must return the desired value for $(D a[n]) given $(D a[n
- 1]), $(D a[n - 2]), $(D a[n - 3]),..., $(D a[n - stateSize]). The
state size is dictated by the number of arguments passed to the call
to $(D recurrence). The $(D Recurrence) class itself takes care of
managing the recurrence's state and shifting it appropriately.
Example:
----
// a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n]
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
// print the first 10 Fibonacci numbers
foreach (e; take(fib, 10)) { writeln(e); }
// print the first 10 factorials
foreach (e; take(recurrence!("a[n-1] * n")(1), 10)) { writeln(e); }
----
*/
struct Recurrence(alias fun, StateType, size_t stateSize)
{
StateType[stateSize] _state;
size_t _n;
this(StateType[stateSize] initial) { _state = initial; }
void popFront()
{
_state[_n % stateSize] = binaryFun!(fun, "a", "n")(
cycle(_state, _n), _n + stateSize);
++_n;
}
StateType front()
{
return _state[_n % stateSize];
}
enum bool empty = false;
}
/// Ditto
Recurrence!(fun, CommonType!(State), State.length)
recurrence(alias fun, State...)(State initial)
{
CommonType!(State)[State.length] state;
foreach (i, Unused; State)
{
state[i] = initial[i];
}
return typeof(return)(state);
}
version(none) unittest
{
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ];
//foreach (e; take(fib, 10)) writeln(e);
assert(equal(take(fib, 10), witness));
foreach (e; take(fib, 10)) {}//writeln(e);
//writeln(s.front);
auto fact = recurrence!("n * a[n-1]")(1);
assert( equal(take(fact, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6,
2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) );
auto piapprox = recurrence!("a[n] + (n & 1 ? 4. : -4.) / (2 * n + 3)")(4.);
foreach (e; take(piapprox, 20)) {}//writeln(e);
}
/**
$(D Sequence) is similar to $(D Recurrence) except that iteration is
presented in the so-called $(WEB en.wikipedia.org/wiki/Closed_form,
closed form). This means that the $(D n)th element in the series is
computable directly from the initial values and $(D n) itself. This
implies that the interface offered by $(D Sequence) is a random-access
range, as opposed to the regular $(D Recurrence), which only offers
forward iteration.
The state of the sequence is stored as a $(D Tuple) so it can be
heterogeneous.
Example:
----
// a[0] = 1, a[1] = 2, a[n] = a[0] + n * a[1]
auto odds = sequence!("a[0] + n * a[1]")(1, 2);
----
*/
struct Sequence(alias fun, State)
{
private:
alias binaryFun!(fun, "a", "n") compute;
alias typeof(compute(State.init, 1u)) ElementType;
State _state;
size_t _n;
ElementType _cache;
public:
this(State initial, size_t n = 0)
{
this._state = initial;
this._cache = compute(this._state, this._n);
}
ElementType front()
{
//return ElementType.init;
return this._cache;
}
ElementType moveFront()
{
return move(_cache);
}
void popFront()
{
this._cache = compute(this._state, ++this._n);
}
ElementType opIndex(size_t n)
{
//return ElementType.init;
return compute(this._state, n);
}
enum bool empty = false;
@property Sequence save() { return this; }
}
/// Ditto
Sequence!(fun, Tuple!(State)) sequence
(alias fun, State...)(State args)
{
return typeof(return)(tuple(args));
}
unittest
{
// alias Sequence!("a.field[0] += a.field[1]",
// Tuple!(int, int)) Gen;
// Gen x = Gen(tuple(0, 5));
// foreach (e; take(x, 15))
// {}//writeln(e);
auto y = Sequence!("a.field[0] + n * a.field[1]", Tuple!(int, int))
(tuple(0, 4));
//@@BUG
//auto y = sequence!("a.field[0] + n * a.field[1]")(0, 4);
//foreach (e; take(y, 15))
{}//writeln(e);
}
/**
Returns a range that goes through the numbers $(D begin), $(D begin +
step), $(D begin + 2 * step), $(D ...), up to and excluding $(D
end). The range offered is a random access range. The two-arguments
version has $(D step = 1).
Example:
----
auto r = iota(0, 10, 1);
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
r = iota(0, 11, 3);
assert(equal(r, [0, 3, 6, 9][]));
assert(r[2] == 6);
auto rf = iota(0.0, 0.5, 0.1);
assert(equal(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
----
*/
Take!(Sequence!("a.field[0] + n * a.field[1]",
Tuple!(CommonType!(B, E), S)))
iota(B, E, S)(B begin, E end, S step)
if (is(typeof((E.init - B.init) + 1 * S.init)))
{
enforce(step != 0);
enforce(begin <= end && step > 0
|| begin >= end && step < 0);
// actual count must be strictly less than aBitAboveCount
immutable ebs = end - begin + step;
auto aBitAboveCount = ebs / step;
assert(aBitAboveCount >= 0);
// "less" function that is "greater" for negative step
bool myless(typeof(ebs) a, typeof(ebs) b)
{
return step > 0 ? a < b : a > b;
}
if (!myless(aBitAboveCount * step, ebs)) --aBitAboveCount;
static if (isFloatingPoint!(typeof(aBitAboveCount)))
{
enforce(aBitAboveCount <= size_t.max,
"iota: too many items in range");
auto count = cast(size_t) aBitAboveCount;
}
else
{
size_t count = aBitAboveCount;
}
if (myless(count * step, end - begin)) ++count;
assert(myless((count - 1) * step, end - begin),
text("begin=", begin, "; end=", end, "; step=", step,
"; count=", count));
assert(!myless(count * step, end - begin), text("begin=", begin,
"; end=", end, "; step=", step, "; count=", count));
return typeof(return)(typeof(return).Source(
Tuple!(CommonType!(B, E), S)(begin, step), 0u), count);
}
/// Ditto
Take!(Sequence!("a.field[0] + n * a.field[1]",
Tuple!(CommonType!(B, E), uint)))
iota(B, E)(B begin, E end)
{
return iota(begin, end, 1u);
}
/// Ditto
Take!(Sequence!("a.field[0] + n * a.field[1]",
Tuple!(E, uint)))
iota(E)(E end)
{
E begin = 0;
return iota(begin, end, 1u);
}
unittest
{
auto r = iota(0, 10, 1);
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
auto rr = iota(10);
assert(equal(rr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
r = iota(0, -10, -1);
assert(equal(r, [0, -1, -2, -3, -4, -5, -6, -7, -8, -9][]));
r = iota(0, 11, 3);
assert(equal(r, [0, 3, 6, 9][]));
assert(r[2] == 6);
int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
auto r1 = iota(a.ptr, a.ptr + a.length, 1);
assert(r1.front == a.ptr);
assert(r1.back == a.ptr + a.length - 1);
auto rf = iota(0.0, 0.5, 0.1);
//foreach (e; rf) writeln(e - 0.3);
assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4][]));
// With something just above 0.5
rf = iota(0.0, nextUp(0.5), 0.1);
//foreach (e; rf) writeln(e);
assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][]));
// going down
rf = iota(0.0, -0.5, -0.1);
//foreach (e; rf) writeln(e);
assert(approxEqual(rf, [0.0, -0.1, -0.2, -0.3, -0.4][]));
rf = iota(0.0, nextDown(-0.5), -0.1);
//foreach (e; rf) writeln(e);
assert(approxEqual(rf, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][]));
}
unittest
{
auto idx = new size_t[100];
copy(iota(0, idx.length), idx);
}
/**
Options for the $(D FrontTransversal) and $(D Transversal) ranges
(below).
*/
enum TransverseOptions
{
/**
When transversed, the elements of a range of ranges are assumed to
have different lengths (e.g. a jagged array).
*/
assumeJagged, //default
/**
The transversal enforces that the elements of a range of ranges have
all the same length (e.g. an array of arrays, all having the same
length). Checking is done once upon construction of the transversal
range.
*/
enforceNotJagged,
/**
The transversal assumes, without verifying, that the elements of a
range of ranges have all the same length. This option is useful if
checking was already done from the outside of the range.
*/
assumeNotJagged,
}
/**
Given a range of ranges, iterate transversally through the first
elements of each of the enclosed ranges.
Example:
----
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = frontTransversal(x);
assert(equals(ror, [ 1, 3 ][]));
---
*/
struct FrontTransversal(RangeOfRanges,
TransverseOptions opt = TransverseOptions.assumeJagged)
{
alias typeof(RangeOfRanges.init.front().front()) ElementType;
private void prime()
{
static if (opt == TransverseOptions.assumeJagged)
{
while (!_input.empty && _input.front.empty)
{
_input.popFront;
}
static if (isBidirectionalRange!RangeOfRanges)
{
while (!_input.empty && _input.back.empty)
{
_input.popBack;
}
}
}
}
/**
Construction from an input.
*/
this(RangeOfRanges input)
{
_input = input;
prime;
static if (opt == TransverseOptions.enforceNotJagged)
// (isRandomAccessRange!RangeOfRanges
// && hasLength!(.ElementType!RangeOfRanges))
{
if (empty) return;
immutable commonLength = _input.front.length;
foreach (e; _input)
{
enforce(e.length == commonLength);
}
}
}
/**
Forward range primitives.
*/
bool empty()
{
return _input.empty;
}
/// Ditto
ref ElementType front()
{
assert(!empty);
return _input.front.front;
}
/// Ditto
void popFront()
{
assert(!empty);
_input.popFront;
prime;
}
static if (isBidirectionalRange!RangeOfRanges)
{
/**
Bidirectional primitives. They are offered if $(D
isBidirectionalRange!RangeOfRanges).
*/
ref ElementType back()
{
return _input.back.front;
}
/// Ditto
void popBack()
{
assert(!empty);
_input.popBack;
prime;
}
}
static if (isRandomAccessRange!RangeOfRanges &&
(opt == TransverseOptions.assumeNotJagged ||
opt == TransverseOptions.enforceNotJagged))
{
/**
Random-access primitive. It is offered if $(D
isRandomAccessRange!RangeOfRanges && (opt ==
TransverseOptions.assumeNotJagged || opt ==
TransverseOptions.enforceNotJagged)).
*/
ref ElementType opIndex(size_t n)
{
return _input[n].front;
}
}
auto opSlice() { return this; }
private:
RangeOfRanges _input;
}
/// Ditto
FrontTransversal!(RangeOfRanges, opt) frontTransversal(
TransverseOptions opt = TransverseOptions.assumeJagged,
RangeOfRanges)
(RangeOfRanges rr)
{
return typeof(return)(rr);
}
/**
Given a range of ranges, iterate transversally through the the $(D
n)th element of each of the enclosed ranges. All elements of the
enclosing range must offer random access.
Example:
----
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = transversal(x, 1);
assert(equals(ror, [ 2, 4 ][]));
---
*/
struct Transversal(RangeOfRanges,
TransverseOptions opt = TransverseOptions.assumeJagged)
{
alias typeof(RangeOfRanges.init.front().front()) ElementType;
private void prime()
{
static if (opt == TransverseOptions.assumeJagged)
{
while (!_input.empty && _input.front.length <= _n)
{
_input.popFront;
}
static if (isBidirectionalRange!RangeOfRanges)
{
while (!_input.empty && _input.back.length <= _n)
{
_input.popBack;
}
}
}
}
/**
Construction from an input and an index.
*/
this(RangeOfRanges input, size_t n)
{
_input = input;
_n = n;
prime;
static if (opt == TransverseOptions.enforceNotJagged)
{
if (empty) return;
immutable commonLength = _input.front.length;
foreach (e; _input)
{
enforce(e.length == commonLength);
}
}
}
/**
Forward range primitives.
*/
bool empty()
{
return _input.empty;
}
/// Ditto
ref ElementType front()
{
assert(!empty);
return _input.front[_n];
}
/// Ditto
void popFront()
{
assert(!empty);
_input.popFront;
prime;
}
static if (isBidirectionalRange!RangeOfRanges)
{
/**
Bidirectional primitives. They are offered if $(D
isBidirectionalRange!RangeOfRanges).
*/
ref ElementType back()
{
return _input.back[_n];
}
void popBack()
{
assert(!empty);
_input.popBack;
prime;
}
}
static if (isRandomAccessRange!RangeOfRanges &&
(opt == TransverseOptions.assumeNotJagged ||
opt == TransverseOptions.enforceNotJagged))
{
/**
Random-access primitive. It is offered if $(D
isRandomAccessRange!RangeOfRanges && (opt ==
TransverseOptions.assumeNotJagged || opt ==
TransverseOptions.enforceNotJagged)).
*/
ref ElementType opIndex(size_t n)
{
return _input[n][_n];
}
}
auto opSlice() { return this; }
private:
RangeOfRanges _input;
size_t _n;
}
/// Ditto
Transversal!(RangeOfRanges, opt) transversal
(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)
(RangeOfRanges rr, size_t n)
{
return typeof(return)(rr, n);
}
unittest
{
int[][] x = new int[][2];
x[0] = [ 1, 2 ];
x[1] = [3, 4];
auto ror = transversal(x, 1);
auto witness = [ 2, 4 ];
uint i;
foreach (e; ror) assert(e == witness[i++]);
assert(i == 2);
}
struct Transposed(RangeOfRanges)
{
alias typeof(map!"a.front"(RangeOfRanges.init)) ElementType;
this(RangeOfRanges input)
{
this._input = input;
}
ElementType front()
{
return map!"a.front"(_input);
}
void popFront()
{
foreach (ref e; _input)
{
if (e.empty) continue;
e.popFront;
}
}
// ElementType opIndex(size_t n)
// {
// return _input[n].front;
// }
bool empty()
{
foreach (e; _input)
if (!e.empty) return false;
return true;
}
@property Transposed save()
{
return Transposed(_input.save);
}
auto opSlice() { return this; }
private:
RangeOfRanges _input;
}
auto transposed(RangeOfRanges)(RangeOfRanges rr)
{
return Transposed!RangeOfRanges(rr);
}
unittest
{
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto tr = transposed(x);
int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ];
uint i;
foreach (e; tr)
{
assert(array(e) == witness[i++]);
}
}
/**
Moves the front of $(D r) out and returns it. Leaves $(D r.front) in a
destroyable state that does not allocate any resources (usually equal
to its $(D .init) value).
*/
ElementType!R moveFront(R)(R r)
if (is(typeof(&r.front()) == ElementType!R*))
{
return move(r.front);
}
unittest
{
struct R
{
ref int front() { static int x = 42; return x; }
}
R r;
assert(moveFront(r) == 42);
}
/// Ditto
ElementType!R moveFront(R)(R r)
if (is(typeof(&R.moveFront)))
{
return r.moveFront();
}
/**
Moves the front of $(D r) out and returns it. Leaves $(D r.front) in a
destroyable state that does not allocate any resources (usually equal
to its $(D .init) value).
*/
ElementType!R moveBack(R)(R r)
if (isInputRange!R && is(typeof(&r.front) == ElementType!R*))
{
return move(r.back);
}
/// Ditto
ElementType!R moveBack(R)(R r)
if (isInputRange!R && is(typeof(&R.moveBack)))
{
return r.moveBack();
}
/**
Moves element at index $(D i) of $(D r) out and returns it. Leaves $(D
r.front) in a destroyable state that does not allocate any resources
(usually equal to its $(D .init) value).
*/
ElementType!R moveAt(R)(R r, size_t i)
if (is(typeof(&r[i]) == ElementType!R*))
{
return move(r[i]);
}
/// Ditto
ElementType!R moveAt(R)(R r, size_t i)
if (is(typeof(&R.moveAt)))
{
return r.moveAt(i);
}
unittest
{
auto a = [ 1, 2, 3 ];
assert(moveFront(a) == 1);
// define a perfunctory input range
struct InputRange
{
@property bool empty() { return false; }
@property int front() { return 42; }
void popFront() {}
int moveFront() { return 43; }
}
InputRange r;
assert(moveFront(r) == 43);
}