mirror of
https://github.com/dlang/phobos.git
synced 2025-05-05 01:20:40 +03:00

fix Slice change concept implement `sliced` change structure remove comment slice constructors add 1D case fix opSlice 1D implement ND, part1 update to modern syntax generic opIndex fix protection cleanup fix cleanup style fix rename update update implement transpose unittest unittest for properties and methods more examples & fixes move code minor fix add header update make fils update make files dmd bug workaround fix module name update imports style fix fix asserts remove createRefCountedSlice add shape property rework `shape`, add `structrure` ndarray swapped transposed opCast remove save calls everted make Slice(size_t N0, Slice(size_t N1, ...)) virtual huge rework remove space move code fix style add packEverted relax constraints add black comments remove spaces fix macros fix doc fix docs update docs reduce template bloat optimize and fix for arrays. update docs remove space update docs update docs update link update doc fix constructor add toString fix `save` add `reversed` method fix constraints for `reversed` optimisation in unittests add `strided` implement `reversed` for ranges (no arrays) reduce constraints cleanup add Iota for static foreach remove string representation DMD 2.067 support fix style fix opIndexUnary constraints move byElement add shift property add CTFE-able strided huge style fix add macros update `sliced` move private code update docs update allIndexesReversed, renamed to allReversed update docs update docs fix macros fix posix.mak update posix.mak update docs and strided move code update sliced constraints update `sliced` docs move code remove whitespaces add static assert to ndslice add one more opIndexUnary update createSlice docs update ndarray update docs for Transpose operators update docs and fix bugs add pragma inline fix docs update docs update docs add inline pragma ditto replace errors with asserts update docs update doc style update docs remove comes in docs update docs remove whitespaces update docs update docs update comment update test update docs fix typo fix docs review fix transposed description change doc tables remove unused RepeatTypeTuple remove function attributes for templates make constructor private make N and Range private make elementsCount public fix createSlice params naming add assert description to sliced [big] fix range primitives wrong asserts and update documentation regroup primitives minor docs fix minor docs fix fix typo fix Slice constraints add indexing using static arrays make byElement a random access range fix by random access primitives for ByElement update unittest fix random access primitives for ByElement remove trailing space implement slicing for ByElement make ByElement more readable update docs for subspace operators remove one See_also revert last commit update docs add descriptions to asserts add more examples minor doc update add example with allocators add makeSlice for allocators update docs table add range checks add more constructors Add description to asserts. add checks for const/immutable ditto update to DMD 2.069 minor fixes add elements property make makeSlice an unittest remove space update docs remove space update docs update doc fix makeSlice example fix strided make strided template variadic add Guide for Slice/Matrix/BLAS contributers remove unused import add better error messages update guide update docs remove space [minor] fix docs minor error messages update minor doc fix rename package split package update posix.mak update win*.mak ditto fix posix mak update *mak. update docs update man files minor doc update update module headers fix opCast rename pop*N to pop*Exactly remove primitives add popFrontN popBackN to Slice [minor] update docs add package primitives to Slice update operators prototypes add drop* primitives [minor] update docs update docs remove spaces remove allocators minor doc fix [minor] update docs add dropToNCube add diagonal add return type for template for better documentation move pack* to iterators rm allocators [minor] doc update add support of packed slice for diagonal update example for diagonal add blocks rename packed and unpacked to pack and unpack update docs renaming [minor] docs update ditto minor style update rm old files [minor] update docs update docs ditto [minor] update docs add rotated [minor] update docs add byElementInStandardSimplex add windows remove space remove structure update docs add spaces add reshape rename packEverted -> evertPack remove spaces fix ReshapeException minor doc fix update windows/blocks template params fix pack windows/blocks add index @property remove spaces minor doc fix add Slice tuples remove spaces update docs update docs and rename dropToHypercube update docs minor doc update fix links remove version OS constraints for allocators assumeSameStructure fix minor doc fix minor doc update after review minor style fix fix Elaborate Assign add index slice fix NSeq fix bug with diagonal fix sliced slice add main example update docs translation fix comment fix style remove spaces update style fix link Vandermonde matrix remove space move example remove `opCast` add opEquals for arrays update opIndex(Op)Assign update docs update docs fix style update docs (russian will be translated) update tests fix doc style ditto ditto update docs update docs update docs update docs update unittests update docs ditto ditto ditto [major] doc update update docs update docs fix voting conditions (docs, style) minor doc update fix style add unittest for `map` ditto fix string mixins add Params and Returns ditto add headers add descriptions Fix m32 mode in example Minor description fix fix spaces ditto Add Internal Binary Representation ditto ditto ditto ditto ditto ditto ditto add description for binary representation ditto minor style fix ditto ditto ditto ditto ditto ditto inlining remove compiler version check fix braces fix docs add Quick_Start Fix English Add two examples fix style remove object file minor doc update ditto remove spaces fix indexing & add unittests ditto
1524 lines
41 KiB
D
1524 lines
41 KiB
D
/**
|
|
$(SCRIPT inhibitQuickIndex = 1;)
|
|
|
|
This is a submodule of $(LINK2 std_experimental_ndslice.html, std.experimental.ndslice).
|
|
|
|
Selectors create new views and iteration patterns over the same data, without copying.
|
|
|
|
$(H2 Subspace selectors)
|
|
|
|
Subspace selectors serve to generalize and combine other selectors easily.
|
|
For a slice of `Slice!(N, Range)` type `slice.pack!K` creates a slice of
|
|
slices of `Slice!(N-K, Slice!(K+1, Range))` type by packing
|
|
the last `K` dimensions of the top dimension pack,
|
|
and the type of element of `slice.byElement` is `Slice!(K, Range)`.
|
|
Another way to use $(LREF pack) is transposition of dimension packs using
|
|
$(LREF evertPack). Examples of use of subspace selectors are available for selectors,
|
|
$(SUBREF slice, Slice.shape), and $(SUBREF slice, Slice.elementsCount).
|
|
|
|
$(BOOKTABLE ,
|
|
|
|
$(TR $(TH Function Name) $(TH Description))
|
|
$(T2 pack , returns slice of slices)
|
|
$(T2 unpack , merges all dimension packs)
|
|
$(T2 evertPack, reverses dimension packs)
|
|
)
|
|
|
|
$(BOOKTABLE $(H2 Selectors),
|
|
|
|
$(TR $(TH Function Name) $(TH Description))
|
|
$(T2 byElement, a random access range of all elements with `index` property)
|
|
$(T2 byElementInStandardSimplex, an input range of all elements in standard simplex of hypercube with `index` property.
|
|
If the slice has two dimensions, it is a range of all elements of upper left triangular matrix.)
|
|
$(T2 indexSlice, returns a slice with elements equal to the initial index)
|
|
$(T2 reshape, returns a new slice for the same data)
|
|
$(T2 diagonal, 1-dimensional slice composed of diagonal elements)
|
|
$(T2 blocks, n-dimensional slice composed of n-dimensional non-overlapping blocks.
|
|
If the slice has two dimensions, it is a block matrix.)
|
|
$(T2 windows, n-dimensional slice of n-dimensional overlapping windows.
|
|
If the slice has two dimensions, it is a sliding window.)
|
|
)
|
|
|
|
License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
|
|
Authors: Ilya Yaroshenko
|
|
|
|
Source: $(PHOBOSSRC std/_experimental/_ndslice/_selection.d)
|
|
|
|
Macros:
|
|
SUBMODULE = $(LINK2 std_experimental_ndslice_$1.html, std.experimental.ndslice.$1)
|
|
SUBREF = $(LINK2 std_experimental_ndslice_$1.html#.$2, $(TT $2))$(NBSP)
|
|
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|
|
T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
|
|
*/
|
|
module std.experimental.ndslice.selection;
|
|
|
|
import std.traits;
|
|
import std.meta; //: allSatisfy;
|
|
|
|
import std.experimental.ndslice.internal;
|
|
import std.experimental.ndslice.slice; //: Slice;
|
|
|
|
/++
|
|
Creates a packed slice, i.e. slice of slices.
|
|
The function does not carry out any calculations, it simply returns the same
|
|
binary data presented differently.
|
|
|
|
Params:
|
|
K = sizes of dimension packs
|
|
Returns:
|
|
`pack!K` returns `Slice!(N-K, Slice!(K+1, Range))`;
|
|
`slice.pack!(K1, K2, ..., Kn)` is the same as `slice.pacKed!K1.pacKed!K2. ... pacKed!Kn`.
|
|
+/
|
|
template pack(K...)
|
|
{
|
|
auto pack(size_t N, Range)(auto ref Slice!(N, Range) slice)
|
|
{
|
|
template Template(size_t NInner, Range, R...)
|
|
{
|
|
static if (R.length > 0)
|
|
{
|
|
static if (NInner > R[0])
|
|
alias Template = Template!(NInner - R[0], Slice!(R[0] + 1, Range), R[1 .. $]);
|
|
else
|
|
static assert(0,
|
|
"Sum of all lengths of packs " ~ K.stringof
|
|
~ " should be less than N = "~ N.stringof
|
|
~ tailErrorMessage!());
|
|
}
|
|
else
|
|
{
|
|
alias Template = Slice!(NInner, Range);
|
|
}
|
|
}
|
|
with (slice) return Template!(N, Range, K)(_lengths, _strides, _ptr);
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range.primitives: ElementType;
|
|
import std.range: iota;
|
|
import std.algorithm.comparison: equal;
|
|
auto r = 100000000.iota;
|
|
auto a = r.sliced(3, 4, 5, 6, 7, 8, 9, 10, 11);
|
|
auto b = a.pack!(2, 3); // same as `a.pack!2.pack!3`
|
|
auto c = b[1, 2, 3, 4];
|
|
auto d = c[5, 6, 7];
|
|
auto e = d[8, 9];
|
|
auto g = a[1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
assert(e == g);
|
|
assert(a == b);
|
|
assert(c == a[1, 2, 3, 4]);
|
|
alias R = typeof(r);
|
|
static assert(is(typeof(b) == typeof(a.pack!2.pack!3)));
|
|
static assert(is(typeof(b) == Slice!(4, Slice!(4, Slice!(3, R)))));
|
|
static assert(is(typeof(c) == Slice!(3, Slice!(3, R))));
|
|
static assert(is(typeof(d) == Slice!(2, R)));
|
|
static assert(is(typeof(e) == ElementType!R));
|
|
}
|
|
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.selection;
|
|
import std.range: iota;
|
|
auto r = 100000000.iota;
|
|
auto a = r.sliced(3, 4, 5, 6, 7, 8, 9, 10, 11);
|
|
auto b = a.pack!(2, 3);
|
|
static assert(b.shape.length == 4);
|
|
static assert(b.structure.lengths.length == 4);
|
|
static assert(b.structure.strides.length == 4);
|
|
static assert(b
|
|
.byElement.front
|
|
.shape.length == 3);
|
|
static assert(b
|
|
.byElement.front
|
|
.byElement.front
|
|
.shape.length == 2);
|
|
}
|
|
|
|
/++
|
|
Unpacks a packed slice.
|
|
|
|
The function does not carry out any calculations, it simply returns the same
|
|
binary data presented differently.
|
|
|
|
Params:
|
|
slice = packed slice
|
|
Returns:
|
|
unpacked slice
|
|
|
|
See_also: $(LREF pack), $(LREF evertPack)
|
|
+/
|
|
Slice!(N, Range).PureThis unpack(size_t N, Range)(auto ref Slice!(N, Range) slice)
|
|
{
|
|
with (slice) return PureThis(_lengths, _strides, _ptr);
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
import std.algorithm.comparison: equal;
|
|
auto r = 100000000.iota;
|
|
auto a = r.sliced(3, 4, 5, 6, 7, 8, 9, 10, 11);
|
|
auto b = a.pack!(2, 3).unpack();
|
|
static assert(is(typeof(a) == typeof(b)));
|
|
assert(a == b);
|
|
}
|
|
|
|
/++
|
|
Reverses the order of dimension packs.
|
|
This function is used in a functional pipeline with other selectors.
|
|
|
|
Params:
|
|
slice = packed slice
|
|
Returns:
|
|
packed slice
|
|
|
|
See_also: $(LREF pack), $(LREF unpack)
|
|
+/
|
|
SliceFromSeq!(Slice!(N, Range).PureRange, NSeqEvert!(Slice!(N, Range).NSeq))
|
|
evertPack(size_t N, Range)(auto ref Slice!(N, Range) slice)
|
|
{
|
|
mixin _DefineRet;
|
|
static assert(Ret.NSeq.length > 0);
|
|
with (slice)
|
|
{
|
|
alias C = Snowball!(Parts!NSeq);
|
|
alias D = Reverse!(Snowball!(Reverse!(Parts!NSeq)));
|
|
foreach (i, _; NSeq)
|
|
{
|
|
foreach (j; Iota!(0, C[i + 1] - C[i]))
|
|
{
|
|
ret._lengths[j + D[i + 1]] = _lengths[j + C[i]];
|
|
ret._strides[j + D[i + 1]] = _strides[j + C[i]];
|
|
}
|
|
}
|
|
ret._ptr = _ptr;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: transposed;
|
|
import std.range: iota;
|
|
auto slice = 100000000.iota.sliced(3, 4, 5, 6, 7, 8, 9, 10, 11);
|
|
assert(slice
|
|
.pack!2
|
|
.evertPack
|
|
.unpack
|
|
== slice.transposed!(
|
|
slice.shape.length-2,
|
|
slice.shape.length-1));
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: transposed;
|
|
import std.range.primitives: ElementType;
|
|
import std.range: iota;
|
|
import std.algorithm.comparison: equal;
|
|
auto r = 100000000.iota;
|
|
auto a = r.sliced(3, 4, 5, 6, 7, 8, 9, 10, 11);
|
|
auto b = a
|
|
.pack!(2, 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]);
|
|
alias R = typeof(r);
|
|
static assert(is(typeof(b) == Slice!(2, Slice!(4, Slice!(5, R)))));
|
|
static assert(is(typeof(c) == Slice!(3, Slice!(5, R))));
|
|
static assert(is(typeof(d) == Slice!(4, R)));
|
|
static assert(is(typeof(e) == ElementType!R));
|
|
}
|
|
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
static assert(is(typeof(new int[20]
|
|
.sliced(20)
|
|
.evertPack)
|
|
== Slice!(1LU, int*)));
|
|
static assert(is(typeof(new int[20]
|
|
.sliced(20)
|
|
.sliced(3)
|
|
.evertPack)
|
|
== Slice!(2LU, int*)));
|
|
static assert(is(typeof(new int[20]
|
|
.sliced(20)
|
|
.sliced(1,2,3)
|
|
.sliced(3)
|
|
.evertPack)
|
|
== Slice!(3LU, Slice!(2LU, int*))));
|
|
static assert(is(typeof(new int[20]
|
|
.sliced(20)
|
|
.sliced(1,2,3)
|
|
.evertPack)
|
|
== Slice!(4LU, int*)));
|
|
}
|
|
|
|
/++
|
|
Returns a 1-dimensional slice over the main diagonal of an n-dimensional slice.
|
|
`diagonal` can be generalized with other selectors such as
|
|
$(LREF blocks) (diagonal blocks) and $(LREF windows) (multi-diagonal slice).
|
|
|
|
Params:
|
|
N = dimension count
|
|
slice = input slice
|
|
Returns:
|
|
packed `1`-dimensional composed of `N`-dimensional slices
|
|
+/
|
|
Slice!(1, Range) diagonal(size_t N, Range)(auto ref Slice!(N, Range) slice)
|
|
{
|
|
auto NewN = slice.PureN - N + 1;
|
|
mixin _DefineRet;
|
|
ret._lengths[0] = slice._lengths[0];
|
|
ret._strides[0] = slice._strides[0];
|
|
foreach (i; Iota!(1, N))
|
|
{
|
|
if (ret._lengths[0] > slice._lengths[i])
|
|
ret._lengths[0] = slice._lengths[i];
|
|
ret._strides[0] += slice._strides[i];
|
|
}
|
|
foreach (i; Iota!(1, ret.PureN))
|
|
{
|
|
ret._lengths[i] = slice._lengths[i + N - 1];
|
|
ret._strides[i] = slice._strides[i + N - 1];
|
|
}
|
|
ret._ptr = slice._ptr;
|
|
return ret;
|
|
}
|
|
|
|
/// Matrix, main diagonal
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota, only;
|
|
|
|
// -------
|
|
// | 0 1 2 |
|
|
// | 3 4 5 |
|
|
// -------
|
|
//->
|
|
// | 0 4 |
|
|
assert(10.iota
|
|
.sliced(2, 3)
|
|
.diagonal
|
|
.equal(only(0, 4)));
|
|
}
|
|
|
|
/// ditto
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
|
|
auto slice = new int[9].sliced(3, 3);
|
|
int i;
|
|
foreach (ref e; slice.diagonal)
|
|
e = ++i;
|
|
assert(slice == [
|
|
[1, 0, 0],
|
|
[0, 2, 0],
|
|
[0, 0, 3]]);
|
|
}
|
|
|
|
/// Matrix, subdiagonal
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: dropOne;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota, only;
|
|
// -------
|
|
// | 0 1 2 |
|
|
// | 3 4 5 |
|
|
// -------
|
|
//->
|
|
// | 1 5 |
|
|
assert(10.iota
|
|
.sliced(2, 3)
|
|
.dropOne!1
|
|
.diagonal
|
|
.equal(only(1, 5)));
|
|
}
|
|
|
|
/// Matrix, antidiagonal
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: dropToHypercube, reversed;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota, only;
|
|
// -------
|
|
// | 0 1 2 |
|
|
// | 3 4 5 |
|
|
// -------
|
|
//->
|
|
// | 1 3 |
|
|
assert(10.iota
|
|
.sliced(2, 3)
|
|
.dropToHypercube
|
|
.reversed!1
|
|
.diagonal
|
|
.equal(only(1, 3)));
|
|
}
|
|
|
|
/// 3D, main diagonal
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota, only;
|
|
// -----------
|
|
// | 0 1 2 |
|
|
// | 3 4 5 |
|
|
// - - - - - -
|
|
// | 6 7 8 |
|
|
// | 9 10 11 |
|
|
// -----------
|
|
//->
|
|
// | 0 10 |
|
|
assert(100.iota
|
|
.sliced(2, 2, 3)
|
|
.diagonal
|
|
.equal(only(0, 10)));
|
|
}
|
|
|
|
/// 3D, subdiagonal
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: dropOne;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota, only;
|
|
// -----------
|
|
// | 0 1 2 |
|
|
// | 3 4 5 |
|
|
// - - - - - -
|
|
// | 6 7 8 |
|
|
// | 9 10 11 |
|
|
// -----------
|
|
//->
|
|
// | 1 11 |
|
|
assert(100.iota
|
|
.sliced(2, 2, 3)
|
|
.dropOne!2
|
|
.diagonal
|
|
.equal(only(1, 11)));
|
|
}
|
|
|
|
/// 3D, diagonal plain
|
|
@safe pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: dropOne;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota;
|
|
// -----------
|
|
// | 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 |
|
|
// -----------
|
|
auto slice = 100.iota
|
|
.sliced(3, 3, 3)
|
|
.pack!2
|
|
.evertPack
|
|
.diagonal
|
|
.evertPack;
|
|
assert(slice ==
|
|
[[ 0, 4, 8],
|
|
[ 9, 13, 17],
|
|
[18, 22, 26]]);
|
|
}
|
|
|
|
/++
|
|
Returns an n-dimensional slice of n-dimensional non-overlapping blocks.
|
|
`blocks` can be generalized with other selectors.
|
|
For example, `blocks` in combination with $(LREF diagonal) can be used to get a slice of diagonal blocks.
|
|
|
|
Params:
|
|
N = dimension count
|
|
slice = slice to be split into blocks
|
|
lengths = dimensions of block, residual blocks are ignored
|
|
Returns:
|
|
packed `N`-dimensional slice composed of `N`-dimensional slices
|
|
+/
|
|
Slice!(N, Slice!(N+1, Range)) blocks(size_t N, Range, Lengths...)(auto ref Slice!(N, Range) slice, Lengths lengths)
|
|
if (allSatisfy!(isIndex, Lengths) && Lengths.length == N)
|
|
in
|
|
{
|
|
foreach (i, length; lengths)
|
|
assert(length > 0, "length of dimension = " ~ i.stringof ~ " must be positive"
|
|
~ tailErrorMessage!());
|
|
}
|
|
body
|
|
{
|
|
mixin _DefineRet;
|
|
foreach (dimension; Iota!(0, N))
|
|
{
|
|
ret._lengths[dimension] = slice._lengths[dimension] / lengths[dimension];
|
|
ret._strides[dimension] = slice._strides[dimension];
|
|
if (ret._lengths[dimension]) //do not remove `if (...)`
|
|
ret._strides[dimension] *= lengths[dimension];
|
|
ret._lengths[dimension + N] = lengths[dimension];
|
|
ret._strides[dimension + N] = slice._strides[dimension];
|
|
}
|
|
foreach (dimension; Iota!(N, slice.PureN))
|
|
{
|
|
ret._lengths[dimension + N] = slice._lengths[dimension];
|
|
ret._strides[dimension + N] = slice._strides[dimension];
|
|
}
|
|
ret._ptr = slice._ptr;
|
|
return ret;
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[1000].sliced(5, 8);
|
|
auto blocks = slice.blocks(2, 3);
|
|
int i;
|
|
foreach (block; blocks.byElement)
|
|
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]]);
|
|
}
|
|
|
|
/// Diagonal blocks
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[1000].sliced(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]]);
|
|
}
|
|
|
|
/// Matrix divided into vertical blocks
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[1000].sliced(5, 13);
|
|
auto blocks = slice
|
|
.pack!1
|
|
.evertPack
|
|
.blocks(3)
|
|
.unpack
|
|
.pack!2;
|
|
|
|
int i;
|
|
foreach (block; blocks.byElement)
|
|
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]]);
|
|
}
|
|
|
|
/++
|
|
Returns an n-dimensional slice of n-dimensional overlapping windows.
|
|
`windows` can be generalized with other selectors.
|
|
For example, `windows` in combination with $(LREF diagonal) can be used to get a multi-diagonal slice.
|
|
|
|
Params:
|
|
N = dimension count
|
|
slice = slice to be iterated
|
|
lengths = dimensions of windows
|
|
Returns:
|
|
packed `N`-dimensional slice composed of `N`-dimensional slices
|
|
+/
|
|
Slice!(N, Slice!(N+1, Range)) windows(size_t N, Range, Lengths...)(auto ref Slice!(N, Range) slice, Lengths lengths)
|
|
if (allSatisfy!(isIndex, Lengths) && Lengths.length == N)
|
|
in
|
|
{
|
|
foreach (i, length; lengths)
|
|
assert(length > 0, "length of dimension = " ~ i.stringof ~ " must be positive"
|
|
~ tailErrorMessage!());
|
|
}
|
|
body
|
|
{
|
|
mixin _DefineRet;
|
|
foreach (dimension; Iota!(0, N))
|
|
{
|
|
ret._lengths[dimension] = slice._lengths[dimension] >= lengths[dimension] ?
|
|
slice._lengths[dimension] - lengths[dimension] + 1: 0;
|
|
ret._strides[dimension] = slice._strides[dimension];
|
|
ret._lengths[dimension + N] = lengths[dimension];
|
|
ret._strides[dimension + N] = slice._strides[dimension];
|
|
}
|
|
foreach (dimension; Iota!(N, slice.PureN))
|
|
{
|
|
ret._lengths[dimension + N] = slice._lengths[dimension];
|
|
ret._strides[dimension + N] = slice._strides[dimension];
|
|
}
|
|
ret._ptr = slice._ptr;
|
|
return ret;
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[1000].sliced(5, 8);
|
|
auto windows = slice.windows(2, 3);
|
|
foreach (window; windows.byElement)
|
|
window[] += 1;
|
|
|
|
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]]);
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[1000].sliced(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]]);
|
|
}
|
|
|
|
/// Multi-diagonal matrix
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[1000].sliced(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]]);
|
|
}
|
|
|
|
/// Sliding window over matrix columns
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[1000].sliced(5, 8);
|
|
auto windows = slice
|
|
.pack!1
|
|
.evertPack
|
|
.windows(3)
|
|
.unpack
|
|
.pack!2;
|
|
|
|
|
|
foreach (window; windows.byElement)
|
|
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]]);
|
|
}
|
|
|
|
/++
|
|
Returns a new slice for the same data.
|
|
|
|
Params:
|
|
slice = slice to be reshaped
|
|
lengths = list of new dimensions. One of the lengths can be set to `-1`.
|
|
In this case, the corresponding dimension is inferable.
|
|
Returns:
|
|
reshaped slice
|
|
Throws:
|
|
$(LREF ReshapeException) if the slice cannot be reshaped with the input lengths.
|
|
+/
|
|
Slice!(Lengths.length, Range)
|
|
reshape
|
|
( size_t N, Range , Lengths... )
|
|
(auto ref Slice!(N, Range) slice, Lengths lengths)
|
|
if ( allSatisfy!(isIndex, Lengths) && Lengths.length)
|
|
{
|
|
mixin _DefineRet;
|
|
foreach (i; Iota!(0, ret.N))
|
|
ret._lengths[i] = lengths[i];
|
|
|
|
immutable size_t eco = slice.elementsCount;
|
|
size_t ecn = ret .elementsCount;
|
|
|
|
if (eco == 0)
|
|
throw new ReshapeException(
|
|
slice._lengths.dup,
|
|
slice._strides.dup,
|
|
ret. _lengths.dup,
|
|
"slice should be not empty");
|
|
|
|
foreach (i; Iota!(0, ret.N))
|
|
if (ret._lengths[i] == -1)
|
|
{
|
|
ecn = -ecn;
|
|
ret._lengths[i] = eco / ecn;
|
|
ecn *= ret._lengths[i];
|
|
break;
|
|
}
|
|
|
|
if (eco != ecn)
|
|
throw new ReshapeException(
|
|
slice._lengths.dup,
|
|
slice._strides.dup,
|
|
ret. _lengths.dup,
|
|
"total element count should be the same");
|
|
|
|
for (size_t oi, ni, oj, nj; oi < slice.N && ni < ret.N; oi = oj, ni = nj)
|
|
{
|
|
size_t op = slice._lengths[oj++];
|
|
size_t np = ret ._lengths[nj++];
|
|
|
|
for (;;)
|
|
{
|
|
if (op < np)
|
|
op *= slice._lengths[oj++];
|
|
if (op > np)
|
|
np *= ret ._lengths[nj++];
|
|
if (op == np)
|
|
break;
|
|
}
|
|
while (oj < slice.N && slice._lengths[oj] == 1) oj++;
|
|
while (nj < ret .N && ret ._lengths[nj] == 1) nj++;
|
|
|
|
for (size_t l = oi, r = oi + 1; r < oj; r++)
|
|
if (slice._lengths[r] != 1)
|
|
{
|
|
if (slice._strides[l] != slice._lengths[r] * slice._strides[r])
|
|
throw new ReshapeException(
|
|
slice._lengths.dup,
|
|
slice._strides.dup,
|
|
ret. _lengths.dup,
|
|
"structure is incompatible with new shape");
|
|
l = r;
|
|
}
|
|
|
|
ret._strides[nj - 1] = slice._strides[oj - 1];
|
|
foreach_reverse(i; ni .. nj - 1)
|
|
ret._strides[i] = ret._lengths[i + 1] * ret._strides[i + 1];
|
|
assert((oi == slice.N) == (ni == ret.N));
|
|
}
|
|
foreach (i; Iota!(ret.N, ret.PureN))
|
|
{
|
|
ret._lengths[i] = slice._lengths[i + slcie.N - ret.N];
|
|
ret._strides[i] = slice._strides[i + slcie.N - ret.N];
|
|
}
|
|
ret._ptr = slice._ptr;
|
|
return ret;
|
|
}
|
|
|
|
///
|
|
@safe pure unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
import std.experimental.ndslice.iteration: allReversed;
|
|
auto slice = 100.iota
|
|
.sliced(3, 4)
|
|
.allReversed
|
|
.reshape(-1, 3);
|
|
assert(slice ==
|
|
[[11, 10, 9],
|
|
[ 8, 7, 6],
|
|
[ 5, 4, 3],
|
|
[ 2, 1, 0]]);
|
|
}
|
|
|
|
/// Reshaping with memory allocation
|
|
pure unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: reversed;
|
|
import std.array: array;
|
|
|
|
auto reshape2(S, L...)(S slice, L lengths)
|
|
{
|
|
// Tries to reshape without allocation
|
|
try return slice.reshape(lengths);
|
|
catch(ReshapeException e)
|
|
//allocates the elements and creates a slice
|
|
//Note: -1 length is not supported by reshape2
|
|
return slice.byElement.array.sliced(lengths);
|
|
}
|
|
|
|
auto slice =
|
|
[0, 1, 2, 3,
|
|
4, 5, 6, 7,
|
|
8, 9, 10, 11]
|
|
.sliced(3, 4)
|
|
.reversed!0;
|
|
|
|
assert(reshape2(slice, 4, 3) ==
|
|
[[ 8, 9, 10],
|
|
[11, 4, 5],
|
|
[ 6, 7, 0],
|
|
[ 1, 2, 3]]);
|
|
}
|
|
|
|
@safe pure unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration: allReversed;
|
|
import std.stdio;
|
|
import std.range: iota;
|
|
auto slice = 100.iota.sliced(1, 1, 3, 2, 1, 2, 1).allReversed;
|
|
assert(slice.reshape(1, -1, 1, 1, 3, 1) ==
|
|
[[[[[[11], [10], [9]]]],
|
|
[[[[ 8], [ 7], [6]]]],
|
|
[[[[ 5], [ 4], [3]]]],
|
|
[[[[ 2], [ 1], [0]]]]]]);
|
|
}
|
|
|
|
/// See_also: $(LREF reshape)
|
|
class ReshapeException: Exception
|
|
{
|
|
/// Old lengths
|
|
size_t[] lengths;
|
|
/// Old strides
|
|
sizediff_t[] strides;
|
|
/// New lengths
|
|
size_t[] newLengths;
|
|
///
|
|
this(
|
|
size_t[] lengths,
|
|
sizediff_t[] strides,
|
|
size_t[] newLengths,
|
|
string msg,
|
|
string file = __FILE__,
|
|
uint line = cast(uint)__LINE__,
|
|
Throwable next = null
|
|
) pure nothrow @nogc @safe
|
|
{
|
|
super(msg, file, line, next);
|
|
this.lengths = lengths;
|
|
this.strides = strides;
|
|
this.newLengths = newLengths;
|
|
}
|
|
}
|
|
|
|
/++
|
|
Returns a random access range of all elements of a slice.
|
|
The order of elements is preserved.
|
|
`byElement` can be generalized with other selectors.
|
|
|
|
Params:
|
|
N = dimension count
|
|
slice = slice to be iterated
|
|
Returns:
|
|
random access range composed of elements of the `slice`
|
|
+/
|
|
auto byElement(size_t N, Range)(auto ref Slice!(N, Range) slice)
|
|
{
|
|
with (Slice!(N, Range))
|
|
{
|
|
/++
|
|
ByElement shifts the range's `_ptr` without modifying its strides and lengths.
|
|
+/
|
|
static struct ByElement
|
|
{
|
|
This _slice;
|
|
size_t _length;
|
|
size_t[N] _indexes;
|
|
|
|
static if (canSave!PureRange)
|
|
auto save() @property
|
|
{
|
|
return typeof(this)(_slice.save, _length, _indexes);
|
|
}
|
|
|
|
bool empty() const @property
|
|
{
|
|
pragma(inline, true);
|
|
return _length == 0;
|
|
}
|
|
|
|
size_t length() const @property
|
|
{
|
|
pragma(inline, true);
|
|
return _length;
|
|
}
|
|
|
|
auto ref front() @property
|
|
{
|
|
assert(!this.empty);
|
|
static if (N == PureN)
|
|
{
|
|
return _slice._ptr[0];
|
|
}
|
|
else with (_slice)
|
|
{
|
|
alias M = DeepElemType.PureN;
|
|
return DeepElemType(_lengths[$ - M .. $], _strides[$ - M .. $], _ptr);
|
|
}
|
|
}
|
|
|
|
static if (PureN == 1 && rangeHasMutableElements && !hasAccessByRef)
|
|
auto front(DeepElemType elem) @property
|
|
{
|
|
assert(!this.empty);
|
|
return _slice._ptr[0] = elem;
|
|
}
|
|
|
|
void popFront()
|
|
{
|
|
assert(_length != 0);
|
|
_length--;
|
|
popFrontImpl;
|
|
}
|
|
|
|
private void popFrontImpl()
|
|
{
|
|
foreach_reverse(i; Iota!(0, N)) with (_slice)
|
|
{
|
|
_ptr += _strides[i];
|
|
_indexes[i]++;
|
|
if (_indexes[i] < _lengths[i])
|
|
return;
|
|
debug (ndslice) assert(_indexes[i] == _lengths[i]);
|
|
_ptr -= _lengths[i] * _strides[i];
|
|
_indexes[i] = 0;
|
|
}
|
|
}
|
|
|
|
auto ref back() @property
|
|
{
|
|
assert(!this.empty);
|
|
return opIndex(_length - 1);
|
|
}
|
|
|
|
static if (PureN == 1 && rangeHasMutableElements && !hasAccessByRef)
|
|
auto back(DeepElemType elem) @property
|
|
{
|
|
assert(!this.empty);
|
|
return opIndexAssign(_length - 1, elem);
|
|
}
|
|
|
|
void popBack()
|
|
{
|
|
pragma(inline, true);
|
|
assert(_length != 0);
|
|
_length--;
|
|
}
|
|
|
|
void popFrontExactly(size_t n)
|
|
in
|
|
{
|
|
assert(n <= _length);
|
|
}
|
|
body
|
|
{
|
|
_length -= n;
|
|
//calculates shift and new indexes
|
|
sizediff_t _shift;
|
|
n += _indexes[N-1];
|
|
foreach_reverse(i; Iota!(1, N)) with (_slice)
|
|
{
|
|
immutable v = n / _lengths[i];
|
|
n %= _lengths[i];
|
|
_shift += (n - _indexes[i]) * _strides[i];
|
|
_indexes[i] = n;
|
|
n = _indexes[i - 1] + v;
|
|
}
|
|
assert(n < _slice._lengths[0]);
|
|
with (_slice)
|
|
{
|
|
_shift += (n - _indexes[0]) * _strides[0];
|
|
_indexes[0] = n;
|
|
}
|
|
_slice._ptr += _shift;
|
|
}
|
|
|
|
void popBackExactly(size_t n)
|
|
in
|
|
{
|
|
assert(n <= _length);
|
|
}
|
|
body
|
|
{
|
|
pragma(inline, true);
|
|
_length -= n;
|
|
}
|
|
|
|
//calculates shift for index n
|
|
private sizediff_t getShift(size_t n)
|
|
in
|
|
{
|
|
assert(n < _length);
|
|
}
|
|
body
|
|
{
|
|
sizediff_t _shift;
|
|
n += _indexes[N-1];
|
|
foreach_reverse(i; Iota!(1, N)) with (_slice)
|
|
{
|
|
immutable v = n / _lengths[i];
|
|
n %= _lengths[i];
|
|
_shift += (n - _indexes[i]) * _strides[i];
|
|
n = _indexes[i - 1] + v;
|
|
}
|
|
debug (ndslice) assert(n < _slice._lengths[0]);
|
|
with (_slice)
|
|
_shift += (n - _indexes[0]) * _strides[0];
|
|
return _shift;
|
|
}
|
|
|
|
auto ref opIndex(size_t index)
|
|
{
|
|
return _slice._ptr[getShift(index)];
|
|
}
|
|
|
|
static if (PureN == 1 && rangeHasMutableElements && !hasAccessByRef)
|
|
auto opIndexAssign(DeepElemType elem, size_t index)
|
|
{
|
|
return _slice[getShift(index)] = elem;
|
|
}
|
|
|
|
auto opIndex(_Slice sl)
|
|
{
|
|
auto ret = this;
|
|
ret.popFrontExactly(sl.i);
|
|
ret.popBackExactly(_length - sl.j);
|
|
return ret;
|
|
}
|
|
|
|
alias opDollar = length;
|
|
|
|
_Slice opSlice(size_t pos : 0)(size_t i, size_t j)
|
|
in
|
|
{
|
|
assert(i <= j,
|
|
"the left bound must be less than or equal to the right bound"
|
|
~ tailErrorMessage!());
|
|
assert(j - i <= _length,
|
|
"the difference between the right and the left bound must be less than or equal to range length"
|
|
~ tailErrorMessage!());
|
|
}
|
|
body
|
|
{
|
|
pragma(inline, true);
|
|
return typeof(return)(i, j);
|
|
}
|
|
|
|
size_t[N] index() @property
|
|
{
|
|
pragma(inline, true);
|
|
return _indexes;
|
|
}
|
|
}
|
|
return ByElement(slice, slice.elementsCount);
|
|
}
|
|
}
|
|
|
|
/// Regular slice
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota;
|
|
assert(100.iota
|
|
.sliced(4, 5)
|
|
.byElement
|
|
.equal(20.iota));
|
|
}
|
|
|
|
/// Packed slice
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration;
|
|
import std.range: iota, drop;
|
|
import std.algorithm.comparison: equal;
|
|
assert(100000.iota
|
|
.sliced(3, 4, 5, 6, 7)
|
|
.pack!2
|
|
.byElement()
|
|
.drop(1)
|
|
.front
|
|
.byElement
|
|
.equal(iota(6 * 7, 6 * 7 * 2)));
|
|
}
|
|
|
|
/// Properties
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
auto elems = 12.iota.sliced(3, 4).byElement;
|
|
elems.popFrontExactly(2);
|
|
assert(elems.front == 2);
|
|
assert(elems.index == [0, 2]);
|
|
elems.popBackExactly(2);
|
|
assert(elems.back == 9);
|
|
assert(elems.length == 8);
|
|
}
|
|
|
|
/// Index property
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new long[20].sliced(5, 4);
|
|
|
|
for(auto elems = slice.byElement; !elems.empty; elems.popFront)
|
|
{
|
|
size_t[2] index = elems.index;
|
|
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]]);
|
|
}
|
|
|
|
/++
|
|
Random access and slicing
|
|
+/
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
auto elems = 100.iota.sliced(4, 5).byElement;
|
|
|
|
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);
|
|
}
|
|
|
|
/++
|
|
Forward access works faster than random access or backward access.
|
|
Use $(SUBREF iteration, allReversed) in pipeline before
|
|
`byElement` to achieve fast backward access.
|
|
+/
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.range: retro, iota;
|
|
import std.experimental.ndslice.iteration: allReversed;
|
|
|
|
auto slice = 100.iota.sliced(3, 4, 5);
|
|
|
|
/// Slow backward iteration #1
|
|
foreach (ref e; slice.byElement.retro)
|
|
{
|
|
//...
|
|
}
|
|
|
|
/// Slow backward iteration #2
|
|
foreach_reverse (ref e; slice.byElement)
|
|
{
|
|
//...
|
|
}
|
|
|
|
/// Fast backward iteration
|
|
foreach (ref e; slice.allReversed.byElement)
|
|
{
|
|
//...
|
|
}
|
|
}
|
|
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, isRandomAccessRange, hasSlicing;
|
|
auto elems = 100.iota.sliced(4, 5).byElement;
|
|
static assert(isRandomAccessRange!(typeof(elems)));
|
|
static assert(hasSlicing!(typeof(elems)));
|
|
}
|
|
|
|
// Checks strides
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration;
|
|
import std.range: iota, isRandomAccessRange;
|
|
auto elems = 100.iota.sliced(4, 5).everted.byElement;
|
|
static assert(isRandomAccessRange!(typeof(elems)));
|
|
|
|
elems = elems[11 .. $ - 2];
|
|
auto elems2 = elems;
|
|
foreach (i; 0 .. 7)
|
|
{
|
|
assert(elems[i] == elems2.front);
|
|
elems2.popFront;
|
|
}
|
|
}
|
|
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration;
|
|
import std.range: iota, isForwardRange, hasLength;
|
|
import std.algorithm.comparison: equal;
|
|
|
|
auto range = 100000.iota;
|
|
auto slice0 = range.sliced(3, 4, 5, 6, 7);
|
|
auto slice1 = slice0.transposed!(2, 1).pack!2;
|
|
auto elems0 = slice0.byElement;
|
|
auto elems1 = slice1.byElement;
|
|
|
|
import std.meta;
|
|
foreach (S; AliasSeq!(typeof(elems0), typeof(elems1)))
|
|
{
|
|
static assert(isForwardRange!S);
|
|
static assert(hasLength!S);
|
|
}
|
|
|
|
assert(elems0.length == slice0.elementsCount);
|
|
assert(elems1.length == 5 * 4 * 3);
|
|
|
|
auto elems2 = elems1;
|
|
foreach (q; slice1)
|
|
foreach (w; q)
|
|
foreach (e; w)
|
|
{
|
|
assert(!elems2.empty);
|
|
assert(e == elems2.front);
|
|
elems2.popFront;
|
|
}
|
|
assert(elems2.empty);
|
|
|
|
elems0.popFront();
|
|
elems0.popFrontExactly(slice0.elementsCount - 14);
|
|
assert(elems0.length == 13);
|
|
assert(elems0.equal(range[slice0.elementsCount - 13 .. slice0.elementsCount]));
|
|
|
|
foreach (elem; elems0) {}
|
|
}
|
|
|
|
/++
|
|
Returns an forward range of all elements of standard simplex of a slice.
|
|
In case the slice has two dimensions, it is composed of elements of upper left triangular matrix.
|
|
The order of elements is preserved.
|
|
`byElementInStandardSimplex` can be generalized with other selectors.
|
|
|
|
Params:
|
|
N = dimension count
|
|
slice = slice to be iterated
|
|
Returns:
|
|
forward range composed of all elements of standard simplex of the `slice`
|
|
+/
|
|
auto byElementInStandardSimplex(size_t N, Range)(auto ref Slice!(N, Range) slice, size_t maxCobeLength = size_t.max)
|
|
{
|
|
with (Slice!(N, Range))
|
|
{
|
|
/++
|
|
ByElementInTopSimplex shifts the range's `_ptr` without modifying its strides and lengths.
|
|
+/
|
|
static struct ByElementInTopSimplex
|
|
{
|
|
This _slice;
|
|
size_t _length;
|
|
size_t maxCobeLength;
|
|
size_t sum;
|
|
size_t[N] _indexes;
|
|
|
|
static if (canSave!PureRange)
|
|
auto save() @property
|
|
{
|
|
return typeof(this)(_slice.save, _length, maxCobeLength, sum, _indexes);
|
|
}
|
|
|
|
bool empty() const @property
|
|
{
|
|
pragma(inline, true);
|
|
return _length == 0;
|
|
}
|
|
|
|
size_t length() const @property
|
|
{
|
|
pragma(inline, true);
|
|
return _length;
|
|
}
|
|
|
|
auto ref front() @property
|
|
{
|
|
assert(!this.empty);
|
|
static if (N == PureN)
|
|
return _slice._ptr[0];
|
|
else with (_slice)
|
|
{
|
|
alias M = DeepElemType.PureN;
|
|
return DeepElemType(_lengths[$ - M .. $], _strides[$ - M .. $], _ptr);
|
|
}
|
|
}
|
|
|
|
static if (PureN == 1 && rangeHasMutableElements && !hasAccessByRef)
|
|
auto front(DeepElemType elem) @property
|
|
{
|
|
pragma(inline, true);
|
|
assert(!this.empty);
|
|
return _slice._ptr[0] = elem;
|
|
}
|
|
|
|
void popFront()
|
|
{
|
|
pragma(inline, true);
|
|
assert(_length != 0);
|
|
_length--;
|
|
popFrontImpl;
|
|
}
|
|
|
|
private void popFrontImpl()
|
|
{
|
|
foreach_reverse(i; Iota!(0, N)) with (_slice)
|
|
{
|
|
_ptr += _strides[i];
|
|
_indexes[i]++;
|
|
debug (ndslice) assert(_indexes[i] <= _lengths[i]);
|
|
sum++;
|
|
if (sum < maxCobeLength)
|
|
return;
|
|
debug (ndslice) assert(sum == maxCobeLength);
|
|
_ptr -= _indexes[i] * _strides[i];
|
|
sum -= _indexes[i];
|
|
_indexes[i] = 0;
|
|
}
|
|
}
|
|
|
|
size_t[N] index() @property
|
|
{
|
|
pragma(inline, true);
|
|
return _indexes;
|
|
}
|
|
}
|
|
foreach (i; Iota!(0, N))
|
|
if (maxCobeLength > slice._lengths[i])
|
|
maxCobeLength = slice._lengths[i];
|
|
immutable size_t elementsCount = ((maxCobeLength + 1) * maxCobeLength ^^ (N - 1)) / 2;
|
|
return ByElementInTopSimplex(slice, elementsCount, maxCobeLength);
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = new int[20].sliced(4, 5);
|
|
auto elems = slice
|
|
.byElementInStandardSimplex;
|
|
int i;
|
|
foreach (ref e; elems)
|
|
e = ++i;
|
|
assert(slice ==
|
|
[[ 1, 2, 3, 4, 0],
|
|
[ 5, 6, 7, 0, 0],
|
|
[ 8, 9, 0, 0, 0],
|
|
[10, 0, 0, 0, 0]]);
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.iteration;
|
|
auto slice = new int[20].sliced(4, 5);
|
|
auto elems = slice
|
|
.transposed
|
|
.allReversed
|
|
.byElementInStandardSimplex;
|
|
int i;
|
|
foreach (ref e; elems)
|
|
e = ++i;
|
|
assert(slice ==
|
|
[[0, 0, 0, 0, 4],
|
|
[0, 0, 0, 7, 3],
|
|
[0, 0, 9, 6, 2],
|
|
[0, 10, 8, 5, 1]]);
|
|
}
|
|
|
|
/// Properties
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
auto elems = 12.iota.sliced(3, 4).byElementInStandardSimplex;
|
|
elems.popFront;
|
|
assert(elems.front == 1);
|
|
assert(elems.index == cast(size_t[2])[0, 1]);
|
|
import std.range: popFrontN;
|
|
elems.popFrontN(3);
|
|
assert(elems.front == 5);
|
|
assert(elems.index == cast(size_t[2])[1, 1]);
|
|
assert(elems.length == 2);
|
|
}
|
|
|
|
/++
|
|
Returns a slice, the elements of which are equal to the initial index value.
|
|
|
|
Params:
|
|
N = dimension count
|
|
lengths = list of dimension lengths
|
|
Returns:
|
|
`N`-dimensional slice composed of indexes
|
|
See_also: $(LREF IndexSlice)
|
|
+/
|
|
IndexSlice!(Lengths.length) indexSlice(Lengths...)(Lengths lengths)
|
|
if (allSatisfy!(isIndex, Lengths))
|
|
{
|
|
return .indexSlice!(Lengths.length)([lengths]);
|
|
}
|
|
|
|
///ditto
|
|
IndexSlice!N indexSlice(size_t N)(auto ref size_t[N] lengths)
|
|
{
|
|
import std.experimental.ndslice.slice: sliced;
|
|
with (typeof(return)) return Range(lengths[1 .. $]).sliced(lengths);
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
auto im = indexSlice(7, 9);
|
|
|
|
assert(im[2, 1] == cast(size_t[2])[2, 1]);
|
|
|
|
for (auto elems = im.byElement; !elems.empty; elems.popFront)
|
|
assert(elems.front == elems.index);
|
|
|
|
//slicing works correctly
|
|
auto cm = im[1 .. $ - 3, 4 .. $ - 1];
|
|
assert(cm[2, 1] == cast(size_t[2])[3, 5]);
|
|
}
|
|
|
|
/++
|
|
Slice composed of indexes.
|
|
See_also: $(LREF indexSlice)
|
|
+/
|
|
template IndexSlice(size_t N)
|
|
if (N)
|
|
{
|
|
struct IndexMap
|
|
{
|
|
private size_t[N-1] _lengths;
|
|
|
|
auto save() @property const {
|
|
pragma(inline, true);
|
|
return this;
|
|
}
|
|
|
|
size_t[N] opIndex(size_t index) const
|
|
{
|
|
pragma(inline, true);
|
|
size_t[N] indexes = void;
|
|
foreach_reverse(i; Iota!(0, N - 1))
|
|
{
|
|
indexes[i + 1] = index % _lengths[i];
|
|
index /= _lengths[i];
|
|
}
|
|
indexes[0] = index;
|
|
return indexes;
|
|
}
|
|
}
|
|
alias IndexSlice = Slice!(N, IndexMap);
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
alias IS4 = IndexSlice!4;
|
|
static assert(is(IS4 == Slice!(4, Range), Range));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto r = indexSlice(1);
|
|
}
|