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.topology

This is a submodule of mir.ndslice.
Selectors create new views and iteration patterns over the same data, without copying.

Sequence Selectors

Function Name Description
cycle Cycle repeates 1-dimensional field/range/array/slice in a fixed length 1-dimensional slice
iota Contiguous Slice with initial flattened (contiguous) index.
linspace Evenly spaced numbers over a specified interval.
magic Magic square.
ndiota Contiguous Slice with initial multidimensional index.
repeat Slice with identical values

Shape Selectors

Function Name Description
blocks n-dimensional slice composed of n-dimensional non-overlapping blocks. If the slice has two dimensions, it is a block matrix.
diagonal 1-dimensional slice composed of diagonal elements
dropBorders Drops borders for all dimensions.
reshape New slice view with changed dimensions
squeeze New slice view of an n-dimensional slice with dimension removed
unsqueeze New slice view of an n-dimensional slice with a dimension added
windows n-dimensional slice of n-dimensional overlapping windows. If the slice has two dimensions, it is a sliding window.

Subspace Selectors

Function Name Description
alongDim Returns a slice that can be iterated along dimension.
byDim Returns a slice that can be iterated by dimension.
pack Returns slice of slices.
ipack Returns slice of slices.
unpack Merges two hight dimension packs. See also fuse .
evertPack Reverses dimension packs.

SliceKind Selectors

Function Name Description
asKindOf Converts a slice to a user provied kind SliceKind .
universal Converts a slice to universal SliceKind .
canonical Converts a slice to canonical SliceKind .
assumeCanonical Converts a slice to canonical SliceKind . Does only assert checks.
assumeContiguous Converts a slice to contiguous SliceKind . Does only assert checks.
assumeHypercube Helps the compiler to use optimisations related to the shape form. Does only assert checks.
assumeSameShape Helps the compiler to use optimisations related to the shape form. Does only assert checks.

Products

Function Name Description
cartesian Cartesian product.
kronecker Kronecker product.

Representation Selectors

