Report a bug If you spot a problem with this page, click here to create a Bugzilla issue. Improve this page Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using local clone.

std.range

This module defines the notion of a range. Ranges generalize the concept of arrays, lists, or anything that involves sequential access. This abstraction enables the same set of algorithms (see std.algorithm) to be used with a vast variety of different concrete types. For example, a linear search algorithm such as std.algorithm.find works not just for arrays, but for linked-lists, input files, incoming network data, etc.
For more detailed information about the conceptual aspect of ranges and the motivation behind them, see Andrei Alexandrescu's article On Iteration.

Submodules: This module has two submodules:

The std.range.primitives submodule provides basic range functionality. It defines several templates for testing whether a given object is a range, what kind of range it is, and provides some common range operations.

The std.range.interfaces submodule provides object-based interfaces for working with ranges via runtime polymorphism.

The remainder of this module provides a rich set of range creation and composition templates that let you construct new ranges out of existing ranges:

chain Concatenates several ranges into a single range.
choose Chooses one of two ranges at runtime based on a boolean condition.
chooseAmong Chooses one of several ranges at runtime based on an index.
chunks Creates a range that returns fixed-size chunks of the original range.
cycle Creates an infinite range that repeats the given forward range indefinitely. Good for implementing circular buffers.
drop Creates the range that results from discarding the first n elements from the given range.
dropExactly Creates the range that results from discarding exactly n of the first elements from the given range.
dropOne Creates the range that results from discarding the first elements from the given range.
enumerate Iterates a range with an attached index variable.
frontTransversal Creates a range that iterates over the first elements of the given ranges.
indexed Creates a range that offers a view of a given range as though its elements were reordered according to a given range of indices.
iota Creates a range consisting of numbers between a starting point and ending point, spaced apart by a given interval.
lockstep Iterates n ranges in lockstep, for use in a foreach loop. Similar to zip, except that lockstep is designed especially for foreach loops.
NullSink An output range that discards the data it receives.
only Creates a range that iterates over the given arguments.
radial Given a random-access range and a starting point, creates a range that alternately returns the next left and next right element to the starting point.
recurrence Creates a forward range whose values are defined by a mathematical recurrence relation.
repeat Creates a range that consists of a single element repeated n times, or an infinite range repeating that element indefinitely.
retro Iterates a bidirectional range backwards.
roundRobin Given n ranges, creates a new range that return the n first elements of each range, in turn, then the second element of each range, and so on, in a round-robin fashion.
sequence Similar to recurrence, except that a random-access range is created.
stride Iterates a range with stride n.
take Creates a sub-range consisting of only up to the first n elements of the given range.
takeExactly Like take, but assumes the given range actually has n elements, and therefore also defines the length property.
takeNone Creates a random-access range consisting of zero elements of the given range.
takeOne Creates a random-access range consisting of exactly the first element of the given range.
tee Creates a range that wraps a given range, forwarding along its elements while also calling a provided function with each element.
transposed Transposes a range of ranges.
transversal Creates a range that iterates over the n'th elements of the given random-access ranges.
zip Given n ranges, creates a range that successively returns a tuple of all the first elements, a tuple of all the second elements, etc.

Ranges whose elements are sorted afford better efficiency with certain operations. For this, the assumeSorted function can be used to construct a SortedRange from a pre-sorted range. The std.algorithm.sort function also conveniently returns a SortedRange. SortedRange objects provide some additional range operations that take advantage of the fact that the range is sorted.

Source: std/range/package.d

License:
Authors:
Andrei Alexandrescu, David Simcha, and Jonathan M Davis. Credit for some of the ideas in building this module goes to Leonardo Maffi.
auto retro(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));
Iterates a bidirectional range backwards. The original range can be accessed by using the source property. Applying retro twice to the same range yields the original range.
Examples:
import std.algorithm : equal;
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(retro(a), [ 5, 4, 3, 2, 1 ][]));
assert(retro(a).source is a);
assert(retro(retro(a)) is a);
auto stride(Range)(Range r, size_t n) if (isInputRange!(Unqual!Range));
Iterates range r with stride n. If the range is a random-access range, moves by indexing into the range; otherwise, moves by successive calls to popFront. Applying stride twice to the same range results in a stride with a step that is the product of the two applications. It is an error for n to be 0.
Examples:
import std.algorithm : equal;

int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][]));
assert(stride(stride(a, 2), 3) == stride(a, 6));
auto chain(Ranges...)(Ranges rs) if (Ranges.length > 0 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void));
Spans multiple ranges in sequence. The function chain takes any number of ranges and returns a 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 front, popFront, and empty primitives. If all input ranges offer random access and length, Chain offers them as well.
If only one range is offered to Chain or chain, the Chain type exits the picture by aliasing itself directly to that range's type.
Examples:
import std.algorithm : equal;

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][]));
auto choose(R1, R2)(bool condition, R1 r1, R2 r2) if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void));
Choose one of two ranges at runtime depending on a Boolean condition.
The ranges may be different, but they must have compatible element types (i.e. CommonType must exist for the two element types). The result is a range that offers the weakest capabilities of the two (e.g. ForwardRange if R1 is a random-access range and R2 is a forward range).
Parameters:
bool condition which range to choose: r1 if true, r2 otherwise
R1 r1 the "true" range
R2 r2 the "false" range
Returns:
A range type dependent on R1 and R2.
Examples:
import std.algorithm.iteration : filter, map;
import std.algorithm.comparison : equal;

auto data1 = [ 1, 2, 3, 4 ].filter!(a => a != 3);
auto data2 = [ 5, 6, 7, 8 ].map!(a => a + 1);

// choose() is primarily useful when you need to select one of two ranges
// with different types at runtime.
static assert(!is(typeof(data1) == typeof(data2)));

auto chooseRange(bool pickFirst)
{
    // The returned range is a common wrapper type that can be used for
    // returning or storing either range without running into a type error.
    return choose(pickFirst, data1, data2);

    // Simply returning the chosen range without using choose() does not
    // work, because map() and filter() return different types.
    //return pickFirst ? data1 : data2; // does not compile
}

auto result = chooseRange(true);
assert(result.equal([ 1, 2, 4 ]));

