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

Selectors create new views and iteration patterns over the same data, without copying.

Kind Selectors

Function Name Description
universal Converts a slice to universal SliceKind .
canonical Converts a slice to canonical SliceKind .
assumeCanonical Converts a slice to canonical SliceKind  (unsafe).
assumeContiguous Converts a slice to contiguous SliceKind  (unsafe).

Sequence Selectors

Function Name Description
repeat Slice with identical values
iota Contiguous Slice with initial flattened (contiguous) index.
ndiota Contiguous Slice with initial multidimensional index.
linspace Evenly spaced numbers over a specified interval.
magic Magic square.

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.
diff Differences between vector elements.
flattened Contiguous 1-dimensional slice of all elements of a slice.
map Multidimensional functional map.
pairwise Pairwise map for vectors.
retro Reverses order of iteration for all dimensions.
slide Sliding map for vectors.
stairs Two functions to pack, unpack, and iterate triangular and symmetric matrix storage.
stride Strides 1-dimensional slice.
unzip Selects a slice from a zipped slice.
zip Zips slices into a slice of refTuples.

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
reshape New slice with changed dimensions for the same data
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
pack Returns slice of slices.
ipack Returns slice of slices.
unpack Merges all dimension packs.
evertPack Reverses dimension packs.
byDim Returns a slice that can be iterated by dimension. Transposes dimensions on top and then packs them.
Subspace selectors serve to generalize and combine other selectors easily. For a slice of Slice!(kind, [N], Iterator) 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!(Contiguous, [K], IteratorX). 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.elementsCount .
Authors:
Ilya Yaroshenko

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