Function Name Description
as Convenience function that creates a lazy view, where each element of the original slice is converted to a type T.
bitpack Bitpack slice over an unsigned integral slice.
bitwise Bitwise slice over an unsigned integral slice.
bytegroup Groups existing slice into fixed length chunks and uses them as data store for destination type.
cached Random access cache. It is usefull in combiation with map and vmap.
cachedGC Random access cache auto-allocated in GC heap. It is usefull in combiation with map and vmap.
diff Differences between vector elements.
flattened Contiguous 1-dimensional slice of all elements of a slice.
map Multidimensional functional map.
member Field (element's member) projection.
orthogonalReduceField Functional deep-element wise reduce of a slice composed of fields or iterators.
pairwise Pairwise map for vectors.
pairwiseMapSubSlices Maps pairwise index pairs to subslices.
retro Reverses order of iteration for all dimensions.
slide Lazy convolution for tensors.
slideAlong Lazy convolution for tensors.
stairs Two functions to pack, unpack, and iterate triangular and symmetric matrix storage.
stride Strides 1-dimensional slice.
subSlices Maps index pairs to subslices.
triplets Constructs a lazy view of triplets with left, center, and right members. The topology is usefull for Math and Physics.
unzip Selects a slice from a zipped slice.
withNeighboursSum Zip view of elements packed with sum of their neighbours.
zip Zips slices into a slice of refTuples.
Subspace selectors serve to generalize and combine other selectors easily. For a slice of Slice!(Iterator, N, kind) type slice.pack!K creates a slice of slices of Slice!(kind, [N - K, K], Iterator) type by packing the last K dimensions of the top dimension pack, and the type of element of flattened is Slice!(Iterator, K). Another way to use pack is transposition of dimension packs using evertPack. Examples of use of subspace selectors are available for selectors, Slice.shape , and Slice.elementCount .
License:
Authors:
Ilya Yaroshenko, John Michael Hall, Shigeki Karita (original numir code)

Sponsors Part of this work has been sponsored by Symmetry Investments and Kaleidic Associates.

template asKindOf(SliceKind kind)
Converts a slice to user provided kind.
Contiguous slices can be converted to any kind. Canonical slices can't be converted to contiguous slices. Universal slices can be converted only to the same kind.
Examples:
Universal
import mir.ndslice.slice: Universal;
auto slice = iota(2, 3).asKindOf!Universal;
assert(slice == [[0, 1, 2], [3, 4, 5]]);
assert(slice._lengths == [2, 3]);
assert(slice._strides == [3, 1]);
Examples:
Canonical
import mir.ndslice.slice: Canonical;
auto slice = iota(2, 3).asKindOf!Canonical;
assert(slice == [[0, 1, 2], [3, 4, 5]]);
assert(slice._lengths == [2, 3]);
assert(slice._strides == [3]);
Examples:
Contiguous
import mir.ndslice.slice: Contiguous;
auto slice = iota(2, 3).asKindOf!Contiguous;
assert(slice == [[0, 1, 2], [3, 4, 5]]);
assert(slice._lengths == [2, 3]);
assert(slice._strides == []);
auto universal(Iterator, size_t N, SliceKind kind, Labels...)(Slice!(Iterator, N, kind, Labels) slice);
Converts a slice to universal kind.
Parameters:
Slice!(Iterator, N, kind, Labels) slice a slice
Returns:
universal slice
Examples:
auto slice = iota(2, 3).universal;
assert(slice == [[0, 1, 2], [3, 4, 5]]);
assert(slice._lengths == [2, 3]);
assert(slice._strides == [3, 1]);
Examples:
import mir.ndslice.slice;
import mir.ndslice.allocation: slice;

auto dataframe = slice!(double, int, string)(2, 3);
dataframe.label[] = [1, 2];
dataframe.label!1[] = ["Label1", "Label2", "Label3"];

auto universaldf = dataframe.universal;
assert(universaldf._lengths == [2, 3]);
assert(universaldf._strides == [3, 1]);

assert(is(typeof(universaldf) ==
    Slice!(double*, 2, Universal, int*, string*)));
assert(universaldf.label!0[0] == 1);
assert(universaldf.label!1[1] == "Label2");
Slice!(Iterator, N, N == 1 ? Contiguous : Canonical, Labels) canonical(Iterator, size_t N, SliceKind kind, Labels...)(Slice!(Iterator, N, kind, Labels) slice)
if (kind == Contiguous || kind == Canonical);
Converts a slice to canonical kind.
Parameters:
Slice!(Iterator, N, kind, Labels) slice contiguous or canonical slice
Returns:
canonical slice
Examples:
auto slice = iota(2, 3).canonical;
assert(slice == [[0, 1, 2], [3, 4, 5]]);
assert(slice._lengths == [2, 3]);
assert(slice._strides == [3]);
Examples:
import mir.ndslice.slice;
import mir.ndslice.allocation: slice;

auto dataframe = slice!(double, int, string)(2, 3);
dataframe.label[] = [1, 2];
dataframe.label!1[] = ["Label1", "Label2", "Label3"];

auto canonicaldf = dataframe.canonical;
assert(canonicaldf._lengths == [2, 3]);
assert(canonicaldf._strides == [3]);

assert(is(typeof(canonicaldf) ==
    Slice!(double*, 2, Canonical, int*, string*)));
assert(canonicaldf.label!0[0] == 1);
assert(canonicaldf.label!1[1] == "Label2");
Slice!(Iterator, N, Canonical, Labels) assumeCanonical(Iterator, size_t N, SliceKind kind, Labels...)(Slice!(Iterator, N, kind, Labels) slice);
Converts a slice to canonical kind (unsafe).
Parameters:
Slice!(Iterator, N, kind, Labels) slice a slice
Returns:
canonical slice
Examples:
auto slice = iota(2, 3).universal.assumeCanonical;
assert(slice == [[0, 1, 2], [3, 4, 5]]);
assert(slice._lengths == [2, 3]);
assert(slice._strides == [3]);
Examples:
import mir.ndslice.slice;
import mir.ndslice.allocation: slice;

auto dataframe = slice!(double, int, string)(2, 3);
dataframe.label[] = [1, 2];
dataframe.label!1[] = ["Label1", "Label2", "Label3"];

auto assmcanonicaldf = dataframe.assumeCanonical;
assert(assmcanonicaldf._lengths == [2, 3]);
assert(assmcanonicaldf._strides == [3]);

assert(is(typeof(assmcanonicaldf) ==
    Slice!(double*, 2, Canonical, int*, string*)));
assert(assmcanonicaldf.label!0[0] == 1);
assert(assmcanonicaldf.label!1[1] == "Label2");
Slice!(Iterator, N, Contiguous, Labels) assumeContiguous(Iterator, size_t N, SliceKind kind, Labels...)(Slice!(Iterator, N, kind, Labels) slice);
Converts a slice to contiguous kind (unsafe).
Parameters:
Slice!(Iterator, N, kind, Labels) slice a slice
Returns:
canonical slice
Examples:
auto slice = iota(2, 3).universal.assumeContiguous;
assert(slice == [[0, 1, 2], [3, 4, 5]]);
assert(slice._lengths == [2, 3]);
static assert(slice._strides.length == 0);
Examples:
import mir.ndslice.slice;
import mir.ndslice.allocation: slice;

auto dataframe = slice!(double, int, string)(2, 3);
dataframe.label[] = [1, 2];
dataframe.label!1[] = ["Label1", "Label2", "Label3"];

auto assmcontdf = dataframe.canonical.assumeContiguous;
assert(assmcontdf._lengths == [2, 3]);
static assert(assmcontdf._strides.length == 0);

assert(is(typeof(assmcontdf) ==
    Slice!(double*, 2, Contiguous, int*, string*)));
assert(assmcontdf.label!0[0] == 1);
assert(assmcontdf.label!1[1] == "Label2");
void assumeHypercube(Iterator, size_t N, SliceKind kind, Labels...)(ref scope Slice!(Iterator, N, kind, Labels) slice);
Helps the compiler to use optimisations related to the shape form
Examples:
auto b = iota(5, 5);

assumeHypercube(b);

assert(b == iota(5, 5));
void assumeSameShape(T...)(ref scope T slices)
if (allSatisfy!(isSlice, T));
Helps the compiler to use optimisations related to the shape form
Examples:
auto a = iota(5, 5);
auto b = iota(5, 5);

assumeHypercube(a); // first use this one, if applicable
assumeSameShape(a, b); //

assert(a == iota(5, 5));
assert(b == iota(5, 5));
auto assumeFieldsHaveZeroShift(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if (__traits(hasMember, Iterator, "assumeFieldsHaveZeroShift"));
Slice!(SliceIterator!(Iterator, P, P == 1 && (kind == Canonical) ? Contiguous : kind), N - P, Universal) pack(size_t P, Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if (P && (P < N));
Creates a packed slice, i.e. slice of slices. Packs the last P dimensions. The function does not allocate any data.
Parameters:
P size of dimension pack
Slice!(Iterator, N, kind) slice a slice to pack
Returns:
slice.pack!p returns Slice!(kind, [N - p, p], Iterator)
See Also:
Examples:
import mir.ndslice.slice: sliced, Slice;

auto a = iota(3, 4, 5, 6);
auto b = a.pack!2;

static immutable res1 = [3, 4];
static immutable res2 = [5, 6];
assert(b.shape == res1);
assert(b[0, 0].shape == res2);
assert(a == b.unpack);
assert(a.pack!2 == b);
static assert(is(typeof(b) == typeof(a.pack!2)));
Slice!(SliceIterator!(Iterator, N - P, N - P == 1 && (kind == Canonical) ? Contiguous : kind), P, Universal) ipack(size_t P, Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if (P && (P < N));
Creates a packed slice, i.e. slice of slices. Packs the last N - P dimensions. The function does not allocate any data.
Parameters:
Slice!(Iterator, N, kind) slice a slice to pack
See Also:
Examples:
import mir.ndslice.slice: sliced, Slice;

auto a = iota(3, 4, 5, 6);
auto b = a.ipack!2;

static immutable res1 = [3, 4];
static immutable res2 = [5, 6];
assert(b.shape == res1);
assert(b[0, 0].shape == res2);
assert(a.ipack!2 == b);
static assert(is(typeof(b) == typeof(a.ipack!2)));
Slice!(Iterator, N + M, min(innerKind, Canonical)) unpack(Iterator, size_t M, SliceKind innerKind, size_t N, SliceKind outerKind)(Slice!(SliceIterator!(Iterator, M, innerKind), N, outerKind) slice);
Unpacks a packed slice.
The functions does not allocate any data.
Parameters:
Slice!(SliceIterator!(Iterator, M, innerKind), N, outerKind) slice packed slice
Returns:
unpacked slice, that is a view on the same data.
See Also:
Slice!(SliceIterator!(Iterator, N, outerKind), M, innerKind) evertPack(Iterator, size_t M, SliceKind innerKind, size_t N, SliceKind outerKind)(Slice!(SliceIterator!(Iterator, M, innerKind), N, outerKind) slice);
Reverses the order of dimension packs. This function is used in a functional pipeline with other selectors.
Parameters:
Slice!(SliceIterator!(Iterator, M, innerKind), N, outerKind) slice packed slice
Returns:
packed slice
See Also:
Examples:
import mir.ndslice.dynamic : transposed;
auto slice = iota(3, 4, 5, 6, 7, 8, 9, 10, 11).universal;
assert(slice
    .pack!2
    .evertPack
    .unpack
         == slice.transposed!(
            slice.shape.length-2,
            slice.shape.length-1));
Examples:
import mir.ndslice.iterator: SliceIterator;
import mir.ndslice.slice: sliced, Slice, Universal;
import mir.ndslice.allocation: slice;
static assert(is(typeof(
    slice!int(6)
        .sliced(1,2,3)
        .pack!1
        .evertPack
    )
     == Slice!(SliceIterator!(int*, 2, Universal), 1)));
Examples:
auto a = iota(3, 4, 5, 6, 7, 8, 9, 10, 11);
auto b = a.pack!2.unpack;
static assert(is(typeof(a.canonical) == typeof(b)));
assert(a == b);
Slice!(IotaIterator!I, N) iota(I = sizediff_t, size_t N)(size_t[N] lengths...)
if (__traits(isIntegral, I));

Slice!(IotaIterator!sizediff_t, N) iota(size_t N)(size_t[N] lengths, sizediff_t start);

Slice!(StrideIterator!(IotaIterator!sizediff_t), N) iota(size_t N)(size_t[N] lengths, sizediff_t start, size_t stride);

template iota(I) if (__traits(isIntegral, I))

Slice!(IotaIterator!I, N) iota(I, size_t N)(size_t[N] lengths, I start)
if (is(I P : P*));

Slice!(StrideIterator!(IotaIterator!I), N) iota(I, size_t N)(size_t[N] lengths, I start, size_t stride)
if (is(I P : P*));
Returns a slice, the elements of which are equal to the initial flattened index value.
Parameters:
N dimension count
size_t[N] lengths list of dimension lengths
sizediff_t start value of the first element in a slice (optional for integer I)
size_t stride value of the stride between elements (optional)
Returns:
n-dimensional slice composed of indices
See Also:
Examples:
import mir.primitives: DeepElementType;
auto slice = iota(2, 3);
static immutable array =
    [[0, 1, 2],
     [3, 4, 5]];

assert(slice == array);

static assert(is(DeepElementType!(typeof(slice)) == sizediff_t));
Examples:
int[6] data;
auto slice = iota([2, 3], data.ptr);
assert(slice[0, 0] == data.ptr);
assert(slice[0, 1] == data.ptr + 1);
assert(slice[1, 0] == data.ptr + 3);
Examples:
auto im = iota([10, 5], 100);
assert(im[2, 1] == 111); // 100 + 2 * 5 + 1

//slicing works correctly
auto cm = im[1 .. $, 3 .. $];
assert(cm[2, 1] == 119); // 119 = 100 + (1 + 2) * 5 + (3 + 1)
Examples:
iota with step
auto sl = iota([2, 3], 10, 10);

assert(sl == [[10, 20, 30],
              [40, 50, 60]]);
Slice!(Iterator, 1, N == 1 ? kind : Universal) diagonal(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
Returns a 1-dimensional slice over the main diagonal of an n-dimensional slice. diagonal can be generalized with other selectors such as blocks (diagonal blocks) and windows (multi-diagonal slice).
Parameters:
Slice!(Iterator, N, kind) slice input slice
Returns:
1-dimensional slice composed of diagonal elements
See Also:
Examples:
Matrix, main diagonal
//  -------
// | 0 1 2 |
// | 3 4 5 |
//  -------
//->
// | 0 4 |
static immutable d = [0, 4];
assert(iota(2, 3).diagonal == d);
Examples:
Non-square matrix
//  -------
// | 0 1 |
// | 2 3 |
// | 4 5 |
//  -------
//->
// | 0 3 |

assert(iota(3, 2).diagonal == iota([2], 0, 3));
Examples:
Loop through diagonal
import mir.ndslice.slice;
import mir.ndslice.allocation;

auto slice = slice!int(3, 3);
int i;
foreach (ref e; slice.diagonal)
    e = ++i;
assert(slice == [
    [1, 0, 0],
    [0, 2, 0],
    [0, 0, 3]]);
Examples:
Matrix, subdiagonal
//  -------
// | 0 1 2 |
// | 3 4 5 |
//  -------
//->
// | 1 5 |
static immutable d = [1, 5];
auto a = iota(2, 3).canonical;
a.popFront!1;
assert(a.diagonal == d);
Examples:
3D, main diagonal
//  -----------
// |  0   1  2 |
// |  3   4  5 |
//  - - - - - -
// |  6   7  8 |
// |  9  10 11 |
//  -----------
//->
// | 0 10 |
static immutable d = [0, 10];
assert(iota(2, 2, 3).diagonal == d);
Examples:
3D, subdiagonal
//  -----------
// |  0   1  2 |
// |  3   4  5 |
//  - - - - - -
// |  6   7  8 |
// |  9  10 11 |
//  -----------
//->
// | 1 11 |
static immutable d = [1, 11];
auto a = iota(2, 2, 3).canonical;
a.popFront!2;
assert(a.diagonal == d);
Examples:
3D, diagonal plain
//  -----------
// |  0   1  2 |
// |  3   4  5 |
// |  6   7  8 |
//  - - - - - -
// |  9  10 11 |
// | 12  13 14 |
// | 15  16 17 |
//  - - - - - -
// | 18  20 21 |
// | 22  23 24 |
// | 24  25 26 |
//  -----------
//->
//  -----------
// |  0   4  8 |
// |  9  13 17 |
// | 18  23 26 |
//  -----------

static immutable d =
    [[ 0,  4,  8],
     [ 9, 13, 17],
     [18, 22, 26]];

auto slice = iota(3, 3, 3)
    .pack!2
    .evertPack
    .diagonal
    .evertPack;

assert(slice == d);
Slice!(Iterator, 1, Universal) antidiagonal(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if (N == 2);
Returns a 1-dimensional slice over the main antidiagonal of an 2D-dimensional slice. antidiagonal can be generalized with other selectors such as blocks (diagonal blocks) and windows (multi-diagonal slice).
It runs from the top right corner to the bottom left corner.

Pseudo code

auto antidiagonal = slice.dropToHypercube.reversed!1.diagonal;

Parameters:
Slice!(Iterator, N, kind) slice input slice
Returns:
1-dimensional slice composed of antidiagonal elements.
See Also:
Examples:
//  -----
// | 0 1 |
// | 2 3 |
//  -----
//->
// | 1 2 |
static immutable c = [1, 2];
import std.stdio;
assert(iota(2, 2).antidiagonal == c);
Examples:
//  -------
// | 0 1 2 |
// | 3 4 5 |
//  -------
//->
// | 1 3 |
static immutable d = [1, 3];
assert(iota(2, 3).antidiagonal == d);
Slice!(SliceIterator!(Iterator, N, N == 1 ? Universal : min(kind, Canonical)), N, Universal) blocks(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, size_t[N] rlengths_...);
Returns an n-dimensional slice of n-dimensional non-overlapping blocks. blocks can be generalized with other selectors. For example, blocks in combination with diagonal can be used to get a slice of diagonal blocks. For overlapped blocks, combine windows with strided .
Parameters:
N dimension count
Slice!(Iterator, N, kind) slice slice to be split into blocks
size_t[N] rlengths_ dimensions of block, residual blocks are ignored
Returns:
packed N-dimensional slice composed of N-dimensional slices
See Also:
Examples:
import mir.ndslice.slice;
import mir.ndslice.allocation;
auto slice = slice!int(5, 8);
auto blocks = slice.blocks(2, 3);
int i;
foreach (blocksRaw; blocks)
    foreach (block; blocksRaw)
        block[] = ++i;

assert(blocks ==
    [[[[1, 1, 1], [1, 1, 1]],
      [[2, 2, 2], [2, 2, 2]]],
     [[[3, 3, 3], [3, 3, 3]],
      [[4, 4, 4], [4, 4, 4]]]]);

assert(    slice ==
    [[1, 1, 1,  2, 2, 2,  0, 0],
     [1, 1, 1,  2, 2, 2,  0, 0],

     [3, 3, 3,  4, 4, 4,  0, 0],
     [3, 3, 3,  4, 4, 4,  0, 0],

     [0, 0, 0,  0, 0, 0,  0, 0]]);
Examples:
Diagonal blocks
import mir.ndslice.slice;
import mir.ndslice.allocation;
auto slice = slice!int(5, 8);
auto blocks = slice.blocks(2, 3);
auto diagonalBlocks = blocks.diagonal.unpack;

diagonalBlocks[0][] = 1;
diagonalBlocks[1][] = 2;

assert(diagonalBlocks ==
    [[[1, 1, 1], [1, 1, 1]],
     [[2, 2, 2], [2, 2, 2]]]);

assert(blocks ==
    [[[[1, 1, 1], [1, 1, 1]],
      [[0, 0, 0], [0, 0, 0]]],
     [[[0, 0, 0], [0, 0, 0]],
      [[2, 2, 2], [2, 2, 2]]]]);

assert(slice ==
    [[1, 1, 1,  0, 0, 0,  0, 0],
     [1, 1, 1,  0, 0, 0,  0, 0],

     [0, 0, 0,  2, 2, 2,  0, 0],
     [0, 0, 0,  2, 2, 2,  0, 0],

     [0, 0, 0, 0, 0, 0, 0, 0]]);
Examples:
Matrix divided into vertical blocks
import mir.ndslice.allocation;
import mir.ndslice.slice;
auto slice = slice!int(5, 13);
auto blocks = slice
    .pack!1
    .evertPack
    .blocks(3)
    .unpack;

int i;
foreach (block; blocks)
    block[] = ++i;

assert(slice ==
    [[1, 1, 1,  2, 2, 2,  3, 3, 3,  4, 4, 4,  0],
     [1, 1, 1,  2, 2, 2,  3, 3, 3,  4, 4, 4,  0],
     [1, 1, 1,  2, 2, 2,  3, 3, 3,  4, 4, 4,  0],
     [1, 1, 1,  2, 2, 2,  3, 3, 3,  4, 4, 4,  0],
     [1, 1, 1,  2, 2, 2,  3, 3, 3,  4, 4, 4,  0]]);
Slice!(SliceIterator!(Iterator, N, N == 1 ? kind : min(kind, Canonical)), N, Universal) windows(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, size_t[N] rlengths...);
Returns an n-dimensional slice of n-dimensional overlapping windows. windows can be generalized with other selectors. For example, windows in combination with diagonal can be used to get a multi-diagonal slice.
Parameters:
N dimension count
Slice!(Iterator, N, kind) slice slice to be iterated
size_t[N] rlengths dimensions of windows
Returns:
packed N-dimensional slice composed of N-dimensional slices
Examples:
import mir.ndslice.allocation;
import mir.ndslice.slice;
auto slice = slice!int(5, 8);
auto windows = slice.windows(2, 3);

int i;
foreach (windowsRaw; windows)
    foreach (window; windowsRaw)
        ++window[];

assert(slice ==
    [[1,  2,  3, 3, 3, 3,  2,  1],

     [2,  4,  6, 6, 6, 6,  4,  2],
     [2,  4,  6, 6, 6, 6,  4,  2],
     [2,  4,  6, 6, 6, 6,  4,  2],

     [1,  2,  3, 3, 3, 3,  2,  1]]);
Examples:
import mir.ndslice.allocation;
import mir.ndslice.slice;
auto slice = slice!int(5, 8);
auto windows = slice.windows(2, 3);
windows[1, 2][] = 1;
windows[1, 2][0, 1] += 1;
windows.unpack[1, 2, 0, 1] += 1;

assert(slice ==
    [[0, 0,  0, 0, 0,  0, 0, 0],

     [0, 0,  1, 3, 1,  0, 0, 0],
     [0, 0,  1, 1, 1,  0, 0, 0],

     [0, 0,  0, 0, 0,  0, 0, 0],
     [0, 0,  0, 0, 0,  0, 0, 0]]);
Examples:
Multi-diagonal matrix
import mir.ndslice.allocation;
import mir.ndslice.slice;
auto slice = slice!int(8, 8);
auto windows = slice.windows(3, 3);

auto multidiagonal = windows
    .diagonal
    .unpack;
foreach (window; multidiagonal)
    window[] += 1;

assert(slice ==
    [[ 1, 1, 1,  0, 0, 0, 0, 0],
     [ 1, 2, 2, 1,  0, 0, 0, 0],
     [ 1, 2, 3, 2, 1,  0, 0, 0],
     [0,  1, 2, 3, 2, 1,  0, 0],
     [0, 0,  1, 2, 3, 2, 1,  0],
     [0, 0, 0,  1, 2, 3, 2, 1],
     [0, 0, 0, 0,  1, 2, 2, 1],
     [0, 0, 0, 0, 0,  1, 1, 1]]);
Examples:
Sliding window over matrix columns
import mir.ndslice.allocation;
import mir.ndslice.slice;
auto slice = slice!int(5, 8);
auto windows = slice
    .pack!1
    .evertPack
    .windows(3)
    .unpack;

foreach (window; windows)
    window[] += 1;

assert(slice ==
    [[1,  2,  3, 3, 3, 3,  2,  1],
     [1,  2,  3, 3, 3, 3,  2,  1],
     [1,  2,  3, 3, 3, 3,  2,  1],
     [1,  2,  3, 3, 3, 3,  2,  1],
     [1,  2,  3, 3, 3, 3,  2,  1]]);
Examples:
Overlapping blocks using windows
//  ----------------
// |  0  1  2  3  4 |
// |  5  6  7  8  9 |
// | 10 11 12 13 14 |
// | 15 16 17 18 19 |
// | 20 21 22 23 24 |
//  ----------------
//->
//  ---------------------
// |  0  1  2 |  2  3  4 |
// |  5  6  7 |  7  8  9 |
// | 10 11 12 | 12 13 14 |
// | - - - - - - - - - - |
// | 10 11 13 | 12 13 14 |
// | 15 16 17 | 17 18 19 |
// | 20 21 22 | 22 23 24 |
//  ---------------------

import mir.ndslice.slice;
import mir.ndslice.dynamic : strided;

auto overlappingBlocks = iota(5, 5)
    .windows(3, 3)
    .universal
    .strided!(0, 1)(2, 2);

assert(overlappingBlocks ==
        [[[[ 0,  1,  2], [ 5,  6,  7], [10, 11, 12]],
          [[ 2,  3,  4], [ 7,  8,  9], [12, 13, 14]]],
         [[[10, 11, 12], [15, 16, 17], [20, 21, 22]],
          [[12, 13, 14], [17, 18, 19], [22, 23, 24]]]]);
enum ReshapeError: int;
Error codes for reshape.
none
No error
empty
Slice should be not empty
total
Total element count should be the same
incompatible
Structure is incompatible with new shape
Slice!(Iterator, M, kind) reshape(Iterator, size_t N, SliceKind kind, size_t M)(Slice!(Iterator, N, kind) slice, ptrdiff_t[M] rlengths, ref int err);
Returns a new slice for the same data with different dimensions.
Parameters:
Slice!(Iterator, N, kind) slice slice to be reshaped
ptrdiff_t[M] rlengths list of new dimensions. One of the lengths can be set to -1. In this case, the corresponding dimension is inferable.
int err ReshapeError code
Returns:
reshaped slice
Examples:
import mir.ndslice.dynamic : allReversed;
int err;
auto slice = iota(3, 4)
    .universal
    .allReversed
    .reshape([-1, 3], err);
assert(err == 0);
assert(slice ==
    [[11, 10, 9],
     [ 8,  7, 6],
     [ 5,  4, 3],
     [ 2,  1, 0]]);
Examples:
Reshaping with memory allocation
import mir.ndslice.slice: sliced;
import mir.ndslice.allocation: slice;
import mir.ndslice.dynamic : reversed;

auto reshape2(S, size_t M)(S sl, ptrdiff_t[M] lengths)
{
    int err;
    // Tries to reshape without allocation
    auto ret = sl.reshape(lengths, err);
    if (!err)
        return ret;
    if (err == ReshapeError.incompatible)
        // allocates, flattens, reshapes with `sliced`, converts to universal kind
        return sl.slice.flattened.sliced(cast(size_t[M])lengths).universal;
    throw new Exception("total elements count is different or equals to zero");
}

auto sl = iota!int(3, 4)
    .slice
    .universal
    .reversed!0;

assert(reshape2(sl, [4, 3]) ==
    [[ 8, 9, 10],
     [11, 4,  5],
     [ 6, 7,  0],
     [ 1, 2,  3]]);
Slice!(FlattenedIterator!(Iterator, N, kind)) flattened(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if (N != 1 && (kind != Contiguous));

Slice!Iterator flattened(Iterator, size_t N)(Slice!(Iterator, N) slice);

Slice!(StrideIterator!Iterator) flattened(Iterator)(Slice!(Iterator, 1, Universal) slice);
A contiguous 1-dimensional slice of all elements of a slice. flattened iterates existing data. The order of elements is preserved.
flattened can be generalized with other selectors.
Parameters:
Slice!(Iterator, N, kind) slice slice to be iterated
Returns:
contiguous 1-dimensional slice of elements of the slice
Examples:
Regular slice
assert(iota(4, 5).flattened == iota(20));
assert(iota(4, 5).canonical.flattened == iota(20));
assert(iota(4, 5).universal.flattened == iota(20));
Examples:
Packed slice
import mir.ndslice.slice;
import mir.ndslice.dynamic;
assert(iota(3, 4, 5, 6, 7).pack!2.flattened[1] == iota([6, 7], 6 * 7));
Examples:
Properties
auto elems = iota(3, 4).universal.flattened;

elems.popFrontExactly(2);
assert(elems.front == 2);
/// `_index` is available only for canonical and universal ndslices.
assert(elems._iterator._indices == [0, 2]);

elems.popBackExactly(2);
assert(elems.back == 9);
assert(elems.length == 8);
Examples:
Index property
import mir.ndslice.slice;
auto slice = new long[20].sliced(5, 4);

for (auto elems = slice.universal.flattened; !elems.empty; elems.popFront)
{
    ptrdiff_t[2] index = elems._iterator._indices;
    elems.front = index[0] * 10 + index[1] * 3;
}
assert(slice ==
    [[ 0,  3,  6,  9],
     [10, 13, 16, 19],
     [20, 23, 26, 29],
     [30, 33, 36, 39],
     [40, 43, 46, 49]]);
Examples:
Random access and slicing
import mir.ndslice.allocation: slice;
import mir.ndslice.slice: sliced;

auto elems = iota(4, 5).slice.flattened;

elems = elems[11 .. $ - 2];

assert(elems.length == 7);
assert(elems.front == 11);
assert(elems.back == 17);

foreach (i; 0 .. 7)
    assert(elems[i] == i + 11);

// assign an element
elems[2 .. 6] = -1;
assert(elems[2 .. 6] == repeat(-1, 4));

// assign an array
static ar = [-1, -2, -3, -4];
elems[2 .. 6] = ar;
assert(elems[2 .. 6] == ar);

// assign a slice
ar[] *= 2;
auto sl = ar.sliced(ar.length);
elems[2 .. 6] = sl;
assert(elems[2 .. 6] == sl);
Slice!(FieldIterator!(ndIotaField!N), N) ndiota(size_t N)(size_t[N] lengths...)
if (N);
Returns a slice, the elements of which are equal to the initial multidimensional index value. For a flattened (contiguous) index, see iota.
Parameters:
N dimension count
size_t[N] lengths list of dimension lengths
Returns:
N-dimensional slice composed of indices
See Also:
Examples:
auto slice = ndiota(2, 3);
static immutable array =
    [[[0, 0], [0, 1], [0, 2]],
     [[1, 0], [1, 1], [1, 2]]];

assert(slice == array);
Examples:
auto im = ndiota(7, 9);

assert(im[2, 1] == [2, 1]);

//slicing works correctly
auto cm = im[1 .. $, 4 .. $];
assert(cm[2, 1] == [3, 5]);
auto linspace(T, size_t N)(size_t[N] lengths, T[2][N] intervals...)
if (N && (isFloatingPoint!T || isComplex!T));
Evenly spaced numbers over a specified interval.
Parameters:
T floating point or complex numbers type
size_t[N] lengths list of dimension lengths. Each length must be greater then 1.
T[2][N] intervals list of [start, end] pairs.
Returns:
n-dimensional grid of evenly spaced numbers over specified intervals.
See Also:
Examples:
1D
auto s = linspace!double([5], [1.0, 2.0]);
assert(s == [1.0, 1.25, 1.5, 1.75, 2.0]);

// reverse order
assert(linspace!double([5], [2.0, 1.0]) == s.retro);

// remove endpoint
s.popBack;
assert(s == [1.0, 1.25, 1.5, 1.75]);
Examples:
2D
import mir.functional: refTuple;

auto s = linspace!double([5, 3], [1.0, 2.0], [0.0, 1.0]);

assert(s == [
    [refTuple(1.00, 0.00), refTuple(1.00, 0.5), refTuple(1.00, 1.0)],
    [refTuple(1.25, 0.00), refTuple(1.25, 0.5), refTuple(1.25, 1.0)],
    [refTuple(1.50, 0.00), refTuple(1.50, 0.5), refTuple(1.50, 1.0)],
    [refTuple(1.75, 0.00), refTuple(1.75, 0.5), refTuple(1.75, 1.0)],
    [refTuple(2.00, 0.00), refTuple(2.00, 0.5), refTuple(2.00, 1.0)],
    ]);

assert(s.map!"a * b" == [
    [0.0, 0.500, 1.00],
    [0.0, 0.625, 1.25],
    [0.0, 0.750, 1.50],
    [0.0, 0.875, 1.75],
    [0.0, 1.000, 2.00],
    ]);
Examples:
Complex numbers
import mir.complex;
alias C = Complex!double;
auto s = linspace!C([3], [C(1.0, 0), C(2.0, 4)]);
assert(s == [C(1.0, 0), C(1.5, 2), C(2.0, 4)]);
@trusted Slice!(FieldIterator!(RepeatField!T), M, Universal) repeat(T, size_t M)(T value, size_t[M] lengths...)
if (M && !isSlice!T);

Slice!(SliceIterator!(Iterator, N, kind), M, Universal) repeat(SliceKind kind, size_t N, Iterator, size_t M)(Slice!(Iterator, N, kind) slice, size_t[M] lengths...)
if (M);
Returns a slice with identical elements. RepeatSlice stores only single value.
Parameters:
size_t[M] lengths list of dimension lengths
Returns:
n-dimensional slice composed of identical values, where n is dimension count.
Examples:
auto sl = iota(3).repeat(4);
assert(sl == [[0, 1, 2],
              [0, 1, 2],
              [0, 1, 2],
              [0, 1, 2]]);
Examples:
import mir.ndslice.dynamic : transposed;

auto sl = iota(3)
    .repeat(4)
    .unpack
    .universal
    .transposed;

assert(sl == [[0, 0, 0, 0],
              [1, 1, 1, 1],
              [2, 2, 2, 2]]);
Examples:
import mir.ndslice.allocation;

auto sl = iota([3], 6).slice;
auto slC = sl.repeat(2, 3);
sl[1] = 4;
assert(slC == [[[6, 4, 8],
                [6, 4, 8],
                [6, 4, 8]],
               [[6, 4, 8],
                [6, 4, 8],
                [6, 4, 8]]]);
Examples:
import mir.primitives: DeepElementType;

auto sl = repeat(4.0, 2, 3);
assert(sl == [[4.0, 4.0, 4.0],
              [4.0, 4.0, 4.0]]);

static assert(is(DeepElementType!(typeof(sl)) == double));

sl[1, 1] = 3;
assert(sl == [[3.0, 3.0, 3.0],
              [3.0, 3.0, 3.0]]);
auto cycle(Field)(Field field, size_t loopLength, size_t length)
if (!isSlice!Field && !is(Field : T[], T));

auto cycle(size_t loopLength, Field)(Field field, size_t length)
if (!isSlice!Field && !is(Field : T[], T));

auto cycle(Iterator, SliceKind kind)(Slice!(Iterator, 1, kind) slice, size_t length);

auto cycle(size_t loopLength, Iterator, SliceKind kind)(Slice!(Iterator, 1, kind) slice, size_t length);

auto cycle(T)(T[] array, size_t length);

auto cycle(size_t loopLength, T)(T[] array, size_t length);

auto cycle(size_t loopLength, T)(T withAsSlice, size_t length)
if (hasAsSlice!T);
Cycle repeates 1-dimensional field/range/array/slice in a fixed length 1-dimensional slice.
Examples:
auto slice = iota(3);
assert(slice.cycle(7) == [0, 1, 2, 0, 1, 2, 0]);
assert(slice.cycle!2(7) == [0, 1, 0, 1, 0, 1, 0]);
assert([0, 1, 2].cycle(7) == [0, 1, 2, 0, 1, 2, 0]);
assert([4, 3, 2, 1].cycle!4(7) == [4, 3, 2, 1, 4, 3, 2]);
auto stride(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, ptrdiff_t factor)
if (N == 1);

template stride(size_t factor = 2) if (factor > 1)

auto stride(T)(T[] array, ptrdiff_t factor);

auto stride(T)(T withAsSlice, ptrdiff_t factor)
if (hasAsSlice!T);
Strides 1-dimensional slice.
Parameters:
Slice!(Iterator, N, kind) slice 1-dimensional unpacked slice.
ptrdiff_t factor positive stride size.
Returns:
Contiguous slice with strided iterator.
See Also:
Examples:
auto slice = iota(6);
static immutable str = [0, 2, 4];
assert(slice.stride(2) == str); // runtime factor
assert(slice.stride!2 == str); // compile time factor
assert(slice.stride == str); // default compile time factor is 2
assert(slice.universal.stride(2) == str);
Examples:
ND-compile time
auto slice = iota(4, 6);
static immutable str = [[0, 2, 4], [12, 14, 16]];
assert(slice.stride!2 == str); // compile time factor
assert(slice.stride == str); // default compile time factor is 2
@trusted auto retro(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto retro(T)(T[] array);

auto retro(T)(T withAsSlice)
if (hasAsSlice!T);

auto retro(Range)(Range r)
if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T));

struct RetroRange(Range);
Reverses order of iteration for all dimensions.
Parameters:
Slice!(Iterator, N, kind) slice slice, range, or array.
Returns:
Slice/range with reversed order of iteration for all dimensions.
See Also:
Examples:
auto slice = iota(2, 3);
static immutable reversed = [[5, 4, 3], [2, 1, 0]];
assert(slice.retro == reversed);
assert(slice.canonical.retro == reversed);
assert(slice.universal.retro == reversed);

static assert(is(typeof(slice.retro.retro) == typeof(slice)));
static assert(is(typeof(slice.canonical.retro.retro) == typeof(slice.canonical)));
static assert(is(typeof(slice.universal.retro) == typeof(slice.universal)));
Examples:
Ranges
import mir.algorithm.iteration: equal;
import std.range: std_iota = iota;

assert(std_iota(4).retro.equal(iota(4).retro));
static assert(is(typeof(std_iota(4).retro.retro) == typeof(std_iota(4))));
auto bitwise(Iterator, size_t N, SliceKind kind, I = typeof(Iterator.init[size_t.init]))(Slice!(Iterator, N, kind) slice)
if (__traits(isIntegral, I) && (kind != Universal || N == 1));

auto bitwise(T)(T[] array);

auto bitwise(T)(T withAsSlice)
if (hasAsSlice!T);
Bitwise slice over an integral slice.
Parameters:
Slice!(Iterator, N, kind) slice a contiguous or canonical slice on top of integral iterator.
Returns:
A bitwise slice.
Examples:
size_t[10] data;
auto bits = data[].bitwise;
assert(bits.length == data.length * size_t.sizeof * 8);
bits[111] = true;
assert(bits[111]);

bits.popFront;
assert(bits[110]);
bits[] = true;
bits[110] = false;
bits = bits[10 .. $];
assert(bits[100] == false);
auto bitwiseField(Field, I = typeof(Field.init[size_t.init]))(Field field)
if (__traits(isUnsigned, I));
Bitwise field over an integral field.
Parameters:
Field field an integral field.
Returns:
A bitwise field.
auto bitpack(size_t pack, Iterator, size_t N, SliceKind kind, I = typeof(Iterator.init[size_t.init]))(Slice!(Iterator, N, kind) slice)
if (__traits(isIntegral, I) && (kind == Contiguous || kind == Canonical) && (pack > 1));

auto bitpack(size_t pack, T)(T[] array);

auto bitpack(size_t pack, T)(T withAsSlice)
if (hasAsSlice!T);
Bitpack slice over an integral slice.
Bitpack is used to represent unsigned integer slice with fewer number of bits in integer binary representation.
Parameters:
pack counts of bits in the integer.
Slice!(Iterator, N, kind) slice a contiguous or canonical slice on top of integral iterator.
Returns:
A bitpack slice.
Examples:
size_t[10] data;
// creates a packed unsigned integer slice with max allowed value equal to `2^^6 - 1 == 63`.
auto packs = data[].bitpack!6;
assert(packs.length == data.length * size_t.sizeof * 8 / 6);
packs[$ - 1] = 24;
assert(packs[$ - 1] == 24);

packs.popFront;
assert(packs[$ - 1] == 24);
Slice!(BytegroupIterator!(Iterator, group, DestinationType), N, kind) bytegroup(size_t group, DestinationType, Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if ((kind == Contiguous || kind == Canonical) && group);

auto bytegroup(size_t pack, DestinationType, T)(T[] array);

auto bytegroup(size_t pack, DestinationType, T)(T withAsSlice)
if (hasAsSlice!T);
Bytegroup slice over an integral slice.
Groups existing slice into fixed length chunks and uses them as data store for destination type.
Correctly handles scalar types on both little-endian and big-endian platforms.
Parameters:
group count of iterator items used to store the destination type.
DestinationType deep element type of the result slice.
Slice!(Iterator, N, kind) slice a contiguous or canonical slice.
Returns:
A bytegroup slice.
Examples:
24 bit integers
import mir.ndslice.slice: DeepElementType, sliced;

ubyte[20] data;
// creates a packed unsigned integer slice with max allowed value equal to `2^^6 - 1 == 63`.
auto int24ar = data[].bytegroup!(3, int); // 24 bit integers
assert(int24ar.length == data.length / 3);

enum checkInt = ((1 << 20) - 1);

int24ar[3] = checkInt;
assert(int24ar[3] == checkInt);

int24ar.popFront;
assert(int24ar[2] == checkInt);

static assert(is(DeepElementType!(typeof(int24ar)) == int));
Examples:
48 bit integers
import mir.ndslice.slice: DeepElementType, sliced;
ushort[20] data;
// creates a packed unsigned integer slice with max allowed value equal to `2^^6 - 1 == 63`.
auto int48ar = data[].sliced.bytegroup!(3, long); // 48 bit integers
assert(int48ar.length == data.length / 3);

enum checkInt = ((1L << 44) - 1);

int48ar[3] = checkInt;
assert(int48ar[3] == checkInt);

int48ar.popFront;
assert(int48ar[2] == checkInt);

static assert(is(DeepElementType!(typeof(int48ar)) == long));
template map(fun...) if (fun.length)

struct MapRange(alias fun, Range);
Implements the homonym function (also known as transform) present in many languages of functional flavor. The call map!(fun)(slice) returns a slice of which elements are obtained by applying fun for all elements in slice. The original slices are not changed. Evaluation is done lazily.

Note transposed  and pack  can be used to specify dimensions.

Parameters:
fun One or more functions.
Examples:
import mir.ndslice.topology : iota;
auto s = iota(2, 3).map!(a => a * 3);
assert(s == [[ 0,  3,  6],
             [ 9, 12, 15]]);
Examples:
String lambdas
import mir.ndslice.topology : iota;
assert(iota(2, 3).map!"a * 2" == [[0, 2, 4], [6, 8, 10]]);
Examples:
Input ranges
import mir.algorithm.iteration: filter, equal;
assert (6.iota.filter!"a % 2".map!"a * 10".equal([10, 30, 50]));
Examples:
Packed tensors
import mir.ndslice.topology : iota, windows;
import mir.math.sum: sum;

//  iota        windows     map  sums ( reduce!"a + b" )
//                --------------
//  -------      |  ---    ---  |      ------
// | 0 1 2 |  => || 0 1 || 1 2 ||  => | 8 12 |
// | 3 4 5 |     || 3 4 || 4 5 ||      ------
//  -------      |  ---    ---  |
//                --------------
auto s = iota(2, 3)
    .windows(2, 2)
    .map!sum;

assert(s == [[8, 12]]);
Examples:
Zipped tensors
import mir.ndslice.topology : iota, zip;

// 0 1 2
// 3 4 5
auto sl1 = iota(2, 3);
// 1 2 3
// 4 5 6
auto sl2 = iota([2, 3], 1);

auto z = zip(sl1, sl2);

assert(zip(sl1, sl2).map!"a + b" == sl1 + sl2);
assert(zip(sl1, sl2).map!((a, b) => a + b) == sl1 + sl2);
Examples:
Multiple functions can be passed to map. In that case, the element type of map is a refTuple containing one element for each function.
import mir.ndslice.topology : iota;

auto sl = iota(2, 3);
auto s = sl.map!("a + a", "a * a");

auto sums     = [[0, 2, 4], [6,  8, 10]];
auto products = [[0, 1, 4], [9, 16, 25]];

assert(s.map!"a[0]" == sl + sl);
assert(s.map!"a[1]" == sl * sl);
Examples:
map can be aliased to a symbol and be used separately:
import mir.ndslice.topology : iota;

alias halfs = map!"double(a) / 2";
assert(halfs(iota(2, 3)) == [[0.0, 0.5, 1], [1.5, 2, 2.5]]);
Examples:
Type normalization
import mir.functional : pipe;
import mir.ndslice.topology : iota;
auto a = iota(2, 3).map!"a + 10".map!(pipe!("a * 2", "a + 1"));
auto b = iota(2, 3).map!(pipe!("a + 10", "a * 2", "a + 1"));
assert(a == b);
static assert(is(typeof(a) == typeof(b)));
Examples:
Use map with byDim/alongDim to apply functions to each dimension
import mir.ndslice.topology: byDim, alongDim;
import mir.ndslice.fuse: fuse;
import mir.math.stat: mean;
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

// Use byDim/alongDim with map to compute mean of row/column.
assert(x.byDim!0.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.byDim!1.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
assert(x.alongDim!1.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.alongDim!0.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
Examples:
Use map with a lambda and with byDim/alongDim, but may need to allocate result. This example uses fuse, which allocates. Note: fuse!1 will transpose the result.
import mir.ndslice.topology: iota, byDim, alongDim, map;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;

auto x = [1, 2, 3].sliced;
auto y = [1, 2].sliced;

auto s1 = iota(2, 3).byDim!0.map!(a => a * x).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s2 = iota(2, 3).byDim!1.map!(a => a * y).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
auto s3 = iota(2, 3).alongDim!1.map!(a => a * x).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s4 = iota(2, 3).alongDim!0.map!(a => a * y).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
Examples:
import mir.algorithm.iteration: reduce;
import mir.math.common: fmax;
import mir.math.stat: mean;
import mir.math.sum;
/// Returns maximal column average.
auto maxAvg(S)(S matrix) {
    return reduce!fmax(0.0, matrix.alongDim!1.map!mean);
}
// 1 2
// 3 4
auto matrix = iota([2, 2], 1);
assert(maxAvg(matrix) == 3.5);
auto map(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto map(T)(T[] array);

auto map(T)(T withAsSlice)
if (hasAsSlice!T);

auto map(Range)(Range r)
if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T));
Parameters:
Slice!(Iterator, N, kind) slice An ndslice, array, or an input range.
Returns:
ndslice or an input range with each fun applied to all the elements. If there is more than one fun, the element type will be Tuple containing one element for each fun.
auto vmap(Iterator, size_t N, SliceKind kind, Callable)(Slice!(Iterator, N, kind) slice, Callable callable);

auto vmap(T, Callable)(T[] array, Callable callable);

auto vmap(T, Callable)(T withAsSlice, Callable callable)
if (hasAsSlice!T);
Implements the homonym function (also known as transform) present in many languages of functional flavor. The call slice.vmap(fun) returns a slice of which elements are obtained by applying fun for all elements in slice. The original slices are not changed. Evaluation is done lazily.

Note transposed  and pack  can be used to specify dimensions.

Parameters:
Slice!(Iterator, N, kind) slice ndslice
Callable callable callable object, structure, delegate, or function pointer.
Examples:
import mir.ndslice.topology : iota;

static struct Mul {
    double factor; this(double f) { factor = f; }
    auto opCall(long x) const {return x * factor; }
    auto lightConst()() const @property { return Mul(factor); }
}

auto callable = Mul(3);
auto s = iota(2, 3).vmap(callable);

assert(s == [[ 0,  3,  6],
             [ 9, 12, 15]]);
Examples:
Packed tensors.
import mir.math.sum: sum;
import mir.ndslice.topology : iota, windows;

//  iota        windows     vmap  scaled sums
//                --------------
//  -------      |  ---    ---  |      -----
// | 0 1 2 |  => || 0 1 || 1 2 ||  => | 4 6 |
// | 3 4 5 |     || 3 4 || 4 5 ||      -----
//  -------      |  ---    ---  |
//                --------------

struct Callable
{
    double factor;
    this(double f) {factor = f;}
    auto opCall(S)(S x) { return x.sum * factor; }

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

auto callable = Callable(0.5);

auto s = iota(2, 3)
    .windows(2, 2)
    .vmap(callable);

assert(s == [[4, 6]]);
Examples:
Zipped tensors
import mir.ndslice.topology : iota, zip;

struct Callable
{
    double factor;
    this(double f) {factor = f;}
    auto opCall(S, T)(S x, T y) { return x + y * factor; }

    auto lightConst()() const { return Callable(factor); }
    auto lightImmutable()() immutable { return Callable(factor); }
}

auto callable = Callable(10);

// 0 1 2
// 3 4 5
auto sl1 = iota(2, 3);
// 1 2 3
// 4 5 6
auto sl2 = iota([2, 3], 1);

auto z = zip(sl1, sl2);

assert(zip(sl1, sl2).vmap(callable) ==
        [[10,  21,  32],
         [43,  54,  65]]);
Examples:
Use vmap with byDim/alongDim to apply functions to each dimension
import mir.ndslice.fuse: fuse;
import mir.math.stat: mean;
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

static struct Callable
{
    double factor;
    this(double f) {factor = f;}
    auto opCall(U)(U x) const {return x.mean + factor; }
    auto lightConst()() const @property { return Callable(factor); }
}

auto callable = Callable(0.0);

// Use byDim/alongDim with map to compute callable of row/column.
assert(x.byDim!0.vmap(callable).all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.byDim!1.vmap(callable).all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
assert(x.alongDim!1.vmap(callable).all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.alongDim!0.vmap(callable).all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
Examples:
Use vmap with a lambda and with byDim/alongDim, but may need to allocate result. This example uses fuse, which allocates. Note: fuse!1 will transpose the result.
import mir.ndslice.topology: iota, alongDim, map;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;

static struct Mul(T)
{
    T factor;
    this(T f) { factor = f; }
    auto opCall(U)(U x) {return x * factor; }
    auto lightConst()() const @property { return Mul!(typeof(factor.lightConst))(factor.lightConst); }
}

auto a = [1, 2, 3].sliced;
auto b = [1, 2].sliced;
auto A = Mul!(typeof(a))(a);
auto B = Mul!(typeof(b))(b);

auto x = [
    [0, 1, 2],
    [3, 4, 5]
].fuse;

auto s1 = x.byDim!0.vmap(A).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s2 = x.byDim!1.vmap(B).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
auto s3 = x.alongDim!1.vmap(A).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s4 = x.alongDim!0.vmap(B).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
template rcmap(fun...) if (fun.length)
Implements the homonym function (also known as transform) present in many languages of functional flavor. The call rmap!(fun)(slice) returns an RC array (1D) or RC slice (ND) of which elements are obtained by applying fun for all elements in slice. The original slices are not changed. Evaluation is done eagerly.

Note transposed  and pack  can be used to specify dimensions.

Parameters:
fun One or more functions.
Examples:
Returns RCArray for input ranges and one-dimensional slices.
import mir.algorithm.iteration: filter, equal;
auto factor = 10;
auto step = 20;
assert (3.iota.rcmap!(a => a * factor).moveToSlice.equal(3.iota * factor)); 
assert (6.iota.filter!"a % 2".rcmap!(a => a * factor).moveToSlice.equal([3].iota(factor, step)));
Examples:
For multidimensional case returns Slice!(RCI!T, N).
import mir.ndslice.topology : iota;
auto factor = 3;
auto s = iota(2, 3).rcmap!(a => a * factor);
assert(s == iota(2, 3) * factor);
Examples:
String lambdas
import mir.ndslice.topology : iota;
assert(iota(2, 3).rcmap!"a * 2" == iota(2, 3) * 2);
auto rcmap(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto rcmap(T)(T[] array);

auto rcmap(T)(T withAsSlice)
if (hasAsSlice!T);

auto rcmap(Range)(Range r)
if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T));
Parameters:
Slice!(Iterator, N, kind) slice An ndslice, array, or an input range.
Returns:
ndslice or an input range with each fun applied to all the elements. If there is more than one fun, the element type will be Tuple containing one element for each fun.
Slice!(CachedIterator!(Iterator, CacheIterator, FlagIterator), N, kind) cached(Iterator, SliceKind kind, size_t N, CacheIterator, FlagIterator)(Slice!(Iterator, N, kind) original, Slice!(CacheIterator, N, kind) caches, Slice!(FlagIterator, N, kind) flags);
Creates a random access cache for lazyly computed elements.
Parameters:
Slice!(Iterator, N, kind) original original ndslice
Slice!(CacheIterator, N, kind) caches cached values
Slice!(FlagIterator, N, kind) flags array composed of flags that indicates if values are already computed
Returns:
ndslice, which is internally composed of three ndslices: original, allocated cache and allocated bit-ndslice.
See Also:
Examples:
import mir.ndslice.topology: cached, iota, map;
import mir.ndslice.allocation: bitSlice, uninitSlice;

int[] funCalls;

auto v = 5.iota!int
    .map!((i) {
        funCalls ~= i;
        return 2 ^^ i;
    });
auto flags = v.length.bitSlice;
auto cache = v.length.uninitSlice!int;
// cached lazy slice: 1 2 4 8 16
auto sl = v.cached(cache, flags);

assert(funCalls == []);
assert(sl[1] == 2); // remember result
assert(funCalls == [1]);
assert(sl[1] == 2); // reuse result
assert(funCalls == [1]);

assert(sl[0] == 1);
assert(funCalls == [1, 0]);
funCalls = [];

// set values directly
sl[1 .. 3] = 5;
assert(sl[1] == 5);
assert(sl[2] == 5);
// no function calls
assert(funCalls == []);
Examples:
Cache of immutable elements
import mir.ndslice.slice: DeepElementType;
import mir.ndslice.topology: cached, iota, map, as;
import mir.ndslice.allocation: bitSlice, uninitSlice;

int[] funCalls;

auto v = 5.iota!int
    .map!((i) {
        funCalls ~= i;
        return 2 ^^ i;
    })
    .as!(immutable int);
auto flags = v.length.bitSlice;
auto cache = v.length.uninitSlice!(immutable int);

// cached lazy slice: 1 2 4 8 16
auto sl = v.cached(cache, flags);

static assert(is(DeepElementType!(typeof(sl)) == immutable int));

assert(funCalls == []);
assert(sl[1] == 2); // remember result
assert(funCalls == [1]);
assert(sl[1] == 2); // reuse result
assert(funCalls == [1]);

assert(sl[0] == 1);
assert(funCalls == [1, 0]);
@trusted Slice!(CachedIterator!(Iterator, typeof(Iterator.init[0])*, FieldIterator!(BitField!(size_t*))), N) cachedGC(Iterator, size_t N)(Slice!(Iterator, N) original);

auto cachedGC(Iterator)(Slice!(Iterator, 1, Universal) from);

auto cachedGC(T)(T withAsSlice)
if (hasAsSlice!T);
Creates a random access cache for lazyly computed elements.
Parameters:
Slice!(Iterator, N) original ND Contiguous or 1D Universal ndslice.
Returns:
ndslice, which is internally composed of three ndslices: original, allocated cache and allocated bit-ndslice.
See Also:
Examples:
import mir.ndslice.topology: cachedGC, iota, map;

int[] funCalls;

// cached lazy slice: 1 2 4 8 16
auto sl = 5.iota!int
    .map!((i) {
        funCalls ~= i;
        return 2 ^^ i;
    })
    .cachedGC;

assert(funCalls == []);
assert(sl[1] == 2); // remember result
assert(funCalls == [1]);
assert(sl[1] == 2); // reuse result
assert(funCalls == [1]);

assert(sl[0] == 1);
assert(funCalls == [1, 0]);
funCalls = [];

// set values directly
sl[1 .. 3] = 5;
assert(sl[1] == 5);
assert(sl[2] == 5);
// no function calls
assert(funCalls == []);
Examples:
Cache of immutable elements
import mir.ndslice.slice: DeepElementType;
import mir.ndslice.topology: cachedGC, iota, map, as;

int[] funCalls;

// cached lazy slice: 1 2 4 8 16
auto sl = 5.iota!int
    .map!((i) {
        funCalls ~= i;
        return 2 ^^ i;
    })
    .as!(immutable int)
    .cachedGC;

static assert(is(DeepElementType!(typeof(sl)) == immutable int));

assert(funCalls == []);
assert(sl[1] == 2); // remember result
assert(funCalls == [1]);
assert(sl[1] == 2); // reuse result
assert(funCalls == [1]);

assert(sl[0] == 1);
assert(funCalls == [1, 0]);
template as(T)
Convenience function that creates a lazy view, where each element of the original slice is converted to the type T. It uses map and to  composition under the hood.
Parameters:
Slice!(Iterator, N, kind) slice a slice to create a view on.
Returns:
A lazy slice with elements converted to the type T.
See Also:
Examples:
import mir.ndslice.slice: Slice;
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : diagonal, as;

auto matrix = slice!double([2, 2], 0);
auto stringMatrixView = matrix.as!int;
assert(stringMatrixView ==
        [[0, 0],
         [0, 0]]);

matrix.diagonal[] = 1;
assert(stringMatrixView ==
        [[1, 0],
         [0, 1]]);

/// allocate new slice composed of strings
Slice!(int*, 2) stringMatrix = stringMatrixView.slice;
Examples:
Special behavior for pointers to a constant data.
import mir.ndslice.allocation : slice;
import mir.ndslice.slice: Contiguous, Slice;

Slice!(double*, 2)              matrix = slice!double([2, 2], 0);
Slice!(const(double)*, 2) const_matrix = matrix.as!(const double);
Examples:
Ranges
import mir.algorithm.iteration: filter, equal;
assert(5.iota.filter!"a % 2".as!double.map!"a / 2".equal([0.5, 1.5]));
auto as(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto as(S)(S[] array);

auto as(S)(S withAsSlice)
if (hasAsSlice!S);

auto as(Range)(Range r)
if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T));
Slice!(IndexIterator!(Iterator, Field), N, kind) indexed(Field, Iterator, size_t N, SliceKind kind)(Field source, Slice!(Iterator, N, kind) indices);

auto indexed(Field, S)(Field source, S[] indices);

auto indexed(Field, S)(Field source, S indices)
if (hasAsSlice!S);
Takes a field source and a slice 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.
Parameters:
Field source a filed, source of data. source must be an array or a pointer, or have opIndex primitive. Full random access range API is not required.
Slice!(Iterator, N, kind) indices a slice, source of indices.
Returns:
n-dimensional slice with the same kind, shape and strides.
See Also:
indexed is similar to vmap, but a field ([]) is used instead of a function (()), and order of arguments is reversed.
Examples:
auto source = [1, 2, 3, 4, 5];
auto indices = [4, 3, 1, 2, 0, 4];
auto ind = source.indexed(indices);
assert(ind == [5, 4, 2, 3, 1, 5]);

assert(ind.retro == source.indexed(indices.retro));

ind[3] += 10; // for index 2
//                0  1   2  3  4
assert(source == [1, 2, 13, 4, 5]);
Slice!(SubSliceIterator!(Iterator, Sliceable), N, kind) subSlices(Iterator, size_t N, SliceKind kind, Sliceable)(Sliceable sliceable, Slice!(Iterator, N, kind) slices);

auto subSlices(S, Sliceable)(Sliceable sliceable, S[] slices);

auto subSlices(S, Sliceable)(Sliceable sliceable, S slices)
if (hasAsSlice!S);
Maps index pairs to subslices.
Parameters:
Sliceable sliceable pointer, array, ndslice, series, or something sliceable with [a .. b].
Slice!(Iterator, N, kind) slices ndslice composed of index pairs.
Returns:
ndslice composed of subslices.
See Also:
Examples:
import mir.functional: staticArray;
auto subs =[
        staticArray(2, 4),
        staticArray(2, 10),
    ];
auto sliceable = 10.iota;

auto r = sliceable.subSlices(subs);
assert(r == [
    iota([4 - 2], 2),
    iota([10 - 2], 2),
    ]);
Slice!(ChopIterator!(Iterator, Sliceable)) chopped(Iterator, Sliceable)(Sliceable sliceable, Slice!Iterator bounds);

auto chopped(S, Sliceable)(Sliceable sliceable, S[] bounds);

auto chopped(S, Sliceable)(Sliceable sliceable, S bounds)
if (hasAsSlice!S);
Maps index pairs to subslices.
Parameters:
Slice!Iterator bounds ndslice composed of consequent (a_i <= a_(i+1)) pairwise index bounds.
Sliceable sliceable pointer, array, ndslice, series, or something sliceable with [a_i .. a_(i+1)].
Returns:
ndslice composed of subslices.
See Also:
Examples:
import mir.functional: staticArray;
import mir.ndslice.slice: sliced;
auto pairwiseIndexes = [2, 4, 10].sliced;
auto sliceable = 10.iota;

auto r = sliceable.chopped(pairwiseIndexes);
assert(r == [
    iota([4 - 2], 2),
    iota([10 - 4], 4),
    ]);
template zip(bool sameStrides = false)
Groups slices into a slice of refTuples. The slices must have identical strides or be 1-dimensional.
Parameters:
sameStrides if true assumes that all slices has the same strides.
Slices slices list of slices
Returns:
n-dimensional slice of elements refTuple
See Also:
Examples:
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : flattened, iota;

auto alpha = iota!int(4, 3);
auto beta = slice!int(4, 3).universal;

auto m = zip!true(alpha, beta);
foreach (r; m)
    foreach (e; r)
        e.b = e.a;
assert(alpha == beta);

beta[] = 0;
foreach (e; m.flattened)
    e.b = cast(int)e.a;
assert(alpha == beta);
auto zip(Slices...)(Slices slices)
if (Slices.length > 1 && allSatisfy!(isConvertibleToSlice, Slices));
Groups slices into a slice of refTuples. The slices must have identical strides or be 1-dimensional.
Parameters:
Slices slices list of slices
Returns:
n-dimensional slice of elements refTuple
See Also:
auto unzip(char name, size_t N, SliceKind kind, Iterators...)(Slice!(ZipIterator!Iterators, N, kind) slice);

auto unzip(char name, size_t N, SliceKind kind, Iterators...)(ref Slice!(ZipIterator!Iterators, N, kind) slice);
Selects a slice from a zipped slice.
Parameters:
name name of a slice to unzip.
Slice!(ZipIterator!Iterators, N, kind) slice zipped slice
Returns:
unzipped slice
Examples:
import mir.ndslice.allocation : slice;
import mir.ndslice.topology : iota;

auto alpha = iota!int(4, 3);
auto beta = iota!int([4, 3], 1).slice;

auto m = zip(alpha, beta);

static assert(is(typeof(unzip!'a'(m)) == typeof(alpha)));
static assert(is(typeof(unzip!'b'(m)) == typeof(beta)));

assert(m.unzip!'a' == alpha);
assert(m.unzip!'b' == beta);
template slideAlong(size_t params, alias fun, SDimensions...) if (params <= 'z' - 'a' + 1 && (SDimensions.length > 0))
Lazy convolution for tensors.
Suitable for advanced convolution algorithms.
Parameters:
params convolution windows length.
fun one dimensional convolution function with params arity.
SDimensions dimensions to perform lazy convolution along. Negative dimensions are supported.
See Also:
Examples:
auto data = [4, 5].iota;

alias scaled = a => a * 0.25;

auto v = data.slideAlong!(3, "a + 2 * b + c", 0).map!scaled;
auto h = data.slideAlong!(3, "a + 2 * b + c", 1).map!scaled;

assert(v == [4, 5].iota[1 .. $ - 1, 0 .. $]);
assert(h == [4, 5].iota[0 .. $, 1 .. $ - 1]);
auto slideAlong(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto slideAlong(S)(S[] slice);

auto slideAlong(S)(S slice)
if (hasAsSlice!S);
Parameters:
Slice!(Iterator, N, kind) slice ndslice or array
Returns:
lazy convolution result
template slide(size_t params, alias fun) if (params <= 'z' - 'a' + 1)
Lazy convolution for tensors.
Suitable for simple convolution algorithms.
Parameters:
params windows length.
fun one dimensional convolution function with params arity.
Examples:
auto data = 10.iota;
auto sw = data.slide!(3, "a + 2 * b + c");

import mir.utility: max;
assert(sw.length == max(0, cast(ptrdiff_t)data.length - 3 + 1));
assert(sw == sw.length.iota.map!"(a + 1) * 4");
assert(sw == [4, 8, 12, 16, 20, 24, 28, 32]);
Examples:
ND-use case
auto data = [4, 5].iota;

enum factor = 1.0 / 4 ^^ data.shape.length;
alias scaled = a => a * factor;

auto sw = data.slide!(3, "a + 2 * b + c").map!scaled;

assert(sw == [4, 5].iota[1 .. $ - 1, 1 .. $ - 1]);
auto slide(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto slide(S)(S[] slice);

auto slide(S)(S slice)
if (hasAsSlice!S);
Parameters:
Slice!(Iterator, N, kind) slice ndslice or array
Returns:
lazy convolution result
template pairwise(alias fun, size_t lag = 1)
Pairwise map for tensors.
The computation is performed on request, when the element is accessed.
Parameters:
fun function to accumulate
lag an integer indicating which lag to use
Returns:
lazy ndslice composed of fun(a_n, a_n+1) values.
Examples:
import mir.ndslice.slice: sliced;
assert([2, 4, 3, -1].sliced.pairwise!"a + b" == [6, 7, 2]);
Examples:
N-dimensional
// performs pairwise along each dimension
// 0 1 2 3
// 4 5 6 7
// 8 9 10 11
assert([3, 4].iota.pairwise!"a + b" == [[10, 14, 18], [26, 30, 34]]);
template diff(size_t lag = 1)
Differences between tensor elements.
The computation is performed on request, when the element is accessed.
Parameters:
lag an integer indicating which lag to use
Returns:
lazy differences.
See Also:
Examples:
import mir.ndslice.slice: sliced;
assert([2, 4, 3, -1].sliced.diff == [2, -1, -4]);
Examples:
N-dimensional
// 0 1 2 3
// 4 5 6 7     =>
// 8 9 10 11

// 1 1 1
// 1 1 1      =>
// 1 1 1

// 0 0 0
// 0 0 0

assert([3, 4].iota.diff == repeat(0, [2, 3]));
Examples:
packed slices
// 0 1  2  3
// 4 5  6  7
// 8 9 10 11
auto s = iota(3, 4);
import std.stdio;
assert(iota(3, 4).byDim!0.diff == [
    [4, 4, 4, 4],
    [4, 4, 4, 4]]);
assert(iota(3, 4).byDim!1.diff == [
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1]]);
Slice!(Iterator, N, N > 1 && (kind == Contiguous) ? Canonical : kind, Labels) dropBorders(Iterator, size_t N, SliceKind kind, Labels...)(Slice!(Iterator, N, kind, Labels) slice);
Drops borders for all dimensions.
Parameters:
Slice!(Iterator, N, kind, Labels) slice ndslice
Returns:
Tensors with striped borders
Examples:
assert([4, 5].iota.dropBorders == [[6, 7, 8], [11, 12, 13]]);
template withNeighboursSum(alias fun = "a + b")
Lazy zip view of elements packed with sum of their neighbours.
Parameters:
fun neighbours accumulation function.
See Also:
Examples:
import mir.ndslice.allocation: slice;
import mir.algorithm.iteration: all;

auto wn = [4, 5].iota.withNeighboursSum;
assert(wn.all!"a[0] == a[1] * 0.25");
assert(wn.map!"a" == wn.map!"b * 0.25");
auto withNeighboursSum(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto withNeighboursSum(S)(S[] slice);

auto withNeighboursSum(S)(S slice)
if (hasAsSlice!S);
Parameters:
Slice!(Iterator, N, kind) slice ndslice or array
Returns:
Lazy zip view of elements packed with sum of their neighbours.
auto cartesian(NdFields...)(NdFields fields)
if (NdFields.length > 1 && allSatisfy!(templateOr!(hasShape, hasLength), NdFields));
Cartesian product.
Constructs lazy cartesian product Slice  without memory allocation.
Parameters:
NdFields fields list of fields with lengths or ndFields with shapes
Returns:
Cartesian !NdFields(fields).slicedNdField ;
Examples:
1D x 1D
auto a = [10, 20, 30];
auto b = [ 1,  2,  3];

auto c = cartesian(a, b)
    .map!"a + b";

assert(c == [
    [11, 12, 13],
    [21, 22, 23],
    [31, 32, 33]]);
Examples:
1D x 2D
auto a = [10, 20, 30];
auto b = iota([2, 3], 1);

auto c = cartesian(a, b)
    .map!"a + b";

assert(c.shape == [3, 2, 3]);

assert(c == [
    [
        [11, 12, 13],
        [14, 15, 16],
    ],
    [
        [21, 22, 23],
        [24, 25, 26],
    ],
    [
        [31, 32, 33],
        [34, 35, 36],
    ]]);
Examples:
1D x 1D x 1D
auto u = [100, 200];
auto v = [10, 20, 30];
auto w = [1, 2];

auto c = cartesian(u, v, w)
    .map!"a + b + c";

assert(c.shape == [2, 3, 2]);

assert(c == [
    [
        [111, 112],
        [121, 122],
        [131, 132],
    ],
    [
        [211, 212],
        [221, 222],
        [231, 232],
    ]]);
template kronecker(alias fun = product)
Constructs lazy kronecker product Slice  without memory allocation.
Examples:
2D
import mir.ndslice.allocation: slice;
import mir.ndslice.slice: sliced;

// eye
auto a = slice!double([4, 4], 0);
a.diagonal[] = 1;

auto b = [ 1, -1,
          -1,  1].sliced(2, 2);

auto c = kronecker(a, b);

assert(c == [
    [ 1, -1,  0,  0,  0,  0,  0,  0],
    [-1,  1,  0,  0,  0,  0,  0,  0],
    [ 0,  0,  1, -1,  0,  0,  0,  0],
    [ 0,  0, -1,  1,  0,  0,  0,  0],
    [ 0,  0,  0,  0,  1, -1,  0,  0],
    [ 0,  0,  0,  0, -1,  1,  0,  0],
    [ 0,  0,  0,  0,  0,  0,  1, -1],
    [ 0,  0,  0,  0,  0,  0, -1,  1]]);
Examples:
1D
auto a = iota([3], 1);

auto b = [ 1, -1];

auto c = kronecker(a, b);

assert(c == [1, -1, 2, -2, 3, -3]);
Examples:
2D with 3 arguments
import mir.ndslice.allocation: slice;
import mir.ndslice.slice: sliced;

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

auto b = [ 1,  0,
           0,  1].sliced(2, 2);

auto c = [ 1, -1,
          -1,  1].sliced(2, 2);

auto d = kronecker(a, b, c);

assert(d == [
    [ 1, -1,  0,  0,  2, -2,  0,  0],
    [-1,  1,  0,  0, -2,  2,  0,  0],
    [ 0,  0,  1, -1,  0,  0,  2, -2],
    [ 0,  0, -1,  1,  0,  0, -2,  2],
    [ 3, -3,  0,  0,  4, -4,  0,  0],
    [-3,  3,  0,  0, -4,  4,  0,  0],
    [ 0,  0,  3, -3,  0,  0,  4, -4],
    [ 0,  0, -3,  3,  0,  0, -4,  4]]);
auto kronecker(NdFields...)(NdFields fields)
if (allSatisfy!(hasShape, NdFields) || allSatisfy!(hasLength, NdFields));
Parameters:
NdFields fields list of either fields with lengths or ndFields with shapes. All ndFields must have the same dimension count.
Returns:
Kronecker !(fun, NdFields)(fields).slicedNdField 
auto magic(size_t length);
Parameters:
size_t length square matrix length.
Returns:
Lazy magic matrix.
Examples:
import mir.math.sum;
import mir.ndslice: slice, magic, byDim, map, as, repeat, diagonal, antidiagonal;

bool isMagic(S)(S matrix)
{
    auto n = matrix.length;
    auto c = n * (n * n + 1) / 2; // magic number
    return // check shape
        matrix.length!0 > 0 && matrix.length!0 == matrix.length!1
        && // each row sum should equal magic number
        matrix.byDim!0.map!sum == c.repeat(n)
        && // each columns sum should equal magic number
        matrix.byDim!1.map!sum == c.repeat(n)
        && // diagonal sum should equal magic number
        matrix.diagonal.sum == c
        && // antidiagonal sum should equal magic number
        matrix.antidiagonal.sum == c;
}

assert(isMagic(magic(1)));
assert(!isMagic(magic(2))); // 2x2 magic square does not exist
foreach(n; 3 .. 24)
    assert(isMagic(magic(n)));
assert(isMagic(magic(3).as!double.slice));
Slice!(StairsIterator!(Iterator, type)) stairs(string type, Iterator)(Slice!Iterator slice, size_t n)
if (type == "+" || type == "-");

Slice!(StairsIterator!(S*, type)) stairs(string type, S)(S[] slice, size_t n)
if (type == "+" || type == "-");

auto stairs(string type, S)(S slice, size_t n)
if (hasAsSlice!S && (type == "+" || type == "-"));
Chops 1D input slice into n chunks with ascending or descending lengths.
stairs can be used to pack and unpack symmetric and triangular matrix storage.

Note stairs is defined for 1D (packet) input and 2D (general) input. This part of documentation is for 1D input.

Parameters:
type
  • "-" for stairs with lengths n, n-1, ..., 1.
  • "+" for stairs with lengths 1, 2, ..., n;
Slice!Iterator slice input slice with length equal to n * (n + 1) / 2
size_t n stairs count
Returns:
1D contiguous slice composed of 1D contiguous slices.
Examples:
import mir.ndslice.topology: iota, stairs;

auto pck = 15.iota;
auto inc = pck.stairs!"+"(5);
auto dec = pck.stairs!"-"(5);

assert(inc == [
    [0],
    [1, 2],
    [3, 4, 5],
    [6, 7, 8, 9],
    [10, 11, 12, 13, 14]]);
assert(inc[1 .. $][2] == [6, 7, 8, 9]);

assert(dec == [
    [0, 1, 2, 3, 4],
       [5, 6, 7, 8],
        [9, 10, 11],
           [12, 13],
               [14]]);
assert(dec[1 .. $][2] == [12, 13]);

static assert(is(typeof(inc.front) == typeof(pck)));
static assert(is(typeof(dec.front) == typeof(pck)));
auto stairs(string type, Iterator, SliceKind kind)(Slice!(Iterator, 2, kind) slice)
if (type == "+" || type == "-");
Slice composed of rows of lower or upper triangular matrix.
stairs can be used to pack and unpack symmetric and triangular matrix storage.

Note stairs is defined for 1D (packet) input and 2D (general) input. This part of documentation is for 2D input.

Parameters:
type
  • "+" for stairs with lengths 1, 2, ..., n, lower matrix;
  • "-" for stairs with lengths n, n-1, ..., 1, upper matrix.
Slice!(Iterator, 2, kind) slice input slice with length equal to n * (n + 1) / 2
Returns:
1D slice composed of 1D contiguous slices.
Examples:
import mir.ndslice.topology: iota, as, stairs;

auto gen = [3, 3].iota.as!double;
auto inc = gen.stairs!"+";
auto dec = gen.stairs!"-";

assert(inc == [
    [0],
    [3, 4],
    [6, 7, 8]]);

assert(dec == [
    [0, 1, 2],
       [4, 5],
          [8]]);

static assert(is(typeof(inc.front) == typeof(gen.front)));
static assert(is(typeof(dec.front) == typeof(gen.front)));

/////////////////////////////////////////
// Pack lower and upper matrix parts
auto n = gen.length;
auto m = n * (n + 1) / 2;
// allocate memory
import mir.ndslice.allocation: uninitSlice;
auto lowerData = m.uninitSlice!double;
auto upperData = m.uninitSlice!double;
// construct packed stairs
auto lower = lowerData.stairs!"+"(n);
auto upper = upperData.stairs!"-"(n);
// copy data
import mir.algorithm.iteration: each;
each!"a[] = b"(lower, inc);
each!"a[] = b"(upper, dec);

assert(&lower[0][0] is &lowerData[0]);
assert(&upper[0][0] is &upperData[0]);

assert(lowerData == [0, 3, 4, 6, 7, 8]);
assert(upperData == [0, 1, 2, 4, 5, 8]);
template alongDim(SDimensions...) if (SDimensions.length > 0)
Returns a slice that can be iterated along dimension. Transposes other dimensions on top and then packs them.
Combines byDim and evertPack.
Parameters:
SDimensions dimensions to iterate along, length of d, 1 <= d < n. Negative dimensions are supported.
Returns:
(n-d)-dimensional slice composed of d-dimensional slices
See Also:
Examples:
2-dimensional slice support
import mir.ndslice;

//  ------------
// | 0  1  2  3 |
// | 4  5  6  7 |
// | 8  9 10 11 |
//  ------------
auto slice = iota(3, 4);
//->
// | 3 |
//->
// | 4 |
size_t[1] shape3 = [3];
size_t[1] shape4 = [4];

//  ------------
// | 0  1  2  3 |
// | 4  5  6  7 |
// | 8  9 10 11 |
//  ------------
auto x = slice.alongDim!(-1); // -1 is the last dimension index, the same as 1 for this case.
static assert(is(typeof(x) == Slice!(SliceIterator!(IotaIterator!sizediff_t), 1, Universal)));

assert(x.shape == shape3);
assert(x.front.shape == shape4);
assert(x.front == iota(4));
x.popFront;
assert(x.front == iota([4], 4));

//  ---------
// | 0  4  8 |
// | 1  5  9 |
// | 2  6 10 |
// | 3  7 11 |
//  ---------
auto y = slice.alongDim!0; // alongDim!(-2) is the same for matrices.
static assert(is(typeof(y) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 1, Universal))));

assert(y.shape == shape4);
assert(y.front.shape == shape3);
assert(y.front == iota([3], 0, 4));
y.popFront;
assert(y.front == iota([3], 1, 4));
Examples:
3-dimensional slice support, N-dimensional also supported
import mir.ndslice;

//  ----------------
// | 0   1  2  3  4 |
// | 5   6  7  8  9 |
// | 10 11 12 13 14 |
// | 15 16 17 18 19 |
//  - - - - - - - -
// | 20 21 22 23 24 |
// | 25 26 27 28 29 |
// | 30 31 32 33 34 |
// | 35 36 37 38 39 |
//  - - - - - - - -
// | 40 41 42 43 44 |
// | 45 46 47 48 49 |
// | 50 51 52 53 54 |
// | 55 56 57 58 59 |
//  ----------------
auto slice = iota(3, 4, 5);

size_t[2] shape45 = [4, 5];
size_t[2] shape35 = [3, 5];
size_t[2] shape34 = [3, 4];
size_t[2] shape54 = [5, 4];
size_t[1] shape3 = [3];
size_t[1] shape4 = [4];
size_t[1] shape5 = [5];

//  ----------
// |  0 20 40 |
// |  5 25 45 |
// | 10 30 50 |
// | 15 35 55 |
//  - - - - -
// |  1 21 41 |
// |  6 26 46 |
// | 11 31 51 |
// | 16 36 56 |
//  - - - - -
// |  2 22 42 |
// |  7 27 47 |
// | 12 32 52 |
// | 17 37 57 |
//  - - - - -
// |  3 23 43 |
// |  8 28 48 |
// | 13 33 53 |
// | 18 38 58 |
//  - - - - -
// |  4 24 44 |
// |  9 29 49 |
// | 14 34 54 |
// | 19 39 59 |
//  ----------
auto a = slice.alongDim!0.transposed;
static assert(is(typeof(a) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 1, Universal), 2, Universal)));

assert(a.shape == shape54);
assert(a.front.shape == shape4);
assert(a.front.unpack == iota([3, 4], 0, 5).universal.transposed);
a.popFront;
assert(a.front.front == iota([3], 1, 20));

//  ----------------
// |  0  1  2  3  4 |
// |  5  6  7  8  9 |
// | 10 11 12 13 14 |
// | 15 16 17 18 19 |
//  - - - - - - - -
// | 20 21 22 23 24 |
// | 25 26 27 28 29 |
// | 30 31 32 33 34 |
// | 35 36 37 38 39 |
//  - - - - - - - -
// | 40 41 42 43 44 |
// | 45 46 47 48 49 |
// | 50 51 52 53 54 |
// | 55 56 57 58 59 |
//  ----------------
auto x = slice.alongDim!(1, 2);
static assert(is(typeof(x) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 2), 1, Universal)));

assert(x.shape == shape3);
assert(x.front.shape == shape45);
assert(x.front == iota([4, 5]));
x.popFront;
assert(x.front == iota([4, 5], (4 * 5)));

//  ----------------
// |  0  1  2  3  4 |
// | 20 21 22 23 24 |
// | 40 41 42 43 44 |
//  - - - - - - - -
// |  5  6  7  8  9 |
// | 25 26 27 28 29 |
// | 45 46 47 48 49 |
//  - - - - - - - -
// | 10 11 12 13 14 |
// | 30 31 32 33 34 |
// | 50 51 52 53 54 |
//  - - - - - - - -
// | 15 16 17 18 19 |
// | 35 36 37 38 39 |
// | 55 56 57 58 59 |
//  ----------------
auto y = slice.alongDim!(0, 2);
static assert(is(typeof(y) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 2, Canonical), 1, Universal)));

assert(y.shape == shape4);
assert(y.front.shape == shape35);
int err;
assert(y.front == slice.universal.strided!1(4).reshape([3, -1], err));
y.popFront;
assert(y.front.front == iota([5], 5));

//  -------------
// |  0  5 10 15 |
// | 20 25 30 35 |
// | 40 45 50 55 |
//  - - - - - - -
// |  1  6 11 16 |
// | 21 26 31 36 |
// | 41 46 51 56 |
//  - - - - - - -
// |  2  7 12 17 |
// | 22 27 32 37 |
// | 42 47 52 57 |
//  - - - - - - -
// |  3  8 13 18 |
// | 23 28 33 38 |
// | 43 48 53 58 |
//  - - - - - - -
// |  4  9 14 19 |
// | 24 29 34 39 |
// | 44 49 54 59 |
//  -------------
auto z = slice.alongDim!(0, 1);
static assert(is(typeof(z) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 2, Universal))));

assert(z.shape == shape5);
assert(z.front.shape == shape34);
assert(z.front == iota([3, 4], 0, 5));
z.popFront;
assert(z.front.front == iota([4], 1, 5));
Examples:
Use alongDim to calculate column mean/row mean of 2-dimensional slice
import mir.ndslice.topology: alongDim;
import mir.ndslice.fuse: fuse;
import mir.math.stat: mean;
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

// Use alongDim with map to compute mean of row/column.
assert(x.alongDim!1.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.alongDim!0.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));