result = chooseRange(false);
assert(result.equal([ 6, 7, 8, 9 ]));
auto chooseAmong(Ranges...)(size_t index, Ranges rs) if (Ranges.length > 2 && is(typeof(choose(true, rs[0], rs[1]))) && is(typeof(chooseAmong(0, rs[1..$]))));
auto chooseAmong(Ranges...)(size_t index, Ranges rs) if (Ranges.length == 2 && is(typeof(choose(true, rs[0], rs[1]))));
Choose one of multiple ranges at runtime.
The ranges may be different, but they must have compatible element types. The result is a range that offers the weakest capabilities of all Ranges.
Parameters:
size_t index which range to choose, must be less than the number of ranges
Ranges rs two or more ranges
Returns:
The indexed range. If rs consists of only one range, the return type is an alias of that range's type.
Examples:
import std.algorithm : equal;

int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
int[] arr3 = [ 7 ];

{
    auto s = chooseAmong(0, arr1, arr2, arr3);
    auto t = s.save;
    assert(s.length == 4);
    assert(s[2] == 3);
    s.popFront();
    assert(equal(t, [1, 2, 3, 4][]));
}
{
    auto s = chooseAmong(1, arr1, arr2, arr3);
    assert(s.length == 2);
    s.front = 8;
    assert(equal(s, [8, 6][]));
}
{
    auto s = chooseAmong(1, arr1, arr2, arr3);
    assert(s.length == 2);
    s[1] = 9;
    assert(equal(s, [8, 9][]));
}
{
    auto s = chooseAmong(1, arr2, arr1, arr3)[1..3];
    assert(s.length == 2);
    assert(equal(s, [2, 3][]));
}
{
    auto s = chooseAmong(0, arr1, arr2, arr3);
    assert(s.length == 4);
    assert(s.back == 4);
    s.popBack();
    s.back = 5;
    assert(equal(s, [1, 2, 5][]));
    s.back = 3;
    assert(equal(s, [1, 2, 3][]));
}
{
    uint[] foo = [1,2,3,4,5];
    uint[] bar = [6,7,8,9,10];
    auto c = chooseAmong(1,foo, bar);
    assert(c[3] == 9);
    c[3] = 42;
    assert(c[3] == 42);
    assert(c.moveFront() == 6);
    assert(c.moveBack() == 10);
    assert(c.moveAt(4) == 10);
}
{
    import std.range : cycle;
    auto s = chooseAmong(1, cycle(arr2), cycle(arr3));
    assert(isInfinite!(typeof(s)));
    assert(!s.empty);
    assert(s[100] == 7);
}
auto roundRobin(Rs...)(Rs rs) if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs)));
roundRobin(r1, r2, r3) yields r1.front, then r2.front, then r3.front, after which it pops off one element from each and continues again from r1. For example, if two ranges are involved, it alternately yields elements off the two ranges. roundRobin stops after it has consumed all ranges (skipping over the ones that finish early).
Examples:
import std.algorithm : equal;

int[] a = [ 1, 2, 3 ];
int[] b = [ 10, 20, 30, 40 ];
auto r = roundRobin(a, b);
assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ]));
auto radial(Range, I)(Range r, I startingIndex) if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && isIntegral!I);
auto radial(R)(R r) if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R));
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.
Examples:
import std.algorithm : equal;
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(radial(a), [ 3, 4, 2, 5, 1 ]));
a = [ 1, 2, 3, 4 ];
assert(equal(radial(a), [ 2, 3, 1, 4 ]));

// If the left end is reached first, the remaining elements on the right
// are concatenated in order:
a = [ 0, 1, 2, 3, 4, 5 ];
assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ]));

// If the right end is reached first, the remaining elements on the left
// are concatenated in reverse order:
assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ]));
struct Take(Range) if (isInputRange!(Unqual!Range) && !(!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range) || is(Range T == Take!T)));
Take!R take(R)(R input, size_t n) if (isInputRange!(Unqual!R) && !isInfinite!(Unqual!R) && hasSlicing!(Unqual!R) && !is(R T == Take!T));
Lazily takes only up to n elements of a range. This is particularly useful when using with infinite ranges. If the range offers random access and length, Take offers them as well.
Examples:
import std.algorithm : equal;

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 ][]));
Examples:
If the range runs out before n elements, take simply returns the entire range (unlike takeExactly, which will cause an assertion failure if the range ends prematurely):
import std.algorithm : equal;

int[] arr2 = [ 1, 2, 3 ];
auto t = take(arr2, 5);
assert(t.length == 3);
assert(equal(t, [ 1, 2, 3 ]));
auto takeExactly(R)(R range, size_t n) if (isInputRange!R);
Similar to take, but assumes that range has at least n elements. Consequently, the result of takeExactly(range, n) always defines the length property (and initializes it to n) even when range itself does not define length.
The result of takeExactly is identical to that of take in cases where the original range defines length or is infinite.

Unlike take, however, it is illegal to pass a range with less than n elements to takeExactly; this will cause an assertion failure.
Examples:
import std.algorithm : equal;

auto a = [ 1, 2, 3, 4, 5 ];

auto b = takeExactly(a, 3);
assert(equal(b, [1, 2, 3]));
static assert(is(typeof(b.length) == size_t));
assert(b.length == 3);
assert(b.front == 1);
assert(b.back == 3);
auto takeOne(R)(R source) if (isInputRange!R);
Returns a range with at most one element; for example, takeOne([42, 43, 44]) returns a range consisting of the integer 42. Calling popFront() off that range renders it empty.
In effect takeOne(r) is somewhat equivalent to take(r, 1) but in certain interfaces it is important to know statically that the range may only have at most one element.

