Functions and types that manipulate multidimensional rectangular arrays.

Boost License 1.0.
Denis Shelomovskij

struct  MultidimArray(T, size_t n) if (n >= 1);

Implements multidimensional rectangular arrays.

Something like FORTRAN's one.

// Let's creates an GC allocated three-dimensional rectangular array from 2 matrices 3x4
auto matrices = multidimArray!int(2, 3, 4); // matrices has a type MultidimArray!(int, 3)

// Setting an element at intersection of the first column and
// the third row of the secon matrix to seven:
matrices[1, 0, 2] = 7;

// Filling the whole first column of the secon matrix with sixes:
matrices[1, 0, 0..$][] = 6;

// Filling the whole array with fives:
matrices[] = 5;

// Iterating the array
foreach(z, y, x, ref el; matrices) // using opApply
	el = cast(int) (z * 100 + y * 10 + x);

int c = 0;
foreach(ref el; matrices.byElementForward)
	el = c++;

c = 0;
foreach(i; 0 .. matrices.elements)
	matrices.byElementRandomAccess[i] = c++;

c = 0;
foreach(matrix; matrices.byTopDimension)       // for each of two matrices
	foreach(row; matrix.byTopDimension)        // for each row
		foreach(ref el; row.byTopDimension) // for each element
			el = c++;

c = 0;
foreach_reverse(ref el; matrices.byElementRandomAccess)
	el = c++;

c = 0;
foreach_reverse(i; 0 .. matrices.elements)
	matrices.byElementRandomAccess[i] = c++;

// Inexing/slicing
// * use <integer> to select a position
// * use <integer> .. <integer> to select a range
// E.g. use 0..$  to select the whole range
matrices = matrices[0..$, 0..$, 0..$];  // the entire array, same as [0..2, 0..3, 0..4]
auto array2d = matrices[0, 0..$, 0..$]; // the first matrix
auto array1d = matrices[0, 1, 0..$];  // the second row of the first matrix
array1d = matrices[0, 0..$, 1];       // the second column of the first matrix
matrices[0, 1, 1] = 9;                // setting an element at a crossing of the row an the column

// first two rows and three columns of the secon matrix
array2d = matrices[1, 0 .. 2, 0 .. 3];

alias  dimensions = n;

Dimensions of this array.

const pure nothrow @safe const(size_t)[n]  lengths();

Returns the read only view at its  lengths array.

const pure nothrow @safe size_t  elements();

Returns the  elements count of the array.

const pure nothrow @safe size_t  packedDimensions();

Returns the maximum number of tail dimensions without pading. Note, that there can be no such dimensions.

pure nothrow @property @safe auto  byElementForward();

Returns a forward range which has mutable elements and a length for iteration by an element.

pure nothrow @property @safe auto  byElementRandomAccess();

Returns a finite random-access range which has mutable elements and a length for iteration by an element.

pure nothrow @property @safe auto  byTopDimension();

Returns a finite random-access range for iteration over the top dimension.

It has mutable elements iff dimensions is 1.

@property auto  byFunction(string pred)();

Returns a forward range which has mutable elements for iteration using indices defined by pred starting from a = 0 and incrementing it while indices are in valid range.

auto matrix = multidimArray!char(30, 20);
matrix[] = ' ';

foreach(ref el; matrix.byFunction!`a, a`) // fills a diagonal
	el = 'X';

foreach(ref el; matrix.byFunction!`a^^2 / 5, a`()) // fills a parabola points
	el = 'Y';

import std.stdio;

int  opApply(int delegate(RepeatTuple!(n, size_t), ref T) dg);

Implements by-element iteration with inidces starting from the top dimension.

auto matrix = multidimArray!int(2, 3, 4);
foreach(z, y, x, ref el; matrices)
	el = z * 100 + y * 10 + x;

MultidimArray  opSliceAssign(T value);
MultidimArray  opSliceAssign(Range)(Range value) if (isInputRange!Range && isAssignable!(T, ElementType!Range));
MultidimArray  opSliceAssign(U)(MultidimArray!(U, n) value) if (isAssignable!(T, U));

Implements elements initialisation with a value, where value can be of type T or an input range which front can be assigned to an element. The range should contain exectly elements elements, otherwise an Exception will be thrown.