// FIXME
// Without using map, computes the mean of the whole slice
// assert(x.alongDim!1.mean == x.sliced.mean);
// assert(x.alongDim!0.mean == x.sliced.mean);
Examples:
Use alongDim and map with a lambda, but may need to allocate result. This example uses fuse, which allocates. Note: fuse!1 will transpose the result.
import mir.ndslice.topology: iota, alongDim, map;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;

auto x = [1, 2, 3].sliced;
auto y = [1, 2].sliced;

auto s1 = iota(2, 3).alongDim!1.map!(a => a * x).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s2 = iota(2, 3).alongDim!0.map!(a => a * y).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
auto alongDim(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if (N > SDimensions.length);
Parameters:
Slice!(Iterator, N, kind) slice input n-dimensional slice, n > d
Returns:
(n-d)-dimensional slice composed of d-dimensional slices
template byDim(SDimensions...) if (SDimensions.length > 0)
Returns a slice that can be iterated by dimension. Transposes dimensions on top and then packs them.
Combines transposed , ipack, and SliceKind Selectors.
Parameters:
SDimensions dimensions to perform iteration on, length of d, 1 <= d <= n. Negative dimensions are supported.
Returns:
d-dimensional slice composed of (n-d)-dimensional slices
See Also:
Examples:
2-dimensional slice support
import mir.ndslice;

//  ------------
// | 0  1  2  3 |
// | 4  5  6  7 |
// | 8  9 10 11 |
//  ------------
auto slice = iota(3, 4);
//->
// | 3 |
//->
// | 4 |
size_t[1] shape3 = [3];
size_t[1] shape4 = [4];

//  ------------
// | 0  1  2  3 |
// | 4  5  6  7 |
// | 8  9 10 11 |
//  ------------
auto x = slice.byDim!0; // byDim!(-2) is the same for matrices.
static assert(is(typeof(x) == Slice!(SliceIterator!(IotaIterator!sizediff_t), 1, Universal)));

assert(x.shape == shape3);
assert(x.front.shape == shape4);
assert(x.front == iota(4));
x.popFront;
assert(x.front == iota([4], 4));

//  ---------
// | 0  4  8 |
// | 1  5  9 |
// | 2  6 10 |
// | 3  7 11 |
//  ---------
auto y = slice.byDim!(-1); // -1 is the last dimension index, the same as 1 for this case.
static assert(is(typeof(y) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 1, Universal))));