The type returned by takeOne is a random-access range with length regardless of R's capabilities (another feature that distinguishes takeOne from take).
Examples:
auto s = takeOne([42, 43, 44]);
static assert(isRandomAccessRange!(typeof(s)));
assert(s.length == 1);
assert(!s.empty);
assert(s.front == 42);
s.front = 43;
assert(s.front == 43);
assert(s.back == 43);
assert(s[0] == 43);
s.popFront();
assert(s.length == 0);
assert(s.empty);
auto takeNone(R)() if (isInputRange!R);
Returns an empty range which is statically known to be empty and is guaranteed to have length and be random access regardless of R's capabilities.
Examples:
auto range = takeNone!(int[])();
assert(range.length == 0);
assert(range.empty);
auto takeNone(R)(R range) if (isInputRange!R);
Creates an empty range from the given range in Ο(1). If it can, it will return the same range type. If not, it will return takeExactly(range, 0).
Examples:
import std.algorithm : filter;
assert(takeNone([42, 27, 19]).empty);
assert(takeNone("dlang.org").empty);
assert(takeNone(filter!"true"([42, 27, 19])).empty);
R drop(R)(R range, size_t n) if (isInputRange!R);
R dropBack(R)(R range, size_t n) if (isBidirectionalRange!R);
Convenience function which calls range.popFrontN(n) and returns range. drop makes it easier to pop elements from a range and then pass it to another function within a single expression, whereas popFrontN would require multiple statements.
dropBack provides the same functionality but instead calls range.popBackN(n).

Note: drop and dropBack will only pop up to n elements but will stop if the range is empty first.

Examples:
import std.algorithm : equal;

assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]);
assert("hello world".drop(6) == "world");
assert("hello world".drop(50).empty);
assert("hello world".take(6).drop(3).equal("lo "));
R dropExactly(R)(R range, size_t n) if (isInputRange!R);
R dropBackExactly(R)(R range, size_t n) if (isBidirectionalRange!R);
Similar to drop and dropBack but they call range.popFrontExactly(n) and range.popBackExactly(n) instead.

Note: Unlike drop, dropExactly will assume that the range holds at least n elements. This makes dropExactly faster than drop, but it also means that if range does not contain at least n elements, it will attempt to call popFront on an empty range, which is undefined behavior. So, only use popFrontExactly when it is guaranteed that range holds at least n elements.

Examples:
import std.algorithm : equal, filterBidirectional;

auto a = [1, 2, 3];
assert(a.dropExactly(2) == [3]);
assert(a.dropBackExactly(2) == [1]);

string s = "日本語";
assert(s.dropExactly(2) == "語");
assert(s.dropBackExactly(2) == "日");

auto bd = filterBidirectional!"true"([1, 2, 3]);
assert(bd.dropExactly(2).equal([3]));
assert(bd.dropBackExactly(2).equal([1]));
R dropOne(R)(R range) if (isInputRange!R);
R dropBackOne(R)(R range) if (isBidirectionalRange!R);
Convenience function which calls range.popFront() and returns range. dropOne makes it easier to pop an element from a range and then pass it to another function within a single expression, whereas popFront would require multiple statements.
dropBackOne provides the same functionality but instead calls range.popBack().
Examples:
import std.algorithm : equal, filterBidirectional;

import std.container.dlist;

auto dl = DList!int(9, 1, 2, 3, 9);
assert(dl[].dropOne().dropBackOne().equal([1, 2, 3]));

auto a = [1, 2, 3];
assert(a.dropOne() == [2, 3]);
assert(a.dropBackOne() == [1, 2]);

string s = "日本語";
assert(s.dropOne() == "本語");
assert(s.dropBackOne() == "日本");

auto bd = filterBidirectional!"true"([1, 2, 3]);
assert(bd.dropOne().equal([2, 3]));
assert(bd.dropBackOne().equal([1, 2]));
struct Repeat(T);
Repeat!T repeat(T)(T value);
Repeats one value forever.
Models an infinite bidirectional and random access range, with slicing.
Examples:
import std.algorithm : equal;

assert(equal(5.repeat().take(4), [ 5, 5, 5, 5 ]));
Take!(Repeat!T) repeat(T)(T value, size_t n);
Repeats value exactly n times. Equivalent to take(repeat(value), n).
Examples:
import std.algorithm : equal;

assert(equal(5.repeat(4), 5.repeat().take(4)));
auto generate(Fun)(Fun fun) if (isCallable!fun);
auto generate(alias fun)() if (isCallable!fun);
Given callable (std.traits.isCallable) fun, create as a range whose front is defined by successive calls to fun(). This is especially useful to call function with global side effects (random functions), or to create ranges expressed as a single delegate, rather than an entire front/popFront/empty structure. fun maybe be passed either a template alias parameter (existing function, delegate, struct type defining static opCall... ) or a run-time value argument (delegate, function object... ). The result range models an InputRange (std.range.primitives.isInputRange). The resulting range will call fun() on every call to front, and only when front is called, regardless of how the range is iterated. It is advised to compose generate with either std.algorithm.iteration.cache or std.array.array, or to use it in a foreach loop. A by-value foreach loop means that the loop value is not ref.
Parameters:
Fun fun is the isCallable that gets called on every call to front.
Returns:
an inputRange that returns a new value generated by Fun on any call to front.
Examples:
import std.algorithm : equal, map;

int i = 1;
auto powersOfTwo = generate!(() => i *= 2)().take(10);
assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"()));
Examples:
import std.algorithm : equal;

//Returns a run-time delegate
auto infiniteIota(T)(T low, T high)
{
    T i = high;
    return (){if (i == high) i = low; return i++;};
}
//adapted as a range.
assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]));
Examples:
import std.format, std.random;

auto r = generate!(() => uniform(0, 6)).take(10);
format("%(%s %)", r);
struct Cycle(R) if (isForwardRange!R && !isInfinite!R);
Cycle!R cycle(R)(R input) if (isForwardRange!R && !isInfinite!R);
Cycle!R cycle(R)(R input, size_t index = 0) if (isRandomAccessRange!R && !isInfinite!R);
Repeats the given forward range ad infinitum. If the original range is infinite (fact that would make Cycle the identity application), Cycle detects that and aliases itself to the range type itself. If the original range has random access, Cycle offers random access and also offers a constructor taking an initial position index. Cycle works with static arrays in addition to ranges, mostly for performance reasons.

Note: The input range must not be empty.

Tip: This is a great way to implement simple circular buffers.

Examples:
import std.algorithm : equal;
import std.range : cycle, take;

