Report a bug
If you spot a problem with this page, click here to create a GitHub 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 a local clone.

mir.ndslice.slice

This is a submodule of mir.ndslice.

Safety note User-defined iterators should care about their safety except bounds checks. Bounds are checked in ndslice code.

License:
Authors:
Ilya Yaroshenko

Definitions

Name Description
Slice N-dimensional slice.
SliceKind SliceKind of Slice enumeration.
Universal Alias for .SliceKind.universal.
Canonical Alias for .SliceKind.canonical.
Contiguous Alias for .SliceKind.contiguous.
sliced Creates a slice on top of an iterator, a pointer, or an array's pointer.
slicedField Creates a slice on top of a field, a random access range, or an array.
slicedNdField Creates a slice on top of an ndField.
kindOf Extracts SliceKind.
isSlice Checks if the type is Slice instance.
Structure A tuple of lengths and strides.
template hasAsSlice(T)
Checks if type T has asSlice property and its returns a slices. Aliases itself to a dimension count
Examples:
import mir.series;
static assert(!hasAsSlice!(int[]));
static assert(hasAsSlice!(SeriesMap!(int, string)) == 1);
enum auto isConvertibleToSlice(T);
Check if toConst function can be called with type T.
Examples:
import mir.series: SeriesMap;
static assert(isConvertibleToSlice!(immutable int[]));
static assert(isConvertibleToSlice!(string[]));
static assert(isConvertibleToSlice!(SeriesMap!(string, int)));
static assert(isConvertibleToSlice!(Slice!(int*)));
auto toSlice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) val);

auto toSlice(Iterator, size_t N, SliceKind kind)(const Slice!(Iterator, N, kind) val);

auto toSlice(Iterator, size_t N, SliceKind kind)(immutable Slice!(Iterator, N, kind) val);

auto toSlice(T)(T[] val);

auto toSlice(T)(T val)
if (hasAsSlice!T || __traits(hasMember, T, "moveToSlice"));

auto toSlice(T)(ref T val)
if (hasAsSlice!T);

Reurns Ndslice view in the same data.

template toSlices(args...)
enum auto isSlice(T);
Checks if the type is Slice instance.
Examples:
alias A = uint[];
alias S = Slice!(int*);

static assert(isSlice!S);
static assert(!isSlice!A);
enum mir_slice_kind: int;

alias SliceKind = mir_slice_kind;
SliceKind of Slice.
universal
A slice has strides for all dimensions.
canonical
A slice has >=2 dimensions and row dimension is contiguous.
contiguous
A slice is a flat contiguous data without strides.
alias Universal = mir_slice_kind.universal;
See Also:
Internal Binary Representation section in Slice.
alias Canonical = mir_slice_kind.canonical;
See Also:
Internal Binary Representation section in Slice.
alias Contiguous = mir_slice_kind.contiguous;
See Also:
Internal Binary Representation section in Slice.
enum auto kindOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind);
Extracts SliceKind.
Examples:
static assert(kindOf!(Slice!(int*, 1, Universal)) == Universal);
template IteratorOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind)
Extracts iterator type from a Slice.
auto sliced(size_t N, Iterator)(Iterator iterator, size_t[N] lengths...)
if (!__traits(isStaticArray, Iterator) && N && !is(Iterator : Slice!(_Iterator, _N, kind), _Iterator, size_t _N, SliceKind kind));
Creates an n-dimensional slice-shell over an iterator.
Parameters:
Iterator iterator An iterator, a pointer, or an array.
size_t[N] lengths A list of lengths for each dimension
Returns:
n-dimensional slice
Examples:
Random access range primitives for slices over user defined types
struct MyIota
{
    //`[index]` operator overloading
    auto opIndex(size_t index) @safe nothrow
    {
        return index;
    }

    auto lightConst()() const @property { return MyIota(); }
    auto lightImmutable()() immutable @property { return MyIota(); }
}

import mir.ndslice.iterator: FieldIterator;
alias Iterator = FieldIterator!MyIota;
alias S = Slice!(Iterator, 2);
import std.range.primitives;
static assert(hasLength!S);
static assert(hasSlicing!S);
static assert(isRandomAccessRange!S);

auto slice = Iterator().sliced(20, 10);
assert(slice[1, 2] == 12);
auto sCopy = slice.save;
assert(slice[1, 2] == 12);
@trusted Slice!(T*) sliced(T)(T[] array);
Creates an 1-dimensional slice-shell over an array.
Parameters:
T[] array An array.
Returns:
1-dimensional slice
Examples:
Creates a slice from an array.
auto slice = new int[10].sliced;
assert(slice.length == 10);
static assert(is(typeof(slice) == Slice!(int*)));
Slice!(Iterator, N, kind) sliced(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, 1, kind) slice, size_t[N] lengths...)
if (N);
Creates an n-dimensional slice-shell over the 1-dimensional input slice.
Parameters:
Slice!(Iterator, 1, kind) slice slice
size_t[N] lengths A list of lengths for each dimension.
Returns:
n-dimensional slice
Examples:
import mir.ndslice.topology : iota;
auto data = new int[24];
foreach (i, ref e; data)
    e = cast(int)i;
auto a = data[0..10].sliced(10)[0..6].sliced(2, 3);
auto b = iota!int(10)[0..6].sliced(2, 3);
assert(a == b);
a[] += b;
foreach (i, e; data[0..6])
    assert(e == 2*i);
foreach (i, e; data[6..$])
    assert(e == i+6);
Slice!(FieldIterator!Field, N) slicedField(Field, size_t N)(Field field, size_t[N] lengths...)
if (N);