assert(y.shape == shape4);
assert(y.front.shape == shape3);
assert(y.front == iota([3], 0, 4));
y.popFront;
assert(y.front == iota([3], 1, 4));
Examples:
3-dimensional slice support, N-dimensional also supported
import mir.ndslice;

//  ----------------
// | 0   1  2  3  4 |
// | 5   6  7  8  9 |
// | 10 11 12 13 14 |
// | 15 16 17 18 19 |
//  - - - - - - - -
// | 20 21 22 23 24 |
// | 25 26 27 28 29 |
// | 30 31 32 33 34 |
// | 35 36 37 38 39 |
//  - - - - - - - -
// | 40 41 42 43 44 |
// | 45 46 47 48 49 |
// | 50 51 52 53 54 |
// | 55 56 57 58 59 |
//  ----------------
auto slice = iota(3, 4, 5);

size_t[2] shape45 = [4, 5];
size_t[2] shape35 = [3, 5];
size_t[2] shape34 = [3, 4];
size_t[2] shape54 = [5, 4];
size_t[1] shape3 = [3];
size_t[1] shape4 = [4];
size_t[1] shape5 = [5];

//  ----------------
// |  0  1  2  3  4 |
// |  5  6  7  8  9 |
// | 10 11 12 13 14 |
// | 15 16 17 18 19 |
//  - - - - - - - -
// | 20 21 22 23 24 |
// | 25 26 27 28 29 |
// | 30 31 32 33 34 |
// | 35 36 37 38 39 |
//  - - - - - - - -
// | 40 41 42 43 44 |
// | 45 46 47 48 49 |
// | 50 51 52 53 54 |
// | 55 56 57 58 59 |
//  ----------------
auto x = slice.byDim!0;
static assert(is(typeof(x) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 2), 1, Universal)));