// Here we create an infinitive cyclic sequence from [1, 2]
// (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then
// take 5 elements of this sequence (so we have [1, 2, 1, 2, 1])
// and compare them with the expected values for equality.
assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ]));
struct Zip(Ranges...) if (Ranges.length && allSatisfy!(isInputRange, Ranges));
auto zip(Ranges...)(Ranges ranges) if (Ranges.length && allSatisfy!(isInputRange, Ranges));
auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges) if (Ranges.length && allSatisfy!(isInputRange, Ranges));
Iterate several ranges in lockstep. The element type is a proxy tuple that allows accessing the current element in the nth range by using e[n]. 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, Zip is extremely powerful because it allows manipulating several ranges in lockstep. For example, the following code sorts two arrays in parallel:
Examples:
import std.algorithm : sort;
int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "b", "c" ];
sort!((c, d) => c[0] > d[0])(zip(a, b));
assert(a == [ 3, 2, 1 ]);
assert(b == [ "c", "b", "a" ]);
Examples:
int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "b", "c" ];

size_t idx = 0;
foreach (e; zip(a, b))
{
    assert(e[0] == a[idx]);
    assert(e[1] == b[idx]);
    ++idx;
}
this(R rs, StoppingPolicy s = StoppingPolicy.shortest);
Builds an object. Usually this is invoked indirectly by using the zip function.
enum bool empty;
Returns true if the range is at end. The test depends on the stopping policy.
@property ElementType front();
Returns the current iterated element.
@property void front(ElementType v);
Sets the front of all iterated ranges.
ElementType moveFront();
Moves out the front.
@property ElementType back();
Returns the rightmost element.
ElementType moveBack();
Moves out the back.
Returns the rightmost element.
@property void back(ElementType v);
Returns the current iterated element.
Returns the rightmost element.
void popFront();
Advances to the next element in all controlled ranges.
void popBack();
Calls popBack for all controlled ranges.
@property auto length();
Returns the length of this range. Defined only if all ranges define length.
alias opDollar = length;
Returns the length of this range. Defined only if all ranges define length.
auto opSlice(size_t from, size_t to);
Returns a slice of the range. Defined only if all range define slicing.
ElementType opIndex(size_t n);
Returns the nth element in the composite range. Defined if all ranges offer random access.
void opIndexAssign(ElementType v, size_t n);
Assigns to the nth element in the composite range. Defined if all ranges offer random access.
Returns the nth element in the composite range. Defined if all ranges offer random access.
ElementType moveAt(size_t n);
Destructively reads the nth element in the composite range. Defined if all ranges offer random access.
Returns the nth element in the composite range. Defined if all ranges offer random access.
enum StoppingPolicy: int;
Dictates how iteration in a Zip should stop. By default stop at the end of the shortest of all ranges.
shortest
Stop when the shortest range is exhausted
longest
Stop when the longest range is exhausted
requireSameLength
Require that all ranges are equal
struct Lockstep(Ranges...) if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges));
Lockstep!Ranges lockstep(Ranges...)(Ranges ranges) if (allSatisfy!(isInputRange, Ranges));
Lockstep!Ranges lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) if (allSatisfy!(isInputRange, Ranges));
Iterate multiple ranges in lockstep using a foreach loop. If only a single range is passed in, the Lockstep aliases itself away. If the ranges are of different lengths and s == StoppingPolicy.shortest stop after the shortest range is empty. If the ranges are of different lengths and s == StoppingPolicy.requireSameLength, throw an exception. s may not be StoppingPolicy.longest, and passing this will throw an exception.
By default StoppingPolicy is set to StoppingPolicy.shortest.

Lockstep also supports iterating with an index variable:
foreach (index, a, b; lockstep(arr1, arr2))
{
    writefln("Index %s:  a = %s, b = %s", index, a, b);
}
Examples:
auto arr1 = [1,2,3,4,5];
auto arr2 = [6,7,8,9,10];

foreach (ref a, ref b; lockstep(arr1, arr2))
{
    a += b;
}

assert(arr1 == [7,9,11,13,15]);
struct Recurrence(alias fun, StateType, size_t stateSize);
Recurrence!(fun, CommonType!State, State.length) recurrence(alias fun, State...)(State initial);
Creates a mathematical sequence given the initial values and a recurrence function that computes the next value from the existing values. The sequence comes in the form of an infinite forward range. The type Recurrence itself is seldom used directly; most often, recurrences are obtained by calling the function recurrence.
When calling 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 next Fibonacci value needs the past two values.

The signature of this function should be:
auto fun(R)(R state, size_t n)
where n will be the index of the current value, and state will be an opaque state vector that can be indexed with array-indexing notation state[i], where valid values of i range from (n - 1) to (n - State.length).

If the function is passed in string form, the state has name "a" and the zero-based index in the recurrence has name "n". The given string must return the desired value for a[n] given a[n - 1], a[n - 2], a[n - 3],..., a[n - stateSize]. The state size is dictated by the number of arguments passed to the call to recurrence. The Recurrence struct itself takes care of managing the recurrence's state and shifting it appropriately.
Examples:
import std.algorithm : equal;

// The Fibonacci numbers, using function in string form:
// 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);
assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]));

// The factorials, using function in lambda form:
auto fac = recurrence!((a,n) => a[n-1] * n)(1);
assert(take(fac, 10).equal([
    1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
]));

// The triangular numbers, using function in explicit form:
static size_t genTriangular(R)(R state, size_t n)
{
    return state[n-1] + n;
}
auto tri = recurrence!genTriangular(0);
assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45]));
struct Sequence(alias fun, State);
auto sequence(alias fun, State...)(State args);
Sequence is similar to Recurrence except that iteration is presented in the so-called closed form. This means that the nth element in the series is computable directly from the initial values and n itself. This implies that the interface offered by Sequence is a random-access range, as opposed to the regular Recurrence, which only offers forward iteration.
The state of the sequence is stored as a Tuple so it can be heterogeneous.
Examples:
Odd numbers, using function in string form:
auto odds = sequence!("a[0] + n * a[1]")(1, 2);
assert(odds.front == 1);
odds.popFront();
assert(odds.front == 3);
odds.popFront();
assert(odds.front == 5);
Examples:
Triangular numbers, using function in lambda form:
auto tri = sequence!((a,n) => n*(n+1)/2)();

