diff --git a/doc/Makefile b/doc/Makefile index 25fd9a1a..b33f8c83 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -29,7 +29,7 @@ ARTWORK_DIR=$(DOC_SOURCE_DIR)/artwork MIR_PACKAGES = mir mir/ndslice PACKAGE_mir = conv functional primitives utility -PACKAGE_mir_ndslice = package algorithm allocation dynamic field iterator package slice sorting concatenation topology +PACKAGE_mir_ndslice = package algorithm allocation dynamic field ndfield iterator package slice sorting concatenation topology MOD_EXCLUDES=$(addprefix --ex=, mir.ndslice.internal) diff --git a/index.d b/index.d index 4ad7bea4..6c1ecfb7 100644 --- a/index.d +++ b/index.d @@ -51,6 +51,10 @@ $(BOOKTABLE , $(TDNW $(MREF mir,ndslice,field)) $(TD Field declarations) ) + $(TR + $(TDNW $(MREF mir,ndslice,ndfield)) + $(TD NdField declarations) + ) $(LEADINGROW Accessories) $(TR $(TDNW $(MREF mir,conv)) diff --git a/ndslice.graffle b/ndslice.graffle index 6394f145..f0c5ad26 100644 Binary files a/ndslice.graffle and b/ndslice.graffle differ diff --git a/ndslice.svg b/ndslice.svg index cd0d9c23..725f0c7c 100644 --- a/ndslice.svg +++ b/ndslice.svg @@ -191,6 +191,9 @@ + + + @@ -831,36 +834,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1145,41 +1148,41 @@ - + - - - + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + - - - - - - + + + + + + @@ -1290,124 +1293,124 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1816,120 +1819,120 @@ - + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1969,40 +1972,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2029,37 +2032,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3675,195 +3678,195 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4395,7 +4398,7 @@ - + @@ -4902,5 +4905,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/mir/ndslice/algorithm.d b/source/mir/ndslice/algorithm.d index 21239b9d..c620f3ac 100644 --- a/source/mir/ndslice/algorithm.d +++ b/source/mir/ndslice/algorithm.d @@ -123,7 +123,6 @@ template reduce(alias fun) } else alias reduce = .reduce!(naryFun!fun); - } /// Single slice diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d index 16dda651..28398fc8 100644 --- a/source/mir/ndslice/field.d +++ b/source/mir/ndslice/field.d @@ -12,6 +12,7 @@ $(T2 BitwiseField, $(SUBREF topology, bitwise)) $(T2 MapField, $(SUBREF topology, map)) $(T2 ndIotaField, $(SUBREF topology, ndiota)) $(T2 RepeatField, $(SUBREF topology, repeat)) +$(T2 LinspaceField, $(SUBREF topology, linspace)) ) @@ -52,10 +53,25 @@ struct MapField(Field, alias fun) +/ static alias __map(alias fun1) = MapField__map!(Field, fun, fun1); - auto ref opIndex()(ptrdiff_t index) + auto ref opIndex(T...)(T index) { return fun(_field[index]); } + + auto length()() @property + { + return _field.length; + } + + auto shape()() @property + { + return _field.shape; + } + + auto elemenstCount()() @property + { + return _field.elemenstCount; + } } /++ @@ -250,3 +266,43 @@ struct ndIotaField(size_t N) return indexes; } } + +/++ +`LinspaceField` is used by $(SUBREF topology, linspace). ++/ +struct LinspaceField(T) +{ + /// + size_t _length; + + /// + T start, stop; + + // no fastmath + /// + T opIndex()(size_t index) + { + auto d = _length - 1; + auto v = typeof(T.init.re)(d - index); + auto w = typeof(T.init.re)(index); + v /= d; + w /= d; + auto a = v * start; + auto b = w * stop; + return a + b; + } + +@fastmath: + + size_t length()() @property + { + return _length; + } + + size_t[1] shape()() @property + { + size_t[1] ret = void; + ret[0] = _length; + return ret; + } +} diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d index 66753f76..c418e288 100644 --- a/source/mir/ndslice/internal.d +++ b/source/mir/ndslice/internal.d @@ -8,6 +8,24 @@ import mir.internal.utility; @fastmath: +private template _prod(size_t len) + if (len) +{ + static if (len == 1) + enum _prod = "elems[0]"; + else + { + enum i = len - 1; + enum _prod = ._prod!i ~ " * elems[" ~ i.stringof ~ "]"; + } +} + +auto product(Elems...)(auto ref Elems elems) +{ + return mixin(_prod!(Elems.length)); +} + + template _iotaArgs(size_t length, string prefix, string suffix) { static if (length) diff --git a/source/mir/ndslice/ndfield.d b/source/mir/ndslice/ndfield.d new file mode 100644 index 00000000..63dda473 --- /dev/null +++ b/source/mir/ndslice/ndfield.d @@ -0,0 +1,232 @@ +/++ +This is a submodule of $(MREF mir,ndslice). + +NdField is a type with `opIndex(size_t[N] index...)` primitive. +An ndslice can be created on top of a ndField using $(SUBREF slice, slicedNdField). + +$(BOOKTABLE $(H2 NdFields), +$(TR $(TH NdField Name) $(TH Used By)) +$(T2 Cartesian, $(SUBREF topology, cartesian)) +$(T2 Kronecker, $(SUBREF topology, kronecker)) +) + +See_also: $(SUBREF concatenation, concatenation). + +License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). +Copyright: Copyright © 2016-, Ilya Yaroshenko +Authors: Ilya Yaroshenko + +Macros: +SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) +T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) ++/ +module mir.ndslice.ndfield; + +import mir.internal.utility; +import mir.ndslice.internal; +import mir.ndslice.slice; +import mir.primitives; +import std.meta; + +private template _indexes(NdFields...) +{ + static if (NdFields.length == 0) + enum _indexes = ""; + else + { + alias Next = NdFields[0 .. $ - 1]; + enum i = Next.length; + enum _indexes = ._indexes!Next ~ + "_fields[" ~ i.stringof ~ "][" ~ _indexes_range!([staticMap!(DimensionCount, Next)].sum, DimensionCount!(NdFields[$ - 1])) ~ "], "; + } +} + +private template _indexes_range(size_t begin, size_t count) +{ + static if (count == 0) + enum _indexes_range = ""; + else + { + enum next = count - 1; + enum elem = begin + next; + enum _indexes_range = ._indexes_range!(begin, next) ~ "indexes[" ~ elem.stringof ~ "], "; + } +} + +/// +struct Cartesian(NdFields...) + if (NdFields.length > 1) +{ + /// + NdFields _fields; + + package enum size_t M(size_t f) = [staticMap!(DimensionCount, NdFields[0..f])].sum; + package enum size_t N = M!(NdFields.length); + + /// + size_t length(size_t d = 0)() @property + { + foreach(f, ref field; _fields) + static if (M!(f + 1) > d) + return field.length!(d - M!f); + } + + /// + size_t[N] shape()() @property + { + typeof(return) ret = void; + foreach(f, ref field; _fields) + { + static if (hasShape!(NdFields[f])) + { + auto s = field.shape; + foreach(j; Iota!(s.length)) + ret[M!f + j] = s[j]; + } + else + { + ret[M!f] = field.length; + } + } + return ret; + } + + /// + size_t elementsCount()() @property + { + size_t ret = 1; + foreach (ref field; _fields) + ret *= field.elementsCount; + ret; + } + + /// + auto opIndex()(size_t[N] indexes...) + { + import mir.functional : tuple; + return mixin("tuple(" ~ _indexes!(NdFields) ~ ")"); + } +} + +private template _kr_indexes(size_t n) +{ + static if (n == 0) + enum _kr_indexes = ""; + else + { + enum i = n - 1; + enum _kr_indexes = ._kr_indexes!i ~ "_fields[" ~ i.stringof ~ "][ind[" ~ i.stringof ~ "]], "; + } +} + +/// +struct Kronecker(alias fun, NdFields...) + if (NdFields.length > 1 && allSatisfy!(templateOr!(hasShape, hasLength), NdFields[1 .. $])) +{ + /// + NdFields _fields; + + private enum N = DimensionCount!(NdFields[$-1]); + + /// + size_t length(size_t d = 0)() @property + { + static if (d == 0) + { + size_t ret = _fields[0].length; + foreach(ref field; _fields[1 .. $]) + ret *= field.length; + } + else + { + size_t ret = _fields[0].length!d; + foreach(ref field; _fields[1 .. $]) + ret *= field.length!d; + } + return ret; + } + + + /// + size_t[N] shape()() @property + { + static if (N > 1) + { + size_t[N] ret = _fields[0].shape; + foreach(ref field; _fields[1 .. $]) + { + auto s = field.shape; + foreach(i; Iota!N) + ret[i] *= s[i]; + } + return ret; + } + else + { + size_t[1] ret = void; + ret[0] = _fields[0].length; + foreach(ref field; _fields[1 .. $]) + ret[0] *= field.length; + return ret; + } + } + + /// + size_t elementsCount()() @property + { + size_t ret = 1; + foreach (ref field; _fields) + ret *= field.elementsCount; + ret; + } + + /// + auto opIndex()(size_t[N] indexes...) + { + static if (N > 1) + size_t[N][NdFields.length] ind = void; + else + size_t[NdFields.length] ind = void; + foreach_reverse (f, ref field; _fields) + { + static if (f) + { + static if (hasShape!(NdFields[f])) + { + auto s = field.shape; + } + else + { + size_t[1] s = void; + s[0] = field.length; + } + static if (N > 1) + { + foreach(i; Iota!N) + { + ind[f][i] = indexes[i] % s[i]; + indexes[i] /= s[i]; + } + } + else + { + ind[f] = indexes[0] % s[0]; + indexes[0] /= s[0]; + } + } + else + { + static if (N > 1) + { + foreach(i; Iota!N) + ind[f][i] = indexes[i]; + } + else + { + ind[f] = indexes[0]; + } + } + } + return mixin("fun(" ~ _kr_indexes!(ind.length) ~ ")"); + } +} diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d index 1d6c613d..d040810d 100644 --- a/source/mir/ndslice/package.d +++ b/source/mir/ndslice/package.d @@ -123,12 +123,15 @@ $(TR $(TDNW $(SUBMODULE topology) $(BR) $(SUBREF topology, bitwise) $(SUBREF topology, blocks) $(SUBREF topology, canonical) + $(SUBREF topology, cartesian) $(SUBREF topology, diagonal) $(SUBREF topology, evertPack) $(SUBREF topology, flattened) $(SUBREF topology, indexed) $(SUBREF topology, iota) $(SUBREF topology, ipack) + $(SUBREF topology, kronecker) + $(SUBREF topology, linspace) $(SUBREF topology, map) $(SUBREF topology, ndiota) $(SUBREF topology, pack) @@ -223,6 +226,14 @@ $(TR $(TDNW $(SUBMODULE field) ) ) +$(TR $(TDNW $(SUBMODULE ndfield) + $(BR) $(SMALL Declarations)) + $(TD + $(SUBREF ndfield, Cartesian) + $(SUBREF ndfield, Kronecker) + ) +) + )) $(H2 Example: Image Processing) diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d index 319cf5e3..37ada589 100644 --- a/source/mir/ndslice/topology.d +++ b/source/mir/ndslice/topology.d @@ -11,6 +11,24 @@ $(T2 assumeContiguous, Converts a slice to contiguous $(SUBREF slice, SliceKind) ) +$(BOOKTABLE $(H2 Sequence Selectors), +$(TR $(TH Function Name) $(TH Description)) + +$(T2 repeat, Slice with identical values) +$(T2 iota, Contiguous Slice with initial flattened (contiguous) index.) +$(T2 ndiota, Contiguous Slice with initial multidimensional index.) +$(T2 linspace, Evenly spaced numbers over a specified interval.) + +) + +$(BOOKTABLE $(H2 Products), +$(TR $(TH Function Name) $(TH Description)) + +$(T2 cartesian, Cartesian product.) +$(T2 kronecker, Kronecker product.) + +) + $(BOOKTABLE $(H2 Representation Selectors), $(TR $(TH Function Name) $(TH Description)) @@ -19,9 +37,7 @@ where each element of the original slice is converted to a type `T`.) $(T2 bitpack, Bitpack slice over an unsigned integral slice.) $(T2 bitwise, Bitwise slice over an unsigned integral slice.) $(T2 flattened, Contiguous 1-dimensional slice of all elements of a slice.) -$(T2 iota, Contiguous Slice with initial flattened (contiguous) index.) $(T2 map, Multidimensional functional map.) -$(T2 ndiota, Contiguous Slice with initial multidimensional index.) $(T2 retro, Reverses order of iteration for all dimensions) $(T2 stride, Strides 1-dimensional slice) $(T2 zip, Zips slices into a slice of tuples.) @@ -29,18 +45,17 @@ $(T2 unzip, Selects a slice from a zipped slice.) ) + $(BOOKTABLE $(H2 Shape Selectors), $(TR $(TH Function Name) $(TH Description)) $(T2 blocks, n-dimensional slice composed of n-dimensional non-overlapping blocks. If the slice has two dimensions, it is a block matrix.) $(T2 diagonal, 1-dimensional slice composed of diagonal elements) -$(T2 repeat, Slice with identical values) $(T2 reshape, New slice with changed dimensions for the same data) $(T2 windows, n-dimensional slice of n-dimensional overlapping windows. If the slice has two dimensions, it is a sliding window.) ) - $(BOOKTABLE $(H2 Subspace Selectors), $(TR $(TH Function Name) $(TH Description)) @@ -77,9 +92,11 @@ import std.meta; import mir.internal.utility; import mir.ndslice.field; +import mir.ndslice.ndfield; import mir.ndslice.internal; import mir.ndslice.iterator; import mir.ndslice.slice; +import mir.primitives; @fastmath: @@ -1630,7 +1647,6 @@ unittest /++ Returns a slice, the elements of which are equal to the initial multidimensional index value. -This is multidimensional analog of $(REF iota, std, range). For a flattened (contiguous) index, see $(LREF iota). Params: @@ -1681,6 +1697,74 @@ unittest static assert(isRandomAccessRange!(typeof(r))); } +/++ +Evenly spaced numbers over a specified interval. + +Params: + T = floating point or complex numbers type + lengths = list of dimension lengths. Each length must be greater then 1. + internvals = list of [start, end] pairs. +Returns: + `n`-dimensional grid of evenly spaced numbers over specified intervals. +See_also: $(LREF) ++/ +auto linspace(T, size_t N)(size_t[N] lengths, T[2][N] intervals...) + if (N && (isFloatingPoint!T || isComplex!T)) +{ + Repeat!(N, LinspaceField!T) fields = void; + foreach(i; Iota!N) + { + assert(lengths[i] > 1, "linspace: all lengths must be greater then 1."); + fields[i] = LinspaceField!T(lengths[i], intervals[i][0], intervals[i][1]); + } + static if (N == 1) + return slicedField(fields); + else + return cartesian(fields); +} + +/// 1D +unittest +{ + 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]); +} + +/// 2D +unittest +{ + import mir.functional: tuple; + + auto s = linspace!double([5, 3], [1.0, 2.0], [0.0, 1.0]); + + assert(s == [ + [tuple(1.00, 0.00), tuple(1.00, 0.5), tuple(1.00, 1.0)], + [tuple(1.25, 0.00), tuple(1.25, 0.5), tuple(1.25, 1.0)], + [tuple(1.50, 0.00), tuple(1.50, 0.5), tuple(1.50, 1.0)], + [tuple(1.75, 0.00), tuple(1.75, 0.5), tuple(1.75, 1.0)], + [tuple(2.00, 0.00), tuple(2.00, 0.5), tuple(2.00, 1.0)], + ]); + + assert(s.map!"a.a * 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], + ]); +} + +/// Complex numbers +unittest +{ + auto s = linspace!cdouble([3], [1.0 + 0i, 2.0 + 4i]); + assert(s == [1.0 + 0i, 1.5 + 2i, 2.0 + 4i]); +} + /++ Returns a slice with identical elements. `RepeatSlice` stores only single value. @@ -1688,7 +1772,6 @@ Params: lengths = list of dimension lengths Returns: `n`-dimensional slice composed of identical values, where `n` is dimension count. -See_also: $(REF repeat, std,range) +/ Slice!(Contiguous, [M], FieldIterator!(RepeatField!T)) repeat(T, size_t M)(T value, size_t[M] lengths...) @@ -2456,3 +2539,178 @@ pure nothrow unittest assert(m.unzip!'a' == alpha); assert(m.unzip!'b' == beta); } + +private enum TotalDim(NdFields...) = [staticMap!(DimensionCount, NdFields)].sum; + +/++ +Cartesian product. + +Constructs lazy cartesian product $(SUBREF slice, Slice) without memory allocation. + +Params: + fields = list of fields with lengths or ndFields with shapes +Returns: $(SUBREF ndfield, Cartesian)`!NdFields(fields).`$(SUBREF slice, slicedNdField)`;` ++/ +auto cartesian(NdFields...)(NdFields fields) + if (NdFields.length > 1 && allSatisfy!(templateOr!(hasShape, hasLength), NdFields)) +{ + return Cartesian!NdFields(fields).slicedNdField; +} + +/// 1D x 1D +unittest +{ + auto a = [10, 20, 30]; + auto b = [ 1, 2, 3]; + + auto c = cartesian(a, b) + .map!"a.a + a.b"; + + assert(c == [ + [11, 12, 13], + [21, 22, 23], + [31, 32, 33]]); +} + +/// 1D x 2D +unittest +{ + auto a = [10, 20, 30]; + auto b = iota([2, 3], 1); + + auto c = cartesian(a, b) + .map!"a.a + 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], + ]]); +} + +/// 1D x 1D x 1D +unittest +{ + auto u = [100, 200]; + auto v = [10, 20, 30]; + auto w = [1, 2]; + + auto c = cartesian(u, v, w) + .map!"a.a + a.b + a.c"; + + assert(c.shape == [2, 3, 2]); + + assert(c == [ + [ + [111, 112], + [121, 122], + [131, 132], + ], + [ + [211, 212], + [221, 222], + [231, 232], + ]]); +} + + + +/++ +$(LINK2 https://en.wikipedia.org/wiki/Kronecker_product, Kronecker product). + +Constructs lazy kronecker product $(SUBREF slice, Slice) without memory allocation. ++/ +template kronecker(alias fun = product) +{ + import mir.functional: naryFun; + static if (__traits(isSame, naryFun!fun, fun)) + + /++ + Params: + fields = list of either fields with lengths or ndFields with shapes. + All ndFields must have the same dimension count. + Returns: + $(SUBREF ndfield, Kronecker)`!(fun, NdFields)(fields).`$(SUBREF slice, slicedNdField) + +/ + @fastmath auto kronecker(NdFields...)(NdFields fields) + if (allSatisfy!(hasShape, NdFields) || allSatisfy!(hasLength, NdFields)) + { + return Kronecker!(fun, NdFields)(fields).slicedNdField; + } + else + alias kronecker = .kronecker!(naryFun!fun); +} + +/// 2D +unittest +{ + 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]]); +} + +/// 1D +unittest +{ + auto a = iota([3], 1); + + auto b = [ 1, -1]; + + auto c = kronecker(a, b); + + assert(c == [1, -1, 2, -2, 3, -3]); +} + +/// 2D with 3 arguments +unittest +{ + 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]]); +} diff --git a/source/mir/primitives.d b/source/mir/primitives.d index 638b00b8..22d1367c 100644 --- a/source/mir/primitives.d +++ b/source/mir/primitives.d @@ -3,6 +3,10 @@ Templates used to check primitives. +/ module mir.primitives; +import mir.ndslice.internal; + +@fastmath: + /++ Returns: `true` if `R` has a `length` member that returns an integral type implicitly convertible to `size_t`. @@ -53,3 +57,23 @@ enum bool hasShape(R) = is(typeof( static assert(hasLength!(B)); static assert(hasLength!(C)); } + +/// +template DimensionCount(T) + if (hasShape!T || hasLength!T) +{ + static if (hasShape!T) + enum size_t DimensionCount = typeof(T.shape).length; + else + enum size_t DimensionCount = 1; +} + +/// +size_t length(size_t d : 0, T)(T[] array) +{ + pragma(inline, true); + return array.length; +} + +/// +alias elementsCount = length;