assert(x.shape == shape3);
assert(x.front.shape == shape45);
assert(x.front == iota([4, 5]));
x.popFront;
assert(x.front == iota([4, 5], (4 * 5)));

//  ----------------
// |  0  1  2  3  4 |
// | 20 21 22 23 24 |
// | 40 41 42 43 44 |
//  - - - - - - - -
// |  5  6  7  8  9 |
// | 25 26 27 28 29 |
// | 45 46 47 48 49 |
//  - - - - - - - -
// | 10 11 12 13 14 |
// | 30 31 32 33 34 |
// | 50 51 52 53 54 |
//  - - - - - - - -
// | 15 16 17 18 19 |
// | 35 36 37 38 39 |
// | 55 56 57 58 59 |
//  ----------------
auto y = slice.byDim!1;
static assert(is(typeof(y) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 2, Canonical), 1, Universal)));

assert(y.shape == shape4);
assert(y.front.shape == shape35);
int err;
assert(y.front == slice.universal.strided!1(4).reshape([3, -1], err));
y.popFront;
assert(y.front.front == iota([5], 5));

//  -------------
// |  0  5 10 15 |
// | 20 25 30 35 |
// | 40 45 50 55 |
//  - - - - - - -
// |  1  6 11 16 |
// | 21 26 31 36 |
// | 41 46 51 56 |
//  - - - - - - -
// |  2  7 12 17 |
// | 22 27 32 37 |
// | 42 47 52 57 |
//  - - - - - - -
// |  3  8 13 18 |
// | 23 28 33 38 |
// | 43 48 53 58 |
//  - - - - - - -
// |  4  9 14 19 |
// | 24 29 34 39 |
// | 44 49 54 59 |
//  -------------
auto z = slice.byDim!2;
static assert(is(typeof(z) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 2, Universal))));