auto universal(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice);
Converts a slice to universal kind.
Parameters:
Slice!(kind, packs, Iterator) 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]);
Slice!(packs == [1] ? Contiguous : Canonical, packs, Iterator) canonical(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (kind == Contiguous || kind == Canonical);
Converts a slice to canonical kind.
Parameters:
Slice!(kind, packs, Iterator) 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]);
Slice!(Canonical, packs, Iterator) assumeCanonical(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice);
Converts a slice to canonical kind (unsafe).
Parameters:
Slice!(kind, packs, Iterator) 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]);
Slice!(Contiguous, packs, Iterator) assumeContiguous(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice);
Converts a slice to contiguous kind (unsafe).
Parameters:
Slice!(kind, packs, Iterator) 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]);
assert(slice._strides == []);
Slice!(kind, [packs[0] - p, p] ~ packs[1..$], Iterator) pack(size_t p, SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (p);
Creates a packed slice, i.e. slice of slices. Packs the last p dimensions of the first pack. The function does not carry out any calculations, it simply returns the same binary data presented differently.
Parameters:
p size of dimension pack
Slice!(kind, packs, Iterator) slice a slice to pack
Returns:
slice.pack!p returns Slice!(kind, [packs[0] - p, p] ~ packs[1 .. $], 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);
static assert(is(typeof(b) == typeof(a.pack!2)));
static assert(is(typeof(b) == Slice!(Contiguous, [2, 2], IotaIterator!size_t)));
Slice!(kind, [p, packs[0] - p] ~ packs[1..$], Iterator) ipack(size_t p, SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (p);
Creates a packed slice, i.e. slice of slices. Packs the first p dimensions of the first pack. The function does not carry out any calculations, it simply returns the same binary data presented differently.
Parameters:
p size of dimension pack
Slice!(kind, packs, Iterator) slice a slice to pack
Returns:
slice.ipack!p returns Slice!(kind, [p, packs[0] - p] ~ packs[1 .. $], Iterator)
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 == b);
static assert(is(typeof(b) == typeof(a.ipack!2)));
static assert(is(typeof(b) == Slice!(Contiguous, [2, 2], IotaIterator!size_t)));
Slice!(kind, [packs.sum], Iterator) unpack(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice);
Unpacks a packed slice.
The function does not carry out any calculations, it simply returns the same binary data presented differently.
Parameters:
Slice!(kind, packs, Iterator) slice packed slice
Returns:
unpacked slice
See Also:
Slice!(Universal, reverse(packs), Iterator) evertPack(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (packs.length > 1);
Reverses the order of dimension packs. This function is used in a functional pipeline with other selectors.
Parameters:
Slice!(kind, packs, Iterator) slice packed slice
Returns:
packed slice
See Also:
Slice!(kind, packs, Iterator) evertPack(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (packs.length == 1);
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.slice;
import mir.ndslice.dynamic : transposed;
auto a = iota(3, 4, 5, 6, 7, 8, 9, 10, 11).universal;
auto b = a
    .pack!2
    .pack!3
    .evertPack;
auto c = b[8, 9];
auto d = c[5, 6, 7];
auto e = d[1, 2, 3, 4];
auto g = a[1, 2, 3, 4, 5, 6, 7, 8, 9];
assert(e == g);
assert(a == b.evertPack);
assert(c == a.transposed!(7, 8, 4, 5, 6)[8, 9]);
static assert(is(typeof(b) == Slice!(Universal, [2, 3, 4], IotaIterator!size_t)));
static assert(is(typeof(c) == Slice!(Universal, [3, 4], IotaIterator!size_t)));
static assert(is(typeof(d) == Slice!(Universal, [4], IotaIterator!size_t)));
static assert(is(typeof(e) == size_t));
Examples:
auto a = iota(3, 4, 5, 6, 7, 8, 9, 10, 11);
auto b = a.pack!2.unpack;
static assert(is(typeof(a) == typeof(b)));
assert(a == b);
Slice!(Contiguous, [N], IotaIterator!I) iota(I = size_t, size_t N)(size_t[N] lengths...)
if (isIntegral!I);

Slice!(Contiguous, [N], IotaIterator!I) iota(I, size_t N)(size_t[N] lengths, I start)
if (isIntegral!I || isPointer!I);

Slice!(Contiguous, [N], StrideIterator!(IotaIterator!I)) iota(I, size_t N)(size_t[N] lengths, I start, size_t stride)
if (isIntegral!I || isPointer!I);
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
I 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 indexes
See Also:
Examples:
auto slice = iota(2, 3);
static immutable array =
    [[0, 1, 2],
     [3, 4, 5]];

assert(slice == array);
Examples:
int[6] data;
auto slice = iota([2, 3], data.ptr);
auto array =
    [[data.ptr + 0, data.ptr + 1, data.ptr + 2],
     [data.ptr + 3, data.ptr + 4, data.ptr + 5]];

assert(slice == array);
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!(packs[0] == 1 ? kind : Universal, 1 ~ packs[1..$], Iterator) diagonal(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) 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!(kind, packs, Iterator) slice input slice
Returns:
1-dimensional slice composed of diagonal elements
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:
Matrix, antidiagonal
import mir.ndslice.dynamic : dropToHypercube, reversed;
//  -------
// | 0 1 2 |
// | 3 4 5 |
//  -------
//->
// | 1 3 |
static immutable d = [1, 3];
assert(iota(2, 3).universal.dropToHypercube.reversed!1.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!(kind == Contiguous ? Canonical : kind, packs[0] ~ packs, Iterator) blocks(SliceKind kind, size_t[] packs, Iterator, size_t N)(Slice!(kind, packs, Iterator) slice, size_t[N] lengths...)
if (packs[0] == N);
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!(kind, packs, Iterator) slice slice to be split into blocks
size_t[N] lengths 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
    .pack!2;

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!(kind == Contiguous ? Canonical : kind, packs[0] ~ packs, Iterator) windows(SliceKind kind, size_t[] packs, Iterator, size_t N)(Slice!(kind, packs, Iterator) slice, size_t[N] lengths...)
if (packs[0] == N);
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!(kind, packs, Iterator) slice slice to be iterated
size_t[N] lengths 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
    .pack!2;


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!(kind, M ~ packs[1..$], Iterator) reshape(SliceKind kind, size_t[] packs, Iterator, size_t M)(Slice!(kind, packs, Iterator) slice, ptrdiff_t[M] lengths, ref int err);
Returns a new slice for the same data with different dimensions.
Parameters:
Slice!(kind, packs, Iterator) slice slice to be reshaped
ptrdiff_t[M] lengths 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!(Contiguous, [1], FlattenedIterator!(kind, packs, Iterator)) flattened(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (packs[0] != 1 && kind != Contiguous);

Slice!(Contiguous, 1 ~ packs[1..$], Iterator) flattened(size_t[] packs, Iterator)(Slice!(Contiguous, packs, Iterator) slice);

Slice!(Contiguous, [1], StrideIterator!Iterator) flattened(Iterator)(Slice!(Universal, [1], Iterator) slice);

Slice!(Contiguous, [1], StrideIterator!(SliceIterator!(packs[1..$].sum == 1 && kind == Canonical ? Contiguous : kind, packs[1 .. $], Iterator))) flattened(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (packs[0] == 1 && kind != Contiguous && packs.length > 1);
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!(kind, packs, Iterator) 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 availble only for canonical and universal ndslices.
assert(elems._iterator._indexes == [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._indexes;
    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;

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!(Contiguous, [N], FieldIterator!(ndIotaField!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 indexes
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]);

// 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
auto s = linspace!cdouble([3], [1.0 + 0i, 2.0 + 4i]);
assert(s == [1.0 + 0i, 1.5 + 2i, 2.0 + 4i]);
Slice!(Contiguous, [M], FieldIterator!(RepeatField!T)) repeat(T, size_t M)(T value, size_t[M] lengths...)
if (M && !is(T : Slice!(kind, packs, Iterator), SliceKind kind, size_t[] packs, Iterator));

Slice!(kind == Contiguous ? Canonical : kind, M ~ packs, Iterator) repeat(SliceKind kind, size_t[] packs, Iterator, size_t M)(Slice!(kind, packs, Iterator) 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:
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 stride(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice, ptrdiff_t factor)
if (packs == [1]);
Strides 1-dimensional slice.
Parameters:
Slice!(kind, packs, Iterator) 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);
assert(slice.universal.stride(2) == str);
@trusted auto retro(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice)
if (packs.length == 1);
Reverses order of iteration for all dimensions.
Parameters:
Slice!(kind, packs, Iterator) slice Unpacked slice.
Returns:
Slice 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)));
auto bitwise(SliceKind kind, size_t[] packs, Iterator, I = typeof(Iterator.init[size_t.init]))(Slice!(kind, packs, Iterator) slice)
if (isIntegral!I && (kind == Contiguous || kind == Canonical));
Bitwise slice over an integral slice.
Parameters:
Slice!(kind, packs, Iterator) slice a contiguous or canonical slice on top of integral iterator.
Returns:
A bitwise slice.
Examples:
size_t[10] data;
auto bits = data[].sliced.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 bitpack(size_t pack, SliceKind kind, size_t[] packs, Iterator, I = typeof(Iterator.init[size_t.init]))(Slice!(kind, packs, Iterator) slice)
if (isIntegral!I && (kind == Contiguous || kind == Canonical) && pack > 1);
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!(kind, packs, Iterator) 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[].sliced.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);
template map(fun...) if (fun.length)
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:
Packed tensors.
import mir.ndslice.topology : iota, windows;

//  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!((a) {
        size_t s;
        foreach (r; a)
            foreach (e; r)
                s += e;
        return s;
        });

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);

auto lazySum = z.map!"a + b";

assert(zip(sl1, sl2).map!"a + b" ==
        [[ 1,  3,  5],
         [ 7,  9, 11]]);
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 s = iota(2, 3).map!("a + a", "a * a");

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

foreach (i; 0..s.length!0)
foreach (j; 0..s.length!1)
{
    auto values = s[i, j];
    assert(values.a == sums[i][j]);
    assert(values.b == products[i][j]);
}
Examples:
You may alias map with some function(s) to a symbol and use it 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:
import std.algorithm.iteration : sum, reduce;
import mir.utility : max;
import mir.ndslice.dynamic : transposed;
/// Returns maximal column average.
auto maxAvg(S)(S matrix) {
    return reduce!max(matrix.universal.transposed.pack!1.map!sum)
         / double(matrix.length);
}
// 1 2
// 3 4
auto matrix = iota([2, 2], 1);
assert(maxAvg(matrix) == 3);
auto map(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice);
Parameters:
Slice!(kind, packs, Iterator) slice An input slice.
Returns:
a slice 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.
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!(kind, packs, Iterator) slice a slice to create a view on.
Returns:
A lazy slice with elements converted to the type T.
Examples:
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!(Contiguous, [2], int*) stringMatrix = stringMatrixView.slice;
Examples:
Special behavior for pointers to a constant data.
import mir.ndslice.allocation : slice;

Slice!(Contiguous, [2], double*)              matrix = slice!double([2, 2], 0);
Slice!(Contiguous, [2], const(double)*) const_matrix = matrix.as!(const double);
auto as(SliceKind kind, size_t[] packs, Iterator)(Slice!(kind, packs, Iterator) slice);
Slice!(kind, packs, IndexIterator!(Iterator, Field)) indexed(Field, SliceKind kind, size_t[] packs, Iterator)(Field source, Slice!(kind, packs, Iterator) indexes);
Takes a field source and a slice indexes, and creates a view of source as if its elements were reordered according to indexes. indexes 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!(kind, packs, Iterator) indexes a slice, source of indexes.
Returns:
n-dimensional slice with the same kind, shape and strides.
See Also:
indexed is similar to , but a field is used instead of a function.
Examples:
auto source = [1, 2, 3, 4, 5];
auto indexes = [4, 3, 1, 2, 0, 4].sliced;
auto ind = source.indexed(indexes);
assert(ind == [5, 4, 2, 3, 1, 5]);

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

ind[3] += 10; // for index 2
//                0  1   2  3  4
assert(source == [1, 2, 13, 4, 5]);
auto zip(bool sameStrides = false, Slices...)(Slices slices)
if (Slices.length > 1 && allSatisfy!(isSlice, Slices));
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 unzip(char name, SliceKind kind, size_t[] packs, Iterator : ZipIterator!Iterators, Iterators...)(Slice!(kind, packs, Iterator) slice);
Selects a slice from a zipped slice.
Parameters:
name name of a slice to unzip.
Slice!(kind, packs, Iterator) 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 slide(size_t params, alias fun) if (params <= 'z' - 'a' + 1)
Sliding map for vectors.
Suitable for simple convolution algorithms.
Parameters:
params windows length.
fun map functions with params arity.
See Also:
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]);
template pairwise(alias fun, size_t lag = 1)
Pairwise map for vectors.
Parameters:
fun function to accomulate
lag an integer indicating which lag to use
Returns:
lazy ndslice composed of fun(a_n, a_n+1) values.
See Also:
Examples:
assert([2, 4, 3, -1].sliced.pairwise!"a + b" == [6, 7, 2]);
template diff(size_t lag = 1)
Differences between vector elements.
Parameters:
lag an integer indicating which lag to use
Returns:
lazy differences.
See Also:
Examples:
assert([2, 4, 3, -1].sliced.diff == [2, -1, -4]);
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;