If value is of type T or a forward range, returns value. Otherwise (value is an input range but not a forward range) returns void.
auto a23 = multidimArray!int(2, 3);
auto a46 = multidimArray!int(4, 6);
auto a234 = multidimArray!int(2, 3, 4);

a23[] = a234[] = 7;
a23[] = take(a46[] = a234[] = iota(24), 6);

@property auto  dup();
@property auto  idup();

Support for  dup and idup properties for MultidimArray.

inout pure nothrow ref @safe auto  opIndex(in size_t[n] indices...);
inout pure nothrow @trusted auto  opIndex(A...)(A args) if (args.length == n && allTuple!(isROrSize, A) && RCount!A);
auto  opIndexAssign(U, A...)(U value, A args) if (args.length == n && allTuple!(isROrSize, A));


A parameter can be:

type meaning effect on a resulting dimensions
n a position -1
m .. n a range 0

See MultidimArray examples.
Known Bugs
A bit ugly syntax is used because dmd hasn't support for a better one yet (see D Bugzilla 6798).

pure @safe auto  reorderIndices(in size_t[n] newOrder...);

Creates a slice of this entire array with reordered indices. newOrder[i] = n means that i-th index of a resulting array will behave like n-th index of the original array. Every index sould be used once, otherwise an Exception will be thrown.

auto matrix3x4 = multidimArray!int(3, 4);
auto transposed = matrix3x4.reorderIndices(1, 0);
assert(transposed.lengths == [4, 3]);
assert(&matrix3x4[2, 3] == &transposed[3, 2]);
auto a = multidimArray!int(2, 3, 4);
auto b = a.reorderIndices(2, 0, 1);
assert(b.lengths == [4, 2, 3]);
assert(&a[1, 2, 3] == &b[3, 1, 2]);

string  toString();

Conversion to string function for debugging purposes.

Implemented for dimensions <= 3.

pure nothrow @safe auto  multidimArray(T, size_t n)(size_t[n] lengths...) if (n > 0);
pure nothrow @safe auto  multidimArray(size_t n, T)(T[] data, size_t[n] lengths...) if (n > 0);
pure nothrow auto  multidimArray(size_t n, A)(ref A array) if (n > 0 && n <= staticArrayDims!A);
pure nothrow auto  multidimArray(A)(ref A array) if (isStaticArray!A);
pure nothrow @safe auto  multidimArray(size_t n, A)(A array) if (isDynamicArray!A && n > 0 && n - 1 <= staticArrayDims!(ElementType!A));
pure nothrow @safe auto  multidimArray(A)(A array) if (isDynamicArray!A);

Convenience function that returns an MultidimArray!(T, n) object.

The first overload returns a MultidimArray with a newly allocated data Others use an existing storage.
data A memory storage for a resulting array of type T[].
array An array to wrap. It can be a multidimensional static array or a slice of it (has a dynamic top dimension).
size_t[n] lengths Lengths of a resulting array.
Template parameters:
T Element type of a resulting array. Should be explicitly defined only for the first overload which has no memory storage.

n Dimensions of a resulting array. Can be explicitly defined to use only first n of array dimensions.

A Type of a wrapping array. It is inferred from the array argument and should not be explicitly defined.
See Also
The first overload throws an RangeError in debug build if data length isn't equal to lengths prouct.
// Let's create an GC allocated three-dimensional rectangular array from 2 matrices 3x4
auto matrix1 = multidimArray!int(2, 3, 4);

// Let's create the same array using an existing storage
auto darr2 = new int[24]; // At least 24 elements are needed
auto matrix2 = multidimArray(darr2, 2, 3, 4); // No need for explicit element type declaration

// Let's create the same array using an existing static array as data storage
int[4][3][2] sarr3; // or in a postfix form: int sarr[2][3][4];
auto matrix3 = multidimArray(sarr3); // No need for any explicit template declarations

// The head array can be dynamic
int[4][3][] darr3 = sarr3[];
auto matrix31 = multidimArray(darr3); // Works like previous one

// Let's create an array of static arrays
ubyte[4][4][3][2] sarr4; // a postfix form: ubyte[4] sarr[2][3][4];
auto matrix4 = multidimArray!3(sarr4); // Only 3 major of 4 dimensions are indeces

// The head array can also be dynamic
auto matrix41 = multidimArray!3(sarr4[]); // Works like previous one