assert(z.shape == shape5);
assert(z.front.shape == shape34);
assert(z.front == iota([3, 4], 0, 5));
z.popFront;
assert(z.front.front == iota([4], 1, 5));

//  ----------
// |  0 20 40 |
// |  5 25 45 |
// | 10 30 50 |
// | 15 35 55 |
//  - - - - -
// |  1 21 41 |
// |  6 26 46 |
// | 11 31 51 |
// | 16 36 56 |
//  - - - - -
// |  2 22 42 |
// |  7 27 47 |
// | 12 32 52 |
// | 17 37 57 |
//  - - - - -
// |  3 23 43 |
// |  8 28 48 |
// | 13 33 53 |
// | 18 38 58 |
//  - - - - -
// |  4 24 44 |
// |  9 29 49 |
// | 14 34 54 |
// | 19 39 59 |
//  ----------
auto a = slice.byDim!(2, 1);
static assert(is(typeof(a) == Slice!(SliceIterator!(IotaIterator!sizediff_t, 1, Universal), 2, Universal)));

assert(a.shape == shape54);
assert(a.front.shape == shape4);
assert(a.front.unpack == iota([3, 4], 0, 5).universal.transposed);
a.popFront;
assert(a.front.front == iota([3], 1, 20));
Examples:
Use byDim to calculate column mean/row mean of 2-dimensional slice
import mir.ndslice.topology: byDim;
import mir.ndslice.fuse: fuse;
import mir.math.stat: mean;
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