auto slicedField(Field)(Field field)
if (hasLength!Field);
Creates an n-dimensional slice-shell over a field.
Parameters:
Field field A field. The length of the array should be equal to or less then the product of lengths.
size_t[N] lengths A list of lengths for each dimension.
Returns:
n-dimensional slice
Examples:
Creates an 1-dimensional slice over a field, array, or random access range.
import mir.ndslice.topology : iota;
auto slice = 10.iota.slicedField;
assert(slice.length == 10);
Slice!(IndexIterator!(FieldIterator!(ndIotaField!N), ndField), N) slicedNdField(ndField, size_t N)(ndField field, size_t[N] lengths...)
if (N);

auto slicedNdField(ndField)(ndField field)
if (hasShape!ndField);
Creates an n-dimensional slice-shell over an ndField.
Parameters:
ndField field A ndField. Lengths should fit into field's shape.
size_t[N] lengths A list of lengths for each dimension.
Returns:
n-dimensional slice
See Also:
concatenation  examples.
struct CoordinateValue(T, size_t N = 1);
Combination of coordinate(s) and value.
size_t[N] index;
T value;
const int opCmp()(auto ref scope const typeof(this) rht);
struct Structure(size_t N);
size_t[N] lengths;
sizediff_t[N] strides;
struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_...) if (0 < N_ && (N_ < 255) && !(kind_ == Canonical && (N_ == 1)) && (Labels_.length <= N_) && isIterator!Iterator_);

alias Slice = mir_slice(Iterator_, ulong N_ = 1, mir_slice_kind kind_ = Contiguous, Labels_...) if (0 < N_ && (N_ < 255) && !(kind_ == Canonical && (N_ == 1)) && (Labels_.length <= N_) && isIterator!Iterator_);
Presents an n-dimensional view over a range.

Definitions

In order to change data in a slice using overloaded operators such as =, +=, ++, a syntactic structure of type <slice to change>[<index and interval sequence...>] must be used. It is worth noting that just like for regular arrays, operations a = b and a[] = b have different meanings. In the first case, after the operation is carried out, a simply points at the same data as b does, and the data which a previously pointed at remains unmodified. Here, а and b must be of the same type. In the second case, a points at the same data as before, but the data itself will be changed. In this instance, the number of dimensions of b may be less than the number of dimensions of а; and b can be a Slice, a regular multidimensional array, or simply a value (e.g. a number).
In the following table you will find the definitions you might come across in comments on operator overloading.
Operator Overloading Examples at N == 3
An interval is a part of a sequence of type i .. j. 2..$-3, 0..4
An index is a part of a sequence of type i. 3, $-1
A partially defined slice is a sequence composed of intervals and indices with an overall length strictly less than N. [3], [0..$], [3, 3], [0..$,0..3], [0..$,2]
A fully defined index is a sequence composed only of indices with an overall length equal to N. [2,3,1]
A fully defined slice is an empty sequence or a sequence composed of indices and at least one interval with an overall length equal to N. [], [3..$,0..3,0..$-1], [2,0..$,1]
An indexed slice is syntax sugar for indexed  and cartesian . [anNdslice], [$.iota, anNdsliceForCartesian1, $.iota]
See Also:
iota .

Internal Binary Representation

Multidimensional Slice is a structure that consists of lengths, strides, and a iterator (pointer).
FieldIterator  shell is used to wrap fields and random access ranges. FieldIterator contains a shift of the current initial element of a multidimensional slice and the field itself.
With the exception of mir.ndslice.allocation module, no functions in this package move or copy data. The operations are only carried out on lengths, strides, and pointers. If a slice is defined over a range, only the shift of the initial element changes instead of the range.
Mir n-dimensional Slices can be one of the three kinds.

Contiguous slice

