Remove std.experimental.ndslice (#5187)

Remove std.experimental.ndslice
merged-on-behalf-of: Sebastian Wilzbach <sebi.wilzbach@gmail.com>
This commit is contained in:
Sebastian Wilzbach 2017-03-02 09:02:45 +01:00 committed by The Dlang Bot
parent 023f525b83
commit d6d6831d3b
10 changed files with 10 additions and 8287 deletions

View file

@ -0,0 +1,5 @@
`std.experimental.ndslice` has been removed
The synchronization between Phobos and Mir turned out to be a high amount of work with litte gain.
Users of `std.experimental.ndslice` are advised to switch to the upstream
$(LINK2 https://github.com/libmir/mir-algorithm, mir package).

View file

@ -32,10 +32,6 @@ $(BOOKTABLE ,
comparison, iteration, sorting, set operations, and mutation.
)
)
$(TR
$(TDNW $(LINK2 std_experimental_ndslice.html, std.experimental.ndslice))
$(TD Multidimensional random access ranges and arrays.)
)
$(LEADINGROW Array manipulation)
$(TR
$(TDNW
@ -326,10 +322,6 @@ $(BOOKTABLE ,
$(TDNW $(LINK2 std_mathspecial.html, std.mathspecial))
$(TD Families of transcendental functions.)
)
$(TR
$(TDNW $(LINK2 std_experimental_ndslice.html, std.experimental.ndslice))
$(TD Multidimensional random access ranges and arrays.)
)
$(TR
$(TDNW $(LINK2 std_numeric.html, std.numeric))
$(TD Floating point numerics functions.)

View file

@ -163,7 +163,6 @@ P2MODULES=$(foreach P,$1,$(addprefix $P/,$(PACKAGE_$(subst /,_,$P))))
STD_PACKAGES = std $(addprefix std/,\
algorithm container digest experimental/allocator \
experimental/allocator/building_blocks experimental/logger \
experimental/ndslice \
net \
experimental range regex)
@ -189,7 +188,6 @@ PACKAGE_std_experimental_allocator_building_blocks = \
fallback_allocator free_list free_tree bitmapped_block \
kernighan_ritchie null_allocator package quantizer \
region scoped_allocator segregator stats_collector
PACKAGE_std_experimental_ndslice = package iteration selection slice
PACKAGE_std_net = curl isemail
PACKAGE_std_range = interfaces package primitives
PACKAGE_std_regex = package $(addprefix internal/,generator ir parser \
@ -214,7 +212,6 @@ EXTRA_DOCUMENTABLES := $(EXTRA_MODULES_LINUX) $(EXTRA_MODULES_WIN32) $(EXTRA_MOD
EXTRA_MODULES_INTERNAL := $(addprefix std/, \
algorithm/internal concurrencybase \
experimental/ndslice/internal \
$(addprefix internal/, \
cstring digest/sha_SSSE3 encodinginit \
$(addprefix math/, biguintcore biguintnoasm biguintx86 \
@ -538,7 +535,7 @@ style: ../dscanner/dsc $(LIB) has_public_example publictests
done
@echo "Check that Ddoc runs without errors"
$(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -w -D -main -c -o- $$(find etc std -type f -name '*.d' | grep -vE 'std/experimental/ndslice/iteration.d') 2>&1 | grep -v "Deprecation:"; test $$? -eq 1
$(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -w -D -main -c -o- $$(find etc std -type f -name '*.d') 2>&1 | grep -v "Deprecation:"; test $$? -eq 1
# at the moment libdparse has problems to parse some modules (->excludes)
@echo "Running DScanner"
@ -547,14 +544,14 @@ style: ../dscanner/dsc $(LIB) has_public_example publictests
publictests: $(LIB)
# parse all public unittests from Phobos and runs them (for now some modules are excluded)
rm -rf ./out
DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c tests_extractor -- --inputdir . --ignore "base64.d,building_blocks/free_list,building_blocks/quantizer,digest/hmac.d,file.d,index.d,math.d,ndslice/selection.d,stdio.d,traits.d,typecons.d,uuid.d" --outputdir ./out
DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c tests_extractor -- --inputdir . --ignore "base64.d,building_blocks/free_list,building_blocks/quantizer,digest/hmac.d,file.d,index.d,math.d,stdio.d,traits.d,typecons.d,uuid.d" --outputdir ./out
# execute all parsed tests
for file in $$(find out -name '*.d'); do echo "executing $${file}" && $(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -main -unittest -run $$file || exit 1 ; done
has_public_example: $(LIB)
# checks whether public function have public examples (for now some modules are excluded)
rm -rf ./out
DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,ndslice/selection.d,ndslice/slice.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d"
DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d"
.PHONY : auto-tester-build
auto-tester-build: all checkwhitespace

View file

@ -1,521 +0,0 @@
/// @@@DEPRECATED_2017-04@@@
deprecated("Please use mir-algorithm DUB package: http://github.com/libmir/mir-algorithm")
module std.experimental.ndslice.internal;
import std.range.primitives;
import std.traits;
import std.meta;
import std.experimental.ndslice.slice;
/+
fastmath do nothing here,
but remove constraint for LDC that operations for pointer's computations
maybe mixed up with callers computations if both computations has fastmath attribute.
So, it is just a bridge between two fastmath functions.
fmb alias is used to relax users.
+/
package alias fmb = fastmath;
version(LDC)
{
static import ldc.attributes;
alias fastmath = ldc.attributes.fastmath;
}
else
{
alias fastmath = fastmathDummy;
}
enum FastmathDummy { init }
FastmathDummy fastmathDummy() { return FastmathDummy.init; }
template PtrTuple(Names...)
{
@LikePtr struct PtrTuple(Ptrs...)
if (allSatisfy!(isSlicePointer, Ptrs) && Ptrs.length == Names.length)
{
@fmb:
Ptrs ptrs;
void opOpAssign(string op)(sizediff_t shift)
if (op == `+` || op == `-`)
{
foreach (ref ptr; ptrs)
mixin (`ptr ` ~ op ~ `= shift;`);
}
auto opBinary(string op)(sizediff_t shift)
if (op == `+` || op == `-`)
{
auto ret = this;
ret.opOpAssign!op(shift);
return ret;
}
public struct Index
{
Ptrs _ptrs__;
mixin (PtrTupleFrontMembers!Names);
}
auto opIndex(sizediff_t index)
{
auto p = ptrs;
foreach (ref ptr; p)
ptr += index;
return Index(p);
}
}
}
// ISSUE 16501
@system unittest
{
import std.experimental.ndslice;
alias sab = sliced!("a", "b");
auto sl = sab(new double[12], new double[12], 3, 4);
auto psl = sl.pack!1;
}
struct PtrShell(Range)
{
sizediff_t _shift;
Range _range;
@fmb:
enum hasAccessByRef = isPointer!Range ||
__traits(compiles, &_range[0]);
void opOpAssign(string op)(sizediff_t shift)
if (op == `+` || op == `-`)
{
mixin (`_shift ` ~ op ~ `= shift;`);
}
auto opBinary(string op)(sizediff_t shift)
if (op == `+` || op == `-`)
{
mixin (`return typeof(this)(_shift ` ~ op ~ ` shift, _range);`);
}
auto opUnary(string op)()
if (op == `++` || op == `--`)
{
mixin(op ~ `_shift;`);
return this;
}
auto ref opIndex(sizediff_t index)
in
{
assert(_shift + index >= 0);
static if (hasLength!Range)
assert(_shift + index <= _range.length);
}
body
{
return _range[_shift + index];
}
static if (!hasAccessByRef)
{
auto ref opIndexAssign(T)(T value, sizediff_t index)
in
{
assert(_shift + index >= 0);
static if (hasLength!Range)
assert(_shift + index <= _range.length);
}
body
{
return _range[_shift + index] = value;
}
auto ref opIndexOpAssign(string op, T)(T value, sizediff_t index)
in
{
assert(_shift + index >= 0);
static if (hasLength!Range)
assert(_shift + index <= _range.length);
}
body
{
mixin (`return _range[_shift + index] ` ~ op ~ `= value;`);
}
auto ref opIndexUnary(string op)(sizediff_t index)
in
{
assert(_shift + index >= 0);
static if (hasLength!Range)
assert(_shift + index <= _range.length);
}
body
{
mixin (`return ` ~ op ~ `_range[_shift + index];`);
}
}
auto save() @property
{
return this;
}
}
auto ptrShell(Range)(Range range, sizediff_t shift = 0)
{
return PtrShell!Range(shift, range);
}
@safe pure nothrow unittest
{
import std.internal.test.dummyrange;
foreach (RB; AliasSeq!(ReturnBy.Reference, ReturnBy.Value))
{
DummyRange!(RB, Length.Yes, RangeType.Random) range;
range.reinit;
assert(range.length >= 10);
auto ptr = range.ptrShell;
assert(ptr[0] == range[0]);
auto save0 = range[0];
ptr[0] += 10;
++ptr[0];
assert(ptr[0] == save0 + 11);
(ptr + 5)[2] = 333;
assert(range[7] == 333);
auto ptrCopy = ptr.save;
ptrCopy._range.popFront;
ptr[1] = 2;
assert(ptr[0] == save0 + 11);
assert(ptrCopy[0] == 2);
}
}
private template PtrTupleFrontMembers(Names...)
if (Names.length <= 32)
{
static if (Names.length)
{
alias Top = Names[0..$-1];
enum int m = Top.length;
/+
fastmath do nothing here,
but remove constraint for LDC that operations for pointer's computations
maybe mixed up with callers computations if both computations has fastmath attribute.
So, it is just a bridge between two fastmath functions.
+/
enum PtrTupleFrontMembers = PtrTupleFrontMembers!Top
~ "
@fmb @property auto ref " ~ Names[$-1] ~ "() {
return _ptrs__[" ~ m.stringof ~ "][0];
}
static if (!__traits(compiles, &(_ptrs__[" ~ m.stringof ~ "][0])))
@fmb @property auto ref " ~ Names[$-1] ~ "(T)(auto ref T value) {
return _ptrs__[" ~ m.stringof ~ "][0] = value;
}
";
}
else
{
enum PtrTupleFrontMembers = "";
}
}
@LikePtr struct Pack(size_t N, Range)
{
@fmb:
alias Elem = Slice!(N, Range);
alias PureN = Elem.PureN;
alias PureRange = Elem.PureRange;
size_t[PureN] _lengths;
sizediff_t[PureN] _strides;
SlicePtr!PureRange _ptr;
mixin PropagatePtr;
Elem opIndex(size_t index)
{
return Elem(_lengths, _strides, _ptr + index);
}
}
@LikePtr struct Map(Range, alias fun)
{
Range _ptr;
// can not use @fmb here because fun maybe an LLVM function.
auto ref opIndex(size_t index)
{
return fun(_ptr[index]);
}
mixin PropagatePtr;
}
private mixin template PropagatePtr()
{
@fmb void opOpAssign(string op)(sizediff_t shift)
if (op == `+` || op == `-`)
{
mixin (`_ptr ` ~ op ~ `= shift;`);
}
@fmb auto opBinary(string op)(sizediff_t shift)
if (op == `+` || op == `-`)
{
auto ret = this;
ret.opOpAssign!op(shift);
return ret;
}
@fmb auto opUnary(string op)()
if (op == `++` || op == `--`)
{
mixin(op ~ `_ptr;`);
return this;
}
}
struct LikePtr {}
template SlicePtr(Range)
{
static if (hasPtrBehavior!Range)
alias SlicePtr = Range;
else
alias SlicePtr = PtrShell!Range;
}
enum isSlicePointer(T) = isPointer!T || is(T : PtrShell!R, R);
template hasPtrBehavior(T)
{
static if (isPointer!T)
enum hasPtrBehavior = true;
else
static if (!isAggregateType!T)
enum hasPtrBehavior = false;
else
enum hasPtrBehavior = hasUDA!(T, LikePtr);
}
alias RangeOf(T : Slice!(N, Range), size_t N, Range) = Range;
template isMemory(T)
{
static if (isPointer!T)
enum isMemory = true;
else
static if (is(T : Map!(Range, fun), Range, alias fun))
enum isMemory = .isMemory!Range;
else
static if (__traits(compiles, __traits(isSame, PtrTuple, TemplateOf!(TemplateOf!T))))
static if (__traits(isSame, PtrTuple, TemplateOf!(TemplateOf!T)))
enum isMemory = allSatisfy!(.isMemory, TemplateArgsOf!T);
else
enum isMemory = false;
else
enum isMemory = false;
}
@system unittest
{
import std.experimental.ndslice.slice : PtrTuple;
import std.experimental.ndslice.selection : Map;
static assert(isMemory!(int*));
alias R = PtrTuple!("a", "b");
alias F = R!(double*, double*);
static assert(isMemory!F);
static assert(isMemory!(Map!(F, a => a)));
}
enum indexError(size_t pos, size_t N) =
"index at position " ~ pos.stringof
~ " from the range [0 .." ~ N.stringof ~ ")"
~ " must be less than corresponding length.";
enum string tailErrorMessage(
string fun = __FUNCTION__,
string pfun = __PRETTY_FUNCTION__) =
"
- - -
Error in function
" ~ fun ~ "
- - -
Function prototype
" ~ pfun ~ "
_____";
mixin template _DefineRet()
{
alias Ret = typeof(return);
static if (hasElaborateAssign!(Ret.PureRange))
Ret ret;
else
Ret ret = void;
}
mixin template DimensionsCountCTError()
{
static assert(Dimensions.length <= N,
"Dimensions list length = " ~ Dimensions.length.stringof
~ " should be less than or equal to N = " ~ N.stringof
~ tailErrorMessage!());
}
enum DimensionsCountRTError = q{
assert(dimensions.length <= N,
"Dimensions list length should be less than or equal to N = " ~ N.stringof
~ tailErrorMessage!());
};
mixin template DimensionCTError()
{
static assert(dimension >= 0,
"dimension = " ~ dimension.stringof ~ " at position "
~ i.stringof ~ " should be greater than or equal to 0"
~ tailErrorMessage!());
static assert(dimension < N,
"dimension = " ~ dimension.stringof ~ " at position "
~ i.stringof ~ " should be less than N = " ~ N.stringof
~ tailErrorMessage!());
}
enum DimensionRTError = q{
static if (isSigned!(typeof(dimension)))
assert(dimension >= 0, "dimension should be greater than or equal to 0"
~ tailErrorMessage!());
assert(dimension < N, "dimension should be less than N = " ~ N.stringof
~ tailErrorMessage!());
};
private alias IncFront(Seq...) = AliasSeq!(Seq[0] + 1, Seq[1 .. $]);
private alias DecFront(Seq...) = AliasSeq!(Seq[0] - 1, Seq[1 .. $]);
private enum bool isNotZero(alias t) = t != 0;
alias NSeqEvert(Seq...) = Filter!(isNotZero, DecFront!(Reverse!(IncFront!Seq)));
alias Parts(Seq...) = DecAll!(IncFront!Seq);
alias Snowball(Seq...) = AliasSeq!(size_t.init, SnowballImpl!(size_t.init, Seq));
private template SnowballImpl(size_t val, Seq...)
{
static if (Seq.length == 0)
alias SnowballImpl = AliasSeq!();
else
alias SnowballImpl = AliasSeq!(Seq[0] + val, SnowballImpl!(Seq[0] + val, Seq[1 .. $]));
}
private template DecAll(Seq...)
{
static if (Seq.length == 0)
alias DecAll = AliasSeq!();
else
alias DecAll = AliasSeq!(Seq[0] - 1, DecAll!(Seq[1 .. $]));
}
template SliceFromSeq(Range, Seq...)
{
static if (Seq.length == 0)
alias SliceFromSeq = Range;
else
{
import std.experimental.ndslice.slice : Slice;
alias SliceFromSeq = SliceFromSeq!(Slice!(Seq[$ - 1], Range), Seq[0 .. $ - 1]);
}
}
template DynamicArrayDimensionsCount(T)
{
static if (isDynamicArray!T)
enum size_t DynamicArrayDimensionsCount = 1 + DynamicArrayDimensionsCount!(typeof(T.init[0]));
else
enum size_t DynamicArrayDimensionsCount = 0;
}
bool isPermutation(size_t N)(auto ref in size_t[N] perm)
{
int[N] mask;
return isValidPartialPermutationImpl(perm, mask);
}
@system unittest
{
assert(isPermutation([0, 1]));
// all numbers 0 .. N-1 need to be part of the permutation
assert(!isPermutation([1, 2]));
assert(!isPermutation([0, 2]));
// duplicates are not allowed
assert(!isPermutation([0, 1, 1]));
size_t[0] emptyArr;
// empty permutations are not allowed either
assert(!isPermutation(emptyArr));
}
bool isValidPartialPermutation(size_t N)(in size_t[] perm)
{
int[N] mask;
return isValidPartialPermutationImpl(perm, mask);
}
private bool isValidPartialPermutationImpl(size_t N)(in size_t[] perm, ref int[N] mask)
{
if (perm.length == 0)
return false;
foreach (j; perm)
{
if (j >= N)
return false;
if (mask[j]) //duplicate
return false;
mask[j] = true;
}
return true;
}
enum toSize_t(size_t i) = i;
enum isSize_t(alias i) = is(typeof(i) == size_t);
enum isIndex(I) = is(I : size_t);
enum is_Slice(S) = is(S : _Slice);
private enum isReference(P) =
hasIndirections!P
|| isFunctionPointer!P
|| is(P == interface);
enum hasReference(T) = anySatisfy!(isReference, RepresentationTypeTuple!T);
alias ImplicitlyUnqual(T) = Select!(isImplicitlyConvertible!(T, Unqual!T), Unqual!T, T);
//TODO: replace with `static foreach`
template Iota(size_t i, size_t j)
{
static assert(i <= j, "Iota: i should be less than or equal to j");
static if (i == j)
alias Iota = AliasSeq!();
else
alias Iota = AliasSeq!(i, Iota!(i + 1, j));
}
size_t lengthsProduct(size_t N)(auto ref in size_t[N] lengths)
{
size_t length = lengths[0];
foreach (i; Iota!(1, N))
length *= lengths[i];
return length;
}
pure nothrow @system unittest
{
const size_t[3] lengths = [3, 4, 5];
assert(lengthsProduct(lengths) == 60);
assert(lengthsProduct([3, 4, 5]) == 60);
}
struct _Slice { size_t i, j; }

File diff suppressed because it is too large Load diff

View file

@ -1,581 +0,0 @@
/+
## Guide for Slice/BLAS contributors
1. Make sure functions are
a. inlined(!),
b. `@nogc`,
c. `nothrow`,
d. `pure`.
For this reason, it is preferable to use _simple_ `assert`s with messages
that can be computed at compile time.
The goals are:
1. to reduce executable size for _any_ compilation mode
2. to reduce template bloat in object files
3. to reduce compilation time
4. to allow users to write extern C bindings for code libraries on `Slice` type.
2. `std.format`, `std.string`, and `std.conv` should not be used in error
message formatting.`"Use" ~ Concatenation.stringof`.
3. `mixin template`s may be used for pretty error message formatting.
4. `Exception`s/`enforce`s should no be used to check indexes and lengths.
Exceptions are only allowed for algorithms where validation of input data is
too complicated for the user. `reshape` function is a good example of a case
where Exceptions are required.
If a function might throw an exception, an example with exception handing should be added.
5. For simple checks like matrix transposition, compile time flags should not be used.
It is much better to opt for runtime matrix transposition.
Furthermore, Slice type provides runtime matrix transposition out of the box.
6. _Fortran_VS_C_ flags should not be used. They are about notation,
but not about the algorithm itself. For math world users,
a corresponding code example might be included in the documentation.
`transposed` / `everted` can be used in cache-friendly codes.
7. Compile time evaluation should not be used to produce dummy types like `IdentityMatrix`.
8. Memory allocation and algorithm logic should be separated whenever possible.
9. CTFE unittests should be added to new functions.
+/
/**
$(H1 Multidimensional Random Access Ranges)
The package provides a multidimensional array implementation.
It would be well suited to creating machine learning and image
processing algorithms, but should also be general enough for use anywhere with
homogeneously-typed multidimensional data.
In addition, it includes various functions for iteration, accessing, and manipulation.
Advanced and fast iteration algorithms, matrix multiplication, and BLAS-like functions
can be found in the $(LINK2 http://mir.dlang.io, Mir
LLVM-Accelerated Generic Numerical Library for Science and Machine Learning).
Quick_Start:
$(SUBREF slice, sliced) is a function designed to create
a multidimensional view over a range.
Multidimensional view is presented by $(SUBREF slice, Slice) type.
------
auto matrix = slice!double(3, 4);
matrix[] = 0;
matrix.diagonal[] = 1;
auto row = matrix[2];
row[3] = 6;
assert(matrix[2, 3] == 6); // D & C index order
------
Note:
In many examples $(SUBREF selection, iotaSlice) is used
instead of a regular array, which makes it
possible to carry out tests without memory allocation.
$(SCRIPT inhibitQuickIndex = 1;)
$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TH Submodule) $(TH Declarations))
$(TR $(TDNW $(SUBMODULE slice)
$(BR) $(SMALL $(SUBREF slice, Slice), its properties, operator overloading))
$(TD
$(SUBREF slice, as)
$(SUBREF slice, assumeSameStructure)
$(SUBREF slice, DeepElementType)
$(SUBREF slice, makeNdarray)
$(SUBREF slice, makeSlice)
$(SUBREF slice, makeUninitializedSlice)
$(SUBREF slice, ndarray)
$(SUBREF slice, ReplaceArrayWithPointer)
$(SUBREF slice, shape)
$(SUBREF slice, Slice)
$(SUBREF slice, slice)
$(SUBREF slice, sliced)
$(SUBREF slice, SliceException)
$(SUBREF slice, uninitializedSlice)
)
)
$(TR $(TDNW $(SUBMODULE iteration)
$(BR) $(SMALL Basic iteration operators))
$(TD
$(SUBREF iteration, allReversed)
$(SUBREF iteration, everted)
$(SUBREF iteration, reversed)
$(SUBREF iteration, rotated)
$(SUBREF iteration, strided)
$(SUBREF iteration, swapped)
$(SUBREF iteration, transposed)
$(SUBREF iteration, dropToHypercube) and other `drop` primitives
)
)
$(TR $(TDNW $(SUBMODULE selection)
$(BR) $(SMALL Subspace manipulations $(BR) Operators for loop free programming))
$(TD
$(SUBREF selection, blocks)
$(SUBREF selection, byElement)
$(SUBREF selection, byElementInStandardSimplex)
$(SUBREF selection, diagonal)
$(SUBREF selection, evertPack)
$(SUBREF selection, indexSlice)
$(SUBREF selection, iotaSlice)
$(SUBREF selection, mapSlice)
$(SUBREF selection, pack)
$(SUBREF selection, repeatSlice)
$(SUBREF selection, reshape)
$(SUBREF selection, ReshapeException)
$(SUBREF selection, unpack)
$(SUBREF selection, windows)
)
)
))
$(H2 Example: Image Processing)
A median filter is implemented as an example. The function
`movingWindowByChannel` can also be used with other filters that use a sliding
window as the argument, in particular with convolution matrices such as the
$(LINK2 https://en.wikipedia.org/wiki/Sobel_operator, Sobel operator).
`movingWindowByChannel` iterates over an image in sliding window mode.
Each window is transferred to a `filter`, which calculates the value of the
pixel that corresponds to the given window.
This function does not calculate border cases in which a window overlaps
the image partially. However, the function can still be used to carry out such
calculations. That can be done by creating an amplified image, with the edges
reflected from the original image, and then applying the given function to the
new file.
Note: You can find the example at
$(LINK2 https://github.com/libmir/mir/blob/master/examples/median_filter.d, GitHub).
-------
/++
Params:
filter = unary function. Dimension window 2D is the argument.
image = image dimensions `(h, w, c)`,
where с is the number of channels in the image
nr = number of rows in the window
nс = number of columns in the window
Returns:
image dimensions `(h - nr + 1, w - nc + 1, c)`,
where с is the number of channels in the image.
Dense data layout is guaranteed.
+/
Slice!(3, C*) movingWindowByChannel(alias filter, C)
(Slice!(3, C*) image, size_t nr, size_t nc)
{
// 0. 3D
// The last dimension represents the color channel.
return image
// 1. 2D composed of 1D
// Packs the last dimension.
.pack!1
// 2. 2D composed of 2D composed of 1D
// Splits image into overlapping windows.
.windows(nr, nc)
// 3. 5D
// Unpacks the windows.
.unpack
// 4. 5D
// Brings the color channel dimension to the third position.
.transposed!(0, 1, 4)
// 5. 3D Composed of 2D
// Packs the last two dimensions.
.pack!2
// 2D to pixel lazy conversion.
.mapSlice!filter
// Creates the new image. The only memory allocation in this function.
.slice;
}
-------
A function that calculates the value of iterator median is also necessary.
-------
/++
Params:
r = input range
buf = buffer with length no less than the number of elements in `r`
Returns:
median value over the range `r`
+/
T median(Range, T)(Slice!(2, Range) sl, T[] buf)
{
import std.algorithm.sorting : topN;
// copy sl to the buffer
size_t n;
foreach (r; sl)
foreach (e; r)
buf[n++] = e;
buf[0 .. n].topN(n / 2);
return buf[n / 2];
}
-------
The `main` function:
-------
void main(string[] args)
{
import std.conv : to;
import std.getopt : getopt, defaultGetoptPrinter;
import std.path : stripExtension;
uint nr, nc, def = 3;
auto helpInformation = args.getopt(
"nr", "number of rows in window, default value is " ~ def.to!string, &nr,
"nc", "number of columns in window, default value is equal to nr", &nc);
if (helpInformation.helpWanted)
{
defaultGetoptPrinter(
"Usage: median-filter [<options...>] [<file_names...>]\noptions:",
helpInformation.options);
return;
}
if (!nr) nr = def;
if (!nc) nc = nr;
auto buf = new ubyte[nr * nc];
foreach (name; args[1 .. $])
{
import imageformats; // can be found at code.dlang.org
IFImage image = read_image(name);
auto ret = image.pixels
.sliced(cast(size_t) image.h, cast(size_t) image.w, cast(size_t) image.c)
.movingWindowByChannel
!(window => median(window, buf))
(nr, nc);
write_image(
name.stripExtension ~ "_filtered.png",
ret.length!1,
ret.length!0,
(&ret[0, 0, 0])[0 .. ret.elementsCount]);
}
}
-------
This program works both with color and grayscale images.
-------
$ median-filter --help
Usage: median-filter [<options...>] [<file_names...>]
options:
--nr number of rows in window, default value is 3
--nc number of columns in window default value equals to nr
-h --help This help information.
-------
$(H2 Compared with `numpy.ndarray`)
numpy is undoubtedly one of the most effective software packages that has
facilitated the work of many engineers and scientists. However, due to the
specifics of implementation of Python, a programmer who wishes to use the
functions not represented in numpy may find that the built-in functions
implemented specifically for numpy are not enough, and their Python
implementations work at a very low speed. Extending numpy can be done, but
is somewhat laborious as even the most basic numpy functions that refer
directly to `ndarray` data must be implemented in C for reasonable performance.
At the same time, while working with `ndslice`, an engineer has access to the
whole set of standard D library, so the functions he creates will be as
efficient as if they were written in C.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Ilya Yaroshenko
Acknowledgements: John Loughran Colvin
Source: $(PHOBOSSRC std/_experimental/_ndslice/_package.d)
Macros:
SUBMODULE = $(MREF_ALTTEXT $1, std,experimental, ndslice, $1)
SUBREF = $(REF_ALTTEXT $(TT $2), $2, std,experimental, ndslice, $1)$(NBSP)
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
TDNW2 = <td class="donthyphenate nobr" rowspan="2">$0</td>
*/
/// @@@DEPRECATED_2017-04@@@
deprecated("Please use mir-algorithm DUB package: http://github.com/libmir/mir-algorithm")
module std.experimental.ndslice;
public import std.experimental.ndslice.slice;
public import std.experimental.ndslice.iteration;
public import std.experimental.ndslice.selection;
@system unittest
{
auto matrix = new double[12].sliced(3, 4);
matrix[] = 0;
matrix.diagonal[] = 1;
auto row = matrix[2];
row[3] = 6;
assert(matrix[2, 3] == 6); // D & C index order
assert(matrix(3, 2) == 6); // Math & Fortran index order
}
// relaxed example
@system unittest
{
static Slice!(3, ubyte*) movingWindowByChannel
(Slice!(3, ubyte*) image, size_t nr, size_t nc, ubyte delegate(Slice!(2, ubyte*)) filter)
{
return image
.pack!1
.windows(nr, nc)
.unpack
.transposed!(0, 1, 4)
.pack!2
.mapSlice!filter
.slice;
}
static T median(Range, T)(Slice!(2, Range) sl, T[] buf)
{
import std.algorithm.sorting : topN;
// copy sl to the buffer
size_t n;
foreach (r; sl)
foreach (e; r)
buf[n++] = e;
buf[0 .. n].topN(n / 2);
return buf[n / 2];
}
import std.conv : to;
import std.getopt : getopt, defaultGetoptPrinter;
import std.path : stripExtension;
auto args = ["bin", "image"];
uint nr, nc, def = 3;
auto helpInformation = args.getopt(
"nr", "number of rows in window, default value is " ~ def.to!string, &nr,
"nc", "number of columns in window default value equals to nr", &nc);
if (helpInformation.helpWanted)
{
defaultGetoptPrinter(
"Usage: median-filter [<options...>] [<file_names...>]\noptions:",
helpInformation.options);
return;
}
if (!nr) nr = def;
if (!nc) nc = nr;
auto buf = new ubyte[nr * nc];
foreach (name; args[1 .. $])
{
auto ret =
movingWindowByChannel
(new ubyte[300].sliced(10, 10, 3), nr, nc, window => median(window, buf));
}
}
@safe @nogc pure nothrow unittest
{
import std.algorithm.comparison : equal;
import std.range : iota;
immutable r = 1000.iota;
auto t0 = r.sliced(1000);
assert(t0.front == 0);
assert(t0.back == 999);
assert(t0[9] == 9);
auto t1 = t0[10 .. 20];
assert(t1.front == 10);
assert(t1.back == 19);
assert(t1[9] == 19);
t1.popFront();
assert(t1.front == 11);
t1.popFront();
assert(t1.front == 12);
t1.popBack();
assert(t1.back == 18);
t1.popBack();
assert(t1.back == 17);
assert(t1.equal(iota(12, 18)));
}
pure nothrow @system unittest
{
import std.algorithm.comparison : equal;
import std.array : array;
import std.range : iota;
auto r = 1000.iota.array;
auto t0 = r.sliced(1000);
assert(t0.length == 1000);
assert(t0.front == 0);
assert(t0.back == 999);
assert(t0[9] == 9);
auto t1 = t0[10 .. 20];
assert(t1.front == 10);
assert(t1.back == 19);
assert(t1[9] == 19);
t1.popFront();
assert(t1.front == 11);
t1.popFront();
assert(t1.front == 12);
t1.popBack();
assert(t1.back == 18);
t1.popBack();
assert(t1.back == 17);
assert(t1.equal(iota(12, 18)));
t1.front = 13;
assert(t1.front == 13);
t1.front++;
assert(t1.front == 14);
t1.front += 2;
assert(t1.front == 16);
t1.front = 12;
assert((t1.front = 12) == 12);
t1.back = 13;
assert(t1.back == 13);
t1.back++;
assert(t1.back == 14);
t1.back += 2;
assert(t1.back == 16);
t1.back = 12;
assert((t1.back = 12) == 12);
t1[3] = 13;
assert(t1[3] == 13);
t1[3]++;
assert(t1[3] == 14);
t1[3] += 2;
assert(t1[3] == 16);
t1[3] = 12;
assert((t1[3] = 12) == 12);
t1[3 .. 5] = 100;
assert(t1[2] != 100);
assert(t1[3] == 100);
assert(t1[4] == 100);
assert(t1[5] != 100);
t1[3 .. 5] += 100;
assert(t1[2] < 100);
assert(t1[3] == 200);
assert(t1[4] == 200);
assert(t1[5] < 100);
--t1[3 .. 5];
assert(t1[2] < 100);
assert(t1[3] == 199);
assert(t1[4] == 199);
assert(t1[5] < 100);
--t1[];
assert(t1[3] == 198);
assert(t1[4] == 198);
t1[] += 2;
assert(t1[3] == 200);
assert(t1[4] == 200);
t1[] *= t1[];
assert(t1[3] == 40000);
assert(t1[4] == 40000);
assert(&t1[$ - 1] is &(t1.back()));
}
@safe @nogc pure nothrow unittest
{
import std.range : iota;
auto r = (10_000L * 2 * 3 * 4).iota;
auto t0 = r.sliced(10, 20, 30, 40);
assert(t0.length == 10);
assert(t0.length!0 == 10);
assert(t0.length!1 == 20);
assert(t0.length!2 == 30);
assert(t0.length!3 == 40);
}
pure nothrow @system unittest
{
import std.experimental.ndslice.internal : Iota;
import std.meta : AliasSeq;
import std.range;
import std.typecons : Tuple;
foreach (R; AliasSeq!(
int*, int[], typeof(1.iota),
const(int)*, const(int)[],
immutable(int)*, immutable(int)[],
double*, double[], typeof(10.0.iota),
Tuple!(double, int[string])*, Tuple!(double, int[string])[]))
foreach (n; Iota!(1, 4))
{
alias S = Slice!(n, R);
static assert(isRandomAccessRange!S);
static assert(hasSlicing!S);
static assert(hasLength!S);
}
immutable int[] im = [1,2,3,4,5,6];
auto slice = im.sliced(2, 3);
}
pure nothrow @system unittest
{
auto tensor = new int[3 * 4 * 8].sliced(3, 4, 8);
assert(&(tensor.back.back.back()) is &tensor[2, 3, 7]);
assert(&(tensor.front.front.front()) is &tensor[0, 0, 0]);
}
pure nothrow @system unittest
{
import std.experimental.ndslice.selection : pack;
auto slice = new int[24].sliced(2, 3, 4);
auto r0 = slice.pack!1[1, 2];
slice.pack!1[1, 2][] = 4;
auto r1 = slice[1, 2];
assert(slice[1, 2, 3] == 4);
}
pure nothrow @system unittest
{
auto ar = new int[3 * 8 * 9];
auto tensor = ar.sliced(3, 8, 9);
tensor[0, 1, 2] = 4;
tensor[0, 1, 2]++;
assert(tensor[0, 1, 2] == 5);
tensor[0, 1, 2]--;
assert(tensor[0, 1, 2] == 4);
tensor[0, 1, 2] += 2;
assert(tensor[0, 1, 2] == 6);
auto matrix = tensor[0 .. $, 1, 0 .. $];
matrix[] = 10;
assert(tensor[0, 1, 2] == 10);
assert(matrix[0, 2] == tensor[0, 1, 2]);
assert(&matrix[0, 2] is &tensor[0, 1, 2]);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -327,13 +327,6 @@ SRC_STD_EXP_LOGGER= \
std\experimental\logger\nulllogger.d \
std\experimental\logger\package.d
SRC_STD_EXP_NDSLICE= \
std\experimental\ndslice\package.d \
std\experimental\ndslice\iteration.d \
std\experimental\ndslice\selection.d \
std\experimental\ndslice\slice.d \
std\experimental\ndslice\internal.d
SRC_ETC=
SRC_ETC_C= \
@ -363,7 +356,6 @@ SRC_TO_COMPILE= \
$(SRC_STD_EXP) \
$(SRC_STD_EXP_ALLOC) \
$(SRC_STD_EXP_LOGGER) \
$(SRC_STD_EXP_NDSLICE) \
$(SRC_ETC) \
$(SRC_ETC_C)
@ -523,10 +515,6 @@ DOCS= \
$(DOC)\std_experimental_allocator_showcase.html \
$(DOC)\std_experimental_allocator_typed.html \
$(DOC)\std_experimental_allocator.html \
$(DOC)\std_experimental_ndslice_iteration.html \
$(DOC)\std_experimental_ndslice_selection.html \
$(DOC)\std_experimental_ndslice_slice.html \
$(DOC)\std_experimental_ndslice.html \
$(DOC)\std_experimental_typecons.html \
$(DOC)\std_windows_charset.html \
$(DOC)\std_windows_registry.html \
@ -571,8 +559,7 @@ UNITTEST_OBJS= \
unittest8c.obj \
unittest8d.obj \
unittest8e.obj \
unittest8f.obj \
unittest9a.obj
unittest8f.obj
unittest : $(LIB)
$(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest1.obj $(SRC_STD_1)
@ -591,7 +578,6 @@ unittest : $(LIB)
$(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8d.obj $(SRC_STD_INTERNAL) $(SRC_STD_INTERNAL_DIGEST) $(SRC_STD_INTERNAL_MATH) $(SRC_STD_INTERNAL_WINDOWS)
$(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8e.obj $(SRC_ETC) $(SRC_ETC_C)
$(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest8f.obj $(SRC_STD_EXP)
$(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest9a.obj $(SRC_STD_EXP_NDSLICE)
$(DMD) $(UDFLAGS) -L/co -unittest unittest.d $(UNITTEST_OBJS) \
$(ZLIB) $(DRUNTIMELIB)
.\unittest.exe
@ -1056,18 +1042,6 @@ $(DOC)\std_experimental_allocator.html : $(STDDOC) std\experimental\allocator\pa
$(DOC)\std_experimental_typecons.html : $(STDDOC) std\experimental\typecons.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_typecons.html $(STDDOC) std\experimental\typecons.d
$(DOC)\std_experimental_ndslice_iteration.html : $(STDDOC) std\experimental\ndslice\iteration.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice_iteration.html $(STDDOC) std\experimental\ndslice\iteration.d
$(DOC)\std_experimental_ndslice_selection.html : $(STDDOC) std\experimental\ndslice\selection.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice_selection.html $(STDDOC) std\experimental\ndslice\selection.d
$(DOC)\std_experimental_ndslice_slice.html : $(STDDOC) std\experimental\ndslice\slice.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice_slice.html $(STDDOC) std\experimental\ndslice\slice.d
$(DOC)\std_experimental_ndslice.html : $(STDDOC) std\experimental\ndslice\package.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice.html $(STDDOC) std\experimental\ndslice\package.d
$(DOC)\std_digest_crc.html : $(STDDOC) std\digest\crc.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_crc.html $(STDDOC) std\digest\crc.d

View file

@ -346,13 +346,6 @@ SRC_STD_EXP_LOGGER= \
std\experimental\logger\nulllogger.d \
std\experimental\logger\package.d
SRC_STD_EXP_NDSLICE= \
std\experimental\ndslice\package.d \
std\experimental\ndslice\iteration.d \
std\experimental\ndslice\selection.d \
std\experimental\ndslice\slice.d \
std\experimental\ndslice\internal.d
SRC_ETC=
SRC_ETC_C= \
@ -382,7 +375,6 @@ SRC_TO_COMPILE= \
$(SRC_STD_EXP) \
$(SRC_STD_EXP_ALLOC) \
$(SRC_STD_EXP_LOGGER) \
$(SRC_STD_EXP_NDSLICE) \
$(SRC_ETC) \
$(SRC_ETC_C)
@ -542,10 +534,6 @@ DOCS= \
$(DOC)\std_experimental_allocator_showcase.html \
$(DOC)\std_experimental_allocator_typed.html \
$(DOC)\std_experimental_allocator.html \
$(DOC)\std_experimental_ndslice_iteration.html \
$(DOC)\std_experimental_ndslice_selection.html \
$(DOC)\std_experimental_ndslice_slice.html \
$(DOC)\std_experimental_ndslice.html \
$(DOC)\std_experimental_typecons.html \
$(DOC)\std_windows_charset.html \
$(DOC)\std_windows_registry.html \
@ -599,8 +587,7 @@ UNITTEST_OBJS= \
unittest8d.obj \
unittest8e.obj \
unittest8f.obj \
unittest9.obj \
unittest9a.obj
unittest9.obj
unittest : $(LIB)
$(DMD) $(UDFLAGS) -c -unittest -ofunittest1.obj $(SRC_STD_1)
@ -628,7 +615,6 @@ unittest : $(LIB)
$(DMD) $(UDFLAGS) -c -unittest -ofunittest8e.obj $(SRC_ETC) $(SRC_ETC_C)
$(DMD) $(UDFLAGS) -c -unittest -ofunittest8f.obj $(SRC_STD_EXP)
$(DMD) $(UDFLAGS) -c -unittest -ofunittest9.obj $(SRC_STD_EXP_ALLOC)
$(DMD) $(UDFLAGS) -c -unittest -ofunittest9a.obj $(SRC_STD_EXP_NDSLICE)
$(DMD) $(UDFLAGS) -L/OPT:NOICF -unittest unittest.d $(UNITTEST_OBJS) \
$(ZLIB) $(DRUNTIMELIB)
.\unittest.exe
@ -1028,18 +1014,6 @@ $(DOC)\std_experimental_allocator.html : $(STDDOC) std\experimental\allocator\pa
$(DOC)\std_experimental_typecons.html : $(STDDOC) std\experimental\typecons.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_typecons.html $(STDDOC) std\experimental\typecons.d
$(DOC)\std_experimental_ndslice_iteration.html : $(STDDOC) std\experimental\ndslice\iteration.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice_iteration.html $(STDDOC) std\experimental\ndslice\iteration.d
$(DOC)\std_experimental_ndslice_selection.html : $(STDDOC) std\experimental\ndslice\selection.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice_selection.html $(STDDOC) std\experimental\ndslice\selection.d
$(DOC)\std_experimental_ndslice_slice.html : $(STDDOC) std\experimental\ndslice\slice.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice_slice.html $(STDDOC) std\experimental\ndslice\slice.d
$(DOC)\std_experimental_ndslice.html : $(STDDOC) std\experimental\ndslice\package.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_experimental_ndslice.html $(STDDOC) std\experimental\ndslice\package.d
$(DOC)\std_digest_crc.html : $(STDDOC) std\digest\crc.d
$(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_digest_crc.html $(STDDOC) std\digest\crc.d