mirror of
https://github.com/dlang/phobos.git
synced 2025-05-04 17:11:26 +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
1222 lines
32 KiB
D
1222 lines
32 KiB
D
/**
|
|
$(SCRIPT inhibitQuickIndex = 1;)
|
|
|
|
This is a submodule of $(LINK2 std_experimental_ndslice.html, std.experimental.ndslice).
|
|
|
|
Operators only change strides and lengths of a slice.
|
|
The range of a slice remains unmodified.
|
|
All operators return slice of the same type as the type of the argument.
|
|
|
|
$(BOOKTABLE $(H2 Transpose operators),
|
|
|
|
$(TR $(TH Function Name) $(TH Descriprottion))
|
|
$(T2 transposed, `100000.iota.sliced(3, 4, 5, 6, 7).transposed!(4, 0, 1).shape` returns `[7, 3, 4, 5, 6]`.)
|
|
$(T2 swapped, `1000.iota.sliced(3, 4, 5).swapped!(1, 2).shape` returns `[3, 5, 4]`.)
|
|
$(T2 everted, `1000.iota.sliced(3, 4, 5).everted.shape` returns `[5, 4, 3]`.)
|
|
)
|
|
See also $(SUBREF selection, evertPack).
|
|
|
|
$(BOOKTABLE $(H2 Iteration operators),
|
|
|
|
$(TR $(TH Function Name) $(TH Description))
|
|
$(T2 strided, `1000.iota.sliced(13, 40).strided!(0, 1)(2, 5).shape` equals to `[7, 8]`.)
|
|
$(T2 reversed, `slice.reversed!0` returns the slice with reversed direction of iteration for top level dimension.)
|
|
$(T2 allReversed, `20.iota.sliced(4, 5).allReversed` equals to `20.iota.retro.sliced(4, 5)`.)
|
|
)
|
|
|
|
$(BOOKTABLE $(H2 Other operators),
|
|
$(TR $(TH Function Name) $(TH Description))
|
|
$(T2 rotated, `10.iota.sliced(2, 3).rotated` equals to `[[2, 5], [1, 4], [0, 3]]`.)
|
|
)
|
|
|
|
$(H4 Drop operators)
|
|
|
|
$(LREF dropToHypercube)
|
|
$(LREF drop) $(LREF dropBack)
|
|
$(LREF dropOne) $(LREF dropBackOne)
|
|
$(LREF dropExactly) $(LREF dropBackExactly)
|
|
$(LREF allDrop) $(LREF allDropBack)
|
|
$(LREF allDropOne) $(LREF allDropBackOne)
|
|
$(LREF allDropExactly) $(LREF allDropBackExactly)
|
|
|
|
$(GRAMMAR
|
|
$(GNAME DropOperatorName):
|
|
$(D dropToHypercube)
|
|
$(GLINK DropRoot)
|
|
$(GLINK DropRoot) $(GLINK DropSuffix)
|
|
$(GLINK DropRoot) $(D Back)
|
|
$(GLINK DropRoot) $(D Back) $(GLINK DropSuffix)
|
|
$(GNAME DropRoot):
|
|
$(D drop)
|
|
$(D allDrop)
|
|
$(GNAME DropSuffix):
|
|
$(D One)
|
|
$(D Exactly)
|
|
)
|
|
|
|
$(H2 Bifacial operators)
|
|
|
|
Some operators are bifacial,
|
|
i.e. they have two versions: one with template parameters, and another one
|
|
with function parameters. Versions with template parameters are preferable
|
|
because they allow compile time checks and can be optimized better.
|
|
|
|
$(BOOKTABLE ,
|
|
|
|
$(TR $(TH Function Name) $(TH Variadic) $(TH Template) $(TH Function))
|
|
$(T4 swapped, No, `slice.swapped!(2, 3)`, `slice.swapped(2, 3)`)
|
|
$(T4 rotated, No, `slice.rotated!(2, 3)(-1)`, `slice.rotated(2, 3, -1)`)
|
|
$(T4 strided, Yes/No, `slice.strided!(1, 2)(20, 40)`, `slice.strided(1, 20).strided(2, 40)`)
|
|
$(T4 transposed, Yes, `slice.transposed!(1, 4, 3)`, `slice.transposed(1, 4, 3)`)
|
|
$(T4 reversed, Yes, `slice.reversed!(0, 2)`, `slice.reversed(0, 2)`)
|
|
)
|
|
|
|
Bifacial interface of $(LREF drop), $(LREF dropBack)
|
|
$(LREF dropExactly), and $(LREF dropBackExactly)
|
|
is identical to that of $(LREF strided).
|
|
|
|
Bifacial interface of $(LREF dropOne) and $(LREF dropBackOne)
|
|
is identical to that of $(LREF reversed).
|
|
|
|
License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
|
|
Authors: Ilya Yaroshenko
|
|
|
|
Source: $(PHOBOSSRC std/_experimental/_ndslice/_iteration.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.iteration;
|
|
|
|
import std.traits;
|
|
|
|
import std.experimental.ndslice.internal;
|
|
import std.experimental.ndslice.slice; //: Slice;
|
|
|
|
private enum _swappedCode = q{
|
|
with (slice)
|
|
{
|
|
auto tl = _lengths[dimensionA];
|
|
auto ts = _strides[dimensionA];
|
|
_lengths[dimensionA] = _lengths[dimensionB];
|
|
_strides[dimensionA] = _strides[dimensionB];
|
|
_lengths[dimensionB] = tl;
|
|
_strides[dimensionB] = ts;
|
|
}
|
|
return slice;
|
|
};
|
|
|
|
/++
|
|
Swaps two dimensions.
|
|
|
|
Params:
|
|
slice = input slice
|
|
dimensionA = first dimension
|
|
dimensionB = second dimension
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
See_also: $(LREF everted), $(LREF transposed)
|
|
+/
|
|
template swapped(size_t dimensionA, size_t dimensionB)
|
|
{
|
|
auto swapped(size_t N, Range)(Slice!(N, Range) slice)
|
|
{
|
|
{
|
|
enum i = 0;
|
|
alias dimension = dimensionA;
|
|
mixin DimensionCTError;
|
|
}
|
|
{
|
|
enum i = 1;
|
|
alias dimension = dimensionB;
|
|
mixin DimensionCTError;
|
|
}
|
|
mixin (_swappedCode);
|
|
}
|
|
}
|
|
|
|
/// ditto
|
|
Slice!(N, Range) swapped(size_t N, Range)(Slice!(N, Range) slice, size_t dimensionA, size_t dimensionB)
|
|
in{
|
|
{
|
|
alias dimension = dimensionA;
|
|
mixin (DimensionRTError);
|
|
}
|
|
{
|
|
alias dimension = dimensionB;
|
|
mixin (DimensionRTError);
|
|
}
|
|
}
|
|
body
|
|
{
|
|
mixin (_swappedCode);
|
|
}
|
|
|
|
/// ditto
|
|
Slice!(2, Range) swapped(Range)(Slice!(2, Range) slice)
|
|
body
|
|
{
|
|
return slice.swapped!(0, 1);
|
|
}
|
|
|
|
/// Template
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(10000.iota
|
|
.sliced(3, 4, 5, 6)
|
|
.swapped!(3, 1)
|
|
.shape == cast(size_t[4])[3, 6, 5, 4]);
|
|
}
|
|
|
|
/// Function
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(10000.iota
|
|
.sliced(3, 4, 5, 6)
|
|
.swapped(1, 3)
|
|
.shape == cast(size_t[4])[3, 6, 5, 4]);
|
|
}
|
|
|
|
/// 2D
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(10000.iota
|
|
.sliced(3, 4)
|
|
.swapped
|
|
.shape == cast(size_t[2])[4, 3]);
|
|
}
|
|
|
|
private enum _rotatedCode = q{
|
|
k &= 0b11;
|
|
if (k == 0)
|
|
return slice;
|
|
if (k == 2)
|
|
return slice.allReversed;
|
|
static if (__traits(compiles, { enum _enum = dimensionA + dimensionB; }))
|
|
{
|
|
slice = slice.swapped!(dimensionA, dimensionB);
|
|
if (k == 1)
|
|
return slice.reversed!dimensionA;
|
|
else
|
|
return slice.reversed!dimensionB;
|
|
}
|
|
else
|
|
{
|
|
slice = slice.swapped (dimensionA, dimensionB);
|
|
if (k == 1)
|
|
return slice.reversed(dimensionA);
|
|
else
|
|
return slice.reversed(dimensionB);
|
|
}
|
|
};
|
|
|
|
/++
|
|
Rotates two selected dimensions by `k*90` degrees.
|
|
The order of dimensions is important.
|
|
If the slice has two dimensions, the default direction is counterclockwise.
|
|
|
|
Params:
|
|
slice = input slice
|
|
dimensionA = first dimension
|
|
dimensionB = second dimension
|
|
k = rotation counter, can be negative
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
template rotated(size_t dimensionA, size_t dimensionB)
|
|
{
|
|
auto rotated(size_t N, Range)(Slice!(N, Range) slice, sizediff_t k = 1)
|
|
{
|
|
{
|
|
enum i = 0;
|
|
alias dimension = dimensionA;
|
|
mixin DimensionCTError;
|
|
}
|
|
{
|
|
enum i = 1;
|
|
alias dimension = dimensionB;
|
|
mixin DimensionCTError;
|
|
}
|
|
mixin (_rotatedCode);
|
|
}
|
|
}
|
|
|
|
/// ditto
|
|
Slice!(N, Range) rotated(size_t N, Range)(Slice!(N, Range) slice, size_t dimensionA, size_t dimensionB, sizediff_t k = 1)
|
|
in{
|
|
{
|
|
alias dimension = dimensionA;
|
|
mixin (DimensionRTError);
|
|
}
|
|
{
|
|
alias dimension = dimensionB;
|
|
mixin (DimensionRTError);
|
|
}
|
|
}
|
|
body
|
|
{
|
|
mixin (_rotatedCode);
|
|
}
|
|
|
|
/// ditto
|
|
Slice!(2, Range) rotated(Range)(Slice!(2, Range) slice, sizediff_t k = 1)
|
|
body
|
|
{
|
|
return slice.rotated!(0, 1)(k);
|
|
}
|
|
|
|
/// Template
|
|
@safe pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
auto slice = 10.iota.sliced(2, 3);
|
|
|
|
auto a = [[0, 1, 2],
|
|
[3, 4, 5]];
|
|
|
|
auto b = [[2, 5],
|
|
[1, 4],
|
|
[0, 3]];
|
|
|
|
auto c = [[5, 4, 3],
|
|
[2, 1, 0]];
|
|
|
|
auto d = [[3, 0],
|
|
[4, 1],
|
|
[5, 2]];
|
|
|
|
assert(slice.rotated ( 4) == a);
|
|
assert(slice.rotated!(0, 1)(-4) == a);
|
|
assert(slice.rotated (1, 0, 8) == a);
|
|
|
|
assert(slice.rotated == b);
|
|
assert(slice.rotated!(0, 1)(-3) == b);
|
|
assert(slice.rotated (1, 0, 3) == b);
|
|
|
|
assert(slice.rotated ( 6) == c);
|
|
assert(slice.rotated!(0, 1)( 2) == c);
|
|
assert(slice.rotated (0, 1, -2) == c);
|
|
|
|
assert(slice.rotated ( 7) == d);
|
|
assert(slice.rotated!(0, 1)( 3) == d);
|
|
assert(slice.rotated (1, 0, ) == d);
|
|
}
|
|
|
|
/++
|
|
Reverses the order of dimensions.
|
|
|
|
Params:
|
|
slice = input slice
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
See_also: $(LREF swapped), $(LREF transposed)
|
|
+/
|
|
Slice!(N, Range) everted(size_t N, Range)(auto ref Slice!(N, Range) slice)
|
|
{
|
|
mixin _DefineRet;
|
|
with (slice)
|
|
{
|
|
foreach (i; Iota!(0, N))
|
|
{
|
|
ret._lengths[N - 1 - i] = _lengths[i];
|
|
ret._strides[N - 1 - i] = _strides[i];
|
|
}
|
|
foreach (i; Iota!(N, PureN))
|
|
{
|
|
ret._lengths[i] = _lengths[i];
|
|
ret._strides[i] = _strides[i];
|
|
}
|
|
ret._ptr = _ptr;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(1000.iota
|
|
.sliced(3, 4, 5)
|
|
.everted
|
|
.shape == cast(size_t[3])[5, 4, 3]);
|
|
}
|
|
|
|
private enum _transposedCode = q{
|
|
mixin _DefineRet;
|
|
with (slice)
|
|
{
|
|
foreach (i; Iota!(0, N))
|
|
{
|
|
ret._lengths[i] = _lengths[perm[i]];
|
|
ret._strides[i] = _strides[perm[i]];
|
|
}
|
|
foreach (i; Iota!(N, PureN))
|
|
{
|
|
ret._lengths[i] = _lengths[i];
|
|
ret._strides[i] = _strides[i];
|
|
}
|
|
ret._ptr = _ptr;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
private size_t[N] completeTranspose(size_t N)(in size_t[] dimensions)
|
|
{
|
|
assert(dimensions.length <= N);
|
|
size_t[N] ctr;
|
|
uint[N] mask;
|
|
foreach (i, ref dimension; dimensions)
|
|
{
|
|
mask[dimension] = true;
|
|
ctr[i] = dimension;
|
|
}
|
|
size_t j = dimensions.length;
|
|
foreach (i, e; mask)
|
|
if (e == false)
|
|
ctr[j++] = i;
|
|
return ctr;
|
|
}
|
|
|
|
/++
|
|
N-dimensional transpose operator.
|
|
Brings selected dimensions to the first position.
|
|
Params:
|
|
slice = input slice
|
|
Dimensions = indexes of dimensions to be brought to the first position
|
|
dimensions = indexes of dimensions to be brought to the first position
|
|
dimension = index of dimension to be brought to the first position
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
See_also: $(LREF swapped), $(LREF everted)
|
|
+/
|
|
template transposed(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
Slice!(N, Range) transposed(size_t N, Range)(auto ref Slice!(N, Range) slice)
|
|
{
|
|
mixin DimensionsCountCTError;
|
|
foreach (i, dimension; Dimensions)
|
|
mixin DimensionCTError;
|
|
static assert(isValidPartialPermutation!N([Dimensions]),
|
|
"Failed to complete permutation of dimensions " ~ Dimensions.stringof
|
|
~ tailErrorMessage!());
|
|
enum perm = completeTranspose!N([Dimensions]);
|
|
static assert(perm.isPermutation, __PRETTY_FUNCTION__ ~ ": internal error.");
|
|
mixin (_transposedCode);
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) transposed(size_t N, Range)(auto ref Slice!(N, Range) slice, size_t dimension)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
size_t[1] permutation = void;
|
|
permutation[0] = dimension;
|
|
immutable perm = completeTranspose!N(permutation);
|
|
assert(perm.isPermutation, __PRETTY_FUNCTION__ ~ ": internal error.");
|
|
mixin (_transposedCode);
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) transposed(size_t N, Range)(auto ref Slice!(N, Range) slice, in size_t[] dimensions...)
|
|
in
|
|
{
|
|
mixin (DimensionsCountRTError);
|
|
foreach (dimension; dimensions)
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
assert(dimensions.isValidPartialPermutation!N,
|
|
"Failed to complete permutation of dimensions."
|
|
~ tailErrorMessage!());
|
|
immutable perm = completeTranspose!N(dimensions);
|
|
assert(perm.isPermutation, __PRETTY_FUNCTION__ ~ ": internal error.");
|
|
mixin (_transposedCode);
|
|
}
|
|
|
|
///ditto
|
|
Slice!(2, Range) transposed(Range)(auto ref Slice!(2, Range) slice)
|
|
{
|
|
return .transposed!(1, 0)(slice);
|
|
}
|
|
|
|
/// Template
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(100000.iota
|
|
.sliced(3, 4, 5, 6, 7)
|
|
.transposed!(4, 1, 0)
|
|
.shape == cast(size_t[5])[7, 4, 3, 5, 6]);
|
|
}
|
|
|
|
/// Function
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(100000.iota
|
|
.sliced(3, 4, 5, 6, 7)
|
|
.transposed(4, 1, 0)
|
|
.shape == cast(size_t[5])[7, 4, 3, 5, 6]);
|
|
}
|
|
|
|
/// Single-argument function
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(100000.iota
|
|
.sliced(3, 4, 5, 6, 7)
|
|
.transposed(4)
|
|
.shape == cast(size_t[5])[7, 3, 4, 5, 6]);
|
|
}
|
|
|
|
/// `2`-dimensional transpose
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota;
|
|
assert(100.iota
|
|
.sliced(3, 4)
|
|
.transposed
|
|
.shape == cast(size_t[2])[4, 3]);
|
|
}
|
|
|
|
private enum _reversedCode = q{
|
|
with (slice)
|
|
{
|
|
if (_lengths[dimension])
|
|
_ptr += _strides[dimension] * (_lengths[dimension] - 1);
|
|
_strides[dimension] = -_strides[dimension];
|
|
}
|
|
};
|
|
|
|
/++
|
|
Reverses the direction of iteration for all dimensions.
|
|
Params:
|
|
slice = input slice
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
Slice!(N, Range) allReversed(size_t N, Range)(Slice!(N, Range) slice)
|
|
{
|
|
foreach (dimension; Iota!(0, N))
|
|
{
|
|
mixin (_reversedCode);
|
|
}
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5).allReversed;
|
|
auto b = 20.iota.retro.sliced(4, 5);
|
|
assert(a == b);
|
|
}
|
|
|
|
/++
|
|
Reverses the direction of iteration for selected dimensions.
|
|
|
|
Params:
|
|
slice = input slice
|
|
Dimensions = indexes of dimensions to reverse order of iteration
|
|
dimensions = indexes of dimensions to reverse order of iteration
|
|
dimension = index of dimension to reverse order of iteration
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
template reversed(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
auto reversed(size_t N, Range)(Slice!(N, Range) slice)
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
mixin (_reversedCode);
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) reversed(size_t N, Range)(Slice!(N, Range) slice, size_t dimension)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
mixin (_reversedCode);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) reversed(size_t N, Range)(Slice!(N, Range) slice, in size_t[] dimensions...)
|
|
in
|
|
{
|
|
foreach (dimension; dimensions)
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
foreach (dimension; dimensions)
|
|
mixin (_reversedCode);
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice = [1, 2, 3, 4].sliced(2, 2);
|
|
assert(slice == [[1, 2], [3, 4]]);
|
|
|
|
// Template
|
|
assert(slice.reversed! 0 == [[3, 4], [1, 2]]);
|
|
assert(slice.reversed! 1 == [[2, 1], [4, 3]]);
|
|
assert(slice.reversed!(0, 1) == [[4, 3], [2, 1]]);
|
|
assert(slice.reversed!(1, 0) == [[4, 3], [2, 1]]);
|
|
assert(slice.reversed!(1, 1) == [[1, 2], [3, 4]]);
|
|
assert(slice.reversed!(0, 0, 0) == [[3, 4], [1, 2]]);
|
|
|
|
// Function
|
|
assert(slice.reversed (0) == [[3, 4], [1, 2]]);
|
|
assert(slice.reversed (1) == [[2, 1], [4, 3]]);
|
|
assert(slice.reversed (0, 1) == [[4, 3], [2, 1]]);
|
|
assert(slice.reversed (1, 0) == [[4, 3], [2, 1]]);
|
|
assert(slice.reversed (1, 1) == [[1, 2], [3, 4]]);
|
|
assert(slice.reversed (0, 0, 0) == [[3, 4], [1, 2]]);
|
|
}
|
|
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.selection;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota, retro, chain;
|
|
auto i0 = iota(0, 4); auto r0 = i0.retro;
|
|
auto i1 = iota(4, 8); auto r1 = i1.retro;
|
|
auto i2 = iota(8, 12); auto r2 = i2.retro;
|
|
auto slice = 100.iota.sliced(3, 4);
|
|
assert(slice .byElement.equal(chain(i0, i1, i2)));
|
|
// Template
|
|
assert(slice.reversed!(0) .byElement.equal(chain(i2, i1, i0)));
|
|
assert(slice.reversed!(1) .byElement.equal(chain(r0, r1, r2)));
|
|
assert(slice.reversed!(0, 1) .byElement.equal(chain(r2, r1, r0)));
|
|
assert(slice.reversed!(1, 0) .byElement.equal(chain(r2, r1, r0)));
|
|
assert(slice.reversed!(1, 1) .byElement.equal(chain(i0, i1, i2)));
|
|
assert(slice.reversed!(0, 0, 0).byElement.equal(chain(i2, i1, i0)));
|
|
// Function
|
|
assert(slice.reversed (0) .byElement.equal(chain(i2, i1, i0)));
|
|
assert(slice.reversed (1) .byElement.equal(chain(r0, r1, r2)));
|
|
assert(slice.reversed (0, 1) .byElement.equal(chain(r2, r1, r0)));
|
|
assert(slice.reversed (1, 0) .byElement.equal(chain(r2, r1, r0)));
|
|
assert(slice.reversed (1, 1) .byElement.equal(chain(i0, i1, i2)));
|
|
assert(slice.reversed (0, 0, 0).byElement.equal(chain(i2, i1, i0)));
|
|
}
|
|
|
|
private enum _stridedCode = q{
|
|
assert(factor > 0, "factor must be positive"
|
|
~ tailErrorMessage!());
|
|
immutable rem = slice._lengths[dimension] % factor;
|
|
slice._lengths[dimension] /= factor;
|
|
if (slice._lengths[dimension]) //do not remove `if (...)`
|
|
slice._strides[dimension] *= factor;
|
|
if (rem)
|
|
slice._lengths[dimension]++;
|
|
};
|
|
|
|
/++
|
|
Multiplies the stride of the selected dimension by the factor.
|
|
|
|
Params:
|
|
slice = input slice
|
|
Dimensions = indexes of dimensions to be strided
|
|
dimensions = indexes of dimensions to be strided
|
|
factors = list of step extension factors
|
|
factor = step extension factors
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
template strided(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
auto strided(size_t N, Range)(Slice!(N, Range) slice, Repeat!(size_t, Dimensions.length) factors)
|
|
body
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
immutable factor = factors[i];
|
|
mixin (_stridedCode);
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) strided(size_t N, Range)(Slice!(N, Range) slice, size_t dimension, size_t factor)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
mixin (_stridedCode);
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
auto slice
|
|
= [0,1,2,3, 4,5,6,7, 8,9,10,11].sliced(3, 4);
|
|
|
|
assert(slice
|
|
== [[0,1,2,3], [4,5,6,7], [8,9,10,11]]);
|
|
|
|
// Template
|
|
assert(slice.strided!0(2)
|
|
== [[0,1,2,3], [8,9,10,11]]);
|
|
|
|
assert(slice.strided!1(3)
|
|
== [[0, 3], [4, 7], [8, 11]]);
|
|
|
|
assert(slice.strided!(0, 1)(2, 3)
|
|
== [[0, 3], [8, 11]]);
|
|
|
|
// Function
|
|
assert(slice.strided(0, 2)
|
|
== [[0,1,2,3], [8,9,10,11]]);
|
|
|
|
assert(slice.strided(1, 3)
|
|
== [[0, 3], [4, 7], [8, 11]]);
|
|
|
|
assert(slice.strided(0, 2).strided(1, 3)
|
|
== [[0, 3], [8, 11]]);
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.range: iota;
|
|
static assert(1000.iota.sliced(13, 40).strided!(0, 1)(2, 5).shape == [7, 8]);
|
|
static assert(100.iota.sliced(93).strided!(0, 0)(7, 3).shape == [5]);
|
|
}
|
|
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.experimental.ndslice.selection;
|
|
import std.algorithm.comparison: equal;
|
|
import std.range: iota, stride, chain;
|
|
auto i0 = iota(0, 4); auto s0 = i0.stride(3);
|
|
auto i1 = iota(4, 8); auto s1 = i1.stride(3);
|
|
auto i2 = iota(8, 12); auto s2 = i2.stride(3);
|
|
auto slice = 100.iota.sliced(3, 4);
|
|
assert(slice .byElement.equal(chain(i0, i1, i2)));
|
|
// Template
|
|
assert(slice.strided!0(2) .byElement.equal(chain(i0, i2)));
|
|
assert(slice.strided!1(3) .byElement.equal(chain(s0, s1, s2)));
|
|
assert(slice.strided!(0, 1)(2, 3).byElement.equal(chain(s0, s2)));
|
|
// Function
|
|
assert(slice.strided(0, 2).byElement.equal(chain(i0, i2)));
|
|
assert(slice.strided(1, 3).byElement.equal(chain(s0, s1, s2)));
|
|
assert(slice.strided(0, 2).strided(1, 3).byElement.equal(chain(s0, s2)));
|
|
}
|
|
|
|
/++
|
|
Convenience function which calls `slice.popFront!dimension()` for each dimension and returns the slice.
|
|
|
|
`allDropBackOne` provides the same functionality but calls `slice.popBack!dimension()` instead.
|
|
|
|
Params:
|
|
slice = input slice
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
Slice!(N, Range) allDropOne(size_t N, Range)(Slice!(N, Range) slice)
|
|
{
|
|
foreach (dimension; Iota!(0, N))
|
|
slice.popFront!dimension;
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) allDropBackOne(size_t N, Range)(Slice!(N, Range) slice)
|
|
{
|
|
foreach (dimension; Iota!(0, N))
|
|
slice.popBack!dimension;
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5);
|
|
|
|
assert(a.allDropOne[0, 0] == 6);
|
|
assert(a.allDropOne.shape == cast(size_t[2])[3, 4]);
|
|
assert(a.allDropBackOne[$ - 1, $ - 1] == 13);
|
|
assert(a.allDropBackOne.shape == cast(size_t[2])[3, 4]);
|
|
}
|
|
|
|
/++
|
|
These functions are similar to `allDrop` and `allDropBack` but they call
|
|
`slice.popFrontExactly!dimension(n)` and `slice.popBackExactly!dimension(n)` instead.
|
|
|
|
Note:
|
|
Unlike `allDrop`, `allDropExactly(n)` assume that the slice holds
|
|
a multi-dimensional cube with a size of at least n.
|
|
This makes `allDropExactly` faster than `allDrop`.
|
|
Only use `allDropExactly` when it is guaranteed that the slice holds
|
|
a multi-dimensional cube with a size of at least n.
|
|
|
|
Params:
|
|
slice = input slice
|
|
n = number of elements to drop
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
Slice!(N, Range) allDropExactly(size_t N, Range)(Slice!(N, Range) slice, size_t n)
|
|
{
|
|
foreach (dimension; Iota!(0, N))
|
|
slice.popFrontExactly!dimension(n);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) allDropBackExactly(size_t N, Range)(Slice!(N, Range) slice, size_t n)
|
|
{
|
|
foreach (dimension; Iota!(0, N))
|
|
slice.popBackExactly!dimension(n);
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5);
|
|
|
|
assert(a.allDropExactly(2)[0, 0] == 12);
|
|
assert(a.allDropExactly(2).shape == cast(size_t[2])[2, 3]);
|
|
assert(a.allDropBackExactly(2)[$ - 1, $ - 1] == 7);
|
|
assert(a.allDropBackExactly(2).shape == cast(size_t[2])[2, 3]);
|
|
}
|
|
|
|
/++
|
|
Convenience function which calls `slice.popFrontN!dimension(n)` for each dimension and returns the slice.
|
|
|
|
`allDropBack` provides the same functionality but calls `slice.popBackN!dimension(n)` instead.
|
|
|
|
Note:
|
|
`allDrop` and `allDropBack` remove up to n elements and stop when the slice is empty.
|
|
|
|
Params:
|
|
slice = input slice
|
|
n = number of elements to drop
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
Slice!(N, Range) allDrop(size_t N, Range)(Slice!(N, Range) slice, size_t n)
|
|
{
|
|
foreach (dimension; Iota!(0, N))
|
|
slice.popFrontN!dimension(n);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) allDropBack(size_t N, Range)(Slice!(N, Range) slice, size_t n)
|
|
{
|
|
foreach (dimension; Iota!(0, N))
|
|
slice.popBackN!dimension(n);
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5);
|
|
|
|
assert(a.allDrop(2)[0, 0] == 12);
|
|
assert(a.allDrop(2).shape == cast(size_t[2])[2, 3]);
|
|
assert(a.allDropBack(2)[$ - 1, $ - 1] == 7);
|
|
assert(a.allDropBack(2).shape == cast(size_t[2])[2, 3]);
|
|
|
|
assert(a.allDrop (5).shape == cast(size_t[2])[0, 0]);
|
|
assert(a.allDropBack(5).shape == cast(size_t[2])[0, 0]);
|
|
}
|
|
|
|
/++
|
|
Convenience function which calls `slice.popFront!dimension()` for selected dimensions and returns the slice.
|
|
|
|
`dropBackOne` provides the same functionality but calls `slice.popBack!dimension()` instead.
|
|
|
|
Params:
|
|
slice = input slice
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
template dropOne(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
Slice!(N, Range) dropOne(size_t N, Range)(Slice!(N, Range) slice)
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
slice.popFront!dimension;
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) dropOne(size_t N, Range)(Slice!(N, Range) slice, size_t dimension)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
slice.popFront(dimension);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) dropOne(size_t N, Range)(Slice!(N, Range) slice, in size_t[] dimensions...)
|
|
in
|
|
{
|
|
foreach (dimension; dimensions)
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
foreach (dimension; dimensions)
|
|
slice.popFront(dimension);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
template dropBackOne(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
Slice!(N, Range) dropBackOne(size_t N, Range)(Slice!(N, Range) slice)
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
slice.popBack!dimension;
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) dropBackOne(size_t N, Range)(Slice!(N, Range) slice, size_t dimension)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
slice.popBack(dimension);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) dropBackOne(size_t N, Range)(Slice!(N, Range) slice, in size_t[] dimensions...)
|
|
in
|
|
{
|
|
foreach (dimension; dimensions)
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
foreach (dimension; dimensions)
|
|
slice.popBack(dimension);
|
|
return slice;
|
|
}
|
|
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5);
|
|
|
|
assert(a.dropOne!(1, 0)[0, 0] == 6);
|
|
assert(a.dropOne (1, 0)[0, 0] == 6);
|
|
assert(a.dropOne!(1, 0).shape == cast(size_t[2])[3, 4]);
|
|
assert(a.dropOne (1, 0).shape == cast(size_t[2])[3, 4]);
|
|
assert(a.dropBackOne!(1, 0)[$ - 1, $ - 1] == 13);
|
|
assert(a.dropBackOne (1, 0)[$ - 1, $ - 1] == 13);
|
|
assert(a.dropBackOne!(1, 0).shape == cast(size_t[2])[3, 4]);
|
|
assert(a.dropBackOne (1, 0).shape == cast(size_t[2])[3, 4]);
|
|
|
|
assert(a.dropOne!(0, 0)[0, 0] == 10);
|
|
assert(a.dropOne (0, 0)[0, 0] == 10);
|
|
assert(a.dropOne!(0, 0).shape == cast(size_t[2])[2, 5]);
|
|
assert(a.dropOne (0, 0).shape == cast(size_t[2])[2, 5]);
|
|
assert(a.dropBackOne!(1, 1)[$ - 1, $ - 1] == 17);
|
|
assert(a.dropBackOne (1, 1)[$ - 1, $ - 1] == 17);
|
|
assert(a.dropBackOne!(1, 1).shape == cast(size_t[2])[4, 3]);
|
|
assert(a.dropBackOne (1, 1).shape == cast(size_t[2])[4, 3]);
|
|
}
|
|
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5);
|
|
|
|
assert(a.dropOne(0).dropOne(0)[0, 0] == 10);
|
|
assert(a.dropOne(0).dropOne(0).shape == cast(size_t[2])[2, 5]);
|
|
assert(a.dropBackOne(1).dropBackOne(1)[$ - 1, $ - 1] == 17);
|
|
assert(a.dropBackOne(1).dropBackOne(1).shape == cast(size_t[2])[4, 3]);
|
|
}
|
|
|
|
|
|
/++
|
|
These functions are similar to `drop` and `dropBack` but they call
|
|
`slice.popFrontExactly!dimension(n)` and `slice.popBackExactly!dimension(n)` instead.
|
|
|
|
Note:
|
|
Unlike `drop`, `dropExactly` assumes that the slice holds enough elements in
|
|
the selected dimension.
|
|
This makes `dropExactly` faster than `drop`.
|
|
|
|
Params:
|
|
slice = input slice
|
|
ns = list of numbers of elements to drop
|
|
n = number of elements to drop
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
template dropExactly(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
Slice!(N, Range) dropExactly(size_t N, Range)(Slice!(N, Range) slice, Repeat!(size_t, Dimensions.length) ns)
|
|
body
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
slice.popFrontExactly!dimension(ns[i]);
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) dropExactly(size_t N, Range)(Slice!(N, Range) slice, size_t dimension, size_t n)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
slice.popFrontExactly(dimension, n);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
template dropBackExactly(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
Slice!(N, Range) dropBackExactly(size_t N, Range)(Slice!(N, Range) slice, Repeat!(size_t, Dimensions.length) ns)
|
|
body
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
slice.popBackExactly!dimension(ns[i]);
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) dropBackExactly(size_t N, Range)(Slice!(N, Range) slice, size_t dimension, size_t n)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
slice.popBackExactly(dimension, n);
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5);
|
|
|
|
assert(a.dropExactly !(1, 0)(2, 3)[0, 0] == 17);
|
|
assert(a.dropExactly !(1, 0)(2, 3).shape == cast(size_t[2])[1, 3]);
|
|
assert(a.dropBackExactly!(0, 1)(2, 3)[$ - 1, $ - 1] == 6);
|
|
assert(a.dropBackExactly!(0, 1)(2, 3).shape == cast(size_t[2])[2, 2]);
|
|
|
|
assert(a.dropExactly(1, 2).dropExactly(0, 3)[0, 0] == 17);
|
|
assert(a.dropExactly(1, 2).dropExactly(0, 3).shape == cast(size_t[2])[1, 3]);
|
|
assert(a.dropBackExactly(0, 2).dropBackExactly(1, 3)[$ - 1, $ - 1] == 6);
|
|
assert(a.dropBackExactly(0, 2).dropBackExactly(1, 3).shape == cast(size_t[2])[2, 2]);
|
|
}
|
|
|
|
/++
|
|
Convenience function which calls `slice.popFrontN!dimension(n)` for the selected
|
|
dimension and returns the slice.
|
|
|
|
`dropBack` provides the same functionality but calls `slice.popBackN!dimension(n)` instead.
|
|
|
|
Note:
|
|
`drop` and `dropBack` remove up to n elements and stop when the slice is empty.
|
|
|
|
Params:
|
|
slice = input slice
|
|
ns = list of numbers of elements to drop
|
|
n = number of elements to drop
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
template drop(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
Slice!(N, Range) drop(size_t N, Range)(Slice!(N, Range) slice, Repeat!(size_t, Dimensions.length) ns)
|
|
body
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
slice.popFrontN!dimension(ns[i]);
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) drop(size_t N, Range)(Slice!(N, Range) slice, size_t dimension, size_t n)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
slice.popFrontN(dimension, n);
|
|
return slice;
|
|
}
|
|
|
|
///ditto
|
|
template dropBack(Dimensions...)
|
|
if (Dimensions.length)
|
|
{
|
|
Slice!(N, Range) dropBack(size_t N, Range)(Slice!(N, Range) slice, Repeat!(size_t, Dimensions.length) ns)
|
|
body
|
|
{
|
|
foreach (i, dimension; Dimensions)
|
|
{
|
|
mixin DimensionCTError;
|
|
slice.popBackN!dimension(ns[i]);
|
|
}
|
|
return slice;
|
|
}
|
|
}
|
|
|
|
///ditto
|
|
Slice!(N, Range) dropBack(size_t N, Range)(Slice!(N, Range) slice, size_t dimension, size_t n)
|
|
in
|
|
{
|
|
mixin (DimensionRTError);
|
|
}
|
|
body
|
|
{
|
|
slice.popBackN(dimension, n);
|
|
return slice;
|
|
}
|
|
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
auto a = 20.iota.sliced(4, 5);
|
|
|
|
assert(a.drop !(1, 0)(2, 3)[0, 0] == 17);
|
|
assert(a.drop !(1, 0)(2, 3).shape == cast(size_t[2])[1, 3]);
|
|
assert(a.dropBack!(0, 1)(2, 3)[$ - 1, $ - 1] == 6);
|
|
assert(a.dropBack!(0, 1)(2, 3).shape == cast(size_t[2])[2, 2]);
|
|
assert(a.dropBack!(0, 1)(5, 5).shape == cast(size_t[2])[0, 0]);
|
|
|
|
|
|
assert(a.drop(1, 2).drop(0, 3)[0, 0] == 17);
|
|
assert(a.drop(1, 2).drop(0, 3).shape == cast(size_t[2])[1, 3]);
|
|
assert(a.dropBack(0, 2).dropBack(1, 3)[$ - 1, $ - 1] == 6);
|
|
assert(a.dropBack(0, 2).dropBack(1, 3).shape == cast(size_t[2])[2, 2]);
|
|
assert(a.dropBack(0, 5).dropBack(1, 5).shape == cast(size_t[2])[0, 0]);
|
|
}
|
|
|
|
/++
|
|
Returns maximal multidimensional cube.
|
|
|
|
Params:
|
|
slice = input slice
|
|
Returns:
|
|
n-dimensional slice of the same type
|
|
+/
|
|
Slice!(N, Range) dropToHypercube(size_t N, Range)(Slice!(N, Range) slice)
|
|
body
|
|
{
|
|
size_t length = slice._lengths[0];
|
|
foreach (i; Iota!(1, N))
|
|
if (length > slice._lengths[i])
|
|
length = slice._lengths[i];
|
|
foreach (i; Iota!(0, N))
|
|
slice._lengths[i] = length;
|
|
return slice;
|
|
}
|
|
|
|
///
|
|
@safe @nogc pure nothrow unittest
|
|
{
|
|
import std.experimental.ndslice.slice;
|
|
import std.range: iota, retro;
|
|
assert(1000.iota
|
|
.sliced(5, 3, 6, 7)
|
|
.dropToHypercube
|
|
.shape == cast(size_t[4])[3, 3, 3, 3]);
|
|
}
|