Contiguous in memory (or in a user-defined iterator's field) row-major tensor that doesn't store strides because they can be computed on the fly using lengths. The row stride is always equaled 1.

Canonical slice

Canonical slice as contiguous in memory (or in a user-defined iterator's field) rows of a row-major tensor, it doesn't store the stride for row dimension because it is always equaled 1. BLAS/LAPACK matrices are Canonical but originally have column-major order. In the same time you can use 2D Canonical Slices with LAPACK assuming that rows are columns and columns are rows.

Universal slice

A row-major tensor that stores the strides for all dimensions. NumPy strides are Universal.

Internal Representation for Universal Slices

Type definition
Slice!(Iterator, N, Universal)
Schema
Slice!(Iterator, N, Universal)
    size_t[N]     _lengths
    sizediff_t[N] _strides
    Iterator      _iterator
Example
Definitions
import mir.ndslice;
auto a = new double[24];
Slice!(double*, 3, Universal) s = a.sliced(2, 3, 4).universal;
Slice!(double*, 3, Universal) t = s.transposed!(1, 2, 0);
Slice!(double*, 3, Universal) r = t.reversed!1;
Representation
s________________________
    lengths[0] ::=  2
    lengths[1] ::=  3
    lengths[2] ::=  4

    strides[0] ::= 12
    strides[1] ::=  4
    strides[2] ::=  1

    iterator        ::= &a[0]

t____transposed!(1, 2, 0)
    lengths[0] ::=  3
    lengths[1] ::=  4
    lengths[2] ::=  2

    strides[0] ::=  4
    strides[1] ::=  1
    strides[2] ::= 12

    iterator        ::= &a[0]

r______________reversed!1
    lengths[0] ::=  2
    lengths[1] ::=  3
    lengths[2] ::=  4

    strides[0] ::= 12
    strides[1] ::= -4
    strides[2] ::=  1

    iterator        ::= &a[8] // (old_strides[1] * (lengths[1] - 1)) = 8

Internal Representation for Canonical Slices

Type definition
Slice!(Iterator, N, Canonical)
Schema
Slice!(Iterator, N, Canonical)
    size_t[N]       _lengths
    sizediff_t[N-1] _strides
    Iterator        _iterator

Internal Representation for Contiguous Slices

Type definition
Slice!(Iterator, N)
Schema
Slice!(Iterator, N, Contiguous)
    size_t[N]     _lengths
    sizediff_t[0] _strides
    Iterator      _iterator
Examples:
Slicing, indexing, and arithmetic operations.
import mir.ndslice.allocation;
import mir.ndslice.dynamic : transposed;
import mir.ndslice.topology : iota, universal;
auto tensor = iota(3, 4, 5).slice;

assert(tensor[1, 2] == tensor[1][2]);
assert(tensor[1, 2, 3] == tensor[1][2][3]);

assert( tensor[0..$, 0..$, 4] == tensor.universal.transposed!2[4]);
assert(&tensor[0..$, 0..$, 4][1, 2] is &tensor[1, 2, 4]);

tensor[1, 2, 3]++; //`opIndex` returns value by reference.
--tensor[1, 2, 3]; //`opUnary`

++tensor[];
tensor[] -= 1;

// `opIndexAssing` accepts only fully defined indices and slices.
// Use an additional empty slice `[]`.
static assert(!__traits(compiles, tensor[0 .. 2] *= 2));

tensor[0 .. 2][] *= 2;          //OK, empty slice
tensor[0 .. 2, 3, 0..$] /= 2; //OK, 3 index or slice positions are defined.

//fully defined index may be replaced by a static array
size_t[3] index = [1, 2, 3];
assert(tensor[index] == tensor[1, 2, 3]);
Examples:
Operations with rvalue slices.
import mir.ndslice.allocation;
import mir.ndslice.topology: universal;
import mir.ndslice.dynamic: transposed, everted;

auto tensor = slice!int(3, 4, 5).universal;
auto matrix = slice!int(3, 4).universal;
auto vector = slice!int(3);

foreach (i; 0..3)
    vector[i] = i;

// fills matrix columns
matrix.transposed[] = vector;

// fills tensor with vector
// transposed tensor shape is (4, 5, 3)
//            vector shape is (      3)
tensor.transposed!(1, 2)[] = vector;

// transposed tensor shape is (5, 3, 4)
//            matrix shape is (   3, 4)
tensor.transposed!2[] += matrix;

// transposed tensor shape is (5, 4, 3)
// transposed matrix shape is (   4, 3)
tensor.everted[] ^= matrix.transposed; // XOR
Examples:
Creating a slice from text. See also std.format.
import mir.algorithm.iteration: filter, all;
import mir.array.allocation;
import mir.exception;
import mir.functional: not;
import mir.ndslice.allocation;
import mir.parse;
import mir.primitives: empty;

import std.algorithm: map;
import std.string: lineSplitter, split;

    // std.functional, std.string, std.range;

Slice!(int*, 2) toMatrix(string str)
{
    string[][] data = str.lineSplitter.filter!(not!empty).map!split.array;

    size_t rows    = data   .length.enforce!"empty input";
    size_t columns = data[0].length.enforce!"empty first row";

    data.all!(a => a.length == columns).enforce!"rows have different lengths";
    auto slice = slice!int(rows, columns);
    foreach (i, line; data)
        foreach (j, num; line)
            slice[i, j] = num.fromString!int;
    return slice;
}

auto input = "\r1 2  3\r\n 4 5 6\n";

auto matrix = toMatrix(input);
assert(matrix == [[1, 2, 3], [4, 5, 6]]);

// back to text
import std.format;
auto text2 = format("%(%(%s %)\n%)\n", matrix);
assert(text2 == "1 2 3\n4 5 6\n");
enum SliceKind kind;
enum size_t N;
Dimensions count
enum size_t S;
Strides count
enum size_t L;
Labels count.
alias Iterator = Iterator_;
Data iterator type
alias This = Slice!(Iterator, N, kind);
This type
alias DeepElement = typeof(Iterator.init[size_t.init]);
Data element type
alias serdeKeysProxy = DeepElement;
alias Labels = Labels_;
Label Iterators types
template Element(size_t dimension) if (dimension < N)
alias _Structure = AliasSeq!(size_t[N], ptrdiff_t[S]);
_Structure _structure;
alias _lengths = _structure[0];
alias _strides = _structure[1];
Iterator _iterator;
Data Iterator
Labels _labels;
Labels iterators
@property scope auto lightScope()() return;

const @property scope auto lightScope()() return;

immutable @property scope auto lightScope()() return;
Returns:
View with stripped out reference counted context. The lifetime of the result mustn't be longer then the lifetime of the original slice.
immutable @property scope Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightImmutable()() return;
Returns:
Mutable slice over immutable data.
const @property scope @trusted Slice!(LightConstOf!Iterator, N, kind, staticMap!(LightConstOf, Labels)) lightConst()() return;

immutable @property scope Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightConst()() return;
Returns:
Mutable slice over const data.
@property Slice!(Labels[d]) label(size_t d = 0)()
if (d <= L);

@property void label(size_t d = 0)(Slice!(Labels[d]) rhs)
if (d <= L);

const @property Slice!(LightConstOf!(Labels[d])) label(size_t d = 0)()
if (d <= L);

immutable @property Slice!(LightImmutableOf!(Labels[d])) label(size_t d = 0)()
if (d <= L);
Label for the dimensions 'd'. By default returns the row label.
@property auto values()();

const @property auto values()();

immutable @property auto values()();
Strips label off the DataFrame
const ref @trusted auto opIndex(Indexes...)(Indexes indices)
if (isPureSlice!Indexes || isIndexedSlice!Indexes);
opIndex overload for const slice
immutable ref @trusted auto opIndex(Indexes...)(Indexes indices)
if (isPureSlice!Indexes || isIndexedSlice!Indexes);
opIndex overload for immutable slice
immutable pure nothrow @nogc scope @trusted auto toImmutable()() return;

const pure nothrow @nogc scope @trusted auto toConst()() return;
Cast to const and immutable slices in case of underlying range is a pointer.
inout @property scope auto iterator()() return;
Iterator
Returns:
Iterator (pointer) to the .Slice.first element.
alias ptr = iterator;
ptr alias is available only if the slice kind is Contiguous contiguous and the .Slice.iterator is a pointers.
@property scope @trusted auto field()() return;

const @property scope @trusted auto field()() return;

immutable @property scope @trusted auto field()() return;
Field (array) data.
Returns:
Raw data slice.

Constraints Field is defined only for contiguous slices.

Examples:
auto arr = [1, 2, 3, 4];
auto sl0 = arr.sliced;
auto sl1 = arr.slicedField;

assert(sl0.field is arr);
assert(sl1.field is arr);

arr = arr[1 .. $];
sl0 = sl0[1 .. $];
sl1 = sl1[1 .. $];

assert(sl0.field is arr);
assert(sl1.field is arr);
assert((cast(const)sl1).field is arr);
()@trusted{ assert((cast(immutable)sl1).field is arr); }();
const @property scope @trusted size_t[N] shape()();
Returns:
static array of lengths
Examples:
Regular slice
import mir.ndslice.topology : iota;
assert(iota(3, 4, 5).shape == cast(size_t[3])[3, 4, 5]);
Examples:
Packed slice
import mir.ndslice.topology : pack, iota;
size_t[3] s = [3, 4, 5];
assert(iota(3, 4, 5, 6, 7).pack!2.shape == s);
const @property scope @trusted ptrdiff_t[N] strides()();
Returns:
static array of lengths
Examples:
Regular slice
import mir.ndslice.topology : iota;
size_t[3] s = [20, 5, 1];
assert(iota(3, 4, 5).strides == s);
Examples:
Modified regular slice
import mir.ndslice.topology : pack, iota, universal;
import mir.ndslice.dynamic : reversed, strided, transposed;
assert(iota(3, 4, 50)
    .universal
    .reversed!2      //makes stride negative
    .strided!2(6)    //multiplies stride by 6 and changes corresponding length
    .transposed!2    //brings dimension `2` to the first position
    .strides == cast(ptrdiff_t[3])[-6, 200, 50]);
Examples:
Packed slice
import mir.ndslice.topology : pack, iota;
size_t[3] s = [20 * 42, 5 * 42, 1 * 42];
assert(iota(3, 4, 5, 6, 7)
    .pack!2
    .strides == s);
const @property scope @safe Structure!N structure()();
Returns:
static array of lengths and static array of strides
See Also:
Examples:
Regular slice
import mir.ndslice.topology : iota;
assert(iota(3, 4, 5)
    .structure == Structure!3([3, 4, 5], [20, 5, 1]));
Examples:
Modified regular slice
import mir.ndslice.topology : pack, iota, universal;
import mir.ndslice.dynamic : reversed, strided, transposed;
assert(iota(3, 4, 50)
    .universal
    .reversed!2      //makes stride negative
    .strided!2(6)    //multiplies stride by 6 and changes corresponding length
    .transposed!2    //brings dimension `2` to the first position
    .structure == Structure!3([9, 3, 4], [-6, 200, 50]));
Examples:
Packed slice
import mir.ndslice.topology : pack, iota;
assert(iota(3, 4, 5, 6, 7)
    .pack!2
    .structure == Structure!3([3, 4, 5], [20 * 42, 5 * 42, 1 * 42]));
inout @property scope auto save()() return;
Save primitive.
Examples:
Save range
import mir.ndslice.topology : iota;
auto slice = iota(2, 3).save;
Examples:
Pointer type.
import mir.ndslice.allocation;
//sl type is `Slice!(2, int*)`
auto sl = slice!int(2, 3).save;
const @property scope @safe size_t length(size_t dimension = 0)()
if (dimension < N);
Multidimensional length property.
Returns:
length of the corresponding dimension
Examples:
import mir.ndslice.topology : iota;
auto slice = iota(3, 4, 5);
assert(slice.length   == 3);
assert(slice.length!0 == 3);
assert(slice.length!1 == 4);
assert(slice.length!2 == 5);
const @property scope @safe sizediff_t _stride(size_t dimension = 0)()
if (dimension < N);
Multidimensional stride property.
Returns:
stride of the corresponding dimension
Examples:
Regular slice
import mir.ndslice.topology : iota;
auto slice = iota(3, 4, 5);
assert(slice._stride   == 20);
assert(slice._stride!0 == 20);
assert(slice._stride!1 == 5);
assert(slice._stride!2 == 1);
Examples:
Modified regular slice
import mir.ndslice.dynamic : reversed, strided, swapped;
import mir.ndslice.topology : universal, iota;
assert(iota(3, 4, 50)
    .universal
    .reversed!2      //makes stride negative
    .strided!2(6)    //multiplies stride by 6 and changes the corresponding length
    .swapped!(1, 2)  //swaps dimensions `1` and `2`
    ._stride!1 == -6);
const @property scope @safe bool empty(size_t dimension = 0)()
if (dimension < N);

@property ref scope @trusted auto front(size_t dimension = 0)() return
if (dimension == 0);

const @property ref scope @trusted auto front(size_t dimension = 0)() return
if (dimension == 0);

immutable @property ref scope @trusted auto front(size_t dimension = 0)() return
if (dimension == 0);

@property ref scope @trusted auto front(size_t dimension = 0, T)(T value) return
if (dimension == 0);

@property ref scope @trusted Element!dimension back(size_t dimension = 0)() return
if (dimension < N);

@property ref scope @trusted auto back(size_t dimension = 0, T)(T value) return
if (dimension == 0);

@trusted void popFront(size_t dimension = 0)()
if (dimension < N && (dimension == 0 || kind != Contiguous));

scope @safe void popBack(size_t dimension = 0)()
if (dimension < N && (dimension == 0 || kind != Contiguous));

scope @trusted void popFrontExactly(size_t dimension = 0)(size_t n)
if (dimension < N && (dimension == 0 || kind != Contiguous));

scope @safe void popBackExactly(size_t dimension = 0)(size_t n)
if (dimension < N && (dimension == 0 || kind != Contiguous));

scope @trusted void popFrontN(size_t dimension = 0)(size_t n)
if (dimension < N && (dimension == 0 || kind != Contiguous));

scope @safe void popBackN(size_t dimension = 0)(size_t n)
if (dimension < N && (dimension == 0 || kind != Contiguous));
Multidimensional input range primitive.
Examples:
import std.range.primitives;
import mir.ndslice.topology : iota, canonical;
auto slice = iota(10, 20, 30).canonical;

static assert(isRandomAccessRange!(typeof(slice)));
static assert(hasSlicing!(typeof(slice)));
static assert(hasLength!(typeof(slice)));

assert(slice.shape == cast(size_t[3])[10, 20, 30]);
slice.popFront;
slice.popFront!1;
slice.popBackExactly!2(4);
assert(slice.shape == cast(size_t[3])[9, 19, 26]);

auto matrix = slice.front!1;
assert(matrix.shape == cast(size_t[2])[9, 26]);

auto column = matrix.back!1;
assert(column.shape == cast(size_t[1])[9]);

slice.popFrontExactly!1(slice.length!1);
assert(slice.empty   == false);
assert(slice.empty!1 == true);
assert(slice.empty!2 == false);
assert(slice.shape == cast(size_t[3])[9, 0, 26]);

assert(slice.back.front!1.empty);

slice.popFrontN!0(40);
slice.popFrontN!2(40);
assert(slice.shape == cast(size_t[3])[0, 0, 0]);
@property ref scope @trusted auto first()() return;

@property ref scope @trusted auto first(T)(T value) return;
Accesses the first deep element of the slice.
Examples:
import mir.ndslice.topology: iota, universal, canonical;
auto f = 5;
assert([2, 3].iota(f).first == f);
@property ref scope @trusted auto last()() return;

@property ref scope @trusted auto last(T)(T value) return;
Accesses the last deep element of the slice.
Examples:
import mir.ndslice.topology: iota;
auto f = 5;
assert([2, 3].iota(f).last == f + 2 * 3 - 1);
void popFrontAll();
Peforms popFrontAll for all dimensions
Examples:
import mir.ndslice.topology: iota, canonical;
auto v = [2, 3].iota.canonical;
v.popFrontAll;
assert(v == [[4, 5]]);
void popBackAll();
Peforms popBackAll for all dimensions
Examples:
import mir.ndslice.topology: iota, canonical;
auto v = [2, 3].iota.canonical;
v.popBackAll;
assert(v == [[0, 1]]);
const @property scope @trusted bool anyEmpty()();
Returns:
true if for any dimension the length equals to 0, and false otherwise.
Examples:
import mir.ndslice.topology : iota, canonical;
auto s = iota(2, 3).canonical;
assert(!s.anyEmpty);
s.popFrontExactly!1(3);
assert(s.anyEmpty);
ref scope auto backward()(size_t[N] index) return;

const ref scope auto backward()(size_t[N] index) return;

const ref scope auto backward()(size_t[N] index) return;
Convenience function for backward indexing.
Returns:
this[$-index[0], $-index[1], ..., $-index[N-1]]
Examples:
import mir.ndslice.topology : iota;
auto s = iota(2, 3);
assert(s[$ - 1, $ - 2] == s.backward([1, 2]));
const @property scope @safe size_t elementCount()();
Returns:
Total number of elements in a slice
Examples:
Regular slice
import mir.ndslice.topology : iota;
assert(iota(3, 4, 5).elementCount == 60);
Examples:
Packed slice
import mir.ndslice.topology : pack, evertPack, iota;
auto slice = iota(3, 4, 5, 6, 7, 8);
auto p = slice.pack!2;
assert(p.elementCount == 360);
assert(p[0, 0, 0, 0].elementCount == 56);
assert(p.evertPack.elementCount == 56);
@trusted auto select(size_t dimension)(size_t begin, size_t end);
Slice selected dimension.
Parameters:
size_t begin initial index of the sub-slice (inclusive)
size_t end final index of the sub-slice (noninclusive)
Returns:
ndslice with length!dimension equal to end - begin.
Examples:
import mir.ndslice.topology : iota;
auto sl = iota(3, 4);
assert(sl.select!1(1, 3) == sl[0 .. $, 1 .. 3]);
scope auto selectFront(size_t dimension)(size_t n) return;
Select the first n elements for the dimension.
Parameters:
dimension Dimension to slice.
size_t n count of elements for the dimension
Returns:
ndslice with length!dimension equal to n.
Examples:
import mir.ndslice.topology : iota;
auto sl = iota(3, 4);
assert(sl.selectFront!1(2) == sl[0 .. $, 0 .. 2]);
scope auto selectBack(size_t dimension)(size_t n) return;

const scope @trusted bool opEquals(IteratorR, SliceKind rkind)(auto ref const Slice!(IteratorR, N, rkind) rslice);

const scope @trusted bool opEquals(T)(scope const(T)[] arr);
Select the last n elements for the dimension.
Parameters:
dimension Dimension to slice.
size_t n count of elements for the dimension
Returns:
ndslice with length!dimension equal to n.
Examples:
import mir.ndslice.topology : iota;
auto sl = iota(3, 4);
assert(sl.selectBack!1(2) == sl[0 .. $, $ - 2 .. $]);
Examples:
auto a = [1, 2, 3, 4].sliced(2, 2);

assert(a != [1, 2, 3, 4, 5, 6].sliced(2, 3));
assert(a != [[1, 2, 3], [4, 5, 6]]);

assert(a == [1, 2, 3, 4].sliced(2, 2));
assert(a == [[1, 2], [3, 4]]);

assert(a != [9, 2, 3, 4].sliced(2, 2));
assert(a != [[9, 2], [3, 4]]);
const scope @safe Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j)
if (dimension < N);
Slice!(IotaIterator!size_t) is the basic type for [a .. b] syntax for all ndslice based code.
ref scope @trusted auto opIndex()(size_t[N] _indices...) return;

const ref scope @trusted auto opIndex()(size_t[N] _indices...) return;

immutable ref scope @trusted auto opIndex()(size_t[N] _indices...) return;
scope @trusted auto opIndex(size_t I)(size_t[I] _indices...) return
if (I && (I < N));

const scope auto opIndex(size_t I)(size_t[I] _indices...) return
if (I && (I < N));

immutable scope auto opIndex(size_t I)(size_t[I] _indices...) return
if (I && (I < N));
scope @trusted auto opIndex(Slices...)(Slices slices) return
if (isPureSlice!Slices);
Examples:
import mir.ndslice.allocation;
auto slice = slice!int(5, 3);

/// Fully defined slice
assert(slice[] == slice);
auto sublice = slice[0..$-2, 1..$];

/// Partially defined slice
auto row = slice[3];
auto col = slice[0..$, 1];
scope auto opIndex(Slices...)(return scope Slices slices) return
if (isIndexedSlice!Slices);
Examples:
import mir.ndslice.allocation: slice;
auto sli = slice!int(4, 3);
auto idx = slice!(size_t[2])(3);
idx[] = [
    cast(size_t[2])[0, 2],
    cast(size_t[2])[3, 1],
    cast(size_t[2])[2, 0]];

// equivalent to:
// import mir.ndslice.topology: indexed;
// sli.indexed(indx)[] = 1;
sli[idx] = 1;

assert(sli == [
    [0, 0, 1],
    [0, 0, 0],
    [1, 0, 0],
    [0, 1, 0],
    ]);

foreach (row; sli[[1, 3].sliced])
    row[] += 2;

assert(sli == [
    [0, 0, 1],
    [2, 2, 2], // <--  += 2
    [1, 0, 0],
    [2, 3, 2], // <--  += 2
    ]);
Examples:
import mir.ndslice.topology: iota;
import mir.ndslice.allocation: slice;
auto sli = slice!int(5, 6);

// equivalent to
// import mir.ndslice.topology: indexed, cartesian;
// auto a = [0, sli.length!0 / 2, sli.length!0 - 1].sliced;
// auto b = [0, sli.length!1 / 2, sli.length!1 - 1].sliced;
// auto c = cartesian(a, b);
// auto minor = sli.indexed(c);
auto minor = sli[[0, $ / 2, $ - 1].sliced, [0, $ / 2, $ - 1].sliced];

minor[] = iota!int([3, 3], 1);

assert(sli == [
//   ↓     ↓        ↓︎
    [1, 0, 0, 2, 0, 3], // <---
    [0, 0, 0, 0, 0, 0],
    [4, 0, 0, 5, 0, 6], // <---
    [0, 0, 0, 0, 0, 0],
    [7, 0, 0, 8, 0, 9], // <---
    ]);
scope auto opUnary(string op)() return
if (op == "*" || op == "~" || op == "-" || op == "+");
Element-wise binary operator overloading.
Returns:
lazy slice of the same kind and the same structure

Note Does not allocate neither new slice nor a closure.

Examples:
import mir.ndslice.topology;

auto payload = [1, 2, 3, 4];
auto s = iota([payload.length], payload.ptr); // slice of references;
assert(s[1] == payload.ptr + 1);

auto c = *s; // the same as s.map!"*a"
assert(c[1] == *s[1]);

*s[1] = 3;
assert(c[1] == *s[1]);
scope auto opBinary(string op, T)(return scope T value) return
if (!isSlice!T);

scope auto opBinaryRight(string op, T)(return scope T value) return
if (!isSlice!T);
Element-wise operator overloading for scalars.
Parameters:
T value a scalar
Returns:
lazy slice of the same kind and the same structure

Note Does not allocate neither new slice nor a closure.

Examples:
import mir.ndslice.topology;

// 0 1 2 3
auto s = iota([4]);
// 0 1 2 0
assert(s % 3 == iota([4]).map!"a % 3");
// 0 2 4 6
assert(2 * s == iota([4], 0, 2));
Examples:
import mir.ndslice.topology;

// 0 1 2 3
auto s = iota([4]);
// 0 1 4 9
assert(s ^^ 2.0 == iota([4]).map!"a ^^ 2.0");
scope auto opBinary(string op, RIterator, size_t RN, SliceKind rkind)(return scope Slice!(RIterator, RN, rkind) rhs) return
if (N == RN && (kind == Contiguous && (rkind == Contiguous) || N == 1) && (op != "~"));
Element-wise operator overloading for slices.
Parameters:
Slice!(RIterator, RN, rkind) rhs a slice of the same shape.
Returns:
lazy slice the same shape that has Contiguous kind

Note Binary operator overloading is allowed if both slices are contiguous or one-dimensional.
Does not allocate neither new slice nor a closure.

Examples:
import mir.ndslice.topology: iota, map, zip;

auto s = iota([2, 3]);
auto c = iota([2, 3], 5, 8);
assert(s * s + c == s.map!"a * a".zip(c).map!"a + b");
@property scope Slice!(Unqual!DeepElement*, N) dup()();

const @property scope Slice!(immutable(DeepElement)*, N) dup()();

immutable @property scope Slice!(immutable(DeepElement)*, N) dup()();
Duplicates slice.
Returns:
GC-allocated Contiguous mutable slice.
See Also:
Examples:
import mir.ndslice;
auto x = 3.iota!int;
Slice!(immutable(int)*) imm = x.idup;
Slice!(int*) mut = imm.dup;
assert(imm == x);
assert(mut == x);
@property scope Slice!(immutable(DeepElement)*, N) idup()();

const @property scope Slice!(immutable(DeepElement)*, N) idup()();

immutable @property scope Slice!(immutable(DeepElement)*, N) idup()();
Duplicates slice.
Returns:
GC-allocated Contiguous immutable slice.
See Also:
Examples:
import mir.ndslice;
auto x = 3.iota!int;
Slice!(int*) mut = x.dup;
Slice!(immutable(int)*) imm = mut.idup;
assert(imm == x);
assert(mut == x);
ref scope @trusted auto accessFlat(size_t index) return;
Provides access to a slice as if it were flattened.
Parameters:
size_t index location in slice
Returns:
value of flattened slice at index
See Also:
Examples:
import mir.ndslice.topology: iota, flattened;

auto x = iota(2, 3, 4);
assert(x.accessFlat(9) == x.flattened[9]);
scope void opIndexAssign(RIterator, size_t RN, SliceKind rkind, Slices...)(Slice!(RIterator, RN, rkind) value, Slices slices) return
if (isFullPureSlice!Slices || isIndexedSlice!Slices);
Assignment of a value of Slice type to a fully defined slice.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);
auto b = [1, 2, 3, 4].sliced(2, 2);