// Note random access
assert(tri[0] == 0);
assert(tri[3] == 6);
assert(tri[1] == 1);
assert(tri[4] == 10);
assert(tri[2] == 3);
Examples:
Fibonacci numbers, using function in explicit form:
import std.math : pow, round, sqrt;
static ulong computeFib(S)(S state, size_t n)
{
    // Binet's formula
    return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) /
                             state[2]));
}
auto fib = sequence!computeFib(
    (1.0 + sqrt(5.0)) / 2.0,    // Golden Ratio
    (1.0 - sqrt(5.0)) / 2.0,    // Conjugate of Golden Ratio
    sqrt(5.0));

// Note random access with [] operator
assert(fib[1] == 1);
assert(fib[4] == 5);
assert(fib[3] == 3);
assert(fib[2] == 2);
assert(fib[9] == 55);
auto iota(B, E, S)(B begin, E end, S step) if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) && isIntegral!S);
auto iota(B, E)(B begin, E end) if (isFloatingPoint!(CommonType!(B, E)));
auto iota(B, E)(B begin, E end) if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)));
auto iota(E)(E end);
auto iota(B, E, S)(B begin, E end, S step) if (isFloatingPoint!(CommonType!(B, E, S)));
auto iota(B, E)(B begin, E end) if (!isIntegral!(CommonType!(B, E)) && !isFloatingPoint!(CommonType!(B, E)) && !isPointer!(CommonType!(B, E)) && is(typeof((ref B b) { ++b; } )) && (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))));
Construct a range of values that span the given starting and stopping values.
Parameters:
B begin The starting value.
E end The value that serves as the stopping criterion. This value is not included in the range.
S step The value to add to the current value at each iteration.
Returns:
A range that goes through the numbers begin, begin + step, begin + 2 * step, ..., up to and excluding end.

The two-argument overloads have step = 1. If begin < end && step < 0 or begin > end && step > 0 or begin == end, then an empty range is returned. If step == 0 then begin == end is an error.

For built-in types, the range returned is a random access range. For user-defined types that support ++, the range is an input range.

Example:

void main()
{
    import std.stdio;

    // The following groups all produce the same output of:
    // 0 1 2 3 4

    foreach (i; 0..5)
        writef("%s ", i);
    writeln();

    import std.range : iota;
    foreach (i; iota(0, 5))
        writef("%s ", i);
    writeln();

    writefln("%(%s %|%)", iota(0, 5));

    import std.algorithm : map, copy;
    import std.format;
    iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter());
    writeln();
}
Examples:
import std.algorithm : equal;
import std.math : approxEqual;

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(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
Examples:
User-defined types such as std.bigint.BigInt are also supported, as long as they can be incremented with ++ and compared with < or ==.
import std.algorithm.comparison : equal;
import std.bigint;

auto s = BigInt(1_000_000_000_000);
auto e = BigInt(1_000_000_000_003);
auto r = iota(s, e);
assert(r.equal([
    BigInt(1_000_000_000_000),
    BigInt(1_000_000_000_001),
    BigInt(1_000_000_000_002)
]));
enum TransverseOptions: int;
Options for the FrontTransversal and Transversal ranges (below).
assumeJagged
When transversed, the elements of a range of ranges are assumed to have different lengths (e.g. a jagged array).
enforceNotJagged
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.
assumeNotJagged
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.
struct FrontTransversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged);
FrontTransversal!(RangeOfRanges, opt) frontTransversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr);
Given a range of ranges, iterate transversally through the first elements of each of the enclosed ranges.
Examples:
import std.algorithm : equal;
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = frontTransversal(x);
assert(equal(ror, [ 1, 3 ][]));
this(RangeOfRanges input);
Construction from an input.
enum bool empty;
@property ref auto front();
ElementType moveFront();
void popFront();
Forward range primitives.
@property FrontTransversal save();
Duplicates this frontTransversal. Note that only the encapsulating range of range will be duplicated. Underlying ranges will not be duplicated.
@property ref auto back();
void popBack();
ElementType moveBack();
Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
ref auto opIndex(size_t n);
ElementType moveAt(size_t n);
void opIndexAssign(ElementType val, size_t n);
Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
typeof(this) opSlice(size_t lower, size_t upper);
Slicing if offered if RangeOfRanges supports slicing and all the conditions for supporting indexing are met.
struct Transversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged);
Transversal!(RangeOfRanges, opt) transversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr, size_t n);
Given a range of ranges, iterate transversally through the the nth element of each of the enclosed ranges. All elements of the enclosing range must offer random access.
Examples:
import std.algorithm : equal;
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = transversal(x, 1);
assert(equal(ror, [ 2, 4 ][]));
this(RangeOfRanges input, size_t n);
Construction from an input and an index.
enum bool empty;
@property ref auto front();
E moveFront();
@property auto front(E val);
void popFront();
@property typeof(this) save();
Forward range primitives.
@property ref auto back();
void popBack();
E moveBack();
@property auto back(E val);
Bidirectional primitives. They are offered if isBidirectionalRange!RangeOfRanges.
ref auto opIndex(size_t n);
E moveAt(size_t n);
void opIndexAssign(E val, size_t n);
@property size_t length();
alias opDollar = length;
Random-access primitive. It is offered if isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).
typeof(this) opSlice(size_t lower, size_t upper);
Slicing if offered if RangeOfRanges supports slicing and all the conditions for supporting indexing are met.
Transposed!RangeOfRanges transposed(RangeOfRanges)(RangeOfRanges rr) if (isForwardRange!RangeOfRanges && isInputRange!(ElementType!RangeOfRanges) && hasAssignableElements!RangeOfRanges);
Given a range of ranges, returns a range of ranges where the i'th subrange contains the i'th elements of the original subranges.
Examples:
import std.algorithm : equal;
int[][] ror = [
    [1, 2, 3],
    [4, 5, 6]
];
auto xp = transposed(ror);
assert(equal!"a.equal(b)"(xp, [
    [1, 4],
    [2, 5],
    [3, 6]
]));
Examples:
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++]);
}
struct Indexed(Source, Indices) if (isRandomAccessRange!Source && isInputRange!Indices && is(typeof(Source.init[ElementType!Indices.init])));
Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indices);
This struct takes two ranges, source and indices, and creates a view of source as if its elements were reordered according to indices. indices may include only a subset of the elements of source and may also repeat elements.
Source must be a random access range. The returned range will be bidirectional or random-access if Indices is bidirectional or random-access, respectively.
Examples:
import std.algorithm : equal;
auto source = [1, 2, 3, 4, 5];
auto indices = [4, 3, 1, 2, 0, 4];
auto ind = indexed(source, indices);
assert(equal(ind, [5, 4, 2, 3, 1, 5]));
assert(equal(retro(ind), [5, 1, 3, 2, 4, 5]));
@property ref auto front();
void popFront();
@property typeof(this) save();
@property ref auto front(ElementType!Source newVal);
auto moveFront();
@property ref auto back();
void popBack();
@property ref auto back(ElementType!Source newVal);
auto moveBack();
@property size_t length();
ref auto opIndex(size_t index);
typeof(this) opSlice(size_t a, size_t b);
auto opIndexAssign(ElementType!Source newVal, size_t index);
auto moveAt(size_t index);
Range primitives
@property Source source();
Returns the source range.
@property Indices indices();
Returns the indices range.
size_t physicalIndex(size_t logicalIndex);
Returns the physical index into the source range corresponding to a given logical index. This is useful, for example, when indexing an Indexed without adding another layer of indirection.
Examples:
auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]);
assert(ind.physicalIndex(0) == 1);
struct Chunks(Source) if (isForwardRange!Source);
Chunks!Source chunks(Source)(Source source, size_t chunkSize) if (isForwardRange!Source);
This range iterates over fixed-sized chunks of size chunkSize of a source range. Source must be a forward range.
If !isInfinite!Source and source.walkLength is not evenly divisible by chunkSize, the back element of this range will contain fewer than chunkSize elements.
Examples:
import std.algorithm : equal;
auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
auto chunks = chunks(source, 4);
assert(chunks[0] == [1, 2, 3, 4]);
assert(chunks[1] == [5, 6, 7, 8]);
assert(chunks[2] == [9, 10]);
assert(chunks.back == chunks[2]);
assert(chunks.front == chunks[0]);
assert(chunks.length == 3);
assert(equal(retro(array(chunks)), array(retro(chunks))));
this(Source source, size_t chunkSize);
Standard constructor
@property auto front();
void popFront();
@property bool empty();
@property typeof(this) save();
Forward range primitives. Always present.
@property size_t length();
Length. Only if hasLength!Source is true
auto opIndex(size_t index);
typeof(this) opSlice(size_t lower, size_t upper);
Indexing and slicing operations. Provided only if hasSlicing!Source is true.
@property auto back();
void popBack();
Bidirectional range primitives. Provided only if both hasSlicing!Source and hasLength!Source are true.
auto only(Values...)(auto ref Values values) if (!is(CommonType!Values == void) || Values.length == 0);
Assemble values into a range that carries all its elements in-situ.
Useful when a single value or multiple disconnected values must be passed to an algorithm expecting a range, without having to perform dynamic memory allocation.