// 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;

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:
auto isMagic(S)(S matrix)
{
    import mir.math.sum;
    import mir.ndslice.algorithm: all;
    import mir.ndslice.allocation: slice;
    import mir.ndslice.dynamic: transposed, reversed;
    import mir.ndslice.topology: flattened, universal, diagonal;

    // check shape
    if (matrix.length == 0)
        return false;
    if (matrix.length!0 != matrix.length!1)
        return false;

    // checks that matrix is composed of consequent elements from interval 1:N^2.
    auto n2 = matrix.elementsCount;
    enum bc = size_t.sizeof * 8;
    auto flags = slice!size_t(n2 / bc + (n2 % bc != 0))
        .bitwise[0 .. n2];
    foreach(elem; matrix.flattened)
    {
        assert(elem > 0);
        assert(elem <= n2);
        flags[elem - 1] = true;
    }
    if (!flags.all)
        return false;

    // calculate magic number
    auto n = matrix.length;
    auto c = n * (n * n + 1) / 2;

    // each row sum should equal magic number
    foreach(row; matrix)
        if (row.sum != c)
            return false;
    // each columns sum should equal magic number
    foreach(col; matrix.universal.transposed)
        if (col.sum != c)
            return false;
    // each diagonal sum should equal magic number
    if (matrix.diagonal.sum != c)
        return false;
    if (matrix.universal.reversed!0.diagonal.sum != c)
        return false;

    return true;
}