a[0..$, 0..$-1] = b;
assert(a == [[1, 2, 0], [3, 4, 0]]);

// fills both rows with b[0]
a[0..$, 0..$-1] = b[0];
assert(a == [[1, 2, 0], [1, 2, 0]]);

a[1, 0..$-1] = b[1];
assert(a[1] == [3, 4, 0]);

a[1, 0..$-1][] = b[0];
assert(a[1] == [1, 2, 0]);
Examples:
Left slice is packed
import mir.ndslice.topology : blocks, iota;
import mir.ndslice.allocation : slice;
auto a = slice!int(4, 4);
a.blocks(2, 2)[] = iota!int(2, 2);

assert(a ==
        [[0, 0, 1, 1],
         [0, 0, 1, 1],
         [2, 2, 3, 3],
         [2, 2, 3, 3]]);
Examples:
Both slices are packed
import mir.ndslice.topology : blocks, iota, pack;
import mir.ndslice.allocation : slice;
auto a = slice!int(4, 4);
a.blocks(2, 2)[] = iota!int(2, 2, 2).pack!1;

assert(a ==
        [[0, 1, 2, 3],
         [0, 1, 2, 3],
         [4, 5, 6, 7],
         [4, 5, 6, 7]]);
scope void opIndexAssign(T, Slices...)(T[] value, Slices slices) return
if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && (!isDynamicArray!DeepElement || isDynamicArray!T) && (DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= (typeof(this.opIndex(slices))).N));
Assignment of a regular multidimensional array to a fully defined slice.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);
auto b = [[1, 2], [3, 4]];