As copying the range means copying all elements, it can be safely returned from functions. For the same reason, copying the returned range may be expensive for a large number of arguments.
Examples:
import std.algorithm;
import std.uni;

assert(equal(only('♡'), "♡"));
assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]);

assert(only("one", "two", "three").joiner(" ").equal("one two three"));

string title = "The D Programming Language";
assert(filter!isUpper(title).map!only().join(".") == "T.D.P.L");
auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0) if (isIntegral!Enumerator && isInputRange!Range);
Iterate over range with an attached index variable.
Each element is a std.typecons.Tuple containing the index and the element, in that order, where the index member is named index and the element member is named value.

The index starts at start and is incremented by one on every iteration.

Bidirectionality is propagated only if range has length.

Overflow: If range has length, then it is an error to pass a value for start so that start + range.length is bigger than Enumerator.max, thus it is ensured that overflow cannot happen.

If range does not have length, and popFront is called when front.index == Enumerator.max, the index will overflow and continue from Enumerator.min.
Examples:
Useful for using foreach with an index loop variable:
    import std.stdio : stdin, stdout;
    import std.range : enumerate;

    foreach (lineNum, line; stdin.byLine().enumerate(1))
        stdout.writefln("line #%s: %s", lineNum, line);
Examples:
Can start enumeration from a negative position:
import std.array : assocArray;
import std.range : enumerate;

bool[int] aa = true.repeat(3).enumerate(-1).assocArray();
assert(aa[-1]);
assert(aa[0]);
assert(aa[1]);
enum isTwoWayCompatible(alias fn, T1, T2);
Returns true if fn accepts variables of type T1 and T2 in any order. The following code should compile:
T1 foo();
T2 bar();

fn(foo(), bar());
fn(bar(), foo());
enum SearchPolicy: int;
Policy used with the searching primitives lowerBound, upperBound, and equalRange of SortedRange below.
linear
Searches in a linear fashion.
trot
Searches with a step that is grows linearly (1, 2, 3,...) leading to a quadratic search schedule (indexes tried are 0, 1, 3, 6, 10, 15, 21, 28,...) Once the search overshoots its target, the remaining interval is searched using binary search. The search is completed in Ο(sqrt(n)) time. Use it when you are reasonably confident that the value is around the beginning of the range.
gallop
Performs a galloping search algorithm, i.e. searches with a step that doubles every time, (1, 2, 4, 8, ...) leading to an exponential search schedule (indexes tried are 0, 1, 3, 7, 15, 31, 63,...) Once the search overshoots its target, the remaining interval is searched using binary search. A value is found in Ο(log(n)) time.
binarySearch
Searches using a classic interval halving policy. The search starts in the middle of the range, and each search step cuts the range in half. This policy finds a value in Ο(log(n)) time but is less cache friendly than gallop for large ranges. The binarySearch policy is used as the last step of trot, gallop, trotBackwards, and gallopBackwards strategies.
trotBackwards
Similar to trot but starts backwards. Use it when confident that the value is around the end of the range.
gallopBackwards
Similar to gallop but starts backwards. Use it when confident that the value is around the end of the range.
struct SortedRange(Range, alias pred = "a < b") if (isInputRange!Range);
Represents a sorted range. In addition to the regular range primitives, supports additional operations that take advantage of the ordering, such as merge and binary search. To obtain a SortedRange from an unsorted range r, use std.algorithm.sorting.sort which sorts r in place and returns the corresponding SortedRange. To construct a SortedRange from a range r that is known to be already sorted, use assumeSorted described below.
Examples:
import std.algorithm : sort;
auto a = [ 1, 2, 3, 42, 52, 64 ];
auto r = assumeSorted(a);
assert(r.contains(3));
assert(!r.contains(32));
auto r1 = sort!"a > b"(a);
assert(r1.contains(3));
assert(!r1.contains(32));
assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]);
Examples:
SortedRange could accept ranges weaker than random-access, but it is unable to provide interesting functionality for them. Therefore, SortedRange is currently restricted to random-access ranges.

