├── ndslice.graffle ├── images └── kaleidic.jpeg ├── NOTICE ├── subprojects └── mir-core.wrap ├── bigint_benchmark ├── dub.sdl └── source │ └── app.d ├── .github └── workflows │ ├── compilers.json │ └── ci.yml ├── .gitmodules ├── cpp_example ├── meson.build ├── eye.d ├── init_rcarray.d └── main.cpp ├── .circleci └── config.yml ├── LICENSE ├── .gitignore ├── include └── mir │ ├── interpolate.h │ ├── small_string.h │ ├── numeric.h │ └── slim_rcptr.h ├── source └── mir │ ├── lob.d │ ├── ndslice │ ├── filling.d │ ├── mutation.d │ ├── ndfield.d │ └── traits.d │ ├── math │ └── func │ │ ├── expdigamma.d │ │ └── hermite.d │ ├── cpp_export │ └── numeric.d │ ├── range.d │ ├── type_info.d │ ├── rc │ ├── package.d │ ├── context.d │ └── slim_ptr.d │ ├── algebraic_alias │ ├── json.d │ ├── ion.d │ └── transform.d │ ├── test.d │ ├── graph │ └── package.d │ ├── interpolate │ ├── generic.d │ ├── mod.d │ ├── utility.d │ └── extrapolate.d │ ├── polynomial.d │ ├── bignum │ └── internal │ │ └── kernel.d │ ├── algorithm │ └── setops.d │ ├── internal │ └── ldc_simd.d │ ├── stdio.d │ ├── annotated.d │ └── array │ └── allocation.d ├── dub.sdl ├── README.md ├── meson.build └── index.d /ndslice.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libmir/mir-algorithm/HEAD/ndslice.graffle -------------------------------------------------------------------------------- /images/kaleidic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libmir/mir-algorithm/HEAD/images/kaleidic.jpeg -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ### Ryu 2 | 3 | [Original Ryu algorithm](https://github.com/ulfjack/ryu) was developed by Ulf Adams. 4 | -------------------------------------------------------------------------------- /subprojects/mir-core.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-core 3 | url=https://github.com/libmir/mir-core.git 4 | revision=head 5 | -------------------------------------------------------------------------------- /bigint_benchmark/dub.sdl: -------------------------------------------------------------------------------- 1 | name "bigint_benchmark" 2 | dependency "mir-algorithm" path="../" 3 | dependency "gmp-d" version="~master" 4 | -------------------------------------------------------------------------------- /.github/workflows/compilers.json: -------------------------------------------------------------------------------- 1 | [ 2 | "dmd-master", 3 | "dmd-latest", 4 | "dmd-beta", 5 | "ldc-master", 6 | "ldc-latest", 7 | "ldc-beta" 8 | ] -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/artwork"] 2 | path = doc/artwork 3 | url = https://github.com/libmir/mir-artwork 4 | [submodule "doc/dlang.org"] 5 | path = doc/dlang.org 6 | url = https://github.com/dlang/dlang.org 7 | -------------------------------------------------------------------------------- /cpp_example/meson.build: -------------------------------------------------------------------------------- 1 | mir_algorithm_cpp_test_exe = executable(meson.project_name() + '-test', 2 | ['eye.d', 'init_rcarray.d', 'main.cpp'], 3 | include_directories: directories, 4 | dependencies: mir_algorithm_dep, 5 | ) 6 | 7 | test(meson.project_name() + '-cpp-test', mir_algorithm_cpp_test_exe) 8 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | mirci: libmir/upload_docs@0.3.0 5 | 6 | workflows: 7 | version: 2 8 | build-deploy: 9 | jobs: 10 | - mirci/test_and_build_docs: 11 | filters: 12 | tags: 13 | only: /^v(\d)+(\.(\d)+)+$/ 14 | - mirci/upload_docs: 15 | to: mir-algorithm.libmir.org 16 | requires: 17 | - mirci/test_and_build_docs 18 | filters: 19 | branches: 20 | ignore: /.*/ 21 | tags: 22 | only: /^v(\d)+(\.(\d)+)+$/ 23 | -------------------------------------------------------------------------------- /cpp_example/eye.d: -------------------------------------------------------------------------------- 1 | module eye; 2 | 3 | import mir.ndslice; 4 | 5 | extern(C++, Space) 6 | { 7 | Slice!(double*, 2) eye(size_t n) nothrow @nogc 8 | { 9 | auto ret = stdcUninitSlice!double(n, n); 10 | ret[] = 0; 11 | ret.diagonal[] = 1; 12 | return ret; 13 | } 14 | 15 | void printMatrix(mir_slice!(double*, 2) matrix) 16 | { 17 | import core.stdc.stdio; 18 | 19 | foreach(row; matrix) 20 | { 21 | foreach(e; row) 22 | printf("%f ", e); 23 | printf("\n"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.log 3 | *.lst 4 | *.o 5 | *.pdf 6 | *.s 7 | *.exe 8 | *.sublime-project 9 | *.sublime-workspace 10 | .dub 11 | .generated 12 | .vscode 13 | __* 14 | dub.selections.json 15 | mir-algorithm 16 | mir-algorithm-test-library 17 | mir-internal 18 | web 19 | .DS_* 20 | docs 21 | *.html 22 | docs.json 23 | out/ 24 | /tests_extractor.d 25 | build/ 26 | files 27 | mir-algorithm.lib 28 | mir-algorithm-test-default 29 | mir-algorithm-test-dips 30 | subprojects/mir-core 31 | mir-core 32 | mir-algorithm-test-dip1008 33 | test 34 | test.d 35 | docgen 36 | _build_dir_ 37 | .VSCodeCounter 38 | builddir/ 39 | mir-algorithm-test-secure 40 | *.pdb 41 | bigint_benchmark/bigint_benchmark 42 | bigint_benchmark/temp.d 43 | mir-algorithm-test-silly 44 | mir-algorithm-test-ci-bignum-test 45 | -------------------------------------------------------------------------------- /include/mir/interpolate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mir { 4 | namespace interpolate 5 | { 6 | enum class SplineType 7 | { 8 | c2, 9 | cardinal, 10 | monotone, 11 | doubleQuadratic, 12 | akima, 13 | makima 14 | }; 15 | 16 | enum class SplineBoundaryType 17 | { 18 | periodic = -1, 19 | notAKnot, 20 | firstDerivative, 21 | secondDerivative, 22 | parabolic, 23 | monotone, 24 | akima, 25 | makima 26 | }; 27 | 28 | template 29 | struct SplineBoundaryCondition 30 | { 31 | SplineBoundaryType type = SplineBoundaryType::notAKnot; 32 | T value = 0; 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/mir/lob.d: -------------------------------------------------------------------------------- 1 | /++ 2 | +/ 3 | module mir.lob; 4 | 5 | import mir.serde: serdeRegister; 6 | 7 | /++ 8 | Values of type clob are encoded as a sequence of octets that should be interpreted as text 9 | with an unknown encoding (and thus opaque to the application). 10 | +/ 11 | @serdeRegister 12 | struct Clob 13 | { 14 | /// 15 | const(char)[] data; 16 | 17 | /// 18 | int opCmp(scope const typeof(this) rhs) 19 | @safe pure nothrow @nogc scope const 20 | { 21 | return __cmp(data, rhs.data); 22 | } 23 | } 24 | 25 | /++ 26 | This is a sequence of octets with no interpretation (and thus opaque to the application). 27 | +/ 28 | @serdeRegister 29 | struct Blob 30 | { 31 | /// 32 | const(ubyte)[] data; 33 | 34 | /// 35 | int opCmp(scope const typeof(this) rhs) 36 | @safe pure nothrow @nogc scope const 37 | { 38 | return __cmp(data, rhs.data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cpp_example/init_rcarray.d: -------------------------------------------------------------------------------- 1 | module init_rcarray; 2 | 3 | import mir.ndslice; 4 | import mir.rc.array; 5 | import mir.rc.ptr; 6 | 7 | // force template instatiations 8 | alias RCArrayDouble = RCArray!double; 9 | alias RCArrayInt = RCArray!int; 10 | 11 | extern(C++) 12 | struct S { double d = 0; this(double e) { d = e;} } 13 | extern(C++) 14 | struct C { double k = 0; S s; } 15 | alias SPtr = RCPtr!S; 16 | alias CPtr = RCPtr!C; 17 | 18 | extern(C++, Space) 19 | { 20 | void initWithIota(ref RCArray!double a) 21 | { 22 | foreach(i, ref e; a) 23 | e = i; 24 | } 25 | 26 | void reverseRcSlice(ref Slice!(RCI!double) a) 27 | { 28 | import mir.utility: swap; 29 | foreach(i; 0 .. a.length / 2) 30 | swap(a[i], a[$ - 1 - i]); 31 | } 32 | 33 | void reverseRcSlice(ref Slice!(RCI!int) a) 34 | { 35 | import mir.utility: swap; 36 | foreach(i; 0 .. a.length / 2) 37 | swap(a[i], a[$ - 1 - i]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/mir/ndslice/filling.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This is a submodule of $(MREF mir,ndslice). 3 | 4 | Initialisation routines. 5 | 6 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 8 | Authors: Ilia Ki 9 | 10 | Macros: 11 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 12 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 13 | +/ 14 | module mir.ndslice.filling; 15 | 16 | import mir.ndslice.slice: Slice, SliceKind; 17 | 18 | /++ 19 | Fills a matrix with the terms of a geometric progression in each row. 20 | Params: 21 | matrix = `m × n` matrix to fill 22 | vec = vector of progression coefficients length of `m` 23 | See_also: $(LINK2 https://en.wikipedia.org/wiki/Vandermonde_matrix, Vandermonde matrix) 24 | +/ 25 | void fillVandermonde(F, SliceKind matrixKind, SliceKind kind)(Slice!(F*, 2, matrixKind) matrix, Slice!(const(F)*, 1, kind) vec) 26 | in { 27 | assert(matrix.length == vec.length); 28 | } 29 | do { 30 | import mir.conv: to; 31 | 32 | foreach (v; matrix) 33 | { 34 | F a = vec.front; 35 | vec.popFront; 36 | F x = to!F(1); 37 | foreach (ref e; v) 38 | { 39 | e = x; 40 | x *= a; 41 | } 42 | } 43 | } 44 | 45 | /// 46 | @safe pure nothrow version(mir_ndslice_test) unittest 47 | { 48 | import mir.ndslice.slice: sliced; 49 | import mir.ndslice.allocation: uninitSlice; 50 | auto x = [1.0, 2, 3, 4, 5].sliced; 51 | auto v = uninitSlice!double(x.length, x.length); 52 | v.fillVandermonde(x); 53 | assert(v == 54 | [[ 1.0, 1, 1, 1, 1], 55 | [ 1.0, 2, 4, 8, 16], 56 | [ 1.0, 3, 9, 27, 81], 57 | [ 1.0, 4, 16, 64, 256], 58 | [ 1.0, 5, 25, 125, 625]]); 59 | } 60 | -------------------------------------------------------------------------------- /source/mir/ndslice/mutation.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H2 Multidimensional mutation algorithms) 3 | 4 | This is a submodule of $(MREF mir,ndslice). 5 | 6 | $(BOOKTABLE $(H2 Function), 7 | $(TR $(TH Function Name) $(TH Description)) 8 | ) 9 | 10 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 11 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 12 | Authors: Ilia Ki 13 | 14 | Macros: 15 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 16 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 17 | +/ 18 | module mir.ndslice.mutation; 19 | 20 | import mir.ndslice.slice: Slice, SliceKind; 21 | 22 | /++ 23 | Copies n-dimensional minor. 24 | +/ 25 | void copyMinor(size_t N, IteratorFrom, SliceKind KindFrom, IteratorTo, SliceKind KindTo, IndexIterator)( 26 | Slice!(IteratorFrom, N, KindFrom) from, 27 | Slice!(IteratorTo, N, KindTo) to, 28 | Slice!IndexIterator[N] indices... 29 | ) 30 | in { 31 | import mir.internal.utility: Iota; 32 | static foreach (i; Iota!N) 33 | assert(indices[i].length == to.length!i); 34 | } 35 | do { 36 | static if (N == 1) 37 | to[] = from[indices[0]]; 38 | else 39 | foreach (i; 0 .. indices[0].length) 40 | { 41 | copyMinor!(N - 1)(from[indices[0][i]], to[i], indices[1 .. N]); 42 | } 43 | } 44 | 45 | /// 46 | version(mir_ndslice_test) 47 | @safe pure nothrow 48 | unittest 49 | { 50 | import mir.ndslice; 51 | // 0 1 2 3 52 | // 4 5 6 7 53 | // 8 9 10 11 54 | auto a = iota!int(3, 4); 55 | auto b = slice!int(2, 2); 56 | copyMinor(a, b, [2, 1].sliced, [0, 3].sliced); 57 | assert(b == [[8, 11], [4, 7]]); 58 | } 59 | 60 | /++ 61 | Reverses data in the 1D slice. 62 | +/ 63 | void reverseInPlace(Iterator)(Slice!Iterator slice) 64 | { 65 | import mir.utility : swap; 66 | foreach (i; 0 .. slice.length / 2) 67 | swap(slice[i], slice[$ - (i + 1)]); 68 | } 69 | 70 | /// 71 | version(mir_ndslice_test) 72 | @safe pure nothrow 73 | unittest 74 | { 75 | import mir.ndslice; 76 | auto s = 5.iota.slice; 77 | s.reverseInPlace; 78 | assert([4, 3, 2, 1, 0]); 79 | } 80 | -------------------------------------------------------------------------------- /source/mir/math/func/expdigamma.d: -------------------------------------------------------------------------------- 1 | /** 2 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 3 | 4 | Authors: Ilia Ki 5 | */ 6 | module mir.math.func.expdigamma; 7 | 8 | /++ 9 | Optimized and more precise analog of `y = exp(digamma(x))`. 10 | 11 | Returns: 12 | `exp(digamma(x))` 13 | +/ 14 | F expDigamma(F)(in F x) 15 | { 16 | import mir.math.common; 17 | 18 | static immutable F[7] c = [ 19 | F(1.0 / 24), 20 | F(1.0L / 48), 21 | F(23.0L / 5760), 22 | F(17.0L / 3840), 23 | F(10_099.0L / 2_903_040), 24 | F(2501.0L / 1_161_216), 25 | F(795_697.0L / 199_065_600), 26 | ]; 27 | 28 | if (!(x >= 0)) 29 | return F.nan; 30 | F s = x; 31 | F w = 0; 32 | while ( s < F(10) ) 33 | { 34 | w += 1 / s; 35 | s += 1; 36 | } 37 | F y = F(-0.5); 38 | F t = 1; 39 | import mir.internal.utility; 40 | foreach (i; Iota!(0, c.length)) 41 | { 42 | t *= s; 43 | y += c[i] / t; 44 | } 45 | y += s; 46 | y /= exp(w); 47 | return y; 48 | } 49 | 50 | version(mir_test) 51 | unittest 52 | { 53 | import std.meta: AliasSeq; 54 | import std.mathspecial: digamma; 55 | import mir.math: approxEqual, exp, nextUp, nextDown; 56 | assert(approxEqual(expDigamma(0.001), exp(digamma(0.001)))); 57 | assert(approxEqual(expDigamma(0.1), exp(digamma(0.1)))); 58 | assert(approxEqual(expDigamma(1.0), exp(digamma(1.0)))); 59 | assert(approxEqual(expDigamma(2.3), exp(digamma(2.3)))); 60 | assert(approxEqual(expDigamma(20.0), exp(digamma(20.0)))); 61 | assert(approxEqual(expDigamma(40.0), exp(digamma(40.0)))); 62 | foreach (F; AliasSeq!(float, double, real)) 63 | { 64 | assert(expDigamma!F(0.0) == 0); 65 | assert(expDigamma!F(0.0.nextUp) >= 0); 66 | assert(expDigamma!F(0.0.min_normal) >= 0); 67 | assert(expDigamma!F(0.5.nextUp) >= 0); 68 | assert(expDigamma!F(0.5.nextDown) >= 0); 69 | foreach (i; 1 .. 10) 70 | { 71 | assert(expDigamma(F(i)) >= expDigamma(F(i).nextDown)); 72 | assert(expDigamma(F(i)) <= expDigamma(F(i).nextUp)); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /include/mir/small_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Self contained generic small string implementaton. 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace mir 11 | { 12 | template 13 | struct SmallString 14 | { 15 | SmallString() noexcept {} 16 | 17 | SmallString(std::string_view str) 18 | { 19 | auto length = str.size(); 20 | if (length > sizeof(SmallString)) 21 | throw std::range_error("Cannot create SmallString: input string exceeds maximum allowed length."); 22 | std::memcpy(_data, str.data(), length); 23 | } 24 | 25 | SmallString(const std::string& str) : SmallString((std::string_view)str) {} 26 | SmallString(const char* str) : SmallString(std::string_view(str)) {} 27 | std::string_view str() const noexcept { return std::string_view(_data, _data[maxLength - 1] ? maxLength : std::strlen(_data)); } 28 | operator std::string_view() const noexcept { return str(); } 29 | operator bool() const noexcept { return _data[0] != 0; } 30 | bool operator !() const noexcept { return _data[0] == 0; } 31 | bool operator==(const SmallString& rhs) const noexcept { return memcmp(_data, rhs._data, maxLength) == 0; } 32 | bool operator!=(const SmallString& rhs) const noexcept { return memcmp(_data, rhs._data, maxLength) != 0; } 33 | bool operator<(const SmallString& rhs) const noexcept { return memcmp(_data, rhs._data, maxLength) < 0; } 34 | bool operator<=(const SmallString& rhs) const noexcept { return memcmp(_data, rhs._data, maxLength) <= 0; } 35 | bool operator>(const SmallString& rhs) const noexcept { return memcmp(_data, rhs._data, maxLength) > 0; } 36 | bool operator>=(const SmallString& rhs) const noexcept { return memcmp(_data, rhs._data, maxLength) >= 0; } 37 | private: 38 | char _data[maxLength] = {0}; 39 | }; 40 | } 41 | 42 | namespace std 43 | { 44 | template 45 | std::ostream &operator<<(std::ostream &os, const mir::SmallString& ss) 46 | { 47 | return os << ss.str(); 48 | } 49 | 50 | template 51 | struct hash> 52 | { 53 | inline size_t operator()(const mir::SmallString& s) const noexcept 54 | { 55 | return std::hash()(s.str()); 56 | } 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "mir-algorithm" 2 | description "Dlang Core Library" 3 | 4 | authors "Ilia Ki" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And others" "mir.date and a bit of other code is based on Phobos" 5 | copyright "2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments" 6 | license "Apache-2.0" 7 | 8 | dependency "mir-core" version=">=1.6.0" 9 | 10 | // dflags "-version=MirNoSIMD" 11 | // dflags "-mtriple=aarch64-linux-gnu" 12 | 13 | // versions "TeslAlgoM" 14 | 15 | buildType "unittest" { 16 | buildOptions "unittests" "debugMode" "debugInfo" 17 | versions "mir_bignum_test" "mir_bignum_test_llv" 18 | versions "mir_ndslice_test" 19 | versions "mir_test" 20 | dflags "-lowmem" 21 | } 22 | 23 | buildType "unittest-verbose" { 24 | buildOptions "unittests" "debugMode" "debugInfo" 25 | versions "mir_bignum_test" "mir_bignum_test_llv" 26 | // versions "mir_ndslice_test" 27 | versions "mir_test" 28 | dflags "-lowmem" "-checkaction=context" "-allinst" 29 | } 30 | buildType "unittest-dip1008" { 31 | buildOptions "unittests" "debugMode" "debugInfo" 32 | versions "mir_bignum_test" "mir_ndslice_test" "mir_test" "mir_bignum_test_llv" 33 | dflags "-lowmem" "-preview=dip1008" 34 | } 35 | buildType "unittest-dip1000" { 36 | buildOptions "unittests" "debugMode" "debugInfo" 37 | versions "mir_bignum_test" "mir_ndslice_test" "mir_test" 38 | dflags "-lowmem" "-preview=dip1000" 39 | } 40 | buildType "unittest-cov" { 41 | buildOptions "unittests" "coverage" "debugMode" "debugInfo" 42 | versions "mir_bignum_test" "mir_bignum_test_llv" "mir_ndslice_test" "mir_test" 43 | dflags "-lowmem" 44 | } 45 | buildType "unittest-ci" { 46 | buildOptions "unittests" "coverage" "debugMode" "debugInfo" 47 | dflags "-lowmem" 48 | } 49 | buildType "unittest-release" { 50 | buildOptions "unittests" "releaseMode" "optimize" "inline" "noBoundsCheck" 51 | versions "mir_bignum_test" "mir_ndslice_test" "mir_test" 52 | dflags "-lowmem" 53 | } 54 | 55 | configuration "default" { 56 | } 57 | 58 | configuration "silly" { 59 | dependency "silly" version="~>1.1.1" 60 | } 61 | 62 | configuration "dip1008" { 63 | dflags "-preview=dip1008" 64 | } 65 | 66 | configuration "dips" { 67 | dflags "-preview=dip1000" "-preview=dip1008" 68 | } 69 | 70 | configuration "ci-bignum-test" { 71 | versions "mir_bignum_test" "mir_bignum_test_llv" 72 | dflags "-lowmem" "-preview=dip1000" 73 | } 74 | 75 | configuration "ci-core-test" { 76 | versions "mir_core_test" 77 | } 78 | 79 | configuration "ci-ndslice-test" { 80 | versions "mir_ndslice_test" 81 | } 82 | 83 | configuration "ci-test" { 84 | versions "mir_test" 85 | } 86 | -------------------------------------------------------------------------------- /source/mir/cpp_export/numeric.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This module contans extern C++ wrappers for $(MREF mir, numeric). 3 | +/ 4 | module mir.cpp_export.numeric; 5 | 6 | import mir.numeric: findRootImpl, mir_find_root_result; 7 | 8 | private alias CFunction(T) = extern(C++) T function(scope const(void)* ctx, T) @safe pure nothrow @nogc; 9 | 10 | private alias CTolerance(T) = extern(C++) bool function(scope const(void)* ctx, T, T) @safe pure nothrow @nogc; 11 | 12 | export extern(C++) @safe pure nothrow @nogc: 13 | 14 | /// Wrapper for $(REF_ALTTEXT $(TT findRoot), findRoot, mir, numeric)$(NBSP) 15 | mir_find_root_result!float mir_find_root( 16 | float ax, 17 | float bx, 18 | float fax, 19 | float fbx, 20 | float lowerBound, 21 | float upperBound, 22 | uint maxIterations, 23 | uint steps, 24 | scope CFunction!float f, 25 | scope const(void)* f_ctx, 26 | scope CTolerance!float tolerance, 27 | scope const(void)* tolerance_ctx, 28 | ) 29 | { 30 | pragma(inline, false); 31 | if (tolerance is null) 32 | return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, (float x) => f(f_ctx, x), null); 33 | else 34 | return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, (float x) => f(f_ctx, x), (float a, float b) => tolerance(tolerance_ctx, a, b) != 0); 35 | } 36 | 37 | /// ditto 38 | mir_find_root_result!double mir_find_root( 39 | double ax, 40 | double bx, 41 | double fax, 42 | double fbx, 43 | double lowerBound, 44 | double upperBound, 45 | uint maxIterations, 46 | uint steps, 47 | scope CFunction!double f, 48 | scope const(void)* f_ctx, 49 | scope CTolerance!double tolerance, 50 | scope const(void)* tolerance_ctx, 51 | ) 52 | { 53 | pragma(inline, false); 54 | if (tolerance is null) 55 | return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, (double x) => f(f_ctx, x), null); 56 | else 57 | return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, (double x) => f(f_ctx, x), (double a, double b) => tolerance(tolerance_ctx, a, b) != 0); 58 | } 59 | 60 | /// ditto 61 | mir_find_root_result!real mir_find_root( 62 | real ax, 63 | real bx, 64 | real fax, 65 | real fbx, 66 | real lowerBound, 67 | real upperBound, 68 | uint maxIterations, 69 | uint steps, 70 | scope CFunction!real f, 71 | scope const(void)* f_ctx, 72 | scope CTolerance!real tolerance, 73 | scope const(void)* tolerance_ctx, 74 | ) 75 | { 76 | pragma(inline, false); 77 | if (tolerance is null) 78 | return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, (real x) => f(f_ctx, x), null); 79 | else 80 | return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, (real x) => f(f_ctx, x), (real a, real b) => tolerance(tolerance_ctx, a, b) != 0); 81 | } 82 | -------------------------------------------------------------------------------- /source/mir/range.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Ranges. 3 | 4 | See_also: $(MREF mir,_primitives). 5 | 6 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 8 | Authors: Ilia Ki, Phobos Authors 9 | +/ 10 | module mir.range; 11 | 12 | /++ 13 | Data size counter. 14 | 15 | Does not store anything. 16 | +/ 17 | struct Counter(T) 18 | { 19 | import std.range: isInputRange, ElementType; 20 | import std.traits: isImplicitlyConvertible, isSomeChar; 21 | /// 22 | size_t _count; 23 | 24 | /// Data count. 25 | size_t count()() @property 26 | { 27 | return _count; 28 | } 29 | 30 | private template canPutItem(U) 31 | { 32 | enum bool canPutItem = 33 | isImplicitlyConvertible!(U, T) || 34 | isSomeChar!T && isSomeChar!U; 35 | } 36 | 37 | private template canPutRange(Range) 38 | { 39 | import mir.primitives: front; 40 | enum bool canPutRange = 41 | isInputRange!Range && 42 | is(typeof(Counter.init.put(Range.init.front))); 43 | } 44 | 45 | /// 46 | void put(U)(auto ref U item) if (canPutItem!U) 47 | { 48 | static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof) 49 | { 50 | import std.utf: codeLength; 51 | _count += codeLength!T(item); 52 | } 53 | else 54 | { 55 | _count++; 56 | } 57 | } 58 | 59 | /// 60 | void put(Range)(Range items) if (canPutRange!Range) 61 | { 62 | // note, we disable this branch for appending one type of char to 63 | // another because we can't trust the length portion. 64 | static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) && 65 | !is(immutable Range == immutable T[]))) 66 | { 67 | import mir.primitives: hasLength; 68 | static if (hasLength!Range) 69 | { 70 | _count += items.length; 71 | } 72 | else 73 | { 74 | for (;!items.empty; items.popFront) 75 | _count++; 76 | } 77 | } 78 | else 79 | { 80 | import std.utf: codeLength; 81 | _count += codeLength!T(items); 82 | } 83 | } 84 | } 85 | 86 | /// 87 | version(mir_test) unittest 88 | { 89 | Counter!char counter; 90 | counter.put("Ми"); 91 | assert(counter.count == 4); 92 | counter.put('р'); // Cyrillic 93 | assert(counter.count == 6); 94 | } 95 | 96 | /// 97 | version(mir_test) unittest 98 | { 99 | Counter!wchar counter; 100 | counter.put("Ми"); 101 | assert(counter.count == 2); 102 | counter.put('р'); // Cyrillic 103 | assert(counter.count == 3); 104 | } 105 | 106 | /// 107 | version(mir_test) unittest 108 | { 109 | Counter!int counter; 110 | import std.algorithm: until; 111 | counter.put([1, 2, 3, 4, 5].until(3)); 112 | } 113 | -------------------------------------------------------------------------------- /bigint_benchmark/source/app.d: -------------------------------------------------------------------------------- 1 | import mir.stdio; 2 | import std.datetime.stopwatch; 3 | 4 | immutable ps = "E5B5B1EDC8DF0F307C2220151CFCBE31F69B15659A5D6FBA1E50F55A08B341218312D707CFC16ED86A1765F5AEAFA7E6A11C4431038914C76F0F398FE6BE031E289B220D13D9E02226C691D15BC6E1186EA18222D93F52A393BE1DA1A42853512419B5E6E304FD02E962A4C2D0ECDDB8F44AC094FACA8333AE94110A5B10DA539C24A96F08530E7699E3F705165CF14B7F90A2F32ED28D21615F91D7C808AC566D6EEEF6773450AB53542CDAC337C3124530CB16319752267C3422149D41543D8742586BAB578F4E06360745AE0BD8F0E800D1920DC1F3661287367A78967458383A82465C5D966E7299EFCF58BD860185F96655E1F8D300F6B096DFE883CF15"; 5 | immutable qs = "D9757338E9A6B363F227F3104EDEF6240C0CAF53B7D509F48870553C4A821F460469AE5616301B9CC30FBF4598A176B84284AF3A41D697A34CDC2C8D88A4C4BE82AE8DB5347511FE5B4DD915CA6A728CCFD0444CE38FC7190824059D86A9083C273581EA5AD1D5E3A8D8EC6858F291A5EADA98B0F5FD7C8E8CA6226657B8B7955796B22899B087714E293A86C78D42A7021754A6220F1D0A9588C280DD9AEC376E421D539F30A3053D95C7D70F24B471D14ECF282FA3E0B1CED2C405BA22404F3B75CD961A46097D7C098324FC47281D298734DA0DFCD8AF82E685657C926672727296147867EAEDFDEF89A79DE81FF104CF7D9157EF65A1BC333C98A7FED685"; 6 | immutable es = ps ~ qs; 7 | 8 | void testStd() 9 | { 10 | import std.bigint; 11 | BigInt p = "0x" ~ ps; 12 | BigInt q = "0x" ~ qs; 13 | BigInt m = p; 14 | m *= q; 15 | BigInt e = "0x" ~ es; 16 | BigInt b = e; 17 | b = powmod(b, e, m); 18 | debug dout << b << endl; 19 | } 20 | 21 | void testMir() 22 | { 23 | import mir.bignum.integer; 24 | auto p = BigInt!64.fromHexString(ps); 25 | auto q = BigInt!64.fromHexString(qs); 26 | BigInt!64 m = p; 27 | m *= q; 28 | auto e = BigInt!64.fromHexString(es); 29 | BigInt!64 b = e; 30 | b.powMod(e, m); 31 | debug dout << b << endl; 32 | } 33 | 34 | void testGmp() 35 | { 36 | import gmp.z : BigInt = MpZ, powmod; 37 | import std.algorithm.mutation : move; 38 | auto p = BigInt.fromHexString(ps); 39 | auto q = BigInt.fromHexString(qs); 40 | BigInt m = p.move(); 41 | m *= q; 42 | auto e = BigInt.fromHexString(es); 43 | BigInt b = e.dup; 44 | b = b.powmod(e, m); 45 | debug dout << b << endl; 46 | } 47 | 48 | void main() 49 | { 50 | version (assert) 51 | { 52 | testStd(); 53 | testMir(); 54 | testGmp(); 55 | dout << "please compile with --build=release" << endl; 56 | } 57 | else 58 | { 59 | import std.system: os; 60 | const res = 10.benchmark!(testStd, testMir, testGmp); 61 | const mirRatio = double(res[0].total!"usecs") / res[1].total!"usecs"; 62 | const gmpRatio = double(res[0].total!"usecs") / res[2].total!"usecs"; 63 | dout 64 | << "--------------------------------------------" << endl 65 | << "gmp speedup = " << cast(int)((gmpRatio - 1) * 100_0) / 10.0 << "%" << endl 66 | << "mir speedup = " << cast(int)((mirRatio - 1) * 100_0) / 10.0 << "%" << endl 67 | << "std = " << res[0] << endl 68 | << "mir = " << res[1] << endl 69 | << "gmp = " << res[2] << endl 70 | << " ............... " << size_t.sizeof * 8 << "bit " << os << " ............... " << endl 71 | << "--------------------------------------------" 72 | << endl; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /source/mir/type_info.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Type Information) 3 | 4 | Type Information implementation compatible with BetterC mode. 5 | 6 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 7 | Authors: Ilia Ki 8 | 9 | Macros: 10 | NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 11 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 12 | +/ 13 | module mir.type_info; 14 | 15 | /++ 16 | +/ 17 | struct mir_type_info 18 | { 19 | /// 20 | extern(C) 21 | void function(void*) @safe pure nothrow @nogc destructor; 22 | /++ 23 | Note: Negative values are used for classes to indicate that 24 | +/ 25 | int size; 26 | } 27 | 28 | /++ 29 | +/ 30 | ref immutable(mir_type_info) mir_get_type_info(T)() @trusted 31 | { 32 | import std.traits: Unqual, hasElaborateDestructor; 33 | 34 | static if (is(T == class)) 35 | enum sizeof = __traits(classInstanceSize, T); 36 | else 37 | enum sizeof = T.sizeof; 38 | 39 | static if (!is(T == Unqual!T)) 40 | { 41 | return mir_get_type_info!(Unqual!T); 42 | } 43 | else 44 | static if (hasElaborateDestructor!T) 45 | { 46 | import std.traits: SetFunctionAttributes, functionAttributes; 47 | alias fun = void function(void*) @safe pure nothrow @nogc; 48 | extern(C) 49 | static void destroy_impl(void* ptr) nothrow 50 | { 51 | static if (is(T == class)) 52 | T inst() return @trusted 53 | { 54 | return cast(T)ptr; 55 | } 56 | else 57 | ref T inst() return @trusted 58 | { 59 | return *cast(T*)ptr; 60 | } 61 | version(assert) 62 | destroy!true(inst()); 63 | else 64 | destroy!false(inst()); 65 | } 66 | 67 | static immutable ti = mir_type_info(cast(SetFunctionAttributes!(fun, "C", functionAttributes!fun))&destroy_impl, sizeof); 68 | return ti; 69 | } 70 | else 71 | { 72 | return .mir_get_type_info!sizeof; 73 | } 74 | } 75 | 76 | /++ 77 | +/ 78 | ref immutable(mir_type_info) mir_get_type_info(uint sizeof)() 79 | { 80 | static immutable ti = mir_type_info(null, sizeof); 81 | return ti; 82 | } 83 | 84 | package template hasDestructor(T) 85 | { 86 | import std.traits: Unqual; 87 | 88 | static if (is(T == struct)) 89 | { 90 | static if (__traits(hasMember, Unqual!T, "__xdtor")) 91 | enum hasDestructor = __traits(isSame, Unqual!T, __traits(parent, T.init.__xdtor)); 92 | else 93 | enum hasDestructor = false; 94 | } 95 | else 96 | static if (is(T == class)) 97 | { 98 | enum hasDestructor = __traits(hasMember, Unqual!T, "__xdtor"); 99 | } 100 | else 101 | { 102 | enum hasDestructor = false; 103 | } 104 | } 105 | 106 | package const(void)* mir_get_payload_ptr(T)() 107 | { 108 | import std.traits: Unqual; 109 | 110 | static if (!is(T == Unqual!T)) 111 | { 112 | return mir_get_payload_ptr!(Unqual!T); 113 | } 114 | else 115 | static if (is(T == class)) 116 | { 117 | return typeid(T).initializer.ptr; 118 | } 119 | else 120 | static if (__traits(isZeroInit, T) || __traits(isFloating, T)) 121 | { 122 | return null; 123 | } 124 | else 125 | { 126 | static immutable payload = T.init; 127 | return &payload; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![codecov.io](https://codecov.io/github/libmir/mir-algorithm/coverage.svg?branch=master)](https://codecov.io/github/libmir/mir-algorithm?branch=master) 2 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/libmir/mir-algorithm/ci.yml?branch=master)](https://github.com/libmir/mir-algorithm/actions) 3 | [![Circle CI](https://circleci.com/gh/libmir/mir-algorithm.svg?style=svg)](https://circleci.com/gh/libmir/mir-algorithm) 4 | 5 | [![Dub downloads](https://img.shields.io/dub/dt/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm) 6 | [![Dub downloads](https://img.shields.io/dub/dm/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm) 7 | [![License](https://img.shields.io/dub/l/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm) 8 | [![Latest version](https://img.shields.io/dub/v/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm) 9 | [![Bountysource](https://www.bountysource.com/badge/team?team_id=145399&style=bounties_received)](https://www.bountysource.com/teams/libmir) 10 | 11 | Mir Algorithm 12 | ============= 13 | 14 | #### [API Documentation](http://mir-algorithm.libmir.org) 15 | 16 | #### Blogs 17 | - Tasty D - [Multidimensional Arrays in D](https://tastyminerals.github.io/tasty-blog/dlang/2020/03/22/multidimensional_arrays_in_d.html) 18 | - Tasty D - [Using External D Libraries in D Scripts and Projects](https://tastyminerals.github.io/tasty-blog/dlang/2020/03/01/how_to_use_external_libraries_in_d_project.html) 19 | - Tasty D - [Pretty-printing D Arrays](https://tastyminerals.github.io/tasty-blog/dlang/2020/06/25/pretty_printing_arrays.html) 20 | - Shigeki Karita - [D言語で数値計算 mir-algorithm](https://shigekikarita.github.io/blog/2017/09/22/026.html) 21 | - Shigeki Karita - [D言語(mir)でNumPyを拡張する](https://qiita.com/ShigekiKarita/items/af84b0ef864608ee1f21) (mir-pybuffer integration) 22 | - [Mir Blog](http://blog.mir.dlang.io/) (deprecated) 23 | 24 | #### [Mir Type System for .NET](https://github.com/libmir/mir.net) 25 | 26 | #### Example (3 sec) 27 | ```d 28 | /+dub.sdl: 29 | dependency "mir-algorithm" version="~>2.0.0" 30 | +/ 31 | 32 | void main() 33 | { 34 | import mir.ndslice; 35 | 36 | auto matrix = slice!double(3, 4); 37 | matrix[] = 0; 38 | matrix.diagonal[] = 1; 39 | 40 | auto row = matrix[2]; 41 | row[3] = 6; 42 | assert(matrix[2, 3] == 6); // D & C index order 43 | 44 | import mir.stdio; 45 | matrix.writeln; 46 | // prints [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 6.0]] 47 | } 48 | ``` 49 | 50 | [![Open on run.dlang.io](https://img.shields.io/badge/run.dlang.io-open-blue.svg)](https://run.dlang.io/is/OdGbCj) 51 | 52 | #### Example (30 sec) 53 | ```d 54 | /+dub.sdl: 55 | dependency "mir-algorithm" version="~>2.0.0" 56 | +/ 57 | void main() 58 | { 59 | import mir.ndslice; 60 | import std.stdio : writefln; 61 | 62 | enum fmt = "%(%(%.2f %)\n%)\n"; 63 | 64 | // Magic sqaure. 65 | // `a` is lazy, each element is computed on-demand. 66 | auto a = magic(5).as!float; 67 | writefln(fmt, a); 68 | 69 | // 5x5 grid on sqaure [1, 2] x [0, 1] with values x * x + y. 70 | // `b` is lazy, each element is computed on-demand. 71 | auto b = linspace!float([5, 5], [1f, 2f], [0f, 1f]).map!"a * a + b"; 72 | writefln(fmt, b); 73 | 74 | // allocate a 5 x 5 contiguous matrix 75 | auto c = slice!float(5, 5); 76 | 77 | c[] = transposed(a + b / 2); // no memory allocations here 78 | // 1. b / 2 - lazy element-wise operation with scalars 79 | // 2. a + (...) - lazy element-wise operation with other slices 80 | // Both slices must be `contiguous` or one-dimensional. 81 | // 3. transposed(...) - trasposes matrix view. The result is `universal` (numpy-like) matrix. 82 | // 5. c[] = (...) -- performs element-wise assignment. 83 | writefln(fmt, c); 84 | } 85 | ``` 86 | 87 | [![Open on run.dlang.io](https://img.shields.io/badge/run.dlang.io-open-blue.svg)](https://run.dlang.io/is/67Gi6X) 88 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('mir-algorithm', 'cpp', 'd', version : '3.7.0', license: 'Apache-2.0', 2 | default_options : ['cpp_std=c++1z']) 3 | 4 | description = 'Mir Algorithm - Dlang Core Library' 5 | 6 | subprojects = ['mir-core'] 7 | 8 | has_cpp_headers = true 9 | 10 | sources_list = [ 11 | 'mir/algebraic_alias/ion', 12 | 'mir/algebraic_alias/json', 13 | 'mir/algebraic_alias/transform', 14 | 'mir/algorithm/iteration', 15 | 'mir/algorithm/setops', 16 | 'mir/annotated', 17 | 'mir/appender', 18 | 'mir/array/allocation', 19 | 'mir/base64', 20 | 'mir/bignum/decimal', 21 | 'mir/bignum/fixed', 22 | 'mir/bignum/fp', 23 | 'mir/bignum/integer', 24 | 'mir/bignum/internal/dec2float_table', 25 | 'mir/bignum/internal/dec2float', 26 | 'mir/bignum/internal/kernel', 27 | 'mir/bignum/internal/phobos_kernel', 28 | 'mir/bignum/internal/ryu/generic_128', 29 | 'mir/bignum/internal/ryu/table', 30 | 'mir/bignum/low_level_view', 31 | 'mir/combinatorics/package', 32 | 'mir/container/binaryheap', 33 | 'mir/cpp_export/numeric', 34 | 'mir/date', 35 | 'mir/ediff', 36 | 'mir/format_impl', 37 | 'mir/format', 38 | 'mir/graph/package', 39 | 'mir/graph/tarjan', 40 | 'mir/interpolate/constant', 41 | 'mir/interpolate/extrapolate', 42 | 'mir/interpolate/generic', 43 | 'mir/interpolate/linear', 44 | 'mir/interpolate/mod', 45 | 'mir/interpolate/package', 46 | 'mir/interpolate/polynomial', 47 | 'mir/interpolate/spline', 48 | 'mir/interpolate/utility', 49 | 'mir/lob', 50 | 'mir/math/func/expdigamma', 51 | 'mir/math/func/hermite', 52 | 'mir/math/func/normal', 53 | 'mir/math/numeric', 54 | 'mir/math/stat', 55 | 'mir/math/sum', 56 | 'mir/ndslice/allocation', 57 | 'mir/ndslice/chunks', 58 | 'mir/ndslice/concatenation', 59 | 'mir/ndslice/connect/cpython', 60 | 'mir/ndslice/dynamic', 61 | 'mir/ndslice/field', 62 | 'mir/ndslice/filling', 63 | 'mir/ndslice/fuse', 64 | 'mir/ndslice/internal', 65 | 'mir/ndslice/iterator', 66 | 'mir/ndslice/mutation', 67 | 'mir/ndslice/ndfield', 68 | 'mir/ndslice/package', 69 | 'mir/ndslice/slice', 70 | 'mir/ndslice/sorting', 71 | 'mir/ndslice/topology', 72 | 'mir/ndslice/traits', 73 | 'mir/numeric', 74 | 'mir/parse', 75 | 'mir/polynomial', 76 | 'mir/range', 77 | 'mir/rc/array', 78 | 'mir/rc/context', 79 | 'mir/rc/package', 80 | 'mir/rc/ptr', 81 | 'mir/rc/slim_ptr', 82 | 'mir/serde', 83 | 'mir/series', 84 | 'mir/small_array', 85 | 'mir/small_string', 86 | 'mir/string_map', 87 | 'mir/test', 88 | 'mir/timestamp', 89 | 'mir/type_info', 90 | ] 91 | 92 | sources = [] 93 | foreach s : sources_list 94 | sources += 'source/' + s + '.d' 95 | endforeach 96 | 97 | add_project_arguments([ 98 | '-preview=dip1008', 99 | '-lowmem', 100 | ], language: 'd') 101 | 102 | required_deps = [] 103 | 104 | foreach p : subprojects 105 | required_deps += dependency(p, fallback : [p, 'this_dep']) 106 | endforeach 107 | 108 | directories = ['source'] 109 | 110 | if has_cpp_headers 111 | directories += 'include' 112 | endif 113 | 114 | directories = include_directories(directories) 115 | 116 | this_lib = library(meson.project_name(), 117 | sources, 118 | include_directories: directories, 119 | install: true, 120 | version: meson.project_version(), 121 | dependencies: required_deps, 122 | ) 123 | 124 | this_dep = declare_dependency( 125 | link_with: [this_lib], 126 | include_directories: directories, 127 | dependencies: required_deps, 128 | ) 129 | 130 | test_versions = ['mir_test', 'mir_bignum_test', 'mir_bignum_test_llv', 'mir_ndslice_test', 'mir_secure_memory'] 131 | 132 | if has_cpp_headers 133 | install_subdir('include/', 134 | strip_directory :true, 135 | install_dir: 'include/', 136 | ) 137 | endif 138 | 139 | install_subdir('source/', 140 | strip_directory : true, 141 | install_dir: 'include/d/' + meson.project_name(), 142 | ) 143 | 144 | import('pkgconfig').generate(this_lib, 145 | description: description, 146 | subdirs: 'd/' + meson.project_name(), 147 | ) 148 | 149 | mir_algorithm_dep = this_dep 150 | mir_algorithm_lib = this_lib 151 | 152 | test_subdirs = ['cpp_example'] 153 | -------------------------------------------------------------------------------- /source/mir/rc/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Thread-safe reference-counted arrays and pointers) 3 | 4 | Mir provides two kinds of ref-counting pointers and two kinds of ref-counted arrays. 5 | 6 | The first kind pointer is `RCPtr`, which consists of a pointer to the context and pointer to the value.`RCPtr` supports structural and object polymorphism. It allows getting members with the same context as the root. 7 | The second kind is `SlimRCPtr`, which consist only from a pointer to the value. The context for `SlimRCPtr`is computed using a fixed-length memory shift from the pointer to the value. 8 | `SlimRCPtr` can be converted to an `RCPtr` and to an `RCArray` of the one element. 9 | 10 | `RCArray` is an array type without range primitives. It's length can't be reduced after construction.In the other hand, `Slice!(RCI!(T))` is an ndslice with all random-access range primitives.`RCI` is an iterator, which consists of `RCArray` and the pointer to the current element. 11 | `RCArray!T` can be converted or moved to `Slice!(RCI!(T))` using `.asSlice` or `.moveToSlice` methods respectively. 12 | 13 | $(RED `RCArray!T` aliases itself to a common D array slice. This feature may cause a segmentation fault in safe code if used without DIP1000.) 14 | 15 | `RCPtr!T` can be constructed from an element index and `RCArray!T` / `Slice!(RCI!(T))`. 16 | 17 | The package publicly imports $(MREF mir,rc,array), $(MREF mir,rc,ptr), and $(MREF mir,rc,slim_ptr). 18 | 19 | See_also: $(MREF mir,ndslice). 20 | +/ 21 | module mir.rc; 22 | 23 | /// 24 | public import mir.rc.array; 25 | /// 26 | public import mir.rc.ptr; 27 | /// 28 | public import mir.rc.slim_ptr; 29 | 30 | import mir.ndslice.slice; 31 | 32 | /++ 33 | Returns: shared pointer constructed from the slim shared pointer. 34 | 35 | The function has zero computation cost. 36 | +/ 37 | RCPtr!F toRCPtr(F)(return SlimRCPtr!F contextAndValue) @trusted 38 | { 39 | typeof(return) ret; 40 | ret._value = contextAndValue._value; 41 | ret._context = &contextAndValue.context(); 42 | contextAndValue._value = null; 43 | return ret; 44 | } 45 | 46 | /// 47 | version(mir_test) 48 | @safe pure @nogc nothrow 49 | unittest 50 | { 51 | import core.lifetime: move; 52 | struct S 53 | { 54 | double e; 55 | } 56 | struct C 57 | { 58 | int i; 59 | S s; 60 | } 61 | 62 | auto a = createSlimRC!C(10, S(3)); 63 | auto s = a.move.toRCPtr.shareMember!"s"; 64 | assert(s._counter == 1); 65 | assert(s.e == 3); 66 | } 67 | 68 | /++ 69 | Returns: shared pointer constructed with the `array`'s context and the value points to `array[index]`. 70 | 71 | The function has zero computation cost. 72 | +/ 73 | RCPtr!F toRCPtrAt(F)(return RCArray!F array, size_t index) @trusted 74 | if (!is(R == class) && !is(R == interface)) 75 | in { 76 | assert(index < array.length, "toRCPtrAt: index should be less then array.length"); 77 | } 78 | do { 79 | typeof(return) ret; 80 | ret._value = array._payload + index; 81 | ret._context = &array.context(); 82 | array._payload = null; 83 | return ret; 84 | } 85 | 86 | /// 87 | version(mir_test) 88 | @safe pure @nogc nothrow 89 | unittest 90 | { 91 | struct S { double e; } 92 | 93 | auto a = RCArray!S(10); 94 | a[3].e = 4; 95 | 96 | auto s = a.toRCPtrAt(3); 97 | 98 | assert(s._counter == 2); 99 | assert(s.e == 4); 100 | } 101 | 102 | /// ditto 103 | RCPtr!F toRCPtrAt(F)(return Slice!(RCI!F) array, size_t index) @trusted 104 | if (!is(R == class) && !is(R == interface)) 105 | in { 106 | assert(index < array.length, "toRCPtrAt: index should be less then array.length"); 107 | } 108 | do { 109 | typeof(return) ret; 110 | ret._value = array._iterator._iterator + index; 111 | ret._context = &array._iterator._array.context(); 112 | array._iterator._array._payload = null; 113 | return ret; 114 | } 115 | 116 | /// 117 | version(mir_test) 118 | @safe pure @nogc nothrow 119 | unittest 120 | { 121 | struct S { double e; } 122 | 123 | auto a = RCArray!S(10).asSlice[5 .. $]; 124 | a[3].e = 4; 125 | 126 | auto s = a.toRCPtrAt(3); 127 | 128 | assert(s._counter == 2); 129 | assert(s.e == 4); 130 | } 131 | 132 | /++ 133 | Returns: RC array length of one constructed from the slim shared pointer. 134 | 135 | The function has zero computation cost. 136 | +/ 137 | RCArray!F toRCArray(F)(return SlimRCPtr!F context) @trusted 138 | { 139 | typeof(return) ret; 140 | ret._payload = context._value; 141 | context._value = null; 142 | return ret; 143 | } 144 | 145 | /// 146 | version(mir_test) 147 | @safe pure @nogc nothrow 148 | unittest 149 | { 150 | struct S { double e; } 151 | 152 | auto a = createSlimRC!S(4).toRCArray; 153 | assert(a._counter == 1); 154 | assert(a.length == 1); 155 | assert(a[0].e == 4); 156 | } 157 | -------------------------------------------------------------------------------- /source/mir/algebraic_alias/json.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Mutable JSON value) 3 | 4 | This module contains a single alias definition and doesn't provide JSON serialization API. 5 | 6 | See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf); 7 | 8 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 9 | Authors: Ilia Ki 10 | Macros: 11 | +/ 12 | module mir.algebraic_alias.json; 13 | 14 | import mir.algebraic: Algebraic, This; 15 | /// 16 | public import mir.string_map: StringMap; 17 | 18 | /++ 19 | Definition union for $(LREF JsonAlgebraic). 20 | +/ 21 | union Json_ 22 | { 23 | /// 24 | typeof(null) null_; 25 | /// 26 | bool boolean; 27 | /// 28 | long integer; 29 | /// 30 | double float_; 31 | /// 32 | immutable(char)[] string; 33 | /// Self alias in array. 34 | This[] array; 35 | /// Self alias in $(MREF mir,string_map). 36 | StringMap!This object; 37 | } 38 | 39 | /++ 40 | JSON tagged algebraic alias. 41 | 42 | The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic). 43 | See also $(MREF mir,string_map) - ordered string-value associative array. 44 | +/ 45 | alias JsonAlgebraic = Algebraic!Json_; 46 | 47 | /// 48 | version(mir_test) 49 | @safe pure 50 | unittest 51 | { 52 | import mir.test: should; 53 | import mir.ndslice.topology: map; 54 | import mir.array.allocation: array; 55 | 56 | JsonAlgebraic value; 57 | 58 | StringMap!JsonAlgebraic object; 59 | 60 | // Default 61 | assert(value.isNull); 62 | assert(value.kind == JsonAlgebraic.Kind.null_); 63 | 64 | // Boolean 65 | value = object["bool"] = true; 66 | assert(!value.isNull); 67 | assert(value == true); 68 | assert(value.kind == JsonAlgebraic.Kind.boolean); 69 | // access 70 | assert(value.boolean == true); 71 | assert(value.get!bool == true); 72 | assert(value.get!"boolean" == true); 73 | assert(value.get!(JsonAlgebraic.Kind.boolean) == true); 74 | // nothrow access 75 | assert(value.trustedGet!bool == true); 76 | assert(value.trustedGet!"boolean" == true); 77 | assert(value.trustedGet!(JsonAlgebraic.Kind.boolean) == true); 78 | // checks 79 | assert(!value._is!string); 80 | assert(value._is!bool); 81 | assert(value._is!"boolean"); 82 | assert(value._is!(JsonAlgebraic.Kind.boolean)); 83 | 84 | // Null 85 | value = object["null"] = null; 86 | assert(value.isNull); 87 | assert(value == null); 88 | assert(value.kind == JsonAlgebraic.Kind.null_); 89 | // access 90 | assert(value.null_ == null); 91 | assert(value.get!(typeof(null)) == null); 92 | assert(value.get!(JsonAlgebraic.Kind.null_) == null); 93 | 94 | // String 95 | value = object["string"] = "s"; 96 | assert(value.kind == JsonAlgebraic.Kind.string); 97 | assert(value == "s"); 98 | // access 99 | // Yep, `string` here is an alias to `get!(immutable(char)[])` method 100 | assert(value.string == "s"); 101 | // `string` here is an alias of type `immutable(char)[]` 102 | assert(value.get!string == "s"); 103 | assert(value.get!"string" == "s"); 104 | // finally, `string` here is an enum meber 105 | assert(value.get!(JsonAlgebraic.Kind.string) == "s"); 106 | 107 | // Integer 108 | value = object["integer"] = 4; 109 | assert(value.kind == JsonAlgebraic.Kind.integer); 110 | assert(value == 4); 111 | assert(value != 4.0); 112 | assert(value.integer == 4); 113 | 114 | // Float 115 | value = object["float"] = 3.0; 116 | assert(value.kind == JsonAlgebraic.Kind.float_); 117 | assert(value != 3); 118 | assert(value == 3.0); 119 | assert(value.float_ == 3.0); 120 | 121 | // Array 122 | JsonAlgebraic[] arr = [0, 1, 2, 3, 4].map!JsonAlgebraic.array; 123 | 124 | value = object["array"] = arr; 125 | assert(value.kind == JsonAlgebraic.Kind.array); 126 | assert(value == arr); 127 | assert(value.array[3] == 3); 128 | 129 | // Object 130 | assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]); 131 | object.values[0] = "false"; 132 | assert(object["bool"] == "false"); // it is a string now 133 | object.remove("bool"); // remove the member 134 | 135 | value = object["array"] = object; 136 | assert(value.kind == JsonAlgebraic.Kind.object); 137 | assert(value.object.keys is object.keys); 138 | 139 | JsonAlgebraic[string] aa = object.toAA; 140 | object = aa.StringMap!JsonAlgebraic; 141 | 142 | JsonAlgebraic fromAA = ["a" : JsonAlgebraic(3), "b" : JsonAlgebraic("b")]; 143 | assert(fromAA.object["a"] == 3); 144 | assert(fromAA.object["b"] == "b"); 145 | 146 | // object foreach iteration 147 | long sum; 148 | foreach (ref key, ref val; fromAA.object) 149 | if (key == "a") 150 | sum += val.get!long; 151 | sum.should == 3; 152 | } 153 | -------------------------------------------------------------------------------- /index.d: -------------------------------------------------------------------------------- 1 | Ddoc 2 | 3 | $(P Generic Algorithm Collection.) 4 | 5 | $(P The following table is a quick reference guide for which Mir Algorithm modules to 6 | use for a given category of functionality.) 7 | 8 | $(BOOKTABLE , 9 | $(TR 10 | $(TH Modules) 11 | $(TH Description) 12 | ) 13 | $(LEADINGROW Accessories) 14 | $(TR $(TDNW $(MREF mir,algebraic_alias,ion)) $(TD Mutable Amazon's Ion value)) 15 | $(TR $(TDNW $(MREF mir,algebraic_alias,json)) $(TD Mutable JSON value)) 16 | $(TR $(TDNW $(MREF mir,algebraic_alias,transform)) $(TD Mutation algorithms for Ion/JSON-like values)) 17 | $(TR $(TDNW $(MREF mir,annotated)) $(TD Mutable generic Annotated value)) 18 | $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir)) 19 | $(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities)) 20 | $(TR $(TDNW $(MREF mir,lob)) $(TD Binar and Char Large Objects )) 21 | $(TR $(TDNW $(MREF mir,parse)) $(TD @nogc Parsing Utilities)) 22 | $(TR $(TDNW $(MREF mir,range)) $(TD Ranges)) 23 | $(TR $(TDNW $(MREF mir,serde)) $(TD Utilities for serialization libraries )) 24 | $(TR $(TDNW $(MREF mir,small_array)) $(TD Generic Small Arrays)) 25 | $(TR $(TDNW $(MREF mir,small_string)) $(TD Generic Small Strings)) 26 | $(TR $(TDNW $(MREF mir,string_map)) $(TD Ordered string-value associative array with fast lookup)) 27 | $(TR $(TDNW $(MREF mir,test)) $(TD Testing utilities)) 28 | $(LEADINGROW Date and time) 29 | $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability)) 30 | $(TR $(TDNW $(MREF mir,timestamp)) $(TD General purpose timestamp implementation with arbitrary precision )) 31 | $(LEADINGROW NDarrays and Algorithms) 32 | $(TR $(TDNW $(MREF mir,algorithm,iteration)) $(TD Mir & BetterC rework of Phobos.)) 33 | $(TR $(TDNW $(MREF mir,algorithm,setops)) $(TD Mir & BetterC rework of Phobos.)) 34 | $(TR $(TDNW $(MREF mir,ndslice)★) $(TD Package for ndarrays and iteration algorithms.)) 35 | $(TR $(TDNW $(MREF mir,range)) $(TD Additional primitive ranges. See also $(MREF mir,ndslice), which contains a lot of range constructos.)) 36 | $(LEADINGROW Math) 37 | $(TR $(TDNW $(MREF mir,interpolate)★) $(TD Interpolation algorithms)) 38 | $(TR $(TDNW $(MREF mir,math,numeric)) $(TD Simple numeric algorithms)) 39 | $(TR $(TDNW $(MREF mir,math,stat)) $(TD Basic API for statistics)) 40 | $(TR $(TDNW $(MREF mir,math,sum)) $(TD Various precise summation algorithms)) 41 | $(TR $(TDNW $(MREF mir,numeric)) $(TD Basic numeric optimisations)) 42 | $(TR $(TDNW $(MREF mir,polynomial)) $(TD Polynomial ref-counted structure)) 43 | $(TR $(TDNW $(MREF mir,ediff)) $(TD Expression differentiation)) 44 | $(TR $(TDNW $(MREF mir,math,func,expdigamma)) $(TD `exp(digamma(x))`)) 45 | $(TR $(TDNW $(MREF mir,math,func,normal)) $(TD Normal Distribution API)) 46 | $(LEADINGROW Reference counting) 47 | $(TR $(TDNW $(MREF mir,rc,array)) $(TD Thread safe reference count array and the iterator to adopt it to ndslice.)) 48 | $(TR $(TDNW $(MREF mir,rc,ptr)) $(TD Thread safe reference count pointer with polymorphism support for strucs and objects.)) 49 | $(TR $(TDNW $(MREF mir,rc,slim_ptr)) $(TD Thread safe reference count pointer for strucs and objects.)) 50 | $(TR $(TDNW $(MREF mir,rc)) $(TD Reference counting package and RC conversion utilities.)) 51 | $(LEADINGROW Containers) 52 | $(TR $(TDNW $(MREF mir,appender)) $(TD Scoped Buffer.)) 53 | $(TR $(TDNW $(MREF mir,container,binaryheap)★) $(TD Mir & BetterC rework of Phobos.)) 54 | $(TR $(TDNW $(MREF mir,series)★) $(TD Generic series suitable for time-series or semi-immutable ordered maps with CPU cache friendly binary search.)) 55 | $(LEADINGROW Graphs) 56 | $(TR $(TDNW $(MREF mir,graph,tarjan)★) $(TD Tarjan's strongly connected components algorithm)) 57 | $(TR $(TDNW $(MREF mir,graph)) $(TD Basic routines to work with graphs)) 58 | $(LEADINGROW Big Numbers (partial implementation)) 59 | $(TR $(TDNW $(MREF mir,bignum, decimal)) $(TD Stack-allocated decimal type)) 60 | $(TR $(TDNW $(MREF mir,bignum, fixed)) $(TD Stack-allocated fixed length unsigned integer type (like 256bit integers).)) 61 | $(TR $(TDNW $(MREF mir,bignum, fp)) $(TD Stack-allocated fixed length software floating point type.)) 62 | $(TR $(TDNW $(MREF mir,bignum, integer)) $(TD Stack-allocated integer type.)) 63 | $(TR $(TDNW $(MREF mir,bignum, low_level_view)) $(TD Low-level universal number representation formats.)) 64 | $(LEADINGROW Combinatrorics) 65 | $(TR $(TDNW $(MREF mir,combinatorics)★) $(TD Combinations, combinations with repeats, cartesian power, permutations.)) 66 | $(LEADINGROW Interconnection with other languages) 67 | $(TR $(TDNW $(MREF mir,ndslice,connect,cpython)) $(TD Utilities for $(HTTPS docs.python.org/3/c-api/buffer.html, Python Buffer Protocol))) 68 | ) 69 | 70 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 71 | 72 | Macros: 73 | TITLE=Mir Algorithm 74 | WIKI=Mir Algorithm 75 | DDOC_BLANKLINE= 76 | _= 77 | -------------------------------------------------------------------------------- /source/mir/test.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Testing utilities 3 | 4 | Authors: Ilya Yaroshenko 5 | +/ 6 | module mir.test; 7 | 8 | import mir.exception: MirError; 9 | 10 | private noreturn assumeAllAttrAndCall(scope const void delegate() t) 11 | @nogc pure nothrow @trusted { 12 | (cast(const void delegate() @safe pure nothrow @nogc) t)(); 13 | assert(0); 14 | } 15 | 16 | /// 17 | struct ShouldApprox(T, F = T) 18 | if ((__traits(isFloating, T) || __traits(hasMember, T, "approxEqual")) && __traits(isFloating, F)) 19 | { 20 | /// 21 | T value; 22 | /// 23 | F maxRelDiff = 0x1p-20f; 24 | /// 25 | F maxAbsDiff = 0x1p-20f; 26 | 27 | /// 28 | void opEquals(T expected, string file = __FILE__, int line = __LINE__) @safe pure nothrow @nogc 29 | { 30 | import mir.format: stringBuf, getData; 31 | import mir.math.common: approxEqual; 32 | if (value.approxEqual(expected, maxRelDiff, maxAbsDiff)) 33 | return; 34 | auto buf = stringBuf; 35 | assumeAllAttrAndCall({ 36 | throw new MirError(buf 37 | << "expected approximately " << expected 38 | << ", got " << value 39 | << ", maxRelDiff = " << maxRelDiff 40 | << ", maxAbsDiff = " << maxAbsDiff 41 | << getData, file, line); 42 | }); 43 | assert(0); 44 | } 45 | } 46 | 47 | /// ditto 48 | ShouldApprox!T shouldApprox(T)(const T value, const T maxRelDiff = T(0x1p-20f), const T maxAbsDiff = T(0x1p-20f)) 49 | if (__traits(isFloating, T)) 50 | { 51 | return typeof(return)(value, maxRelDiff, maxAbsDiff); 52 | } 53 | 54 | /// 55 | version(mir_test) 56 | unittest 57 | { 58 | 1.0.shouldApprox == 1 + 9e-7; 59 | shouldApprox(1 + 9e-7, 1e-6, 1e-6) == 1; 60 | } 61 | 62 | /// ditto 63 | ShouldApprox!(T, F) shouldApprox(T, F)(const T value, const F maxRelDiff = double(0x1p-20f), const F maxAbsDiff = double(0x1p-20f)) 64 | if (__traits(hasMember, T, "approxEqual") && __traits(isFloating, F)) 65 | { 66 | return typeof(return)(value, maxRelDiff, maxAbsDiff); 67 | } 68 | 69 | /// 70 | version(mir_test) 71 | unittest 72 | { 73 | static struct C { 74 | double re, im; 75 | auto approxEqual(C rhs, double maxRelDiff, double maxAbsDiff) 76 | { 77 | import mir.math.common: approxEqual; 78 | return approxEqual(re, rhs.re, maxRelDiff, maxAbsDiff) && approxEqual(im, rhs.im, maxRelDiff, maxAbsDiff); 79 | } 80 | } 81 | C(1.0, 1.0).shouldApprox == C(1 + 9e-7, 1 - 9e-7); 82 | } 83 | 84 | /// 85 | struct Should(T) 86 | { 87 | /// 88 | T value; 89 | 90 | static if(!is(immutable T == immutable ubyte[])) 91 | /// 92 | void opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__) @trusted 93 | { 94 | import mir.format: stringBuf, getData; 95 | static if (__traits(isFloating, R)) 96 | { 97 | if (expected != expected && value != value) 98 | return; 99 | } 100 | if (value == expected) 101 | return; 102 | auto buf = stringBuf; 103 | buf << "mir.test.should:\n"; 104 | buf << "expected " << expected << "\n" 105 | << " got " << value; 106 | assumeAllAttrAndCall({ throw new MirError(buf << getData, file, line); }); 107 | } 108 | else 109 | /// ditto 110 | void opEquals(scope const ubyte[] expected, string file = __FILE__, int line = __LINE__) @trusted 111 | pure nothrow @nogc 112 | { 113 | import mir.format: stringBuf, getData; 114 | if (value == expected) 115 | return; 116 | auto buf = stringBuf; 117 | import mir.format: printHexArray; 118 | import mir.ndslice.topology: map; 119 | buf << "mir.test.should:\n"; 120 | buf << "expected "; 121 | buf.printHexArray(expected); 122 | buf << "\n"; 123 | buf << " got "; 124 | buf.printHexArray( value); 125 | assumeAllAttrAndCall({ throw new MirError(buf << getData, file, line); }); 126 | } 127 | } 128 | 129 | /// ditto 130 | Should!T should(T)(T value) 131 | { 132 | return typeof(return)(value); 133 | } 134 | 135 | /// 136 | version(mir_test) 137 | unittest 138 | { 139 | 1.0.should == 1; 140 | should(1) == 1; 141 | 142 | ubyte[] val = [0, 2, 3]; 143 | val.should == [0, 2, 3]; 144 | } 145 | 146 | /// 147 | void should(alias fun = "a == b", T, R)(const T value, const R expected, string file = __FILE__, int line = __LINE__) 148 | { 149 | import mir.functional; 150 | import mir.format: stringBuf, getData; 151 | if (naryFun!fun(value, expected)) 152 | return; 153 | auto buf = stringBuf; 154 | buf << fun.stringof 155 | << " returns false" 156 | << " for a = " << value 157 | << ", b = " << expected; 158 | throw new MirError(buf << getData, file, line); 159 | } 160 | 161 | /// 162 | version(mir_test) 163 | unittest 164 | { 165 | 1.0.should!"a < b"(1.3); 166 | } 167 | -------------------------------------------------------------------------------- /source/mir/graph/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Basic routines to work with graphs. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 6 | Authors: Ilia Ki 7 | 8 | Macros: 9 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, graph, $1)$(NBSP) 10 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 11 | +/ 12 | 13 | module mir.graph; 14 | 15 | import mir.math.common: fmamath; 16 | 17 | import mir.series; 18 | import mir.rc.array; 19 | import mir.ndslice.iterator: ChopIterator; 20 | 21 | /// 22 | alias GraphIterator(I = uint, J = size_t) = ChopIterator!(size_t*, uint*); 23 | /// 24 | alias Graph(I = uint, J = size_t) = Slice!(GraphIterator!(I, J)); 25 | /// 26 | alias GraphSeries(T, I = uint, J = size_t) = Series!(T*, GraphIterator!(I, J)); 27 | 28 | /// 29 | alias RCGraphIterator(I = uint, J = size_t) = ChopIterator!(RCI!size_t, RCI!uint); 30 | /// 31 | alias RCGraph(I = uint, J = size_t) = Slice!(RCGraphIterator!(I, J)); 32 | /// 33 | alias RCGraphSeries(T, I = uint, J = size_t) = Series!(RCI!T, RCGraphIterator!(I, J)); 34 | 35 | private static immutable exc_msg = "graphSeries: graph should contains keys for all vertixes"; 36 | version(D_Exceptions) 37 | { 38 | private static immutable exception = new Exception(exc_msg); 39 | } 40 | 41 | /++ 42 | Params: 43 | aaGraph = graph that is represented as associative array 44 | Returns: 45 | A graph series composed of keys (sorted `.index`) and arrays of indeces (`.data`) 46 | Complexity: `O(log(V) (V + E))` 47 | +/ 48 | @fmamath 49 | GraphSeries!(T, I, J) graphSeries(I = uint, J = size_t, T, Range)(in Range[T] aaGraph) 50 | { 51 | import mir.array.allocation: array; 52 | import mir.ndslice.sorting; 53 | import mir.ndslice; 54 | auto keys = aaGraph.byKey.array.sliced; 55 | sort(keys); 56 | size_t dataLength; 57 | foreach (ref v; aaGraph) 58 | dataLength += v.length; 59 | auto data = uninitSlice!I(dataLength); 60 | auto components = uninitSlice!J(keys.length + 1); 61 | size_t dataIndex; 62 | 63 | foreach (i; 0 .. keys.length) 64 | { 65 | components[i] = cast(J) dataIndex; 66 | foreach(ref elem; aaGraph[keys[i]]) 67 | { 68 | import mir.ndslice.sorting: transitionIndex; 69 | auto index = keys.transitionIndex(elem); 70 | if (index >= keys.length) 71 | { 72 | version(D_Exceptions) 73 | { import mir.exception : toMutable; throw exception.toMutable; } 74 | else 75 | assert(0, exc_msg); 76 | } 77 | data[dataIndex++] = cast(I) index; 78 | } 79 | } 80 | components[keys.length] = dataIndex; 81 | auto sliceable = (() @trusted => data.ptr)(); 82 | return keys.series(sliceable.chopped(components)); 83 | } 84 | 85 | /// 86 | pure version(mir_test) unittest 87 | { 88 | auto gs = [ 89 | "b" : ["a"], 90 | "a" : ["b", "c"], 91 | "c" : ["b"], 92 | ].graphSeries; 93 | 94 | assert (gs.index == ["a", "b", "c"]); // sorted 95 | assert (gs.data == [ 96 | [1, 2], // a 97 | [0], // b 98 | [1], // c 99 | ]); 100 | } 101 | 102 | /++ 103 | Params: 104 | graph = graph that is represented a series 105 | Returns: 106 | A graph as an arrays of indeces 107 | Complexity: `O(log(V) (V + E))` 108 | +/ 109 | @fmamath 110 | RCGraph!(I, J) rcgraph(I = uint, J = size_t, KeyIterator, RangeIterator)(Series!(KeyIterator, RangeIterator) graph) 111 | { 112 | import mir.array.allocation: array; 113 | import mir.ndslice.sorting; 114 | import mir.ndslice; 115 | auto scopeGraph = graph.lightScope; 116 | auto keys = scopeGraph.index; 117 | auto graphData = scopeGraph.data; 118 | size_t dataLength; 119 | foreach (ref v; graphData) 120 | dataLength += v.length; 121 | auto data = rcslice!I(dataLength); 122 | auto components = rcslice!J(keys.length + 1); 123 | size_t dataIndex; 124 | 125 | foreach (i; 0 .. keys.length) 126 | { 127 | components[i] = cast(J) dataIndex; 128 | foreach(ref elem; graphData[i]) 129 | { 130 | import mir.ndslice.sorting: transitionIndex; 131 | auto index = keys.transitionIndex(elem); 132 | if (index >= keys.length) 133 | { 134 | version(D_Exceptions) 135 | { import mir.exception : toMutable; throw exception.toMutable; } 136 | else 137 | assert(0, exc_msg); 138 | } 139 | data[dataIndex++] = cast(I) index; 140 | } 141 | } 142 | components[keys.length] = dataIndex; 143 | return data._iterator.chopped(components); 144 | } 145 | 146 | /// 147 | @safe pure @nogc version(mir_test) 148 | unittest 149 | { 150 | import mir.series: series; 151 | 152 | static immutable keys = ["a", "b", "c"]; 153 | static immutable data = [ 154 | ["b", "c"], 155 | ["a"], 156 | ["b"], 157 | ]; 158 | 159 | static immutable graphValue = [ 160 | [1, 2], // a 161 | [0], // b 162 | [1], // c 163 | ]; 164 | 165 | assert (series(keys, data).rcgraph == graphValue); 166 | } 167 | -------------------------------------------------------------------------------- /source/mir/algebraic_alias/ion.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Mutable Ion value) 3 | 4 | This module contains a single alias definition and doesn't provide Ion serialization API. 5 | 6 | See_also: Ion library $(MIR_PACKAGE mir-ion) 7 | 8 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 9 | Authors: Ilia Ki 10 | Macros: 11 | +/ 12 | module mir.algebraic_alias.ion; 13 | 14 | import mir.algebraic: Algebraic, This; 15 | /// 16 | public import mir.annotated: Annotated; 17 | /// 18 | public import mir.lob: Clob, Blob; 19 | /// 20 | public import mir.string_map: StringMap; 21 | /// 22 | public import mir.timestamp: Timestamp; 23 | 24 | 25 | /++ 26 | Definition union for $(LREF IonAlgebraic). 27 | +/ 28 | union Ion_ 29 | { 30 | /// 31 | typeof(null) null_; 32 | /// 33 | bool boolean; 34 | /// 35 | long integer; 36 | /// 37 | double float_; 38 | /// 39 | immutable(char)[] string; 40 | /// 41 | Blob blob; 42 | /// 43 | Clob clob; 44 | /// 45 | Timestamp timestamp; 46 | /// Self alias in array. 47 | This[] array; 48 | /// Self alias in $(MREF mir,string_map). 49 | StringMap!This object; 50 | /// Self alias in $(MREF mir,annotated). 51 | Annotated!This annotated; 52 | } 53 | 54 | /++ 55 | Ion tagged algebraic alias. 56 | 57 | The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic). 58 | See also $(MREF mir,string_map) - ordered string-value associative array. 59 | +/ 60 | alias IonAlgebraic = Algebraic!Ion_; 61 | 62 | /// 63 | @safe pure 64 | version(mir_test) 65 | unittest 66 | { 67 | import mir.test: should; 68 | import mir.ndslice.topology: map; 69 | import mir.array.allocation: array; 70 | 71 | IonAlgebraic value; 72 | 73 | StringMap!IonAlgebraic object; 74 | 75 | // Default 76 | assert(value.isNull); 77 | assert(value.kind == IonAlgebraic.Kind.null_); 78 | 79 | // Boolean 80 | value = object["bool"] = true; 81 | assert(!value.isNull); 82 | assert(value == true); 83 | assert(value.kind == IonAlgebraic.Kind.boolean); 84 | // access 85 | assert(value.boolean == true); 86 | assert(value.get!bool == true); 87 | assert(value.get!"boolean" == true); 88 | assert(value.get!(IonAlgebraic.Kind.boolean) == true); 89 | // nothrow access 90 | assert(value.trustedGet!bool == true); 91 | assert(value.trustedGet!"boolean" == true); 92 | assert(value.trustedGet!(IonAlgebraic.Kind.boolean) == true); 93 | // checks 94 | assert(!value._is!string); 95 | assert(value._is!bool); 96 | assert(value._is!"boolean"); 97 | assert(value._is!(IonAlgebraic.Kind.boolean)); 98 | 99 | // Null 100 | value = object["null"] = null; 101 | assert(value.isNull); 102 | assert(value == null); 103 | assert(value.kind == IonAlgebraic.Kind.null_); 104 | // access 105 | assert(value.null_ == null); 106 | assert(value.get!(typeof(null)) == null); 107 | assert(value.get!(IonAlgebraic.Kind.null_) == null); 108 | 109 | // String 110 | value = object["string"] = "s"; 111 | assert(value.kind == IonAlgebraic.Kind.string); 112 | assert(value == "s"); 113 | // access 114 | // Yep, `string` here is an alias to `get!(immutable(char)[])` method 115 | assert(value.string == "s"); 116 | // `string` here is an alias of type `immutable(char)[]` 117 | assert(value.get!string == "s"); 118 | assert(value.get!"string" == "s"); 119 | // finally, `string` here is an enum meber 120 | assert(value.get!(IonAlgebraic.Kind.string) == "s"); 121 | 122 | // Integer 123 | value = object["integer"] = 4; 124 | assert(value.kind == IonAlgebraic.Kind.integer); 125 | assert(value == 4); 126 | assert(value != 4.0); 127 | assert(value.integer == 4); 128 | 129 | // Float 130 | value = object["float"] = 3.0; 131 | assert(value.kind == IonAlgebraic.Kind.float_); 132 | assert(value != 3); 133 | assert(value == 3.0); 134 | assert(value.float_ == 3.0); 135 | 136 | // Array 137 | IonAlgebraic[] arr = [0, 1, 2, 3, 4].map!IonAlgebraic.array; 138 | 139 | value = object["array"] = arr; 140 | assert(value.kind == IonAlgebraic.Kind.array); 141 | assert(value == arr); 142 | assert(value == [0, 1, 2, 3, 4].map!IonAlgebraic.array);// by value 143 | assert(value.array[3] == 3); 144 | 145 | // Object 146 | assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]); 147 | object.values[0] = "false"; 148 | assert(object["bool"] == "false"); // it is a string now 149 | object.remove("bool"); // remove the member 150 | 151 | value = object["array"] = object; 152 | assert(value.kind == IonAlgebraic.Kind.object); 153 | assert(value.object.keys is object.keys); 154 | 155 | IonAlgebraic[string] aa = object.toAA; 156 | object = aa.StringMap!IonAlgebraic; 157 | 158 | IonAlgebraic fromAA = ["a" : IonAlgebraic(3), "b" : IonAlgebraic("b")]; 159 | assert(fromAA.object["a"] == 3); 160 | assert(fromAA.object["b"] == "b"); 161 | 162 | // object foreach iteration 163 | long sum; 164 | foreach (ref key, ref val; fromAA.object) 165 | if (key == "a") 166 | sum += val.get!long; 167 | sum.should == 3; 168 | 169 | // annotations 170 | auto annotated = Annotated!IonAlgebraic(["birthday"], Timestamp("2001-01-01")); 171 | value = annotated; 172 | assert(value == annotated); 173 | value = annotated.IonAlgebraic; 174 | assert(value == annotated); 175 | } 176 | -------------------------------------------------------------------------------- /include/mir/numeric.h: -------------------------------------------------------------------------------- 1 | #ifndef MIR_NUMERIC 2 | 3 | #define MIR_NUMERIC 4 | 5 | #include 6 | #include 7 | 8 | enum class mir_find_root_status 9 | { 10 | /// Success 11 | success, 12 | /// 13 | badBounds, 14 | /// 15 | nanX, 16 | /// 17 | nanY, 18 | }; 19 | 20 | template 21 | struct mir_find_root_result 22 | { 23 | /// Left bound 24 | T ax = 0; 25 | /// Rifht bound 26 | T bx = 0; 27 | /// `f(ax)` or `f(axfabs()).fmin(T.max / 2).copysign(f(ax))`. 28 | T ay = 0; 29 | /// `f(bx)` or `f(bxfabs()).fmin(T.max / 2).copysign(f(bx))`. 30 | T by = 0; 31 | /// Amount of target function calls. 32 | unsigned int iterations = 0; 33 | 34 | /** 35 | Returns: self 36 | Required_versions:`D_Exceptions` 37 | Throws: `Exception` if $(LREF FindRootResult.status) isn't $(LREF mir_find_root_status.success). 38 | */ 39 | const mir_find_root_result& validate() const 40 | { 41 | switch(status()) 42 | { 43 | case mir_find_root_status::success: return *this; 44 | case mir_find_root_status::badBounds: throw std::domain_error("findRoot: f(ax) and f(bx) must have opposite signs to bracket the root."); 45 | case mir_find_root_status::nanX: throw std::domain_error("findRoot: ax or bx is NaN."); 46 | case mir_find_root_status::nanY: throw std::domain_error("findRoot: f(x) returned NaN."); 47 | default: throw std::domain_error("findRoot: unknown error."); 48 | } 49 | } 50 | 51 | /** 52 | Returns: $(LREF mir_find_root_status) 53 | */ 54 | mir_find_root_status status() const noexcept; 55 | 56 | /** 57 | A bound that corresponds to the minimal absolute function value. 58 | 59 | Returns: `!(fabs(ay) > fabs(by)) ? ax : bx` 60 | */ 61 | T x() const noexcept; 62 | 63 | /** 64 | The minimal of absolute function values. 65 | 66 | Returns: `!(fabs(ay) > fabs(by)) ? ay : by` 67 | */ 68 | T y() const noexcept; 69 | }; 70 | 71 | template 72 | T mir_internal_find_root_f(const void* ctx, T x) 73 | { 74 | return (*((const std::function*)ctx))(x); 75 | } 76 | 77 | template 78 | bool mir_internal_find_root_tolerance(const void* ctx, T a, T b) 79 | { 80 | return (*((const std::function*)ctx))(a, b); 81 | } 82 | 83 | mir_find_root_result mir_find_root( 84 | float ax, 85 | float bx, 86 | float fax, 87 | float fbx, 88 | float lower_bound, 89 | float upper_bound, 90 | unsigned int maxIterations, 91 | unsigned int steps, 92 | float (*f)(const void* ctx, float x), 93 | const void* f_ctx = NULL, 94 | bool (*tolerance)(const void* ctx, float a, float b) = NULL, 95 | const void* tolerance_ctx = NULL 96 | ); 97 | 98 | mir_find_root_result mir_find_root( 99 | double ax, 100 | double bx, 101 | double fax, 102 | double fbx, 103 | double lower_bound, 104 | double upper_bound, 105 | unsigned int maxIterations, 106 | unsigned int steps, 107 | double (*f)(const void* ctx, double x), 108 | const void* f_ctx = NULL, 109 | bool (*tolerance)(const void* ctx, double a, double b) = NULL, 110 | const void* tolerance_ctx = NULL 111 | ); 112 | 113 | mir_find_root_result mir_find_root( 114 | long double ax, 115 | long double bx, 116 | long double fax, 117 | long double fbx, 118 | long double lower_bound, 119 | long double upper_bound, 120 | unsigned int maxIterations, 121 | unsigned int steps, 122 | long double (*f)(const void* ctx, long double x), 123 | const void* f_ctx = NULL, 124 | bool (*tolerance)(const void* ctx, long double a, long double b) = NULL, 125 | const void* tolerance_ctx = NULL 126 | ); 127 | 128 | template 129 | mir_find_root_result mir_find_root( 130 | const std::function& f, 131 | const std::function& tolerance, 132 | const T a, 133 | const T b, 134 | const T fa = std::numeric_limits::quiet_NaN(), 135 | const T fb = std::numeric_limits::quiet_NaN(), 136 | const T lower_bound = std::numeric_limits::quiet_NaN(), 137 | const T upper_bound = std::numeric_limits::quiet_NaN(), 138 | unsigned int maxIterations = sizeof(T) * 16, 139 | unsigned int steps = 0 140 | ) 141 | { 142 | return mir_find_root( 143 | a, 144 | b, 145 | fa, 146 | fb, 147 | lower_bound, 148 | upper_bound, 149 | maxIterations, 150 | steps, 151 | &mir_internal_find_root_f, 152 | &f, 153 | &mir_internal_find_root_tolerance, 154 | &tolerance 155 | ); 156 | } 157 | 158 | template 159 | mir_find_root_result mir_find_root( 160 | const std::function& f, 161 | const T absoluteTolerance, 162 | const T a, 163 | const T b, 164 | const T fa = std::numeric_limits::quiet_NaN(), 165 | const T fb = std::numeric_limits::quiet_NaN(), 166 | const T lower_bound = std::numeric_limits::quiet_NaN(), 167 | const T upper_bound = std::numeric_limits::quiet_NaN(), 168 | unsigned int maxIterations = sizeof(T) * 16, 169 | unsigned int steps = 0 170 | ) 171 | { 172 | return mir_find_root( 173 | f, 174 | [absoluteTolerance](T a, T b) { return b - a < absoluteTolerance; }, 175 | a, 176 | b, 177 | fa, 178 | fb, 179 | lower_bound, 180 | upper_bound, 181 | maxIterations, 182 | steps 183 | ); 184 | } 185 | 186 | #endif 187 | -------------------------------------------------------------------------------- /source/mir/algebraic_alias/transform.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Transformation utilities for JSON-like values) 3 | 4 | See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf); 5 | 6 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 | Authors: Ilia Ki 8 | Macros: 9 | +/ 10 | module mir.algebraic_alias.transform; 11 | 12 | import mir.algebraic: Algebraic, tryVisit, visit, optionalVisit; 13 | import mir.functional: naryFun; 14 | private alias AliasSeq(T...) = T; 15 | 16 | /++ 17 | Transforms algebraics leafs recursively in place, 18 | ensuring that all leaf types are handled by the visiting functions. 19 | 20 | Recursion is done for `This[]`, `StringMap!This`, `This[string]`, and `Annotated!This` types. 21 | +/ 22 | alias transformLeafs(visitors...) = transformLeafsImpl!(visit, naryFun!visitors); 23 | 24 | /// 25 | version(mir_test) 26 | unittest 27 | { 28 | import mir.format: text; 29 | import mir.algebraic_alias.json; 30 | JsonAlgebraic value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, null.JsonAlgebraic].JsonAlgebraic]; 31 | 32 | // converts all leavs to a text form 33 | value.transformLeafs!text; 34 | assert(value == ["key" : ["str".JsonAlgebraic, "2.32".JsonAlgebraic, "null".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 35 | 36 | value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, true.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic; 37 | 38 | /// converts only bool values 39 | value.transformLeafs!( 40 | (bool b) => b.text, 41 | v => v, // other values are copied as is 42 | ); 43 | 44 | assert(value == ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, "true".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 45 | } 46 | 47 | /++ 48 | Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions. 49 | 50 | Throws: Exception if `naryFun!visitors` can't be called with provided arguments 51 | +/ 52 | alias tryTransformLeafs(visitors...) = transformLeafsImpl!(tryVisit, naryFun!visitors); 53 | 54 | /// 55 | version(mir_test) 56 | unittest 57 | { 58 | import mir.format: text; 59 | import mir.algebraic_alias.json; 60 | JsonAlgebraic value = ["key" : [true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic]; 61 | 62 | // converts long and double numbers to a text form, bool values is converted to `long` 63 | value.tryTransformLeafs!((double v) => v.text, (long v) => v.text); 64 | assert(value == ["key" : ["1".JsonAlgebraic, "100".JsonAlgebraic, "2.32".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 65 | } 66 | 67 | /++ 68 | Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions. 69 | 70 | The function ignores leafs that can't be handled by the visiting functions. 71 | +/ 72 | alias optionalTransformLeafs(visitors...) = transformLeafsImpl!(optionalVisit, naryFun!visitors); 73 | 74 | /// 75 | version(mir_test) 76 | unittest 77 | { 78 | import mir.format: text; 79 | import mir.algebraic_alias.json; 80 | JsonAlgebraic value = ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic]; 81 | 82 | // converts long numbers to a text form, ignores other types 83 | value.optionalTransformLeafs!( 84 | (long v) => v.text, 85 | (bool b) => b, // needs special overload for bool to get rid of implicit converion to long/double 86 | ); 87 | assert(value == ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, "100".JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic); 88 | } 89 | 90 | /// 91 | template transformLeafsImpl(alias handler, alias visitor) 92 | { 93 | /// 94 | ref Algebraic!Types transformLeafsImpl(Types...)(ref return Algebraic!Types value) 95 | { 96 | import core.lifetime: move; 97 | import mir.algebraic: visit; 98 | import mir.annotated: Annotated; 99 | import mir.string_map: StringMap; 100 | alias T = Algebraic!Types; 101 | static if (is(T.AllowedTypes[0] == typeof(null))) 102 | { 103 | enum nullCompiles = __traits(compiles, value = visitor(null)); 104 | static if (nullCompiles || __traits(isSame, handler, visit)) 105 | { 106 | alias nullHandler = (typeof(null)) { 107 | static if (nullCompiles) 108 | value = visitor(null); 109 | else 110 | assert(0, "Null " ~ T.stringof); 111 | }; 112 | } 113 | else 114 | { 115 | alias nullHandler = AliasSeq!(); 116 | } 117 | } 118 | else 119 | { 120 | alias nullHandler = AliasSeq!(); 121 | } 122 | handler!( 123 | (T[] v) { 124 | foreach (ref e; v) 125 | transformLeafsImpl(e); 126 | }, 127 | (StringMap!T v) { 128 | foreach (ref e; v.values) 129 | transformLeafsImpl(e); 130 | }, 131 | (T[string] v) { 132 | foreach (key, ref e; v) 133 | transformLeafsImpl(e); 134 | }, 135 | (Annotated!T v) { 136 | transformLeafsImpl(v.value); 137 | }, 138 | nullHandler, 139 | (ref v) { // auto for typeof(null) support 140 | static if (__traits(compiles, value = visitor(move(v)))) 141 | value = visitor(move(v)); 142 | else 143 | value = visitor(v); 144 | } 145 | )(value); 146 | return value; 147 | } 148 | 149 | /// ditto 150 | Algebraic!Types transformLeafsImpl(Types...)(Algebraic!Types value) 151 | { 152 | import core.lifetime: move; 153 | transformLeafsImpl(value); 154 | return move(value); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /include/mir/slim_rcptr.h: -------------------------------------------------------------------------------- 1 | #ifndef MIR_SLIM_RCPTR 2 | 3 | #define MIR_SLIM_RCPTR 4 | 5 | #include "rcptr.h" 6 | 7 | // Does not support allocators for now 8 | template 9 | struct mir_slim_rcptr 10 | { 11 | private: 12 | 13 | T* _payload = nullptr; 14 | using U = typename std::remove_const::type; 15 | 16 | public: 17 | 18 | using element_type = T; 19 | 20 | mir_slim_rcptr() noexcept {} 21 | mir_slim_rcptr(std::nullptr_t) noexcept {} 22 | mir_slim_rcptr(const mir_rc_context* context) noexcept : _payload(nullptr) 23 | { 24 | if (context) 25 | { 26 | mir_rc_increase_counter((mir_rc_context*)context); 27 | _payload = (T*)(context + 1); 28 | } 29 | } 30 | ~mir_slim_rcptr() noexcept { if (_payload) mir_rc_decrease_counter(getContext()); } 31 | mir_slim_rcptr(const mir_slim_rcptr& rhs) noexcept : _payload(rhs._payload) { if (_payload) mir_rc_increase_counter(getContext()); } 32 | mir_slim_rcptr(mir_slim_rcptr&& rhs) noexcept : _payload(rhs._payload) { rhs.__reset(); } 33 | mir_slim_rcptr& operator=(const mir_slim_rcptr& rhs) noexcept 34 | { 35 | if (_payload != rhs._payload) 36 | { 37 | if (_payload) mir_rc_decrease_counter(getContext()); 38 | _payload = (T*) rhs._payload; 39 | if (_payload) mir_rc_increase_counter(getContext());; 40 | } 41 | return *this; 42 | } 43 | 44 | template 45 | static mir_slim_rcptr make_shared(Args&& ...args) 46 | { 47 | using U = typename std::remove_const::type; 48 | static_assert( std::is_constructible::value, "Can't construct object in mir_slim_rcptr constructor" ); 49 | mir_slim_rcptr ret; 50 | auto context = mir_rc_create(mir::typeInfoT_(), 1); 51 | if (context == nullptr) 52 | throw std::bad_alloc(); 53 | ret._payload = (T*)(context + 1); 54 | ::new((U*)ret._payload) U(std::forward(args)...); 55 | return ret; 56 | } 57 | 58 | void __reset() { _payload = nullptr; } 59 | 60 | template::value>::type> 61 | mir_slim_rcptr& operator=(const mir_slim_rcptr& rhs) noexcept 62 | { 63 | auto rhsv = rhs.get(); 64 | if (_payload != rhsv) 65 | { 66 | if (_payload) mir_rc_decrease_counter(getContext()); 67 | _payload = rhsv; 68 | if (_payload) mir_rc_increase_counter(getContext()); 69 | } 70 | return *this; 71 | } 72 | 73 | template::value>::type> 74 | mir_slim_rcptr(const mir_slim_rcptr& rhs) noexcept : _payload(rhs.get()) 75 | { 76 | if (_payload) mir_rc_increase_counter(getContext()); 77 | } 78 | 79 | template::value>::type> 80 | mir_slim_rcptr(mir_slim_rcptr&& rhs) noexcept : _payload(rhs.get()) 81 | { 82 | rhs.__reset(); 83 | } 84 | 85 | mir_slim_rcptr light_const() const noexcept { return *(mir_slim_rcptr*)this; } 86 | 87 | mir_rc_context* getContext() noexcept { return _payload ? (mir_rc_context*)_payload - 1 : nullptr; } 88 | mir_slim_rcptr& operator=(std::nullptr_t) noexcept { if (_payload) mir_rc_decrease_counter(getContext()); __reset(); return *this; } 89 | T& operator*() noexcept { assert(_payload != nullptr); return *_payload; } 90 | T* operator->() noexcept { assert(_payload != nullptr); return _payload; } 91 | T* get() noexcept { return _payload; } 92 | const T* get() const noexcept { return _payload; } 93 | const mir_rc_context* getContext() const noexcept { return _payload ? (const mir_rc_context*)_payload - 1 : nullptr; } 94 | const T& operator*() const noexcept { assert(_payload != nullptr); return *_payload; } 95 | const T* operator->() const noexcept { assert(_payload != nullptr); return _payload; } 96 | template bool operator==(const mir_slim_rcptr& rhs) const noexcept { return _payload == rhs._payload; } 97 | template bool operator!=(const mir_slim_rcptr& rhs) const noexcept { return _payload != rhs._payload; } 98 | template bool operator<=(const mir_slim_rcptr& rhs) const noexcept { return _payload <= rhs._payload; } 99 | template bool operator>=(const mir_slim_rcptr& rhs) const noexcept { return _payload >= rhs._payload; } 100 | template bool operator<(const mir_slim_rcptr& rhs) const noexcept { return _payload < rhs._payload; } 101 | template bool operator>(const mir_slim_rcptr& rhs) const noexcept { return _payload > rhs._payload; } 102 | explicit operator bool() const noexcept { return _payload != nullptr; } 103 | }; 104 | 105 | namespace mir 106 | { 107 | template 108 | mir_slim_rcptr make_slim_shared(Args&& ...args) 109 | { 110 | return mir_slim_rcptr::make_shared(std::forward(args)...); 111 | } 112 | 113 | template< class T, class U > 114 | mir_slim_rcptr static_pointer_cast( const mir_slim_rcptr& r ) noexcept 115 | { 116 | auto p = static_cast::element_type*>(r.get()); 117 | return mir_slim_rcptr(r.getContext()); 118 | } 119 | 120 | template< class T, class U > 121 | mir_slim_rcptr const_pointer_cast( const mir_slim_rcptr& r ) noexcept 122 | { 123 | auto p = const_cast::element_type*>(r.get()); 124 | return mir_slim_rcptr(r.getContext()); 125 | } 126 | } 127 | 128 | namespace std 129 | { 130 | template 131 | struct hash > 132 | { 133 | typedef mir_slim_rcptr argument_type; 134 | typedef size_t result_type; 135 | 136 | result_type operator()(const argument_type& ptr) const noexcept 137 | { 138 | return (result_type)(ptr.get()); 139 | } 140 | }; 141 | } 142 | 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /source/mir/interpolate/generic.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H2 Generic Piecewise Interpolant) 3 | 4 | See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate) 5 | 6 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 8 | Authors: Ilia Ki 9 | 10 | Macros: 11 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP) 12 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 13 | +/ 14 | module mir.interpolate.generic; 15 | 16 | @fmamath: 17 | 18 | /// 19 | version(mir_test) 20 | @safe pure @nogc unittest 21 | { 22 | import mir.ndslice; 23 | import mir.math.common: approxEqual; 24 | 25 | struct PieceInterpolant 26 | { 27 | int value; 28 | 29 | this()(int value) 30 | { 31 | this.value = value; 32 | } 33 | 34 | int opCall(uint derivative : 0, X)(int x0, int x1, X x) const 35 | { 36 | return value; 37 | } 38 | 39 | enum uint derivativeOrder = 0; 40 | } 41 | 42 | alias S = PieceInterpolant; 43 | static immutable x = [0, 1, 2, 3]; // can be also an array of floating point numbers 44 | static immutable y = [S(10), S(20), S(30)]; 45 | 46 | auto interpolant = generic(x.rcslice, y.rcslice!(const S)); 47 | 48 | assert(interpolant(-1) == 10); 49 | assert(interpolant(0) == 10); 50 | assert(interpolant(0.5) == 10); 51 | 52 | assert(interpolant(1) == 20); 53 | assert(interpolant(1.5) == 20); 54 | 55 | assert(interpolant(2) == 30); 56 | assert(interpolant(3) == 30); 57 | assert(interpolant(3.4) == 30); 58 | assert(interpolant(3) == 30); 59 | assert(interpolant(4) == 30); 60 | } 61 | 62 | 63 | import core.lifetime: move; 64 | import mir.internal.utility; 65 | import mir.functional; 66 | import mir.interpolate; 67 | import mir.math.common: fmamath; 68 | import mir.ndslice.slice; 69 | import mir.primitives; 70 | import mir.rc.array; 71 | import mir.utility: min, max; 72 | import std.meta: AliasSeq, staticMap; 73 | import std.traits; 74 | 75 | /// 76 | public import mir.interpolate: atInterval; 77 | 78 | /++ 79 | Constructs multivariate generic interpolant with nodes on rectilinear grid. 80 | 81 | Params: 82 | grid = `x` values for interpolant 83 | values = `f(x)` values for interpolant 84 | 85 | Constraints: 86 | `grid`, `values` must have the same length >= 1 87 | 88 | Returns: $(LREF Generic) 89 | +/ 90 | Generic!(X, F) generic(X, F) 91 | (Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) values) 92 | { 93 | return typeof(return)(forward!grid, values.move); 94 | } 95 | 96 | /++ 97 | Multivariate generic interpolant with nodes on rectilinear grid. 98 | +/ 99 | struct Generic(X, F) 100 | { 101 | @fmamath: 102 | 103 | /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.) 104 | Slice!(RCI!(const F)) _data; 105 | /// Grid iterators. $(RED For internal use.) 106 | RCI!(immutable X) _grid; 107 | 108 | bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc 109 | { 110 | if (rhs._data != this._data) 111 | return false; 112 | static foreach (d; 0 .. 1) 113 | if (gridScopeView!d != rhs.gridScopeView!d) 114 | return false; 115 | return true; 116 | } 117 | 118 | extern(D): 119 | 120 | /++ 121 | +/ 122 | this(Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) data) @safe @nogc 123 | { 124 | import core.lifetime: move; 125 | enum msg_min = "generic interpolant: minimal allowed length for the grid equals 2."; 126 | enum msg_eq = "generic interpolant: X length and Y values length + 1 should be equal."; 127 | version(D_Exceptions) 128 | { 129 | static immutable exc_min = new Exception(msg_min); 130 | static immutable exc_eq = new Exception(msg_eq); 131 | } 132 | if (grid.length < 2) 133 | { 134 | version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; } 135 | else assert(0, msg_min); 136 | } 137 | if (grid.length != data._lengths[0] + 1) 138 | { 139 | version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; } 140 | else assert(0, msg_eq); 141 | } 142 | _grid = move(grid._iterator); 143 | _data = move(data); 144 | } 145 | 146 | @trusted: 147 | 148 | /// 149 | Generic lightConst()() const @property { return *cast(Generic*)&this; } 150 | 151 | /// 152 | Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property 153 | if (dimension == 0) 154 | { 155 | return _grid.lightConst.sliced(_data._lengths[0]); 156 | } 157 | 158 | /// 159 | immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted 160 | if (dimension == 0) 161 | { 162 | return _grid._iterator[0 .. _data._lengths[0]]; 163 | } 164 | 165 | /++ 166 | Returns: intervals count. 167 | +/ 168 | size_t intervalCount(size_t dimension = 0)() scope const @property 169 | if (dimension == 0) 170 | { 171 | assert(_data._lengths[0] > 1); 172 | return _data._lengths[0] - 0; 173 | } 174 | 175 | /// 176 | size_t[1] gridShape()() scope const @property 177 | { 178 | return _data.shape; 179 | } 180 | 181 | /// 182 | enum uint derivativeOrder = F.derivativeOrder; 183 | 184 | /// 185 | template opCall(uint derivative = 0) 186 | if (derivative == 0) 187 | { 188 | /++ 189 | `(x)` operator. 190 | Complexity: 191 | `O(log(grid.length))` 192 | +/ 193 | auto opCall(X)(in X x) const 194 | { 195 | return opCall!(X)(Tuple!(size_t, X)(this.findInterval(x), x)); 196 | } 197 | 198 | /// 199 | auto opCall(X)(Tuple!(size_t, X) tuple) const 200 | { 201 | X x = tuple[1]; 202 | size_t idx = tuple[0]; 203 | return _data[idx].opCall!derivative(_grid[idx], _grid[idx + 1], x); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /source/mir/math/func/hermite.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Hermite Polynomial Coefficients 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: John Hall 6 | 7 | +/ 8 | 9 | module mir.math.func.hermite; 10 | 11 | /++ 12 | Normalized (probabilist's) Hermite polynomial coefficients 13 | 14 | Params: 15 | N = Degree of polynomial 16 | 17 | See_also: 18 | $(LINK2 https://en.wikipedia.org/wiki/Hermite_polynomials, Hermite polynomials) 19 | +/ 20 | @safe pure nothrow 21 | long[] hermiteCoefficientsNorm(size_t N) 22 | { 23 | if (N == 0) { 24 | return [1]; 25 | } else { 26 | typeof(return) output = [0, 1]; 27 | if (N > 1) { 28 | output.length = N + 1; 29 | int K; 30 | typeof(return) h_N_minus_1 = hermiteCoefficientsNorm(0); // to be copied to h_N_minus_2 in loop 31 | h_N_minus_1.length = N; 32 | typeof(return) h_N_minus_2; //value replaced later 33 | h_N_minus_2.length = N - 1; 34 | 35 | foreach (size_t j; 2..(N + 1)) { 36 | h_N_minus_2[0..(j - 1)] = h_N_minus_1[0..(j - 1)]; 37 | h_N_minus_1[0..j] = output[0..j]; 38 | K = -(cast(int) j - 1); 39 | output[0] = K * h_N_minus_2[0]; 40 | foreach (size_t i; 1..(j - 1)) { 41 | output[i] = h_N_minus_1[i - 1] + K * h_N_minus_2[i]; 42 | } 43 | foreach (size_t i; (j - 1)..(j + 1)) { 44 | output[i] = h_N_minus_1[i - 1]; 45 | } 46 | } 47 | } 48 | return output; 49 | } 50 | } 51 | 52 | /// 53 | version(mir_test) 54 | @safe pure nothrow 55 | unittest 56 | { 57 | import mir.polynomial: polynomial; 58 | import mir.rc.array: rcarray; 59 | import mir.test: should; 60 | 61 | auto h0 = hermiteCoefficientsNorm(0).rcarray!(const double).polynomial; 62 | auto h1 = hermiteCoefficientsNorm(1).rcarray!(const double).polynomial; 63 | auto h2 = hermiteCoefficientsNorm(2).rcarray!(const double).polynomial; 64 | auto h3 = hermiteCoefficientsNorm(3).rcarray!(const double).polynomial; 65 | auto h4 = hermiteCoefficientsNorm(4).rcarray!(const double).polynomial; 66 | auto h5 = hermiteCoefficientsNorm(5).rcarray!(const double).polynomial; 67 | auto h6 = hermiteCoefficientsNorm(6).rcarray!(const double).polynomial; 68 | auto h7 = hermiteCoefficientsNorm(7).rcarray!(const double).polynomial; 69 | auto h8 = hermiteCoefficientsNorm(8).rcarray!(const double).polynomial; 70 | auto h9 = hermiteCoefficientsNorm(9).rcarray!(const double).polynomial; 71 | auto h10 = hermiteCoefficientsNorm(10).rcarray!(const double).polynomial; 72 | 73 | h0(3).should == 1; 74 | h1(3).should == 3; 75 | h2(3).should == 8; 76 | h3(3).should == 18; 77 | h4(3).should == 30; 78 | h5(3).should == 18; 79 | h6(3).should == -96; 80 | h7(3).should == -396; 81 | h8(3).should == -516; 82 | h9(3).should == 1_620; 83 | h10(3).should == 9_504; 84 | } 85 | 86 | /// Also works with @nogc CTFE 87 | version(mir_test) 88 | @safe pure nothrow @nogc 89 | unittest 90 | { 91 | import mir.ndslice.slice: sliced; 92 | import mir.test: should; 93 | 94 | static immutable result = [-1, 0, 1]; 95 | 96 | static immutable hc2 = hermiteCoefficientsNorm(2); 97 | hc2.sliced.should == result; 98 | } 99 | 100 | /++ 101 | Physicist's Hermite polynomial coefficients 102 | 103 | Params: 104 | N = Degree of polynomial 105 | 106 | See_also: 107 | $(LINK2 https://en.wikipedia.org/wiki/Hermite_polynomials, Hermite polynomials) 108 | +/ 109 | @safe pure nothrow 110 | long[] hermiteCoefficients(size_t N) 111 | { 112 | if (N == 0) { 113 | return [1]; 114 | } else { 115 | typeof(return) output = [0, 2]; 116 | if (N > 1) { 117 | output.length = N + 1; 118 | typeof(return) h_N_minus_1 = hermiteCoefficients(0); 119 | h_N_minus_1.length = N; 120 | 121 | foreach (size_t j; 2..(N + 1)) { 122 | h_N_minus_1[0..j] = output[0..j]; 123 | output[0] = -h_N_minus_1[1]; 124 | foreach (size_t i; 1..(j - 1)) { 125 | output[i] = 2 * h_N_minus_1[i - 1] - (cast(int) i + 1) * h_N_minus_1[i + 1]; 126 | } 127 | foreach (size_t i; (j - 1)..(j + 1)) { 128 | output[i] = 2 * h_N_minus_1[i - 1]; 129 | } 130 | } 131 | } 132 | return output; 133 | } 134 | } 135 | 136 | /// 137 | version(mir_test) 138 | @safe pure nothrow 139 | unittest 140 | { 141 | import mir.polynomial: polynomial; 142 | import mir.rc.array: rcarray; 143 | import mir.test: should; 144 | 145 | auto h0 = hermiteCoefficients(0).rcarray!(const double).polynomial; 146 | auto h1 = hermiteCoefficients(1).rcarray!(const double).polynomial; 147 | auto h2 = hermiteCoefficients(2).rcarray!(const double).polynomial; 148 | auto h3 = hermiteCoefficients(3).rcarray!(const double).polynomial; 149 | auto h4 = hermiteCoefficients(4).rcarray!(const double).polynomial; 150 | auto h5 = hermiteCoefficients(5).rcarray!(const double).polynomial; 151 | auto h6 = hermiteCoefficients(6).rcarray!(const double).polynomial; 152 | auto h7 = hermiteCoefficients(7).rcarray!(const double).polynomial; 153 | auto h8 = hermiteCoefficients(8).rcarray!(const double).polynomial; 154 | auto h9 = hermiteCoefficients(9).rcarray!(const double).polynomial; 155 | auto h10 = hermiteCoefficients(10).rcarray!(const double).polynomial; 156 | 157 | h0(3).should == 1; 158 | h1(3).should == 6; 159 | h2(3).should == 34; 160 | h3(3).should == 180; 161 | h4(3).should == 876; 162 | h5(3).should == 3_816; 163 | h6(3).should == 14_136; 164 | h7(3).should == 39_024; 165 | h8(3).should == 36_240; 166 | h9(3).should == -406_944; 167 | h10(3).should == -3_093_984; 168 | } 169 | 170 | /// Also works with @nogc CTFE 171 | version(mir_test) 172 | @safe pure nothrow @nogc 173 | unittest 174 | { 175 | import mir.ndslice.slice: sliced; 176 | import mir.test: should; 177 | 178 | static immutable result = [-2, 0, 4]; 179 | 180 | static immutable hc2 = hermiteCoefficients(2); 181 | hc2.sliced.should == result; 182 | } 183 | -------------------------------------------------------------------------------- /source/mir/interpolate/mod.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H2 Interpolation Modifier) 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Copyright: 2022 Ilia, Symmetry Investments 6 | Authors: Ilia Ki 7 | 8 | Macros: 9 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP) 10 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 11 | +/ 12 | module mir.interpolate.mod; 13 | 14 | import mir.math.common; 15 | 16 | /++ 17 | Applies function to the interpolated value. 18 | 19 | Params: 20 | fun = two arguments `(x, derivativeOrder)` function 21 | +/ 22 | template interpolationMap(alias fun) 23 | { 24 | /// 25 | auto interpolationMap(T)(T interpolator) 26 | { 27 | import core.lifetime: move; 28 | alias S = InterpolationMap!fun; 29 | return S!T(interpolator.move); 30 | } 31 | } 32 | 33 | /// ditto 34 | template InterpolationMap(alias fun) 35 | { 36 | /// 37 | struct InterpolationMap(T) 38 | { 39 | static if (__traits(hasMember, T, "derivativeOrder")) 40 | enum derivativeOrder = T.derivativeOrder; 41 | 42 | static if (__traits(hasMember, T, "dimensionCount")) 43 | enum uint dimensionCount = T.dimensionCount; 44 | 45 | /// 46 | T interpolator; 47 | 48 | /// 49 | this(T interpolator) 50 | { 51 | import core.lifetime: move; 52 | this.interpolator = interpolator.move; 53 | } 54 | 55 | /// 56 | template opCall(uint derivative = 0) 57 | // if (derivative <= derivativeOrder) 58 | { 59 | /++ 60 | `(x)` operator. 61 | Complexity: 62 | `O(log(grid.length))` 63 | +/ 64 | auto opCall(X...)(const X xs) scope const @trusted 65 | // if (X.length == dimensionCount) 66 | { 67 | auto g = interpolator.opCall!derivative(xs); 68 | 69 | static if (derivative == 0) 70 | { 71 | typeof(g)[1] ret; 72 | fun(g, ret); 73 | return ret[0]; 74 | } 75 | else 76 | { 77 | static if (X.length == 1) 78 | auto g0 = g[0]; 79 | else 80 | static if (X.length == 2) 81 | auto g0 = g[0][0]; 82 | else 83 | static if (X.length == 3) 84 | auto g0 = g[0][0][0]; 85 | else 86 | static assert(0, "Not implemented"); 87 | 88 | typeof(g0)[derivative + 1] f; 89 | 90 | fun(g0, f); 91 | 92 | static if (X.length == 1) 93 | { 94 | typeof(g) r; 95 | r[0] = f[0]; 96 | r[1] = f[1] * g[1]; 97 | 98 | static if (derivative >= 2) 99 | { 100 | r[2] = f[2] * (g[1] * g[1]) + f[1] * g[2]; 101 | } 102 | static if (derivative >= 3) 103 | { 104 | r[3] = f[3] * (g[1] * g[1] * g[1]) + f[1] * g[3] + 3 * (f[2] * g[1] * g[2]); 105 | } 106 | static if (derivative >= 4) 107 | { 108 | static assert(0, "Not implemented"); 109 | } 110 | 111 | return r; 112 | } else static assert(0, "Not implemented"); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | 119 | /// 120 | version (mir_test) 121 | unittest 122 | { 123 | import mir.interpolate.spline; 124 | import mir.math.common: log; 125 | import mir.ndslice.allocation: rcslice; 126 | import mir.test; 127 | 128 | alias g = (double x, uint d = 0) => 129 | d == 0 ? 3 * x ^^ 3 + 5 * x ^^ 2 + 0.23 * x + 2 : 130 | d == 1 ? 9 * x ^^ 2 + 10 * x + 0.23 : 131 | double.nan; 132 | 133 | alias f = (double x, ref scope y) 134 | { 135 | y[0] = log(x); 136 | static if (y.length >= 2) 137 | y[1] = 1 / x; 138 | static if (y.length >= 3) 139 | y[2] = -y[1] * y[1]; 140 | static if (y.length >= 4) 141 | y[3] = -2 * y[1] * y[2]; 142 | static if (y.length >= 5) 143 | static assert(0, "Not implemented"); 144 | }; 145 | 146 | auto s = spline!double( 147 | [0.1, 0.4, 0.5, 1.0].rcslice!(immutable double), 148 | [g(0.1), g(0.4), g(0.5), g(1.0)].rcslice!(const double) 149 | ); 150 | 151 | auto m = s.interpolationMap!f; 152 | 153 | m(0.7).shouldApprox == log(g(0.7)); 154 | auto d = m.opCall!3(0.7); 155 | d[0].shouldApprox == log(g(0.7)); 156 | d[1].shouldApprox == 1 / g(0.7) * g(0.7, 1); 157 | d[2].shouldApprox == -0.252301; 158 | d[3].shouldApprox == -4.03705; 159 | } 160 | 161 | private alias implSqrt = (x, ref scope y) 162 | { 163 | import mir.math.common: sqrt; 164 | y[0] = sqrt(x); 165 | static if (y.length >= 2) 166 | y[1] = 0.5f / y[0]; 167 | static if (y.length >= 3) 168 | y[2] = -0.5f * y[1] / x; 169 | static if (y.length >= 4) 170 | y[3] = -1.5f * y[2] / x; 171 | static if (y.length >= 5) 172 | static assert(0, "Not implemented"); 173 | }; 174 | 175 | /++ 176 | Applies square root function to the interpolated value. 177 | +/ 178 | alias interpolationSqrt = interpolationMap!implSqrt; 179 | /// ditto 180 | alias InterpolationSqrt = InterpolationMap!implSqrt; 181 | 182 | /// 183 | version (mir_test) 184 | unittest 185 | { 186 | import mir.interpolate.spline; 187 | import mir.math.common: sqrt; 188 | import mir.ndslice.allocation: rcslice; 189 | import mir.test; 190 | 191 | alias g = (double x, uint d = 0) => 192 | d == 0 ? 3 * x ^^ 3 + 5 * x ^^ 2 + 0.23 * x + 2 : 193 | d == 1 ? 9 * x ^^ 2 + 10 * x + 0.23 : 194 | double.nan; 195 | 196 | auto s = spline!double( 197 | [0.1, 0.4, 0.5, 1.0].rcslice!(immutable double), 198 | [g(0.1), g(0.4), g(0.5), g(1.0)].rcslice!(const double) 199 | ); 200 | 201 | auto m = s.interpolationSqrt; 202 | 203 | m(0.7).shouldApprox == sqrt(g(0.7)); 204 | auto d = m.opCall!3(0.7); 205 | d[0].shouldApprox == sqrt(g(0.7)); 206 | d[1].shouldApprox == 0.5 / sqrt(g(0.7)) * g(0.7, 1); 207 | d[2].shouldApprox == 2.2292836438189414; 208 | d[3].shouldApprox == -3.11161; 209 | } 210 | -------------------------------------------------------------------------------- /source/mir/polynomial.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Polynomial ref-counted structure. 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: Ilia Ki 6 | +/ 7 | module mir.polynomial; 8 | 9 | import mir.math.common: fmamath; 10 | import mir.rc.array; 11 | 12 | @fmamath: 13 | 14 | /++ 15 | Polynomial callable ref-counted structure. 16 | +/ 17 | struct Polynomial(F) 18 | { 19 | /// 20 | RCArray!(const F) coefficients; 21 | 22 | /++ 23 | Params: 24 | coefficients = coefficients `c[i]` for polynomial function `f(x)=c[0]+c[1]*x^^1+...+c[n]*x^^n` 25 | +/ 26 | this(RCArray!(const F) coefficients) 27 | { 28 | import core.lifetime: move; 29 | this.coefficients = coefficients.move; 30 | } 31 | 32 | /++ 33 | Params: 34 | derivative = derivative order 35 | +/ 36 | template opCall(uint derivative = 0) 37 | { 38 | /++ 39 | Params: 40 | x = `x` point 41 | +/ 42 | @fmamath typeof(F.init * X.init * 1f + F.init) opCall(X)(in X x) const 43 | { 44 | return x.poly!derivative(this.coefficients[]); 45 | } 46 | } 47 | } 48 | 49 | /// ditto 50 | Polynomial!F polynomial(F)(RCArray!(const F) coefficients) 51 | { 52 | import core.lifetime: move; 53 | return typeof(return)(coefficients.move); 54 | } 55 | 56 | /// 57 | version (mir_test) @safe pure nothrow @nogc unittest 58 | { 59 | import mir.test; 60 | import mir.rc.array; 61 | auto a = rcarray!(const double)(3.0, 4.5, 1.9, 2); 62 | auto p = a.polynomial; 63 | 64 | alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3; 65 | alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2; 66 | alias d2f = (x) => 2 * 1.9 + 6 * 2 * x^^1; 67 | 68 | p(3.3).shouldApprox == f(3.3); 69 | p(7.2).shouldApprox == f(7.2); 70 | 71 | p.opCall!1(3.3).shouldApprox == df(3.3); 72 | p.opCall!1(7.2).shouldApprox == df(7.2); 73 | 74 | p.opCall!2(3.3).shouldApprox == d2f(3.3); 75 | p.opCall!2(7.2).shouldApprox == d2f(7.2); 76 | } 77 | 78 | /++ 79 | Evaluate polynomial. 80 | 81 | Coefficients assumed to be in the order a0 + a1 * x ^^ 1 + ... + aN * x ^^ N 82 | 83 | Params: 84 | F = controls type of output 85 | derivative = order of derivatives (default = 0) 86 | 87 | Returns: 88 | Value of the polynomial, evaluated at `x` 89 | 90 | See_also: 91 | $(WEB en.wikipedia.org/wiki/Polynomial, Polynomial). 92 | +/ 93 | template poly(uint derivative = 0) 94 | { 95 | import std.traits: ForeachType; 96 | /++ 97 | Params: 98 | x = value to evaluate 99 | coefficients = coefficients of polynomial 100 | +/ 101 | @fmamath typeof(F.init * X.init * 1f + F.init) poly(X, F)(in X x, scope const F[] coefficients...) 102 | { 103 | import mir.internal.utility: Iota; 104 | auto ret = cast(typeof(return))0; 105 | if (coefficients.length > 0) 106 | { 107 | ptrdiff_t i = coefficients.length - 1; 108 | assert(i >= 0); 109 | auto c = cast()coefficients[i]; 110 | static foreach (d; Iota!derivative) 111 | c *= i - d; 112 | ret = cast(typeof(return)) c; 113 | while (--i >= cast(ptrdiff_t)derivative) 114 | { 115 | assert(i < coefficients.length); 116 | c = cast()coefficients[i]; 117 | static foreach (d; Iota!derivative) 118 | c *= i - d; 119 | ret *= x; 120 | ret += c; 121 | } 122 | } 123 | return ret; 124 | } 125 | } 126 | 127 | /// 128 | version (mir_test) @safe pure nothrow unittest 129 | { 130 | import mir.math.common: approxEqual; 131 | 132 | double[] x = [3.0, 4.5, 1.9, 2]; 133 | 134 | alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3; 135 | alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2; 136 | alias d2f = (x) => 2 * 1.9 + 6 * 2 * x^^1; 137 | 138 | assert(poly(3.3, x).approxEqual(f(3.3))); 139 | assert(poly(7.2, x).approxEqual(f(7.2))); 140 | 141 | assert(poly!1(3.3, x).approxEqual(df(3.3))); 142 | assert(poly!1(7.2, x).approxEqual(df(7.2))); 143 | 144 | assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 145 | assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 146 | } 147 | 148 | // static array test 149 | version (mir_test) @safe pure @nogc nothrow unittest 150 | { 151 | import mir.math.common: approxEqual; 152 | 153 | double[4] x = [3.0, 4.5, 1.9, 2]; 154 | 155 | alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3; 156 | alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2; 157 | alias d2f = (x) => 2 * 1.9 + 6 * 2 * x^^1; 158 | 159 | assert(poly(3.3, x).approxEqual(f(3.3))); 160 | assert(poly(7.2, x).approxEqual(f(7.2))); 161 | 162 | assert(poly!1(3.3, x).approxEqual(df(3.3))); 163 | assert(poly!1(7.2, x).approxEqual(df(7.2))); 164 | 165 | assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 166 | assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 167 | } 168 | 169 | // Check coefficient.length = 3 170 | version (mir_test) @safe pure nothrow unittest 171 | { 172 | import mir.math.common: approxEqual; 173 | 174 | double[] x = [3.0, 4.5, 1.9]; 175 | 176 | alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2; 177 | alias df = (x) => 4.5 + 2 * 1.9 * x^^1; 178 | alias d2f = (x) => 2 * 1.9; 179 | 180 | assert(poly(3.3, x).approxEqual(f(3.3))); 181 | assert(poly(7.2, x).approxEqual(f(7.2))); 182 | 183 | assert(poly!1(3.3, x).approxEqual(df(3.3))); 184 | assert(poly!1(7.2, x).approxEqual(df(7.2))); 185 | 186 | assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 187 | assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 188 | } 189 | 190 | // Check coefficient.length = 2 191 | version (mir_test) @safe pure nothrow unittest 192 | { 193 | import mir.math.common: approxEqual; 194 | 195 | double[] x = [3.0, 4.5]; 196 | 197 | alias f = (x) => 3.0 + 4.5 * x^^1; 198 | alias df = (x) => 4.5; 199 | alias d2f = (x) => 0.0; 200 | 201 | assert(poly(3.3, x).approxEqual(f(3.3))); 202 | assert(poly(7.2, x).approxEqual(f(7.2))); 203 | 204 | assert(poly!1(3.3, x).approxEqual(df(3.3))); 205 | assert(poly!1(7.2, x).approxEqual(df(7.2))); 206 | 207 | assert(poly!2(3.3, x).approxEqual(d2f(3.3))); 208 | assert(poly!2(7.2, x).approxEqual(d2f(7.2))); 209 | } 210 | 211 | // Check coefficient.length = 1 212 | version (mir_test) @safe pure nothrow unittest 213 | { 214 | import mir.math.common: approxEqual; 215 | 216 | double[] x = [3.0]; 217 | 218 | alias f = (x) => 3.0; 219 | alias df = (x) => 0.0; 220 | 221 | assert(poly(3.3, x).approxEqual(f(3.3))); 222 | assert(poly(7.2, x).approxEqual(f(7.2))); 223 | 224 | assert(poly!1(3.3, x).approxEqual(df(3.3))); 225 | assert(poly!1(7.2, x).approxEqual(df(7.2))); 226 | } 227 | -------------------------------------------------------------------------------- /source/mir/rc/context.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Thread-safe reference-counted context implementation). 3 | +/ 4 | module mir.rc.context; 5 | 6 | import mir.type_info; 7 | 8 | /++ 9 | +/ 10 | struct mir_rc_context 11 | { 12 | /// 13 | extern (C) void function(mir_rc_context*) @system nothrow @nogc pure deallocator; 14 | /// 15 | immutable(mir_type_info)* typeInfo; 16 | /// 17 | shared size_t counter; 18 | /// 19 | size_t length; 20 | } 21 | 22 | /++ 23 | Increase counter by 1. 24 | 25 | Params: 26 | context = shared_ptr context (not null) 27 | +/ 28 | export extern(C) 29 | void mir_rc_increase_counter(ref mir_rc_context context) @system nothrow @nogc pure 30 | { 31 | import core.atomic: atomicOp; 32 | with(context) 33 | { 34 | if (counter) 35 | { 36 | counter.atomicOp!"+="(1); 37 | } 38 | } 39 | } 40 | 41 | /++ 42 | Decrease counter by 1. 43 | Destroys data if counter decreased from 1 to 0. 44 | 45 | Params: 46 | context = shared_ptr context (not null) 47 | +/ 48 | export extern(C) 49 | void mir_rc_decrease_counter(ref mir_rc_context context) @system nothrow @nogc pure 50 | { 51 | pragma(inline, true); 52 | import core.atomic: atomicOp; 53 | with(context) 54 | { 55 | if (counter) 56 | { 57 | if (counter.atomicOp!"-="(1) == 0) 58 | { 59 | mir_rc_delete(context); 60 | } 61 | } 62 | // else 63 | // { 64 | // assert(0); 65 | // } 66 | } 67 | } 68 | 69 | /++ 70 | +/ 71 | export extern(C) 72 | void mir_rc_delete(ref mir_rc_context context) 73 | @system nothrow @nogc pure 74 | { 75 | assert(context.deallocator); 76 | with(context) 77 | { 78 | with(typeInfo) 79 | { 80 | if (destructor) 81 | { 82 | auto ptr = cast(void*)(&context + 1); 83 | auto i = length; 84 | assert(i); 85 | do 86 | { 87 | destructor(ptr); 88 | ptr += size; 89 | } 90 | while(--i); 91 | } 92 | } 93 | } 94 | if (context.counter) 95 | assert(0); 96 | version (mir_secure_memory) 97 | { 98 | (cast(ubyte*)(&context + 1))[0 .. context.length * context.typeInfo.size] = 0; 99 | } 100 | context.deallocator(&context); 101 | } 102 | 103 | /++ 104 | +/ 105 | export extern(C) 106 | mir_rc_context* mir_rc_create( 107 | ref immutable(mir_type_info) typeInfo, 108 | size_t length, 109 | scope const void* payload = null, 110 | bool initialize = true, 111 | bool deallocate = true, 112 | ) @system nothrow @nogc pure 113 | { 114 | import mir.internal.memory: malloc, free; 115 | import core.stdc.string: memset, memcpy; 116 | 117 | assert(length); 118 | auto size = length * typeInfo.size; 119 | auto fullSize = mir_rc_context.sizeof + size; 120 | if (auto p = malloc(fullSize)) 121 | { 122 | version (mir_secure_memory) 123 | { 124 | (cast(ubyte*)p)[0 .. fullSize] = 0; 125 | } 126 | auto context = cast(mir_rc_context*)p; 127 | context.deallocator = &free; 128 | context.typeInfo = &typeInfo; 129 | context.counter = deallocate; 130 | context.length = length; 131 | 132 | if (initialize) 133 | { 134 | auto ptr = cast(void*)(context + 1); 135 | if (payload) 136 | { 137 | switch(typeInfo.size) 138 | { 139 | case 1: 140 | memset(ptr, *cast(ubyte*)payload, size); 141 | break; 142 | case 2: 143 | (cast(ushort*)ptr)[0 .. length] = *cast(ushort*)payload; 144 | break; 145 | case 4: 146 | (cast(uint*)ptr)[0 .. length] = *cast(uint*)payload; 147 | break; 148 | case 8: 149 | (cast(ulong*)ptr)[0 .. length] = *cast(ulong*)payload; 150 | break; 151 | static if (is(ucent)) 152 | { 153 | case 16: 154 | (cast(ucent*)ptr)[0 .. length] = *cast(ucent*)payload; 155 | break; 156 | } 157 | default: 158 | foreach(i; 0 .. length) 159 | { 160 | memcpy(ptr, payload, typeInfo.size); 161 | ptr += typeInfo.size; 162 | } 163 | } 164 | } 165 | else 166 | { 167 | memset(ptr, 0, size); 168 | } 169 | } 170 | return context; 171 | } 172 | return null; 173 | } 174 | 175 | /// 176 | package mixin template CommonRCImpl() 177 | { 178 | /// 179 | ThisTemplate!(const T) lightConst()() return scope const @nogc nothrow @trusted @property 180 | { return *cast(typeof(return)*) &this; } 181 | 182 | /// ditto 183 | ThisTemplate!(immutable T) lightImmutable()() return scope immutable @nogc nothrow @trusted @property 184 | { return *cast(typeof(return)*) &this; } 185 | 186 | /// 187 | ThisTemplate!(const Unqual!T) moveToConst()() return scope @nogc nothrow @trusted @property 188 | { 189 | import core.lifetime: move; 190 | return move(*cast(typeof(return)*) &this); 191 | } 192 | 193 | /// 194 | pragma(inline, true) 195 | size_t _counter() @trusted scope pure nothrow @nogc const @property 196 | { 197 | return cast(bool)this ? context.counter : 0; 198 | } 199 | 200 | /// 201 | C opCast(C)() const 202 | if (is(Unqual!C == bool)) 203 | { 204 | return _thisPtr !is null; 205 | } 206 | 207 | /// 208 | ref C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc @trusted 209 | if (isImplicitlyConvertible!(T*, Q*)) 210 | { 211 | return *cast(typeof(return)*)&this; 212 | } 213 | 214 | /// ditto 215 | C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc const @trusted 216 | if (isImplicitlyConvertible!(const(T)*, Q*)) 217 | { 218 | return *cast(typeof(return)*)&this; 219 | } 220 | 221 | /// ditto 222 | C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc immutable @trusted 223 | if (isImplicitlyConvertible!(immutable(T)*, Q*)) 224 | { 225 | return *cast(typeof(return)*)&this; 226 | } 227 | 228 | /// ditto 229 | C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc const @system 230 | if (isImplicitlyConvertible!(immutable(T)*, Q*) && !isImplicitlyConvertible!(const(T)*, Q*)) 231 | { 232 | return *cast(typeof(return)*)&this; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /source/mir/interpolate/utility.d: -------------------------------------------------------------------------------- 1 | module mir.interpolate.utility; 2 | 3 | import mir.ndslice.slice; 4 | import std.traits; 5 | import mir.math.common: fmamath; 6 | 7 | package template DeepType(T) 8 | { 9 | static if (is(T : E[N], E, size_t N)) 10 | alias DeepType = DeepType!E; 11 | else 12 | alias DeepType = T; 13 | } 14 | 15 | /++ 16 | Quadratic function structure 17 | +/ 18 | struct ParabolaKernel(T) 19 | { 20 | /// 21 | T a = 0; 22 | /// 23 | T b = 0; 24 | /// 25 | T c = 0; 26 | 27 | @fmamath: 28 | 29 | /// 30 | this(T a, T b, T c) 31 | { 32 | this.a = a; 33 | this.b = b; 34 | this.c = c; 35 | } 36 | 37 | /// Builds parabola given three points 38 | this(T x0, T x1, T x2, T y0, T y1, T y2) 39 | { 40 | auto b1 = x1 - x0; 41 | auto b2 = x2 - x1; 42 | auto d1 = y1 - y0; 43 | auto d2 = y2 - y1; 44 | auto a1 = b1 * (x1 + x0); 45 | auto a2 = b2 * (x2 + x1); 46 | auto bm = - (b2 / b1); 47 | auto a3 = bm * a1 + a2; 48 | auto d3 = bm * d1 + d2; 49 | a = d3 / a3; 50 | b = (d1 - a1 * a) / b1; 51 | c = y1 - x1 * (a * x1 + b); 52 | } 53 | 54 | /++ 55 | Params: 56 | x0 = `x0` 57 | x1 = `x1` 58 | y0 = `f(x0)` 59 | y1 = `f(x1)` 60 | d1 = `f'(x1)` 61 | +/ 62 | static ParabolaKernel fromFirstDerivative(T x0, T x1, T y0, T y1, T d1) 63 | { 64 | auto dy = y1 - y0; 65 | auto dx = x1 - x0; 66 | auto dd = dy / dx; 67 | auto a = (d1 - dd) / dx; 68 | auto b = dd - a * (x0 + x1); 69 | auto c = y1 - (a * x1 + b) * x1; 70 | return ParabolaKernel(a, b, c); 71 | } 72 | 73 | /// 74 | auto opCall(uint derivative = 0)(T x) const 75 | if (derivative <= 2) 76 | { 77 | auto y = (a * x + b) * x + c; 78 | static if (derivative == 0) 79 | return y; 80 | else 81 | { 82 | auto y1 = 2 * a * x + b; 83 | static if (derivative == 1) 84 | return cast(T[2])[y, y1]; 85 | else 86 | return cast(T[3])[y, y1, 2 * a]; 87 | } 88 | } 89 | 90 | /// 91 | alias withDerivative = opCall!1; 92 | /// 93 | alias withTwoDerivatives = opCall!2; 94 | } 95 | 96 | /// ditto 97 | ParabolaKernel!(Unqual!(typeof(X.init - Y.init))) parabolaKernel(X, Y)(in X x0, in X x1, in X x2, const Y y0, const Y y1, const Y y2) 98 | { 99 | return typeof(return)(x0, x1, x2, y0, y1, y2); 100 | } 101 | 102 | /++ 103 | Returns: `[f'(x0), f'(x1), f'(x2)]` 104 | +/ 105 | Unqual!(typeof(X.init - Y.init))[3] parabolaDerivatives(X, Y)(in X x0, in X x1, in X x2, const Y y0, const Y y1, const Y y2) 106 | { 107 | auto d0 = (y2 - y1) / (x2 - x1); 108 | auto d1 = (y0 - y2) / (x0 - x2); 109 | auto d2 = (y1 - y0) / (x1 - x0); 110 | return [d1 + d2 - d0, d0 + d2 - d1, d0 + d1 - d2]; 111 | } 112 | 113 | version(mir_test) 114 | /// 115 | unittest 116 | { 117 | import mir.math.common: approxEqual; 118 | 119 | alias f = (double x) => 3 * x ^^ 2 + 7 * x + 5; 120 | auto x0 = 4; 121 | auto x1 = 9; 122 | auto x2 = 20; 123 | auto p = parabolaKernel(x0, x1, x2, f(x0), f(x1), f(x2)); 124 | 125 | assert(p.a.approxEqual(3)); 126 | assert(p.b.approxEqual(7)); 127 | assert(p.c.approxEqual(5)); 128 | assert(p(10).approxEqual(f(10))); 129 | } 130 | 131 | 132 | version(mir_test) 133 | /// 134 | unittest 135 | { 136 | import mir.math.common: approxEqual; 137 | 138 | alias f = (double x) => 3 * x ^^ 2 + 7 * x + 5; 139 | alias d = (double x) => 2 * 3 * x + 7; 140 | auto x0 = 4; 141 | auto x1 = 9; 142 | auto p = ParabolaKernel!double.fromFirstDerivative(x0, x1, f(x0), f(x1), d(x1)); 143 | 144 | assert(p.a.approxEqual(3)); 145 | assert(p.b.approxEqual(7)); 146 | assert(p.c.approxEqual(5)); 147 | } 148 | 149 | /++ 150 | Cubic function structure 151 | +/ 152 | struct CubicKernel(T) 153 | { 154 | /// 155 | T a = 0; 156 | /// 157 | T b = 0; 158 | /// 159 | T c = 0; 160 | /// 161 | T d = 0; 162 | 163 | @fmamath: 164 | 165 | /// 166 | this(T a, T b, T c, T d) 167 | { 168 | this.a = a; 169 | this.b = b; 170 | this.c = c; 171 | this.d = d; 172 | } 173 | 174 | /++ 175 | Params: 176 | x0 = `x0` 177 | x1 = `x1` 178 | y0 = `f(x0)` 179 | y1 = `f(x1)` 180 | dd0 = `f''(x0)` 181 | d1 = `f'(x1)` 182 | +/ 183 | static CubicKernel fromSecondAndFirstDerivative(T x0, T x1, T y0, T y1, T dd0, T d1) 184 | { 185 | auto hdd0 = 0.5f * dd0; 186 | auto dy = y1 - y0; 187 | auto dx = x1 - x0; 188 | auto dd = dy / dx; 189 | auto a = 0.5f * ((d1 - dd) / dx - hdd0) / dx; 190 | auto b = hdd0 - 3 * a * x0; 191 | auto c = d1 - (3 * a * x1 + 2 * b) * x1; 192 | auto d = y1 - ((a * x1 + b) * x1 + c) * x1; 193 | return CubicKernel!T(a, b, c, d); 194 | } 195 | 196 | /// 197 | auto opCall(uint derivative = 0)(T x) const 198 | if (derivative <= 3) 199 | { 200 | auto y = ((a * x + b) * x + c) * x + d; 201 | static if (derivative == 0) 202 | return y; 203 | else 204 | { 205 | T[derivative + 1] ret; 206 | ret[0] = y; 207 | ret[1] = (3 * a * x + 2 * b) * x + c; 208 | static if (derivative > 1) 209 | { 210 | ret[2] = 6 * a * x + 2 * b; 211 | static if (derivative > 2) 212 | ret[3] = 6 * a; 213 | } 214 | return ret; 215 | } 216 | } 217 | 218 | /// 219 | alias withDerivative = opCall!1; 220 | /// 221 | alias withTwoDerivatives = opCall!2; 222 | } 223 | 224 | version(mir_test) 225 | /// 226 | unittest 227 | { 228 | import mir.math.common: approxEqual; 229 | 230 | alias f = (double x) => 3 * x ^^ 3 + 7 * x ^^ 2 + 5 * x + 10; 231 | alias d = (double x) => 3 * 3 * x ^^ 2 + 2 * 7 * x + 5; 232 | alias s = (double x) => 6 * 3 * x + 2 * 7; 233 | auto x0 = 4; 234 | auto x1 = 9; 235 | auto p = CubicKernel!double.fromSecondAndFirstDerivative(x0, x1, f(x0), f(x1), s(x0), d(x1)); 236 | 237 | assert(p.a.approxEqual(3)); 238 | assert(p.b.approxEqual(7)); 239 | assert(p.c.approxEqual(5)); 240 | assert(p.d.approxEqual(10)); 241 | assert(p(13).approxEqual(f(13))); 242 | assert(p.opCall!1(13)[1].approxEqual(d(13))); 243 | assert(p.opCall!2(13)[2].approxEqual(s(13))); 244 | assert(p.opCall!3(13)[3].approxEqual(18)); 245 | } 246 | 247 | 248 | package auto pchipTail(T)(in T step0, in T step1, in T diff0, in T diff1) 249 | { 250 | import mir.math.common: copysign, fabs; 251 | if (!diff0) 252 | { 253 | return 0; 254 | } 255 | auto slope = ((step0 * 2 + step1) * diff0 - step0 * diff1) / (step0 + step1); 256 | if (copysign(1f, slope) != copysign(1f, diff0)) 257 | { 258 | return 0; 259 | } 260 | if ((copysign(1f, diff0) != copysign(1f, diff1)) && (fabs(slope) > fabs(diff0 * 3))) 261 | { 262 | return diff0 * 3; 263 | } 264 | return slope; 265 | } 266 | -------------------------------------------------------------------------------- /source/mir/bignum/internal/kernel.d: -------------------------------------------------------------------------------- 1 | module mir.bignum.internal.kernel; 2 | 3 | import mir.bignum.internal.phobos_kernel; 4 | public import mir.bignum.internal.phobos_kernel: karatsubaRequiredBuffSize, divisionRequiredBuffSize; 5 | 6 | private inout(uint)[] toUints(return scope inout ulong[] data) 7 | @trusted pure nothrow @nogc 8 | { 9 | auto ret = cast(typeof(return)) data; 10 | if (ret.length && ret[$ - 1] == 0) 11 | ret = ret[0 .. $ - 1]; 12 | return ret; 13 | } 14 | 15 | static if (is(size_t == ulong)) 16 | size_t multiply( 17 | scope ulong[] c, 18 | scope const(ulong)[] a, 19 | scope const(ulong)[] b, 20 | scope ulong[] buffer, 21 | ) 22 | @safe pure nothrow @nogc 23 | in (c.length >= a.length + b.length) 24 | { 25 | pragma (inline, false); 26 | 27 | c[a.length + b.length - 1] = 0; 28 | 29 | auto length = multiply( 30 | cast(uint[]) c[], 31 | a.toUints, 32 | b.toUints, 33 | cast(uint[]) buffer[], 34 | ); 35 | 36 | return length / 2 + length % 2; 37 | } 38 | 39 | size_t multiply( 40 | scope uint[] c, 41 | scope const(uint)[] a, 42 | scope const(uint)[] b, 43 | scope uint[] buffer, 44 | ) 45 | @trusted pure nothrow @nogc 46 | in (c.length >= a.length + b.length) 47 | { 48 | pragma (inline, false); 49 | 50 | if (a.length < b.length) 51 | { 52 | auto t = a; 53 | a = b; 54 | b = t; 55 | } 56 | 57 | if (b.length == 0) 58 | return 0; 59 | 60 | assert(a.length); 61 | assert(b.length); 62 | assert(c.length); 63 | 64 | c = c[0 .. a.length + b.length]; 65 | 66 | if (a is b) 67 | squareInternal(c, a, buffer); 68 | else 69 | mulInternal(c, a, b, buffer); 70 | 71 | auto ret = a.length + b.length; 72 | ret -= ret && !c[ret - 1]; 73 | return ret; 74 | } 75 | 76 | 77 | static if (is(size_t == ulong)) 78 | size_t divMod( 79 | scope ulong[] q, 80 | scope ulong[] r, 81 | scope const(ulong)[] u, 82 | scope const(ulong)[] v, 83 | scope ulong[] buffer, 84 | ) 85 | @trusted pure nothrow @nogc 86 | in (u.length == 0 || u[$ - 1]) 87 | in (v.length) 88 | in (v[$ - 1]) 89 | in (q.length + v.length >= u.length + 1) 90 | { 91 | pragma (inline, false); 92 | 93 | sizediff_t idx = u.length - v.length; 94 | if (idx < 0) 95 | return 0; 96 | 97 | auto ui = u.toUints; 98 | auto vi = v.toUints; 99 | 100 | auto length = divMod( 101 | cast(uint[])q, 102 | cast(uint[])r, 103 | ui, 104 | vi, 105 | cast(uint[]) buffer, 106 | ); 107 | 108 | if (r.length && vi.length % 2) 109 | r[vi.length / 2] &= uint.max; 110 | 111 | if (q.ptr is r.ptr) 112 | return 0; 113 | 114 | auto ret = length / 2; 115 | if (length % 2) 116 | q[ret++] &= uint.max; 117 | return ret; 118 | } 119 | 120 | size_t divMod( 121 | scope uint[] q, 122 | scope uint[] r, 123 | scope const(uint)[] u, 124 | scope const(uint)[] v, 125 | scope uint[] buffer, 126 | ) 127 | @trusted pure nothrow @nogc 128 | in (u.length == 0 || u[$ - 1]) 129 | in (v.length) 130 | in (v[$ - 1]) 131 | in (q.length + v.length >= u.length + 1) 132 | { 133 | pragma (inline, false); 134 | 135 | import mir.bitop: ctlz; 136 | import mir.utility: _expect; 137 | 138 | if (u.length < v.length) 139 | return 0; 140 | 141 | q = q[0 .. u.length - v.length + 1]; 142 | if (v.length == 1) 143 | { 144 | if (q !is u) 145 | q[] = u; 146 | auto rem = multibyteDivAssign(q, v[0], 0); 147 | if (r) 148 | { 149 | r[0] = rem; 150 | r[1 .. $] = 0; 151 | } 152 | } 153 | else 154 | { 155 | divModInternal(q[], r ? r[0 .. v.length] : null, u, v, buffer); 156 | } 157 | 158 | auto length = u.length - v.length; 159 | return length + (q[length] != 0); 160 | } 161 | 162 | /// 163 | version(mir_bignum_test) 164 | unittest 165 | { 166 | import mir.bignum.fixed: UInt; 167 | import mir.bignum.low_level_view: BigUIntView; 168 | 169 | uint[100] buffer = void; 170 | auto divMod(BigUIntView!uint a, BigUIntView!uint b, BigUIntView!uint c) 171 | { 172 | .divMod( 173 | c.coefficients, 174 | a.coefficients, 175 | a.coefficients, 176 | b.coefficients, 177 | buffer, 178 | ); 179 | a.coefficients[b.coefficients.length .. $] = 0; 180 | } 181 | 182 | { 183 | // Test division by a single-digit divisor here. 184 | auto dividend = BigUIntView!uint.fromHexString("5"); 185 | auto divisor = BigUIntView!uint.fromHexString("5"); 186 | auto quotient = BigUIntView!uint([0u]); 187 | // auto remainder = BigUIntView!uint.fromHexString("0"); 188 | 189 | divMod(dividend, divisor, quotient); 190 | assert(quotient == BigUIntView!uint.fromHexString("1")); 191 | } 192 | 193 | { 194 | // Test division by a single-digit divisor here. 195 | auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf"); 196 | auto divisor = BigUIntView!uint.fromHexString("5"); 197 | auto quotient = BigUIntView!uint.fromHexString("1000000000000000000000000000000000000000000000000000000000000000"); 198 | divMod(dividend, divisor, quotient); 199 | assert(dividend.normalized == BigUIntView!uint.fromHexString("3")); 200 | assert(quotient == BigUIntView!uint.fromHexString("1120a1229e8a217d0691b02b819107174a4b677088ef0df874259b283c1d588c")); 201 | } 202 | 203 | // Test big number division 204 | { 205 | auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3"); 206 | auto divisor = BigUIntView!uint.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072"); 207 | auto quotient = BigUIntView!uint.fromHexString("10000000000000000000000000000000000000000000000000000000000000000"); 208 | // auto remainder = BigUIntView!uint.fromHexString("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); 209 | 210 | divMod(dividend, divisor, quotient); 211 | assert(quotient.normalized == BigUIntView!uint.fromHexString("ff3a8aa4da35237811a0ffbf007fe938630dee8a810f2f82ae01f80c033291f6")); 212 | assert(dividend.normalized == BigUIntView!uint.fromHexString("27926263cf248bef1c2cd63ea004d9f7041bffc8568560ec30fc9a9548057857")); 213 | } 214 | 215 | // Trigger the adding back sequence 216 | { 217 | auto dividend = BigUIntView!uint.fromHexString("800000000000000000000003"); 218 | auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001"); 219 | auto quotient = BigUIntView!uint([0u]); 220 | // auto remainder = BigUIntView!uint.fromHexString("100000000000000000000000"); 221 | divMod(dividend, divisor, quotient); 222 | assert(quotient == BigUIntView!uint.fromHexString("3")); 223 | assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000")); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /cpp_example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "mir/interpolate.h" 7 | #include "mir/series.h" 8 | #include "mir/rcarray.h" 9 | #include "mir/rcptr.h" 10 | #include "mir/slim_rcptr.h" 11 | #include "mir/ndslice.h" 12 | #include "mir/numeric.h" 13 | 14 | namespace Space 15 | { 16 | mir_slice eye(size_t n); 17 | void printMatrix(mir_slice matrix); 18 | void initWithIota(mir_rcarray &a); 19 | void reverseRcSlice(mir_slice>& a); 20 | } 21 | 22 | void testSeries(); 23 | void testSRCPtr(); 24 | void testRCPtr(); 25 | void testPM(); 26 | void testFindRoot(); 27 | void testStringView(); 28 | void testDestructorView(); 29 | 30 | int main() 31 | { 32 | mir_slice matrix = Space::eye(3); 33 | matrix(1, 2) = 4; 34 | assert(matrix.row(1)[2] == 4); 35 | assert(matrix.col(2)[1] == 4); 36 | Space::printMatrix(matrix); 37 | std::free(matrix._iterator); 38 | 39 | 40 | // test rcarray constructors and destructors 41 | mir_rcarray a(3); // [NaN, NaN, NaN] 42 | mir_rcarray al = {5, 6, 4}; // [5, 6, 4] 43 | mir_rcarray av = std::vector({5, 6, 4}); // [5, 6, 4] 44 | assert(a.size() == 3); 45 | assert(al.size() == 3); 46 | assert(av.size() == 3); 47 | 48 | assert(al[0] == 5); 49 | assert(al[1] == 6); 50 | assert(al[2] == 4); 51 | 52 | assert(al.backward(2) == 5); 53 | assert(al.backward(1) == 6); 54 | assert(al.backward(0) == 4); 55 | 56 | assert(av[0] == 5); 57 | assert(av[1] == 6); 58 | assert(av[2] == 4); 59 | 60 | Space::initWithIota(a); //[0, 1, 2] 61 | auto b = a; // check copy constructor 62 | auto c = b.asSlice(); 63 | auto d = c; // check copy constructor 64 | Space::reverseRcSlice(d); //[2, 1, 0] 65 | 66 | // reversed 0 1 2 (iota) 67 | assert(a[0] == 2); 68 | assert(a[1] == 1); 69 | assert(a[2] == 0); 70 | 71 | assert(c[0] == 2); 72 | assert(c[1] == 1); 73 | assert(c[2] == 0); 74 | 75 | assert(&c[1] == &a[1]); 76 | 77 | assert(d._iterator._iterator == a.data()); 78 | 79 | // check foreach loops for rcarray 80 | for (auto& elem : a) 81 | { 82 | elem = 0; 83 | } 84 | 85 | const auto e = a; 86 | for (auto& elem : e) 87 | { 88 | assert(elem == 0); 89 | } 90 | 91 | for (auto& elem : c) 92 | { 93 | elem = 1; 94 | } 95 | 96 | for (auto& elem : c) 97 | { 98 | assert(elem == 1); 99 | } 100 | 101 | a = b; 102 | al = a; 103 | 104 | testSeries(); 105 | testSRCPtr(); 106 | testRCPtr(); 107 | testPM(); 108 | testStringView(); 109 | testDestructorView(); 110 | 111 | return 0; 112 | } 113 | 114 | void testSeries() 115 | { 116 | std::map map; 117 | map[1] = 4.0; 118 | map[2] = 5.0; 119 | map[3] = 6.0; // index 5 replaced with index 4 below 120 | map[5] = 10.0; 121 | map[10] = 11.0; 122 | 123 | auto series = mir::make_series(map); 124 | 125 | assert(series[1].first == 2); 126 | assert(series[1].second == 5); 127 | 128 | double sum = 0; 129 | for (auto&& [key, value] : series) 130 | { 131 | sum += value; 132 | } 133 | 134 | assert(sum == 36); 135 | 136 | series.index()[2] = 4; 137 | series.data()[2] = 10; 138 | 139 | assert(series[2].first == 4); 140 | assert(series[2].second == 10); 141 | 142 | auto s = std::move(series); 143 | auto s2 = s; 144 | s2 = s; 145 | 146 | double value; 147 | int key; 148 | assert(s.try_get(2, value) && value == 5.0); 149 | assert(*s.try_get_ptr(2) == 5.0); 150 | // printf("%ld\n", s.index()[s.transition_index_less(4)]); 151 | assert(!s.try_get(8, value)); 152 | 153 | assert(s.try_get_next(2, value) && value == 5.0); 154 | assert(s.try_get_prev(2, value) && value == 5.0); 155 | assert(s.try_get_next(8, value) && value == 11.0); 156 | assert(s.try_get_prev(8, value) && value == 10.0); 157 | assert(!s.try_get_first(8, 9, value)); 158 | assert(s.try_get_first(2, 10, value) && value == 5.0); 159 | assert(s.try_get_last(2, 10, value) && value == 11.0); 160 | assert(s.try_get_last(2, 8, value) && value == 10.0); 161 | 162 | key = 2; assert(s.try_get_next_update_key(key, value) && key == 2 && value == 5.0); 163 | key = 2; assert(s.try_get_prev_update_key(key, value) && key == 2 && value == 5.0); 164 | key = 8; assert(s.try_get_next_update_key(key, value) && key == 10 && value == 11.0); 165 | key = 8; assert(s.try_get_prev_update_key(key, value) && key == 5 && value == 10.0); 166 | key = 2; assert(s.try_get_first_update_lower(key, 10, value) && key == 2 && value == 5.0); 167 | key = 10; assert(s.try_get_last_update_upper(2, key, value) && key == 10 && value == 11.0); 168 | key = 8; assert(s.try_get_last_update_upper(2, key, value) && key == 5 && value == 10.0); 169 | } 170 | 171 | struct S { double d = 0; S() {}; S(double e) : d(e) {} }; 172 | struct C : S { double j = 3; C(double d, double k) : S(d) { k = j; }; }; 173 | 174 | void testSRCPtr() 175 | { 176 | auto s = mir::make_slim_shared(3.0); 177 | auto e = mir::make_slim_shared(5.0); 178 | s = e; 179 | (*e).d = 4; 180 | assert(s->d == 4); 181 | assert(s.getContext()->counter == 2); 182 | s = nullptr; 183 | assert(e.getContext()->counter == 1); 184 | } 185 | 186 | void testRCPtr() 187 | { 188 | auto s = mir::make_shared(3.0); 189 | auto e = mir::make_shared(5.0); 190 | s = e; 191 | (*e).d = 4; 192 | assert(s->d == 4); 193 | assert(s.getContext()->counter == 2); 194 | s = nullptr; 195 | assert(e.getContext()->counter == 1); 196 | } 197 | 198 | void testPM() 199 | { 200 | auto c = mir::make_shared(3.0, 4); 201 | assert(c.getContext()->counter == 1); 202 | auto s = mir_rcptr(c); 203 | assert(c.getContext()->counter == 2); 204 | assert(s->d == 3); 205 | } 206 | 207 | void testFindRoot() 208 | { 209 | std::function func = [](double x) { return x * x - 1; }; 210 | std::function tolerance = [](double a, double b) { return b - a < 1e-6; }; 211 | double a = 0; 212 | double b = 10e100; 213 | auto result = mir_find_root(func, tolerance, a, b); 214 | assert(result.validate().x() == 1); 215 | 216 | // with relative tolerance 217 | assert(mir_find_root(func, 1e-6, a, b).validate().x()); 218 | } 219 | 220 | void testStringView() 221 | { 222 | auto ref = std::string_view("Hi"); 223 | auto b = mir::rcarray_from_string(ref); 224 | mir_rcarray c = mir::rcarray_from_string(std::string("Hi")); 225 | mir_rcarray d = mir::rcarray_from_string("Hi"); 226 | assert(ref == mir::get_string_view(b)); 227 | assert(ref == mir::get_string_view(c)); 228 | assert(ref == mir::get_string_view(d)); 229 | assert(typeid((mir::get_string_view(c))) == typeid((mir::get_string_view(d)))); 230 | ref = c; // implicit conversion 231 | ref = d; // implicit conversion 232 | } 233 | 234 | static int CD_i; 235 | 236 | struct CD 237 | { 238 | double d; 239 | ~CD() noexcept 240 | { 241 | CD_i++; 242 | } 243 | }; 244 | 245 | void testDestructorView() 246 | { 247 | auto s = new mir_rcarray(10); 248 | delete s; 249 | printf("%d\n", CD_i); 250 | assert(CD_i == 10); 251 | } 252 | -------------------------------------------------------------------------------- /source/mir/algorithm/setops.d: -------------------------------------------------------------------------------- 1 | // Written in the D programming language. 2 | /** 3 | This is a submodule of $(MREF mir, algorithm). It contains `nothrow` `@nogc` BetterC alternative to `MultiwayMerge` from `std.algorithm.setops`. 4 | 5 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 6 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 7 | 8 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilia Ki (Mir & BetterC rework, optimization). 9 | */ 10 | module mir.algorithm.setops; 11 | 12 | import core.lifetime: move; 13 | import mir.functional: naryFun; 14 | import mir.primitives; 15 | import mir.qualifier; 16 | import std.range.primitives: isRandomAccessRange; 17 | 18 | /** 19 | Merges multiple sets. The input sets are passed as a 20 | range of ranges and each is assumed to be sorted by $(D 21 | less). Computation is done lazily, one union element at a time. The 22 | complexity of one $(D popFront) operation is $(BIGOH 23 | log(ror.length)). However, the length of $(D ror) decreases as ranges 24 | in it are exhausted, so the complexity of a full pass through $(D 25 | MultiwayMerge) is dependent on the distribution of the lengths of ranges 26 | contained within $(D ror). If all ranges have the same length $(D n) 27 | (worst case scenario), the complexity of a full pass through $(D 28 | MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D 29 | log(ror.length)) times worse than just spanning all ranges in 30 | turn. The output comes sorted (unstably) by $(D less). 31 | The length of the resulting range is the sum of all lengths of 32 | the ranges passed as input. This means that all elements (duplicates 33 | included) are transferred to the resulting range. 34 | For backward compatibility, `multiwayMerge` is available under 35 | the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` . 36 | Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion` 37 | and `NWayUnion` will be deprecated. 38 | Params: 39 | less = Predicate the given ranges are sorted by. 40 | ror = A range of ranges sorted by `less` to compute the union for. 41 | Returns: 42 | A range of the union of the ranges in `ror`. 43 | Warning: Because $(D MultiwayMerge) does not allocate extra memory, it 44 | will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership 45 | of $(D ror) and discretionarily swaps and advances elements of it. If 46 | you want $(D ror) to preserve its contents after the call, you may 47 | want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the 48 | duplicate in between calls). 49 | */ 50 | struct MultiwayMerge(alias less, RangeOfRanges) 51 | if (isRandomAccessRange!RangeOfRanges) 52 | { 53 | import mir.primitives; 54 | import mir.container.binaryheap; 55 | 56 | /// 57 | @disable this(); 58 | /// 59 | @disable this(this); 60 | 61 | /// 62 | static bool compFront(ElementType!RangeOfRanges a, ElementType!RangeOfRanges b) 63 | { 64 | // revert comparison order so we get the smallest elements first 65 | return less(b.front, a.front); 66 | } 67 | 68 | /// Heap 69 | BinaryHeap!(compFront, RangeOfRanges) _heap; 70 | 71 | /// 72 | this(scope return RangeOfRanges ror) 73 | { 74 | // Preemptively get rid of all empty ranges in the input 75 | // No need for stability either 76 | auto temp = ror.lightScope; 77 | for (;!temp.empty;) 78 | { 79 | if (!temp.front.empty) 80 | { 81 | temp.popFront; 82 | continue; 83 | } 84 | import mir.utility: swap; 85 | () @trusted {swap(temp.back, temp.front);} (); 86 | temp.popBack; 87 | ror.popBack; 88 | } 89 | //Build the heap across the range 90 | _heap = typeof(_heap)(ror.move); 91 | } 92 | 93 | /// 94 | @property bool empty() scope const { return _heap.empty; } 95 | 96 | /// 97 | @property auto ref front() 98 | { 99 | assert(!empty); 100 | return _heap.front.front; 101 | } 102 | 103 | /// 104 | void popFront() scope @safe 105 | { 106 | _heap._store.front.popFront; 107 | if (!_heap._store.front.empty) 108 | _heap.siftDown(_heap._store[], 0, _heap._length); 109 | else 110 | _heap.removeFront; 111 | } 112 | } 113 | 114 | /// Ditto 115 | MultiwayMerge!(naryFun!less, RangeOfRanges) multiwayMerge 116 | (alias less = "a < b", RangeOfRanges) 117 | (scope RangeOfRanges ror) 118 | { 119 | return typeof(return)(move(ror)); 120 | } 121 | 122 | /// 123 | @safe nothrow @nogc version(mir_test) unittest 124 | { 125 | import mir.algorithm.iteration: equal; 126 | 127 | static a = 128 | [ 129 | [ 1, 4, 7, 8 ], 130 | [ 1, 7 ], 131 | [ 1, 7, 8], 132 | [ 4 ], 133 | [ 7 ], 134 | ]; 135 | static witness = [ 136 | 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8 137 | ]; 138 | assert(a.multiwayMerge.equal(witness)); 139 | 140 | static b = 141 | [ 142 | // range with duplicates 143 | [ 1, 1, 4, 7, 8 ], 144 | [ 7 ], 145 | [ 1, 7, 8], 146 | [ 4 ], 147 | [ 7 ], 148 | ]; 149 | // duplicates are propagated to the resulting range 150 | assert(b.multiwayMerge.equal(witness)); 151 | } 152 | 153 | /** 154 | Computes the union of multiple ranges. The input ranges are passed 155 | as a range of ranges and each is assumed to be sorted by $(D 156 | less). Computation is done lazily, one union element at a time. 157 | `multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`. 158 | "The output of multiwayUnion has no duplicates even when its inputs contain duplicates." 159 | Params: 160 | less = Predicate the given ranges are sorted by. 161 | ror = A range of ranges sorted by `less` to compute the intersection for. 162 | Returns: 163 | A range of the union of the ranges in `ror`. 164 | See also: $(LREF multiwayMerge) 165 | */ 166 | auto multiwayUnion(alias less = "a < b", RangeOfRanges)(scope RangeOfRanges ror) 167 | { 168 | import mir.functional: not; 169 | import mir.algorithm.iteration : Uniq; 170 | 171 | return Uniq!(not!less, typeof(multiwayMerge!less(ror)))(multiwayMerge!less(move(ror))); 172 | } 173 | 174 | /// 175 | @safe version(mir_test) unittest 176 | { 177 | import mir.algorithm.iteration: equal; 178 | 179 | // sets 180 | double[][] a = 181 | [ 182 | [ 1, 4, 7, 8 ], 183 | [ 1, 7 ], 184 | [ 1, 7, 8], 185 | [ 4 ], 186 | [ 7 ], 187 | ]; 188 | 189 | auto witness = [1, 4, 7, 8]; 190 | assert(a.multiwayUnion.equal(witness)); 191 | 192 | // multisets 193 | double[][] b = 194 | [ 195 | [ 1, 1, 1, 4, 7, 8 ], 196 | [ 1, 7 ], 197 | [ 1, 7, 7, 8], 198 | [ 4 ], 199 | [ 7 ], 200 | ]; 201 | assert(b.multiwayUnion.equal(witness)); 202 | } 203 | 204 | /++ 205 | Computes the length of union of multiple ranges. The input ranges are passed 206 | as a range of ranges and each is assumed to be sorted by `less`. 207 | 208 | Params: 209 | less = Predicate the given ranges are sorted by. 210 | ror = A range of ranges sorted by `less` to compute the intersection for. 211 | Returns: 212 | A length of the union of the ranges in `ror`. 213 | +/ 214 | pragma(inline, false) 215 | size_t unionLength(alias less = "a < b", RangeOfRanges)(scope RangeOfRanges ror) 216 | { 217 | size_t length; 218 | auto u = move(ror).multiwayUnion!less; 219 | if (!u.empty) do { 220 | length++; 221 | u.popFront; 222 | } while(!u.empty); 223 | return length; 224 | } 225 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | # allow this workflow to be triggered manually 12 | 13 | # Only allow for one job to run at a time, and cancel any jobs currently in progress. 14 | concurrency: 15 | group: gh-actions-${{ github.actor }}-${{ github.head_ref }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | setup: 20 | name: 'Load job configuration' 21 | runs-on: ubuntu-20.04 22 | outputs: 23 | compilers: ${{ steps.load-config.outputs.compilers }} 24 | steps: 25 | - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 26 | # This step checks if we want to only run tests on a specific platform or 27 | # if we want to skip CI entirely, then outputs the compilers to be used for 28 | # each job. 29 | - id: load-config 30 | uses: actions/github-script@9ac08808f993958e9de277fe43a64532a609130e 31 | with: 32 | script: | 33 | const base_compiler_config = require("./.github/workflows/compilers.json"); 34 | const compilers = {"windows": [], "macos": [], "ubuntu": []}; 35 | const {owner, repo} = context.repo; 36 | let commit_sha = context.sha; 37 | if (context.eventName == "pull_request") 38 | { 39 | commit_sha = context.payload.pull_request.head.sha; 40 | } 41 | 42 | const commit = await github.rest.git.getCommit({ 43 | owner, 44 | repo, 45 | commit_sha 46 | }); 47 | const head_commit_message = commit.data.message; 48 | 49 | if (head_commit_message.startsWith("[windows-only]")) 50 | { 51 | compilers.windows = base_compiler_config; 52 | } 53 | else if (head_commit_message.startsWith("[macos-only]")) 54 | { 55 | compilers.macos = base_compiler_config; 56 | } 57 | else if (head_commit_message.startsWith("[ubuntu-only]")) 58 | { 59 | compilers.ubuntu = base_compiler_config; 60 | } 61 | else if (!head_commit_message.startsWith("[skip-ci]")) 62 | { 63 | compilers.windows = base_compiler_config; 64 | compilers.macos = base_compiler_config; 65 | compilers.ubuntu = base_compiler_config; 66 | } 67 | core.setOutput("compilers", JSON.stringify(compilers)); 68 | 69 | macos: 70 | name: '[macos] x86_64/${{ matrix.dc }}' 71 | runs-on: macos-11 72 | needs: setup 73 | # Only run if the setup phase explicitly defined compilers to be used 74 | if: ${{ fromJSON(needs.setup.outputs.compilers).macos != '' && fromJSON(needs.setup.outputs.compilers).macos != '[]' }} 75 | # Beta / master versions of any compiler are allowed to fail 76 | continue-on-error: ${{ contains(matrix.dc, 'beta') || contains(matrix.dc, 'master') }} 77 | env: 78 | ARCH: x86_64 79 | strategy: 80 | fail-fast: false 81 | matrix: 82 | dc: ${{ fromJSON(needs.setup.outputs.compilers).macos }} 83 | steps: 84 | - name: Checkout repo 85 | uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 86 | - name: Setup D compiler 87 | uses: dlang-community/setup-dlang@763d869b4d67e50c3ccd142108c8bca2da9df166 88 | with: 89 | compiler: ${{ matrix.dc }} 90 | - name: Cache dub dependencies 91 | uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed 92 | with: 93 | path: ~/.dub/packages 94 | key: macos-latest-build-${{ hashFiles('**/dub.sdl', '**/dub.json') }} 95 | restore-keys: | 96 | macos-latest-build- 97 | - name: Build / test 98 | run: | 99 | dub test --arch=$ARCH --build=unittest-cov 100 | dub test --arch=$ARCH --combined 101 | shell: bash 102 | - name: Upload coverage data 103 | uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b 104 | 105 | ubuntu: 106 | name: '[ubuntu] ${{ matrix.arch }}/${{ matrix.dc }}' 107 | runs-on: ubuntu-20.04 108 | needs: setup 109 | # Only run if the setup phase explicitly defined compilers to be used 110 | if: ${{ fromJSON(needs.setup.outputs.compilers).ubuntu != '' && fromJSON(needs.setup.outputs.compilers).ubuntu != '[]' }} 111 | # Beta / master versions of any compiler are allowed to fail 112 | continue-on-error: ${{ contains(matrix.dc, 'beta') || contains(matrix.dc, 'master') }} 113 | env: 114 | ARCH: ${{ matrix.arch }} 115 | strategy: 116 | fail-fast: false 117 | matrix: 118 | dc: ${{ fromJSON(needs.setup.outputs.compilers).ubuntu }} 119 | arch: [x86, x86_64] 120 | steps: 121 | - name: Checkout repo 122 | uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 123 | - name: Setup D compiler 124 | uses: dlang-community/setup-dlang@763d869b4d67e50c3ccd142108c8bca2da9df166 125 | with: 126 | compiler: ${{ matrix.dc }} 127 | - name: Install multi-lib for 32-bit systems 128 | if: matrix.arch == 'x86' 129 | run: sudo apt-get update && sudo apt-get install gcc-multilib 130 | - name: Cache dub dependencies 131 | uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed 132 | with: 133 | path: ~/.dub/packages 134 | key: ubuntu-latest-build-${{ hashFiles('**/dub.sdl', '**/dub.json') }} 135 | restore-keys: | 136 | ubuntu-latest-build- 137 | - name: Build / test 138 | run: | 139 | dub test --arch=$ARCH --build=unittest-cov 140 | dub test --arch=$ARCH --combined 141 | shell: bash 142 | - name: Upload coverage data 143 | uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b 144 | 145 | windows: 146 | name: '[windows] x86_64/${{ matrix.dc }}' 147 | runs-on: windows-2022 148 | needs: setup 149 | # Only run if the setup phase explicitly defined compilers to be used 150 | if: ${{ fromJSON(needs.setup.outputs.compilers).windows != '' && fromJSON(needs.setup.outputs.compilers).windows != '[]' }} 151 | # Beta / master versions of any compiler are allowed to fail 152 | continue-on-error: ${{ contains(matrix.dc, 'beta') || contains(matrix.dc, 'master') }} 153 | env: 154 | ARCH: x86_64 155 | strategy: 156 | fail-fast: false 157 | matrix: 158 | dc: ${{ fromJSON(needs.setup.outputs.compilers).windows }} 159 | steps: 160 | - name: Checkout repo 161 | uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 162 | - name: Setup D compiler 163 | uses: dlang-community/setup-dlang@763d869b4d67e50c3ccd142108c8bca2da9df166 164 | with: 165 | compiler: ${{ matrix.dc }} 166 | - name: Cache dub dependencies 167 | uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed 168 | with: 169 | path: ~\AppData\Local\dub 170 | key: windows-latest-build-${{ hashFiles('**/dub.sdl', '**/dub.json') }} 171 | restore-keys: | 172 | windows-latest-build- 173 | # Tests are split up to work around OOM errors -- no combined testing is done 174 | # as it's simply too big for the compiler to handle on Windows. 175 | - name: Build / test 176 | run: | 177 | dub test --arch=$ARCH --build=unittest-ci -c ci-bignum-test 178 | dub test --arch=$ARCH --build=unittest-ci -c ci-core-test 179 | dub test --arch=$ARCH --build=unittest-ci -c ci-ndslice-test 180 | dub test --arch=$ARCH --build=unittest-ci -c ci-test 181 | shell: bash 182 | - name: Upload coverage data 183 | uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b 184 | 185 | 186 | -------------------------------------------------------------------------------- /source/mir/ndslice/ndfield.d: -------------------------------------------------------------------------------- 1 | /++ 2 | This is a submodule of $(MREF mir,ndslice). 3 | 4 | NdField is a type with `opIndex(size_t[N] index...)` primitive. 5 | An ndslice can be created on top of a ndField using $(SUBREF slice, slicedNdField). 6 | 7 | $(BOOKTABLE $(H2 NdFields), 8 | $(TR $(TH NdField Name) $(TH Used By)) 9 | $(T2 Cartesian, $(SUBREF topology, cartesian)) 10 | $(T2 Kronecker, $(SUBREF topology, kronecker)) 11 | ) 12 | 13 | See_also: $(SUBREF concatenation, concatenation). 14 | 15 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 16 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 17 | Authors: Ilia Ki 18 | 19 | Macros: 20 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 21 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 22 | +/ 23 | module mir.ndslice.ndfield; 24 | 25 | import mir.qualifier; 26 | import mir.internal.utility; 27 | import mir.ndslice.internal; 28 | import mir.ndslice.slice; 29 | import mir.primitives; 30 | import std.meta; 31 | 32 | private template _indices(NdFields...) 33 | { 34 | static if (NdFields.length == 0) 35 | enum _indices = ""; 36 | else 37 | { 38 | alias Next = NdFields[0 .. $ - 1]; 39 | enum i = Next.length; 40 | enum _indices = ._indices!Next ~ 41 | "_fields[" ~ i.stringof ~ "][" ~ _indices_range!([staticMap!(DimensionCount, Next)].sum, DimensionCount!(NdFields[$ - 1])) ~ "], "; 42 | } 43 | } 44 | 45 | private template _indices_range(size_t begin, size_t count) 46 | { 47 | static if (count == 0) 48 | enum _indices_range = ""; 49 | else 50 | { 51 | enum next = count - 1; 52 | enum elem = begin + next; 53 | enum _indices_range = ._indices_range!(begin, next) ~ "indices[" ~ elem.stringof ~ "], "; 54 | } 55 | } 56 | 57 | /// 58 | struct Cartesian(NdFields...) 59 | if (NdFields.length > 1) 60 | { 61 | /// 62 | NdFields _fields; 63 | 64 | package(mir) enum size_t M(size_t f) = [staticMap!(DimensionCount, NdFields[0..f])].sum; 65 | package(mir) enum size_t N = M!(NdFields.length); 66 | 67 | /// 68 | auto lightConst()() const @property 69 | { 70 | import std.format; 71 | import mir.ndslice.topology: iota; 72 | return mixin("Cartesian!(staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota)); 73 | } 74 | 75 | /// 76 | auto lightImmutable()() immutable @property 77 | { 78 | import std.format; 79 | import mir.ndslice.topology: iota; 80 | return mixin("Cartesian!(staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota)); 81 | } 82 | 83 | /// 84 | size_t length(size_t d = 0)() @safe scope const @property 85 | { 86 | foreach(f, NdField; NdFields) 87 | static if (M!f <= d && M!(f + 1) > d) 88 | { 89 | enum d = d - M!f; 90 | static if (d) 91 | return _fields[f].length!(d - M!f); 92 | else 93 | return _fields[f].length; 94 | } 95 | } 96 | 97 | /// 98 | size_t[N] shape()() @safe scope const @property 99 | { 100 | typeof(return) ret; 101 | foreach(f, NdField; NdFields) 102 | { 103 | static if (hasShape!NdField) 104 | { 105 | auto s = _fields[f].shape; 106 | foreach(j; Iota!(s.length)) 107 | ret[M!f + j] = s[j]; 108 | } 109 | else 110 | { 111 | ret[M!f] = _fields[f].length; 112 | } 113 | } 114 | return ret; 115 | } 116 | 117 | /// 118 | size_t elementCount()() @safe scope const @property 119 | { 120 | size_t ret = 1; 121 | foreach (f, NdField; NdFields) 122 | ret *= _fields[f].elementCount; 123 | return ret; 124 | } 125 | 126 | /// 127 | auto opIndex(size_t[N] indices...) 128 | { 129 | import mir.functional : tuple; 130 | return mixin("tuple(" ~ _indices!(NdFields) ~ ")"); 131 | } 132 | } 133 | 134 | private template _kr_indices(size_t n) 135 | { 136 | static if (n == 0) 137 | enum _kr_indices = ""; 138 | else 139 | { 140 | enum i = n - 1; 141 | enum _kr_indices = ._kr_indices!i ~ "_fields[" ~ i.stringof ~ "][ind[" ~ i.stringof ~ "]], "; 142 | } 143 | } 144 | 145 | /// 146 | struct Kronecker(alias fun, NdFields...) 147 | if (NdFields.length > 1 && allSatisfy!(templateOr!(hasShape, hasLength), NdFields[1 .. $])) 148 | { 149 | /// 150 | NdFields _fields; 151 | 152 | /// 153 | auto lightConst()() const @property 154 | { 155 | import std.format; 156 | import mir.ndslice.topology: iota; 157 | return mixin("Kronecker!(fun, staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota)); 158 | } 159 | 160 | /// 161 | auto lightImmutable()() immutable @property 162 | { 163 | import std.format; 164 | import mir.ndslice.topology: iota; 165 | return mixin("Kronecker!(fun, staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota)); 166 | } 167 | 168 | private enum N = DimensionCount!(NdFields[$-1]); 169 | 170 | /// 171 | size_t length(size_t d = 0)() scope const @property 172 | { 173 | static if (d == 0) 174 | { 175 | size_t ret = 1; 176 | foreach (f, NdField; NdFields) 177 | ret *= _fields[f].length; 178 | } 179 | else 180 | { 181 | size_t ret = 1; 182 | foreach (f, NdField; NdFields) 183 | ret *= _fields[f].length!d; 184 | } 185 | return ret; 186 | } 187 | 188 | 189 | /// 190 | size_t[N] shape()() scope const @property 191 | { 192 | static if (N > 1) 193 | { 194 | size_t[N] ret = 1; 195 | foreach (f, NdField; NdFields) 196 | { 197 | auto s = _fields[f].shape; 198 | foreach(i; Iota!N) 199 | ret[i] *= s[i]; 200 | } 201 | return ret; 202 | } 203 | else 204 | { 205 | size_t[1] ret = 1; 206 | foreach (f, NdField; NdFields) 207 | ret[0] *= _fields[f].length; 208 | return ret; 209 | } 210 | } 211 | 212 | /// 213 | size_t elementCount()() scope const @property 214 | { 215 | size_t ret = 1; 216 | foreach (f, NdField; NdFields) 217 | ret *= _fields[f].elementCount; 218 | ret; 219 | } 220 | 221 | /// 222 | auto ref opIndex()(size_t[N] indices...) 223 | { 224 | static if (N > 1) 225 | size_t[N][NdFields.length] ind; 226 | else 227 | size_t[NdFields.length] ind; 228 | foreach_reverse (f, NdField; NdFields) 229 | { 230 | static if (f) 231 | { 232 | static if (hasShape!(NdFields[f])) 233 | { 234 | auto s = _fields[f].shape; 235 | } 236 | else 237 | { 238 | size_t[1] s; 239 | s[0] = _fields[f].length; 240 | } 241 | static if (N > 1) 242 | { 243 | foreach(i; Iota!N) 244 | { 245 | ind[f][i] = indices[i] % s[i]; 246 | indices[i] /= s[i]; 247 | } 248 | } 249 | else 250 | { 251 | ind[f] = indices[0] % s[0]; 252 | indices[0] /= s[0]; 253 | } 254 | } 255 | else 256 | { 257 | static if (N > 1) 258 | { 259 | foreach(i; Iota!N) 260 | ind[f][i] = indices[i]; 261 | } 262 | else 263 | { 264 | ind[f] = indices[0]; 265 | } 266 | } 267 | } 268 | return mixin("fun(" ~ _kr_indices!(ind.length) ~ ")"); 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /source/mir/interpolate/extrapolate.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H2 Extrapolators) 3 | 4 | See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate) 5 | 6 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 8 | Authors: Ilia Ki 9 | 10 | Macros: 11 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP) 12 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 13 | +/ 14 | module mir.interpolate.extrapolate; 15 | 16 | /++ 17 | Constant extrapolator 18 | +/ 19 | ConstantExtrapolator!T constantExtrapolator(T)(T interpolator) 20 | { 21 | import core.lifetime: move; 22 | return typeof(return)(interpolator.move); 23 | } 24 | 25 | 26 | /// ditto 27 | struct ConstantExtrapolator(T) 28 | if (__traits(hasMember, T, "gridScopeView")) 29 | { 30 | /// 31 | T data; 32 | 33 | /// 34 | this(T data) 35 | { 36 | import core.lifetime: move; 37 | this.data = data.move; 38 | } 39 | 40 | /// 41 | ConstantExtrapolator lightConst()() const @property { return *cast(ConstantExtrapolator*)&this; } 42 | 43 | /// 44 | auto gridScopeView(size_t dimension = 0)() return scope const @property @trusted 45 | { 46 | return data.gridScopeView!dimension; 47 | } 48 | 49 | /// 50 | enum uint derivativeOrder = 1; 51 | 52 | static if (__traits(compiles, {enum N = T.dimensionCount;})) 53 | /// 54 | enum uint dimensionCount = T.dimensionCount + 1; 55 | 56 | /// 57 | template opCall(uint derivative = 0) 58 | { 59 | /++ 60 | `(x)` operator. 61 | Complexity: 62 | `O(log(grid.length))` 63 | +/ 64 | auto opCall(X...)(const X xs) scope const @trusted 65 | if (X.length >= 1) 66 | { 67 | import mir.internal.utility: Iota; 68 | import mir.math.common: fmin, fmax; 69 | import std.meta: staticMap; 70 | 71 | static if (derivative) 72 | bool[X.length] extrpolated; 73 | 74 | auto mod(size_t i)() 75 | { 76 | static if (__traits(compiles, gridScopeView!i)) 77 | { 78 | auto grid = gridScopeView!i; 79 | static if (derivative) 80 | extrpolated[i] = grid.length != 0 && (xs[i] < grid[0] || grid[$ - 1] < xs[i]); 81 | return grid.length ? xs[i].fmax(grid[0]).fmin(grid[$ - 1]) : xs[i]; 82 | } 83 | else 84 | { 85 | return xs[i]; 86 | } 87 | } 88 | 89 | alias xm = staticMap!(mod, Iota!(X.length)); 90 | 91 | static if (derivative == 0) 92 | return data(xm); 93 | else 94 | { 95 | static assert (X.length <= 4, "multidimensional constant exrapolation with derivatives isn't implemented"); 96 | auto ret = data.opCall!derivative(xm); 97 | 98 | static if (X.length >= 1) 99 | { 100 | if (extrpolated[0]) 101 | foreach (ref a; ret[1 .. $]) 102 | a = 0; 103 | } 104 | 105 | static if (X.length >= 2) 106 | { 107 | if (extrpolated[1]) 108 | foreach (ref a; ret) 109 | foreach (ref b; a[1 .. $]) 110 | b = 0; 111 | } 112 | 113 | static if (X.length >= 3) 114 | { 115 | if (extrpolated[2]) 116 | foreach (ref a; ret) 117 | foreach (ref b; a) 118 | foreach (ref c; b[1 .. $]) 119 | c = 0; 120 | } 121 | 122 | static if (X.length >= 4) 123 | { 124 | if (extrpolated[2]) 125 | foreach (ref a; ret) 126 | foreach (ref b; a) 127 | foreach (ref c; b) 128 | foreach (ref d; c[1 .. $]) 129 | d = 0; 130 | } 131 | 132 | return ret; 133 | } 134 | } 135 | } 136 | } 137 | 138 | 139 | /// 140 | version(mir_test) 141 | unittest 142 | { 143 | import mir.interpolate.linear; 144 | 145 | auto x = [0.0, 1, 2, 3, 5]; 146 | auto y = [4.0, 0, 9, 23, 40]; 147 | 148 | auto g = [7.0, 10, 15]; 149 | 150 | import mir.ndslice.allocation: rcslice; 151 | 152 | auto linear = linear!double( 153 | x.rcslice!(immutable double), 154 | y.rcslice!(const double), 155 | ).constantExtrapolator; 156 | 157 | assert(linear(2) == 9); 158 | assert(linear(-1) == 4); 159 | assert(linear(100) == 40); 160 | 161 | assert(linear.opCall!1(-1) == [4, 0]); 162 | 163 | } 164 | 165 | /++ 166 | Linear extrapolator. 167 | +/ 168 | LinearExtrapolator!T linearExtrapolator(T)(T interpolator) 169 | { 170 | import core.lifetime: move; 171 | return typeof(return)(interpolator.move); 172 | } 173 | 174 | 175 | /// ditto 176 | struct LinearExtrapolator(T) 177 | if (__traits(hasMember, T, "gridScopeView")) 178 | { 179 | /// 180 | T data; 181 | 182 | /// 183 | this(T data) 184 | { 185 | import core.lifetime: move; 186 | this.data = data.move; 187 | } 188 | 189 | /// 190 | LinearExtrapolator lightConst()() const @property { return *cast(LinearExtrapolator*)&this; } 191 | 192 | /// 193 | auto gridScopeView(size_t dimension = 0)() return scope const @property @trusted 194 | { 195 | return data.gridScopeView!dimension; 196 | } 197 | 198 | /// 199 | enum uint derivativeOrder = 1; 200 | 201 | static if (__traits(compiles, {enum N = T.dimensionCount;})) 202 | /// 203 | enum uint dimensionCount = T.dimensionCount + 1; 204 | 205 | /// 206 | template opCall(uint derivative = 0) 207 | { 208 | /++ 209 | `(x)` operator. 210 | Complexity: 211 | `O(log(grid.length))` 212 | +/ 213 | auto opCall(X...)(const X xs) scope const @trusted 214 | if (X.length >= 1) 215 | { 216 | import mir.internal.utility: Iota; 217 | import mir.math.common: fmin, fmax; 218 | import std.meta: staticMap; 219 | 220 | bool[X.length] extrpolated; 221 | 222 | auto mod(size_t i)() 223 | { 224 | static if (__traits(compiles, gridScopeView!i)) 225 | { 226 | auto grid = gridScopeView!i; 227 | extrpolated[i] = grid.length != 0 && (xs[i] < grid[0] || grid[$ - 1] < xs[i]); 228 | return grid.length ? xs[i].fmax(grid[0]).fmin(grid[$ - 1]) : xs[i]; 229 | } 230 | else 231 | { 232 | return xs[i]; 233 | } 234 | } 235 | 236 | alias xm = staticMap!(mod, Iota!(X.length)); 237 | 238 | import mir.utility: max; 239 | 240 | static assert (X.length <= 2, "multidimensional linear exrapolation with derivatives isn't implemented"); 241 | auto ret = data.opCall!(derivative.max(1u))(xm); 242 | 243 | static if (X.length >= 1) 244 | { 245 | if (extrpolated[0]) 246 | { 247 | ret[0] += ret[1] * (xs[0] - xm[0]); 248 | foreach (ref a; ret[2 .. $]) 249 | a = 0; 250 | } 251 | } 252 | 253 | static if (derivative == 0) 254 | return ret[0]; 255 | else 256 | return ret; 257 | } 258 | } 259 | } 260 | 261 | 262 | /// 263 | version(mir_test) 264 | unittest 265 | { 266 | import mir.test; 267 | import mir.interpolate.linear; 268 | 269 | auto x = [0.0, 1, 2, 3, 5]; 270 | auto y = [4.0, 0, 9, 23, 40]; 271 | 272 | auto g = [7.0, 10, 15]; 273 | 274 | import mir.ndslice.allocation: rcslice; 275 | 276 | auto linear = linear!double( 277 | x.rcslice!(immutable double), 278 | y.rcslice!(const double), 279 | ); 280 | 281 | auto linearLinear = linear.linearExtrapolator; 282 | 283 | linearLinear(2).should == linear(2); 284 | linearLinear(-1).should == linear(-1); 285 | linearLinear.opCall!1(-1).should == [8, -4]; 286 | linearLinear(100).shouldApprox == linear(100); 287 | } 288 | -------------------------------------------------------------------------------- /source/mir/ndslice/traits.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H2 Multidimensional traits) 3 | 4 | This is a submodule of $(MREF mir,ndslice). 5 | 6 | $(BOOKTABLE $(H2 Function), 7 | $(TR $(TH Function Name) $(TH Description)) 8 | 9 | $(T2 isVector, Test if type is a one-dimensional slice.) 10 | $(T2 isMatrix, Test if type is a two-dimensional slice.) 11 | $(T2 isContiguousSlice, Test if type is a contiguous slice.) 12 | $(T2 isCanonicalSlice, Test if type is a canonical slice.) 13 | $(T2 isUniversalSlice, Test if type is a universal slice.) 14 | $(T2 isContiguousVector, Test if type is a contiguous one-dimensional slice.) 15 | $(T2 isUniversalVector, Test if type is a universal one-dimensional slice.) 16 | $(T2 isContiguousMatrix, Test if type is a contiguous two-dimensional slice.) 17 | $(T2 isCanonicalMatrix, Test if type is a canonical two-dimensional slice.) 18 | $(T2 isUniversalMatrix, Test if type is a universal two-dimensional slice.) 19 | $(T2 isIterator, Test if type is a random access iterator.) 20 | ) 21 | 22 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 23 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 24 | Authors: John Hall 25 | 26 | 27 | Macros: 28 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 29 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 30 | +/ 31 | 32 | module mir.ndslice.traits; 33 | 34 | import mir.ndslice.slice : Slice, SliceKind, Contiguous, Universal, Canonical; 35 | 36 | /// Test if type is a one-dimensional slice. 37 | enum bool isVector(T) = is(T : Slice!(Iterator, 1, kind), SliceKind kind, Iterator); 38 | 39 | /// Test if type is a two-dimensional slice. 40 | enum bool isMatrix(T) = is(T : Slice!(Iterator, 2, kind), SliceKind kind, Iterator); 41 | 42 | /// Test if type is a contiguous slice. 43 | enum bool isContiguousSlice(T) = is(T : Slice!(Iterator, N, Contiguous), Iterator, size_t N); 44 | 45 | /// Test if type is a canonical slice. 46 | enum bool isCanonicalSlice(T) = is(T : Slice!(Iterator, N, Canonical), Iterator, size_t N); 47 | 48 | /// Test if type is a universal slice. 49 | enum bool isUniversalSlice(T) = is(T : Slice!(Iterator, N, Universal), Iterator, size_t N); 50 | 51 | /// Test if type is a contiguous one-dimensional slice. 52 | enum bool isContiguousVector(T) = is(T : Slice!(Iterator, 1, Contiguous), Iterator); 53 | 54 | /// Test if type is a universal one-dimensional slice. 55 | enum bool isUniversalVector(T) = is(T : Slice!(Iterator, 1, Universal), Iterator); 56 | 57 | /// Test if type is a contiguous two-dimensional slice. 58 | enum bool isContiguousMatrix(T) = is(T : Slice!(Iterator, 2, Contiguous), Iterator); 59 | 60 | /// Test if type is a canonical two-dimensional slice. 61 | enum bool isCanonicalMatrix(T) = is(T : Slice!(Iterator, 2, Canonical), Iterator); 62 | 63 | /// Test if type is a universal two-dimensional slice. 64 | enum bool isUniversalMatrix(T) = is(T : Slice!(Iterator, 2, Universal), Iterator); 65 | 66 | /// 67 | @safe pure nothrow @nogc 68 | version(mir_ndslice_test) unittest 69 | { 70 | import mir.ndslice.slice : Slice; 71 | 72 | alias S1 = Slice!(int*); 73 | static assert(isContiguousVector!S1); 74 | static assert(!isUniversalVector!S1); 75 | 76 | static assert(!isContiguousMatrix!S1); 77 | static assert(!isCanonicalMatrix!S1); 78 | static assert(!isUniversalMatrix!S1); 79 | 80 | static assert(isVector!S1); 81 | static assert(!isMatrix!S1); 82 | 83 | static assert(isContiguousSlice!S1); 84 | static assert(!isCanonicalSlice!S1); 85 | static assert(!isUniversalSlice!S1); 86 | } 87 | 88 | @safe pure nothrow @nogc 89 | version(mir_ndslice_test) unittest 90 | { 91 | alias S2 = Slice!(float*, 1, Universal); 92 | static assert(!isContiguousVector!S2); 93 | static assert(isUniversalVector!S2); 94 | 95 | static assert(!isContiguousMatrix!S2); 96 | static assert(!isCanonicalMatrix!S2); 97 | static assert(!isUniversalMatrix!S2); 98 | 99 | static assert(isVector!S2); 100 | static assert(!isMatrix!S2); 101 | 102 | static assert(!isContiguousSlice!S2); 103 | static assert(!isCanonicalSlice!S2); 104 | static assert(isUniversalSlice!S2); 105 | } 106 | 107 | @safe pure nothrow @nogc 108 | version(mir_ndslice_test) unittest 109 | { 110 | alias S3 = Slice!(byte*, 2); 111 | static assert(!isContiguousVector!S3); 112 | static assert(!isUniversalVector!S3); 113 | 114 | static assert(isContiguousMatrix!S3); 115 | static assert(!isCanonicalMatrix!S3); 116 | static assert(!isUniversalMatrix!S3); 117 | 118 | static assert(!isVector!S3); 119 | static assert(isMatrix!S3); 120 | 121 | static assert(isContiguousSlice!S3); 122 | static assert(!isCanonicalSlice!S3); 123 | static assert(!isUniversalSlice!S3); 124 | } 125 | 126 | @safe pure nothrow @nogc 127 | version(mir_ndslice_test) unittest 128 | { 129 | alias S4 = Slice!(int*, 2, Canonical); 130 | static assert(!isContiguousVector!S4); 131 | static assert(!isUniversalVector!S4); 132 | 133 | static assert(!isContiguousMatrix!S4); 134 | static assert(isCanonicalMatrix!S4); 135 | static assert(!isUniversalMatrix!S4); 136 | 137 | static assert(!isVector!S4); 138 | static assert(isMatrix!S4); 139 | 140 | static assert(!isContiguousSlice!S4); 141 | static assert(isCanonicalSlice!S4); 142 | static assert(!isUniversalSlice!S4); 143 | } 144 | 145 | @safe pure nothrow @nogc 146 | version(mir_ndslice_test) unittest 147 | { 148 | alias S5 = Slice!(int*, 2, Universal); 149 | static assert(!isContiguousVector!S5); 150 | static assert(!isUniversalVector!S5); 151 | 152 | static assert(!isContiguousMatrix!S5); 153 | static assert(!isCanonicalMatrix!S5); 154 | static assert(isUniversalMatrix!S5); 155 | 156 | static assert(!isVector!S5); 157 | static assert(isMatrix!S5); 158 | 159 | static assert(!isContiguousSlice!S5); 160 | static assert(!isCanonicalSlice!S5); 161 | static assert(isUniversalSlice!S5); 162 | } 163 | 164 | @safe pure nothrow @nogc 165 | version(mir_ndslice_test) unittest 166 | { 167 | alias S6 = Slice!(int*, 3); 168 | 169 | static assert(!isContiguousVector!S6); 170 | static assert(!isUniversalVector!S6); 171 | 172 | static assert(!isContiguousMatrix!S6); 173 | static assert(!isCanonicalMatrix!S6); 174 | static assert(!isUniversalMatrix!S6); 175 | 176 | static assert(!isVector!S6); 177 | static assert(!isMatrix!S6); 178 | 179 | static assert(isContiguousSlice!S6); 180 | static assert(!isCanonicalSlice!S6); 181 | static assert(!isUniversalSlice!S6); 182 | } 183 | 184 | @safe pure nothrow @nogc 185 | version(mir_ndslice_test) unittest 186 | { 187 | alias S7 = Slice!(int*, 3, Canonical); 188 | 189 | static assert(!isContiguousVector!S7); 190 | static assert(!isUniversalVector!S7); 191 | 192 | static assert(!isContiguousMatrix!S7); 193 | static assert(!isCanonicalMatrix!S7); 194 | static assert(!isUniversalMatrix!S7); 195 | 196 | static assert(!isVector!S7); 197 | static assert(!isMatrix!S7); 198 | 199 | static assert(!isContiguousSlice!S7); 200 | static assert(isCanonicalSlice!S7); 201 | static assert(!isUniversalSlice!S7); 202 | } 203 | 204 | @safe pure nothrow @nogc 205 | version(mir_ndslice_test) unittest 206 | { 207 | alias S8 = Slice!(int*, 3, Universal); 208 | 209 | static assert(!isContiguousVector!S8); 210 | static assert(!isUniversalVector!S8); 211 | 212 | static assert(!isContiguousMatrix!S8); 213 | static assert(!isCanonicalMatrix!S8); 214 | static assert(!isUniversalMatrix!S8); 215 | 216 | static assert(!isVector!S8); 217 | static assert(!isMatrix!S8); 218 | 219 | static assert(!isContiguousSlice!S8); 220 | static assert(!isCanonicalSlice!S8); 221 | static assert(isUniversalSlice!S8); 222 | } 223 | 224 | /// 225 | template isIterator(T) 226 | { 227 | enum isIterator = __traits(compiles, (T a, T b) 228 | { 229 | sizediff_t diff = a - b; 230 | ++a; 231 | ++b; 232 | --a; 233 | --b; 234 | void foo(V)(auto ref V v) 235 | { 236 | 237 | } 238 | foo(a[sizediff_t(3)]); 239 | auto c = a + sizediff_t(3); 240 | auto d = a - sizediff_t(3); 241 | a += sizediff_t(3); 242 | a -= sizediff_t(3); 243 | foo(*a); 244 | }); 245 | } 246 | -------------------------------------------------------------------------------- /source/mir/internal/ldc_simd.d: -------------------------------------------------------------------------------- 1 | // module ldc.simd; 2 | // compilers has permanent issues with .di files 3 | module mir.internal.ldc_simd; 4 | 5 | version(LDC): 6 | 7 | import core.simd; 8 | import ldc.llvmasm; 9 | 10 | pure: 11 | nothrow: 12 | @nogc: 13 | @trusted: 14 | 15 | private template isFloatingPoint(T) 16 | { 17 | enum isFloatingPoint = 18 | is(T == float) || 19 | is(T == double) || 20 | is(T == real); 21 | } 22 | 23 | private template isIntegral(T) 24 | { 25 | enum isIntegral = 26 | is(T == byte) || 27 | is(T == ubyte) || 28 | is(T == short) || 29 | is(T == ushort) || 30 | is(T == int) || 31 | is(T == uint) || 32 | is(T == long) || 33 | is(T == ulong); 34 | } 35 | 36 | private template isSigned(T) 37 | { 38 | enum isSigned = 39 | is(T == byte) || 40 | is(T == short) || 41 | is(T == int) || 42 | is(T == long); 43 | } 44 | 45 | private template IntOf(T) 46 | if(isIntegral!T || isFloatingPoint!T) 47 | { 48 | enum n = T.sizeof; 49 | static if(n == 1) 50 | alias byte IntOf; 51 | else static if(n == 2) 52 | alias short IntOf; 53 | else static if(n == 4) 54 | alias int IntOf; 55 | else static if(n == 8) 56 | alias long IntOf; 57 | else 58 | static assert(0, "Type not supported"); 59 | } 60 | 61 | private template BaseType(V) 62 | { 63 | alias typeof(V.array[0]) BaseType; 64 | } 65 | 66 | private template numElements(V) 67 | { 68 | enum numElements = V.sizeof / BaseType!(V).sizeof; 69 | } 70 | 71 | private template llvmType(T) 72 | { 73 | static if(is(T == float)) 74 | enum llvmType = "float"; 75 | else static if(is(T == double)) 76 | enum llvmType = "double"; 77 | else static if(is(T == byte) || is(T == ubyte) || is(T == void)) 78 | enum llvmType = "i8"; 79 | else static if(is(T == short) || is(T == ushort)) 80 | enum llvmType = "i16"; 81 | else static if(is(T == int) || is(T == uint)) 82 | enum llvmType = "i32"; 83 | else static if(is(T == long) || is(T == ulong)) 84 | enum llvmType = "i64"; 85 | else 86 | static assert(0, 87 | "Can't determine llvm type for D type " ~ T.stringof); 88 | } 89 | 90 | private template llvmVecType(V) 91 | { 92 | static if(is(V == void16)) 93 | enum llvmVecType = "<16 x i8>"; 94 | else static if(is(V == void32)) 95 | enum llvmVecType = "<32 x i8>"; 96 | else 97 | { 98 | alias BaseType!V T; 99 | enum int n = numElements!V; 100 | enum llvmT = llvmType!T; 101 | enum llvmVecType = "<"~n.stringof~" x "~llvmT~">"; 102 | } 103 | } 104 | 105 | /** 106 | This template provides access to 107 | $(LINK2 http://llvm.org/docs/LangRef.html#i_shufflevector, 108 | LLVM's shufflevector instruction). 109 | 110 | Example: 111 | --- 112 | int4 a = [0, 10, 20, 30]; 113 | int4 b = [40, 50, 60, 70]; 114 | int4 c = shufflevector!(int4, 0, 2, 4, 6)(a, b); 115 | assert(c.array == [0, 20, 40, 60]); 116 | --- 117 | */ 118 | 119 | template shufflevector(V, mask...) 120 | if(is(typeof(llvmVecType!V)) && mask.length == numElements!V) 121 | { 122 | enum int n = mask.length; 123 | enum llvmV = llvmVecType!V; 124 | 125 | template genMaskIr(string ir, m...) 126 | { 127 | static if(m.length == 0) 128 | enum genMaskIr = ir; 129 | else 130 | { 131 | enum int mfront = m[0]; 132 | 133 | enum genMaskIr = 134 | genMaskIr!(ir ~ ", i32 " ~ mfront.stringof, m[1 .. $]); 135 | } 136 | } 137 | enum maskIr = genMaskIr!("", mask)[2 .. $]; 138 | enum ir = ` 139 | %r = shufflevector `~llvmV~` %0, `~llvmV~` %1, <`~n.stringof~` x i32> <`~maskIr~`> 140 | ret `~llvmV~` %r`; 141 | 142 | alias __ir_pure!(ir, V, V, V) shufflevector; 143 | } 144 | 145 | /** 146 | This template provides access to 147 | $(LINK2 http://llvm.org/docs/LangRef.html#i_extractelement, 148 | LLVM's extractelement instruction). 149 | 150 | Example: 151 | --- 152 | int4 a = [0, 10, 20, 30]; 153 | int k = extractelement!(int4, 2)(a); 154 | assert(k == 20); 155 | --- 156 | */ 157 | 158 | template extractelement(V, int i) 159 | if(is(typeof(llvmVecType!V)) && i < numElements!V) 160 | { 161 | enum llvmT = llvmType!(BaseType!V); 162 | enum llvmV = llvmVecType!V; 163 | enum ir = ` 164 | %r = extractelement `~llvmV~` %0, i32 `~i.stringof~` 165 | ret `~llvmT~` %r`; 166 | 167 | alias __ir_pure!(ir, BaseType!V, V) extractelement; 168 | } 169 | 170 | /** 171 | This template provides access to 172 | $(LINK2 http://llvm.org/docs/LangRef.html#i_insertelement, 173 | LLVM's insertelement instruction). 174 | 175 | Example: 176 | --- 177 | int4 a = [0, 10, 20, 30]; 178 | int b = insertelement!(int4, 2)(a, 50); 179 | assert(b.array == [0, 10, 50, 30]); 180 | --- 181 | */ 182 | 183 | template insertelement(V, int i) 184 | if(is(typeof(llvmVecType!V)) && i < numElements!V) 185 | { 186 | enum llvmT = llvmType!(BaseType!V); 187 | enum llvmV = llvmVecType!V; 188 | enum ir = ` 189 | %r = insertelement `~llvmV~` %0, `~llvmT~` %1, i32 `~i.stringof~` 190 | ret `~llvmV~` %r`; 191 | 192 | alias __ir_pure!(ir, V, V, BaseType!V) insertelement; 193 | } 194 | 195 | /** 196 | loadUnaligned: Loads a vector from an unaligned pointer. 197 | Example: 198 | --- 199 | int[4] a = [0, 10, 20, 30]; 200 | int4 v = loadUnaligned!int4(a.ptr); 201 | assert(v.array == a); 202 | --- 203 | */ 204 | template loadUnaligned(V) 205 | if(is(typeof(llvmVecType!V))) 206 | { 207 | enum llvmElementType = llvmType!(BaseType!V); 208 | enum llvmV = llvmVecType!V; 209 | enum ir = ` 210 | %p = bitcast `~llvmElementType~`* %0 to `~llvmV~`* 211 | %r = load `~llvmV~`, `~llvmV~`* %p, align 1 212 | ret `~llvmV~` %r`; 213 | private alias impl = __ir_pure!(ir, V, const(BaseType!V)*); 214 | 215 | pragma(inline, true): 216 | 217 | V loadUnaligned(const(BaseType!V)* p) 218 | { 219 | return impl(p); 220 | } 221 | } 222 | 223 | /** 224 | storeUnaligned: Stores a vector to an unaligned pointer. 225 | Example: 226 | --- 227 | int[4] a; 228 | int4 v = [0, 10, 20, 30]; 229 | storeUnaligned!int4(v, a.ptr); 230 | assert(v.array == a); 231 | --- 232 | */ 233 | template storeUnaligned(V) 234 | if(is(typeof(llvmVecType!V))) 235 | { 236 | enum llvmElementType = llvmType!(BaseType!V); 237 | enum llvmV = llvmVecType!V; 238 | enum ir = ` 239 | %p = bitcast `~llvmElementType~`* %1 to `~llvmV~`* 240 | store `~llvmV~` %0, `~llvmV~`* %p, align 1`; 241 | private alias impl = __ir_pure!(ir, void, V, BaseType!V*); 242 | 243 | pragma(inline, true): 244 | 245 | void storeUnaligned(V value, BaseType!V* p) 246 | { 247 | impl(value, p); 248 | } 249 | } 250 | 251 | private enum Cond{ eq, ne, gt, ge } 252 | 253 | private template cmpMask(Cond cond) 254 | { 255 | template cmpMask(V) 256 | if(is(IntOf!(BaseType!V))) 257 | { 258 | alias BaseType!V T; 259 | enum llvmT = llvmType!T; 260 | 261 | alias IntOf!T Relem; 262 | 263 | enum int n = numElements!V; 264 | alias __vector(Relem[n]) R; 265 | 266 | enum llvmV = llvmVecType!V; 267 | enum llvmR = llvmVecType!R; 268 | enum sign = 269 | (cond == Cond.eq || cond == Cond.ne) ? "" : 270 | isSigned!T ? "s" : "u"; 271 | enum condStr = 272 | cond == Cond.eq ? "eq" : 273 | cond == Cond.ne ? "ne" : 274 | cond == Cond.ge ? "ge" : "gt"; 275 | enum op = 276 | isFloatingPoint!T ? "fcmp o"~condStr : "icmp "~sign~condStr; 277 | 278 | enum ir = ` 279 | %cmp = `~op~` `~llvmV~` %0, %1 280 | %r = sext <`~n.stringof~` x i1> %cmp to `~llvmR~` 281 | ret `~llvmR~` %r`; 282 | 283 | alias __ir_pure!(ir, R, V, V) cmpMask; 284 | } 285 | } 286 | 287 | /** 288 | equalMask, notEqualMask, greaterMask and greaterOrEqualMask perform an 289 | element-wise comparison between two vectors and return a vector with 290 | signed integral elements. The number of elements in the returned vector 291 | and their size is the same as in parameter vectors. If the condition in 292 | the name of the function holds for elements of the parameter vectors at 293 | a given index, all bits of the element of the result at that index are 294 | set to 1, otherwise the element of the result is zero. 295 | 296 | Example: 297 | --- 298 | float4 a = [1, 3, 5, 7]; 299 | float4 b = [2, 3, 4, 5]; 300 | int4 c = greaterMask!float4(a, b); 301 | writeln(c.array); 302 | assert(c.array == [0, 0, 0xffff_ffff, 0xffff_ffff]); 303 | --- 304 | */ 305 | alias cmpMask!(Cond.eq) equalMask; 306 | alias cmpMask!(Cond.ne) notEqualMask; /// Ditto 307 | alias cmpMask!(Cond.gt) greaterMask; /// Ditto 308 | alias cmpMask!(Cond.ge) greaterOrEqualMask; /// Ditto 309 | -------------------------------------------------------------------------------- /source/mir/stdio.d: -------------------------------------------------------------------------------- 1 | /++ 2 | A simple I/O routines around ``. 3 | 4 | The implementation is CTFE-friendly. 5 | +/ 6 | module mir.stdio; 7 | 8 | static import core.stdc.stdio; 9 | 10 | import mir.exception: toMutable; 11 | 12 | /// Writes values in a text form 13 | void writeln(string separator = "", Args...)(auto ref const Args args) 14 | if (Args.length > 0) 15 | { 16 | dout.write!separator(args); 17 | dout << endl; 18 | } 19 | 20 | /// ditto 21 | void write(string separator = "", Args...)(auto ref const Args args) 22 | if (Args.length > 0) 23 | { 24 | dout.write!separator(args); 25 | } 26 | 27 | /// Writes values in a text form using nothrow $(LREF tout) 28 | void dump(string separator = " ", Args...)(auto ref const Args args) 29 | if (Args.length > 0) 30 | { 31 | tout.write!separator(args); 32 | tout << endl; 33 | } 34 | 35 | /// Standart output 36 | File dout()() @trusted nothrow @nogc @property 37 | { 38 | version(LDC) 39 | pragma(inline, true); 40 | return File(__ctfe ? null : core.stdc.stdio.stdout); 41 | } 42 | 43 | /// ditto 44 | File derr()() @trusted nothrow @nogc @property 45 | { 46 | version(LDC) 47 | pragma(inline, true); 48 | return File(__ctfe ? null : core.stdc.stdio.stderr); 49 | } 50 | 51 | /// 52 | version(mir_test) 53 | @safe @nogc 54 | unittest 55 | { 56 | dout << "mir.stdio.dout test! - @nogc I/O" << endl; 57 | derr << "mir.stdio.derr test! - @nogc I/O" << endl; 58 | } 59 | 60 | /++ 61 | Nothrow standart output to use in pair with `debug` expression in nothrow 62 | and pure code for testing purpose. 63 | 64 | See_also: $(LREF AssumeNothrowFile) 65 | +/ 66 | AssumeNothrowFile tout()() @trusted nothrow @nogc @property 67 | { 68 | version(LDC) 69 | pragma(inline, true); 70 | return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stdout); 71 | } 72 | 73 | /// ditto 74 | AssumeNothrowFile terr()() @trusted nothrow @nogc @property 75 | { 76 | version(LDC) 77 | pragma(inline, true); 78 | return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stderr); 79 | } 80 | 81 | /// 82 | version(mir_test) 83 | pure @safe @nogc nothrow 84 | unittest 85 | { 86 | debug tout << "mir.stdio.tout test! - @nogc nothrow I/O" << endl; 87 | debug terr << "mir.stdio.terr test! - @nogc nothrow I/O" << endl; 88 | } 89 | 90 | /++ 91 | When used as `file << endl` it adds new line flushes the stream. 92 | +/ 93 | enum NewLine 94 | { 95 | lf = "\n", 96 | lf_cf = "\r\n", 97 | } 98 | 99 | /// ditto 100 | enum endl = NewLine.lf; 101 | 102 | /++ 103 | +/ 104 | struct File 105 | { 106 | /// 107 | core.stdc.stdio.FILE* fp; 108 | 109 | mixin FileMembers; 110 | 111 | @trusted @nogc: 112 | 113 | /++ 114 | Throws: $(LREF FileException) 115 | +/ 116 | void rawWrite(scope const(void)[] data) scope 117 | in (__ctfe || fp !is null) 118 | { 119 | if (__ctfe) 120 | return; 121 | core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp); 122 | if (core.stdc.stdio.ferror(fp)) 123 | throw writeException.toMutable; 124 | } 125 | 126 | /++ 127 | Throws: $(LREF FileException) 128 | +/ 129 | void flush() scope 130 | in (__ctfe || fp !is null) 131 | { 132 | if (__ctfe) 133 | return; 134 | core.stdc.stdio.fflush(fp); 135 | if (core.stdc.stdio.ferror(fp)) 136 | throw writeException.toMutable; 137 | } 138 | } 139 | 140 | /++ 141 | Nothrow File implementation for testing purposes. 142 | See_also: $(LREF tout), $(LREF terr) 143 | +/ 144 | struct AssumeNothrowFile 145 | { 146 | /// 147 | core.stdc.stdio.FILE* fp; 148 | 149 | mixin FileMembers; 150 | 151 | @trusted @nogc nothrow: 152 | 153 | /++ 154 | Throws: $(LREF FileError) 155 | +/ 156 | void rawWrite(scope const(void)[] data) scope 157 | in (__ctfe || fp !is null) 158 | { 159 | if (__ctfe) 160 | return; 161 | core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp); 162 | if (core.stdc.stdio.ferror(fp)) 163 | throw writeError.toMutable; 164 | } 165 | 166 | /++ 167 | Throws: $(LREF FileError) 168 | +/ 169 | void flush() scope 170 | in (__ctfe || fp !is null) 171 | { 172 | if (__ctfe) 173 | return; 174 | core.stdc.stdio.fflush(fp); 175 | if (core.stdc.stdio.ferror(fp)) 176 | throw writeError.toMutable; 177 | } 178 | } 179 | 180 | /// 181 | mixin template FileMembers() 182 | { 183 | /// 184 | void put(C)(const C data) scope 185 | if (is(C == char) || is(C == wchar) | is(C == dchar)) 186 | { 187 | C[1] array = [data]; 188 | this.rawWrite(array); 189 | } 190 | 191 | /// 192 | void put(C)(scope const(C)[] data) scope 193 | if (is(C == char) || is(C == wchar) | is(C == dchar)) 194 | { 195 | this.rawWrite(data); 196 | } 197 | 198 | /// 199 | template opBinary(string op : "<<") 200 | { 201 | /// 202 | ref opBinary(T)(auto ref const T value) scope return 203 | { 204 | if (__ctfe) 205 | return this; 206 | import mir.format: print; 207 | print!char(this, value); 208 | return this; 209 | } 210 | 211 | /// Prints new line and flushes the stream 212 | ref opBinary(NewLine endl) scope return 213 | { 214 | if (__ctfe) 215 | return this; 216 | this.put(endl); 217 | this.flush; 218 | return this; 219 | } 220 | } 221 | 222 | /// Writes values in a text form 223 | void writeln(string separator = "", Args...)(auto ref const Args args) scope 224 | if (Args.length > 0) 225 | { 226 | write(args); 227 | this << endl; 228 | } 229 | 230 | /// ditto 231 | void write(string separator = "", Args...)(auto ref const Args args) scope 232 | if (Args.length > 0) 233 | { 234 | pragma(inline, false); 235 | if (__ctfe) 236 | return; 237 | import mir.format: print, printStaticString; 238 | foreach (i, ref arg; args) 239 | { 240 | print!char(this, arg); 241 | static if (separator.length && i + 1 < args.length) 242 | { 243 | printStaticString!char(this, separator); 244 | } 245 | } 246 | } 247 | } 248 | 249 | /++ 250 | File Exception 251 | +/ 252 | class FileException : Exception 253 | { 254 | /// 255 | this( 256 | string msg, 257 | string file = __FILE__, 258 | size_t line = __LINE__, 259 | Throwable next = null) pure nothrow @nogc @safe 260 | { 261 | super(msg, file, line, next); 262 | } 263 | 264 | /// 265 | this( 266 | string msg, 267 | Throwable next, 268 | string file = __FILE__, 269 | size_t line = __LINE__, 270 | ) pure nothrow @nogc @safe 271 | { 272 | this(msg, file, line, next); 273 | } 274 | 275 | FileException toMutable() @trusted pure nothrow @nogc const 276 | { 277 | return cast() this; 278 | } 279 | 280 | alias toMutable this; 281 | } 282 | 283 | /++ 284 | File Error 285 | +/ 286 | class FileError : Error 287 | { 288 | /// 289 | this( 290 | string msg, 291 | string file = __FILE__, 292 | size_t line = __LINE__, 293 | Throwable next = null) pure nothrow @nogc @safe 294 | { 295 | super(msg, file, line, next); 296 | } 297 | 298 | /// 299 | this( 300 | string msg, 301 | Throwable next, 302 | string file = __FILE__, 303 | size_t line = __LINE__, 304 | ) pure nothrow @nogc @safe 305 | { 306 | this(msg, file, line, next); 307 | } 308 | 309 | FileError toMutable() @trusted pure nothrow @nogc const 310 | { 311 | return cast() this; 312 | } 313 | 314 | alias toMutable this; 315 | } 316 | 317 | private static immutable writeException = new FileException("Error on file write"); 318 | private static immutable flushException = new FileException("Error on file flush"); 319 | private static immutable writeError = new FileError("Error on file write"); 320 | private static immutable flushError = new FileError("Error on file flush"); 321 | 322 | version(mir_test) 323 | @safe unittest 324 | { 325 | static struct ParameterSpec 326 | { 327 | string name; 328 | string type; 329 | string default_; 330 | } 331 | 332 | static struct FunctionOverloadSpec 333 | { 334 | ParameterSpec[] parameters; 335 | string doc; 336 | string test; 337 | size_t minArgs; 338 | } 339 | 340 | import mir.algebraic; 341 | import mir.stdio; 342 | if (false) 343 | Algebraic!(FunctionOverloadSpec, ParameterSpec).init.writeln; // error 344 | } 345 | -------------------------------------------------------------------------------- /source/mir/annotated.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Annotated value) 3 | 4 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 | Authors: Ilia Ki 6 | Macros: 7 | +/ 8 | module mir.annotated; 9 | 10 | import mir.internal.meta: basicElementType; 11 | import mir.serde: serdeRegister, serdeAnnotation, serdeIsDynamicAlgebraic; 12 | 13 | static immutable excMsg = "At least one annotation is required to create an annotated value."; 14 | version (D_Exceptions) 15 | static immutable exc = new Exception(excMsg); 16 | 17 | /++ 18 | A convenience definition of an annotated value. 19 | 20 | A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member. 21 | +/ 22 | @serdeRegister 23 | @serdeAnnotation 24 | struct Annotated(T) { 25 | /// 26 | @serdeAnnotation 27 | string[] annotations; 28 | 29 | static if (!(is(T == union) || is(T == struct))) 30 | private enum _alloc_ = false; 31 | else 32 | static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic")) 33 | private enum _alloc_ = true; 34 | else 35 | { 36 | import mir.algebraic: isVariant; 37 | static if (isVariant!T) 38 | private enum _alloc_ = true; 39 | else 40 | private enum _alloc_ = false; 41 | } 42 | 43 | static if (_alloc_) 44 | { 45 | /// 46 | private T* _value; 47 | /// 48 | ref inout(T) value() inout @property 49 | in(_value) 50 | { 51 | return *_value; 52 | } 53 | 54 | /// 55 | ref T value(T value) @property return scope 56 | { 57 | if (_value is null) 58 | { 59 | _value = new T; 60 | import core.lifetime: move; 61 | *_value = move(value); 62 | } 63 | return *_value; 64 | } 65 | 66 | /// 67 | bool opEquals(scope const Annotated rhs) scope const 68 | { 69 | return annotations == rhs.annotations && value == rhs.value; 70 | } 71 | 72 | size_t toHash() scope @trusted const pure nothrow @nogc 73 | { 74 | static if (__traits(compiles, hashOf(value))) 75 | return hashOf(value); 76 | else 77 | { 78 | debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof); 79 | return cast(size_t)_value; 80 | } 81 | } 82 | } 83 | else 84 | { 85 | /// 86 | T value; 87 | } 88 | 89 | 90 | /++ 91 | Params: 92 | annotations = non-empty array of annotations 93 | args = arguments to construct value with 94 | +/ 95 | this(Args...)(string[] annotations, Args args) @safe pure { 96 | if (annotations.length == 0) 97 | { 98 | version (D_Exceptions) 99 | { import mir.exception : toMutable; throw exc.toMutable; } 100 | else 101 | assert(0, excMsg); 102 | } 103 | import core.lifetime: forward; 104 | this.annotations = annotations; 105 | static if (_alloc_) 106 | this._value = new T(forward!args); 107 | else 108 | static if (__traits(compiles, value = args)) 109 | this.value = args; 110 | else 111 | static if (is(T == class)) 112 | this.value = new T(forward!args); 113 | else 114 | this.value = T(forward!args); 115 | } 116 | 117 | // private alias E = .basicElementType!T; 118 | 119 | import std.traits: isAssociativeArray, isAggregateType; 120 | /// 121 | int opCmp()(scope const typeof(this) rhs) scope const pure nothrow @nogc @system 122 | // if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp"))) 123 | { 124 | if (auto d = __cmp(annotations, rhs.annotations)) 125 | return d; 126 | 127 | static if (__traits(compiles, __cmp(value, rhs.value))) 128 | return __cmp(value, rhs.value); 129 | else 130 | static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U)) 131 | return value.opCmp(rhs.value); 132 | else 133 | return value < rhs.value ? -1 : value > rhs.value ? +1 : 0; 134 | } 135 | } 136 | 137 | /// 138 | version(mir_test) 139 | unittest 140 | { 141 | auto annotations = ["annotation"]; 142 | static struct S {double x;} 143 | auto as = Annotated!S(annotations, 5); 144 | assert(as.annotations == annotations); 145 | assert(as.value.x == 5); 146 | 147 | static struct C {double x;} 148 | auto ac = Annotated!S(annotations, 5); 149 | assert(ac.annotations == annotations); 150 | assert(ac.value.x == 5); 151 | } 152 | 153 | /// 154 | version(mir_test) 155 | unittest 156 | { 157 | import mir.algebraic; 158 | auto annotations = ["annotation"]; 159 | static struct S {double x;} 160 | auto as = Annotated!(Variant!S)(annotations, 5); 161 | assert(as.annotations == annotations); 162 | assert(as.value.x == 5); 163 | 164 | static struct C {double x;} 165 | auto ac = Annotated!(Variant!S)(annotations, 5); 166 | assert(ac.annotations == annotations); 167 | assert(ac.value.x == 5); 168 | } 169 | 170 | /++ 171 | A convenience definition of an annotated value. 172 | 173 | A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member. 174 | +/ 175 | @serdeRegister 176 | @serdeAnnotation 177 | struct AnnotatedOnce(T) { 178 | /// 179 | @serdeAnnotation 180 | string annotation; 181 | 182 | static if (!(is(T == union) || is(T == struct))) 183 | private enum _alloc_ = false; 184 | else 185 | static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic")) 186 | private enum _alloc_ = true; 187 | else 188 | { 189 | import mir.algebraic: isVariant; 190 | static if (isVariant!T) 191 | private enum _alloc_ = true; 192 | else 193 | private enum _alloc_ = false; 194 | } 195 | 196 | static if (_alloc_) 197 | { 198 | /// 199 | private T* _value; 200 | /// 201 | ref inout(T) value() inout @property 202 | { 203 | return *_value; 204 | } 205 | 206 | /// 207 | ref T value(T value) @property 208 | { 209 | if (_value is null) 210 | { 211 | _value = new T; 212 | import core.lifetime: move; 213 | *_value = move(value); 214 | } 215 | return *_value; 216 | } 217 | 218 | /// 219 | bool opEquals(scope const AnnotatedOnce rhs) scope const 220 | { 221 | return annotation == rhs.annotation && value == rhs.value; 222 | } 223 | } 224 | else 225 | { 226 | /// 227 | T value; 228 | } 229 | 230 | 231 | /++ 232 | Params: 233 | annotation = non-empty array of annotation 234 | args = arguments to construct value with 235 | +/ 236 | this(Args...)(string annotation, Args args) @safe pure { 237 | import core.lifetime: forward; 238 | this.annotation = annotation; 239 | static if (_alloc_) 240 | this._value = new T(forward!args); 241 | else 242 | static if (__traits(compiles, value = args)) 243 | this.value = args; 244 | else 245 | static if (is(T == class)) 246 | this.value = new T(forward!args); 247 | else 248 | this.value = T(forward!args); 249 | } 250 | 251 | // private alias E = .basicElementType!T; 252 | 253 | import std.traits: isAssociativeArray, isAggregateType; 254 | // static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp"))) 255 | /// 256 | int opCmp()(scope const typeof(this) rhs) scope const @system pure nothrow @nogc 257 | { 258 | if (auto d = __cmp(annotation, rhs.annotation)) 259 | return d; 260 | 261 | static if (__traits(compiles, __cmp(value, rhs.value))) 262 | return __cmp(value, rhs.value); 263 | else 264 | static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U)) 265 | return value.opCmp(rhs.value); 266 | else 267 | return value < rhs.value ? -1 : value > rhs.value ? +1 : 0; 268 | } 269 | } 270 | 271 | /// 272 | version(mir_test) 273 | unittest 274 | { 275 | auto annotation = "annotation"; 276 | static struct S {double x;} 277 | auto as = AnnotatedOnce!S(annotation, 5); 278 | assert(as.annotation == annotation); 279 | assert(as.value.x == 5); 280 | 281 | static struct C {double x;} 282 | auto ac = AnnotatedOnce!S(annotation, 5); 283 | assert(ac.annotation == annotation); 284 | assert(ac.value.x == 5); 285 | } 286 | 287 | /// 288 | version(mir_test) 289 | unittest 290 | { 291 | import mir.algebraic; 292 | auto annotation = "annotation"; 293 | static struct S {double x;} 294 | auto as = AnnotatedOnce!(Variant!S)(annotation, 5); 295 | assert(as.annotation == annotation); 296 | assert(as.value.x == 5); 297 | 298 | static struct C {double x;} 299 | auto ac = AnnotatedOnce!(Variant!S)(annotation, 5); 300 | assert(ac.annotation == annotation); 301 | assert(ac.value.x == 5); 302 | } 303 | -------------------------------------------------------------------------------- /source/mir/rc/slim_ptr.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H1 Thread-safe slim reference-counted shared pointers). 3 | 4 | This implementation does not support polymorphism. 5 | +/ 6 | module mir.rc.slim_ptr; 7 | 8 | import mir.rc.context; 9 | import mir.type_info; 10 | import std.traits; 11 | 12 | package static immutable allocationExcMsg = "mir_slim_rcptr: out of memory error."; 13 | package static immutable getExcMsg = "mir_slim_rcptr: trying to use null value."; 14 | 15 | version (D_Exceptions) 16 | { 17 | import core.exception: OutOfMemoryError, InvalidMemoryOperationError; 18 | package static immutable allocationError = new OutOfMemoryError(allocationExcMsg); 19 | } 20 | 21 | /++ 22 | Thread safe reference counting array. 23 | 24 | This implementation does not support polymorphism. 25 | 26 | The implementation never adds roots into the GC. 27 | +/ 28 | struct mir_slim_rcptr(T) 29 | { 30 | static if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union)) 31 | static assert(!__traits(isNested, T), "mir_slim_rcptr does not support nested types."); 32 | 33 | /// 34 | static if (is(T == class) || is(T == interface)) 35 | package Unqual!T _value; 36 | else 37 | package T* _value; 38 | 39 | package ref mir_rc_context context() inout return scope pure nothrow @nogc @trusted @property 40 | { 41 | assert(_value); 42 | return (cast(mir_rc_context*)_value)[-1]; 43 | } 44 | 45 | package void _reset() 46 | { 47 | _value = null; 48 | } 49 | 50 | inout(void)* _thisPtr() inout return scope @trusted @property 51 | { 52 | return cast(inout(void)*) _value; 53 | } 54 | 55 | package alias ThisTemplate = .mir_slim_rcptr; 56 | 57 | /// ditto 58 | alias opUnary(string op : "*") = _get_value; 59 | /// ditto 60 | alias _get_value this; 61 | 62 | static if (is(T == class) || is(T == interface)) 63 | /// 64 | pragma(inline, true) 65 | inout(T) _get_value() return scope inout @property 66 | { 67 | assert(this, getExcMsg); 68 | return _value; 69 | } 70 | else 71 | /// 72 | pragma(inline, true) 73 | ref inout(T) _get_value() return scope inout @property 74 | { 75 | assert(this, getExcMsg); 76 | return *_value; 77 | } 78 | 79 | /// 80 | void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe 81 | { 82 | auto t = this._value; 83 | this._value = rhs._value; 84 | rhs._value = t; 85 | } 86 | 87 | /// 88 | this(typeof(null)) 89 | { 90 | } 91 | 92 | /// 93 | mixin CommonRCImpl; 94 | 95 | 96 | /// 97 | pragma(inline, true) 98 | bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc 99 | { 100 | return !this; 101 | } 102 | 103 | /// ditto 104 | bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc 105 | { 106 | if (_thisPtr is null) 107 | return rhs._thisPtr is null; 108 | if (rhs._thisPtr is null) 109 | return false; 110 | return _get_value == rhs._get_value; 111 | } 112 | 113 | /// 114 | auto opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc 115 | { 116 | if (_thisPtr is null) 117 | return (rhs._thisPtr is null) - 1; 118 | if (rhs._thisPtr is null) 119 | return 1; 120 | return _get_value.opCmp(rhs._get_value); 121 | } 122 | 123 | /// 124 | size_t toHash() @trusted scope const pure nothrow @nogc 125 | { 126 | if (_thisPtr is null) 127 | return 0; 128 | return hashOf(_get_value); 129 | } 130 | 131 | /// 132 | ~this() nothrow 133 | { 134 | static if (hasElaborateDestructor!T || hasDestructor!T) 135 | { 136 | if (false) // break @safe and pure attributes 137 | { 138 | Unqual!T* object; 139 | (*object).__xdtor; 140 | } 141 | } 142 | if (this) 143 | { 144 | (() @trusted { mir_rc_decrease_counter(context); })(); 145 | debug _reset; 146 | } 147 | } 148 | 149 | static if (is(T == const) || is(T == immutable)) 150 | this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc 151 | { 152 | if (rhs) 153 | { 154 | this._value = cast(typeof(this._value))rhs._value; 155 | mir_rc_increase_counter(context); 156 | } 157 | } 158 | 159 | static if (is(T == immutable)) 160 | this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc 161 | { 162 | if (rhs) 163 | { 164 | this._value = cast(typeof(this._value))rhs._value; 165 | mir_rc_increase_counter(context); 166 | } 167 | } 168 | 169 | static if (is(T == immutable)) 170 | this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc 171 | { 172 | if (rhs) 173 | { 174 | this._value = cast(typeof(this._value))rhs._value; 175 | mir_rc_increase_counter(context); 176 | } 177 | } 178 | 179 | this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc 180 | { 181 | if (rhs) 182 | { 183 | this._value = rhs._value; 184 | mir_rc_increase_counter(context); 185 | } 186 | } 187 | 188 | /// 189 | ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc 190 | { 191 | this = typeof(this).init; 192 | } 193 | 194 | /// 195 | ref opAssign(return typeof(this) rhs) scope return @trusted // pure nothrow @nogc 196 | { 197 | this.proxySwap(rhs); 198 | return this; 199 | } 200 | 201 | /// 202 | ref opAssign(Q)(return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc 203 | if (isImplicitlyConvertible!(Q*, T*)) 204 | { 205 | this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}()); 206 | return this; 207 | } 208 | } 209 | 210 | /// 211 | alias SlimRCPtr = mir_slim_rcptr; 212 | 213 | /// 214 | template createSlimRC(T) 215 | if (!is(T == interface) && !__traits(isAbstractClass, T)) 216 | { 217 | /// 218 | mir_slim_rcptr!T createSlimRC(Args...)(auto ref Args args) 219 | { 220 | typeof(return) ret; 221 | with (ret) () @trusted { 222 | auto context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T); 223 | if (!context) 224 | { 225 | version(D_Exceptions) 226 | { import mir.exception : toMutable; throw allocationError.toMutable; } 227 | else 228 | assert(0, allocationExcMsg); 229 | } 230 | _value = cast(typeof(_value))(context + 1); 231 | } (); 232 | import mir.functional: forward; 233 | import mir.conv: emplace; 234 | cast(void) emplace!T(ret._value, forward!args); 235 | return ret; 236 | } 237 | } 238 | 239 | /// 240 | version(mir_test) 241 | @safe pure @nogc nothrow 242 | unittest 243 | { 244 | auto a = createSlimRC!double(10); 245 | auto b = a; 246 | assert(*b == 10); 247 | *b = 100; 248 | assert(*a == 100); 249 | } 250 | 251 | /// Classes 252 | version(mir_test) 253 | @safe pure @nogc nothrow 254 | unittest 255 | { 256 | static class C 257 | { 258 | int index; 259 | double value; 260 | ref double bar() @safe pure nothrow @nogc { return value; } 261 | this(double d) { value = d; } 262 | 263 | override size_t toHash() const scope @safe pure nothrow @nogc 264 | { 265 | return index; 266 | } 267 | } 268 | auto a = createSlimRC!C(10); 269 | assert(a._counter == 1); 270 | auto b = a; 271 | assert(a._counter == 2); 272 | assert((*b).value == 10); 273 | b.value = 100; // access via alias this syntax 274 | assert(a.value == 100); 275 | assert(a._counter == 2); 276 | } 277 | 278 | /// Structs 279 | version(mir_test) 280 | @safe pure @nogc nothrow 281 | unittest 282 | { 283 | struct S 284 | { 285 | double e; 286 | } 287 | struct C 288 | { 289 | int i; 290 | S s; 291 | // 'alias' should be accesable by reference 292 | // or a class/interface 293 | alias s this; 294 | } 295 | 296 | auto a = createSlimRC!C(10, S(3)); 297 | auto s = a; 298 | assert(s._counter == 2); 299 | assert(s.e == 3); 300 | } 301 | 302 | /// Classes with empty constructor 303 | version(mir_test) 304 | @safe pure @nogc nothrow 305 | unittest 306 | { 307 | static class C 308 | { 309 | int index = 34; 310 | 311 | override size_t toHash() const scope @safe pure nothrow @nogc 312 | { 313 | return index; 314 | } 315 | } 316 | assert(createSlimRC!C.index == 34); 317 | } 318 | 319 | version(unittest): 320 | 321 | package struct _test_unpure_system_dest_s__ { 322 | static int numStructs; 323 | int i; 324 | 325 | this(this This)(int i) { 326 | this.i = i; 327 | ++numStructs; 328 | } 329 | 330 | ~this() @system nothrow { 331 | auto d = new int[2]; 332 | --numStructs; 333 | } 334 | } 335 | 336 | version(mir_test) 337 | @system nothrow 338 | unittest 339 | { 340 | import mir.rc.array; 341 | auto ptr = createSlimRC!_test_unpure_system_dest_s__(42); 342 | auto arr = rcarray!_test_unpure_system_dest_s__(3); 343 | } 344 | -------------------------------------------------------------------------------- /source/mir/array/allocation.d: -------------------------------------------------------------------------------- 1 | /** 2 | Functions and types that manipulate built-in arrays and associative arrays. 3 | 4 | This module provides all kinds of functions to create, manipulate or convert arrays: 5 | 6 | $(SCRIPT inhibitQuickIndex = 1;) 7 | $(BOOKTABLE , 8 | $(TR $(TH Function Name) $(TH Description) 9 | ) 10 | $(TR $(TD $(LREF _array)) 11 | $(TD Returns a copy of the input in a newly allocated dynamic _array. 12 | )) 13 | ) 14 | 15 | Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 16 | 17 | License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 18 | 19 | Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis 20 | 21 | Source: $(PHOBOSSRC std/_array.d) 22 | */ 23 | module mir.array.allocation; 24 | 25 | import mir.functional; 26 | import mir.primitives; 27 | import std.traits; 28 | 29 | /** 30 | * Allocates an array and initializes it with copies of the elements 31 | * of range $(D r). 32 | * 33 | * Narrow strings are handled as a special case in an overload. 34 | * 35 | * Params: 36 | * r = range (or aggregate with $(D opApply) function) whose elements are copied into the allocated array 37 | * Returns: 38 | * allocated and initialized array 39 | */ 40 | auto array(Range)(Range r) 41 | if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !__traits(isStaticArray, Range) || isPointer!Range && (isInputRange!(PointerTarget!Range) || isIterable!(PointerTarget!Range))) 42 | { 43 | static if (isIterable!Range) 44 | alias E = ForeachType!Range; 45 | else 46 | static if (isPointer!Range && isIterable!(PointerTarget!Range)) 47 | alias E = ForeachType!(PointerTarget!Range); 48 | else 49 | alias E = ElementType!Range; 50 | 51 | if (__ctfe) 52 | { 53 | // Compile-time version to avoid memcpy calls. 54 | // Also used to infer attributes of array(). 55 | E[] result; 56 | static if (isInputRange!Range) 57 | for (; !r.empty; r.popFront) 58 | result ~= r.front; 59 | else 60 | static if (isPointer!Range) 61 | foreach (e; *r) 62 | result ~= e; 63 | else 64 | foreach (e; r) 65 | result ~= e; 66 | return result; 67 | } 68 | 69 | import mir.primitives: hasLength; 70 | 71 | static if (hasLength!Range) 72 | { 73 | auto length = r.length; 74 | if (length == 0) 75 | return null; 76 | 77 | import mir.conv : emplaceRef; 78 | import std.array: uninitializedArray; 79 | 80 | auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))(); 81 | 82 | static if (isInputRange!Range) 83 | { 84 | foreach(ref e; result) 85 | { 86 | emplaceRef!E(e, r.front); 87 | r.popFront; 88 | } 89 | } 90 | else 91 | static if (isPointer!Range) 92 | { 93 | auto it = result; 94 | foreach(ref f; *r) 95 | { 96 | emplaceRef!E(it[0], f); 97 | it = it[1 .. $]; 98 | } 99 | } 100 | else 101 | { 102 | auto it = result; 103 | foreach (f; r) 104 | { 105 | import mir.functional: forward; 106 | emplaceRef!E(it[0], forward!f); 107 | it = it[1 .. $]; 108 | } 109 | } 110 | 111 | return (() @trusted => cast(E[]) result)(); 112 | } 113 | else 114 | { 115 | import std.array: std_appender = appender; 116 | 117 | auto a = std_appender!(E[]); 118 | 119 | static if (isInputRange!Range) 120 | for (; !r.empty; r.popFront) 121 | a.put(r.front); 122 | else 123 | static if (isPointer!Range) 124 | { 125 | foreach (e; *r) 126 | a.put(forward!e); 127 | } 128 | else 129 | { 130 | foreach (e; r) 131 | a.put(forward!e); 132 | } 133 | return a.data; 134 | } 135 | } 136 | 137 | /// 138 | @safe pure nothrow version(mir_test) unittest 139 | { 140 | auto a = array([1, 2, 3, 4, 5][]); 141 | assert(a == [ 1, 2, 3, 4, 5 ]); 142 | } 143 | 144 | @safe pure nothrow version(mir_test) unittest 145 | { 146 | import mir.algorithm.iteration : equal; 147 | struct Foo 148 | { 149 | int a; 150 | } 151 | auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]); 152 | assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)])); 153 | } 154 | 155 | @safe pure nothrow version(mir_test) unittest 156 | { 157 | struct MyRange 158 | { 159 | enum front = 123; 160 | enum empty = true; 161 | void popFront() {} 162 | } 163 | 164 | auto arr = (new MyRange).array; 165 | assert(arr.empty); 166 | } 167 | 168 | @system pure nothrow version(mir_test) unittest 169 | { 170 | immutable int[] a = [1, 2, 3, 4]; 171 | auto b = (&a).array; 172 | assert(b == a); 173 | } 174 | 175 | @system version(mir_test) unittest 176 | { 177 | import mir.algorithm.iteration : equal; 178 | struct Foo 179 | { 180 | int a; 181 | void opAssign(Foo) 182 | { 183 | assert(0); 184 | } 185 | auto opEquals(Foo foo) 186 | { 187 | return a == foo.a; 188 | } 189 | } 190 | auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]); 191 | assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)])); 192 | } 193 | 194 | @safe version(mir_test) unittest 195 | { 196 | // Issue 12315 197 | static struct Bug12315 { immutable int i; } 198 | enum bug12315 = [Bug12315(123456789)].array(); 199 | static assert(bug12315[0].i == 123456789); 200 | } 201 | 202 | @safe version(mir_test) unittest 203 | { 204 | import mir.ndslice.topology: repeat; 205 | static struct S{int* p;} 206 | auto a = array(immutable(S).init.repeat(5)); 207 | assert(a.length == 5); 208 | } 209 | 210 | /// 211 | @safe version(mir_test) unittest 212 | { 213 | assert("Hello D".array == "Hello D"); 214 | assert("Hello D"w.array == "Hello D"w); 215 | assert("Hello D"d.array == "Hello D"d); 216 | } 217 | 218 | @system version(mir_test) unittest 219 | { 220 | // @system due to array!string 221 | import std.conv : to; 222 | 223 | static struct TestArray { int x; string toString() scope const @safe { return to!string(x); } } 224 | 225 | static struct OpAssign 226 | { 227 | uint num; 228 | this(uint num) { this.num = num; } 229 | 230 | // Templating opAssign to make sure the bugs with opAssign being 231 | // templated are fixed. 232 | void opAssign(T)(T rhs) { this.num = rhs.num; } 233 | } 234 | 235 | static struct OpApply 236 | { 237 | int opApply(scope int delegate(ref int) dg) 238 | { 239 | int res; 240 | foreach (i; 0 .. 10) 241 | { 242 | res = dg(i); 243 | if (res) break; 244 | } 245 | 246 | return res; 247 | } 248 | } 249 | 250 | auto a = array([1, 2, 3, 4, 5][]); 251 | assert(a == [ 1, 2, 3, 4, 5 ]); 252 | 253 | auto b = array([TestArray(1), TestArray(2)][]); 254 | assert(b == [TestArray(1), TestArray(2)]); 255 | 256 | class C 257 | { 258 | int x; 259 | this(int y) { x = y; } 260 | override string toString() const @safe { return to!string(x); } 261 | } 262 | auto c = array([new C(1), new C(2)][]); 263 | assert(c[0].x == 1); 264 | assert(c[1].x == 2); 265 | 266 | auto d = array([1.0, 2.2, 3][]); 267 | assert(is(typeof(d) == double[])); 268 | assert(d == [1.0, 2.2, 3]); 269 | 270 | auto e = [OpAssign(1), OpAssign(2)]; 271 | auto f = array(e); 272 | assert(e == f); 273 | 274 | assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]); 275 | assert(array("ABC") == "ABC"); 276 | assert(array("ABC".dup) == "ABC"); 277 | } 278 | 279 | //Bug# 8233 280 | @safe version(mir_test) unittest 281 | { 282 | assert(array("hello world"d) == "hello world"d); 283 | immutable a = [1, 2, 3, 4, 5]; 284 | assert(array(a) == a); 285 | const b = a; 286 | assert(array(b) == a); 287 | 288 | //To verify that the opAssign branch doesn't get screwed up by using Unqual. 289 | //EDIT: array no longer calls opAssign. 290 | struct S 291 | { 292 | ref S opAssign(S)(const ref S rhs) 293 | { 294 | assert(0); 295 | } 296 | 297 | int i; 298 | } 299 | 300 | alias AliasSeq(T...) = T; 301 | foreach (T; AliasSeq!(S, const S, immutable S)) 302 | { 303 | auto arr = [T(1), T(2), T(3), T(4)]; 304 | assert(array(arr) == arr); 305 | } 306 | } 307 | 308 | @safe version(mir_test) unittest 309 | { 310 | //9824 311 | static struct S 312 | { 313 | @disable void opAssign(S); 314 | int i; 315 | } 316 | auto arr = [S(0), S(1), S(2)]; 317 | arr.array; 318 | } 319 | 320 | // Bugzilla 10220 321 | @safe version(mir_test) unittest 322 | { 323 | import mir.algorithm.iteration : equal; 324 | import std.exception; 325 | import mir.ndslice.topology: repeat; 326 | 327 | static struct S 328 | { 329 | int val; 330 | 331 | @disable this(); 332 | this(int v) { val = v; } 333 | } 334 | static immutable r = S(1).repeat(2).array(); 335 | assert(equal(r, [S(1), S(1)])); 336 | } 337 | 338 | @safe version(mir_test) unittest 339 | { 340 | //Turn down infinity: 341 | static assert(!is(typeof( 342 | repeat(1).array() 343 | ))); 344 | } 345 | --------------------------------------------------------------------------------