a[] = [[1, 2, 3], [4, 5, 6]];
assert(a == [[1, 2, 3], [4, 5, 6]]);

a[0..$, 0..$-1] = [[1, 2], [3, 4]];
assert(a == [[1, 2, 3], [3, 4, 6]]);

a[0..$, 0..$-1] = [1, 2];
assert(a == [[1, 2, 3], [1, 2, 6]]);

a[1, 0..$-1] = [3, 4];
assert(a[1] == [3, 4, 6]);

a[1, 0..$-1][] = [3, 4];
assert(a[1] == [3, 4, 6]);
Examples:
Packed slices
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : blocks;
auto a = slice!int(4, 4);
a.blocks(2, 2)[] = [[0, 1], [2, 3]];

assert(a ==
        [[0, 0, 1, 1],
         [0, 0, 1, 1],
         [2, 2, 3, 3],
         [2, 2, 3, 3]]);
scope void opIndexAssign(T, Slices...)(T concatenation, Slices slices) return
if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T);
scope void opIndexAssign(T, Slices...)(T value, Slices slices)
if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && (!isDynamicArray!T || isDynamicArray!DeepElement) && (DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement) && !isSlice!T && !isConcatenation!T);
Assignment of a value (e.g. a number) to a fully defined slice.
ref scope @trusted auto opIndexAssign(T)(T value, size_t[N] _indices...) return;