No copy of the original range is ever made. If the underlying range is changed concurrently with its corresponding SortedRange in ways that break its sortedness, SortedRange will work erratically.
import std.algorithm : swap;
auto a = [ 1, 2, 3, 42, 52, 64 ];
auto r = assumeSorted(a);
assert(r.contains(42));
swap(a[3], a[5]);         // illegal to break sortedness of original range
assert(!r.contains(42));  // passes although it shouldn't
@property bool empty();
@property auto save();
@property ref auto front();
void popFront();
@property ref auto back();
void popBack();
ref auto opIndex(size_t i);
auto opSlice(size_t a, size_t b);
@property size_t length();
alias opDollar = length;
Range primitives.
auto release();
Releases the controlled range and returns it.
auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V) && hasSlicing!Range);
This function uses a search with policy sp to find the largest left subrange on which pred(x, value) is true for all x (e.g., if pred is "less than", returns the portion of the range with elements strictly smaller than value). The search schedule and its complexity are documented in SearchPolicy. See also STL's lower_bound.

Example:

auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]);
auto p = a.lowerBound(4);
assert(equal(p, [ 0, 1, 2, 3 ]));
auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V));
This function searches with policy sp to find the largest right subrange on which pred(value, x) is true for all x (e.g., if pred is "less than", returns the portion of the range with elements strictly greater than value). The search schedule and its complexity are documented in SearchPolicy.
For ranges that do not offer random access, SearchPolicy.linear is the only policy allowed (and it must be specified explicitly lest it exposes user code to unexpected inefficiencies). For random-access searches, all policies are allowed, and SearchPolicy.binarySearch is the default.
See Also:
STL's upper_bound.

Example:

auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]);
auto p = a.upperBound(3);
assert(equal(p, [4, 4, 5, 6]));
auto equalRange(V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V) && isRandomAccessRange!Range);
Returns the subrange containing all elements e for which both pred(e, value) and pred(value, e) evaluate to false (e.g., if pred is "less than", returns the portion of the range with elements equal to value). Uses a classic binary search with interval halving until it finds a value that satisfies the condition, then uses SearchPolicy.gallopBackwards to find the left boundary and SearchPolicy.gallop to find the right boundary. These policies are justified by the fact that the two boundaries are likely to be near the first found value (i.e., equal ranges are relatively small). Completes the entire search in Ο(log(n)) time. See also STL's equal_range.

Example:

auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
auto r = equalRange(a, 3);
assert(equal(r, [ 3, 3, 3 ]));
auto trisect(V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V) && isRandomAccessRange!Range);
Returns a tuple r such that r[0] is the same as the result of lowerBound(value), r[1] is the same as the result of equalRange(value), and r[2] is the same as the result of upperBound(value). The call is faster than computing all three separately. Uses a search schedule similar to equalRange. Completes the entire search in Ο(log(n)) time.

Example:

auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
auto r = assumeSorted(a).trisect(3);
assert(equal(r[0], [ 1, 2 ]));
assert(equal(r[1], [ 3, 3, 3 ]));
assert(equal(r[2], [ 4, 4, 5, 6 ]));
bool contains(V)(V value) if (isRandomAccessRange!Range);
Returns true if and only if value can be found in range, which is assumed to be sorted. Performs Ο(log(r.length)) evaluations of pred. See also STL's binary_search.
auto groupBy()();
Returns a range of subranges of elements that are equivalent according to the sorting relation.
auto assumeSorted(alias pred = "a < b", R)(R r) if (isInputRange!(Unqual!R));
Assumes r is sorted by predicate pred and returns the corresponding SortedRange!(pred, R) having r as support. To keep the checking costs low, the cost is Ο(1) in release mode (no checks for sortedness are performed). In debug mode, a few random elements of r are checked for sortedness. The size of the sample is proportional Ο(log(r.length)). That way, checking has no effect on the complexity of subsequent operations specific to sorted ranges (such as binary search). The probability of an arbitrary unsorted range failing the test is very high (however, an almost-sorted range is likely to pass it). To check for sortedness at cost Ο(n), use std.algorithm.sorting.isSorted.
struct RefRange(R) if (isInputRange!R);
Wrapper which effectively makes it possible to pass a range by reference. Both the original range and the RefRange will always have the exact same elements. Any operation done on one will affect the other. So, for instance, if it's passed to a function which would implicitly copy the original range if it were passed to it, the original range is not copied but is consumed as if it were a reference type.
Note that save works as normal and operates on a new range, so if save is ever called on the RefRange, then no operations on the saved range will affect the original.
Examples:
Basic Example
import std.algorithm;
ubyte[] buffer = [1, 9, 45, 12, 22];
auto found1 = find(buffer, 45);
assert(found1 == [45, 12, 22]);
assert(buffer == [1, 9, 45, 12, 22]);

auto wrapped1 = refRange(&buffer);
auto found2 = find(wrapped1, 45);
assert(*found2.ptr == [45, 12, 22]);
assert(buffer == [45, 12, 22]);

auto found3 = find(wrapped1.save, 22);
assert(*found3.ptr == [22]);
assert(buffer == [45, 12, 22]);

string str = "hello world";
auto wrappedStr = refRange(&str);
assert(str.front == 'h');
str.popFrontN(5);
assert(str == " world");
assert(wrappedStr.front == ' ');
assert(*wrappedStr.ptr == " world");
Examples:
opAssign Example.
ubyte[] buffer1 = [1, 2, 3, 4, 5];
ubyte[] buffer2 = [6, 7, 8, 9, 10];
auto wrapped1 = refRange(&buffer1);
auto wrapped2 = refRange(&buffer2);
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is &buffer2);
assert(wrapped1.ptr !is wrapped2.ptr);
assert(buffer1 != buffer2);