// Use byDim with map to compute mean of row/column.
assert(x.byDim!0.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
assert(x.byDim!1.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));

// FIXME
// Without using map, computes the mean of the whole slice
// assert(x.byDim!0.mean == x.sliced.mean);
// assert(x.byDim!1.mean == x.sliced.mean);
Examples:
Use byDim and map with a lambda, but may need to allocate result. This example uses fuse, which allocates. Note: fuse!1 will transpose the result.
import mir.ndslice.topology: iota, byDim, map;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;

auto x = [1, 2, 3].sliced;
auto y = [1, 2].sliced;

auto s1 = iota(2, 3).byDim!0.map!(a => a * x).fuse;
assert(s1 == [[ 0, 2,  6],
              [ 3, 8, 15]]);
auto s2 = iota(2, 3).byDim!1.map!(a => a * y).fuse!1;
assert(s2 == [[ 0, 1,  2],
              [ 6, 8, 10]]);
auto byDim(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
if (N >= SDimensions.length);
Parameters:
Slice!(Iterator, N, kind) slice input n-dimensional slice, n >= d
Returns:
d-dimensional slice composed of (n-d)-dimensional slices
template squeeze(sizediff_t axis = 0)
Constructs a new view of an n-dimensional slice with dimension axis removed.
Throws:
AssertError if the length of the corresponding dimension doesn' equal 1.
Parameters:
axis dimension to remove, if it is single-dimensional
Slice!(Iterator, N, kind) slice n-dimensional slice
Returns:
new view of a slice with dimension removed
See Also:
Examples:
import mir.ndslice.topology : iota;
import mir.ndslice.allocation : slice;

// [[0, 1, 2]] -> [0, 1, 2]
assert([1, 3].iota.squeeze == [3].iota);
// [[0], [1], [2]] -> [0, 1, 2]
assert([3, 1].iota.squeeze!1 == [3].iota);
assert([3, 1].iota.squeeze!(-1) == [3].iota);

assert([1, 3].iota.canonical.squeeze == [3].iota);
assert([3, 1].iota.canonical.squeeze!1 == [3].iota);
assert([3, 1].iota.canonical.squeeze!(-1) == [3].iota);

assert([1, 3].iota.universal.squeeze == [3].iota);
assert([3, 1].iota.universal.squeeze!1 == [3].iota);
assert([3, 1].iota.universal.squeeze!(-1) == [3].iota);

assert([1, 3, 4].iota.squeeze == [3, 4].iota);
assert([3, 1, 4].iota.squeeze!1 == [3, 4].iota);
assert([3, 4, 1].iota.squeeze!(-1) == [3, 4].iota);

assert([1, 3, 4].iota.canonical.squeeze == [3, 4].iota);
assert([3, 1, 4].iota.canonical.squeeze!1 == [3, 4].iota);
assert([3, 4, 1].iota.canonical.squeeze!(-1) == [3, 4].iota);

assert([1, 3, 4].iota.universal.squeeze == [3, 4].iota);
assert([3, 1, 4].iota.universal.squeeze!1 == [3, 4].iota);
assert([3, 4, 1].iota.universal.squeeze!(-1) == [3, 4].iota);
Slice!(Iterator, N + 1, kind) unsqueeze(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, sizediff_t axis);

template unsqueeze(sizediff_t axis = 0)
Constructs a view of an n-dimensional slice with a dimension added at axis. Used to unsqueeze a squeezed slice.
Parameters:
Slice!(Iterator, N, kind) slice n-dimensional slice
sizediff_t axis dimension to be unsqueezed (add new dimension), default values is 0, the first dimension
Returns:
unsqueezed n+1-dimensional slice of the same slice kind
See Also:
Examples:
// [0, 1, 2] -> [[0, 1, 2]]
assert([3].iota.unsqueeze == [1, 3].iota);

assert([3].iota.universal.unsqueeze == [1, 3].iota);
assert([3, 4].iota.unsqueeze == [1, 3, 4].iota);
assert([3, 4].iota.canonical.unsqueeze == [1, 3, 4].iota);
assert([3, 4].iota.universal.unsqueeze == [1, 3, 4].iota);

// [0, 1, 2] -> [[0], [1], [2]]
assert([3].iota.unsqueeze(-1) == [3, 1].iota);
assert([3].iota.unsqueeze!(-1) == [3, 1].iota);

assert([3].iota.universal.unsqueeze(-1) == [3, 1].iota);
assert([3].iota.universal.unsqueeze!(-1) == [3, 1].iota);
assert([3, 4].iota.unsqueeze(-1) == [3, 4, 1].iota);
assert([3, 4].iota.unsqueeze!(-1) == [3, 4, 1].iota);
assert([3, 4].iota.canonical.unsqueeze(-1) == [3, 4, 1].iota);
assert([3, 4].iota.canonical.unsqueeze!(-1) == [3, 4, 1].iota);
assert([3, 4].iota.universal.unsqueeze(-1) == [3, 4, 1].iota);
assert([3, 4].iota.universal.unsqueeze!(-1) == [3, 4, 1].iota);
template member(string name) if (name.length)
Field (element's member) projection.
Parameters:
name element's member name
Returns:
lazy n-dimensional slice of the same shape
See Also:
Examples:
// struct, union or class
struct S
{
    // Property support
    // Getter always must be defined.
    double _x;
    double x() @property
    {
        return x;
    }
    void x(double x) @property
    {
        _x = x;
    }

    /// Field support
    double y;

    /// Zero argument function support
    double f()
    {
        return _x * 2;
    }
}

import mir.ndslice.allocation: slice;
import mir.ndslice.topology: iota;

auto matrix = slice!S(2, 3);
matrix.member!"x"[] = [2, 3].iota;
matrix.member!"y"[] = matrix.member!"f";
assert(matrix.member!"y" == [2, 3].iota * 2);
Slice!(MemberIterator!(Iterator, name), N, kind) member(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

Slice!(MemberIterator!(T*, name)) member(T)(T[] array);

auto member(T)(T withAsSlice)
if (hasAsSlice!T);
Parameters:
Slice!(Iterator, N, kind) slice n-dimensional slice composed of structs, classes or unions
Returns:
lazy n-dimensional slice of the same shape
template orthogonalReduceField(alias fun)
Functional deep-element wise reduce of a slice composed of fields or iterators.
Examples:
bit array operations
import mir.ndslice.slice: slicedField;
import mir.ndslice.allocation: bitSlice;
import mir.ndslice.dynamic: strided;
import mir.ndslice.topology: iota, orthogonalReduceField;
auto len = 100;
auto a = len.bitSlice;
auto b = len.bitSlice;
auto c = len.bitSlice;
a[len.iota.strided!0(7)][] = true;
b[len.iota.strided!0(11)][] = true;
c[len.iota.strided!0(13)][] = true;

// this is valid since bitslices above are oroginal slices of allocated memory.
auto and =
    orthogonalReduceField!"a & b"(size_t.max, [
        a.iterator._field._field, // get raw data pointers
        b.iterator._field._field,
        c.iterator._field._field,
    ]) // operation on size_t
    .bitwiseField
    .slicedField(len);

assert(and == (a & b & c));
OrthogonalReduceField!(Iterator, fun, I) orthogonalReduceField(I, Iterator)(I initialValue, Slice!Iterator slice);

OrthogonalReduceField!(T*, fun, I) orthogonalReduceField(I, T)(I initialValue, T[] array);

auto orthogonalReduceField(I, T)(I initialValue, T withAsSlice)
if (hasAsSlice!T);
Parameters:
Slice!Iterator slice Non empty input slice composed of fields or iterators.
Returns:
a lazy field with each element of which is reduced value of element of the same index of all iterators.
Slice!(TripletIterator!(Iterator, kind)) triplets(Iterator, SliceKind kind)(Slice!(Iterator, 1, kind) slice);

Slice!(TripletIterator!(T*)) triplets(T)(return scope T[] slice);

auto triplets(string type, S)(S slice, size_t n)
if (hasAsSlice!S);
Constructs a lazy view of triplets with left, center, and right members.
Returns:
Slice of the same length composed of Triplet  triplets. The center member is type of a slice element. The left and right members has the same type as slice.
The module contains special function collapse to handle left and right side of triplets in one expression.
Parameters:
Slice!(Iterator, 1, kind) slice a slice or an array to iterate over

Example

triplets(eeeeee) =>

||c|lllll|
|r|c|llll|
|rr|c|lll|
|rrr|c|ll|
|rrrr|c|l|
|rrrrr|c||

See Also:
Examples:
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: triplets, member, iota;

auto a = [4, 5, 2, 8];
auto h = a.triplets;

assert(h[1].center == 5);
assert(h[1].left == [4]);
assert(h[1].right == [2, 8]);

h[1].center = 9;
assert(a[1] == 9);

assert(h.member!"center" == a);

// `triplets` topology can be used with iota to index a slice
auto s = a.sliced;
auto w = s.length.iota.triplets[1];

assert(&s[w.center] == &a[1]);
assert(s[w.left].field is a[0 .. 1]);
assert(s[w.right].field is a[2 .. $]);