ref scope @trusted auto opIndexAssign()(DeepElement value, size_t[N] _indices...) return;
Assignment of a value (e.g. a number) to a fully defined index.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);

a[1, 2] = 3;
assert(a[1, 2] == 3);
ref scope @trusted auto opIndexOpAssign(string op, T)(T value, size_t[N] _indices...) return;
Op Assignment op= of a value (e.g. a number) to a fully defined index.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);

a[1, 2] += 3;
assert(a[1, 2] == 3);
scope void opIndexOpAssign(string op, RIterator, SliceKind rkind, size_t RN, Slices...)(Slice!(RIterator, RN, rkind) value, Slices slices) return
if (isFullPureSlice!Slices || isIndexedSlice!Slices);
Op Assignment op= of a value of Slice type to a fully defined slice.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);
auto b = [1, 2, 3, 4].sliced(2, 2);

a[0..$, 0..$-1] += b;
assert(a == [[1, 2, 0], [3, 4, 0]]);

a[0..$, 0..$-1] += b[0];
assert(a == [[2, 4, 0], [4, 6, 0]]);

a[1, 0..$-1] += b[1];
assert(a[1] == [7, 10, 0]);

a[1, 0..$-1][] += b[0];
assert(a[1] == [8, 12, 0]);
Examples:
Left slice is packed
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : blocks, iota;
auto a = slice!size_t(4, 4);
a.blocks(2, 2)[] += iota(2, 2);