wrapped1 = wrapped2;

//Everything points to the same stuff as before.
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is &buffer2);
assert(wrapped1.ptr !is wrapped2.ptr);

//But buffer1 has changed due to the assignment.
assert(buffer1 == [6, 7, 8, 9, 10]);
assert(buffer2 == [6, 7, 8, 9, 10]);

buffer2 = [11, 12, 13, 14, 15];

//Everything points to the same stuff as before.
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is &buffer2);
assert(wrapped1.ptr !is wrapped2.ptr);

//But buffer2 has changed due to the assignment.
assert(buffer1 == [6, 7, 8, 9, 10]);
assert(buffer2 == [11, 12, 13, 14, 15]);

wrapped2 = null;

//The pointer changed for wrapped2 but not wrapped1.
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is null);
assert(wrapped1.ptr !is wrapped2.ptr);

//buffer2 is not affected by the assignment.
assert(buffer1 == [6, 7, 8, 9, 10]);
assert(buffer2 == [11, 12, 13, 14, 15]);
pure nothrow @safe this(R* range);

auto opAssign(RefRange rhs);
This does not assign the pointer of rhs to this RefRange. Rather it assigns the range pointed to by rhs to the range pointed to by this RefRange. This is because any operation on a RefRange is the same is if it occurred to the original range. The one exception is when a RefRange is assigned null either directly or because rhs is null. In that case, RefRange no longer refers to the original range but is null.
auto opAssign(typeof(null) rhs);

inout pure nothrow @property @safe inout(R*) ptr();
A pointer to the wrapped range.
@property auto front();
const @property auto front();
@property auto front(ElementType!R value);

@property bool empty();
const @property bool empty();

void popFront();

@property auto save();
const @property auto save();
auto opSlice();
const auto opSlice();
Only defined if isForwardRange!R is true.
@property auto back();
const @property auto back();
@property auto back(ElementType!R value);
void popBack();
Only defined if isBidirectionalRange!R is true.
ref auto opIndex(IndexType)(IndexType index);
const ref auto opIndex(IndexType)(IndexType index);
Only defined if isRandomAccesRange!R is true.
auto moveFront();
Only defined if hasMobileElements!R and isForwardRange!R are true.
auto moveBack();
Only defined if hasMobileElements!R and isBidirectionalRange!R are true.
auto moveAt(IndexType)(IndexType index) if (is(typeof((*_range).moveAt(index))));
Only defined if hasMobileElements!R and isRandomAccessRange!R are true.
@property auto length();
const @property auto length();
Only defined if hasLength!R is true.
auto opSlice(IndexType1, IndexType2)(IndexType1 begin, IndexType2 end);
const auto opSlice(IndexType1, IndexType2)(IndexType1 begin, IndexType2 end);
Only defined if hasSlicing!R is true.
auto refRange(R)(R* range) if (isInputRange!R && !is(R == class));
auto refRange(R)(R* range) if (isInputRange!R && is(R == class));
Helper function for constructing a RefRange.
If the given range is a class type (and thus is already a reference type), then the original range is returned rather than a RefRange.
Examples:
import std.algorithm : map, joiner, group, until;
// fix for std.algorithm
auto r = map!(x => 0)([1]);
chain(r, r);
zip(r, r);
roundRobin(r, r);

struct NRAR {
    typeof(r) input;
    @property empty() { return input.empty; }
    @property front() { return input.front; }
    void popFront()   { input.popFront(); }
    @property save()  { return NRAR(input.save); }
}
auto n1 = NRAR(r);
cycle(n1);  // non random access range version

assumeSorted(r);

// fix for std.range
joiner([r], [9]);

struct NRAR2 {
    NRAR input;
    @property empty() { return true; }
    @property front() { return input; }
    void popFront() { }
    @property save()  { return NRAR2(input.save); }
}
auto n2 = NRAR2(n1);
joiner(n2);

group(r);

until(r, 7);
static void foo(R)(R r) { until!(x => x > 7)(r); }
foo(r);
struct NullSink;
An OutputRange that discards the data it receives.
Examples:
import std.algorithm : map, copy;
[4, 5, 6].map!(x => x * 2).copy(NullSink()); // data is discarded
auto tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1 inputRange, R2 outputRange) if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1));
auto tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1 inputRange) if (is(typeof(fun) == void) || isSomeFunction!fun);
Implements a "tee" style pipe, wrapping an input range so that elements of the range can be passed to a provided function or OutputRange as they are iterated over. This is useful for printing out intermediate values in a long chain of range code, performing some operation with side-effects on each call to front or popFront, or diverting the elements of a range into an auxiliary OutputRange.
It is important to note that as the resultant range is evaluated lazily, in the case of the version of tee that takes a function, the function will not actually be executed until the range is "walked" using functions that evaluate ranges, such as std.array.array or std.algorithm.iteration.reduce.
Parameters:
pipeOnPop If Yes.pipeOnPop, simply iterating the range without ever calling front is enough to have tee mirror elements to outputRange (or, respectively, fun). If No.pipeOnPop, only elements for which front does get called will be also sent to outputRange/fun.
R1 inputRange The input range beeing passed through.
R2 outputRange This range will receive elements of inputRange progressively as iteration proceeds.
fun This function will be called with elements of inputRange progressively as iteration proceeds.
Returns:
An input range that offers the elements of inputRange. Regardless of whether inputRange is a more powerful range (forward, bidirectional etc), the result is always an input range. Reading this causes inputRange to be iterated and returns its elements in turn. In addition, the same elements will be passed to outputRange or fun as well.
Examples:
import std.algorithm : equal, filter, map;

// Sum values while copying
int[] values = [1, 4, 9, 16, 25];
int sum = 0;
auto newValues = values.tee!(a => sum += a).array;
assert(equal(newValues, values));
assert(sum == 1 + 4 + 9 + 16 + 25);

// Count values that pass the first filter
int count = 0;
auto newValues4 = values.filter!(a => a < 10)
                        .tee!(a => count++)
                        .map!(a => a + 1)
                        .filter!(a => a < 10);

//Fine, equal also evaluates any lazy ranges passed to it.
//count is not 3 until equal evaluates newValues4
assert(equal(newValues4, [2, 5]));
assert(count == 3);