assert(isMagic(magic(1)));
assert(!isMagic(magic(2))); // 2x2 magic square does not exist
foreach(n; 3 .. 24)
    assert(isMagic(magic(n)));
auto stairs(string type, Iterator)(Slice!(Contiguous, [1], Iterator) slice, size_t n)
if (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!(Contiguous, [1], 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.
See Also:
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(dec == [
    [0, 1, 2, 3, 4],
       [5, 6, 7, 8],
        [9, 10, 11],
           [12, 13],
               [14]]);

static assert(is(typeof(inc.front) == typeof(pck)));
static assert(is(typeof(dec.front) == typeof(pck)));
auto stairs(string type, SliceKind kind, Iterator)(Slice!(kind, [2], Iterator) 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!(kind, [2], Iterator) 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.ndslice.algorithm: 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 byDim(Dimensions...) if (Dimensions.length > 0)
Returns a slice that can be iterated by dimension. Transposes dimensions on top and then packs them.
Combines transposed and ipack.
Parameters:
dim dimension to perform iteration on
slice input slice (may not be 1-dimensional slice)
Returns:
n-dimensional slice ipacked to allow iteration by dimension
See Also:
Examples:
2-dimensional slice support
import mir.ndslice.topology : iota;
//  ------------
// | 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;
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;
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.topology : iota, universal, flattened, reshape;
import mir.ndslice.dynamic : strided, transposed;
//  ----------------
// | 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);
//->
// | 4 5 |
//->
// | 3 5 |
//->
// | 3 4 |
//->
// | 5 4 |
//->
// | 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;
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;
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;
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);
assert(a.shape == shape54);
assert(a.front.shape == shape4);
assert(a.front == iota([3, 4], 0, 5).universal.transposed!1);
a.popFront;
assert(a.front.front == iota([3], 1, 20));