assert(a ==
        [[0, 0, 1, 1],
         [0, 0, 1, 1],
         [2, 2, 3, 3],
         [2, 2, 3, 3]]);
Examples:
Both slices are packed
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : blocks, iota, pack;
auto a = slice!size_t(4, 4);
a.blocks(2, 2)[] += iota(2, 2, 2).pack!1;

assert(a ==
        [[0, 1, 2, 3],
         [0, 1, 2, 3],
         [4, 5, 6, 7],
         [4, 5, 6, 7]]);
scope void opIndexOpAssign(string op, T, Slices...)(T[] value, Slices slices) return
if (isFullPureSlice!Slices && (!isDynamicArray!DeepElement || isDynamicArray!T) && (DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= (typeof(this.opIndex(slices))).N));
Op Assignment op= of a regular multidimensional array to a fully defined slice.
Examples:
import mir.ndslice.allocation : slice;
auto a = slice!int(2, 3);

a[0..$, 0..$-1] += [[1, 2], [3, 4]];
assert(a == [[1, 2, 0], [3, 4, 0]]);

a[0..$, 0..$-1] += [1, 2];
assert(a == [[2, 4, 0], [4, 6, 0]]);

a[1, 0..$-1] += [3, 4];
assert(a[1] == [7, 10, 0]);

a[1, 0..$-1][] += [1, 2];
assert(a[1] == [8, 12, 0]);
Examples:
Packed slices
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : blocks;
auto a = slice!int(4, 4);
a.blocks(2, 2)[] += [[0, 1], [2, 3]];

assert(a ==
        [[0, 0, 1, 1],
         [0, 0, 1, 1],
         [2, 2, 3, 3],
         [2, 2, 3, 3]]);
scope void opIndexOpAssign(string op, T, Slices...)(T value, Slices slices) return
if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && (!isDynamicArray!T || isDynamicArray!DeepElement) && (DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement) && !isSlice!T && !isConcatenation!T);
Op Assignment op= of a value (e.g. a number) to a fully defined slice.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);

a[] += 1;
assert(a == [[1, 1, 1], [1, 1, 1]]);

a[0..$, 0..$-1] += 2;
assert(a == [[3, 3, 1], [3, 3, 1]]);

a[1, 0..$-1] += 3;
assert(a[1] == [6, 6, 1]);
scope void opIndexOpAssign(string op, T, Slices...)(T concatenation, Slices slices) return
if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T);
Examples:
Packed slices have the same behavior.
import mir.ndslice.allocation;
import mir.ndslice.topology : pack;
auto a = slice!int(2, 3).pack!1;

a[] += 9;
assert(a == [[9, 9, 9], [9, 9, 9]]);
ref scope @trusted auto opIndexUnary(string op)(size_t[N] _indices...) return;
Increment ++ and Decrement -- operators for a fully defined index.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);

++a[1, 2];
assert(a[1, 2] == 1);
scope void opIndexUnary(string op, Slices...)(Slices slices) return
if (isFullPureSlice!Slices && (op == "++" || op == "--"));
Increment ++ and Decrement -- operators for a fully defined slice.
Examples:
import mir.ndslice.allocation;
auto a = slice!int(2, 3);

++a[];
assert(a == [[1, 1, 1], [1, 1, 1]]);

--a[1, 0..$-1];

assert(a[1] == [0, 0, 1]);
@property auto ndassign(string op = "", L, R)(ref L lside, auto ref R rside)
if (!isSlice!L && (op.length == 0 || op[$ - 1] != '='));

@property auto ndassign(string op = "", L, R)(L lside, auto ref R rside)
if (isSlice!L && (op.length == 0 || op[$ - 1] != '='));
Assignment utility for generic code that works both with scalars and with ndslices.
Parameters:
op assign operation (generic, optional)
L lside left side
R rside right side
Returns:
expression value
Examples:
import mir.ndslice.topology: iota;
import mir.ndslice.allocation: slice;
auto scalar = 3;
auto vector = 3.iota.slice; // [0, 1, 2]

// scalar = 5;
scalar.ndassign = 5;
assert(scalar == 5);

// vector[] = vector * 2;
vector.ndassign = vector * 2;
assert(vector == [0, 2, 4]);

// vector[] += scalar;
vector.ndassign!"+"= scalar;
assert(vector == [5, 7, 9]);