.gitmodules
Dockerfile.aarch64
LICENSE
NOTICE
cpp_example/eye.d
cpp_example/init_rcarray.d
cpp_example/main.cpp
cpp_example/meson.build
dub.sdl
include/mir/interpolate.h
include/mir/ndslice.h
include/mir/numeric.h
include/mir/rcarray.h
include/mir/rcptr.h
include/mir/series.h
include/mir/slim_rcptr.h
include/mir/small_string.h
index.d
meson.build
ndslice.graffle
ndslice.svg
source/mir/algebraic_alias/json.d
source/mir/algorithm/iteration.d
source/mir/algorithm/setops.d
source/mir/appender.d
source/mir/array/allocation.d
source/mir/bignum/decimal.d
source/mir/bignum/fixed.d
source/mir/bignum/fp.d
source/mir/bignum/integer.d
source/mir/bignum/internal/dec2flt_table.d
source/mir/bignum/internal/ryu/generic_128.d
source/mir/bignum/low_level_view.d
source/mir/combinatorics/package.d
source/mir/container/binaryheap.d
source/mir/cpp_export/numeric.d
source/mir/date.d
source/mir/format.d
source/mir/format_impl.d
source/mir/graph/package.d
source/mir/graph/tarjan.d
source/mir/interpolate/constant.d
source/mir/interpolate/generic.d
source/mir/interpolate/linear.d
source/mir/interpolate/package.d
source/mir/interpolate/polynomial.d
source/mir/interpolate/spline.d
source/mir/interpolate/utility.d
source/mir/lob.d
source/mir/math/func/expdigamma.d
source/mir/math/numeric.d
source/mir/math/stat.d
source/mir/math/sum.d
source/mir/ndslice/allocation.d
source/mir/ndslice/chunks.d
source/mir/ndslice/concatenation.d
source/mir/ndslice/connect/cpython.d
source/mir/ndslice/dynamic.d
source/mir/ndslice/field.d
source/mir/ndslice/filling.d
source/mir/ndslice/fuse.d
source/mir/ndslice/internal.d
source/mir/ndslice/iterator.d
source/mir/ndslice/mutation.d
source/mir/ndslice/ndfield.d
source/mir/ndslice/package.d
source/mir/ndslice/slice.d
source/mir/ndslice/sorting.d
source/mir/ndslice/topology.d
source/mir/ndslice/traits.d
source/mir/numeric.d
source/mir/parse.d
source/mir/polynomial.d
source/mir/range.d
source/mir/rc/array.d
source/mir/rc/context.d
source/mir/rc/package.d
source/mir/rc/ptr.d
source/mir/rc/slim_ptr.d
source/mir/serde.d
source/mir/series.d
source/mir/small_array.d
source/mir/small_string.d
source/mir/string_map.d
source/mir/timestamp.d
source/mir/type_info.d
subprojects/mir-core.wrap
test_travis.sh
<<<<<< network
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-exception.lst
|/++
|`@nogc` exceptions and errors definitions.
|
|Most of the API Requires DIP1008.
|+/
|module mir.exception;
|
|version(D_Exceptions):
|
|version(D_Ddoc)
| private enum _version_D_Ddoc = true;
|else
| private enum _version_D_Ddoc = false;
|
|private enum NOGCEXP = __traits(compiles, (()@nogc {throw new Exception("");})());
|private enum HASFORMAT = __traits(compiles, (()@nogc {import mir.format;})());
|
|package template staticException(string fmt, string file, int line)
|{
| static immutable staticException = new Exception(fmt, file, line);
|}
|
|///
|auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(scope auto return ref Expr arg) @trusted
|{
| version(LDC) pragma(inline, true);
| import core.lifetime: forward;
| import mir.utility: _expect;
| static if (__traits(compiles, arg !is null))
| {
| if (_expect(arg !is null, true))
| return forward!arg;
| }
| else
| {
| if (_expect(cast(bool)arg, true))
| return forward!arg;
| }
| throw staticException!(fmt, file, line);
|}
|
|///
|@safe pure nothrow @nogc
|version (mir_core_test) unittest
|{
| import mir.exception;
| try enforce!"Msg"(false);
| catch(Exception e) assert(e.msg == "Msg");
|}
|
|/++
|+/
|class MirException : Exception
|{
| ///
| mixin MirThrowableImpl;
|}
|
|/// Generic style
|version (mir_test) static if (NOGCEXP && HASFORMAT)
|@safe pure nothrow @nogc
|unittest
|{
| static if (__traits(compiles, (()@nogc {import mir.format;})()))
| {
| import mir.exception;
| try throw new MirException("Hi D", 2, "!");
| catch(Exception e) assert(e.msg == "Hi D2!");
| }
|}
|
|/// C++ style
|version (mir_test) static if (NOGCEXP && HASFORMAT)
|@safe pure nothrow @nogc
|unittest
|{
| static if (__traits(compiles, (()@nogc {import mir.format;})()))
| {
| import mir.exception;
| import mir.format;
| try throw new MirException(stringBuf() << "Hi D" << 2 << "!" << getData);
| catch(Exception e) assert(e.msg == "Hi D2!");
| }
|}
|
|/// Low-level style
|version (mir_test) static if (NOGCEXP && HASFORMAT)
|@safe pure nothrow @nogc
|unittest
|{
| static if (__traits(compiles, (()@nogc {import mir.format;})()))
| {
| import mir.exception;
| import mir.format;
| stringBuf buf;
| try throw new MirException(buf.print( "Hi D", 2, "!").data);
| catch(Exception e) assert(e.msg == "Hi D2!");
| }
|}
|
|///
|version (mir_core_test) static if (NOGCEXP)
|@safe pure nothrow @nogc
|unittest
|{
| @safe pure nothrow @nogc
| bool func(scope const(char)[] msg)
| {
| /// scope messages are copied
| try throw new MirException(msg);
| catch(Exception e) assert(e.msg == msg);
|
| /// immutable strings are not copied
| static immutable char[] gmsg = "global msg";
| try throw new MirException(gmsg);
| catch(Exception e) assert(e.msg is gmsg);
|
| return __ctfe;
| }
|
| assert(func("runtime-time check") == 0);
|
| static assert(func("compile-time check") == 1);
|}
|
|// ///
|// auto ref enforce(T, Args...)(scope auto return ref T arg, lazy @nogc Args args, string file = __FILE__, int line = __LINE__) @nogc
|// if (Args.length)
|// {
|// import mir.utility: _expect;
|// static if (__traits(compiles, arg !is null))
|// {
|// if (_expect(arg !is null, true))
|// return arg;
|// }
|// else
|// {
|// if (_expect(cast(bool)arg, true))
|// return arg;
|// }
|// import mir.format;
|// stringBuf buf;
|// throw new MirException(buf.print(args).data, file, line);
|// }
|
|// ///
|// @safe pure nothrow @nogc
|// version (mir_core_test) unittest static if (NOGCEXP && HASFORMAT)
|// {
|// import mir.exception;
|// try enforce(false, "Hi D", 2, "!");
|// catch(Exception e) assert(e.msg == "Hi D2!");
|// }
|
|// ///
|// auto ref enforce(T)(scope auto return ref T arg, lazy scope const(char)[] msg, string file = __FILE__, int line = __LINE__) @nogc
|// {
|// import core.lifetime: forward;
|// import mir.utility: _expect;
|// static if (__traits(compiles, arg !is null))
|// {
|// if (_expect(arg !is null, true))
|// return forward!arg[0];
|// }
|// else
|// {
|// if (_expect(cast(bool)arg, true))
|// return forward!arg[0];
|// }
|// throw new MirException(msg, file, line);
|// }
|
|// ///
|// @safe pure nothrow @nogc
|// version (mir_core_test) unittest static if (NOGCEXP && HASFORMAT)
|// {
|// import mir.exception;
|// try enforce(false, "Msg");
|// catch(Exception e) assert(e.msg == "Msg");
|// }
|
|
|/++
|+/
|class MirError : Error
|{
| ///
| mixin MirThrowableImpl;
|}
|
|///
|@system pure nothrow @nogc
|version (mir_test) static if (NOGCEXP)
|unittest
|{
| @system pure nothrow @nogc
| bool func(scope const(char)[] msg)
| {
| /// scope messages are copied
| try throw new MirException(msg);
| catch(Exception e) assert(e.msg == msg);
|
| /// immutable strings are not copied
| static immutable char[] gmsg = "global msg";
| try throw new MirError(gmsg);
| catch(Error e) assert(e.msg is gmsg);
|
| return __ctfe;
| }
|
| assert(func("runtime-time check") == 0);
|
| static assert(func("compile-time check") == 1);
|}
|
|/++
|+/
|mixin template MirThrowableImpl()
|{
| private bool _global;
| private char[maxMirExceptionMsgLen] _payload = void;
| import mir.exception: maxMirExceptionMsgLen, mirExceptionInitilizePayloadImpl;
|
| /++
| Params:
| msg = message. No-scope `msg` is assumed to have the same lifetime as the throwable. scope strings are copied to internal buffer.
| file = file name, zero terminated global string
| line = line number
| nextInChain = next exception in the chain (optional)
| +/
0000000| @nogc @safe pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
| {
0000000| super((() @trusted => cast(immutable) mirExceptionInitilizePayloadImpl(_payload, msg))(), file, line, nextInChain);
| }
|
| /// ditto
0000000| @nogc @safe pure nothrow this(scope const(char)[] msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
| {
0000000| super((() @trusted => cast(immutable) mirExceptionInitilizePayloadImpl(_payload, msg))(), file, line, nextInChain);
| }
|
| /// ditto
0000000| @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
| {
0000000| this._global = true;
0000000| super(msg, file, line, nextInChain);
| }
|
| /// ditto
0000000| @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
| {
0000000| this._global = true;
0000000| super(msg, file, line, nextInChain);
| }
|
| ///
| ~this() @trusted
| {
| import mir.internal.memory: free;
0000000| if (!_global && msg.ptr != _payload.ptr)
0000000| free(cast(void*)msg.ptr);
| }
|
| /++
| Generic multiargument overload.
| Constructs a string using the `print` function.
| +/
| @nogc @safe pure nothrow this(Args...)(scope auto ref Args args, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
| if (Args.length > 1)
| {
| static assert (__traits(compiles, {import mir.format;}), "MirThrowableImpl needs mir-algorithm for mir.format and exception formatting.");
| import mir.format;
| stringBuf buf;
| this(buf.print(args).data, file, line, nextInChain);
| }
|}
|
|///
|enum maxMirExceptionMsgLen = 447;
|
|pragma(inline, false)
|pure nothrow @nogc @safe
|const(char)[] mirExceptionInitilizePayloadImpl(ref return char[maxMirExceptionMsgLen] payload, scope const(char)[] msg)
|{
| import mir.internal.memory: malloc;
| import core.stdc.string: memcpy;
0000000| if (msg.length > payload.length)
| {
0000000| if (auto ret = (() @trusted
| {
0000000| if (__ctfe)
0000000| return null;
0000000| if (auto ptr = malloc(msg.length))
| {
0000000| memcpy(ptr, msg.ptr, msg.length);
0000000| return cast(const(char)[]) ptr[0 .. msg.length];
| }
0000000| return null;
| })())
0000000| return ret;
0000000| msg = msg[0 .. payload.length];
| // remove tail UTF-8 symbol chunk if any
0000000| uint c = msg[$-1];
0000000| if (c > 0b_0111_1111)
| {
| do {
0000000| c = msg[$-1];
0000000| msg = msg[0 .. $ - 1];
| }
0000000| while (msg.length && c < 0b_1100_0000);
| }
| }
0000000| if (__ctfe)
0000000| payload[][0 .. msg.length] = msg;
| else
0000000| (() @trusted => memcpy(payload.ptr, msg.ptr, msg.length))();
0000000| return payload[0 .. msg.length];
|}
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/exception.d is 0% covered
<<<<<< EOF
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-enums.lst
|/++
|Enum utilities.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilya Yaroshenko
|Macros:
|+/
|module mir.enums;
|
|private bool hasSeqGrow(T)(T[] elems)
| if (__traits(isIntegral, T))
|{
| assert(elems.length);
| auto min = elems[0];
| foreach (i, e; elems)
| if (i != e - min)
| return false;
| return true;
|}
|
|/++
|Enum index that corresponds of the list returned by `std.traits.EnumMembers`.
|Returns:
| enum member position index in the enum definition that corresponds the `value`.
|+/
|bool getEnumIndex(T)(const T value, ref uint index)
| @safe pure nothrow @nogc
| if (is(T == enum))
|{
| import std.traits: EnumMembers, isSomeString;
| import mir.utility: _expect;
|
| static if (__traits(isFloating, T))
| {
| // TODO: index based binary searach
| foreach (i, member; enumMembers!T)
| {
| if (value == member)
| {
| index = cast(uint) i;
| return true;
| }
| }
| return false;
| }
| else
| static if (!__traits(isIntegral, T)) //strings
| {
| enum string[1] stringEnumValue(alias symbol) = [symbol];
| return getEnumIndexFromKey!(T, false, stringEnumValue)(value, index);
| }
| else
| static if (hasSeqGrow(enumMembers!T))
| {
| import std.traits: Unsigned;
| const shifted = cast(Unsigned!(typeof(value - T.min)))(value - T.min);
| if (_expect(shifted < enumMembers!T.length, true))
| {
| index = cast(uint) shifted;
| return true;
| }
| return false;
| }
| else
| static if (is(T : bool))
| {
| index = !value;
| return true;
| }
| else
| {
| import std.traits: Unsigned;
| alias U = Unsigned!(typeof(T.max - T.min + 1));
|
| enum length = cast(size_t)cast(U)(T.max - T.min + 1);
|
| const shifted = cast(size_t)cast(U)(value - T.min);
|
| static if (length <= 255)
| {
| static immutable ubyte[length] table = (){
| ubyte[length] ret;
| foreach (i, member; enumMembers!T)
| {
| ret[member - T.min] = cast(ubyte)(i + 1);
| }
| return ret;
| }();
|
| if (_expect(shifted < length, true))
| {
| int id = table[shifted] - 1;
| if (_expect(id >= 0, true))
| {
| index = id;
| return true;
| }
| }
| return false;
| }
| else
| {
| switch (value)
| {
| foreach (i, member; EnumMembers!T)
| {
| case member:
| index = i;
| return true;
| }
| default: return false;
| }
| }
| }
|}
|
|///
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| import std.meta: AliasSeq;
|
| enum Common { a, b, c }
| enum Reversed { a = 1, b = 0, c = -1 }
| enum Shifted { a = -4, b, c }
| enum Small { a = -4, b, c = 10 }
| enum Big { a = -4, b, c = 1000 }
| enum InverseBool { True = true, False = false }
| enum FP : float { a = -4, b, c }
| enum S : string { a = "а", b = "б", c = "ц" }
|
| uint index = -1;
| foreach (E; AliasSeq!(Common, Reversed, Shifted, Small, Big, FP, S))
| {
| assert(getEnumIndex(E.a, index) && index == 0);
| assert(getEnumIndex(E.b, index) && index == 1);
| assert(getEnumIndex(E.c, index) && index == 2);
| }
|
| assert(getEnumIndex(InverseBool.True, index) && index == 0);
| assert(getEnumIndex(InverseBool.False, index) && index == 1);
|}
|
|/++
|Static immutable instance of `[std.traits.EnumMembers!T]`.
|+/
|template enumMembers(T)
| if (is(T == enum))
|{
| import std.traits: EnumMembers;
| ///
| static immutable T[EnumMembers!T.length] enumMembers = [EnumMembers!T];
|}
|
|///
|version(mir_core_test) unittest
|{
| enum E {a = 1, b = -1, c}
| static assert(enumMembers!E == [E.a, E.b, E.c]);
|}
|
|/++
|Static immutable instance of Enum Identifiers.
|+/
|template enumIdentifiers(T)
| if (is(T == enum))
|{
| import std.traits: EnumMembers;
| static immutable string[EnumMembers!T.length] enumIdentifiers = () {
| string[EnumMembers!T.length] identifiers;
| static foreach(i, member; EnumMembers!T)
| identifiers[i] = __traits(identifier, EnumMembers!T[i]);
| return identifiers;
| } ();
|}
|
|///
|version(mir_core_test) unittest
|{
| enum E {z = 1, b = -1, c}
| static assert(enumIdentifiers!E == ["z", "b", "c"]);
|}
|
|/++
|Aliases itself to $(LREF enumMembers) for string enums and
|$(LREF enumIdentifiers) for integral and floating point enums.
|+/
|template enumStrings(T)
| if (is(T == enum))
|{
| static if (is(T : C[], C))
| alias enumStrings = enumMembers!T;
| else
| alias enumStrings = enumIdentifiers!T;
|}
|
|///
|version(mir_core_test) unittest
|{
| enum E {z = 1, b = -1, c}
| static assert(enumStrings!E == ["z", "b", "c"]);
|
| enum S {a = "A", b = "B", c = ""}
| static assert(enumStrings!S == [S.a, S.b, S.c]);
|}
|
|/++
|Params:
| index = enum index `std.traits.EnumMembers!T`
|Returns:
| A enum value that corresponds to the index.
|Note:
| The function doesn't check that index is less then `EnumMembers!T.length`.
|+/
|T unsafeEnumFromIndex(T)(size_t index)
| @trusted pure nothrow @nogc
| if (is(T == enum))
|{
| static if (__traits(isIntegral, T))
| enum bool fastConv = hasSeqGrow(enumMembers!T);
| else
| enum bool fastConv = false;
|
| assert(index < enumMembers!T.length);
|
| static if (fastConv)
| {
| return cast(T) index;
| }
| else
| {
| return enumMembers!T[index];
| }
|}
|
|/++
|+/
|template getEnumIndexFromKey(T, bool caseInsesetive = true, getKeysTemplate...)
| if (is(T == enum) && getKeysTemplate.length <= 1)
|{
| ///
| bool getEnumIndexFromKey(C)(scope const(C)[] key, ref uint index)
| @safe pure nothrow @nogc
| if (is(C == char) || is(C == wchar) || is(C == dchar))
| {
| import mir.string_table;
| import mir.utility: simpleSort, _expect;
| import std.traits: EnumMembers;
| import std.meta: staticIndexOf;
|
| alias String = immutable(C)[];
|
| static if (getKeysTemplate.length)
| {
| alias keysOfImpl = getKeysTemplate[0];
| enum String[] keysOf(alias symbol) = keysOfImpl!symbol;
| }
| else
| static if (is(T : W[], W))
| enum String[1] keysOf(alias symbol) = [cast(String)symbol];
| else
| enum String[1] keysOf(alias symbol) = [__traits(identifier, symbol)];
|
| enum keys = () {
| String[] keys;
| foreach(i, member; EnumMembers!T)
| keys ~= keysOf!(EnumMembers!T[i]);
| return keys;
| } ();
|
| static if (keys.length == 0)
| {
| return false;
| }
| else
| {
| enum indexLength = keys.length + 1;
| alias ct = createTable!C;
| static immutable table = ct!(keys, caseInsesetive);
| static immutable indices = ()
| {
| minimalSignedIndexType!indexLength[indexLength] indices;
|
| foreach (i, member; EnumMembers!T)
| foreach (key; keysOf!(EnumMembers!T[i]))
| {
| static if (caseInsesetive)
| {
| key = key.dup.fastToUpperInPlace;
| }
| indices[table[key]] = i;
| }
|
| return indices;
| } ();
|
| static if (!caseInsesetive && keys.length <= 6 && keys[$ - 1].length <= 32)
| {
|
| }
| else
| {
|
| }
|
| uint stringId = void;
| if (_expect(table.get(key, stringId), true))
| {
| index = indices[stringId];
| return true;
| }
| return false;
| }
| }
|}
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/enums.d has no code
<<<<<< EOF
# path=./source-mir-format_impl.lst
|///
|module mir.format_impl;
|
|import mir.format;
|
|@safe pure @nogc nothrow:
|
|
|size_t printFloatingPointExtend(T, C)(T c, scope ref const FormatSpec spec, scope ref C[512] buf) @trusted
|{
0000000| char[512] cbuf = void;
0000000| return extendASCII(cbuf[].ptr, buf[].ptr, printFloatingPoint(cast(double)c, spec, cbuf));
|}
|
|size_t printFloatingPointGen(T)(T c, scope ref const FormatSpec spec, scope ref char[512] buf) @trusted
| if(is(T == float) || is(T == double) || is(T == real))
|{
| import mir.math.common: copysign, fabs;
0000000| bool neg = copysign(1, c) < 0;
0000000| c = fabs(c);
0000000| char specFormat = spec.format;
| version (CRuntime_Microsoft)
| {
| if (c != c || c.fabs == c.infinity)
| {
| size_t i;
| char s = void;
| if (copysign(1, c) < 0)
| s = '-';
| else
| if (spec.plus)
| s = '+';
| else
| if (spec.space)
| s = ' ';
| else
| goto S;
| buf[0] = s;
| i = 1;
| S:
| static immutable char[3][2][2] special = [["inf", "INF"], ["nan", "NAN"]];
| auto p = &special[c != c][(specFormat & 0xDF) == specFormat][0];
| buf[i + 0] = p[0];
| buf[i + 1] = p[1];
| buf[i + 2] = p[2];
| return i + 3;
| }
| }
| alias T = double;
| static if (is(T == real))
| align(4) char[12] fmt = "%%%%%%*.*gL\0";
| else
| align(4) char[12] fmt = "%%%%%%*.*g\0\0";
|
0000000| if (specFormat && specFormat != 's' && specFormat != 'g' && specFormat != 'G')
| {
0000000| assert (
| specFormat == 'e'
0000000| || specFormat == 'E'
0000000| || specFormat == 'f'
0000000| || specFormat == 'F'
0000000| || specFormat == 'a'
0000000| || specFormat == 'A', "Wrong floating point format specifier.");
0000000| fmt[9] = specFormat;
| }
0000000| uint fmtRevLen = 5;
0000000| if (spec.hash) fmt[fmtRevLen--] = '#';
0000000| if (spec.space) fmt[fmtRevLen--] = ' ';
0000000| if (spec.zero) fmt[fmtRevLen--] = '0';
0000000| if (spec.plus) fmt[fmtRevLen--] = '+';
0000000| if (spec.dash) fmt[fmtRevLen--] = '-';
|
| import core.stdc.stdio : snprintf;
0000000| ptrdiff_t res = assumePureSafe(&snprintf)((()@trusted =>buf.ptr)(), buf.length - 1, &fmt[fmtRevLen], spec.width, spec.precision, c);
0000000| assert (res >= 0, "snprintf failed to print a floating point number");
| import mir.utility: min;
0000000| return res < 0 ? 0 : min(cast(size_t)res, buf.length - 1);
|}
|
|auto assumePureSafe(T)(T t) @trusted
| // if (isFunctionPointer!T || isDelegate!T)
|{
| import std.traits;
| enum attrs = (functionAttributes!T | FunctionAttribute.pure_ | FunctionAttribute.safe) & ~FunctionAttribute.system;
0000000| return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
|}
|
|////////// FLOATING POINT //////////
|
|size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref char[512] buf)
|{
0000000| return printFloatingPoint(cast(double)c, spec, buf);
|}
|
|size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref char[512] buf)
|{
0000000| return printFloatingPointGen(c, spec, buf);
|}
|
|size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref char[512] buf)
|{
| version (CRuntime_Microsoft)
| {
| return printFloatingPoint(cast(double) c, spec, buf);
| }
| else
| {
0000000| return printFloatingPointGen(c, spec, buf);
| }
|}
|
|size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
|{
0000000| return printFloatingPoint(cast(double)c, spec, buf);
|}
|
|size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
|{
0000000| return printFloatingPointExtend(c, spec, buf);
|}
|
|size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
|{
| version (CRuntime_Microsoft)
| {
| return printFloatingPoint(cast(double) c, spec, buf);
| }
| else
| {
0000000| return printFloatingPointExtend(c, spec, buf);
| }
|}
|
|size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
|{
0000000| return printFloatingPoint(cast(double)c, spec, buf);
|}
|
|size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
|{
0000000| return printFloatingPointExtend(c, spec, buf);
|}
|
|size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
|{
| version (CRuntime_Microsoft)
| {
| return printFloatingPoint(cast(double) c, spec, buf);
| }
| else
| {
0000000| return printFloatingPointExtend(c, spec, buf);
| }
|}
|
|nothrow:
|
0000000|size_t printHexadecimal(uint c, ref char[8] buf, bool upper) { return printHexadecimalGen!(uint, char)(c, buf, upper); }
0000000|size_t printHexadecimal(ulong c, ref char[16] buf, bool upper) { return printHexadecimalGen!(ulong, char)(c, buf, upper); }
|static if (is(ucent))
|size_t printHexadecimal(ucent c, ref char[32] buf, bool upper) { return printHexadecimalGen!(ucent, char)(c, buf, upper); }
|
0000000|size_t printHexadecimal(uint c, ref wchar[8] buf, bool upper) { return printHexadecimalGen!(uint, wchar)(c, buf, upper); }
0000000|size_t printHexadecimal(ulong c, ref wchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, wchar)(c, buf, upper); }
|static if (is(ucent))
|size_t printHexadecimal(ucent c, ref wchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, wchar)(c, buf, upper); }
|
0000000|size_t printHexadecimal(uint c, ref dchar[8] buf, bool upper) { return printHexadecimalGen!(uint, dchar)(c, buf, upper); }
0000000|size_t printHexadecimal(ulong c, ref dchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, dchar)(c, buf, upper); }
|static if (is(ucent))
|size_t printHexadecimal(ucent c, ref dchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, dchar)(c, buf, upper); }
|
|size_t printHexadecimalGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) @trusted
|{
0000000| if (c < 10)
| {
0000000| buf[0] = cast(char)('0' + c);
0000000| return 1;
| }
| import mir.bitop: ctlz;
0000000| immutable hexString = upper ? hexStringUpper : hexStringLower;
0000000| size_t ret = cast(size_t) ctlz(c);
0000000| ret = (ret >> 2) + ((ret & 3) != 0);
0000000| size_t i = ret;
| do
| {
0000000| buf.ptr[--i] = hexStringUpper[c & 0xF];
0000000| c >>= 4;
| }
0000000| while(i);
0000000| return ret;
|}
|
1| size_t printHexAddress(ubyte c, ref char[2] buf, bool upper) { return printHexAddressGen!(ubyte, char)(c, buf, upper); }
0000000| size_t printHexAddress(ushort c, ref char[4] buf, bool upper) { return printHexAddressGen!(ushort, char)(c, buf, upper); }
0000000|size_t printHexAddress(uint c, ref char[8] buf, bool upper) { return printHexAddressGen!(uint, char)(c, buf, upper); }
0000000|size_t printHexAddress(ulong c, ref char[16] buf, bool upper) { return printHexAddressGen!(ulong, char)(c, buf, upper); }
|static if (is(ucent))
|size_t printHexAddress(ucent c, ref char[32] buf, bool upper) { return printHexAddressGen!(ucent, char)(c, buf, upper); }
|
0000000| size_t printHexAddress(ubyte c, ref wchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, wchar)(c, buf, upper); }
0000000| size_t printHexAddress(ushort c, ref wchar[4] buf, bool upper) { return printHexAddressGen!(ushort, wchar)(c, buf, upper); }
0000000|size_t printHexAddress(uint c, ref wchar[8] buf, bool upper) { return printHexAddressGen!(uint, wchar)(c, buf, upper); }
0000000|size_t printHexAddress(ulong c, ref wchar[16] buf, bool upper) { return printHexAddressGen!(ulong, wchar)(c, buf, upper); }
|static if (is(ucent))
|size_t printHexAddress(ucent c, ref wchar[32] buf, bool upper) { return printHexAddressGen!(ucent, wchar)(c, buf, upper); }
|
0000000| size_t printHexAddress(ubyte c, ref dchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, dchar)(c, buf, upper); }
0000000| size_t printHexAddress(ushort c, ref dchar[4] buf, bool upper) { return printHexAddressGen!(ushort, dchar)(c, buf, upper); }
0000000|size_t printHexAddress(uint c, ref dchar[8] buf, bool upper) { return printHexAddressGen!(uint, dchar)(c, buf, upper); }
0000000|size_t printHexAddress(ulong c, ref dchar[16] buf, bool upper) { return printHexAddressGen!(ulong, dchar)(c, buf, upper); }
|static if (is(ucent))
|size_t printHexAddress(ucent c, ref dchar[32] buf, bool upper) { return printHexAddressGen!(ucent, dchar)(c, buf, upper); }
|
|size_t printHexAddressGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper)
|{
| static if (T.sizeof == 16)
| {
| printHexAddress(cast(ulong)(c >> 64), buf[0 .. 16], upper);
| printHexAddress(cast(ulong) c, buf[16 .. 32], upper);
| }
| else
| {
2| immutable hexString = upper ? hexStringUpper : hexStringLower;
7| foreach_reverse(ref e; buf)
| {
2| e = hexStringUpper[c & 0xF];
2| c >>= 4;
| }
| }
1| return buf.length;
|}
|
|static immutable hexStringUpper = "0123456789ABCDEF";
|static immutable hexStringLower = "0123456789abcdef";
|
0000000|size_t printBufferShift(size_t length, size_t shift, scope char* ptr) { return printBufferShiftGen!char(length, shift, ptr); }
0000000|size_t printBufferShift(size_t length, size_t shift, scope wchar* ptr) { return printBufferShiftGen!wchar(length, shift, ptr); }
0000000|size_t printBufferShift(size_t length, size_t shift, scope dchar* ptr) { return printBufferShiftGen!dchar(length, shift, ptr); }
|
|size_t printBufferShiftGen(C)(size_t length, size_t shift, scope C* ptr) @trusted
|{
0000000| size_t i;
0000000| do ptr[i] = ptr[shift + i];
0000000| while(++i < length);
0000000| return length;
|}
|
0000000|size_t printSigned(int c, scope ref char[11] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
0000000|size_t printSigned(long c, scope ref char[21] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
|static if (is(cent))
|size_t printSigned(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
|
0000000|size_t printSigned(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
0000000|size_t printSigned(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
|static if (is(cent))
|size_t printSigned(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
|
0000000|size_t printSigned(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
0000000|size_t printSigned(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
|static if (is(cent))
|size_t printSigned(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
|
|
123|size_t printSignedToTail(int c, scope ref char[11] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
5|size_t printSignedToTail(long c, scope ref char[21] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
|static if (is(cent))
|size_t printSignedToTail(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
|
1|size_t printSignedToTail(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
0000000|size_t printSignedToTail(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
|static if (is(cent))
|size_t printSignedToTail(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
|
0000000|size_t printSignedToTail(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
0000000|size_t printSignedToTail(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
|static if (is(cent))
|size_t printSignedToTail(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
|
|size_t printSignedGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) @trusted
|{
0000000| auto ret = printSignedToTail(c, buf, sign);
0000000| if (auto shift = buf.length - ret)
| {
0000000| return printBufferShift(ret, shift, buf[].ptr);
| }
0000000| return ret;
|}
|
|size_t printSignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign)
|{
129| if (c < 0)
| {
36| sign = '-';
36| c = -c;
| }
|
129| auto ret = printUnsignedToTail(c, buf[1 .. N]);
|
129| if (sign != '\0')
| {
40| buf[$ - ++ret] = sign;
| }
129| return ret;
|}
|
0000000|size_t printUnsigned(uint c, scope ref char[10] buf) { return printUnsignedGen(c, buf); }
0000000|size_t printUnsigned(ulong c, scope ref char[20] buf) { return printUnsignedGen(c, buf); }
|static if (is(ucent))
|size_t printUnsigned(ucent c, scope ref char[39] buf) { return printUnsignedGen(c, buf); }
|
0000000|size_t printUnsigned(uint c, scope ref wchar[10] buf) { return printUnsignedGen(c, buf); }
0000000|size_t printUnsigned(ulong c, scope ref wchar[20] buf) { return printUnsignedGen(c, buf); }
|static if (is(ucent))
|size_t printUnsigned(ucent c, scope ref wchar[39] buf) { return printUnsignedGen(c, buf); }
|
0000000|size_t printUnsigned(uint c, scope ref dchar[10] buf) { return printUnsignedGen(c, buf); }
0000000|size_t printUnsigned(ulong c, scope ref dchar[20] buf) { return printUnsignedGen(c, buf); }
|static if (is(ucent))
|size_t printUnsigned(ucent c, scope ref dchar[39] buf) { return printUnsignedGen(c, buf); }
|
360|size_t printUnsignedToTail(uint c, scope ref char[10] buf) { return printUnsignedToTailGen(c, buf); }
5|size_t printUnsignedToTail(ulong c, scope ref char[20] buf) { return printUnsignedToTailGen(c, buf); }
|static if (is(ucent))
|size_t printUnsignedToTail(ucent c, scope ref char[39] buf) { return printUnsignedToTailGen(c, buf); }
|
1|size_t printUnsignedToTail(uint c, scope ref wchar[10] buf) { return printUnsignedToTailGen(c, buf); }
0000000|size_t printUnsignedToTail(ulong c, scope ref wchar[20] buf) { return printUnsignedToTailGen(c, buf); }
|static if (is(ucent))
|size_t printUnsignedToTail(ucent c, scope ref wchar[39] buf) { return printUnsignedToTailGen(c, buf); }
|
1|size_t printUnsignedToTail(uint c, scope ref dchar[10] buf) { return printUnsignedToTailGen(c, buf); }
1|size_t printUnsignedToTail(ulong c, scope ref dchar[20] buf) { return printUnsignedToTailGen(c, buf); }
|static if (is(ucent))
|size_t printUnsignedToTail(ucent c, scope ref dchar[39] buf) { return printUnsignedToTailGen(c, buf); }
|
|size_t printUnsignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted
|{
| static if (T.sizeof == 4)
| {
362| if (c < 10)
| {
172| buf[$ - 1] = cast(char)('0' + c);
172| return 1;
| }
| static assert(N == 10);
| }
| else
| static if (T.sizeof == 8)
| {
6| if (c <= uint.max)
| {
6| return printUnsignedToTail(cast(uint)c, buf[$ - 10 .. $]);
| }
| static assert(N == 20);
| }
| else
| static if (T.sizeof == 16)
| {
| if (c <= ulong.max)
| {
| return printUnsignedToTail(cast(ulong)c, buf[$ - 20 .. $]);
| }
| static assert(N == 39);
| }
| else
| static assert(0);
190| size_t refLen = buf.length;
| do {
547| T nc = c / 10;
547| buf[].ptr[--refLen] = cast(C)('0' + c - nc * 10);
547| c = nc;
| }
547| while(c);
190| return buf.length - refLen;
|}
|
|size_t printUnsignedGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted
|{
0000000| auto ret = printUnsignedToTail(c, buf);
0000000| if (auto shift = buf.length - ret)
| {
0000000| return printBufferShift(ret, shift, buf[].ptr);
| }
0000000| return ret;
|}
|
|nothrow @trusted
|size_t extendASCII(char* from, wchar* to, size_t n)
|{
0000000| foreach (i; 0 .. n)
0000000| to[i] = from[i];
0000000| return n;
|}
|
|nothrow @trusted
|size_t extendASCII(char* from, dchar* to, size_t n)
|{
0000000| foreach (i; 0 .. n)
0000000| to[i] = from[i];
0000000| return n;
|}
|
|version (mir_test) unittest
|{
| import mir.appender;
| import mir.format;
|
1| assert (stringBuf() << 123L << getData == "123");
| static assert (stringBuf() << 123 << getData == "123");
|}
|
|ref W printIntegralZeroImpl(C, size_t N, W, I)(scope return ref W w, I c, size_t zeroLen)
|{
| static if (__traits(isUnsigned, I))
| alias impl = printUnsignedToTail;
| else
| alias impl = printSignedToTail;
| C[N] buf = void;
| size_t n = impl(c, buf);
| static if (!__traits(isUnsigned, I))
| {
| if (c < 0)
| {
| n--;
| w.put(C('-'));
| }
| }
| sizediff_t zeros = zeroLen - n;
| if (zeros > 0)
| {
| do w.put(C('0'));
| while(--zeros);
| }
| w.put(buf[$ - n .. $]);
| return w;
|}
source/mir/format_impl.d is 25% covered
<<<<<< EOF
# path=./source-mir-algebraic_alias-json.lst
|/++
|$(H1 Mutable JSON value)
|
|This module contains a single alias definition and doesn't provide JSON serialization API.
|
|See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf);
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilya Yaroshenko
|Macros:
|+/
|module mir.algebraic_alias.json;
|
|import mir.algebraic: TaggedVariant, This;
|public import mir.string_map: StringMap;
|
|/++
|Definition union for $(LREF JsonAlgebraic).
|+/
|union JsonAlgebraicUnion
|{
| ///
| typeof(null) null_;
| ///
| bool boolean;
| ///
| long integer;
| ///
| double float_;
| ///
| immutable(char)[] string;
| /// Self alias in array.
| This[] array;
| /// Self alias in $(MREF mir,string_map).
| StringMap!This object;
|}
|
|/++
|JSON tagged algebraic alias.
|
|The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic).
|See also $(MREF mir,string_map) - ordered string-value associative array.
|+/
|alias JsonAlgebraic = TaggedVariant!JsonAlgebraicUnion;
|
|///
|unittest
|{
| import mir.ndslice.topology: map;
| import mir.array.allocation: array;
|
1| JsonAlgebraic value;
|
1| StringMap!JsonAlgebraic object;
|
| // Default
1| assert(value.isNull);
1| assert(value.kind == JsonAlgebraic.Kind.null_);
|
| // Boolean
1| value = object["bool"] = true;
1| assert(!value.isNull);
1| assert(value == true);
1| assert(value.kind == JsonAlgebraic.Kind.boolean);
1| assert(value.get!bool == true);
1| assert(value.get!(JsonAlgebraic.Kind.boolean) == true);
|
| // Null
1| value = object["null"] = null;
1| assert(value.isNull);
1| assert(value == null);
1| assert(value.kind == JsonAlgebraic.Kind.null_);
1| assert(value.get!(typeof(null)) == null);
1| assert(value.get!(JsonAlgebraic.Kind.null_) == null);
|
| // String
1| value = object["string"] = "s";
1| assert(value.kind == JsonAlgebraic.Kind.string);
1| assert(value == "s");
1| assert(value.get!string == "s");
1| assert(value.get!(JsonAlgebraic.Kind.string) == "s");
|
| // Integer
1| value = object["integer"] = 4;
1| assert(value.kind == JsonAlgebraic.Kind.integer);
1| assert(value == 4);
1| assert(value != 4.0);
1| assert(value.get!long == 4);
1| assert(value.get!(JsonAlgebraic.Kind.integer) == 4);
|
| // Float
1| value = object["float"] = 3.0;
1| assert(value.kind == JsonAlgebraic.Kind.float_);
1| assert(value != 3);
1| assert(value == 3.0);
1| assert(value.get!double == 3.0);
1| assert(value.get!(JsonAlgebraic.Kind.float_) == 3.0);
|
| // Array
1| JsonAlgebraic[] arr = [0, 1, 2, 3, 4].map!JsonAlgebraic.array;
|
1| value = object["array"] = arr;
1| assert(value.kind == JsonAlgebraic.Kind.array);
1| assert(value == arr);
1| assert(value.get!(JsonAlgebraic[])[3] == 3);
|
| // Object
1| assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]);
1| object.values[0] = "false";
1| assert(object["bool"] == "false"); // it is a string now
1| object.remove("bool"); // remove the member
|
1| value = object["array"] = object;
1| assert(value.kind == JsonAlgebraic.Kind.object);
1| assert(value.get!(StringMap!JsonAlgebraic).keys is object.keys);
1| assert(value.get!(StringMap!JsonAlgebraic).values is object.values);
|
1| JsonAlgebraic[string] aa = object.toAA;
1| object = StringMap!JsonAlgebraic(aa);
|
1| JsonAlgebraic fromAA = ["a" : JsonAlgebraic(3), "b" : JsonAlgebraic("b")];
1| assert(fromAA.get!(StringMap!JsonAlgebraic)["a"] == 3);
1| assert(fromAA.get!(StringMap!JsonAlgebraic)["b"] == "b");
|}
source/mir/algebraic_alias/json.d is 100% covered
<<<<<< EOF
# path=./source-mir-bignum-internal-dec2flt_table.lst
|/++
|Tables of approximations of powers of ten.
|DO NOT MODIFY: Generated by `etc/dec2flt_table.py`
|+/
|module mir.bignum.internal.dec2flt_table;
|
|enum min_p10_e = -512;
|enum max_p10_e = 512;
|
|//
|static immutable align(16) ulong[2][1025] p10_coefficients = [
| [0x9049EE32DB23D21C, 0x7132D332E3F204D5],
| [0xB45C69BF91ECC6A4, 0x8D7F87FF9CEE860A],
| [0xE173842F7667F84C, 0x70DF69FF842A278D],
| [0x8CE8329DAA00FB30, 0xC68BA23FB29A58B8],
| [0xB0223F45148139FC, 0xB82E8ACF9F40EEE6],
| [0xDC2ACF1659A1887B, 0xA63A2D8387112A9F],
| [0x899AC16DF804F54D, 0xA7E45C72346ABAA4],
| [0xAC0171C9760632A0, 0xD1DD738EC185694D],
| [0xD701CE3BD387BF48, 0xC654D07271E6C3A0],
| [0x866120E56434D78D, 0xDBF5024787303A44],
| [0xA7F9691EBD420D70, 0x12F242D968FC48D5],
| [0xD1F7C3666C9290CC, 0x17AED38FC33B5B0A],
| [0x833ADA2003DB9A80, 0x8ECD4439DA0518E6],
| [0xA40990A804D2811F, 0x7280954850865F20],
| [0xCD0BF4D206072167, 0x4F20BA9A64A7F6E8],
| [0x8027790343C474E1, 0x917474A07EE8FA51],
| [0xA031574414B59219, 0xB5D191C89EA338E5],
| [0xC83DAD1519E2F69F, 0xE345F63AC64C071E],
| [0xFA4D185A605BB447, 0x9C1773C977DF08E6],
| [0x9C702F387C3950AC, 0x218EA85DEAEB6590],
| [0xC38C3B069B47A4D7, 0x29F2527565A63EF4],
| [0xF46F49C842198E0D, 0xF46EE712BF0FCEB0],
| [0x98C58E1D294FF8C8, 0x18C5506BB769E12E],
| [0xBEF6F1A473A3F6FA, 0x1EF6A486A544597A],
| [0xEEB4AE0D908CF4B9, 0xA6B44DA84E956FD8],
| [0x9530ECC87A5818F3, 0x6830B089311D65E7],
| [0xBA7D27FA98EE1F30, 0x423CDCAB7D64BF61],
| [0xE91C71F93F29A6FC, 0x52CC13D65CBDEF39],
| [0x91B1C73BC77A085E, 0xB3BF8C65F9F6B584],
| [0xB61E390AB9588A75, 0x20AF6F7F787462E5],
| [0xE3A5C74D67AEAD12, 0x68DB4B5F56917B9E],
| [0x8E479C9060CD2C2C, 0x81890F1B961AED43],
| [0xB1D983B479007736, 0x61EB52E27BA1A893],
| [0xDE4FE4A197409504, 0xFA66279B1A8A12B8],
| [0x8AF1EEE4FE885D22, 0x7C7FD8C0F0964BB3],
| [0xADAE6A9E3E2A746B, 0x1B9FCEF12CBBDEA0],
| [0xD91A0545CDB51186, 0xE287C2AD77EAD648],
| [0x87B0434BA0912AF4, 0xAD94D9AC6AF2C5ED],
| [0xA99C541E88B575B1, 0x98FA101785AF7768],
| [0xD40369262AE2D31D, 0xBF38941D671B5542],
| [0x848221B7DACDC3F2, 0xF7835C9260711549],
| [0xA5A2AA25D18134EE, 0x756433B6F88D5A9C],
| [0xCF0B54AF45E1822A, 0x12BD40A4B6B0B143],
| [0x816714ED8BACF15A, 0x4BB64866F22E6ECA],
| [0xA1C0DA28EE982DB1, 0xDEA3DA80AEBA0A7C],
| [0xCA3110B32A3E391D, 0x164CD120DA688D1B],
| [0xFCBD54DFF4CDC764, 0x5BE005691102B062],
| [0x9DF6550BF9009C9F, 0xB96C0361AAA1AE3D],
| [0xC573EA4EF740C3C6, 0x67C7043A154A19CC],
| [0xF6D0E4E2B510F4B8, 0x01B8C5489A9CA040],
| [0x9A428F0DB12A98F3, 0x01137B4D60A1E428],
| [0xC0D332D11D753F30, 0xC1585A20B8CA5D32],
| [0xF107FF8564D28EFC, 0xB1AE70A8E6FCF47E],
| [0x96A4FFB35F03995D, 0x4F0D0669905E18CF],
| [0xBC4E3FA036C47FB5, 0xA2D04803F4759F02],
| [0xEB61CF8844759FA2, 0xCB845A04F19306C3],
| [0x931D21B52AC983C5, 0x1F32B84316FBE43A],
| [0xB7E46A22757BE4B6, 0x66FF6653DCBADD48],
| [0xE5DD84AB12DADDE4, 0x00BF3FE8D3E9949A],
| [0x8FAA72EAEBC8CAAF, 0x807787F18471FCE1],
| [0xB3950FA5A6BAFD5A, 0x209569EDE58E7C19],
| [0xE07A538F1069BCB1, 0xA8BAC4695EF21B1F],
| [0x8C4C74396A4215EE, 0x6974BAC1DB5750F3],
| [0xAF5F9147C4D29B6A, 0x03D1E972522D2530],
| [0xDB377599B6074245, 0x84C663CEE6B86E7C],
| [0x8902A98011C4896B, 0xD2FBFE615033450E],
| [0xAB4353E01635ABC6, 0x87BAFDF9A4401651],
| [0xD61428D81BC316B7, 0xE9A9BD780D501BE5],
| [0x85CC99871159EE32, 0x520A166B0852116F],
| [0xA73FBFE8D5B069BF, 0xE68C9C05CA6695CB],
| [0xD10FAFE30B1C842F, 0xA02FC3073D003B3E],
| [0x82A9CDEDE6F1D29D, 0x241DD9E486202507],
| [0xA354416960AE4744, 0x6D25505DA7A82E48],
| [0xCC2951C3B8D9D916, 0x886EA475119239DA],
| [0xFF33A634A7104F5B, 0xEA8A4D9255F6C851],
| [0x9F8047E0E86A3199, 0xD296707B75BA3D33],
| [0xC76059D92284BDFF, 0x073C0C9A5328CC7F],
| [0xF938704F6B25ED7F, 0xC90B0FC0E7F2FF9F],
| [0x9BC34631A2F7B46F, 0x3DA6E9D890F7DFC3],
| [0xC2B417BE0BB5A18B, 0x0D10A44EB535D7B4],
| [0xF3611DAD8EA309EE, 0xD054CD6262834DA1],
| [0x981CB28C7925E635, 0xA235005D7D921085],
| [0xBE23DF2F976F5FC2, 0xCAC24074DCF694A6],
| [0xEDACD6FB7D4B37B2, 0x3D72D092143439D0],
| [0x948C065D2E4F02CF, 0x6667C25B4CA0A422],
| [0xB9AF07F479E2C383, 0x4001B2F21FC8CD2A],
| [0xE81AC9F1985B7464, 0x10021FAEA7BB0075],
| [0x9110BE36FF3928BF, 0x8A0153CD28D4E049],
| [0xB554EDC4BF0772EE, 0x2C81A8C0730A185B],
| [0xE2AA2935EEC94FAA, 0xB7A212F08FCC9E72],
| [0x8DAA59C1B53DD1CA, 0x12C54BD659DFE307],
| [0xB114F032228D463D, 0x97769ECBF057DBC9],
| [0xDD5A2C3EAB3097CC, 0xBD54467EEC6DD2BB],
| [0x8A585BA72AFE5EDF, 0x5654AC0F53C4A3B5],
| [0xACEE7290F5BDF697, 0x2BE9D71328B5CCA2],
| [0xD82A0F35332D743D, 0xF6E44CD7F2E33FCB],
| [0x871A49813FFC68A6, 0x1A4EB006F7CE07DF],
| [0xA8E0DBE18FFB82D0, 0xA0E25C08B5C189D7],
| [0xD31912D9F3FA6384, 0x891AF30AE331EC4C],
| [0x83EFABC8387C7E32, 0x35B0D7E6CDFF33B0],
| [0xA4EB96BA469B9DBF, 0xC31D0DE0817F009C],
| [0xCE267C68D842852E, 0x73E45158A1DEC0C2],
| [0x80D80DC18729933D, 0x086EB2D7652B387A],
| [0xA10E1131E8F3F80C, 0x4A8A5F8D3E760698],
| [0xC951957E6330F60F, 0x5D2CF7708E13883E],
| [0xFBA5FADDFBFD3393, 0x3478354CB1986A4D],
| [0x9D47BCCABD7E403C, 0x00CB214FEEFF4270],
| [0xC499ABFD6CDDD04B, 0x00FDE9A3EABF130C],
| [0xF5C016FCC815445E, 0xC13D640CE56ED7D0],
| [0x99980E5DFD0D4ABB, 0x98C65E880F6546E2],
| [0xBFFE11F57C509D69, 0x3EF7F62A133E989A],
| [0xEFFD9672DB64C4C4, 0x8EB5F3B4980E3EC1],
| [0x95FE7E07C91EFAFA, 0x3931B850DF08E738],
| [0xBB7E1D89BB66B9B9, 0xC77E266516CB2106],
| [0xEA5DA4EC2A406827, 0xF95DAFFE5C7DE948],
| [0x927A87139A684118, 0x5BDA8DFEF9CEB1CD],
| [0xB71928D88102515E, 0x72D1317EB8425E40],
| [0xE4DF730EA142E5B6, 0x0F857DDE6652F5D0],
| [0x8F0BA7E924C9CF92, 0xC9B36EAAFFF3D9A2],
| [0xB2CE91E36DFC4376, 0x3C204A55BFF0D00B],
| [0xDF82365C497B5454, 0xCB285CEB2FED040E],
| [0x8BB161F9ADED14B4, 0x5EF93A12FDF42288],
| [0xAE9DBA78196859E1, 0x76B78897BD712B2B],
| [0xDA4529161FC2705A, 0xD4656ABDACCD75F5],
| [0x886B39ADD3D98638, 0x24BF62B68C0069B9],
| [0xAA86081948CFE7C6, 0x2DEF3B642F008428],
| [0xD5278A1F9B03E1B8, 0xB96B0A3D3AC0A531],
| [0x8538B653C0E26D13, 0xD3E2E66644B8673F],
| [0xA686E3E8B11B0858, 0x88DB9FFFD5E6810F],
| [0xD0289CE2DD61CA6D, 0x6B1287FFCB602152],
| [0x8219620DCA5D1E84, 0x62EB94FFDF1C14D3],
| [0xA29FBA913CF46625, 0x7BA67A3FD6E31A08],
| [0xCB47A9358C317FAF, 0xDA9018CFCC9BE08A],
| [0xFE199382EF3DDF9B, 0x91341F03BFC2D8AD],
| [0x9ECFFC31D586ABC1, 0x9AC0936257D9C76C],
| [0xC683FB3E4AE856B1, 0xC170B83AEDD03947],
| [0xF824FA0DDDA26C5D, 0xF1CCE649A9444799],
| [0x9B171C48AA8583BA, 0x17200FEE09CAACC0],
| [0xC1DCE35AD526E4A9, 0x9CE813E98C3D57EF],
| [0xF2541C318A709DD3, 0xC42218E3EF4CADEB],
| [0x9774919EF68662A4, 0xBA954F8E758FECB3],
| [0xBD51B606B427FB4D, 0xA93AA37212F3E7E0],
| [0xECA623886131FA20, 0xD3894C4E97B0E1D8],
| [0x93E7D6353CBF3C54, 0xE435CFB11ECE8D27],
| [0xB8E1CBC28BEF0B69, 0xDD43439D66823071],
| [0xE71A3EB32EEACE43, 0x14941484C022BC8D],
| [0x9070672FFD52C0EA, 0xECDC8CD2F815B5D8],
| [0xB48C80FBFCA77124, 0x6813B007B61B234E],
| [0xE1AFA13AFBD14D6E, 0x82189C09A3A1EC21],
| [0x8D0DC4C4DD62D064, 0x714F618606453395],
| [0xB05135F614BB847E, 0x8DA339E787D6807A],
| [0xDC65837399EA659D, 0xF10C086169CC2099],
| [0x89BF722840327F82, 0x16A7853CE21F945F],
| [0xAC2F4EB2503F1F63, 0x9C51668C1AA77977],
| [0xD73B225EE44EE73B, 0x4365C02F215157D5],
| [0x8684F57B4EB15085, 0x0A1F981D74D2D6E5],
| [0xA82632DA225DA4A6, 0x4CA77E24D2078C9E],
| [0xD22FBF90AAF50DD0, 0xDFD15DAE06896FC6],
| [0x835DD7BA6AD928A2, 0xEBE2DA8CC415E5DC],
| [0xA4354DA9058F72CA, 0x66DB912FF51B5F53],
| [0xCD42A11346F34F7D, 0x0092757BF2623727],
| [0x8049A4AC0C5811AE, 0x205B896D777D6279],
| [0xA05C0DD70F6E161A, 0xA8726BC8D55CBB17],
| [0xC873114CD3499BA0, 0x128F06BB0AB3E9DD],
| [0xFA8FD5A0081C0288, 0x1732C869CD60E454],
| [0x9C99E58405118195, 0x0E7FBD42205C8EB4],
| [0xC3C05EE50655E1FA, 0x521FAC92A873B261],
| [0xF4B0769E47EB5A79, 0xE6A797B752909EFA],
| [0x98EE4A22ECF3188C, 0x9028BED2939A635C],
| [0xBF29DCABA82FDEAE, 0x7432EE873880FC33],
| [0xEEF453D6923BD65A, 0x113FAA2906A13B40],
| [0x9558B4661B6565F8, 0x4AC7CA59A424C508],
| [0xBAAEE17FA23EBF76, 0x5D79BCF00D2DF64A],
| [0xE95A99DF8ACE6F54, 0xF4D82C2C107973DC],
| [0x91D8A02BB6C10594, 0x79071B9B8A4BE86A],
| [0xB64EC836A47146FA, 0x9748E2826CDEE284],
| [0xE3E27A444D8D98B8, 0xFD1B1B2308169B25],
| [0x8E6D8C6AB0787F73, 0xFE30F0F5E50E20F7],
| [0xB208EF855C969F50, 0xBDBD2D335E51A935],
| [0xDE8B2B66B3BC4724, 0xAD2C788035E61382],
| [0x8B16FB203055AC76, 0x4C3BCB5021AFCC31],
| [0xADDCB9E83C6B1794, 0xDF4ABE242A1BBF3E],
| [0xD953E8624B85DD79, 0xD71D6DAD34A2AF0D],
| [0x87D4713D6F33AA6C, 0x8672648C40E5AD68],
| [0xA9C98D8CCB009506, 0x680EFDAF511F18C2],
| [0xD43BF0EFFDC0BA48, 0x0212BD1B2566DEF3],
| [0x84A57695FE98746D, 0x014BB630F7604B58],
| [0xA5CED43B7E3E9188, 0x419EA3BD35385E2E],
| [0xCF42894A5DCE35EA, 0x52064CAC828675B9],
| [0x818995CE7AA0E1B2, 0x7343EFEBD1940994],
| [0xA1EBFB4219491A1F, 0x1014EBE6C5F90BF9],
| [0xCA66FA129F9B60A7, 0xD41A26E077774EF7],
| [0xFD00B897478238D1, 0x8920B098955522B5],
| [0x9E20735E8CB16382, 0x55B46E5F5D5535B1],
| [0xC5A890362FDDBC63, 0xEB2189F734AA831D],
| [0xF712B443BBD52B7C, 0xA5E9EC7501D523E4],
| [0x9A6BB0AA55653B2D, 0x47B233C92125366F],
| [0xC1069CD4EABE89F9, 0x999EC0BB696E840A],
| [0xF148440A256E2C77, 0xC00670EA43CA250D],
| [0x96CD2A865764DBCA, 0x380406926A5E5728],
| [0xBC807527ED3E12BD, 0xC605083704F5ECF2],
| [0xEBA09271E88D976C, 0xF7864A44C633682F],
| [0x93445B8731587EA3, 0x7AB3EE6AFBE0211D],
| [0xB8157268FDAE9E4C, 0x5960EA05BAD82965],
| [0xE61ACF033D1A45DF, 0x6FB92487298E33BE],
| [0x8FD0C16206306BAC, 0xA5D3B6D479F8E057],
| [0xB3C4F1BA87BC8697, 0x8F48A4899877186C],
| [0xE0B62E2929ABA83C, 0x331ACDABFE94DE87],
| [0x8C71DCD9BA0B4926, 0x9FF0C08B7F1D0B15],
| [0xAF8E5410288E1B6F, 0x07ECF0AE5EE44DDA],
| [0xDB71E91432B1A24B, 0xC9E82CD9F69D6150],
| [0x892731AC9FAF056F, 0xBE311C083A225CD2],
| [0xAB70FE17C79AC6CA, 0x6DBD630A48AAF407],
| [0xD64D3D9DB981787D, 0x092CBBCCDAD5B108],
| [0x85F0468293F0EB4E, 0x25BBF56008C58EA5],
| [0xA76C582338ED2622, 0xAF2AF2B80AF6F24E],
| [0xD1476E2C07286FAA, 0x1AF5AF660DB4AEE2],
| [0x82CCA4DB847945CA, 0x50D98D9FC890ED4D],
| [0xA37FCE126597973D, 0xE50FF107BAB528A1],
| [0xCC5FC196FEFD7D0C, 0x1E53ED49A96272C9],
| [0xFF77B1FCBEBCDC4F, 0x25E8E89C13BB0F7B],
| [0x9FAACF3DF73609B1, 0x77B191618C54E9AD],
| [0xC795830D75038C1E, 0xD59DF5B9EF6A2418],
| [0xF97AE3D0D2446F25, 0x4B0573286B44AD1E],
| [0x9BECCE62836AC577, 0x4EE367F9430AEC33],
| [0xC2E801FB244576D5, 0x229C41F793CDA73F],
| [0xF3A20279ED56D48A, 0x6B43527578C1110F],
| [0x9845418C345644D7, 0x830A13896B78AAAA],
| [0xBE5691EF416BD60C, 0x23CC986BC656D554],
| [0xEDEC366B11C6CB8F, 0x2CBFBE86B7EC8AA9],
| [0x94B3A202EB1C3F39, 0x7BF7D71432F3D6AA],
| [0xB9E08A83A5E34F08, 0xDAF5CCD93FB0CC54],
| [0xE858AD248F5C22CA, 0xD1B3400F8F9CFF69],
| [0x91376C36D99995BE, 0x23100809B9C21FA2],
| [0xB58547448FFFFB2E, 0xABD40A0C2832A78A],
| [0xE2E69915B3FFF9F9, 0x16C90C8F323F516D],
| [0x8DD01FAD907FFC3C, 0xAE3DA7D97F6792E4],
| [0xB1442798F49FFB4B, 0x99CD11CFDF41779D],
| [0xDD95317F31C7FA1D, 0x40405643D711D584],
| [0x8A7D3EEF7F1CFC52, 0x482835EA666B2572],
| [0xAD1C8EAB5EE43B67, 0xDA3243650005EECF],
| [0xD863B256369D4A41, 0x90BED43E40076A83],
| [0x873E4F75E2224E68, 0x5A7744A6E804A292],
| [0xA90DE3535AAAE202, 0x711515D0A205CB36],
| [0xD3515C2831559A83, 0x0D5A5B44CA873E04],
| [0x8412D9991ED58092, 0xE858790AFE9486C2],
| [0xA5178FFF668AE0B6, 0x626E974DBE39A873],
| [0xCE5D73FF402D98E4, 0xFB0A3D212DC81290],
| [0x80FA687F881C7F8E, 0x7CE66634BC9D0B9A],
| [0xA139029F6A239F72, 0x1C1FFFC1EBC44E80],
| [0xC987434744AC874F, 0xA327FFB266B56220],
| [0xFBE9141915D7A922, 0x4BF1FF9F0062BAA8],
| [0x9D71AC8FADA6C9B5, 0x6F773FC3603DB4A9],
| [0xC4CE17B399107C23, 0xCB550FB4384D21D4],
| [0xF6019DA07F549B2B, 0x7E2A53A146606A48],
| [0x99C102844F94E0FB, 0x2EDA7444CBFC426D],
| [0xC0314325637A193A, 0xFA911155FEFB5309],
| [0xF03D93EEBC589F88, 0x793555AB7EBA27CB],
| [0x96267C7535B763B5, 0x4BC1558B2F3458DF],
| [0xBBB01B9283253CA3, 0x9EB1AAEDFB016F16],
| [0xEA9C227723EE8BCB, 0x465E15A979C1CADC],
| [0x92A1958A7675175F, 0x0BFACD89EC191ECA],
| [0xB749FAED14125D37, 0xCEF980EC671F667C],
| [0xE51C79A85916F485, 0x82B7E12780E7401B],
| [0x8F31CC0937AE58D3, 0xD1B2ECB8B0908811],
| [0xB2FE3F0B8599EF08, 0x861FA7E6DCB4AA15],
| [0xDFBDCECE67006AC9, 0x67A791E093E1D49A],
| [0x8BD6A141006042BE, 0xE0C8BB2C5C6D24E0],
| [0xAECC49914078536D, 0x58FAE9F773886E19],
| [0xDA7F5BF590966849, 0xAF39A475506A899F],
| [0x888F99797A5E012D, 0x6D8406C952429603],
| [0xAAB37FD7D8F58179, 0xC8E5087BA6D33B84],
| [0xD5605FCDCF32E1D7, 0xFB1E4A9A90880A65],
| [0x855C3BE0A17FCD26, 0x5CF2EEA09A55067F],
| [0xA6B34AD8C9DFC070, 0xF42FAA48C0EA481F],
| [0xD0601D8EFC57B08C, 0xF13B94DAF124DA27],
| [0x823C12795DB6CE57, 0x76C53D08D6B70858],
| [0xA2CB1717B52481ED, 0x54768C4B0C64CA6E],
| [0xCB7DDCDDA26DA269, 0xA9942F5DCF7DFD0A],
| [0xFE5D54150B090B03, 0xD3F93B35435D7C4C],
| [0x9EFA548D26E5A6E2, 0xC47BC5014A1A6DB0],
| [0xC6B8E9B0709F109A, 0x359AB6419CA1091B],
| [0xF867241C8CC6D4C1, 0xC30163D203C94B62],
| [0x9B407691D7FC44F8, 0x79E0DE63425DCF1D],
| [0xC21094364DFB5637, 0x985915FC12F542E5],
| [0xF294B943E17A2BC4, 0x3E6F5B7B17B2939E],
| [0x979CF3CA6CEC5B5B, 0xA705992CEECF9C43],
| [0xBD8430BD08277231, 0x50C6FF782A838353],
| [0xECE53CEC4A314EBE, 0xA4F8BF5635246428],
| [0x940F4613AE5ED137, 0x871B7795E136BE99],
| [0xB913179899F68584, 0x28E2557B59846E3F],
| [0xE757DD7EC07426E5, 0x331AEADA2FE589CF],
| [0x9096EA6F3848984F, 0x3FF0D2C85DEF7622],
| [0xB4BCA50B065ABE63, 0x0FED077A756B53AA],
| [0xE1EBCE4DC7F16DFC, 0xD3E8495912C62894],
| [0x8D3360F09CF6E4BD, 0x64712DD7ABBBD95D],
| [0xB080392CC4349DED, 0xBD8D794D96AACFB4],
| [0xDCA04777F541C568, 0xECF0D7A0FC5583A1],
| [0x89E42CAAF9491B61, 0xF41686C49DB57245],
| [0xAC5D37D5B79B6239, 0x311C2875C522CED6],
| [0xD77485CB25823AC7, 0x7D633293366B828B],
| [0x86A8D39EF77164BD, 0xAE5DFF9C02033197],
| [0xA8530886B54DBDEC, 0xD9F57F830283FDFD],
| [0xD267CAA862A12D67, 0xD072DF63C324FD7C],
| [0x8380DEA93DA4BC60, 0x4247CB9E59F71E6D],
| [0xA46116538D0DEB78, 0x52D9BE85F074E609],
| [0xCD795BE870516656, 0x67902E276C921F8B],
| [0x806BD9714632DFF6, 0x00BA1CD8A3DB53B7],
| [0xA086CFCD97BF97F4, 0x80E8A40ECCD228A5],
| [0xC8A883C0FDAF7DF0, 0x6122CD128006B2CE],
| [0xFAD2A4B13D1B5D6C, 0x796B805720085F81],
| [0x9CC3A6EEC6311A64, 0xCBE3303674053BB1],
| [0xC3F490AA77BD60FD, 0xBEDBFC4411068A9D],
| [0xF4F1B4D515ACB93C, 0xEE92FB5515482D44],
| [0x991711052D8BF3C5, 0x751BDD152D4D1C4B],
| [0xBF5CD54678EEF0B7, 0xD262D45A78A0635D],
| [0xEF340A98172AACE5, 0x86FB897116C87C35],
| [0x9580869F0E7AAC0F, 0xD45D35E6AE3D4DA1],
| [0xBAE0A846D2195713, 0x8974836059CCA109],
| [0xE998D258869FACD7, 0x2BD1A438703FC94B],
| [0x91FF83775423CC06, 0x7B6306A34627DDCF],
| [0xB67F6455292CBF08, 0x1A3BC84C17B1D543],
| [0xE41F3D6A7377EECA, 0x20CABA5F1D9E4A94],
| [0x8E938662882AF53E, 0x547EB47B7282EE9C],
| [0xB23867FB2A35B28E, 0xE99E619A4F23AA43],
| [0xDEC681F9F4C31F31, 0x6405FA00E2EC94D4],
| [0x8B3C113C38F9F37F, 0xDE83BC408DD3DD05],
| [0xAE0B158B4738705F, 0x9624AB50B148D446],
| [0xD98DDAEE19068C76, 0x3BADD624DD9B0957],
| [0x87F8A8D4CFA417CA, 0xE54CA5D70A80E5D6],
| [0xA9F6D30A038D1DBC, 0x5E9FCF4CCD211F4C],
| [0xD47487CC8470652B, 0x7647C3200069671F],
| [0x84C8D4DFD2C63F3B, 0x29ECD9F40041E073],
| [0xA5FB0A17C777CF0A, 0xF468107100525890],
| [0xCF79CC9DB955C2CC, 0x7182148D4066EEB4],
| [0x81AC1FE293D599C0, 0xC6F14CD848405531],
| [0xA21727DB38CB0030, 0xB8ADA00E5A506A7D],
| [0xCA9CF1D206FDC03C, 0xA6D90811F0E4851C],
| [0xFD442E4688BD304B, 0x908F4A166D1DA663],
| [0x9E4A9CEC15763E2F, 0x9A598E4E043287FE],
| [0xC5DD44271AD3CDBA, 0x40EFF1E1853F29FE],
| [0xF7549530E188C129, 0xD12BEE59E68EF47D],
| [0x9A94DD3E8CF578BA, 0x82BB74F8301958CE],
| [0xC13A148E3032D6E8, 0xE36A52363C1FAF02],
| [0xF18899B1BC3F8CA2, 0xDC44E6C3CB279AC2],
| [0x96F5600F15A7B7E5, 0x29AB103A5EF8C0B9],
| [0xBCB2B812DB11A5DE, 0x7415D448F6B6F0E8],
| [0xEBDF661791D60F56, 0x111B495B3464AD21],
| [0x936B9FCEBB25C996, 0xCAB10DD900BEEC35],
| [0xB84687C269EF3BFB, 0x3D5D514F40EEA742],
| [0xE65829B3046B0AFA, 0x0CB4A5A3112A5113],
| [0x8FF71A0FE2C2E6DC, 0x47F0E785EABA72AC],
| [0xB3F4E093DB73A093, 0x59ED216765690F57],
| [0xE0F218B8D25088B8, 0x306869C13EC3532C],
| [0x8C974F7383725573, 0x1E414218C73A13FC],
| [0xAFBD2350644EEAD0, 0xE5D1929EF90898FB],
| [0xDBAC6C247D62A584, 0xDF45F746B74ABF39],
| [0x894BC396CE5DA772, 0x6B8BBA8C328EB784],
| [0xAB9EB47C81F5114F, 0x066EA92F3F326565],
| [0xD686619BA27255A3, 0xC80A537B0EFEFEBE],
| [0x8613FD0145877586, 0xBD06742CE95F5F37],
| [0xA798FC4196E952E7, 0x2C48113823B73704],
| [0xD17F3B51FCA3A7A1, 0xF75A15862CA504C5],
| [0x82EF85133DE648C5, 0x9A984D73DBE722FB],
| [0xA3AB66580D5FDAF6, 0xC13E60D0D2E0EBBA],
| [0xCC963FEE10B7D1B3, 0x318DF905079926A9],
| [0xFFBBCFE994E5C620, 0xFDF17746497F7053],
| [0x9FD561F1FD0F9BD4, 0xFEB6EA8BEDEFA634],
| [0xC7CABA6E7C5382C9, 0xFE64A52EE96B8FC1],
| [0xF9BD690A1B68637B, 0x3DFDCE7AA3C673B1],
| [0x9C1661A651213E2D, 0x06BEA10CA65C084F],
| [0xC31BFA0FE5698DB8, 0x486E494FCFF30A62],
| [0xF3E2F893DEC3F126, 0x5A89DBA3C3EFCCFB],
| [0x986DDB5C6B3A76B8, 0xF89629465A75E01D],
| [0xBE89523386091466, 0xF6BBB397F1135824],
| [0xEE2BA6C0678B597F, 0x746AA07DED582E2D],
| [0x94DB483840B717F0, 0xA8C2A44EB4571CDC],
| [0xBA121A4650E4DDEC, 0x92F34D62616CE413],
| [0xE896A0D7E51E1566, 0x77B020BAF9C81D18],
| [0x915E2486EF32CD60, 0x0ACE1474DC1D122F],
| [0xB5B5ADA8AAFF80B8, 0x0D819992132456BB],
| [0xE3231912D5BF60E6, 0x10E1FFF697ED6C69],
| [0x8DF5EFABC5979C90, 0xCA8D3FFA1EF463C2],
| [0xB1736B96B6FD83B4, 0xBD308FF8A6B17CB2],
| [0xDDD0467C64BCE4A1, 0xAC7CB3F6D05DDBDF],
| [0x8AA22C0DBEF60EE4, 0x6BCDF07A423AA96B],
| [0xAD4AB7112EB3929E, 0x86C16C98D2C953C6],
| [0xD89D64D57A607745, 0xE871C7BF077BA8B8],
| [0x87625F056C7C4A8B, 0x11471CD764AD4973],
| [0xA93AF6C6C79B5D2E, 0xD598E40D3DD89BCF],
| [0xD389B47879823479, 0x4AFF1D108D4EC2C3],
| [0x843610CB4BF160CC, 0xCEDF722A585139BA],
| [0xA54394FE1EEDB8FF, 0xC2974EB4EE658829],
| [0xCE947A3DA6A9273E, 0x733D226229FEEA33],
| [0x811CCC668829B887, 0x0806357D5A3F5260],
| [0xA163FF802A3426A9, 0xCA07C2DCB0CF26F8],
| [0xC9BCFF6034C13053, 0xFC89B393DD02F0B6],
| [0xFC2C3F3841F17C68, 0xBBAC2078D443ACE3],
| [0x9D9BA7832936EDC1, 0xD54B944B84AA4C0E],
| [0xC5029163F384A931, 0x0A9E795E65D4DF11],
| [0xF64335BCF065D37D, 0x4D4617B5FF4A16D6],
| [0x99EA0196163FA42E, 0x504BCED1BF8E4E46],
| [0xC06481FB9BCF8D3A, 0xE45EC2862F71E1D7],
| [0xF07DA27A82C37088, 0x5D767327BB4E5A4D],
| [0x964E858C91BA2655, 0x3A6A07F8D510F870],
| [0xBBE226EFB628AFEB, 0x890489F70A55368C],
| [0xEADAB0ABA3B2DBE5, 0x2B45AC74CCEA842F],
| [0x92C8AE6B464FC96F, 0x3B0B8BC90012929D],
| [0xB77ADA0617E3BBCB, 0x09CE6EBB40173745],
| [0xE55990879DDCAABE, 0xCC420A6A101D0516],
| [0x8F57FA54C2A9EAB7, 0x9FA946824A12232E],
| [0xB32DF8E9F3546564, 0x47939822DC96ABF9],
| [0xDFF9772470297EBD, 0x59787E2B93BC56F7],
| [0x8BFBEA76C619EF36, 0x57EB4EDB3C55B65B],
| [0xAEFAE51477A06B04, 0xEDE622920B6B23F1],
| [0xDAB99E59958885C5, 0xE95FAB368E45ECED],
| [0x88B402F7FD75539B, 0x11DBCB0218EBB414],
| [0xAAE103B5FCD2A882, 0xD652BDC29F26A11A],
| [0xD59944A37C0752A2, 0x4BE76D3346F04960],
| [0x857FCAE62D8493A5, 0x6F70A4400C562DDC],
| [0xA6DFBD9FB8E5B88F, 0xCB4CCD500F6BB953],
| [0xD097AD07A71F26B2, 0x7E2000A41346A7A8],
| [0x825ECC24C8737830, 0x8ED400668C0C28C9],
| [0xA2F67F2DFA90563B, 0x728900802F0F32FB],
| [0xCBB41EF979346BCA, 0x4F2B40A03AD2FFBA],
| [0xFEA126B7D78186BD, 0xE2F610C84987BFA8],
| [0x9F24B832E6B0F436, 0x0DD9CA7D2DF4D7C9],
| [0xC6EDE63FA05D3144, 0x91503D1C79720DBB],
| [0xF8A95FCF88747D94, 0x75A44C6397CE912A],
| [0x9B69DBE1B548CE7D, 0xC986AFBE3EE11ABA],
| [0xC24452DA229B021C, 0xFBE85BADCE996169],
| [0xF2D56790AB41C2A3, 0xFAE27299423FB9C3],
| [0x97C560BA6B0919A6, 0xDCCD879FC967D41A],
| [0xBDB6B8E905CB600F, 0x5400E987BBC1C921],
| [0xED246723473E3813, 0x290123E9AAB23B69],
| [0x9436C0760C86E30C, 0xF9A0B6720AAF6521],
| [0xB94470938FA89BCF, 0xF808E40E8D5B3E6A],
| [0xE7958CB87392C2C3, 0xB60B1D1230B20E04],
| [0x90BD77F3483BB9BA, 0xB1C6F22B5E6F48C3],
| [0xB4ECD5F01A4AA828, 0x1E38AEB6360B1AF3],
| [0xE2280B6C20DD5232, 0x25C6DA63C38DE1B0],
| [0x8D590723948A535F, 0x579C487E5A38AD0E],
| [0xB0AF48EC79ACE837, 0x2D835A9DF0C6D852],
| [0xDCDB1B2798182245, 0xF8E431456CF88E66],
| [0x8A08F0F8BF0F156B, 0x1B8E9ECB641B5900],
| [0xAC8B2D36EED2DAC6, 0xE272467E3D222F40],
| [0xD7ADF884AA879177, 0x5B0ED81DCC6ABB10],
| [0x86CCBB52EA94BAEB, 0x98E947129FC2B4EA],
| [0xA87FEA27A539E9A5, 0x3F2398D747B36224],
| [0xD29FE4B18E88640F, 0x8EEC7F0D19A03AAD],
| [0x83A3EEEEF9153E89, 0x1953CF68300424AC],
| [0xA48CEAAAB75A8E2B, 0x5FA8C3423C052DD7],
| [0xCDB02555653131B6, 0x3792F412CB06794D],
| [0x808E17555F3EBF12, 0xE2BBD88BBEE40BD0],
| [0xA0B19D2AB70E6ED6, 0x5B6ACEAEAE9D0EC4],
| [0xC8DE047564D20A8C, 0xF245825A5A445275],
| [0xFB158592BE068D2F, 0xEED6E2F0F0D56713],
| [0x9CED737BB6C4183D, 0x55464DD69685606C],
| [0xC428D05AA4751E4D, 0xAA97E14C3C26B887],
| [0xF53304714D9265E0, 0xD53DD99F4B3066A8],
| [0x993FE2C6D07B7FAC, 0xE546A8038EFE4029],
| [0xBF8FDB78849A5F97, 0xDE98520472BDD033],
| [0xEF73D256A5C0F77D, 0x963E66858F6D4440],
| [0x95A8637627989AAE, 0xDDE7001379A44AA8],
| [0xBB127C53B17EC159, 0x5560C018580D5D52],
| [0xE9D71B689DDE71B0, 0xAAB8F01E6E10B4A7],
| [0x9226712162AB070E, 0xCAB3961304CA70E8],
| [0xB6B00D69BB55C8D1, 0x3D607B97C5FD0D22],
| [0xE45C10C42A2B3B06, 0x8CB89A7DB77C506B],
| [0x8EB98A7A9A5B04E3, 0x77F3608E92ADB243],
| [0xB267ED1940F1C61C, 0x55F038B237591ED3],
| [0xDF01E85F912E37A3, 0x6B6C46DEC52F6688],
| [0x8B61313BBABCE2C6, 0x2323AC4B3B3DA015],
| [0xAE397D8AA96C1B78, 0xABEC975E0A0D081B],
| [0xD9C7DCED53C72256, 0x96E7BD358C904A21],
| [0x881CEA14545C7575, 0x7E50D64177DA2E55],
| [0xAA242499697392D3, 0xDDE50BD1D5D0B9EA],
| [0xD4AD2DBFC3D07788, 0x955E4EC64B44E864],
| [0x84EC3C97DA624AB5, 0xBD5AF13BEF0B113F],
| [0xA6274BBDD0FADD62, 0xECB1AD8AEACDD58E],
| [0xCFB11EAD453994BA, 0x67DE18EDA5814AF2],
| [0x81CEB32C4B43FCF5, 0x80EACF948770CED7],
| [0xA2425FF75E14FC32, 0xA1258379A94D028D],
| [0xCAD2F7F5359A3B3E, 0x096EE45813A04330],
| [0xFD87B5F28300CA0E, 0x8BCA9D6E188853FC],
| [0x9E74D1B791E07E48, 0x775EA264CF55347E],
| [0xC612062576589DDB, 0x95364AFE032A819D],
| [0xF79687AED3EEC551, 0x3A83DDBD83F52205],
| [0x9ABE14CD44753B53, 0xC4926A9672793543],
| [0xC16D9A0095928A27, 0x75B7053C0F178294],
| [0xF1C90080BAF72CB1, 0x5324C68B12DD6338],
| [0x971DA05074DA7BEF, 0xD3F6FC16EBCA5E03],
| [0xBCE5086492111AEB, 0x88F4BB1CA6BCF584],
| [0xEC1E4A7DB69561A5, 0x2B31E9E3D06C32E5],
| [0x9392EE8E921D5D07, 0x3AFF322E62439FCF],
| [0xB877AA3236A4B449, 0x09BEFEB9FAD487C3],
| [0xE69594BEC44DE15B, 0x4C2EBE687989A9B4],
| [0x901D7CF73AB0ACD9, 0x0F9D37014BF60A10],
| [0xB424DC35095CD80F, 0x538484C19EF38C94],
| [0xE12E13424BB40E13, 0x2865A5F206B06FBA],
| [0x8CBCCC096F5088CC, 0xF93F87B7442E45D4],
| [0xAFEBFF0BCB24AAFF, 0xF78F69A51539D749],
| [0xDBE6FECEBDEDD5BF, 0xB573440E5A884D1B],
| [0x89705F4136B4A597, 0x31680A88F8953031],
| [0xABCC77118461CEFD, 0xFDC20D2B36BA7C3D],
| [0xD6BF94D5E57A42BC, 0x3D32907604691B4D],
| [0x8637BD05AF6C69B6, 0xA63F9A49C2C1B110],
| [0xA7C5AC471B478423, 0x0FCF80DC33721D54],
| [0xD1B71758E219652C, 0xD3C36113404EA4A9],
| [0x83126E978D4FDF3B, 0x645A1CAC083126E9],
| [0xA3D70A3D70A3D70A, 0x3D70A3D70A3D70A4],
| [0xCCCCCCCCCCCCCCCD, 0xCCCCCCCCCCCCCCCD],
| [0x8000000000000000, 0x0000000000000000],
| [0xA000000000000000, 0x0000000000000000],
| [0xC800000000000000, 0x0000000000000000],
| [0xFA00000000000000, 0x0000000000000000],
| [0x9C40000000000000, 0x0000000000000000],
| [0xC350000000000000, 0x0000000000000000],
| [0xF424000000000000, 0x0000000000000000],
| [0x9896800000000000, 0x0000000000000000],
| [0xBEBC200000000000, 0x0000000000000000],
| [0xEE6B280000000000, 0x0000000000000000],
| [0x9502F90000000000, 0x0000000000000000],
| [0xBA43B74000000000, 0x0000000000000000],
| [0xE8D4A51000000000, 0x0000000000000000],
| [0x9184E72A00000000, 0x0000000000000000],
| [0xB5E620F480000000, 0x0000000000000000],
| [0xE35FA931A0000000, 0x0000000000000000],
| [0x8E1BC9BF04000000, 0x0000000000000000],
| [0xB1A2BC2EC5000000, 0x0000000000000000],
| [0xDE0B6B3A76400000, 0x0000000000000000],
| [0x8AC7230489E80000, 0x0000000000000000],
| [0xAD78EBC5AC620000, 0x0000000000000000],
| [0xD8D726B7177A8000, 0x0000000000000000],
| [0x878678326EAC9000, 0x0000000000000000],
| [0xA968163F0A57B400, 0x0000000000000000],
| [0xD3C21BCECCEDA100, 0x0000000000000000],
| [0x84595161401484A0, 0x0000000000000000],
| [0xA56FA5B99019A5C8, 0x0000000000000000],
| [0xCECB8F27F4200F3A, 0x0000000000000000],
| [0x813F3978F8940984, 0x4000000000000000],
| [0xA18F07D736B90BE5, 0x5000000000000000],
| [0xC9F2C9CD04674EDF, 0xA400000000000000],
| [0xFC6F7C4045812296, 0x4D00000000000000],
| [0x9DC5ADA82B70B59E, 0xF020000000000000],
| [0xC5371912364CE305, 0x6C28000000000000],
| [0xF684DF56C3E01BC7, 0xC732000000000000],
| [0x9A130B963A6C115C, 0x3C7F400000000000],
| [0xC097CE7BC90715B3, 0x4B9F100000000000],
| [0xF0BDC21ABB48DB20, 0x1E86D40000000000],
| [0x96769950B50D88F4, 0x1314448000000000],
| [0xBC143FA4E250EB31, 0x17D955A000000000],
| [0xEB194F8E1AE525FD, 0x5DCFAB0800000000],
| [0x92EFD1B8D0CF37BE, 0x5AA1CAE500000000],
| [0xB7ABC627050305AE, 0xF14A3D9E40000000],
| [0xE596B7B0C643C719, 0x6D9CCD05D0000000],
| [0x8F7E32CE7BEA5C70, 0xE4820023A2000000],
| [0xB35DBF821AE4F38C, 0xDDA2802C8A800000],
| [0xE0352F62A19E306F, 0xD50B2037AD200000],
| [0x8C213D9DA502DE45, 0x4526F422CC340000],
| [0xAF298D050E4395D7, 0x9670B12B7F410000],
| [0xDAF3F04651D47B4C, 0x3C0CDD765F114000],
| [0x88D8762BF324CD10, 0xA5880A69FB6AC800],
| [0xAB0E93B6EFEE0054, 0x8EEA0D047A457A00],
| [0xD5D238A4ABE98068, 0x72A4904598D6D880],
| [0x85A36366EB71F041, 0x47A6DA2B7F864750],
| [0xA70C3C40A64E6C52, 0x999090B65F67D924],
| [0xD0CF4B50CFE20766, 0xFFF4B4E3F741CF6D],
| [0x82818F1281ED44A0, 0xBFF8F10E7A8921A4],
| [0xA321F2D7226895C8, 0xAFF72D52192B6A0D],
| [0xCBEA6F8CEB02BB3A, 0x9BF4F8A69F764490],
| [0xFEE50B7025C36A08, 0x02F236D04753D5B5],
| [0x9F4F2726179A2245, 0x01D762422C946591],
| [0xC722F0EF9D80AAD6, 0x424D3AD2B7B97EF5],
| [0xF8EBAD2B84E0D58C, 0xD2E0898765A7DEB2],
| [0x9B934C3B330C8577, 0x63CC55F49F88EB2F],
| [0xC2781F49FFCFA6D5, 0x3CBF6B71C76B25FB],
| [0xF316271C7FC3908B, 0x8BEF464E3945EF7A],
| [0x97EDD871CFDA3A57, 0x97758BF0E3CBB5AC],
| [0xBDE94E8E43D0C8EC, 0x3D52EEED1CBEA317],
| [0xED63A231D4C4FB27, 0x4CA7AAA863EE4BDD],
| [0x945E455F24FB1CF9, 0x8FE8CAA93E74EF6A],
| [0xB975D6B6EE39E437, 0xB3E2FD538E122B45],
| [0xE7D34C64A9C85D44, 0x60DBBCA87196B616],
| [0x90E40FBEEA1D3A4B, 0xBC8955E946FE31CE],
| [0xB51D13AEA4A488DD, 0x6BABAB6398BDBE41],
| [0xE264589A4DCDAB15, 0xC696963C7EED2DD2],
| [0x8D7EB76070A08AED, 0xFC1E1DE5CF543CA3],
| [0xB0DE65388CC8ADA8, 0x3B25A55F43294BCC],
| [0xDD15FE86AFFAD912, 0x49EF0EB713F39EBF],
| [0x8A2DBF142DFCC7AB, 0x6E3569326C784337],
| [0xACB92ED9397BF996, 0x49C2C37F07965405],
| [0xD7E77A8F87DAF7FC, 0xDC33745EC97BE906],
| [0x86F0AC99B4E8DAFD, 0x69A028BB3DED71A4],
| [0xA8ACD7C0222311BD, 0xC40832EA0D68CE0D],
| [0xD2D80DB02AABD62C, 0xF50A3FA490C30190],
| [0x83C7088E1AAB65DB, 0x792667C6DA79E0FA],
| [0xA4B8CAB1A1563F52, 0x577001B891185939],
| [0xCDE6FD5E09ABCF27, 0xED4C0226B55E6F87],
| [0x80B05E5AC60B6178, 0x544F8158315B05B4],
| [0xA0DC75F1778E39D6, 0x696361AE3DB1C721],
| [0xC913936DD571C84C, 0x03BC3A19CD1E38EA],
| [0xFB5878494ACE3A5F, 0x04AB48A04065C724],
| [0x9D174B2DCEC0E47B, 0x62EB0D64283F9C76],
| [0xC45D1DF942711D9A, 0x3BA5D0BD324F8394],
| [0xF5746577930D6501, 0xCA8F44EC7EE36479],
| [0x9968BF6ABBE85F20, 0x7E998B13CF4E1ECC],
| [0xBFC2EF456AE276E9, 0x9E3FEDD8C321A67F],
| [0xEFB3AB16C59B14A3, 0xC5CFE94EF3EA101E],
| [0x95D04AEE3B80ECE6, 0xBBA1F1D158724A13],
| [0xBB445DA9CA61281F, 0x2A8A6E45AE8EDC98],
| [0xEA1575143CF97227, 0xF52D09D71A3293BE],
| [0x924D692CA61BE758, 0x593C2626705F9C56],
| [0xB6E0C377CFA2E12E, 0x6F8B2FB00C77836C],
| [0xE498F455C38B997A, 0x0B6DFB9C0F956447],
| [0x8EDF98B59A373FEC, 0x4724BD4189BD5EAC],
| [0xB2977EE300C50FE7, 0x58EDEC91EC2CB658],
| [0xDF3D5E9BC0F653E1, 0x2F2967B66737E3ED],
| [0x8B865B215899F46D, 0xBD79E0D20082EE74],
| [0xAE67F1E9AEC07188, 0xECD8590680A3AA11],
| [0xDA01EE641A708DEA, 0xE80E6F4820CC9496],
| [0x884134FE908658B2, 0x3109058D147FDCDE],
| [0xAA51823E34A7EEDF, 0xBD4B46F0599FD415],
| [0xD4E5E2CDC1D1EA96, 0x6C9E18AC7007C91A],
| [0x850FADC09923329E, 0x03E2CF6BC604DDB0],
| [0xA6539930BF6BFF46, 0x84DB8346B786151D],
| [0xCFE87F7CEF46FF17, 0xE612641865679A64],
| [0x81F14FAE158C5F6E, 0x4FCB7E8F3F60C07E],
| [0xA26DA3999AEF774A, 0xE3BE5E330F38F09E],
| [0xCB090C8001AB551C, 0x5CADF5BFD3072CC5],
| [0xFDCB4FA002162A63, 0x73D9732FC7C8F7F7],
| [0x9E9F11C4014DDA7E, 0x2867E7FDDCDD9AFA],
| [0xC646D63501A1511E, 0xB281E1FD541501B9],
| [0xF7D88BC24209A565, 0x1F225A7CA91A4227],
| [0x9AE757596946075F, 0x3375788DE9B06958],
| [0xC1A12D2FC3978937, 0x0052D6B1641C83AE],
| [0xF209787BB47D6B85, 0xC0678C5DBD23A49A],
| [0x9745EB4D50CE6333, 0xF840B7BA963646E0],
| [0xBD176620A501FC00, 0xB650E5A93BC3D898],
| [0xEC5D3FA8CE427B00, 0xA3E51F138AB4CEBE],
| [0x93BA47C980E98CE0, 0xC66F336C36B10137],
| [0xB8A8D9BBE123F018, 0xB80B0047445D4185],
| [0xE6D3102AD96CEC1E, 0xA60DC059157491E6],
| [0x9043EA1AC7E41393, 0x87C89837AD68DB30],
| [0xB454E4A179DD1877, 0x29BABE4598C311FC],
| [0xE16A1DC9D8545E95, 0xF4296DD6FEF3D67B],
| [0x8CE2529E2734BB1D, 0x1899E4A65F58660D],
| [0xB01AE745B101E9E4, 0x5EC05DCFF72E7F90],
| [0xDC21A1171D42645D, 0x76707543F4FA1F74],
| [0x899504AE72497EBA, 0x6A06494A791C53A8],
| [0xABFA45DA0EDBDE69, 0x0487DB9D17636892],
| [0xD6F8D7509292D603, 0x45A9D2845D3C42B7],
| [0x865B86925B9BC5C2, 0x0B8A2392BA45A9B2],
| [0xA7F26836F282B733, 0x8E6CAC7768D7141F],
| [0xD1EF0244AF2364FF, 0x3207D795430CD927],
| [0x8335616AED761F1F, 0x7F44E6BD49E807B8],
| [0xA402B9C5A8D3A6E7, 0x5F16206C9C6209A6],
| [0xCD036837130890A1, 0x36DBA887C37A8C10],
| [0x802221226BE55A65, 0xC2494954DA2C978A],
| [0xA02AA96B06DEB0FE, 0xF2DB9BAA10B7BD6C],
| [0xC83553C5C8965D3D, 0x6F92829494E5ACC7],
| [0xFA42A8B73ABBF48D, 0xCB772339BA1F17F9],
| [0x9C69A97284B578D8, 0xFF2A760414536EFC],
| [0xC38413CF25E2D70E, 0xFEF5138519684ABB],
| [0xF46518C2EF5B8CD1, 0x7EB258665FC25D69],
| [0x98BF2F79D5993803, 0xEF2F773FFBD97A62],
| [0xBEEEFB584AFF8604, 0xAAFB550FFACFD8FA],
| [0xEEAABA2E5DBF6785, 0x95BA2A53F983CF39],
| [0x952AB45CFA97A0B3, 0xDD945A747BF26184],
| [0xBA756174393D88E0, 0x94F971119AEEF9E4],
| [0xE912B9D1478CEB17, 0x7A37CD5601AAB85E],
| [0x91ABB422CCB812EF, 0xAC62E055C10AB33B],
| [0xB616A12B7FE617AA, 0x577B986B314D6009],
| [0xE39C49765FDF9D95, 0xED5A7E85FDA0B80B],
| [0x8E41ADE9FBEBC27D, 0x14588F13BE847307],
| [0xB1D219647AE6B31C, 0x596EB2D8AE258FC9],
| [0xDE469FBD99A05FE3, 0x6FCA5F8ED9AEF3BB],
| [0x8AEC23D680043BEE, 0x25DE7BB9480D5855],
| [0xADA72CCC20054AEA, 0xAF561AA79A10AE6A],
| [0xD910F7FF28069DA4, 0x1B2BA1518094DA05],
| [0x87AA9AFF79042287, 0x90FB44D2F05D0843],
| [0xA99541BF57452B28, 0x353A1607AC744A54],
| [0xD3FA922F2D1675F2, 0x42889B8997915CE9],
| [0x847C9B5D7C2E09B7, 0x69956135FEBADA11],
| [0xA59BC234DB398C25, 0x43FAB9837E699096],
| [0xCF02B2C21207EF2F, 0x94F967E45E03F4BB],
| [0x8161AFB94B44F57D, 0x1D1BE0EEBAC278F5],
| [0xA1BA1BA79E1632DC, 0x6462D92A69731732],
| [0xCA28A291859BBF93, 0x7D7B8F7503CFDCFF],
| [0xFCB2CB35E702AF78, 0x5CDA735244C3D43F],
| [0x9DEFBF01B061ADAB, 0x3A0888136AFA64A7],
| [0xC56BAEC21C7A1916, 0x088AAA1845B8FDD1],
| [0xF6C69A72A3989F5C, 0x8AAD549E57273D45],
| [0x9A3C2087A63F6399, 0x36AC54E2F678864B],
| [0xC0CB28A98FCF3C80, 0x84576A1BB416A7DE],
| [0xF0FDF2D3F3C30B9F, 0x656D44A2A11C51D5],
| [0x969EB7C47859E744, 0x9F644AE5A4B1B325],
| [0xBC4665B596706115, 0x873D5D9F0DDE1FEF],
| [0xEB57FF22FC0C795A, 0xA90CB506D155A7EA],
| [0x9316FF75DD87CBD8, 0x09A7F12442D588F3],
| [0xB7DCBF5354E9BECE, 0x0C11ED6D538AEB2F],
| [0xE5D3EF282A242E82, 0x8F1668C8A86DA5FB],
| [0x8FA475791A569D11, 0xF96E017D694487BD],
| [0xB38D92D760EC4455, 0x37C981DCC395A9AC],
| [0xE070F78D3927556B, 0x85BBE253F47B1417],
| [0x8C469AB843B89563, 0x93956D7478CCEC8E],
| [0xAF58416654A6BABB, 0x387AC8D1970027B2],
| [0xDB2E51BFE9D0696A, 0x06997B05FCC0319F],
| [0x88FCF317F22241E2, 0x441FECE3BDF81F03],
| [0xAB3C2FDDEEAAD25B, 0xD527E81CAD7626C4],
| [0xD60B3BD56A5586F2, 0x8A71E223D8D3B075],
| [0x85C7056562757457, 0xF6872D5667844E49],
| [0xA738C6BEBB12D16D, 0xB428F8AC016561DB],
| [0xD106F86E69D785C8, 0xE13336D701BEBA52],
| [0x82A45B450226B39D, 0xECC0024661173473],
| [0xA34D721642B06084, 0x27F002D7F95D0190],
| [0xCC20CE9BD35C78A5, 0x31EC038DF7B441F4],
| [0xFF290242C83396CE, 0x7E67047175A15271],
| [0x9F79A169BD203E41, 0x0F0062C6E984D387],
| [0xC75809C42C684DD1, 0x52C07B78A3E60868],
| [0xF92E0C3537826146, 0xA7709A56CCDF8A83],
| [0x9BBCC7A142B17CCC, 0x88A66076400BB692],
| [0xC2ABF989935DDBFE, 0x6ACFF893D00EA436],
| [0xF356F7EBF83552FE, 0x0583F6B8C4124D43],
| [0x98165AF37B2153DF, 0xC3727A337A8B704A],
| [0xBE1BF1B059E9A8D6, 0x744F18C0592E4C5D],
| [0xEDA2EE1C7064130C, 0x1162DEF06F79DF74],
| [0x9485D4D1C63E8BE8, 0x8ADDCB5645AC2BA8],
| [0xB9A74A0637CE2EE1, 0x6D953E2BD7173693],
| [0xE8111C87C5C1BA9A, 0xC8FA8DB6CCDD0437],
| [0x910AB1D4DB9914A0, 0x1D9C9892400A22A2],
| [0xB54D5E4A127F59C8, 0x2503BEB6D00CAB4B],
| [0xE2A0B5DC971F303A, 0x2E44AE64840FD61E],
| [0x8DA471A9DE737E24, 0x5CEAECFED289E5D3],
| [0xB10D8E1456105DAD, 0x7425A83E872C5F47],
| [0xDD50F1996B947519, 0xD12F124E28F77719],
| [0x8A5296FFE33CC930, 0x82BD6B70D99AAA70],
| [0xACE73CBFDC0BFB7B, 0x636CC64D1001550C],
| [0xD8210BEFD30EFA5A, 0x3C47F7E05401AA4F],
| [0x8714A775E3E95C78, 0x65ACFAEC34810A71],
| [0xA8D9D1535CE3B396, 0x7F1839A741A14D0D],
| [0xD31045A8341CA07C, 0x1EDE48111209A051],
| [0x83EA2B892091E44E, 0x934AED0AAB460432],
| [0xA4E4B66B68B65D61, 0xF81DA84D5617853F],
| [0xCE1DE40642E3F4B9, 0x36251260AB9D668F],
| [0x80D2AE83E9CE78F4, 0xC1D72B7C6B426019],
| [0xA1075A24E4421731, 0xB24CF65B8612F820],
| [0xC94930AE1D529CFD, 0xDEE033F26797B628],
| [0xFB9B7CD9A4A7443C, 0x169840EF017DA3B1],
| [0x9D412E0806E88AA6, 0x8E1F289560EE864F],
| [0xC491798A08A2AD4F, 0xF1A6F2BAB92A27E3],
| [0xF5B5D7EC8ACB58A3, 0xAE10AF696774B1DB],
| [0x9991A6F3D6BF1766, 0xACCA6DA1E0A8EF29],
| [0xBFF610B0CC6EDD3F, 0x17FD090A58D32AF3],
| [0xEFF394DCFF8A948F, 0xDDFC4B4CEF07F5B0],
| [0x95F83D0A1FB69CD9, 0x4ABDAF101564F98E],
| [0xBB764C4CA7A44410, 0x9D6D1AD41ABE37F2],
| [0xEA53DF5FD18D5514, 0x84C86189216DC5EE],
| [0x92746B9BE2F8552C, 0x32FD3CF5B4E49BB5],
| [0xB7118682DBB66A77, 0x3FBC8C33221DC2A2],
| [0xE4D5E82392A40515, 0x0FABAF3FEAA5334A],
| [0x8F05B1163BA6832D, 0x29CB4D87F2A7400E],
| [0xB2C71D5BCA9023F8, 0x743E20E9EF511012],
| [0xDF78E4B2BD342CF7, 0x914DA9246B255417],
| [0x8BAB8EEFB6409C1A, 0x1AD089B6C2F7548E],
| [0xAE9672ABA3D0C321, 0xA184AC2473B529B2],
| [0xDA3C0F568CC4F3E9, 0xC9E5D72D90A2741E],
| [0x8865899617FB1871, 0x7E2FA67C7A658893],
| [0xAA7EEBFB9DF9DE8E, 0xDDBB901B98FEEAB8],
| [0xD51EA6FA85785631, 0x552A74227F3EA565],
| [0x8533285C936B35DF, 0xD53A88958F87275F],
| [0xA67FF273B8460357, 0x8A892ABAF368F137],
| [0xD01FEF10A657842C, 0x2D2B7569B0432D85],
| [0x8213F56A67F6B29C, 0x9C3B29620E29FC73],
| [0xA298F2C501F45F43, 0x8349F3BA91B47B90],
| [0xCB3F2F7642717713, 0x241C70A936219A74],
| [0xFE0EFB53D30DD4D8, 0xED238CD383AA0111],
| [0x9EC95D1463E8A507, 0xF4363804324A40AB],
| [0xC67BB4597CE2CE49, 0xB143C6053EDCD0D5],
| [0xF81AA16FDC1B81DB, 0xDD94B7868E94050A],
| [0x9B10A4E5E9913129, 0xCA7CF2B4191C8327],
| [0xC1D4CE1F63F57D73, 0xFD1C2F611F63A3F0],
| [0xF24A01A73CF2DCD0, 0xBC633B39673C8CEC],
| [0x976E41088617CA02, 0xD5BE0503E085D814],
| [0xBD49D14AA79DBC82, 0x4B2D8644D8A74E19],
| [0xEC9C459D51852BA3, 0xDDF8E7D60ED1219F],
| [0x93E1AB8252F33B46, 0xCABB90E5C942B503],
| [0xB8DA1662E7B00A17, 0x3D6A751F3B936244],
| [0xE7109BFBA19C0C9D, 0x0CC512670A783AD5],
| [0x906A617D450187E2, 0x27FB2B80668B24C5],
| [0xB484F9DC9641E9DB, 0xB1F9F660802DEDF6],
| [0xE1A63853BBD26451, 0x5E7873F8A0396974],
| [0x8D07E33455637EB3, 0xDB0B487B6423E1E8],
| [0xB049DC016ABC5E60, 0x91CE1A9A3D2CDA63],
| [0xDC5C5301C56B75F7, 0x7641A140CC7810FB],
| [0x89B9B3E11B6329BB, 0xA9E904C87FCB0A9D],
| [0xAC2820D9623BF429, 0x546345FA9FBDCD44],
| [0xD732290FBACAF134, 0xA97C177947AD4095],
| [0x867F59A9D4BED6C0, 0x49ED8EABCCCC485D],
| [0xA81F301449EE8C70, 0x5C68F256BFFF5A75],
| [0xD226FC195C6A2F8C, 0x73832EEC6FFF3112],
| [0x83585D8FD9C25DB8, 0xC831FD53C5FF7EAB],
| [0xA42E74F3D032F526, 0xBA3E7CA8B77F5E56],
| [0xCD3A1230C43FB26F, 0x28CE1BD2E55F35EB],
| [0x80444B5E7AA7CF85, 0x7980D163CF5B81B3],
| [0xA0555E361951C367, 0xD7E105BCC3326220],
| [0xC86AB5C39FA63441, 0x8DD9472BF3FEFAA8],
| [0xFA856334878FC151, 0xB14F98F6F0FEB952],
| [0x9C935E00D4B9D8D2, 0x6ED1BF9A569F33D3],
| [0xC3B8358109E84F07, 0x0A862F80EC4700C8],
| [0xF4A642E14C6262C9, 0xCD27BB612758C0FA],
| [0x98E7E9CCCFBD7DBE, 0x8038D51CB897789C],
| [0xBF21E44003ACDD2D, 0xE0470A63E6BD56C3],
| [0xEEEA5D5004981478, 0x1858CCFCE06CAC74],
| [0x95527A5202DF0CCB, 0x0F37801E0C43EBC9],
| [0xBAA718E68396CFFE, 0xD30560258F54E6BB],
| [0xE950DF20247C83FD, 0x47C6B82EF32A2069],
| [0x91D28B7416CDD27E, 0x4CDC331D57FA5442],
| [0xB6472E511C81471E, 0xE0133FE4ADF8E952],
| [0xE3D8F9E563A198E5, 0x58180FDDD97723A7],
| [0x8E679C2F5E44FF8F, 0x570F09EAA7EA7648],
| [0xB201833B35D63F73, 0x2CD2CC6551E513DA],
| [0xDE81E40A034BCF50, 0xF8077F7EA65E58D1],
| [0x8B112E86420F6192, 0xFB04AFAF27FAF783],
| [0xADD57A27D29339F6, 0x79C5DB9AF1F9B563],
| [0xD94AD8B1C7380874, 0x18375281AE7822BC],
| [0x87CEC76F1C830549, 0x8F2293910D0B15B6],
| [0xA9C2794AE3A3C69B, 0xB2EB3875504DDB23],
| [0xD433179D9C8CB841, 0x5FA60692A46151EC],
| [0x849FEEC281D7F329, 0xDBC7C41BA6BCD333],
| [0xA5C7EA73224DEFF3, 0x12B9B522906C0800],
| [0xCF39E50FEAE16BF0, 0xD768226B34870A00],
| [0x81842F29F2CCE376, 0xE6A1158300D46640],
| [0xA1E53AF46F801C53, 0x60495AE3C1097FD0],
| [0xCA5E89B18B602368, 0x385BB19CB14BDFC4],
| [0xFCF62C1DEE382C42, 0x46729E03DD9ED7B5],
| [0x9E19DB92B4E31BA9, 0x6C07A2C26A8346D1],
| [0xC5A05277621BE294, 0xC7098B7305241886],
| [0xF70867153AA2DB39, 0xB8CBEE4FC66D1EA7],
| [0x9A65406D44A5C903, 0x737F74F1DC043328],
| [0xC0FE908895CF3B44, 0x505F522E53053FF2],
| [0xF13E34AABB430A15, 0x647726B9E7C68FEF],
| [0x96C6E0EAB509E64D, 0x5ECA783430DC19F5],
| [0xBC789925624C5FE1, 0xB67D16413D132073],
| [0xEB96BF6EBADF77D9, 0xE41C5BD18C57E88F],
| [0x933E37A534CBAAE8, 0x8E91B962F7B6F15A],
| [0xB80DC58E81FE95A1, 0x723627BBB5A4ADB0],
| [0xE61136F2227E3B0A, 0xCEC3B1AAA30DD91C],
| [0x8FCAC257558EE4E6, 0x213A4F0AA5E8A7B2],
| [0xB3BD72ED2AF29E20, 0xA988E2CD4F62D19E],
| [0xE0ACCFA875AF45A8, 0x93EB1B80A33B8605],
| [0x8C6C01C9498D8B89, 0xBC72F130660533C3],
| [0xAF87023B9BF0EE6B, 0xEB8FAD7C7F8680B4],
| [0xDB68C2CA82ED2A06, 0xA67398DB9F6820E1],
| [0x892179BE91D43A44, 0x88083F8943A1148D],
| [0xAB69D82E364948D4, 0x6A0A4F6B948959B0],
| [0xD6444E39C3DB9B0A, 0x848CE34679ABB01C],
| [0x85EAB0E41A6940E6, 0xF2D80E0C0C0B4E12],
| [0xA7655D1D2103911F, 0x6F8E118F0F0E2196],
| [0xD13EB46469447567, 0x4B7195F2D2D1A9FB],
| [0x82C730BEC1CAC961, 0x8F26FDB7C3C30A3D],
| [0xA378FCEE723D7BB9, 0xB2F0BD25B4B3CCCC],
| [0xCC573C2A0ECCDAA7, 0xDFACEC6F21E0C000],
| [0xFF6D0B3492801151, 0x9798278AEA58EFFF],
| [0x9FA42700DB900AD2, 0x5EBF18B6D2779600],
| [0xC78D30C112740D87, 0xF66EDEE487157B80],
| [0xF9707CF1571110E9, 0xB40A969DA8DADA5F],
| [0x9BE64E16D66AAA91, 0x70869E228988C87C],
| [0xC2DFE19C8C055536, 0xCCA845AB2BEAFA9B],
| [0xF397DA03AF06AA83, 0x3FD25715F6E5B941],
| [0x983EE8424D642A92, 0x07E3766DBA4F93C9],
| [0xBE4EA252E0BD3537, 0x89DC540928E378BB],
| [0xEDE24AE798EC8284, 0x2C53690B731C56EA],
| [0x94AD6ED0BF93D193, 0x9BB421A727F1B652],
| [0xB9D8CA84EF78C5F7, 0x42A12A10F1EE23E7],
| [0xE84EFD262B56F775, 0x134974952E69ACE0],
| [0x91315E37DB165AA9, 0x2C0DE8DD3D020C0C],
| [0xB57DB5C5D1DBF153, 0x771163148C428F0F],
| [0xE2DD23374652EDA8, 0x54D5BBD9AF5332D3],
| [0x8DCA36028BF3D489, 0x350595680D93FFC4],
| [0xB13CC3832EF0C9AC, 0x8246FAC210F8FFB5],
| [0xDD8BF463FAACFC16, 0x62D8B97295373FA2],
| [0x8A7778BE7CAC1D8E, 0xFDC773E79D4287C5],
| [0xAD1556EE1BD724F1, 0x7D3950E1849329B7],
| [0xD85AACA9A2CCEE2E, 0xDC87A519E5B7F424],
| [0x8738ABEA05C014DD, 0xA9D4C7302F92F897],
| [0xA906D6E487301A14, 0xD449F8FC3B77B6BC],
| [0xD3488C9DA8FC2099, 0xC95C773B4A55A46B],
| [0x840D57E2899D945F, 0x7DD9CA850E7586C3],
| [0xA510ADDB2C04F977, 0x5D503D265212E874],
| [0xCE54D951F70637D5, 0x34A44C6FE697A291],
| [0x80F507D33A63E2E5, 0x40E6AFC5F01EC59B],
| [0xA13249C808FCDB9F, 0x91205BB76C267701],
| [0xC97EDC3A0B3C1286, 0x356872A5473014C1],
| [0xFBDE93488E0B1728, 0xC2C28F4E98FC19F2],
| [0x9D6B1C0D58C6EE79, 0xD9B999911F9D9037],
| [0xC4C5E310AEF8AA17, 0x1027FFF56784F445],
| [0xF5F75BD4DAB6D49D, 0xD431FFF2C1663156],
| [0x99BA996508B244E2, 0x049F3FF7B8DFDED6],
| [0xC0293FBE4ADED61B, 0x85C70FF5A717D68B],
| [0xF0338FADDD968BA1, 0x2738D3F310DDCC2E],
| [0x962039CCAA7E1745, 0xB8838477EA8A9F9D],
| [0xBBA8483FD51D9D16, 0xE6A46595E52D4784],
| [0xEA925A4FCA65045B, 0x604D7EFB5E789965],
| [0x929B7871DE7F22B9, 0x1C306F5D1B0B5FDF],
| [0xB742568E561EEB67, 0x633C8B3461CE37D7],
| [0xE512EC31EBA6A641, 0x3C0BAE017A41C5CD],
| [0x8F2BD39F334827E9, 0xC5874CC0EC691BA0],
| [0xB2F6C887001A31E3, 0xF6E91FF127836288],
| [0xDFB47AA8C020BE5C, 0xB4A367ED71643B2A],
| [0x8BD0CCA9781476F9, 0x50E620F466DEA4FA],
| [0xAEC4FFD3D61994B8, 0xA51FA93180964E39],
| [0xDA763FC8CB9FF9E6, 0x8E67937DE0BBE1C7],
| [0x8889E7DD7F43FC2F, 0x7900BC2EAC756D1C],
| [0xAAAC61D4DF14FB3B, 0x5740EB3A5792C863],
| [0xD5577A4A16DA3A0A, 0x2D112608ED777A7C],
| [0x8556AC6E4E486446, 0x5C2AB7C5946AAC8E],
| [0xA6AC5789E1DA7D58, 0xF33565B6F98557B1],
| [0xD0576D6C5A511CAE, 0xF002BF24B7E6AD9D],
| [0x8236A463B872B1ED, 0xB601B776F2F02C82],
| [0xA2C44D7CA68F5E68, 0xE3822554AFAC37A3],
| [0xCB7560DBD0333602, 0xDC62AEA9DB97458C],
| [0xFE52B912C4400382, 0x537B5A54527D16EE],
| [0x9EF3B3ABBAA80231, 0x742D1874B38E2E55],
| [0xC6B0A096A95202BE, 0xD1385E91E071B9EA],
| [0xF85CC8BC53A6836D, 0x45867636588E2865],
| [0x9B39FD75B4481224, 0x4B7409E1F758D93F],
| [0xC2087CD3215A16AD, 0x5E510C5A752F0F8F],
| [0xF28A9C07E9B09C59, 0xB5E54F71127AD373],
| [0x9796A184F20E61B7, 0x71AF51A6AB8CC428],
| [0xBD7C49E62E91FA25, 0x4E1B2610566FF531],
| [0xECDB5C5FBA3678AF, 0xA1A1EF946C0BF27E],
| [0x940919BBD4620B6D, 0x250535BCC387778F],
| [0xB90B602AC97A8E48, 0x6E46832BF4695572],
| [0xE74E38357BD931DB, 0x89D823F6F183AACF],
| [0x9090E3216D67BF29, 0x9627167A56F24AC1],
| [0xB4B51BE9C8C1AEF3, 0xBBB0DC18ECAEDD72],
| [0xE1E262E43AF21AAF, 0x6A9D131F27DA94CE],
| [0x8D2D7DCEA4D750AE, 0xA2A22BF378E89D01],
| [0xB078DD424E0D24D9, 0x0B4AB6F05722C441],
| [0xDC971492E1906E0F, 0x4E1D64AC6CEB7551],
| [0x89DE6CDBCCFA44CA, 0x90D25EEBC4132953],
| [0xAC560812C038D5FC, 0xF506F6A6B517F3A7],
| [0xD76B8A1770470B7B, 0xF248B450625DF091],
| [0x86A3364EA62C672D, 0xD76D70B23D7AB65B],
| [0xA84C03E24FB780F8, 0x0D48CCDECCD963F2],
| [0xD25F04DAE3A56136, 0x109B0016800FBCEE],
| [0x837B6308CE475CC2, 0xCA60E00E1009D615],
| [0xA45A3BCB01D933F2, 0x3CF91811940C4B9A],
| [0xCD70CABDC24F80EF, 0xCC375E15F90F5E80],
| [0x80667EB69971B095, 0x3FA29ACDBBA99B10],
| [0xA0801E643FCE1CBB, 0x8F8B41812A9401D4],
| [0xC8A025FD4FC1A3E9, 0x336E11E175390249],
| [0xFAC82F7CA3B20CE4, 0x80499659D28742DC],
| [0x9CBD1DADE64F480E, 0x302DFDF8239489C9],
| [0xC3EC65195FE31A12, 0xBC397D762C79AC3C],
| [0xF4E77E5FB7DBE096, 0x2B47DCD3B798174B],
| [0x9910AEFBD2E96C5E, 0xDB0CEA0452BF0E8F],
| [0xBF54DABAC7A3C775, 0x51D02485676ED232],
| [0xEF2A1169798CB953, 0xA6442DA6C14A86BF],
| [0x957A4AE1EBF7F3D4, 0xA7EA9C8838CE9437],
| [0xBAD8DD9A66F5F0C9, 0x91E543AA47023945],
| [0xE98F150100B36CFB, 0xB65E9494D8C2C796],
| [0x91F96D20A070241D, 0xB1FB1CDD0779BCBE],
| [0xB677C868C88C2D24, 0xDE79E41449582BED],
| [0xE415BA82FAAF386D, 0xD6185D195BAE36E9],
| [0x8E8D9491DCAD8344, 0x05CF3A2FD94CE251],
| [0xB230F9B653D8E415, 0x074308BBCFA01AE6],
| [0xDEBD3823E8CF1D1A, 0x4913CAEAC388219F],
| [0x8B36431671817230, 0x6DAC5ED2BA351504],
| [0xAE03D3DC0DE1CEBD, 0x8917768768C25A44],
| [0xD984C8D3115A426C, 0xAB5D542942F2F0D6],
| [0x87F2FD83EAD86983, 0x4B1A5499C9D7D685],
| [0xA9EFBCE4E58E83E4, 0x1DE0E9C03C4DCC27],
| [0xD46BAC1E1EF224DD, 0x255924304B613F31],
| [0x84C34B92D357570A, 0x3757B69E2F1CC77E],
| [0xA5F41E77882D2CCD, 0xC52DA445BAE3F95E],
| [0xCF7126156A387800, 0xF6790D57299CF7B5],
| [0x81A6B7CD62634B00, 0xFA0BA8567A021AD1],
| [0xA21065C0BAFC1DC0, 0xF88E926C1882A186],
| [0xCA947F30E9BB2530, 0xF6B237071EA349E7],
| [0xFD399EFD2429EE7C, 0xF45EC4C8E64C1C61],
| [0x9E44035E369A350D, 0x78BB3AFD8FEF91BD],
| [0xC5D50435C440C251, 0xD6EA09BCF3EB762C],
| [0xF74A45433550F2E5, 0x0CA48C2C30E653B7],
| [0x9A8E6B4A015297CF, 0x27E6D79B9E8FF452],
| [0xC132061C81A73DC3, 0xF1E08D828633F167],
| [0xF17E87A3A2110D34, 0xAE58B0E327C0EDC0],
| [0x96EF14C6454AA840, 0x4CF76E8DF8D89498],
| [0xBCAAD9F7D69D5250, 0x60354A31770EB9BE],
| [0xEBD59075CC44A6E4, 0x78429CBDD4D2682E],
| [0x93657A499FAAE84F, 0xCB29A1F6A503811D],
| [0xB83ED8DC0795A262, 0x7DF40A744E446164],
| [0xE64E8F13097B0AFB, 0x1D710D1161D579BD],
| [0x8FF1196BE5ECE6DD, 0xF266A82ADD256C16],
| [0xB3ED5FC6DF682094, 0x2F005235946EC71C],
| [0xE0E8B7B8974228B9, 0x3AC066C2F98A78E2],
| [0x8C9172D35E895974, 0xC4B84039DBF68B8E],
| [0xAFB5CF88362BAFD1, 0xB5E6504852F42E71],
| [0xDBA3436A43B69BC5, 0xE35FE45A67B13A0D],
| [0x89460A226A52215B, 0x0E1BEEB880CEC448],
| [0xAB978CAB04E6A9B2, 0xD1A2EA66A102755A],
| [0xD67D6FD5C620541E, 0x460BA500494312B1],
| [0x860E65E59BD43493, 0xEBC747202DC9EBAF],
| [0xA791FF5F02C941B8, 0xA6B918E8393C669A],
| [0xD1767F36C37B9226, 0x90675F22478B8041],
| [0x82EA0F823A2D3B57, 0x7A409B756CB73028],
| [0xA3A49362C8B88A2D, 0x58D0C252C7E4FC33],
| [0xCC8DB83B7AE6ACB9, 0xAF04F2E779DE3B3F],
| [0xFFB1264A59A057E7, 0xDAC62FA15855CA0F],
| [0x9FCEB7EE780436F0, 0x48BBDDC4D7359E49],
| [0xC7C265EA160544AC, 0x5AEAD5360D0305DC],
| [0xF9B2FF649B8695D7, 0x71A58A839043C753],
| [0x9C0FDF9EE1341DA7, 0xA70776923A2A5C94],
| [0xC313D78699812510, 0x50C95436C8B4F3B9],
| [0xF3D8CD683FE16E54, 0x64FBA9447AE230A7],
| [0x9867806127ECE4F5, 0xBF1D49CACCCD5E68],
| [0xBE81607971E81E32, 0xEEE49C3D8000B602],
| [0xEE21B897CE6225BE, 0x6A9DC34CE000E383],
| [0x94D5135EE0FD5797, 0x02A29A100C008E32],
| [0xBA0A5836993CAD7D, 0xC34B40940F00B1BE],
| [0xE88CEE443F8BD8DC, 0xF41E10B912C0DE2E],
| [0x915814EAA7B76789, 0x7892CA73ABB88ADD],
| [0xB5AE1A2551A5416C, 0xD6B77D1096A6AD94],
| [0xE319A0AEA60E91C7, 0xCC655C54BC5058F9],
|];
|
|//
|static immutable short[1025] p10_exponents = [
| -1764,
| -1761,
| -1758,
| -1754,
| -1751,
| -1748,
| -1744,
| -1741,
| -1738,
| -1734,
| -1731,
| -1728,
| -1724,
| -1721,
| -1718,
| -1714,
| -1711,
| -1708,
| -1705,
| -1701,
| -1698,
| -1695,
| -1691,
| -1688,
| -1685,
| -1681,
| -1678,
| -1675,
| -1671,
| -1668,
| -1665,
| -1661,
| -1658,
| -1655,
| -1651,
| -1648,
| -1645,
| -1641,
| -1638,
| -1635,
| -1631,
| -1628,
| -1625,
| -1621,
| -1618,
| -1615,
| -1612,
| -1608,
| -1605,
| -1602,
| -1598,
| -1595,
| -1592,
| -1588,
| -1585,
| -1582,
| -1578,
| -1575,
| -1572,
| -1568,
| -1565,
| -1562,
| -1558,
| -1555,
| -1552,
| -1548,
| -1545,
| -1542,
| -1538,
| -1535,
| -1532,
| -1528,
| -1525,
| -1522,
| -1519,
| -1515,
| -1512,
| -1509,
| -1505,
| -1502,
| -1499,
| -1495,
| -1492,
| -1489,
| -1485,
| -1482,
| -1479,
| -1475,
| -1472,
| -1469,
| -1465,
| -1462,
| -1459,
| -1455,
| -1452,
| -1449,
| -1445,
| -1442,
| -1439,
| -1435,
| -1432,
| -1429,
| -1425,
| -1422,
| -1419,
| -1416,
| -1412,
| -1409,
| -1406,
| -1402,
| -1399,
| -1396,
| -1392,
| -1389,
| -1386,
| -1382,
| -1379,
| -1376,
| -1372,
| -1369,
| -1366,
| -1362,
| -1359,
| -1356,
| -1352,
| -1349,
| -1346,
| -1342,
| -1339,
| -1336,
| -1332,
| -1329,
| -1326,
| -1323,
| -1319,
| -1316,
| -1313,
| -1309,
| -1306,
| -1303,
| -1299,
| -1296,
| -1293,
| -1289,
| -1286,
| -1283,
| -1279,
| -1276,
| -1273,
| -1269,
| -1266,
| -1263,
| -1259,
| -1256,
| -1253,
| -1249,
| -1246,
| -1243,
| -1239,
| -1236,
| -1233,
| -1229,
| -1226,
| -1223,
| -1220,
| -1216,
| -1213,
| -1210,
| -1206,
| -1203,
| -1200,
| -1196,
| -1193,
| -1190,
| -1186,
| -1183,
| -1180,
| -1176,
| -1173,
| -1170,
| -1166,
| -1163,
| -1160,
| -1156,
| -1153,
| -1150,
| -1146,
| -1143,
| -1140,
| -1136,
| -1133,
| -1130,
| -1127,
| -1123,
| -1120,
| -1117,
| -1113,
| -1110,
| -1107,
| -1103,
| -1100,
| -1097,
| -1093,
| -1090,
| -1087,
| -1083,
| -1080,
| -1077,
| -1073,
| -1070,
| -1067,
| -1063,
| -1060,
| -1057,
| -1053,
| -1050,
| -1047,
| -1043,
| -1040,
| -1037,
| -1034,
| -1030,
| -1027,
| -1024,
| -1020,
| -1017,
| -1014,
| -1010,
| -1007,
| -1004,
| -1000,
| -997,
| -994,
| -990,
| -987,
| -984,
| -980,
| -977,
| -974,
| -970,
| -967,
| -964,
| -960,
| -957,
| -954,
| -950,
| -947,
| -944,
| -940,
| -937,
| -934,
| -931,
| -927,
| -924,
| -921,
| -917,
| -914,
| -911,
| -907,
| -904,
| -901,
| -897,
| -894,
| -891,
| -887,
| -884,
| -881,
| -877,
| -874,
| -871,
| -867,
| -864,
| -861,
| -857,
| -854,
| -851,
| -847,
| -844,
| -841,
| -838,
| -834,
| -831,
| -828,
| -824,
| -821,
| -818,
| -814,
| -811,
| -808,
| -804,
| -801,
| -798,
| -794,
| -791,
| -788,
| -784,
| -781,
| -778,
| -774,
| -771,
| -768,
| -764,
| -761,
| -758,
| -754,
| -751,
| -748,
| -744,
| -741,
| -738,
| -735,
| -731,
| -728,
| -725,
| -721,
| -718,
| -715,
| -711,
| -708,
| -705,
| -701,
| -698,
| -695,
| -691,
| -688,
| -685,
| -681,
| -678,
| -675,
| -671,
| -668,
| -665,
| -661,
| -658,
| -655,
| -651,
| -648,
| -645,
| -642,
| -638,
| -635,
| -632,
| -628,
| -625,
| -622,
| -618,
| -615,
| -612,
| -608,
| -605,
| -602,
| -598,
| -595,
| -592,
| -588,
| -585,
| -582,
| -578,
| -575,
| -572,
| -568,
| -565,
| -562,
| -558,
| -555,
| -552,
| -549,
| -545,
| -542,
| -539,
| -535,
| -532,
| -529,
| -525,
| -522,
| -519,
| -515,
| -512,
| -509,
| -505,
| -502,
| -499,
| -495,
| -492,
| -489,
| -485,
| -482,
| -479,
| -475,
| -472,
| -469,
| -465,
| -462,
| -459,
| -455,
| -452,
| -449,
| -446,
| -442,
| -439,
| -436,
| -432,
| -429,
| -426,
| -422,
| -419,
| -416,
| -412,
| -409,
| -406,
| -402,
| -399,
| -396,
| -392,
| -389,
| -386,
| -382,
| -379,
| -376,
| -372,
| -369,
| -366,
| -362,
| -359,
| -356,
| -353,
| -349,
| -346,
| -343,
| -339,
| -336,
| -333,
| -329,
| -326,
| -323,
| -319,
| -316,
| -313,
| -309,
| -306,
| -303,
| -299,
| -296,
| -293,
| -289,
| -286,
| -283,
| -279,
| -276,
| -273,
| -269,
| -266,
| -263,
| -259,
| -256,
| -253,
| -250,
| -246,
| -243,
| -240,
| -236,
| -233,
| -230,
| -226,
| -223,
| -220,
| -216,
| -213,
| -210,
| -206,
| -203,
| -200,
| -196,
| -193,
| -190,
| -186,
| -183,
| -180,
| -176,
| -173,
| -170,
| -166,
| -163,
| -160,
| -157,
| -153,
| -150,
| -147,
| -143,
| -140,
| -137,
| -133,
| -130,
| -127,
| -123,
| -120,
| -117,
| -113,
| -110,
| -107,
| -103,
| -100,
| -97,
| -93,
| -90,
| -87,
| -83,
| -80,
| -77,
| -73,
| -70,
| -67,
| -63,
| -60,
| -57,
| -54,
| -50,
| -47,
| -44,
| -40,
| -37,
| -34,
| -30,
| -27,
| -24,
| -20,
| -17,
| -14,
| -10,
| -7,
| -4,
| 0,
| 3,
| 6,
| 10,
| 13,
| 16,
| 20,
| 23,
| 26,
| 30,
| 33,
| 36,
| 39,
| 43,
| 46,
| 49,
| 53,
| 56,
| 59,
| 63,
| 66,
| 69,
| 73,
| 76,
| 79,
| 83,
| 86,
| 89,
| 93,
| 96,
| 99,
| 103,
| 106,
| 109,
| 113,
| 116,
| 119,
| 123,
| 126,
| 129,
| 132,
| 136,
| 139,
| 142,
| 146,
| 149,
| 152,
| 156,
| 159,
| 162,
| 166,
| 169,
| 172,
| 176,
| 179,
| 182,
| 186,
| 189,
| 192,
| 196,
| 199,
| 202,
| 206,
| 209,
| 212,
| 216,
| 219,
| 222,
| 226,
| 229,
| 232,
| 235,
| 239,
| 242,
| 245,
| 249,
| 252,
| 255,
| 259,
| 262,
| 265,
| 269,
| 272,
| 275,
| 279,
| 282,
| 285,
| 289,
| 292,
| 295,
| 299,
| 302,
| 305,
| 309,
| 312,
| 315,
| 319,
| 322,
| 325,
| 328,
| 332,
| 335,
| 338,
| 342,
| 345,
| 348,
| 352,
| 355,
| 358,
| 362,
| 365,
| 368,
| 372,
| 375,
| 378,
| 382,
| 385,
| 388,
| 392,
| 395,
| 398,
| 402,
| 405,
| 408,
| 412,
| 415,
| 418,
| 422,
| 425,
| 428,
| 431,
| 435,
| 438,
| 441,
| 445,
| 448,
| 451,
| 455,
| 458,
| 461,
| 465,
| 468,
| 471,
| 475,
| 478,
| 481,
| 485,
| 488,
| 491,
| 495,
| 498,
| 501,
| 505,
| 508,
| 511,
| 515,
| 518,
| 521,
| 524,
| 528,
| 531,
| 534,
| 538,
| 541,
| 544,
| 548,
| 551,
| 554,
| 558,
| 561,
| 564,
| 568,
| 571,
| 574,
| 578,
| 581,
| 584,
| 588,
| 591,
| 594,
| 598,
| 601,
| 604,
| 608,
| 611,
| 614,
| 617,
| 621,
| 624,
| 627,
| 631,
| 634,
| 637,
| 641,
| 644,
| 647,
| 651,
| 654,
| 657,
| 661,
| 664,
| 667,
| 671,
| 674,
| 677,
| 681,
| 684,
| 687,
| 691,
| 694,
| 697,
| 701,
| 704,
| 707,
| 711,
| 714,
| 717,
| 720,
| 724,
| 727,
| 730,
| 734,
| 737,
| 740,
| 744,
| 747,
| 750,
| 754,
| 757,
| 760,
| 764,
| 767,
| 770,
| 774,
| 777,
| 780,
| 784,
| 787,
| 790,
| 794,
| 797,
| 800,
| 804,
| 807,
| 810,
| 813,
| 817,
| 820,
| 823,
| 827,
| 830,
| 833,
| 837,
| 840,
| 843,
| 847,
| 850,
| 853,
| 857,
| 860,
| 863,
| 867,
| 870,
| 873,
| 877,
| 880,
| 883,
| 887,
| 890,
| 893,
| 897,
| 900,
| 903,
| 907,
| 910,
| 913,
| 916,
| 920,
| 923,
| 926,
| 930,
| 933,
| 936,
| 940,
| 943,
| 946,
| 950,
| 953,
| 956,
| 960,
| 963,
| 966,
| 970,
| 973,
| 976,
| 980,
| 983,
| 986,
| 990,
| 993,
| 996,
| 1000,
| 1003,
| 1006,
| 1009,
| 1013,
| 1016,
| 1019,
| 1023,
| 1026,
| 1029,
| 1033,
| 1036,
| 1039,
| 1043,
| 1046,
| 1049,
| 1053,
| 1056,
| 1059,
| 1063,
| 1066,
| 1069,
| 1073,
| 1076,
| 1079,
| 1083,
| 1086,
| 1089,
| 1093,
| 1096,
| 1099,
| 1102,
| 1106,
| 1109,
| 1112,
| 1116,
| 1119,
| 1122,
| 1126,
| 1129,
| 1132,
| 1136,
| 1139,
| 1142,
| 1146,
| 1149,
| 1152,
| 1156,
| 1159,
| 1162,
| 1166,
| 1169,
| 1172,
| 1176,
| 1179,
| 1182,
| 1186,
| 1189,
| 1192,
| 1196,
| 1199,
| 1202,
| 1205,
| 1209,
| 1212,
| 1215,
| 1219,
| 1222,
| 1225,
| 1229,
| 1232,
| 1235,
| 1239,
| 1242,
| 1245,
| 1249,
| 1252,
| 1255,
| 1259,
| 1262,
| 1265,
| 1269,
| 1272,
| 1275,
| 1279,
| 1282,
| 1285,
| 1289,
| 1292,
| 1295,
| 1298,
| 1302,
| 1305,
| 1308,
| 1312,
| 1315,
| 1318,
| 1322,
| 1325,
| 1328,
| 1332,
| 1335,
| 1338,
| 1342,
| 1345,
| 1348,
| 1352,
| 1355,
| 1358,
| 1362,
| 1365,
| 1368,
| 1372,
| 1375,
| 1378,
| 1382,
| 1385,
| 1388,
| 1392,
| 1395,
| 1398,
| 1401,
| 1405,
| 1408,
| 1411,
| 1415,
| 1418,
| 1421,
| 1425,
| 1428,
| 1431,
| 1435,
| 1438,
| 1441,
| 1445,
| 1448,
| 1451,
| 1455,
| 1458,
| 1461,
| 1465,
| 1468,
| 1471,
| 1475,
| 1478,
| 1481,
| 1485,
| 1488,
| 1491,
| 1494,
| 1498,
| 1501,
| 1504,
| 1508,
| 1511,
| 1514,
| 1518,
| 1521,
| 1524,
| 1528,
| 1531,
| 1534,
| 1538,
| 1541,
| 1544,
| 1548,
| 1551,
| 1554,
| 1558,
| 1561,
| 1564,
| 1568,
| 1571,
| 1574,
| 1578,
| 1581,
| 1584,
| 1587,
| 1591,
| 1594,
| 1597,
| 1601,
| 1604,
| 1607,
| 1611,
| 1614,
| 1617,
| 1621,
| 1624,
| 1627,
| 1631,
| 1634,
| 1637,
|];
source/mir/bignum/internal/dec2flt_table.d has no code
<<<<<< EOF
# path=./source-mir-interpolate-polynomial.lst
|/++
|$(H2 Lagrange Barycentric Interpolation)
|
|See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.interpolate.polynomial;
|
|public import mir.interpolate: atInterval;
|import core.lifetime: move;
|import mir.functional: RefTuple;
|import mir.internal.utility : isFloatingPoint, Iota;
|import mir.interpolate: findInterval;
|import mir.math.common;
|import mir.math.numeric;
|import mir.math.sum;
|import mir.ndslice.slice;
|import mir.ndslice.topology: diff, map, member, iota, triplets;
|import mir.rc.array;
|import mir.utility: min, swap;
|import std.traits: Unqual;
|
|@optmath:
|
|///
|version(mir_test)
|@safe pure unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math;
| import mir.ndslice;
|
724| auto f(double x) { return (x-2) * (x-5) * (x-9); }
480| auto fd(double x) { return (x-5) * (x-9) + (x-2) * (x-5) + (x-2) * (x-9); }
240| auto fd2(double x) { return (x-5) + (x-9) + (x-2) + (x-5) + (x-2) + (x-9); }
240| double[2] fWithDerivative(double x) { return [f(x), fd(x)]; }
240| double[3] fWithTwoDerivatives(double x) { return [f(x), fd(x), fd2(x)]; }
|
| //
1| auto x = [0.0, 3, 5, 10];
1| auto y = x.map!f.slice.field;
| // `!2` adds first two derivatives: f, f', and f''
| // default parameter is 0
2| auto l = x.lagrange!2(y);
|
39| foreach(test; x ~ [2.0, 5, 9] ~ [double(PI), double(E), 10, 100, 1e3])
180| foreach(scale; [-1, +1, 1 + double.epsilon, 1 + double.epsilon.sqrt])
864| foreach(shift; [0, double.min_normal/2, double.min_normal*2, double.epsilon, double.epsilon.sqrt])
| {
240| auto testX = test * scale + shift;
|
240| auto lx = l(testX);
240| auto l_ldx = l.withDerivative(testX);
240| auto l_ld_ld2x = l.withTwoDerivatives(testX);
|
240| assert(lx.approxEqual(f(testX)));
240| assert(l_ldx[].all!approxEqual(fWithDerivative(testX)[]));
240| assert(l_ld_ld2x[].all!approxEqual(fWithTwoDerivatives(testX)[]));
| }
|}
|
|/++
|Constructs barycentric lagrange interpolant.
|+/
|Lagrange!(T, maxDerivative) lagrange(uint maxDerivative = 0, T, X)(scope const X[] x, scope const T[] y)
| if (maxDerivative < 16)
|{
| import mir.ndslice.allocation: rcslice;
1| return x.rcslice!(immutable X).lagrange!maxDerivative(y.sliced);
|}
|
|/// ditto
|Lagrange!(Unqual!(Slice!(Iterator, 1, kind).DeepElement), maxDerivative, X)
| lagrange(uint maxDerivative = 0, X, Iterator, SliceKind kind)(Slice!(RCI!(immutable X)) x, Slice!(Iterator, 1, kind) y) @trusted
| if (maxDerivative < 16)
|{
| alias T = Unqual!(Slice!(Iterator, 1, kind).DeepElement);
1| return Lagrange!(T, maxDerivative)(x.move, rcarray!T(y));
|}
|
|/++
|+/
0000000|struct Lagrange(T, uint maxAdditionalFunctions = 0, X = T)
| if (isFloatingPoint!T && maxAdditionalFunctions < 16)
|{
| /// $(RED for internal use only.)
| Slice!(RCI!(immutable X)) _grid;
| /// $(RED for internal use only.)
| RCArray!(immutable T) _inversedBarycentricWeights;
| /// $(RED for internal use only.)
| RCArray!T[maxAdditionalFunctions + 1] _normalizedValues;
| /// $(RED for internal use only.)
| T[maxAdditionalFunctions + 1] _asums;
|
|@optmath @safe pure @nogc extern(D):
|
| ///
| enum uint derivativeOrder = maxAdditionalFunctions;
|
| /++
| Complexity: `O(N)`
| +/
| pragma(inline, false)
| this(Slice!(RCI!(immutable X)) grid, RCArray!T values, RCArray!(immutable T) inversedBarycentricWeights)
| {
| import mir.algorithm.iteration: all;
| assert(grid.length > 1);
| assert(grid.length == values.length);
| assert(grid.length == inversedBarycentricWeights.length);
| enum msg = "Lagrange: grid points are too close or have not finite values.";
| version(D_Exceptions)
| {
| enum T md = T.min_normal / T.epsilon;
| static immutable exc = new Exception(msg);
| if (!grid.lightScope.diff.all!(a => md <= a && a <= T.max))
| throw exc;
| }
| swap(_grid, grid);
| swap(_inversedBarycentricWeights, inversedBarycentricWeights);
| swap(_normalizedValues[0], values);
| auto w = _inversedBarycentricWeights[].sliced;
| foreach (_; Iota!maxAdditionalFunctions)
| {
| auto y = _normalizedValues[_][].sliced;
| static if (_ == 0)
| _asums[_] = y.asumNorm;
| _normalizedValues[_ + 1] = RCArray!T(_grid.length, true, false);
| auto d = _normalizedValues[_ + 1][].sliced;
| polynomialDerivativeValues(d, _grid.lightScope, y, w);
| _asums[_ + 1] = d.asumNorm * _asums[_];
| }
| }
|
| /++
| Complexity: `O(N^^2)`
| +/
1| this(Slice!(RCI!(immutable X)) grid, RCArray!T values)
| {
| import core.lifetime: move;
2| auto weights = grid.lightScope.inversedBarycentricWeights;
1| this(grid.move, values.move, weights.move);
| }
|
|scope const:
|
| ///
| Lagrange lightConst()() const @property @trusted { return *cast(Lagrange*)&this; }
| ///
| Lagrange lightImmutable()() immutable @property @trusted { return *cast(Lagrange*)&this; }
|
| @property
| {
| ///
0000000| ref const(Slice!(RCI!(immutable X))) grid() { return _grid; }
| ///
720| immutable(X)[] gridScopeView() scope return const @property @trusted { return _grid.lightScope.field; }
| ///
0000000| ref const(RCArray!(immutable T)) inversedBarycentricWeights() { return _inversedBarycentricWeights; }
| ///
0000000| ref const(RCArray!T)[maxAdditionalFunctions + 1] normalizedValues() { return _normalizedValues; }
| ///
1272| ref const(T)[maxAdditionalFunctions + 1] asums() { return _asums; }
| ///
| size_t intervalCount(size_t dimension = 0)()
| {
720| assert(_grid.length > 1);
720| return _grid.length - 1;
| }
| }
|
| template opCall(uint derivative = 0)
| if (derivative <= maxAdditionalFunctions)
| {
| ///
| pragma(inline, false)
| auto opCall(T x)
| {
720| return opCall!derivative(RefTuple!(size_t, T)(this.findInterval(x), x));
| }
|
| ///
| pragma(inline, false)
| auto opCall(RefTuple!(size_t, T) tuple)
| {
|
720| const x = tuple[1];
720| const idx = tuple[0];
720| const inversedBarycentricWeights = _inversedBarycentricWeights[].sliced;
720| Slice!(const(T)*)[derivative + 1] values;
| foreach (i; Iota!(derivative + 1))
1440| values[i] = _normalizedValues[i][].sliced;
720| const T[2] pd = [
| T(x - _grid[idx + 0]).fabs,
| T(x - _grid[idx + 1]).fabs,
| ];
720| ptrdiff_t fastIndex =
60| pd[0] < T.min_normal ? idx + 0 :
684| pd[1] < T.min_normal ? idx + 1 :
636| -1;
720| if (fastIndex >= 0)
| {
| static if (derivative == 0)
| {
28| return values[0][fastIndex] * _asums[0];
| }
| else
| {
56| T[derivative + 1] ret = 0;
| foreach (_; Iota!(derivative + 1))
140| ret[_] = values[_][fastIndex] * _asums[_];
56| return ret;
| }
| }
636| T d = 0;
636| T[derivative + 1] n = 0;
9540| foreach (i; 0 .. _grid.length)
| {
2544| T w = 1 / (inversedBarycentricWeights[i] * (x - _grid[i]));
2544| d += w;
| foreach (_; Iota!(derivative + 1))
5088| n[_] += w * values[_][i];
| }
| foreach (_; Iota!(derivative + 1))
| {
1272| n[_] /= d;
1272| n[_] *= asums[_];
| }
| static if (derivative == 0)
212| return n[0];
| else
424| return n;
| }
| }
|
| static if (maxAdditionalFunctions)
| {
| ///
| alias withDerivative = opCall!1;
|
| static if (maxAdditionalFunctions > 1)
| {
| ///
| alias withTwoDerivatives = opCall!2;
| }
| }
|}
|
|
|/++
|+/
|pragma(inline, false)
|@nogc
|RCArray!(immutable T) inversedBarycentricWeights(T)(Slice!(const(T)*) x)
| if (isFloatingPoint!T)
|{
|
1| const n = x.length;
2| scope prodsa = RCArray!(ProdAccumulator!T)(n);
1| scope p = prodsa[].sliced;
18| foreach (triplet; n.iota.triplets) with(triplet)
| {
26| foreach (l; left)
| {
6| auto e = prod!T(x[center] - x[l]);
6| p[l] *= -e;
6| p[center] *= e;
| }
| }
| import mir.algorithm.iteration: reduce;
1| const minExp = long.max.reduce!min(p.member!"exp");
14| foreach (ref e; p)
4| e = e.ldexp(1 - minExp);
1| auto ret = rcarray!(immutable T)(p.member!"prod");
1| return ret;
|}
|
|/++
|Computes derivative values in the same points
|Params:
| d = derivative values (output)
| y = values
| x = grid
| w = inversed barycentric weights
|Returns:
| Derivative values in the same points
|+/
|@nogc
|Slice!(T*) polynomialDerivativeValues(T)(return Slice!(T*) d, Slice!(const(T)*) x, Slice!(const(T)*) y, Slice!(const(T)*) w)
| if (isFloatingPoint!T)
|{
| pragma(inline, false);
| import mir.math.sum;
|
2| const n = x.length;
2| assert(n == w.length);
2| assert(n == y.length);
|
2| d.fillCollapseSums!(Summation.fast,
8| (c, s1, s2) => w[c] * s1 + y[c] * s2,
16| (c, _) => y[_] / (w[_] * (x[c] - x[_])),
16| (c, _) => map!"1 / a"(x[c] - x[_]));
2| return d;
|}
|
|///
|@nogc
|Slice!(T*) polynomialDerivativeValues(T)(return Slice!(T*) d, Slice!(const(T)*) x, Slice!(const(T)*) y)
| if (isFloatingPoint!T)
|{
| return .polynomialDerivativeValues(d, x, y, x.inversedBarycentricWeights[].sliced);
|}
|
|private T asumNorm(T)(Slice!(T*) v)
|{
| pragma(inline, false);
3| auto n = v.map!fabs.sum!"fast";
3| v[] /= n;
3| return n;
|}
source/mir/interpolate/polynomial.d is 95% covered
<<<<<< EOF
# path=./source-mir-bignum-fp.lst
|/++
|Note:
| The module doesn't provide full arithmetic API for now.
|+/
|module mir.bignum.fp;
|
|import std.traits;
|import mir.bitop;
|import mir.utility;
|
|package enum half(size_t hs) = (){
| import mir.bignum.fixed: UInt;
| UInt!hs ret; ret.signBit = true; return ret;
|}();
|
|/++
|Software floating point number.
|
|Params:
| coefficientSize = coefficient size in bits
|
|Note: the implementation doesn't support NaN and Infinity values.
|+/
|struct Fp(size_t coefficientSize, Exp = sizediff_t)
| if ((is(Exp == int) || is(Exp == long)) && coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
|{
| import mir.bignum.fixed: UInt;
|
| bool sign;
| Exp exponent;
| UInt!coefficientSize coefficient;
|
| /++
| +/
| nothrow
1642| this(bool sign, Exp exponent, UInt!coefficientSize normalizedCoefficient)
| {
1642| this.coefficient = normalizedCoefficient;
1642| this.exponent = exponent;
1642| this.sign = sign;
| }
|
| /++
| Constructs $(LREF Fp) from hardaware floating point number.
| Params:
| value = Hardware floating point number. Special values `nan` and `inf` aren't allowed.
| normalize = flag to indicate if the normalization should be performed.
| +/
75| this(T)(const T value, bool normalize = true)
| @safe pure nothrow @nogc
| if (isFloatingPoint!T && T.mant_dig <= coefficientSize)
| {
| import mir.math.common : fabs;
| import mir.math.ieee : frexp, signbit, ldexp;
76| assert(value == value);
76| assert(value.fabs < T.infinity);
76| this.sign = value.signbit != 0;
76| if (value == 0)
1| return;
75| T x = value.fabs;
75| int exp;
| {
| enum scale = T(2) ^^ T.mant_dig;
75| x = frexp(x, exp) * scale;
75| exp -= T.mant_dig;
| }
| static if (T.mant_dig < 64)
| {
74| this.coefficient = UInt!coefficientSize(cast(ulong)cast(long)x);
| }
| else
| static if (T.mant_dig == 64)
| {
1| this.coefficient = UInt!coefficientSize(cast(ulong)x);
| }
| else
| {
| enum scale = T(2) ^^ 64;
| enum scaleInv = 1 / scale;
| x *= scaleInv;
| long high = cast(long) x;
| if (high > x)
| --high;
| x -= high;
| x *= scale;
| this.coefficient = (UInt!coefficientSize(ulong(high)) << 64) | cast(ulong)x;
| }
75| if (normalize)
| {
1| auto shift = cast(int)this.coefficient.ctlz;
1| exp -= shift;
1| this.coefficient <<= shift;
| }
| else
| {
74| int shift = T.min_exp - T.mant_dig - exp;
74| if (shift > 0)
| {
0000000| this.coefficient >>= shift;
0000000| exp = T.min_exp - T.mant_dig;
| }
| }
75| this.exponent = exp;
| }
|
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc nothrow
| unittest
| {
| enum h = -33.0 * 2.0 ^^ -10;
1| auto f = Fp!64(h);
1| assert(f.sign);
1| assert(f.exponent == -10 - (64 - 6));
1| assert(f.coefficient == 33UL << (64 - 6));
1| assert(cast(double) f == h);
|
| // CTFE
| static assert(cast(double) Fp!64(h) == h);
|
1| f = Fp!64(-0.0);
1| assert(f.sign);
1| assert(f.exponent == 0);
1| assert(f.coefficient == 0);
|
| // subnormals
| static assert(cast(float) Fp!64(float.min_normal / 2) == float.min_normal / 2);
| static assert(cast(float) Fp!64(float.min_normal * float.epsilon) == float.min_normal * float.epsilon);
| // subnormals
| static assert(cast(double) Fp!64(double.min_normal / 2) == double.min_normal / 2);
| static assert(cast(double) Fp!64(double.min_normal * double.epsilon) == double.min_normal * double.epsilon);
| // subnormals
| static assert(cast(real) Fp!64(real.min_normal / 2) == real.min_normal / 2);
| static assert(cast(real) Fp!64(real.min_normal * real.epsilon) == real.min_normal * real.epsilon);
|
| enum d = cast(float) Fp!64(float.min_normal / 2, false);
|
| // subnormals
| static assert(cast(float) Fp!64(float.min_normal / 2, false) == float.min_normal / 2, d.stringof);
| static assert(cast(float) Fp!64(float.min_normal * float.epsilon, false) == float.min_normal * float.epsilon);
| // subnormals
| static assert(cast(double) Fp!64(double.min_normal / 2, false) == double.min_normal / 2);
| static assert(cast(double) Fp!64(double.min_normal * double.epsilon, false) == double.min_normal * double.epsilon);
| // subnormals
| static assert(cast(real) Fp!64(real.min_normal / 2, false) == real.min_normal / 2);
| static assert(cast(real) Fp!64(real.min_normal * real.epsilon, false) == real.min_normal * real.epsilon);
| }
|
| static if (coefficientSize == 128)
| /// Without normalization
| version(mir_bignum_test)
| @safe pure @nogc nothrow
| unittest
| {
1| auto f = Fp!64(-33.0 * 2.0 ^^ -10, false);
1| assert(f.sign);
1| assert(f.exponent == -10 - (double.mant_dig - 6));
1| assert(f.coefficient == 33UL << (double.mant_dig - 6));
| }
|
| /++
| +/
839| this(size_t size)(UInt!size integer, bool normalizedInteger = false)
| nothrow
| {
| import mir.bignum.fixed: UInt;
| static if (size < coefficientSize)
| {
3| if (normalizedInteger)
| {
1| this(false, Exp(size) - coefficientSize, integer.rightExtend!(coefficientSize - size));
| }
| else
| {
2| this(integer.toSize!coefficientSize, false);
| }
| }
| else
| {
836| this.exponent = size - coefficientSize;
836| if (!normalizedInteger)
| {
10| auto c = integer.ctlz;
10| integer <<= c;
10| this.exponent -= c;
| }
| static if (size == coefficientSize)
| {
7| coefficient = integer;
| }
| else
| {
| enum N = coefficient.data.length;
| version (LittleEndian)
829| coefficient.data = integer.data[$ - N .. $];
| else
| coefficient.data = integer.data[0 .. N];
| enum tailSize = size - coefficientSize;
829| auto cr = integer.toSize!tailSize.opCmp(half!tailSize);
1465| if (cr > 0 || cr == 0 && coefficient.bt(0))
| {
245| if (auto overflow = coefficient += 1)
| {
1| coefficient = half!coefficientSize;
1| exponent++;
| }
| }
| }
| }
| }
|
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
| import mir.bignum.fixed: UInt;
|
1| auto fp = Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
1| assert(fp.exponent == 0);
1| assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
|
1| fp = Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"), true);
1| assert(fp.exponent == 0);
1| assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
|
1| fp = Fp!128(UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d"));
1| assert(fp.exponent == -20);
1| assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000"));
|
1| fp = Fp!128(UInt!128.fromHexString("e7022b0029d"));
1| assert(fp.exponent == -84);
1| assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
|
1| fp = Fp!128(UInt!64.fromHexString("e7022b0029d"));
1| assert(fp.exponent == -84);
1| assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
|
1| fp = Fp!128(UInt!64.fromHexString("e7022b0029dd0aff"), true);
1| assert(fp.exponent == -64);
1| assert(fp.coefficient == UInt!128.fromHexString("e7022b0029dd0aff0000000000000000"));
|
1| fp = Fp!128(UInt!64.fromHexString("e7022b0029d"));
1| assert(fp.exponent == -84);
1| assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
|
1| fp = Fp!128(UInt!192.fromHexString("ffffffffffffffffffffffffffffffff1000000000000000"));
1| assert(fp.exponent == 64);
1| assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
|
1| fp = Fp!128(UInt!192.fromHexString("ffffffffffffffffffffffffffffffff8000000000000000"));
1| assert(fp.exponent == 65);
1| assert(fp.coefficient == UInt!128.fromHexString("80000000000000000000000000000000"));
|
1| fp = Fp!128(UInt!192.fromHexString("fffffffffffffffffffffffffffffffe8000000000000000"));
1| assert(fp.exponent == 64);
1| assert(fp.coefficient == UInt!128.fromHexString("fffffffffffffffffffffffffffffffe"));
|
1| fp = Fp!128(UInt!192.fromHexString("fffffffffffffffffffffffffffffffe8000000000000001"));
1| assert(fp.exponent == 64);
1| assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
| }
|
| ///
| ref Fp opOpAssign(string op : "*")(Fp rhs) nothrow return
| {
66| this = this.opBinary!op(rhs);
66| return this;
| }
|
| ///
| Fp opBinary(string op : "*")(Fp rhs) nothrow const
| {
67| return cast(Fp) .extendedMul(this, rhs);
| }
|
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
| import mir.bignum.fixed: UInt;
|
1| auto a = Fp!128(0, -13, UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"));
1| auto b = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314"));
1| auto fp = a * b;
1| assert(fp.sign);
1| assert(fp.exponent == 100 - 13 + 128);
1| assert(fp.coefficient == UInt!128.fromHexString("c6841dd302415d785373ab6d93712988"));
| }
|
| ///
| T opCast(T)() nothrow const
| if (is(Unqual!T == bool))
| {
| return coefficient != 0;
| }
|
| ///
| T opCast(T, bool noHalf = false)() nothrow const
| if (isFloatingPoint!T)
| {
| import mir.math.ieee: ldexp;
793| auto exp = cast()exponent;
| static if (coefficientSize == 32)
| {
0000000| Unqual!T c = cast(uint) coefficient;
| }
| else
| static if (coefficientSize == 64)
| {
16| Unqual!T c = cast(ulong) coefficient;
| }
| else
| {
| enum shift = coefficientSize - T.mant_dig;
| enum rMask = (UInt!coefficientSize(1) << shift) - UInt!coefficientSize(1);
| enum rHalf = UInt!coefficientSize(1) << (shift - 1);
| enum rInc = UInt!coefficientSize(1) << shift;
777| UInt!coefficientSize adC = coefficient;
| static if (!noHalf)
| {
777| auto cr = (coefficient & rMask).opCmp(rHalf);
777| if ((cr > 0) | (cr == 0) & coefficient.bt(shift))
| {
300| if (auto overflow = adC += rInc)
| {
25| adC = half!coefficientSize;
25| exp++;
| }
| }
| }
777| adC >>= shift;
777| exp += shift;
777| Unqual!T c = cast(ulong) adC;
| static if (T.mant_dig > 64) //
| {
| static assert (T.mant_dig <= 128);
| c += ldexp(cast(T) cast(ulong) (adC >> 64), 64);
| }
| }
793| if (sign)
5| c = -c;
| static if (exp.sizeof > int.sizeof)
| {
| import mir.utility: min, max;
| exp = exp.max(int.min).min(int.max);
| }
793| return ldexp(c, cast(int)exp);
| }
|
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
| import mir.bignum.fixed: UInt;
1| auto fp = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112cb8b71ad3f85a970a314"));
1| assert(cast(double)fp == -0xE3251BACB112C8p+172);
| }
|
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
| import mir.bignum.fixed: UInt;
1| auto fp = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112cb8b71ad3f85a970a314"));
| static if (real.mant_dig == 64)
1| assert(cast(real)fp == -0xe3251bacb112cb8bp+164L);
| }
|
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
| import mir.bignum.fixed: UInt;
1| auto fp = Fp!64(1, 100, UInt!64(0xe3251bacb112cb8b));
| version (DigitalMars)
| {
| // https://issues.dlang.org/show_bug.cgi?id=20963
1| assert(cast(double)fp == -0xE3251BACB112C8p+108
0000000| || cast(double)fp == -0xE3251BACB112D0p+108);
| }
| else
| {
| assert(cast(double)fp == -0xE3251BACB112C8p+108);
| }
| }
|// -0x1.c64a375962259p+163 =
|// -0xe.3251bacb112cb8bp+160 =
|// -0x1.c64a37596225ap+163 =
|// -0xe.3251bacb112cb8bp+160 =
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
| import mir.bignum.fixed: UInt;
1| auto fp = Fp!64(1, 100, UInt!64(0xe3251bacb112cb8b));
| static if (real.mant_dig == 64)
1| assert(cast(real)fp == -0xe3251bacb112cb8bp+100L);
| }
|
| ///
| T opCast(T : Fp!newCoefficientSize, size_t newCoefficientSize)() nothrow const
| {
825| auto ret = Fp!newCoefficientSize(coefficient, true);
825| ret.exponent += exponent;
825| ret.sign = sign;
825| return ret;
| }
|
| static if (coefficientSize == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
| import mir.bignum.fixed: UInt;
1| auto fp = cast(Fp!64) Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2784a1de7022b0029d"));
1| assert(fp.exponent == 64);
1| assert(fp.coefficient == UInt!64.fromHexString("afbbfae3cd0aff28"));
| }
|}
|
|///
|Fp!(coefficientizeA + coefficientizeB) extendedMul(size_t coefficientizeA, size_t coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
| @safe pure nothrow @nogc
|{
| import mir.bignum.fixed: extendedMul;
842| auto coefficient = extendedMul(a.coefficient, b.coefficient);
842| auto exponent = a.exponent + b.exponent;
842| auto sign = a.sign ^ b.sign;
842| if (!coefficient.signBit)
| {
605| --exponent;
605| coefficient = coefficient.smallLeftShift(1);
| }
842| return typeof(return)(sign, exponent, coefficient);
|}
source/mir/bignum/fp.d is 97% covered
<<<<<< EOF
# path=./source-mir-math-stat.lst
|/++
|This module contains base statistical algorithms.
|
|Note that used specialized summing algorithms execute more primitive operations
|than vanilla summation. Therefore, if in certain cases maximum speed is required
|at expense of precision, one can use $(REF_ALTTEXT $(TT Summation.fast), Summation.fast, mir, math, sum)$(NBSP).
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|
|Authors: Shigeki Karita (original numir code), Ilya Yaroshenko, John Michael Hall
|
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, math, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
|+/
|module mir.math.stat;
|
|import core.lifetime: move;
|import mir.internal.utility: isFloatingPoint;
|import mir.math.common: fmamath;
|import mir.math.sum;
|import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
|import mir.primitives;
|import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType;
|public import mir.math.sum: Summation;
|
|///
|package(mir)
|template statType(T, bool checkComplex = true)
|{
| import mir.internal.utility: isFloatingPoint;
|
| static if (isFloatingPoint!T) {
| import std.traits: Unqual;
| alias statType = Unqual!T;
| } else static if (is(T : double)) {
| alias statType = double;
| } else static if (checkComplex) {
| import mir.internal.utility: isComplex;
| static if (isComplex!T) {
| import std.traits: Unqual;
| static if (is(T : cdouble)) {
| deprecated("Built-in complex types deprecated in D language version 2.097") alias statType = Unqual!T;
| } else {
| alias statType = Unqual!T;
| }
| } else static if (is(T : cdouble)) {
| deprecated("Built-in complex types deprecated in D language version 2.097") alias statType = cdouble;
| } else {
| static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a complex floating point type");
| }
| } else {
| static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a floating point type");
| }
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static assert(is(statType!int == double));
| static assert(is(statType!uint == double));
| static assert(is(statType!double == double));
| static assert(is(statType!float == float));
| static assert(is(statType!real == real));
|
| static assert(is(statType!(const(int)) == double));
| static assert(is(statType!(immutable(int)) == double));
| static assert(is(statType!(const(double)) == double));
| static assert(is(statType!(immutable(double)) == double));
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow @nogc
|unittest
|{
| static assert(is(statType!cfloat == cfloat));
| static assert(is(statType!cdouble == cdouble));
| static assert(is(statType!creal == creal));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import std.complex: Complex;
|
| static assert(is(statType!(Complex!float) == Complex!float));
| static assert(is(statType!(Complex!double) == Complex!double));
| static assert(is(statType!(Complex!real) == Complex!real));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| float x;
| alias x this;
| }
|
| static assert(is(statType!Foo == double)); // note: this is not float
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| cfloat x;
| alias x this;
| }
|
| static assert(is(statType!Foo == cdouble)); // note: this is not Complex!float
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| double x;
| alias x this;
| }
|
| static assert(is(statType!Foo == double));
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| cdouble x;
| alias x this;
| }
|
| static assert(is(statType!Foo == cdouble));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| real x;
| alias x this;
| }
|
| static assert(is(statType!Foo == double)); // note: this is not real
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| creal x;
| alias x this;
| }
|
| static assert(is(statType!Foo == cdouble)); // note: this is not Complex!real
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| int x;
| alias x this;
| }
|
| static assert(is(statType!Foo == double)); // note: this is not ints
|}
|
|///
|package(mir)
|template meanType(T)
|{
| import mir.math.sum: sumType;
|
| alias U = sumType!T;
|
| static if (__traits(compiles, {
| auto temp = U.init + U.init;
| auto a = temp / 2;
| temp += U.init;
| })) {
| alias V = typeof((U.init + U.init) / 2);
| alias meanType = statType!V;
| } else {
| static assert(0, "meanType: Can't calculate mean of elements of type " ~ U.stringof);
| }
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static assert(is(meanType!(int[]) == double));
| static assert(is(meanType!(double[]) == double));
| static assert(is(meanType!(float[]) == float));
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow @nogc
|unittest
|{
| static assert(is(meanType!(cfloat[]) == cfloat));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| float x;
| alias x this;
| }
|
| static assert(is(meanType!(Foo[]) == float));
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| cfloat x;
| alias x this;
| }
|
| static assert(is(meanType!(Foo[]) == cfloat));
|}
|
|/++
|Output range for mean.
|+/
|struct MeanAccumulator(T, Summation summation)
|{
| ///
| size_t count;
| ///
| Summator!(T, summation) summator;
|
| ///
| F mean(F = T)() const @safe @property pure nothrow @nogc
| {
1468| return cast(F) summator.sum / cast(F) count;
| }
|
| ///
| F sum(F = T)() const @safe @property pure nothrow @nogc
| {
1| return cast(F) summator.sum;
| }
|
| ///
| void put(Range)(Range r)
| if (isIterable!Range)
| {
| static if (hasShape!Range)
| {
189| count += r.elementCount;
189| summator.put(r);
| }
| else
| {
| foreach(x; r)
| {
| count++;
| summator.put(x);
| }
| }
| }
|
| ///
| void put()(T x)
| {
851| count++;
851| summator.put(x);
| }
|
| ///
| void put(F = T)(MeanAccumulator!(F, summation) m)
| {
3| count += m.count;
3| summator.put(cast(T) m.summator);
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
1| MeanAccumulator!(double, Summation.pairwise) x;
1| x.put([0.0, 1, 2, 3, 4].sliced);
1| assert(x.mean == 2);
1| x.put(5);
1| assert(x.mean == 2.5);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
1| MeanAccumulator!(float, Summation.pairwise) x;
1| x.put([0, 1, 2, 3, 4].sliced);
1| assert(x.mean == 2);
1| assert(x.sum == 10);
1| x.put(5);
1| assert(x.mean == 2.5);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
1| double[] x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25];
1| double[] y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
|
1| MeanAccumulator!(float, Summation.pairwise) m0;
1| m0.put(x);
1| MeanAccumulator!(float, Summation.pairwise) m1;
1| m1.put(y);
1| m0.put(m1);
1| assert(m0.mean == 29.25 / 12);
|}
|
|/++
|Computes the mean of the input.
|
|By default, if `F` is not floating point type or complex type, then the result
|will have a `double` type if `F` is implicitly convertible to a floating point
|type or a type for which `isComplex!F` is true.
|
|Params:
| F = controls type of output
| summation = algorithm for calculating sums (default: Summation.appropriate)
|Returns:
| The mean of all the elements in the input, must be floating point or complex type
|
|See_also:
| $(SUBREF sum, Summation)
|+/
|template mean(F, Summation summation = Summation.appropriate)
|{
| /++
| Params:
| r = range, must be finite iterable
| +/
| @fmamath meanType!F mean(Range)(Range r)
| if (isIterable!Range)
| {
| alias G = typeof(return);
175| MeanAccumulator!(G, ResolveSummationType!(summation, Range, G)) mean;
172| mean.put(r.move);
172| return mean.mean;
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath meanType!F mean(scope const F[] ar...)
| {
| alias G = typeof(return);
5| MeanAccumulator!(G, ResolveSummationType!(summation, const(G)[], G)) mean;
5| mean.put(ar);
5| return mean.mean;
| }
|}
|
|/// ditto
|template mean(Summation summation = Summation.appropriate)
|{
| /++
| Params:
| r = range, must be finite iterable
| +/
| @fmamath meanType!Range mean(Range)(Range r)
| if (isIterable!Range)
| {
| alias F = typeof(return);
168| return .mean!(F, summation)(r.move);
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath meanType!T mean(T)(scope const T[] ar...)
| {
| alias F = typeof(return);
1| return .mean!(F, summation)(ar);
| }
|}
|
|/// ditto
|template mean(F, string summation)
|{
| mixin("alias mean = .mean!(F, Summation." ~ summation ~ ");");
|}
|
|/// ditto
|template mean(string summation)
|{
| mixin("alias mean = .mean!(Summation." ~ summation ~ ");");
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
1| assert(mean([1.0, 2, 3]) == 2);
1| assert(mean([1.0 + 3i, 2, 3]) == 2 + 1i);
|
1| assert(mean!float([0, 1, 2, 3, 4, 5].sliced(3, 2)) == 2.5);
|
| static assert(is(typeof(mean!float([1, 2, 3])) == float));
|}
|
|/// Mean of vector
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
1| assert(x.mean == 29.25 / 12);
|}
|
|/// Mean of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.ndslice.fuse: fuse;
|
1| auto x = [
| [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
| [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
| ].fuse;
|
1| assert(x.mean == 29.25 / 12);
|}
|
|/// Column mean of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: alongDim, byDim, map;
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
|
1| auto x = [
| [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
| [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
| ].fuse;
1| auto result = [1, 4.25, 3.25, 1.5, 2.5, 2.125];
|
| // Use byDim or alongDim with map to compute mean of row/column.
1| assert(x.byDim!1.map!mean.all!approxEqual(result));
1| assert(x.alongDim!0.map!mean.all!approxEqual(result));
|
| // FIXME
| // Without using map, computes the mean of the whole slice
| // assert(x.byDim!1.mean == x.sliced.mean);
| // assert(x.alongDim!0.mean == x.sliced.mean);
|}
|
|/// Can also set algorithm or output type
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: repeat;
|
| //Set sum algorithm or output type
|
1| auto a = [1, 1e100, 1, -1e100].sliced;
|
1| auto x = a * 10_000;
|
1| assert(x.mean!"kbn" == 20_000 / 4);
1| assert(x.mean!"kb2" == 20_000 / 4);
1| assert(x.mean!"precise" == 20_000 / 4);
1| assert(x.mean!(double, "precise") == 20_000.0 / 4);
|
1| auto y = uint.max.repeat(3);
1| assert(y.mean!ulong == 12884901885 / 3);
|}
|
|/++
|For integral slices, pass output type as template parameter to ensure output
|type is correct.
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0, 1, 1, 2, 4, 4,
| 2, 7, 5, 1, 2, 0].sliced;
|
1| auto y = x.mean;
1| assert(y.approxEqual(29.0 / 12, 1.0e-10));
| static assert(is(typeof(y) == double));
|
1| assert(x.mean!float.approxEqual(29f / 12, 1.0e-10));
|}
|
|/++
|Mean works for complex numbers and other user-defined types (provided they
|can be converted to a floating point or complex type)
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
1| assert(x.mean.approxEqual(2.5 + 3.5i));
|}
|
|/// Compute mean tensors along specified dimention of tensors
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice: alongDim, iota, as, map;
| /++
| [[0,1,2],
| [3,4,5]]
| +/
1| auto x = iota(2, 3).as!double;
1| assert(x.mean == (5.0 / 2.0));
|
1| auto m0 = [(0.0+3.0)/2.0, (1.0+4.0)/2.0, (2.0+5.0)/2.0];
1| assert(x.alongDim!0.map!mean == m0);
1| assert(x.alongDim!(-2).map!mean == m0);
|
1| auto m1 = [(0.0+1.0+2.0)/3.0, (3.0+4.0+5.0)/3.0];
1| assert(x.alongDim!1.map!mean == m1);
1| assert(x.alongDim!(-1).map!mean == m1);
|
1| assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!mean == iota([3, 4, 5], 3 * 4 * 5 / 2));
|}
|
|/// Arbitrary mean
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
1| assert(mean(1.0, 2, 3) == 2);
1| assert(mean!float(1, 2, 3) == 2);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
1| assert([1.0, 2, 3, 4].mean == 2.5);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.topology: iota, alongDim, map;
|
1| auto x = iota([2, 2], 1);
1| auto y = x.alongDim!1.map!mean;
1| assert(y.all!approxEqual([1.5, 3.5]));
| static assert(is(meanType!(typeof(y)) == double));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.ndslice.slice: sliced;
|
| static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
|
1| assert(x.sliced.mean == 29.25 / 12);
1| assert(x.sliced.mean!float == 29.25 / 12);
|}
|
|///
|package(mir)
|template hmeanType(T)
|{
| import mir.math.sum: sumType;
|
| alias U = sumType!T;
|
| static if (__traits(compiles, {
| U t = U.init + cast(U) 1; //added for when U.init = 0
| auto temp = cast(U) 1 / t + cast(U) 1 / t;
| })) {
| alias V = typeof(cast(U) 1 / ((cast(U) 1 / U.init + cast(U) 1 / U.init) / cast(U) 2));
| alias hmeanType = statType!V;
| } else {
| static assert(0, "hmeanType: Can't calculate hmean of elements of type " ~ U.stringof);
| }
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static assert(is(hmeanType!(int[]) == double));
| static assert(is(hmeanType!(double[]) == double));
| static assert(is(hmeanType!(float[]) == float));
| static assert(is(hmeanType!(cfloat[]) == cfloat));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| float x;
| alias x this;
| }
|
| static struct Bar {
| cfloat x;
| alias x this;
| }
|
| static assert(is(hmeanType!(Foo[]) == float));
| static assert(is(hmeanType!(Bar[]) == cfloat));
|}
|
|/++
|Computes the harmonic mean of the input.
|
|By default, if `F` is not floating point type or complex type, then the result
|will have a `double` type if `F` is implicitly convertible to a floating point
|type or a type for which `isComplex!F` is true.
|
|Params:
| F = controls type of output
| summation = algorithm for calculating sums (default: Summation.appropriate)
|Returns:
| harmonic mean of all the elements of the input, must be floating point or complex type
|
|See_also:
| $(SUBREF sum, Summation)
|+/
|template hmean(F, Summation summation = Summation.appropriate)
|{
| /++
| Params:
| r = range
| +/
| @fmamath hmeanType!F hmean(Range)(Range r)
| if (isIterable!Range)
| {
| import mir.ndslice.topology: map;
|
| alias G = typeof(return);
18| auto numerator = cast(G) 1;
|
| static if (summation == Summation.fast && __traits(compiles, r.move.map!"numerator / a"))
| {
| return numerator / r.move.map!"numerator / a".mean!(G, summation);
| }
| else
| {
20| MeanAccumulator!(G, ResolveSummationType!(summation, Range, G)) imean;
243| foreach (e; r)
69| imean.put(numerator / e);
18| return numerator / imean.mean;
| }
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath hmeanType!F hmean(scope const F[] ar...)
| {
| alias G = typeof(return);
|
2| auto numerator = cast(G) 1;
|
| static if (summation == Summation.fast && __traits(compiles, ar.map!"numerator / a"))
| {
| return numerator / ar.map!"numerator / a".mean!(G, summation);
| }
| else
| {
2| MeanAccumulator!(G, ResolveSummationType!(summation, const(G)[], G)) imean;
42| foreach (e; ar)
12| imean.put(numerator / e);
2| return numerator / imean.mean;
| }
| }
|}
|
|/// ditto
|template hmean(Summation summation = Summation.appropriate)
|{
| /++
| Params:
| r = range
| +/
| @fmamath hmeanType!Range hmean(Range)(Range r)
| if (isIterable!Range)
| {
| alias F = typeof(return);
14| return .hmean!(F, summation)(r.move);
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath hmeanType!T hmean(T)(scope const T[] ar...)
| {
| alias F = typeof(return);
1| return .hmean!(F, summation)(ar);
| }
|}
|
|/// ditto
|template hmean(F, string summation)
|{
| mixin("alias hmean = .hmean!(F, Summation." ~ summation ~ ");");
|}
|
|/// ditto
|template hmean(string summation)
|{
| mixin("alias hmean = .hmean!(Summation." ~ summation ~ ");");
|}
|
|/// Harmonic mean of vector
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [20.0, 100.0, 2000.0, 10.0, 5.0, 2.0].sliced;
|
1| assert(x.hmean.approxEqual(6.97269));
|}
|
|/// Harmonic mean of matrix
|version(mir_test)
|pure @safe
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
|
1| auto x = [
| [20.0, 100.0, 2000.0],
| [10.0, 5.0, 2.0]
| ].fuse;
|
1| assert(x.hmean.approxEqual(6.97269));
|}
|
|/// Column harmonic mean of matrix
|version(mir_test)
|pure @safe
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice: fuse;
| import mir.ndslice.topology: alongDim, byDim, map;
|
1| auto x = [
| [20.0, 100.0, 2000.0],
| [ 10.0, 5.0, 2.0]
| ].fuse;
|
1| auto y = [13.33333, 9.52381, 3.996004];
|
| // Use byDim or alongDim with map to compute mean of row/column.
1| assert(x.byDim!1.map!hmean.all!approxEqual(y));
1| assert(x.alongDim!0.map!hmean.all!approxEqual(y));
|}
|
|/// Can also pass arguments to hmean
|version(mir_test)
|pure @safe nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.topology: repeat;
| import mir.ndslice.slice: sliced;
|
| //Set sum algorithm or output type
1| auto x = [1, 1e-100, 1, -1e-100].sliced;
|
1| assert(x.hmean!"kb2".approxEqual(2));
1| assert(x.hmean!"precise".approxEqual(2));
1| assert(x.hmean!(double, "precise").approxEqual(2));
|
| //Provide the summation type
1| assert(float.max.repeat(3).hmean!double.approxEqual(float.max));
|}
|
|/++
|For integral slices, pass output type as template parameter to ensure output
|type is correct.
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [20, 100, 2000, 10, 5, 2].sliced;
|
1| auto y = x.hmean;
|
1| assert(y.approxEqual(6.97269));
| static assert(is(typeof(y) == double));
|
1| assert(x.hmean!float.approxEqual(6.97269));
|}
|
|/++
|hmean works for complex numbers and other user-defined types (provided they
|can be converted to a floating point or complex type)
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
1| assert(x.hmean.approxEqual(1.97110904 + 3.14849332i));
|}
|
|/// Arbitrary harmonic mean
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = hmean(20.0, 100, 2000, 10, 5, 2);
1| assert(x.approxEqual(6.97269));
|
1| auto y = hmean!float(20, 100, 2000, 10, 5, 2);
1| assert(y.approxEqual(6.97269));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
| static immutable x = [20.0, 100.0, 2000.0, 10.0, 5.0, 2.0];
|
1| assert(x.sliced.hmean.approxEqual(6.97269));
1| assert(x.sliced.hmean!float.approxEqual(6.97269));
|}
|
|private
|F nthroot(F)(in F x, in size_t n)
| if (isFloatingPoint!F)
|{
| import mir.math.common: sqrt, pow;
|
54| if (n > 2) {
25| return pow(x, cast(F) 1 / cast(F) n);
29| } else if (n == 2) {
27| return sqrt(x);
2| } else if (n == 1) {
1| return x;
| } else {
1| return cast(F) 1;
| }
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
|
1| assert(nthroot(9.0, 0).approxEqual(1));
1| assert(nthroot(9.0, 1).approxEqual(9));
1| assert(nthroot(9.0, 2).approxEqual(3));
1| assert(nthroot(9.5, 2).approxEqual(3.08220700));
1| assert(nthroot(9.0, 3).approxEqual(2.08008382));
|}
|
|/++
|Output range for gmean.
|+/
|struct GMeanAccumulator(T)
| if (isMutable!T && isFloatingPoint!T)
|{
| import mir.math.numeric: ProdAccumulator;
|
| ///
| size_t count;
| ///
| ProdAccumulator!T prodAccumulator;
|
| ///
| F gmean(F = T)() @property
| if (isFloatingPoint!F)
| {
| import mir.math.common: exp2;
|
49| return nthroot(cast(F) prodAccumulator.mantissa, count) * exp2(cast(F) prodAccumulator.exp / count);
| }
|
| ///
| void put(Range)(Range r)
| if (isIterable!Range)
| {
| static if (hasShape!Range)
| {
47| count += r.elementCount;
47| prodAccumulator.put(r);
| }
| else
| {
| foreach(x; r)
| {
| count++;
| prodAccumulator.put(x);
| }
| }
| }
|
| ///
| void put()(T x)
| {
2| count++;
2| prodAccumulator.put(x);
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| GMeanAccumulator!double x;
1| x.put([1.0, 2, 3, 4].sliced);
1| assert(x.gmean.approxEqual(2.21336384));
1| x.put(5);
1| assert(x.gmean.approxEqual(2.60517108));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| GMeanAccumulator!float x;
1| x.put([1, 2, 3, 4].sliced);
1| assert(x.gmean.approxEqual(2.21336384));
1| x.put(5);
1| assert(x.gmean.approxEqual(2.60517108));
|}
|
|///
|package(mir)
|template gmeanType(T)
|{
| import mir.math.numeric: prodType;
|
| alias U = prodType!T;
|
| static if (__traits(compiles, {
| auto temp = U.init * U.init;
| auto a = nthroot(temp, 2);
| temp *= U.init;
| })) {
| alias V = typeof(nthroot(U.init * U.init, 2));
| alias gmeanType = statType!(V, false);
| } else {
| static assert(0, "gmeanType: Can't calculate gmean of elements of type " ~ U.stringof);
| }
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static assert(is(gmeanType!int == double));
| static assert(is(gmeanType!double == double));
| static assert(is(gmeanType!float == float));
| static assert(is(gmeanType!(int[]) == double));
| static assert(is(gmeanType!(double[]) == double));
| static assert(is(gmeanType!(float[]) == float));
|}
|
|/++
|Computes the geometric average of the input.
|
|By default, if `F` is not floating point type, then the result will have a
|`double` type if `F` is implicitly convertible to a floating point type.
|
|Params:
| r = range, must be finite iterable
|Returns:
| The geometric average of all the elements in the input, must be floating point type
|
|See_also:
| $(SUBREF numeric, prod)
|+/
|@fmamath gmeanType!F gmean(F, Range)(Range r)
| if (isFloatingPoint!F && isIterable!Range)
|{
| alias G = typeof(return);
43| GMeanAccumulator!G gmean;
43| gmean.put(r.move);
43| return gmean.gmean;
|}
|
|/// ditto
|@fmamath gmeanType!Range gmean(Range)(Range r)
| if (isIterable!Range)
|{
| alias G = typeof(return);
38| return .gmean!(G, Range)(r.move);
|}
|
|/++
|Params:
| ar = values
|+/
|@fmamath gmeanType!F gmean(F)(scope const F[] ar...)
| if (isFloatingPoint!F)
|{
| alias G = typeof(return);
2| GMeanAccumulator!G gmean;
2| gmean.put(ar);
2| return gmean.gmean;
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| assert(gmean([1.0, 2, 3]).approxEqual(1.81712059));
|
1| assert(gmean!float([1, 2, 3, 4, 5, 6].sliced(3, 2)).approxEqual(2.99379516));
|
| static assert(is(typeof(gmean!float([1, 2, 3])) == float));
|}
|
|/// Geometric mean of vector
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [3.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 2.0].sliced;
|
1| assert(x.gmean.approxEqual(2.36178395));
|}
|
|/// Geometric mean of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
|
1| auto x = [
| [3.0, 1.0, 1.5, 2.0, 3.5, 4.25],
| [2.0, 7.5, 5.0, 1.0, 1.5, 2.0]
| ].fuse;
|
1| assert(x.gmean.approxEqual(2.36178395));
|}
|
|/// Column gmean of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: alongDim, byDim, map;
|
1| auto x = [
| [3.0, 1.0, 1.5, 2.0, 3.5, 4.25],
| [2.0, 7.5, 5.0, 1.0, 1.5, 2.0]
| ].fuse;
1| auto result = [2.44948974, 2.73861278, 2.73861278, 1.41421356, 2.29128784, 2.91547594];
|
| // Use byDim or alongDim with map to compute mean of row/column.
1| assert(x.byDim!1.map!gmean.all!approxEqual(result));
1| assert(x.alongDim!0.map!gmean.all!approxEqual(result));
|
| // FIXME
| // Without using map, computes the mean of the whole slice
| // assert(x.byDim!1.gmean.all!approxEqual(result));
| // assert(x.alongDim!0.gmean.all!approxEqual(result));
|}
|
|/// Can also set output type
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: repeat;
|
1| auto x = [5120.0, 7340032, 32, 3758096384].sliced;
|
1| assert(x.gmean!float.approxEqual(259281.45295212));
|
1| auto y = uint.max.repeat(2);
1| assert(y.gmean!float.approxEqual(cast(float) uint.max));
|}
|
|/++
|For integral slices, pass output type as template parameter to ensure output
|type is correct.
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [5, 1, 1, 2, 4, 4,
| 2, 7, 5, 1, 2, 10].sliced;
|
1| auto y = x.gmean;
| static assert(is(typeof(y) == double));
|
1| assert(x.gmean!float.approxEqual(2.79160522));
|}
|
|/// gean works for user-defined types, provided the nth root can be taken for them
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| static struct Foo {
| float x;
| alias x this;
| }
|
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [Foo(1.0), Foo(2.0), Foo(3.0)].sliced;
1| assert(x.gmean.approxEqual(1.81712059));
|}
|
|/// Compute gmean tensors along specified dimention of tensors
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: alongDim, iota, map;
|
1| auto x = [
| [1.0, 2, 3],
| [4.0, 5, 6]
| ].fuse;
|
1| assert(x.gmean.approxEqual(2.99379516));
|
1| auto result0 = [2.0, 3.16227766, 4.24264069];
1| assert(x.alongDim!0.map!gmean.all!approxEqual(result0));
1| assert(x.alongDim!(-2).map!gmean.all!approxEqual(result0));
|
1| auto result1 = [1.81712059, 4.93242414];
1| assert(x.alongDim!1.map!gmean.all!approxEqual(result1));
1| assert(x.alongDim!(-1).map!gmean.all!approxEqual(result1));
|
1| auto y = [
| [
| [1.0, 2, 3],
| [4.0, 5, 6]
| ], [
| [7.0, 8, 9],
| [10.0, 9, 10]
| ]
| ].fuse;
|
1| auto result3 = [
| [2.64575131, 4.0, 5.19615242],
| [6.32455532, 6.70820393, 7.74596669]
| ];
1| assert(y.alongDim!0.map!gmean.all!approxEqual(result3));
|}
|
|/// Arbitrary gmean
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
|
1| assert(gmean(1.0, 2, 3).approxEqual(1.81712059));
1| assert(gmean!float(1, 2, 3).approxEqual(1.81712059));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
|
1| assert([1.0, 2, 3, 4].gmean.approxEqual(2.21336384));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
|
1| assert(gmean([1, 2, 3]).approxEqual(1.81712059));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
| static immutable x = [3.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 2.0];
|
1| assert(x.sliced.gmean.approxEqual(2.36178395));
1| assert(x.sliced.gmean!float.approxEqual(2.36178395));
|}
|
|/++
|Computes the median of `slice`.
|
|By default, if `F` is not floating point type or complex type, then the result
|will have a `double` type if `F` is implicitly convertible to a floating point
|type or a type for which `isComplex!F` is true.
|
|Can also pass a boolean variable, `allowModify`, that allows the input slice to
|be modified. By default, a reference-counted copy is made.
|
|Params:
| F = output type
| allowModify = Allows the input slice to be modified, default is false
|Returns:
| the median of the slice
|
|See_also:
| $(SUBREF stat, mean)
|+/
|template median(F, bool allowModify = false)
|{
| /++
| Params:
| slice = slice
| +/
| @nogc
| meanType!F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
| static assert (!allowModify ||
| isMutable!(slice.DeepElement),
| "allowModify must be false or the input must be mutable");
| alias G = typeof(return);
82| size_t len = slice.elementCount;
82| assert(len > 0, "median: slice must have length greater than zero");
|
| import mir.ndslice.topology: as, flattened;
|
| static if (!allowModify) {
| import mir.ndslice.allocation: rcslice;
|
41| if (len > 2) {
39| auto view = slice.lightScope;
78| auto val = view.as!(Unqual!(slice.DeepElement)).rcslice;
39| auto temp = val.lightScope.flattened;
39| return .median!(G, true)(temp);
| } else {
2| return mean!G(slice);
| }
| } else {
| import mir.ndslice.sorting: partitionAt;
|
41| auto temp = slice.flattened;
|
41| if (len > 5) {
36| size_t half_n = len / 2;
36| partitionAt(temp, half_n);
36| if (len % 2 == 1) {
4| return cast(G) temp[half_n];
| } else {
| //move largest value in first half of slice to half_n - 1
32| partitionAt(temp[0 .. half_n], half_n - 1);
32| return (temp[half_n - 1] + temp[half_n]) / cast(G) 2;
| }
| } else {
5| return smallMedianImpl!(G)(temp);
| }
| }
| }
|}
|
|/// ditto
|template median(bool allowModify = false)
|{
| /// ditto
| meanType!(Slice!(Iterator, N, kind))
| median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
| static assert (!allowModify ||
| isMutable!(DeepElementType!(Slice!(Iterator, N, kind))),
| "allowModify must be false or the input must be mutable");
| alias F = typeof(return);
18| return .median!(F, allowModify)(slice.move);
| }
|}
|
|/++
|Params:
| ar = array
|+/
|meanType!(T[]) median(T)(scope const T[] ar...)
|{
| import mir.ndslice.slice: sliced;
|
| alias F = typeof(return);
3| return median!(F, false)(ar.sliced);
|}
|
|/++
|Params:
| withAsSlice = input that satisfies hasAsSlice
|+/
|auto median(T)(T withAsSlice)
| if (hasAsSlice!T)
|{
1| return median(withAsSlice.asSlice);
|}
|
|/// Median of vector
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
1| auto x0 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5].sliced;
1| assert(x0.median == 5);
|
1| auto x1 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10].sliced;
1| assert(x1.median == 5);
|}
|
|/// Median of dynamic array
|version(mir_test)
|@safe pure nothrow
|unittest
|{
1| auto x0 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5];
1| assert(x0.median == 5);
|
1| auto x1 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10];
1| assert(x1.median == 5);
|}
|
|/// Median of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.ndslice.fuse: fuse;
|
1| auto x0 = [
| [9.0, 1, 0, 2, 3],
| [4.0, 6, 8, 7, 10]
| ].fuse;
|
1| assert(x0.median == 5);
|}
|
|/// Row median of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: alongDim, byDim, map;
|
1| auto x = [
| [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
| [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
| ].fuse;
|
1| auto result = [1.75, 1.75].sliced;
|
| // Use byDim or alongDim with map to compute median of row/column.
1| assert(x.byDim!0.map!median.all!approxEqual(result));
1| assert(x.alongDim!1.map!median.all!approxEqual(result));
|}
|
|/// Can allow original slice to be modified or set output type
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
1| auto x0 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5].sliced;
1| assert(x0.median!true == 5);
|
1| auto x1 = [9, 1, 0, 2, 3, 4, 6, 8, 7, 10].sliced;
1| assert(x1.median!(float, true) == 5);
|}
|
|/// Arbitrary median
|version(mir_test)
|@safe pure nothrow
|unittest
|{
1| assert(median(0, 1, 2, 3, 4) == 2);
|}
|
|// @nogc test
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.ndslice.slice: sliced;
|
| static immutable x = [9.0, 1, 0, 2, 3];
1| assert(x.sliced.median == 2);
|}
|
|// withAsSlice test
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.rc.array: RCArray;
|
| static immutable a = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5];
|
2| auto x = RCArray!double(11);
47| foreach(i, ref e; x)
11| e = a[i];
|
1| assert(x.median.approxEqual(5));
|}
|
|/++
|For integral slices, can pass output type as template parameter to ensure output
|type is correct
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
1| auto x = [9, 1, 0, 2, 3, 4, 6, 8, 7, 10].sliced;
1| assert(x.median!float == 5f);
|
1| auto y = x.median;
1| assert(y == 5.0);
| static assert(is(typeof(y) == double));
|}
|
|// additional logic tests
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [3, 3, 2, 0, 2, 0].sliced;
1| assert(x.median!float.approxEqual(2));
|
1| x[] = [2, 2, 4, 0, 4, 3];
1| assert(x.median!float.approxEqual(2.5));
1| x[] = [1, 4, 5, 4, 4, 3];
1| assert(x.median!float.approxEqual(4));
1| x[] = [1, 5, 3, 5, 2, 2];
1| assert(x.median!float.approxEqual(2.5));
1| x[] = [4, 3, 2, 1, 4, 5];
1| assert(x.median!float.approxEqual(3.5));
1| x[] = [4, 5, 3, 5, 5, 4];
1| assert(x.median!float.approxEqual(4.5));
1| x[] = [3, 3, 3, 0, 0, 1];
1| assert(x.median!float.approxEqual(2));
1| x[] = [4, 2, 2, 1, 2, 5];
1| assert(x.median!float.approxEqual(2));
1| x[] = [2, 3, 1, 4, 5, 5];
1| assert(x.median!float.approxEqual(3.5));
1| x[] = [1, 1, 4, 5, 5, 5];
1| assert(x.median!float.approxEqual(4.5));
1| x[] = [2, 4, 0, 5, 1, 0];
1| assert(x.median!float.approxEqual(1.5));
1| x[] = [3, 5, 2, 5, 4, 2];
1| assert(x.median!float.approxEqual(3.5));
1| x[] = [3, 5, 4, 1, 4, 3];
1| assert(x.median!float.approxEqual(3.5));
1| x[] = [4, 2, 0, 3, 1, 3];
1| assert(x.median!float.approxEqual(2.5));
1| x[] = [100, 4, 5, 0, 5, 1];
1| assert(x.median!float.approxEqual(4.5));
1| x[] = [100, 5, 4, 0, 5, 1];
1| assert(x.median!float.approxEqual(4.5));
1| x[] = [100, 5, 4, 0, 1, 5];
1| assert(x.median!float.approxEqual(4.5));
1| x[] = [4, 5, 100, 1, 5, 0];
1| assert(x.median!float.approxEqual(4.5));
1| x[] = [0, 1, 2, 2, 3, 4];
1| assert(x.median!float.approxEqual(2));
1| x[] = [0, 2, 2, 3, 4, 5];
1| assert(x.median!float.approxEqual(2.5));
|}
|
|// smallMedianImpl tests
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x0 = [9.0, 1, 0, 2, 3].sliced;
1| assert(x0.median.approxEqual(2));
|
1| auto x1 = [9.0, 1, 0, 2].sliced;
1| assert(x1.median.approxEqual(1.5));
|
1| auto x2 = [9.0, 0, 1].sliced;
1| assert(x2.median.approxEqual(1));
|
1| auto x3 = [1.0, 0].sliced;
1| assert(x3.median.approxEqual(0.5));
|
1| auto x4 = [1.0].sliced;
1| assert(x4.median.approxEqual(1));
|}
|
|// Check issue #328 fixed
|version(mir_test)
|@safe pure nothrow
|unittest {
| import mir.ndslice.topology: iota;
|
1| auto x = iota(18);
1| auto y = median(x);
1| assert(y == 8.5);
|}
|
|private pure @trusted nothrow @nogc
|F smallMedianImpl(F, Iterator)(Slice!Iterator slice)
|{
13| size_t n = slice.elementCount;
|
13| assert(n > 0, "smallMedianImpl: slice must have elementCount greater than 0");
13| assert(n <= 5, "smallMedianImpl: slice must have elementCount of 5 or less");
|
| import mir.functional: naryFun;
| import mir.ndslice.sorting: medianOf;
| import mir.utility: swapStars;
|
13| auto sliceI0 = slice._iterator;
|
13| if (n == 1) {
1| return cast(F) *sliceI0;
| }
|
12| auto sliceI1 = sliceI0;
12| ++sliceI1;
|
12| if (n > 2) {
11| auto sliceI2 = sliceI1;
11| ++sliceI2;
| alias less = naryFun!("a < b");
|
11| if (n == 3) {
2| medianOf!less(sliceI0, sliceI1, sliceI2);
2| return cast(F) *sliceI1;
| } else {
9| auto sliceI3 = sliceI2;
9| ++sliceI3;
9| if (n == 4) {
| // Put min in slice[0], lower median in slice[1]
5| medianOf!less(sliceI0, sliceI1, sliceI2, sliceI3);
| // Ensure slice[2] < slice[3]
5| medianOf!less(sliceI2, sliceI3);
5| return cast(F) (*sliceI1 + *sliceI2) / cast(F) 2;
| } else {
4| auto sliceI4 = sliceI3;
4| ++sliceI4;
4| medianOf!less(sliceI0, sliceI1, sliceI2, sliceI3, sliceI4);
4| return cast(F) *sliceI2;
| }
| }
| } else {
1| return cast(F) (*sliceI0 + *sliceI1) / cast(F) 2;
| }
|}
|
|// smallMedianImpl tests
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x0 = [9.0, 1, 0, 2, 3].sliced;
1| assert(x0.smallMedianImpl!double.approxEqual(2));
|
1| auto x1 = [9.0, 1, 0, 2].sliced;
1| assert(x1.smallMedianImpl!double.approxEqual(1.5));
|
1| auto x2 = [9.0, 0, 1].sliced;
1| assert(x2.smallMedianImpl!double.approxEqual(1));
|
1| auto x3 = [1.0, 0].sliced;
1| assert(x3.smallMedianImpl!double.approxEqual(0.5));
|
1| auto x4 = [1.0].sliced;
1| assert(x4.smallMedianImpl!double.approxEqual(1));
|
1| auto x5 = [2.0, 1, 0, 9].sliced;
1| assert(x5.smallMedianImpl!double.approxEqual(1.5));
|
1| auto x6 = [1.0, 2, 0, 9].sliced;
1| assert(x6.smallMedianImpl!double.approxEqual(1.5));
|
1| auto x7 = [1.0, 0, 9, 2].sliced;
1| assert(x7.smallMedianImpl!double.approxEqual(1.5));
|}
|
|/++
|Centers `slice`, which must be a finite iterable.
|
|By default, `slice` is centered by the mean. A custom function may also be
|provided using `centralTendency`.
|
|Returns:
| The elements in the slice with the average subtracted from them.
|+/
|template center(alias centralTendency = mean!(Summation.appropriate))
|{
| import mir.ndslice.slice: Slice, SliceKind, sliced, hasAsSlice;
| /++
| Params:
| slice = slice
| +/
| auto center(Iterator, size_t N, SliceKind kind)(
| Slice!(Iterator, N, kind) slice)
| {
| import core.lifetime: move;
| import mir.ndslice.internal: LeftOp, ImplicitlyUnqual;
| import mir.ndslice.topology: vmap;
|
25| auto m = centralTendency(slice.lightScope);
| alias T = typeof(m);
25| return slice.move.vmap(LeftOp!("-", ImplicitlyUnqual!T)(m));
| }
|
| /// ditto
| auto center(T)(T[] array)
| {
2| return center(array.sliced);
| }
|
| /// ditto
| auto center(T)(T withAsSlice)
| if (hasAsSlice!T)
| {
1| return center(withAsSlice.asSlice);
| }
|}
|
|/// Center vector
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [1.0, 2, 3, 4, 5, 6].sliced;
1| assert(x.center.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
|
| // Can center using different functions
1| assert(x.center!hmean.all!approxEqual([-1.44898, -0.44898, 0.55102, 1.55102, 2.55102, 3.55102]));
1| assert(x.center!gmean.all!approxEqual([-1.99379516, -0.99379516, 0.00620483, 1.00620483, 2.00620483, 3.00620483]));
1| assert(x.center!median.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
|}
|
|/// Center dynamic array
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
|
1| auto x = [1.0, 2, 3, 4, 5, 6];
1| assert(x.center.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
|}
|
|/// Center matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice: fuse;
|
1| auto x = [
| [0.0, 1, 2],
| [3.0, 4, 5]
| ].fuse;
|
1| auto y = [
| [-2.5, -1.5, -0.5],
| [ 0.5, 1.5, 2.5]
| ].fuse;
|
1| assert(x.center.all!approxEqual(y));
|}
|
|/// Column center matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all, equal;
| import mir.math.common: approxEqual;
| import mir.ndslice: fuse;
| import mir.ndslice.topology: alongDim, byDim, map;
|
1| auto x = [
| [20.0, 100.0, 2000.0],
| [10.0, 5.0, 2.0]
| ].fuse;
|
1| auto result = [
| [ 5.0, 47.5, 999],
| [-5.0, -47.5, -999]
| ].fuse;
|
| // Use byDim with map to compute average of row/column.
1| auto xCenterByDim = x.byDim!1.map!center;
1| auto resultByDim = result.byDim!1;
1| assert(xCenterByDim.equal!(equal!approxEqual)(resultByDim));
|
1| auto xCenterAlongDim = x.alongDim!0.map!center;
1| auto resultAlongDim = result.alongDim!0;
1| assert(xCenterByDim.equal!(equal!approxEqual)(resultAlongDim));
|}
|
|/// Can also pass arguments to average function used by center
|version(mir_test)
|pure @safe nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
| //Set sum algorithm or output type
1| auto a = [1, 1e100, 1, -1e100];
|
1| auto x = a.sliced * 10_000;
|
| //Due to Floating Point precision, subtracting the mean from the second
| //and fourth numbers in `x` does not change the value of the result
1| auto result = [5000, 1e104, 5000, -1e104].sliced;
|
1| assert(x.center!(mean!"kbn") == result);
1| assert(x.center!(mean!"kb2") == result);
1| assert(x.center!(mean!"precise") == result);
|}
|
|/++
|Passing a centered input to `variance` or `standardDeviation` with the
|`assumeZeroMean` algorithm is equivalent to calculating `variance` or
|`standardDeviation` on the original input.
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [1.0, 2, 3, 4, 5, 6].sliced;
1| assert(x.center.variance!"assumeZeroMean".approxEqual(x.variance));
1| assert(x.center.standardDeviation!"assumeZeroMean".approxEqual(x.standardDeviation));
|}
|
|// dynamic array test
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
|
1| double[] x = [1.0, 2, 3, 4, 5, 6];
|
1| assert(x.center.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
|}
|
|// withAsSlice test
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.rc.array: RCArray;
|
| static immutable a = [1.0, 2, 3, 4, 5, 6];
| static immutable result = [-2.5, -1.5, -0.5, 0.5, 1.5, 2.5];
|
2| auto x = RCArray!double(6);
27| foreach(i, ref e; x)
6| e = a[i];
|
1| assert(x.center.all!approxEqual(result));
|}
|
|/++
|Output range that applies function `fun` to each input before summing
|+/
|struct MapSummator(alias fun, T, Summation summation)
| if(isMutable!T)
|{
| ///
| Summator!(T, summation) summator;
|
| ///
| F sum(F = T)() @property
| {
8| return cast(F) summator.sum;
| }
|
| ///
| void put(Range)(Range r)
| if (isIterable!Range)
| {
| import mir.ndslice.topology: map;
5| summator.put(r.map!fun);
| }
|
| ///
| void put()(T x)
| {
3| summator.put(fun(x));
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: powi;
| import mir.ndslice.slice: sliced;
|
6| alias f = (double x) => (powi(x, 2));
1| MapSummator!(f, double, Summation.pairwise) x;
1| x.put([0.0, 1, 2, 3, 4].sliced);
1| assert(x.sum == 30.0);
1| x.put(5);
1| assert(x.sum == 55.0);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
6| alias f = (double x) => (x + 1);
1| MapSummator!(f, double, Summation.pairwise) x;
1| x.put([0.0, 1, 2, 3, 4].sliced);
1| assert(x.sum == 15.0);
1| x.put(5);
1| assert(x.sum == 21.0);
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.ndslice.slice: sliced;
|
6| alias f = (double x) => (x + 1);
1| MapSummator!(f, double, Summation.pairwise) x;
| static immutable a = [0.0, 1, 2, 3, 4];
1| x.put(a.sliced);
1| assert(x.sum == 15.0);
1| x.put(5);
1| assert(x.sum == 21.0);
|}
|
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.slice: sliced;
|
9| alias f = (double x) => (x + 1);
1| MapSummator!(f, double, Summation.pairwise) x;
1| auto a = [
| [0.0, 1, 2],
| [3.0, 4, 5]
| ].fuse;
1| auto b = [6.0, 7, 8].sliced;
1| x.put(a);
1| assert(x.sum == 21.0);
1| x.put(b);
1| assert(x.sum == 45.0);
|}
|
|/++
|Variance algorithms.
|
|See Also:
| $(WEB en.wikipedia.org/wiki/Algorithms_for_calculating_variance, Algorithms for calculating variance).
|+/
|enum VarianceAlgo
|{
| /++
| Performs Welford's online algorithm for updating variance. Can also `put`
| another VarianceAccumulator of the same type, which uses the parallel
| algorithm from Chan et al., described above.
| +/
| online,
|
| /++
| Calculates variance using E(x^^2) - E(x)^2 (alowing for adjustments for
| population/sample variance). This algorithm can be numerically unstable.
| +/
| naive,
|
| /++
| Calculates variance using a two-pass algorithm whereby the input is first
| centered and then the sum of squares is calculated from that.
| +/
| twoPass,
|
| /++
| Calculates variance assuming the mean of the dataseries is zero.
| +/
| assumeZeroMean
|}
|
|///
|struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
| if (isMutable!T && varianceAlgo == VarianceAlgo.naive)
|{
| import mir.functional: naryFun;
|
| ///
2| this(Range)(Range r)
| if (isIterable!Range)
| {
| import core.lifetime: move;
2| this.put(r.move);
| }
|
| ///
| this()(T x)
| {
| this.put(x);
| }
|
| ///
| MeanAccumulator!(T, summation) meanAccumulator;
|
| ///
| size_t count() @property
| {
22| return meanAccumulator.count;
| }
|
| ///
| F mean(F = T)() @property
| {
| return meanAccumulator.mean;
| }
|
| ///
| Summator!(T, summation) sumOfSquares;
|
| ///
| void put(Range)(Range r)
| if (isIterable!Range)
| {
114| foreach(x; r)
| {
36| this.put(x);
| }
| }
|
| ///
| void put()(T x)
| {
37| meanAccumulator.put(x);
37| sumOfSquares.put(x * x);
| }
|
| ///
| F variance(F = T)(bool isPopulation) @property
| {
10| if (isPopulation == false)
6| return cast(F) sumOfSquares.sum / cast(F) (count - 1) -
| (cast(F) meanAccumulator.mean) ^^ 2 * (cast(F) count / cast(F) (count - 1));
| else
4| return cast(F) sumOfSquares.sum / cast(F) count -
| (cast(F) meanAccumulator.mean) ^^ 2;
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| VarianceAccumulator!(double, VarianceAlgo.naive, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|
1| v.put(4.0);
1| assert(v.variance(PopulationTrueRT).approxEqual(57.01923 / 13));
1| assert(v.variance(PopulationTrueCT).approxEqual(57.01923 / 13));
1| assert(v.variance(PopulationFalseRT).approxEqual(57.01923 / 12));
1| assert(v.variance(PopulationFalseCT).approxEqual(57.01923 / 12));
|}
|
|///
|struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
| if (isMutable!T &&
| varianceAlgo == VarianceAlgo.online)
|{
| ///
213| this(Range)(Range r)
| if (isIterable!Range)
| {
| import core.lifetime: move;
213| this.put(r.move);
| }
|
| ///
| this()(T x)
| {
| this.put(x);
| }
|
| ///
| MeanAccumulator!(T, summation) meanAccumulator;
|
| ///
| size_t count() @property
| {
982| return meanAccumulator.count;
| }
|
| ///
| F mean(F = T)() @property
| {
2| return meanAccumulator.mean;
| }
|
| ///
| Summator!(T, summation) centeredSumOfSquares;
|
| ///
| void put(Range)(Range r)
| if (isIterable!Range)
| {
2719| foreach(x; r)
| {
745| this.put(x);
| }
| }
|
| ///
| void put()(T x)
| {
733| T delta = x;
733| if (count > 0) {
512| delta -= meanAccumulator.mean;
| }
733| meanAccumulator.put(x);
733| centeredSumOfSquares.put(delta * (x - meanAccumulator.mean));
| }
|
| ///
| void put()(VarianceAccumulator!(T, varianceAlgo, summation) v)
| {
2| size_t oldCount = count;
2| T delta = v.mean;
2| if (oldCount > 0) {
2| delta -= meanAccumulator.mean;
| }
2| meanAccumulator.put!T(v.meanAccumulator);
2| centeredSumOfSquares.put(v.centeredSumOfSquares.sum + delta * delta * v.count * oldCount / count);
| }
|
| ///
| F variance(F = T)(bool isPopulation) @property
| {
243| if (isPopulation == false)
222| return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
| else
21| return cast(F) centeredSumOfSquares.sum / cast(F) count;
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
1| v.put(x);
|
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|
1| v.put(4.0);
1| assert(v.variance(PopulationTrueRT).approxEqual(57.01923 / 13));
1| assert(v.variance(PopulationTrueCT).approxEqual(57.01923 / 13));
1| assert(v.variance(PopulationFalseRT).approxEqual(57.01923 / 12));
1| assert(v.variance(PopulationFalseCT).approxEqual(57.01923 / 12));
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
1| auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(PopulationTrueRT).approxEqual(12.55208 / 6));
1| assert(v.variance(PopulationTrueCT).approxEqual(12.55208 / 6));
1| assert(v.variance(PopulationFalseRT).approxEqual(12.55208 / 5));
1| assert(v.variance(PopulationFalseCT).approxEqual(12.55208 / 5));
|
1| v.put(y);
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
1| auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(PopulationTrueRT).approxEqual(12.55208 / 6));
1| assert(v.variance(PopulationTrueCT).approxEqual(12.55208 / 6));
1| assert(v.variance(PopulationFalseRT).approxEqual(12.55208 / 5));
1| assert(v.variance(PopulationFalseCT).approxEqual(12.55208 / 5));
|
1| VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) w;
1| w.put(y);
1| v.put(w);
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
| auto x = [1.0 + 3i, 2, 3].sliced;
|
| VarianceAccumulator!(cdouble, VarianceAlgo.online, Summation.naive) v;
| v.put(x);
| assert(v.variance(true).approxEqual((-4.0 - 6i) / 3));
| assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
| import std.complex: Complex;
|
1| auto x = [Complex!double(1.0, 3), Complex!double(2), Complex!double(3)].sliced;
|
1| VarianceAccumulator!(Complex!double, VarianceAlgo.online, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(true).approxEqual(Complex!double(-4.0, -6) / 3));
1| assert(v.variance(false).approxEqual(Complex!double(-4.0, -6) / 2));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
1| VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(false).approxEqual(54.76562 / 11));
|
1| v.put(4.0);
1| assert(v.variance(false).approxEqual(57.01923 / 12));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
1| auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
1| VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(false).approxEqual(12.55208 / 5));
|
1| VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) w;
1| w.put(y);
1| v.put(w);
1| assert(v.variance(false).approxEqual(54.76562 / 11));
|}
|
|///
|struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
| if (isMutable!T && varianceAlgo == VarianceAlgo.twoPass)
|{
| import mir.functional: naryFun;
| import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
|
| ///
| MeanAccumulator!(T, summation) meanAccumulator;
|
| ///
| size_t count() @property
| {
6| return meanAccumulator.count;
| }
|
| ///
| F mean(F = T)() @property
| {
| return meanAccumulator.mean;
| }
|
| ///
| Summator!(T, summation) centeredSumOfSquares;
|
| ///
5| this(Iterator, size_t N, SliceKind kind)(
| Slice!(Iterator, N, kind) slice)
| {
| import core.lifetime: move;
| import mir.ndslice.internal: LeftOp;
| import mir.ndslice.topology: vmap, map;
|
5| meanAccumulator.put(slice.lightScope);
5| centeredSumOfSquares.put(slice.move.vmap(LeftOp!("-", T)(meanAccumulator.mean)).map!(naryFun!"a * a"));
| }
|
| ///
1| this(U)(U[] array)
| {
| import mir.ndslice.slice: sliced;
1| this(array.sliced);
| }
|
| ///
1| this(T)(T withAsSlice)
| if (hasAsSlice!T)
| {
1| this(withAsSlice.asSlice);
| }
|
| ///
| this()(T x)
| {
| meanAccumulator.put(x);
| centeredSumOfSquares.put(cast(T) 0);
| }
|
| ///
| F variance(F = T)(bool isPopulation) @property
| {
6| if (isPopulation == false)
4| return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
| else
2| return cast(F) centeredSumOfSquares.sum / cast(F) count;
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| auto v = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|}
|
|// dynamic array test
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.rc.array: RCArray;
|
1| double[] x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
|
1| auto v = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
1| assert(v.centeredSumOfSquares.sum.approxEqual(54.76562));
|}
|
|// withAsSlice test
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.rc.array: RCArray;
|
| static immutable a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
|
2| auto x = RCArray!double(12);
51| foreach(i, ref e; x)
12| e = a[i];
|
1| auto v = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
1| assert(v.centeredSumOfSquares.sum.approxEqual(54.76562));
|}
|
|///
|struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
| if (isMutable!T && varianceAlgo == VarianceAlgo.assumeZeroMean)
|{
| import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
|
| private size_t _count;
|
| ///
| size_t count() @property
| {
35| return _count;
| }
|
| ///
| F mean(F = T)() @property
| {
| return cast(F) 0;
| }
|
| ///
| Summator!(T, summation) centeredSumOfSquares;
|
| ///
3| this(Range)(Range r)
| if (isIterable!Range)
| {
3| this.put(r);
| }
|
| ///
| this()(T x)
| {
| this.put(x);
| }
|
| ///
| void put(Range)(Range r)
| if (isIterable!Range)
| {
285| foreach(x; r)
| {
87| this.put(x);
| }
| }
|
| ///
| void put()(T x)
| {
89| _count++;
89| centeredSumOfSquares.put(x * x);
| }
|
| ///
| void put()(VarianceAccumulator!(T, varianceAlgo, summation) v)
| {
2| _count += v.count;
2| centeredSumOfSquares.put(v.centeredSumOfSquares.sum);
| }
|
| ///
| F variance(F = T)(bool isPopulation) @property
| {
33| if (isPopulation == false)
20| return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
| else
13| return cast(F) centeredSumOfSquares.sum / cast(F) count;
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
1| auto x = a.center;
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
1| v.put(x);
|
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|
1| v.put(4.0);
1| assert(v.variance(PopulationTrueRT).approxEqual(70.76562 / 13));
1| assert(v.variance(PopulationTrueCT).approxEqual(70.76562 / 13));
1| assert(v.variance(PopulationFalseRT).approxEqual(70.76562 / 12));
1| assert(v.variance(PopulationFalseCT).approxEqual(70.76562 / 12));
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
1| auto b = a.center;
1| auto x = b[0 .. 6];
1| auto y = b[6 .. $];
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(PopulationTrueRT).approxEqual(13.492188 / 6));
1| assert(v.variance(PopulationTrueCT).approxEqual(13.492188 / 6));
1| assert(v.variance(PopulationFalseRT).approxEqual(13.492188 / 5));
1| assert(v.variance(PopulationFalseCT).approxEqual(13.492188 / 5));
|
1| v.put(y);
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
1| auto b = a.center;
1| auto x = b[0 .. 6];
1| auto y = b[6 .. $];
|
| enum PopulationTrueCT = true;
| enum PopulationFalseCT = false;
1| bool PopulationTrueRT = true;
1| bool PopulationFalseRT = false;
|
1| VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(PopulationTrueRT).approxEqual(13.492188 / 6));
1| assert(v.variance(PopulationTrueCT).approxEqual(13.492188 / 6));
1| assert(v.variance(PopulationFalseRT).approxEqual(13.492188 / 5));
1| assert(v.variance(PopulationFalseCT).approxEqual(13.492188 / 5));
|
1| VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) w;
1| w.put(y);
1| v.put(w);
1| assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
1| assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
1| assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
|}
|
|version(mir_builtincomplex_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
| auto a = [1.0 + 3i, 2, 3].sliced;
| auto x = a.center;
|
| VarianceAccumulator!(cdouble, VarianceAlgo.assumeZeroMean, Summation.naive) v;
| v.put(x);
| assert(v.variance(true).approxEqual((-4.0 - 6i) / 3));
| assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
| import std.complex: Complex;
|
1| auto a = [Complex!double(1.0, 3), Complex!double(2), Complex!double(3)].sliced;
1| auto x = a.center;
|
1| VarianceAccumulator!(Complex!double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(true).approxEqual(Complex!double(-4.0, -6) / 3));
1| assert(v.variance(false).approxEqual(Complex!double(-4.0, -6) / 2));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
1| auto x = a.center;
|
1| VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(false).approxEqual(54.76562 / 11));
|
1| v.put(4.0);
1| assert(v.variance(false).approxEqual(70.76562 / 12));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
1| auto b = a.center;
1| auto x = b[0 .. 6];
1| auto y = b[6 .. $];
|
1| VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
1| v.put(x);
1| assert(v.variance(false).approxEqual(13.492188 / 5));
|
1| VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) w;
1| w.put(y);
1| v.put(w);
1| assert(v.variance(false).approxEqual(54.76562 / 11));
|}
|
|/++
|Calculates the variance of the input
|
|By default, if `F` is not floating point type or complex type, then the result
|will have a `double` type if `F` is implicitly convertible to a floating point
|type or a type for which `isComplex!F` is true.
|
|Params:
| F = controls type of output
| varianceAlgo = algorithm for calculating variance (default: VarianceAlgo.online)
| summation = algorithm for calculating sums (default: Summation.appropriate)
|Returns:
| The variance of the input, must be floating point or complex type
|+/
|template variance(
| F,
| VarianceAlgo varianceAlgo = VarianceAlgo.online,
| Summation summation = Summation.appropriate)
|{
| /++
| Params:
| r = range, must be finite iterable
| isPopulation = true if population variance, false if sample variance (default)
| +/
| @fmamath meanType!F variance(Range)(Range r, bool isPopulation = false)
| if (isIterable!Range)
| {
| import core.lifetime: move;
|
| alias G = typeof(return);
222| auto varianceAccumulator = VarianceAccumulator!(G, varianceAlgo, ResolveSummationType!(summation, Range, G))(r.move);
216| return varianceAccumulator.variance(isPopulation);
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath meanType!F variance(scope const F[] ar...)
| {
| alias G = typeof(return);
4| auto varianceAccumulator = VarianceAccumulator!(G, varianceAlgo, ResolveSummationType!(summation, const(G)[], G))(ar);
4| return varianceAccumulator.variance(false);
| }
|}
|
|/// ditto
|template variance(
| VarianceAlgo varianceAlgo = VarianceAlgo.online,
| Summation summation = Summation.appropriate)
|{
| /++
| Params:
| r = range, must be finite iterable
| isPopulation = true if population variance, false if sample variance (default)
| +/
| @fmamath meanType!Range variance(Range)(Range r, bool isPopulation = false)
| if(isIterable!Range)
| {
| import core.lifetime: move;
|
| alias F = typeof(return);
103| return .variance!(F, varianceAlgo, summation)(r.move, isPopulation);
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath meanType!T variance(T)(scope const T[] ar...)
| {
| alias F = typeof(return);
1| return .variance!(F, varianceAlgo, summation)(ar);
| }
|}
|
|/// ditto
|template variance(F, string varianceAlgo, string summation = "appropriate")
|{
| mixin("alias variance = .variance!(F, VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
|}
|
|/// ditto
|template variance(string varianceAlgo, string summation = "appropriate")
|{
| mixin("alias variance = .variance!(VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| assert(variance([1.0, 2, 3]).approxEqual(2.0 / 2));
1| assert(variance([1.0, 2, 3], true).approxEqual(2.0 / 3));
|
1| assert(variance([1.0 + 3i, 2, 3]).approxEqual((-4.0 - 6i) / 2));
|
1| assert(variance!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 5));
|
| static assert(is(typeof(variance!float([1, 2, 3])) == float));
|}
|
|/// Variance of vector
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
1| assert(x.variance.approxEqual(54.76562 / 11));
|}
|
|/// Variance of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
|
1| auto x = [
| [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
| [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
| ].fuse;
|
1| assert(x.variance.approxEqual(54.76562 / 11));
|}
|
|/// Column variance of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: alongDim, byDim, map;
|
1| auto x = [
| [0.0, 1.0, 1.5, 2.0],
| [3.5, 4.25, 2.0, 7.5],
| [5.0, 1.0, 1.5, 0.0]
| ].fuse;
1| auto result = [13.16667 / 2, 7.041667 / 2, 0.1666667 / 2, 30.16667 / 2];
|
| // Use byDim or alongDim with map to compute variance of row/column.
1| assert(x.byDim!1.map!variance.all!approxEqual(result));
1| assert(x.alongDim!0.map!variance.all!approxEqual(result));
|
| // FIXME
| // Without using map, computes the variance of the whole slice
| // assert(x.byDim!1.variance == x.sliced.variance);
| // assert(x.alongDim!0.variance == x.sliced.variance);
|}
|
|/// Can also set algorithm type
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
1| auto x = a + 1_000_000_000;
|
1| auto y = x.variance;
1| assert(y.approxEqual(54.76562 / 11));
|
| // The naive algorithm is numerically unstable in this case
1| auto z0 = x.variance!"naive";
1| assert(!z0.approxEqual(y));
|
| // But the two-pass algorithm provides a consistent answer
1| auto z1 = x.variance!"twoPass";
1| assert(z1.approxEqual(y));
|
| // And the assumeZeroMean algorithm is way off
1| auto z2 = x.variance!"assumeZeroMean";
1| assert(z2.approxEqual(1.2e19 / 11));
|}
|
|/// Can also set algorithm or output type
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: repeat;
|
| //Set population variance, variance algorithm, sum algorithm or output type
|
1| auto a = [1.0, 1e100, 1, -1e100].sliced;
1| auto x = a * 10_000;
|
1| bool populationTrueRT = true;
1| bool populationFalseRT = false;
| enum PopulationTrueCT = true;
|
| /++
| Due to Floating Point precision, when centering `x`, subtracting the mean
| from the second and fourth numbers has no effect. Further, after centering
| and squaring `x`, the first and third numbers in the slice have precision
| too low to be included in the centered sum of squares.
| +/
1| assert(x.variance(populationFalseRT).approxEqual(2.0e208 / 3));
1| assert(x.variance(populationTrueRT).approxEqual(2.0e208 / 4));
1| assert(x.variance(PopulationTrueCT).approxEqual(2.0e208 / 4));
|
1| assert(x.variance!("online").approxEqual(2.0e208 / 3));
1| assert(x.variance!("online", "kbn").approxEqual(2.0e208 / 3));
1| assert(x.variance!("online", "kb2").approxEqual(2.0e208 / 3));
1| assert(x.variance!("online", "precise").approxEqual(2.0e208 / 3));
1| assert(x.variance!(double, "online", "precise").approxEqual(2.0e208 / 3));
1| assert(x.variance!(double, "online", "precise")(populationTrueRT).approxEqual(2.0e208 / 4));
|
1| auto y = uint.max.repeat(3);
1| auto z = y.variance!ulong;
1| assert(z == 0.0);
| static assert(is(typeof(z) == double));
|}
|
|/++
|For integral slices, pass output type as template parameter to ensure output
|type is correct.
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0, 1, 1, 2, 4, 4,
| 2, 7, 5, 1, 2, 0].sliced;
|
1| auto y = x.variance;
1| assert(y.approxEqual(50.91667 / 11));
| static assert(is(typeof(y) == double));
|
1| assert(x.variance!float.approxEqual(50.91667 / 11));
|}
|
|/++
|Variance works for complex numbers and other user-defined types (provided they
|can be converted to a floating point or complex type)
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
1| auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
1| assert(x.variance.approxEqual((0.0+10.0i)/ 3));
|}
|
|/// Compute variance along specified dimention of tensors
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: as, iota, alongDim, map, repeat;
|
1| auto x = [
| [0.0, 1, 2],
| [3.0, 4, 5]
| ].fuse;
|
1| assert(x.variance.approxEqual(17.5 / 5));
|
1| auto m0 = [4.5, 4.5, 4.5];
1| assert(x.alongDim!0.map!variance.all!approxEqual(m0));
1| assert(x.alongDim!(-2).map!variance.all!approxEqual(m0));
|
1| auto m1 = [1.0, 1.0];
1| assert(x.alongDim!1.map!variance.all!approxEqual(m1));
1| assert(x.alongDim!(-1).map!variance.all!approxEqual(m1));
|
1| assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!variance.all!approxEqual(repeat(3600.0 / 2, 3, 4, 5)));
|}
|
|/// Arbitrary variance
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
1| assert(variance(1.0, 2, 3) == 1.0);
1| assert(variance!float(1, 2, 3) == 1f);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual;
|
1| assert([1.0, 2, 3, 4].variance.approxEqual(5.0 / 3));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.topology: iota, alongDim, map;
|
1| auto x = iota([2, 2], 1);
1| auto y = x.alongDim!1.map!variance;
1| assert(y.all!approxEqual([0.5, 0.5]));
| static assert(is(meanType!(typeof(y)) == double));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
|
| static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
|
1| assert(x.sliced.variance.approxEqual(54.76562 / 11));
1| assert(x.sliced.variance!float.approxEqual(54.76562 / 11));
|}
|
|///
|package(mir)
|template stdevType(T)
|{
| import mir.internal.utility: isFloatingPoint;
|
| alias U = meanType!T;
|
| static if (isFloatingPoint!U) {
| alias stdevType = U;
| } else {
| static assert(0, "stdevType: Can't calculate standard deviation of elements of type " ~ U.stringof);
| }
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static assert(is(stdevType!(int[]) == double));
| static assert(is(stdevType!(double[]) == double));
| static assert(is(stdevType!(float[]) == float));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| static struct Foo {
| float x;
| alias x this;
| }
|
| static assert(is(stdevType!(Foo[]) == float));
|}
|
|/++
|Calculates the standard deviation of the input
|
|By default, if `F` is not floating point type, then the result will have a
|`double` type if `F` is implicitly convertible to a floating point type.
|
|Params:
| F = controls type of output
| varianceAlgo = algorithm for calculating variance (default: VarianceAlgo.online)
| summation = algorithm for calculating sums (default: Summation.appropriate)
|Returns:
| The standard deviation of the input, must be floating point type type
|+/
|template standardDeviation(
| F,
| VarianceAlgo varianceAlgo = VarianceAlgo.online,
| Summation summation = Summation.appropriate)
|{
| import mir.math.common: sqrt;
|
| /++
| Params:
| r = range, must be finite iterable
| isPopulation = true if population standard deviation, false if sample standard deviation (default)
| +/
| @fmamath stdevType!F standardDeviation(Range)(Range r, bool isPopulation = false)
| if (isIterable!Range)
| {
| import core.lifetime: move;
| alias G = typeof(return);
107| return r.move.variance!(G, varianceAlgo, ResolveSummationType!(summation, Range, G))(isPopulation).sqrt;
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath stdevType!F standardDeviation(scope const F[] ar...)
| {
| alias G = typeof(return);
2| return ar.variance!(G, varianceAlgo, ResolveSummationType!(summation, const(G)[], G)).sqrt;
| }
|}
|
|/// ditto
|template standardDeviation(
| VarianceAlgo varianceAlgo = VarianceAlgo.online,
| Summation summation = Summation.appropriate)
|{
| /++
| Params:
| r = range, must be finite iterable
| isPopulation = true if population standard deviation, false if sample standard deviation (default)
| +/
| @fmamath stdevType!Range standardDeviation(Range)(Range r, bool isPopulation = false)
| if(isIterable!Range)
| {
| import core.lifetime: move;
|
| alias F = typeof(return);
101| return .standardDeviation!(F, varianceAlgo, summation)(r.move, isPopulation);
| }
|
| /++
| Params:
| ar = values
| +/
| @fmamath stdevType!T standardDeviation(T)(scope const T[] ar...)
| {
| alias F = typeof(return);
1| return .standardDeviation!(F, varianceAlgo, summation)(ar);
| }
|}
|
|/// ditto
|template standardDeviation(F, string varianceAlgo, string summation = "appropriate")
|{
| mixin("alias standardDeviation = .standardDeviation!(F, VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
|}
|
|/// ditto
|template standardDeviation(string varianceAlgo, string summation = "appropriate")
|{
| mixin("alias standardDeviation = .standardDeviation!(VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.slice: sliced;
|
1| assert(standardDeviation([1.0, 2, 3]).approxEqual(sqrt(2.0 / 2)));
1| assert(standardDeviation([1.0, 2, 3], true).approxEqual(sqrt(2.0 / 3)));
|
1| assert(standardDeviation!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(sqrt(17.5 / 5)));
|
| static assert(is(typeof(standardDeviation!float([1, 2, 3])) == float));
|}
|
|/// Standard deviation of vector
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
1| assert(x.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
|}
|
|/// Standard deviation of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.fuse: fuse;
|
1| auto x = [
| [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
| [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
| ].fuse;
|
1| assert(x.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
|}
|
|/// Column standard deviation of matrix
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: alongDim, byDim, map;
|
1| auto x = [
| [0.0, 1.0, 1.5, 2.0],
| [3.5, 4.25, 2.0, 7.5],
| [5.0, 1.0, 1.5, 0.0]
| ].fuse;
1| auto result = [13.16667 / 2, 7.041667 / 2, 0.1666667 / 2, 30.16667 / 2].map!sqrt;
|
| // Use byDim or alongDim with map to compute standardDeviation of row/column.
1| assert(x.byDim!1.map!standardDeviation.all!approxEqual(result));
1| assert(x.alongDim!0.map!standardDeviation.all!approxEqual(result));
|
| // FIXME
| // Without using map, computes the standardDeviation of the whole slice
| // assert(x.byDim!1.standardDeviation == x.sliced.standardDeviation);
| // assert(x.alongDim!0.standardDeviation == x.sliced.standardDeviation);
|}
|
|/// Can also set algorithm type
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.slice: sliced;
|
1| auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
|
1| auto x = a + 1_000_000_000;
|
1| auto y = x.standardDeviation;
1| assert(y.approxEqual(sqrt(54.76562 / 11)));
|
| // The naive algorithm is numerically unstable in this case
1| auto z0 = x.standardDeviation!"naive";
1| assert(!z0.approxEqual(y));
|
| // But the two-pass algorithm provides a consistent answer
1| auto z1 = x.standardDeviation!"twoPass";
1| assert(z1.approxEqual(y));
|}
|
|/// Can also set algorithm or output type
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: repeat;
|
| //Set population standard deviation, standardDeviation algorithm, sum algorithm or output type
|
1| auto a = [1.0, 1e100, 1, -1e100].sliced;
1| auto x = a * 10_000;
|
1| bool populationTrueRT = true;
1| bool populationFalseRT = false;
| enum PopulationTrueCT = true;
|
| /++
| Due to Floating Point precision, when centering `x`, subtracting the mean
| from the second and fourth numbers has no effect. Further, after centering
| and squaring `x`, the first and third numbers in the slice have precision
| too low to be included in the centered sum of squares.
| +/
1| assert(x.standardDeviation(populationFalseRT).approxEqual(sqrt(2.0e208 / 3)));
1| assert(x.standardDeviation(populationTrueRT).approxEqual(sqrt(2.0e208 / 4)));
1| assert(x.standardDeviation(PopulationTrueCT).approxEqual(sqrt(2.0e208 / 4)));
|
1| assert(x.standardDeviation!("online").approxEqual(sqrt(2.0e208 / 3)));
1| assert(x.standardDeviation!("online", "kbn").approxEqual(sqrt(2.0e208 / 3)));
1| assert(x.standardDeviation!("online", "kb2").approxEqual(sqrt(2.0e208 / 3)));
1| assert(x.standardDeviation!("online", "precise").approxEqual(sqrt(2.0e208 / 3)));
1| assert(x.standardDeviation!(double, "online", "precise").approxEqual(sqrt(2.0e208 / 3)));
1| assert(x.standardDeviation!(double, "online", "precise")(populationTrueRT).approxEqual(sqrt(2.0e208 / 4)));
|
1| auto y = uint.max.repeat(3);
1| auto z = y.standardDeviation!ulong;
1| assert(z == 0.0);
| static assert(is(typeof(z) == double));
|}
|
|/++
|For integral slices, pass output type as template parameter to ensure output
|type is correct.
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.slice: sliced;
|
1| auto x = [0, 1, 1, 2, 4, 4,
| 2, 7, 5, 1, 2, 0].sliced;
|
1| auto y = x.standardDeviation;
1| assert(y.approxEqual(sqrt(50.91667 / 11)));
| static assert(is(typeof(y) == double));
|
1| assert(x.standardDeviation!float.approxEqual(sqrt(50.91667 / 11)));
|}
|
|/++
|Variance works for other user-defined types (provided they
|can be converted to a floating point)
|+/
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
|
| static struct Foo {
| float x;
| alias x this;
| }
|
1| Foo[] foo = [Foo(1f), Foo(2f), Foo(3f)];
1| assert(foo.standardDeviation == 1f);
|}
|
|/// Compute standard deviation along specified dimention of tensors
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: as, iota, alongDim, map, repeat;
|
1| auto x = [
| [0.0, 1, 2],
| [3.0, 4, 5]
| ].fuse;
|
1| assert(x.standardDeviation.approxEqual(sqrt(17.5 / 5)));
|
1| auto m0 = repeat(sqrt(4.5), 3);
1| assert(x.alongDim!0.map!standardDeviation.all!approxEqual(m0));
1| assert(x.alongDim!(-2).map!standardDeviation.all!approxEqual(m0));
|
1| auto m1 = [1.0, 1.0];
1| assert(x.alongDim!1.map!standardDeviation.all!approxEqual(m1));
1| assert(x.alongDim!(-1).map!standardDeviation.all!approxEqual(m1));
|
1| assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!standardDeviation.all!approxEqual(repeat(sqrt(3600.0 / 2), 3, 4, 5)));
|}
|
|/// Arbitrary standard deviation
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: sqrt;
|
1| assert(standardDeviation(1.0, 2, 3) == 1.0);
1| assert(standardDeviation!float(1, 2, 3) == 1f);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
1| assert([1.0, 2, 3, 4].standardDeviation.approxEqual(sqrt(5.0 / 3)));
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.topology: iota, alongDim, map;
|
1| auto x = iota([2, 2], 1);
1| auto y = x.alongDim!1.map!standardDeviation;
1| assert(y.all!approxEqual([sqrt(0.5), sqrt(0.5)]));
| static assert(is(meanType!(typeof(y)) == double));
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.math.common: approxEqual, sqrt;
| import mir.ndslice.slice: sliced;
|
| static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
| 2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
|
1| assert(x.sliced.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
1| assert(x.sliced.standardDeviation!float.approxEqual(sqrt(54.76562 / 11)));
|}
|
|/++
|A linear regression model with a single explanatory variable.
|+/
|template simpleLinearRegression(Summation summation = Summation.kbn)
|{
| import mir.ndslice.slice;
| import mir.primitives: isInputRange;
|
| /++
| Params:
| x = `x[i]` points
| y = `f(x[i])` values
| Returns:
| The pair of shift and slope of the linear curve.
| +/
| @fmamath
| sumType!YRange[2]
| simpleLinearRegression(XRange, YRange)(XRange x, YRange y) @safe
| if (isInputRange!XRange && isInputRange!YRange && !(isArray!XRange && isArray!YRange) && isFloatingPoint!(sumType!YRange))
| in {
| static if (hasLength!XRange && hasLength!YRange)
1| assert(x.length == y.length);
| }
| do {
| alias X = typeof(sumType!XRange.init * sumType!XRange.init);
| alias Y = sumType!YRange;
| enum summationX = !__traits(isIntegral, X) ? summation: Summation.naive;
1| Summator!(X, summationX) xms = 0;
1| Summator!(Y, summation) yms = 0;
1| Summator!(X, summationX) xxms = 0;
1| Summator!(Y, summation) xyms = 0;
|
| static if (hasLength!XRange)
1| sizediff_t n = x.length;
| else
| sizediff_t n = 0;
|
5| while (!x.empty)
| {
| static if (!(hasLength!XRange && hasLength!YRange))
| assert(!y.empty);
|
| static if (!hasLength!XRange)
| n++;
|
4| auto xi = x.front;
4| auto yi = y.front;
4| xms.put(xi);
4| yms.put(yi);
4| xxms.put(xi * xi);
4| xyms.put(xi * yi);
|
4| y.popFront;
4| x.popFront;
| }
|
| static if (!(hasLength!XRange && hasLength!YRange))
| assert(y.empty);
|
1| auto xm = xms.sum;
1| auto ym = yms.sum;
1| auto xxm = xxms.sum;
1| auto xym = xyms.sum;
|
1| auto slope = (xym * n - xm * ym) / (xxm * n - xm * xm);
|
1| return [(ym - slope * xm) / n, slope];
| }
|
| /// ditto
| @fmamath
| sumType!(Y[])[2]
| simpleLinearRegression(X, Y)(scope const X[] x, scope const Y[] y) @safe
| {
1| return .simpleLinearRegression!summation(x.sliced, y.sliced);
| }
|}
|
|/// ditto
|template simpleLinearRegression(string summation)
|{
| mixin("alias simpleLinearRegression = .simpleLinearRegression!(Summation." ~ summation ~ ");");
|}
|
|///
|version(mir_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: approxEqual;
| static immutable x = [0, 1, 2, 3];
| static immutable y = [-1, 0.2, 0.9, 2.1];
1| auto params = x.simpleLinearRegression(y);
1| assert(params[0].approxEqual(-0.95)); // shift
1| assert(params[1].approxEqual(1)); // slope
|}
source/mir/math/stat.d is 100% covered
<<<<<< EOF
# path=./source-mir-bignum-integer.lst
|/++
|Note:
| The module doesn't provide full arithmetic API for now.
|+/
|module mir.bignum.integer;
|
|import mir.bitop;
|import mir.serde: serdeProxy, serdeScoped;
|import mir.utility;
|import std.traits;
|
|/++
|Stack-allocated big signed integer.
|Params:
| maxSize64 = count of 64bit words in coefficient
|+/
|@serdeScoped @serdeProxy!(const(char)[])
|struct BigInt(size_t maxSize64)
| if (maxSize64 && maxSize64 <= ushort.max)
|{
| import mir.bignum.low_level_view;
| import mir.bignum.fixed;
|
| ///
| bool sign;
| ///
| uint length;
| ///
| size_t[ulong.sizeof / size_t.sizeof * maxSize64] data = void;
|
| ///
109| this(size_t size)(UInt!size fixedInt)
| {
109| this(fixedInt.data);
| }
|
| ///
109| this(size_t N)(size_t[N] data)
| if (N <= this.data.length)
| {
109| sign = false;
| version(LittleEndian)
109| this.data[0 .. N] = data;
| else
| this.data[$ - N .. $] = data;
109| length = data.length;
109| normalize;
| }
|
| ///
0000000| this(ulong data)
| {
0000000| sign = false;
| static if (size_t.sizeof == ulong.sizeof)
| {
| length = 1;
| view.leastSignificantFirst[0] = data;
| }
| else
| {
0000000| length = 2;
0000000| auto d = view.leastSignificantFirst;
0000000| d[0] = cast(uint) data;
0000000| d[1] = cast(uint) (data >> 32);
| }
0000000| normalize;
| }
|
| ///
| this()(scope const(char)[] str) @safe pure @nogc
| // if (isSomeChar!C)
| {
6| if (fromStringImpl(str))
6| return;
| static if (__traits(compiles, () @nogc { throw new Exception("Can't parse BigInt."); }))
| {
| import mir.exception: MirException;
| throw new MirException("Can't parse BigInt!" ~ maxSize64.stringof ~ " from string `", str , "`.");
| }
| else
| {
| static immutable exception = new Exception("Can't parse BigInt!" ~ maxSize64.stringof ~ ".");
0000000| throw exception;
| }
| }
|
| ///
| ref opAssign(ulong data) return
| {
| static if (size_t.sizeof == ulong.sizeof)
| {
| length = 1;
| view.leastSignificantFirst[0] = data;
| }
| else
| {
4| length = 2;
4| auto d = view.leastSignificantFirst;
4| d[0] = cast(uint) data;
4| d[1] = cast(uint) (data >> 32);
| }
4| normalize;
4| return this;
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_bignum_test) @safe pure @nogc unittest
| {
| import mir.math.constant: PI;
1| BigInt!4 integer = "-34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
1| assert(integer.sign);
1| integer.sign = false;
1| assert(integer == BigInt!4.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8"));
| }
|
| ///
| ref opAssign(size_t rhsMaxSize64)(auto ref scope const BigInt!rhsMaxSize64 rhs) return
| if (rhsMaxSize64 < maxSize64)
| {
4| this.sign = rhs.sign;
4| this.length = rhs.length;
| version(LittleEndian)
| {
4| data[0 .. length] = rhs.data[0 .. length];
| }
| else
| {
| data[$ - length .. $] = rhs.data[$ - length .. $];
| }
4| return this;
| }
|
| /++
| Returns: false in case of overflow or incorrect string.
| Precondition: non-empty coefficients.
| +/
| bool fromStringImpl(C)(scope const(C)[] str)
| scope @trusted pure @nogc nothrow
| if (isSomeChar!C)
| {
6| auto work = BigIntView!size_t(data[]);
6| if (work.fromStringImpl(str))
| {
6| length = cast(uint) work.coefficients.length;
6| sign = work.sign;
6| return true;
| }
0000000| return false;
| }
|
| ///
| BigInt copy() @property
| {
0000000| BigInt ret;
0000000| ret.sign = sign;
0000000| ret.length = length;
0000000| ret.data = data;
0000000| return ret;
| }
|
| ///
| bool opEquals()(auto ref const BigInt rhs)
| const @safe pure nothrow @nogc
| {
12| return view == rhs.view;
| }
|
| ///
| bool opEquals()(size_t rhs, bool rhsSign = false)
| const @safe pure nothrow @nogc
| {
12| return rhs == 0 && length == 0 || length == 1 && sign == rhsSign && view.unsigned.leastSignificant == rhs;
| }
|
| ///
| bool opEquals()(sizediff_t rhs)
| const @safe pure nothrow @nogc
| {
3| auto sign = rhs < 0;
6| return opEquals(sign ? ulong(-rhs) : ulong(rhs), sign);
| }
|
| /++
| +/
| auto opCmp()(auto ref const BigInt rhs)
| const @safe pure nothrow @nogc
| {
1| return view.opCmp(rhs.view);
| }
|
| ///
| BigIntView!size_t view()() @property
| {
| version (LittleEndian)
1225| return typeof(return)(data[0 .. length], sign);
| else
| return typeof(return)(data[$ - length .. $], sign);
| }
|
| ///
| BigIntView!(const size_t) view()() const @property
| {
| version (LittleEndian)
152| return typeof(return)(data[0 .. length], sign);
| else
| return typeof(return)(data[$ - length .. $], sign);
| }
|
| ///
| void normalize()()
| {
169| auto norm = view.normalized;
169| this.length = cast(uint) norm.unsigned.coefficients.length;
169| this.sign = norm.sign;
| }
|
| /++
| +/
| void putCoefficient(size_t value)
| {
540| assert(length < data.length);
| version (LittleEndian)
540| data[length++] = value;
| else
| data[$ - ++length] = value;
| }
|
| /++
| Performs `size_t overflow = (big += overflow) *= scalar` operatrion.
| Params:
| rhs = unsigned value to multiply by
| overflow = initial overflow value
| Returns:
| unsigned overflow value
| +/
| size_t opOpAssign(string op : "*")(size_t rhs, size_t overflow = 0u)
| @safe pure nothrow @nogc
| {
1| if (length == 0)
0000000| goto L;
1| overflow = view.unsigned.opOpAssign!op(rhs, overflow);
2| if (overflow && length < data.length)
| {
| L:
0000000| putCoefficient(overflow);
0000000| overflow = 0;
| }
1| return overflow;
| }
|
| /++
| Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation.
| Precondition: `overflow < rhs`
| Params:
| rhs = unsigned value to devide by
| overflow = initial unsigned overflow
| Returns:
| unsigned remainder value (evaluated overflow)
| +/
| uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0)
| @safe pure nothrow @nogc
| {
| assert(overflow < rhs);
| if (length)
| return view.unsigned.opOpAssign!op(rhs, overflow);
| return overflow;
| }
|
| /++
| Performs `size_t overflow = (big += overflow) *= fixed` operatrion.
| Params:
| rhs = unsigned value to multiply by
| overflow = initial overflow value
| Returns:
| unsigned overflow value
| +/
| UInt!size opOpAssign(string op : "*", size_t size)(UInt!size rhs, UInt!size overflow = 0)
| @safe pure nothrow @nogc
| {
31| if (length == 0)
0000000| goto L;
31| overflow = view.unsigned.opOpAssign!op(rhs, overflow);
62| if (overflow && length < data.length)
| {
| L:
| static if (size <= 64)
| {
30| auto o = cast(ulong)overflow;
| static if (size_t.sizeof == ulong.sizeof)
| {
| putCoefficient(o);
| overflow = UInt!size.init;
| }
| else
| {
30| putCoefficient(cast(uint)o);
30| o >>= 32;
30| if (length < data.length)
| {
30| putCoefficient(cast(uint)o);
30| o = 0;
| }
30| overflow = UInt!size(o);
| }
| }
| else
| {
| do
| {
0000000| putCoefficient(cast(size_t)overflow);
0000000| overflow >>= size_t.sizeof * 8;
| }
0000000| while(overflow && length < data.length);
| }
| }
31| return overflow;
| }
|
| /++
| Performs `size_t overflow = big *= fixed` operatrion.
| Params:
| rhs = unsigned value to multiply by
| Returns:
| overflow
| +/
| bool opOpAssign(string op, size_t rhsMaxSize64)(ref const BigInt!rhsMaxSize64 rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
38| return opOpAssign!op(rhs.view);
| }
|
| /// ditto
| bool opOpAssign(string op)(BigIntView!(const size_t) rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
58| sizediff_t diff = length - rhs.coefficients.length;
58| if (diff < 0)
| {
5| auto oldLength = length;
5| length = cast(int)rhs.coefficients.length;
5| view.unsigned.leastSignificantFirst[oldLength .. $] = 0;
| }
| else
53| if (rhs.coefficients.length == 0)
2| return false;
56| auto thisView = view;
56| auto overflow = thisView.opOpAssign!op(rhs);
56| this.sign = thisView.sign;
56| if (overflow)
| {
0000000| if (length < data.length)
| {
0000000| putCoefficient(overflow);
0000000| overflow = false;
| }
| }
| else
| {
56| normalize;
| }
56| return overflow;
| }
|
| /++
| +/
| static BigInt fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
| @trusted pure
| {
11| BigInt ret;
11| if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
11| return ret;
| version(D_Exceptions)
0000000| throw hexStringException;
| else
| assert(0, hexStringErrorMsg);
| }
|
| /++
| +/
| bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
| @safe pure @nogc nothrow
| if (isSomeChar!C)
| {
11| auto work = BigIntView!size_t(data);
11| auto ret = work.fromHexStringImpl!(C, allowUnderscores)(str);
11| if (ret)
| {
11| length = cast(uint)work.unsigned.coefficients.length;
11| sign = work.sign;
| }
11| return ret;
| }
|
| ///
| bool mulPow5(size_t degree)
| {
| // assert(approxCanMulPow5(degree));
57| if (length == 0)
4| return false;
| enum n = MaxWordPow5!size_t;
| enum wordInit = size_t(5) ^^ n;
53| size_t word = wordInit;
53| bool of;
592| while(degree)
| {
539| if (degree >= n)
| {
494| degree -= n;
| }
| else
| {
45| word = 1;
274| do word *= 5;
274| while(--degree);
| }
539| if (auto overflow = view *= word)
| {
492| of = length >= data.length;
492| if (!of)
480| putCoefficient(overflow);
| }
| }
53| return of;
| }
|
| ///
| ref BigInt opOpAssign(string op)(size_t shift)
| @safe pure nothrow @nogc return
| if (op == "<<" || op == ">>")
| {
92| auto index = shift / (size_t.sizeof * 8);
92| auto bs = shift % (size_t.sizeof * 8);
92| auto ss = size_t.sizeof * 8 - bs;
| static if (op == ">>")
| {
3| if (index >= length)
| {
0000000| length = 0;
0000000| return this;
| }
3| auto d = view.leastSignificantFirst;
3| if (bs)
| {
51| foreach (j; 0 .. d.length - (index + 1))
| {
14| d[j] = (d[j + index] >>> bs) | (d[j + (index + 1)] << ss);
| }
| }
| else
| {
0000000| foreach (j; 0 .. d.length - (index + 1))
| {
0000000| d[j] = d[j + index];
| }
| }
3| auto most = d[$ - (index + 1)] = d.back >>> bs;
3| length -= index + (most == 0);
| }
| else
| {
176| if (index >= data.length || length == 0)
| {
6| length = 0;
6| return this;
| }
|
83| if (bs)
| {
71| auto most = view.unsigned.mostSignificant >> ss;
71| length += index;
71| if (length < data.length)
| {
68| if (most)
| {
5| length++;
5| view.unsigned.mostSignificant = most;
5| length--;
| }
| }
| else
| {
3| length = data.length;
| }
|
71| auto d = view.leastSignificantFirst;
1199| foreach_reverse (j; index + 1 .. length)
| {
493| d[j] = (d[j - index] << bs) | (d[j - (index + 1)] >> ss);
| }
71| d[index] = d.front << bs;
71| if (length < data.length)
68| length += most != 0;
| }
| else
| {
12| length = cast(uint) min(length + index, cast(uint)data.length);
12| auto d = view.leastSignificantFirst;
84| foreach_reverse (j; index .. length)
| {
24| d[j] = d[j - index];
| }
| }
83| view.leastSignificantFirst[0 .. index] = 0;
| }
86| return this;
| }
|
| ///
| T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
| if (isFloatingPoint!T && isMutable!T)
| {
3| return view.opCast!(T, wordNormalized, nonZero);
| }
|
| ///
| T opCast(T, bool nonZero = false)() const
| if (is(T == long) || is(T == int))
| {
2| return this.view.opCast!(T, nonZero);
| }
|
| /++
| Returns: overflow flag
| +/
| bool copyFrom(W, WordEndian endian)(BigIntView!(const W, endian) view)
| {
| static if (W.sizeof > size_t.sizeof && endian == TargetEndian)
| {
6| return this.copyFrom(cast(BigIntView!(const size_t))view);
| }
| else
| {
47| this.sign = view.sign;
47| auto lhs = BigUIntView!W(cast(W[])data);
47| auto rhs = view;
47| auto overflow = lhs.coefficients.length < rhs.coefficients.length;
94| auto n = overflow ? lhs.coefficients.length : rhs.coefficients.length;
47| lhs.leastSignificantFirst[0 .. n] = rhs.leastSignificantFirst[0 .. n];
47| this.length = cast(uint)(n / (size_t.sizeof / W.sizeof));
47| if (auto tail = n % (size_t.sizeof / W.sizeof))
| {
15| this.length++;
15| auto shift = ((size_t.sizeof / W.sizeof) - tail) * (W.sizeof * 8);
15| auto value = this.view.unsigned.mostSignificant;
15| value <<= shift;
15| value >>= shift;
15| this.view.unsigned.mostSignificant = value;
| }
47| return overflow;
| }
| }
|
| /// ditto
| bool copyFrom(W, WordEndian endian)(BigUIntView!(const W, endian) view)
| {
36| return this.copyFrom(BigIntView!(const W, endian)(view));
| }
|
| ///
| immutable(C)[] toString(C = char)() const @safe pure nothrow
| if(isSomeChar!C && isMutable!C)
| {
2| C[ceilLog10Exp2(data.length * (size_t.sizeof * 8)) + 1] buffer = void;
2| BigInt copy = this;
2| auto len = copy.view.unsigned.toStringImpl(buffer);
2| if (sign)
1| buffer[$ - ++len] = '-';
2| return buffer[$ - len .. $].idup;
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_bignum_test) @safe pure unittest
| {
1| auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
1| auto integer = BigInt!4(str);
1| assert(integer.toString == str);
|
1| integer = BigInt!4.init;
1| assert(integer.toString == "0");
| }
|
| ///
| void toString(C = char, W)(scope ref W w) const
| if(isSomeChar!C && isMutable!C)
| {
1| C[ceilLog10Exp2(data.length * (size_t.sizeof * 8)) + 1] buffer = void;
1| BigInt copy = this;
1| auto len = copy.view.unsigned.toStringImpl(buffer);
1| if (sign)
1| buffer[$ - ++len] = '-';
1| w.put(buffer[$ - len .. $]);
| }
|
| static if (maxSize64 == 3)
| /// Check @nogc toString impl
| version(mir_bignum_test) @safe pure @nogc unittest
| {
| import mir.format: stringBuf;
1| auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
1| auto integer = BigInt!4(str);
2| stringBuf buffer;
1| buffer << integer;
1| assert(buffer.data == str, buffer.data);
| }
|}
|
|
|///
|version(mir_bignum_test)
|unittest
|{
| import mir.bignum.fixed;
| import mir.bignum.low_level_view;
|
1| auto a = BigInt!4.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8");
1| auto b = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
1| auto c = BigInt!4.fromHexString("7869dd864619cace5953a09910327b3971413e6aa5f417fa25a2ac93291b941f");
1| c.sign = true;
1| assert(a != b);
1| assert(a < b);
1| a -= b;
1| assert(a.sign);
1| assert(a == c);
1| a -= a;
1| assert(!a.sign);
1| assert(!a.length);
|
1| auto d = BigInt!4.fromHexString("0de1a911c6dc8f90a7169a148e65d22cf34f6a8254ae26362b064f26ac44218a");
1| assert((b *= 0x7869dd86) == 0x5c019770);
1| assert(b == d);
|
1| d = BigInt!4.fromHexString("856eeb23e68cc73f2a517448862cdc97e83f9dfa23768296724bf00fda7df32a");
1| auto o = b *= UInt!128.fromHexString("f79a222050aaeaaa417fa25a2ac93291");
1| assert(o == UInt!128.fromHexString("d6d15b99499b73e68c3331eb0f7bf16"));
1| assert(b == d);
|
1| d = BigInt!4.fromHexString("d"); // initial value
1| d.mulPow5(60);
1| c = BigInt!4.fromHexString("81704fcef32d3bd8117effd5c4389285b05d");
1| assert(d == c);
|
1| d >>= 80;
1| c = BigInt!4.fromHexString("81704fcef32d3bd8");
1| assert(d == c);
|
1| c = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
1| d = BigInt!4.fromHexString("9935cea0707f79a222050aaeaaaed17feb7aa76999d700000000000000000000");
1| c <<= 80;
1| assert(d == c);
1| c >>= 80;
1| c <<= 84;
1| d <<= 4;
1| assert(d == c);
1| assert(c != b);
1| b.sign = true;
1| assert(!c.copyFrom(b.view));
1| assert(c == b);
1| b >>= 18;
1| auto bView = cast(BigIntView!ushort)b.view;
1| assert(!c.copyFrom(bView.topLeastSignificantPart(bView.unsigned.coefficients.length - 1)));
1| assert(c == b);
|}
|
|version(mir_bignum_test)
|@safe pure @nogc unittest
|{
1| BigInt!4 i = "-0";
1| assert(i.view.coefficients.length == 0);
1| assert(cast(long) i == 0);
|}
source/mir/bignum/integer.d is 88% covered
<<<<<< EOF
# path=./source-mir-timestamp.lst
|/++
|Timestamp
|+/
|module mir.timestamp;
|
360|private alias isDigit = (dchar c) => uint(c - '0') < 10;
|import mir.serde: serdeIgnore;
|
|version(D_Exceptions)
|///
|class DateTimeException : Exception
|{
| ///
0000000| @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
| {
0000000| super(msg, file, line, nextInChain);
| }
|
| /// ditto
0000000| @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
| {
0000000| super(msg, file, line, nextInChain);
| }
|}
|
|version(D_Exceptions)
|{
| private static immutable InvalidMonth = new DateTimeException("Invalid Month");
| private static immutable InvalidDay = new DateTimeException("Invalid Day");
| private static immutable InvalidISOString = new DateTimeException("Invalid ISO String");
| private static immutable InvalidISOExtendedString = new DateTimeException("Invalid ISO Extended String");
| private static immutable InvalidString = new DateTimeException("Invalid String");
|}
|
|/++
|Timestamp
|
|Note: The component values in the binary encoding are always in UTC, while components in the text encoding are in the local time!
|This means that transcoding requires a conversion between UTC and local time.
|
|`Timestamp` precision is up to picosecond (second/10^12).
|+/
|struct Timestamp
|{
| import std.traits: isSomeChar;
|
| ///
| enum Precision : ubyte
| {
| ///
| year,
| ///
| month,
| ///
| day,
| ///
| minute,
| ///
| second,
| ///
| fraction,
| }
|
| ///
8| this(scope const(char)[] str) @safe pure @nogc
| {
8| this = fromString(str);
| }
|
| ///
| version (mir_test)
| @safe pure @nogc unittest
| {
1| assert(Timestamp("2010-07-04") == Timestamp(2010, 7, 4));
1| assert(Timestamp("20100704") == Timestamp(2010, 7, 4));
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
| static assert(Timestamp(2021, 01, 29, 4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30"));
|
1| assert(Timestamp("T0740Z") == Timestamp.onlyTime(7, 40));
1| assert(Timestamp("T074030Z") == Timestamp.onlyTime(7, 40, 30));
1| assert(Timestamp("T074030.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56));
|
1| assert(Timestamp("07:40Z") == Timestamp.onlyTime(7, 40));
1| assert(Timestamp("07:40:30Z") == Timestamp.onlyTime(7, 40, 30));
1| assert(Timestamp("T07:40:30.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56));
| }
|
| version(all)
| {
| short offset;
| }
| else
| /+
| If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of “-00:00”.
| This differs semantically from an offset of “Z” or “+00:00”, which imply that UTC is the preferred reference point for the specified time.
| RFC2822 describes a similar convention for email.
| private short _offset;
| +/
| {
|
| /++
| Timezone offset in minutes
| +/
| short offset() const @safe pure nothrow @nogc @property
| {
| return _offset >> 1;
| }
|
| /++
| Returns: true if timezone has offset
| +/
| bool hasOffset() const @safe pure nothrow @nogc @property
| {
| return _offset & 1;
| }
| }
|
|@serdeIgnore:
|
| /++
| Year
| +/
| short year;
| /++
| +/
| Precision precision;
|
| /++
| Month
|
| If the value equals to thero then this and all the following members are undefined.
| +/
| ubyte month;
| /++
| Day
|
| If the value equals to thero then this and all the following members are undefined.
| +/
| ubyte day;
| /++
| Hour
| +/
| ubyte hour;
|
| version(D_Ddoc)
| {
|
| /++
| Minute
|
| Note: the field is implemented as property.
| +/
| ubyte minute;
| /++
| Second
|
| Note: the field is implemented as property.
| +/
| ubyte second;
| /++
| Fraction
|
| The `fraction_exponent` and `fraction_coefficient` denote the fractional seconds of the timestamp as a decimal value
| The fractional seconds’ value is `coefficient * 10 ^ exponent`.
| It must be greater than or equal to zero and less than 1.
| A missing coefficient defaults to zero.
| Fractions whose coefficient is zero and exponent is greater than -1 are ignored.
|
| 'fractionCoefficient' allowed values are [0 ... 10^12-1].
| 'fractionExponent' allowed values are [-12 ... 0].
|
| Note: the fields are implemented as property.
| +/
| byte fractionExponent;
| /// ditto
| long fractionCoefficient;
| }
| else
| {
| import mir.bitmanip: bitfields;
| version (LittleEndian)
| {
|
| mixin(bitfields!(
| ubyte, "minute", 8,
| ubyte, "second", 8,
| byte, "fractionExponent", 8,
| long, "fractionCoefficient", 40,
| ));
| }
| else
| {
| mixin(bitfields!(
| long, "fractionCoefficient", 40,
| byte, "fractionExponent", 8,
| ubyte, "second", 8,
| ubyte, "minute", 8,
| ));
| }
| }
|
| ///
| @safe pure nothrow @nogc
6| this(short year)
| {
6| this.year = year;
6| this.precision = Precision.year;
| }
|
| ///
| @safe pure nothrow @nogc
4| this(short year, ubyte month)
| {
4| this.year = year;
4| this.month = month;
4| this.precision = Precision.month;
| }
|
| ///
| @safe pure nothrow @nogc
62| this(short year, ubyte month, ubyte day)
| {
62| this.year = year;
62| this.month = month;
62| this.day = day;
62| this.precision = Precision.day;
| }
|
| ///
| @safe pure nothrow @nogc
11| this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute)
| {
11| this.year = year;
11| this.month = month;
11| this.day = day;
11| this.hour = hour;
11| this.minute = minute;
11| this.precision = Precision.minute;
| }
|
| ///
| @safe pure nothrow @nogc
18| this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute, ubyte second)
| {
18| this.year = year;
18| this.month = month;
18| this.day = day;
18| this.hour = hour;
18| this.day = day;
18| this.minute = minute;
18| this.second = second;
18| this.precision = Precision.second;
| }
|
| ///
| @safe pure nothrow @nogc
8| this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute, ubyte second, byte fractionExponent, ulong fractionCoefficient)
| {
8| this.year = year;
8| this.month = month;
8| this.day = day;
8| this.hour = hour;
8| this.day = day;
8| this.minute = minute;
8| this.second = second;
8| assert(fractionExponent < 0);
8| this.fractionExponent = fractionExponent;
8| this.fractionCoefficient = fractionCoefficient;
8| this.precision = Precision.fraction;
| }
|
| ///
| @safe pure nothrow @nogc
| static Timestamp onlyTime(ubyte hour, ubyte minute)
| {
5| return Timestamp(0, 0, 0, hour, minute);
| }
|
| ///
| @safe pure nothrow @nogc
| static Timestamp onlyTime(ubyte hour, ubyte minute, ubyte second)
| {
6| return Timestamp(0, 0, 0, hour, minute, second);
| }
|
| ///
| @safe pure nothrow @nogc
| static Timestamp onlyTime(ubyte hour, ubyte minute, ubyte second, byte fractionExponent, ulong fractionCoefficient)
| {
5| return Timestamp(0, 0, 0, hour, minute, second, fractionExponent, fractionCoefficient);
| }
|
| ///
2| this(Date)(const Date datetime)
| if (Date.stringof == "Date" || Date.stringof == "date")
| {
| static if (__traits(hasMember, Date, "yearMonthDay"))
3| with(datetime.yearMonthDay) this(year, cast(ubyte)month, day);
| else
2| with(datetime) this(year, month, day);
| }
|
| ///
| version (mir_test)
| @safe unittest {
| import mir.date : Date;
1| auto dt = Date(1982, 4, 1);
1| Timestamp ts = dt;
1| assert(ts.opCmp(ts) == 0);
1| assert(dt.toISOExtString == ts.toString);
1| assert(dt == cast(Date) ts);
| }
|
| ///
| version (mir_test)
| @safe unittest {
| import std.datetime.date : Date;
1| auto dt = Date(1982, 4, 1);
1| Timestamp ts = dt;
1| assert(dt.toISOExtString == ts.toString);
1| assert(dt == cast(Date) ts);
| }
|
| ///
1| this(TimeOfDay)(const TimeOfDay timeOfDay)
| if (TimeOfDay.stringof == "TimeOfDay")
| {
2| with(timeOfDay) this = onlyTime(hour, minute, second);
| }
|
| ///
| version (mir_test)
| @safe unittest {
| import std.datetime.date : TimeOfDay;
1| auto dt = TimeOfDay(7, 14, 30);
1| Timestamp ts = dt;
1| assert(dt.toISOExtString ~ "Z" == ts.toString);
1| assert(dt == cast(TimeOfDay) ts);
| }
|
| ///
1| this(DateTime)(const DateTime datetime)
| if (DateTime.stringof == "DateTime")
| {
2| with(datetime) this(year, cast(ubyte)month, day, hour, minute, second);
| }
|
| ///
| version (mir_test)
| @safe unittest {
| import std.datetime.date : DateTime;
1| auto dt = DateTime(1982, 4, 1, 20, 59, 22);
1| Timestamp ts = dt;
1| assert(dt.toISOExtString ~ "Z" == ts.toString);
1| assert(dt == cast(DateTime) ts);
| }
|
| ///
1| this(SysTime)(const SysTime systime)
| if (SysTime.stringof == "SysTime")
| {
3| with(systime.toUTC) this(year, month, day, hour, minute, second, -7, fracSecs.total!"hnsecs");
1| offset = cast(short) systime.utcOffset.total!"minutes";
| }
|
| ///
| version (mir_test)
| @safe unittest {
| import core.time : hnsecs, minutes;
| import std.datetime.date : DateTime;
| import std.datetime.timezone : SimpleTimeZone;
| import std.datetime.systime : SysTime;
|
1| auto dt = DateTime(1982, 4, 1, 20, 59, 22);
1| auto tz = new immutable SimpleTimeZone(-330.minutes);
1| auto st = SysTime(dt, 1234567.hnsecs, tz);
1| Timestamp ts = st;
|
1| assert(st.toISOExtString == ts.toString);
1| assert(st == cast(SysTime) ts);
| }
|
| ///
| T opCast(T)() const
| if (T.stringof == "YearMonth"
| || T.stringof == "YearMonthDay"
| || T.stringof == "Date"
| || T.stringof == "TimeOfDay"
| || T.stringof == "date"
| || T.stringof == "DateTime"
| || T.stringof == "SysTime")
| {
| static if (T.stringof == "YearMonth")
| {
| return T(year, month, day);
| }
| else
| static if (T.stringof == "Date" || T.stringof == "date" || T.stringof == "YearMonthDay")
| {
2| return T(year, month, day);
| }
| else
| static if (T.stringof == "DateTime")
| {
1| return T(year, month, day, hour, minute, second);
| }
| else
| static if (T.stringof == "TimeOfDay")
| {
1| return T(hour, minute, second);
| }
| else
| static if (T.stringof == "SysTime")
| {
| import core.time : hnsecs, minutes;
| import std.datetime.date: DateTime;
| import std.datetime.systime: SysTime;
| import std.datetime.timezone: UTC, SimpleTimeZone;
1| auto ret = SysTime(DateTime(year, month, day, hour, minute, second), UTC());
1| if (fractionCoefficient)
| {
1| long coeff = fractionCoefficient;
1| int exp = fractionExponent;
1| while (exp > -7)
| {
0000000| exp--;
0000000| coeff *= 10;
| }
1| while (exp < -7)
| {
0000000| exp++;
0000000| coeff /= 10;
| }
1| ret.fracSecs = coeff.hnsecs;
| }
1| if (offset)
| {
1| ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes));
| }
1| return ret;
| }
| }
|
| /++
| Returns: true if timestamp represent a time only value.
| +/
| bool isOnlyTime() @property const @safe pure nothrow @nogc
| {
91| return precision > Precision.day && day == 0;
| }
|
| ///
| int opCmp(Timestamp rhs) const @safe pure nothrow @nogc
| {
| import std.meta: AliasSeq;
| static foreach (member; [
| "year",
| "month",
| "day",
| "hour",
| "minute",
| "second",
| ])
6| if (auto d = int(__traits(getMember, this, member)) - int(__traits(getMember, rhs, member)))
0000000| return d;
1| int frel = this.fractionExponent;
1| int frer = rhs.fractionExponent;
1| ulong frcl = this.fractionCoefficient;
1| ulong frcr = rhs.fractionCoefficient;
1| while(frel > frer)
| {
0000000| frel--;
0000000| frcl *= 10;
| }
1| while(frer > frel)
| {
0000000| frer--;
0000000| frcr *= 10;
| }
1| if (frcl < frcr) return -1;
1| if (frcl > frcr) return +1;
1| if (auto d = int(this.fractionExponent) - int(rhs.fractionExponent))
0000000| return d;
1| return int(this.offset) - int(rhs.offset);
| }
|
| /++
| Attaches local offset, doesn't adjust other fields.
| Local-time offsets may be represented as either `hour*60+minute` offsets from UTC,
| or as the zero to denote a local time of UTC. They are required on timestamps with time and are not allowed on date values.
| +/
| @safe pure nothrow @nogc const
| Timestamp withOffset(short minutes)
| {
18| assert(-24 * 60 <= minutes && minutes <= 24 * 60, "Offset absolute value should be less or equal to 24 * 60");
9| assert(precision >= Precision.minute, "Offsets are not allowed on date values.");
9| Timestamp ret = this;
9| ret.offset = minutes;
9| return ret;
| }
|
| version(D_BetterC){} else
| private string toStringImpl(alias fun)() const @safe pure nothrow
| {
| import mir.appender: UnsafeArrayBuffer;
49| char[64] buffer = void;
49| auto w = UnsafeArrayBuffer!char(buffer);
49| fun(w);
49| return w.data.idup;
| }
|
| /++
| Converts this $(LREF Timestamp) to a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`.
|
| If `w` writer is set, the resulting string will be written directly
| to it.
|
| Returns:
| A `string` when not using an output range; `void` otherwise.
| +/
| alias toString = toISOExtString;
|
| ///
| version (mir_test)
| @safe pure nothrow unittest
| {
1| assert(Timestamp.init.toString == "0000T");
1| assert(Timestamp(2010, 7, 4).toString == "2010-07-04");
1| assert(Timestamp(1998, 12, 25).toString == "1998-12-25");
1| assert(Timestamp(0, 1, 5).toString == "0000-01-05");
1| assert(Timestamp(-4, 1, 5).toString == "-0004-01-05");
|
| // YYYY-MM-DDThh:mm:ss±hh:mm
1| assert(Timestamp(2021).toString == "2021T");
1| assert(Timestamp(2021, 01).toString == "2021-01T", Timestamp(2021, 01).toString);
1| assert(Timestamp(2021, 01, 29).toString == "2021-01-29");
1| assert(Timestamp(2021, 01, 29, 19, 42).toString == "2021-01-29T19:42Z");
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString == "2021-01-29T19:42:44+07", Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString);
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toString == "2021-01-29T20:12:44+07:30");
|
1| assert(Timestamp.onlyTime(7, 40).toString == "07:40Z");
1| assert(Timestamp.onlyTime(7, 40, 30).toString == "07:40:30Z");
1| assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toString == "07:40:30.056Z");
| }
|
| ///
| version (mir_test)
| @safe unittest
| {
| // Test A.D.
1| assert(Timestamp(9, 12, 4).toISOExtString == "0009-12-04");
1| assert(Timestamp(99, 12, 4).toISOExtString == "0099-12-04");
1| assert(Timestamp(999, 12, 4).toISOExtString == "0999-12-04");
1| assert(Timestamp(9999, 7, 4).toISOExtString == "9999-07-04");
1| assert(Timestamp(10000, 10, 20).toISOExtString == "+10000-10-20");
|
| // Test B.C.
1| assert(Timestamp(0, 12, 4).toISOExtString == "0000-12-04");
1| assert(Timestamp(-9, 12, 4).toISOExtString == "-0009-12-04");
1| assert(Timestamp(-99, 12, 4).toISOExtString == "-0099-12-04");
1| assert(Timestamp(-999, 12, 4).toISOExtString == "-0999-12-04");
1| assert(Timestamp(-9999, 7, 4).toISOExtString == "-9999-07-04");
1| assert(Timestamp(-10000, 10, 20).toISOExtString == "-10000-10-20");
|
1| assert(Timestamp.onlyTime(7, 40).toISOExtString == "07:40Z");
1| assert(Timestamp.onlyTime(7, 40, 30).toISOExtString == "07:40:30Z");
1| assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOExtString == "07:40:30.056Z");
|
1| const cdate = Timestamp(1999, 7, 6);
1| immutable idate = Timestamp(1999, 7, 6);
1| assert(cdate.toISOExtString == "1999-07-06");
1| assert(idate.toISOExtString == "1999-07-06");
| }
|
| /// ditto
| alias toISOExtString = toISOStringImp!true;
|
| /++
| Converts this $(LREF Timestamp) to a string with the format `YYYYMMDDThhmmss±hhmm`.
|
| If `w` writer is set, the resulting string will be written directly
| to it.
|
| Returns:
| A `string` when not using an output range; `void` otherwise.
| +/
| alias toISOString = toISOStringImp!false;
|
| ///
| version (mir_test)
| @safe pure nothrow unittest
| {
1| assert(Timestamp.init.toISOString == "0000T");
1| assert(Timestamp(2010, 7, 4).toISOString == "20100704");
1| assert(Timestamp(1998, 12, 25).toISOString == "19981225");
1| assert(Timestamp(0, 1, 5).toISOString == "00000105");
1| assert(Timestamp(-4, 1, 5).toISOString == "-00040105");
|
| // YYYYMMDDThhmmss±hhmm
1| assert(Timestamp(2021).toISOString == "2021T");
1| assert(Timestamp(2021, 01).toISOString == "2021-01T"); // always extended
1| assert(Timestamp(2021, 01, 29).toISOString == "20210129");
1| assert(Timestamp(2021, 01, 29, 19, 42).toISOString == "20210129T1942Z");
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toISOString == "20210129T194244+07");
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
| static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
|
1| assert(Timestamp.onlyTime(7, 40).toISOString == "T0740Z");
1| assert(Timestamp.onlyTime(7, 40, 30).toISOString == "T074030Z");
1| assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOString == "T074030.056Z");
| }
|
| /// Helpfer for time zone offsets
| void addMinutes(short minutes) @safe pure nothrow @nogc
| {
10| int totalMinutes = minutes + (this.minute + this.hour * 60u);
10| auto h = totalMinutes / 60;
|
10| int dayShift;
|
11| while (totalMinutes < 0)
| {
1| totalMinutes += 24 * 60;
1| dayShift--;
| }
|
10| while (totalMinutes >= 24 * 60)
| {
0000000| totalMinutes -= 24 * 60;
0000000| dayShift++;
| }
|
10| if (dayShift)
| {
| import mir.date: Date;
1| auto ymd = (Date.trustedCreate(year, month, day) + dayShift).yearMonthDay;
1| year = ymd.year;
1| month = cast(ubyte)ymd.month;
1| day = ymd.day;
| }
|
10| hour = cast(ubyte) (totalMinutes / 60);
10| minute = cast(ubyte) (totalMinutes % 60);
| }
|
| template toISOStringImp(bool ext)
| {
| version(D_BetterC){} else
| string toISOStringImp() const @safe pure nothrow
| {
49| return toStringImpl!toISOStringImp;
| }
|
| /// ditto
| void toISOStringImp(W)(scope ref W w) const scope
| // if (isOutputRange!(W, char))
| {
| import mir.format: printZeroPad;
| // YYYY-MM-DDThh:mm:ss±hh:mm
49| Timestamp t = this;
|
49| if (t.offset)
| {
10| assert(-24 * 60 <= t.offset && t.offset <= 24 * 60, "Offset absolute value should be less or equal to 24 * 60");
5| assert(precision >= Precision.minute, "Offsets are not allowed on date values.");
5| t.addMinutes(t.offset);
| }
|
49| if (!t.isOnlyTime)
| {
39| if (t.year >= 10_000)
1| w.put('+');
117| printZeroPad(w, t.year, t.year >= 0 ? t.year < 10_000 ? 4 : 5 : t.year > -10_000 ? 5 : 6);
39| if (precision == Precision.year)
| {
4| w.put('T');
4| return;
| }
62| if (ext || precision == Precision.month) w.put('-');
|
35| printZeroPad(w, cast(uint)t.month, 2);
35| if (precision == Precision.month)
| {
2| w.put('T');
2| return;
| }
25| static if (ext) w.put('-');
|
33| printZeroPad(w, t.day, 2);
33| if (precision == Precision.day)
25| return;
| }
|
18| if (!ext || !t.isOnlyTime)
11| w.put('T');
|
18| printZeroPad(w, t.hour, 2);
12| static if (ext) w.put(':');
18| printZeroPad(w, t.minute, 2);
|
18| if (precision >= Precision.second)
| {
9| static if (ext) w.put(':');
13| printZeroPad(w, t.second, 2);
|
17| if (precision > Precision.second && (t.fractionExponent < 0 || t.fractionCoefficient))
| {
4| w.put('.');
4| printZeroPad(w, t.fractionCoefficient, -int(t.fractionExponent));
| }
| }
|
18| if (t.offset == 0)
| {
13| w.put('Z');
13| return;
| }
|
5| bool sign = t.offset < 0;
10| uint absoluteOffset = !sign ? t.offset : -int(t.offset);
5| uint offsetHour = absoluteOffset / 60u;
5| uint offsetMinute = absoluteOffset % 60u;
|
6| w.put(sign ? '-' : '+');
5| printZeroPad(w, offsetHour, 2);
5| if (offsetMinute)
| {
2| static if (ext) w.put(':');
3| printZeroPad(w, offsetMinute, 2);
| }
| }
| }
|
| /++
| Creates a $(LREF Timestamp) from a string with the format `YYYYMMDDThhmmss±hhmm
| or its leading part allowed by the standard.
|
| or its leading part allowed by the standard.
|
| Params:
| str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates.
| value = (optional) result value.
|
| Throws:
| $(LREF DateTimeException) if the given string is
| not in the correct format. Two arguments overload is `nothrow`.
| Returns:
| `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload.
| +/
| alias fromISOString = fromISOStringImpl!false;
|
| ///
| version (mir_test)
| @safe unittest
| {
1| assert(Timestamp.fromISOString("20100704") == Timestamp(2010, 7, 4));
1| assert(Timestamp.fromISOString("19981225") == Timestamp(1998, 12, 25));
1| assert(Timestamp.fromISOString("00000105") == Timestamp(0, 1, 5));
| // assert(Timestamp.fromISOString("-00040105") == Timestamp(-4, 1, 5));
|
1| assert(Timestamp(2021) == Timestamp.fromISOString("2021"));
1| assert(Timestamp(2021) == Timestamp.fromISOString("2021T"));
| // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01"));
| // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01T"));
1| assert(Timestamp(2021, 01, 29) == Timestamp.fromISOString("20210129"));
1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942"));
1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942Z"));
1| assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOString("20210129T194212"));
1| assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOString("20210129T194212.067Z"));
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOString("20210129T194244+07"));
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
| static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
| static assert(Timestamp(2021, 01, 29, 4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOString("20210128T211244-0730"));
| }
|
| version (mir_test)
| @safe unittest
| {
| import std.exception: assertThrown;
2| assertThrown!DateTimeException(Timestamp.fromISOString(""));
2| assertThrown!DateTimeException(Timestamp.fromISOString("990704"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("0100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010070"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("120100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("-0100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("+0100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010070a"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("20100a04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010a704"));
|
2| assertThrown!DateTimeException(Timestamp.fromISOString("99-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-0"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("12010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("-010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("+010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-0a"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-0a-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-a7-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/07/04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/7/04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/7/4"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/07/4"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-7-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-7-4"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-4"));
|
2| assertThrown!DateTimeException(Timestamp.fromISOString("99Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010Jul0"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("12010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("-010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("+010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010Jul0a"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010Jua04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010aul04"));
|
2| assertThrown!DateTimeException(Timestamp.fromISOString("99-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-0"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("12010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("-010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("+010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-0a"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jua-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jal-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-aul-04"));
|
| // assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-04"));
|
1| assert(Timestamp.fromISOString("19990706") == Timestamp(1999, 7, 6));
| // assert(Timestamp.fromISOString("-19990706") == Timestamp(-1999, 7, 6));
| // assert(Timestamp.fromISOString("+019990706") == Timestamp(1999, 7, 6));
1| assert(Timestamp.fromISOString("19990706") == Timestamp(1999, 7, 6));
| }
|
| // bug# 17801
| version (mir_test)
| @safe unittest
| {
| import std.conv : to;
| import std.meta : AliasSeq;
| static foreach (C; AliasSeq!(char, wchar, dchar))
| {
| static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9| assert(Timestamp.fromISOString(to!S("20121221")) == Timestamp(2012, 12, 21));
| }
| }
|
| /++
| Creates a $(LREF Timestamp) from a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`
| or its leading part allowed by the standard.
|
|
| Params:
| str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates.
| value = (optional) result value.
|
| Throws:
| $(LREF DateTimeException) if the given string is
| not in the correct format. Two arguments overload is `nothrow`.
| Returns:
| `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload.
| +/
| alias fromISOExtString = fromISOStringImpl!true;
|
|
| ///
| version (mir_test)
| @safe unittest
| {
1| assert(Timestamp.fromISOExtString("2010-07-04") == Timestamp(2010, 7, 4));
1| assert(Timestamp.fromISOExtString("1998-12-25") == Timestamp(1998, 12, 25));
1| assert(Timestamp.fromISOExtString("0000-01-05") == Timestamp(0, 1, 5));
1| assert(Timestamp.fromISOExtString("-0004-01-05") == Timestamp(-4, 1, 5));
|
1| assert(Timestamp(2021) == Timestamp.fromISOExtString("2021"));
1| assert(Timestamp(2021) == Timestamp.fromISOExtString("2021T"));
1| assert(Timestamp(2021, 01) == Timestamp.fromISOExtString("2021-01"));
1| assert(Timestamp(2021, 01) == Timestamp.fromISOExtString("2021-01T"));
1| assert(Timestamp(2021, 01, 29) == Timestamp.fromISOExtString("2021-01-29"));
1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42"));
1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42Z"));
1| assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOExtString("2021-01-29T19:42:12"));
1| assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOExtString("2021-01-29T19:42:12.067Z"));
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOExtString("2021-01-29T19:42:44+07"));
1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30"));
| static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30"));
| static assert(Timestamp(2021, 01, 29, 4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30"));
| }
|
| version (mir_test)
| @safe unittest
| {
| import std.exception: assertThrown;
|
2| assertThrown!DateTimeException(Timestamp.fromISOExtString(""));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("990704"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("0100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("120100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-0100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+0100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010070a"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("20100a04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010a704"));
|
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("99-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-0"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("12010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+010-07-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-0a"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-0a-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-a7-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/07/04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/7/04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/7/4"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/07/4"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-7-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-7-4"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-4"));
|
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("99Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jul0"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("12010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+010Jul04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jul0a"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jua04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010aul04"));
|
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("99-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-0"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("12010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+010-Jul-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-0a"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jua-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jal-04"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-aul-04"));
|
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("20100704"));
2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-04"));
|
1| assert(Timestamp.fromISOExtString("1999-07-06") == Timestamp(1999, 7, 6));
1| assert(Timestamp.fromISOExtString("-1999-07-06") == Timestamp(-1999, 7, 6));
1| assert(Timestamp.fromISOExtString("+01999-07-06") == Timestamp(1999, 7, 6));
| }
|
| // bug# 17801
| version (mir_test)
| @safe unittest
| {
| import std.conv : to;
| import std.meta : AliasSeq;
| static foreach (C; AliasSeq!(char, wchar, dchar))
| {
| static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9| assert(Timestamp.fromISOExtString(to!S("2012-12-21")) == Timestamp(2012, 12, 21));
| }
| }
|
| /++
| Creates a $(LREF Timestamp) from a string with the format YYYY-MM-DD, YYYYMMDD, or YYYY-Mon-DD.
|
| Params:
| str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) and $(LREF .Timestamp.toISOString) format dates. The function is case sensetive.
| value = (optional) result value.
|
| Throws:
| $(LREF DateTimeException) if the given string is
| not in the correct format. Two arguments overload is `nothrow`.
| Returns:
| `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload.
| +/
| static bool fromString(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc
| {
10| return fromISOExtString(str, value)
5| || fromISOString(str, value);
| }
|
| ///
| version (mir_test)
| @safe pure @nogc unittest
| {
1| assert(Timestamp.fromString("2010-07-04") == Timestamp(2010, 7, 4));
1| assert(Timestamp.fromString("20100704") == Timestamp(2010, 7, 4));
| }
|
| /// ditto
| static Timestamp fromString(C)(scope const(C)[] str) @safe pure
| if (isSomeChar!C)
| {
10| Timestamp ret;
10| if (fromString(str, ret))
10| return ret;
0000000| throw InvalidString;
| }
|
| template fromISOStringImpl(bool ext)
| {
| static Timestamp fromISOStringImpl(C)(scope const(C)[] str) @safe pure
| if (isSomeChar!C)
| {
143| Timestamp ret;
143| if (fromISOStringImpl(str, ret))
51| return ret;
92| throw InvalidISOExtendedString;
| }
|
| static bool fromISOStringImpl(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc
| if (isSomeChar!C)
| {
| import mir.parse: fromString, parse;
|
| static if (ext)
243| auto isOnlyTime = str.length >= 3 && (str[0] == 'T' || str[2] == ':');
| else
149| auto isOnlyTime = str.length >= 3 && str[0] == 'T';
|
158| if (!isOnlyTime)
| {
| // YYYY
| static if (ext)
| {{
153| auto startIsDigit = str.length && str[0].isDigit;
77| auto strOldLength = str.length;
77| if (!parse(str, value.year))
10| return false;
67| auto l = strOldLength - str.length;
67| if ((l == 4) != startIsDigit)
16| return false;
| }}
| else
| {
206| if (str.length < 4 || !str[0].isDigit || !fromString(str[0 .. 4], value.year))
15| return false;
57| str = str[4 .. $];
| }
|
108| value.precision = Precision.year;
214| if (str.length == 0 || str == "T")
4| return true;
|
| static if (ext)
| {
49| if (str[0] != '-')
9| return false;
40| str = str[1 .. $];
| }
|
| // MM
256| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.month))
36| return false;
59| str = str[2 .. $];
59| value.precision = Precision.month;
116| if (str.length == 0 || str == "T")
3| return ext;
|
| static if (ext)
| {
28| if (str[0] != '-')
0000000| return false;
28| str = str[1 .. $];
| }
|
| // DD
160| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.day))
6| return false;
50| str = str[2 .. $];
50| value.precision = Precision.day;
50| if (str.length == 0)
36| return true;
| }
|
| // str isn't empty here
| // T
23| if (str[0] == 'T')
| {
20| str = str[1 .. $];
| // OK, onlyTime requires length >= 3
20| if (str.length == 0)
0000000| return true;
| }
| else
| {
3| if (!(ext && isOnlyTime))
1| return false;
| }
|
22| value.precision = Precision.minute; // we don't have hour precision
|
| // hh
66| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.hour))
0000000| return false;
22| str = str[2 .. $];
22| if (str.length == 0)
0000000| return true;
|
| static if (ext)
| {
12| if (str[0] != ':')
3| return false;
9| str = str[1 .. $];
| }
|
| // mm
| {
19| uint minute;
57| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], minute))
0000000| return false;
19| value.minute = cast(ubyte) minute;
19| str = str[2 .. $];
19| if (str.length == 0)
2| return true;
| }
|
| static if (ext)
| {
8| if (str[0] != ':')
2| goto TZ;
6| str = str[1 .. $];
| }
|
| // ss
| {
15| uint second;
28| if (str.length < 2 || !str[0].isDigit)
2| goto TZ;
13| if (!fromString(str[0 .. 2], second))
0000000| return false;
13| value.second = cast(ubyte) second;
13| str = str[2 .. $];
13| value.precision = Precision.second;
13| if (str.length == 0)
2| return true;
| }
|
| // .
11| if (str[0] != '.')
7| goto TZ;
4| str = str[1 .. $];
4| value.precision = Precision.fraction;
|
| // fraction
| {
4| const strOldLength = str.length;
4| ulong fractionCoefficient;
12| if (str.length < 1 || !str[0].isDigit || !parse!ulong(str, fractionCoefficient))
0000000| return false;
4| sizediff_t fractionExponent = str.length - strOldLength;
4| if (fractionExponent < -12)
0000000| return false;
4| value.fractionExponent = cast(byte)fractionExponent;
4| value.fractionCoefficient = fractionCoefficient;
4| if (str.length == 0)
0000000| return true;
| }
|
| TZ:
|
15| if (str == "Z")
10| return true;
|
5| int hour;
5| int minute;
15| if (str.length < 3 || str[0].isDigit || !fromString(str[0 .. 3], hour))
0000000| return false;
5| str = str[3 .. $];
|
5| if (str.length)
| {
| static if (ext)
| {
1| if (str[0] != ':')
0000000| return false;
1| str = str[1 .. $];
| }
9| if (str.length != 2 || !str[0].isDigit || !fromString(str[0 .. 2], minute))
0000000| return false;
| }
|
10| value.offset = cast(short)(hour * 60 + (hour < 0 ? -minute : minute));
5| value.addMinutes(cast(short)-int(value.offset));
5| return true;
| }
| }
|}
source/mir/timestamp.d is 93% covered
<<<<<< EOF
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-reflection.lst
|/++
|Base reflection utilities.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilya Yaroshenko
|Macros:
|+/
|module mir.reflection;
|
|import std.meta;
|import std.traits: hasUDA, getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers, isAggregateType;
|
|deprecated
|package alias isSomeStruct = isAggregateType;
|
|/++
|Match types like `std.typeconst: Nullable`.
|+/
|template isStdNullable(T)
|{
| import std.traits : hasMember;
|
| T* aggregate;
|
| enum bool isStdNullable =
| hasMember!(T, "isNull") &&
| hasMember!(T, "get") &&
| hasMember!(T, "nullify") &&
| is(typeof(__traits(getMember, aggregate, "isNull")()) == bool) &&
| !is(typeof(__traits(getMember, aggregate, "get")()) == void) &&
| is(typeof(__traits(getMember, aggregate, "nullify")()) == void);
|}
|
|///
|version(mir_core_test) unittest
|{
| import std.typecons;
| static assert(isStdNullable!(Nullable!double));
|}
|
|///
|version(mir_core_test) unittest
|{
| import mir.algebraic;
| static assert(isStdNullable!(Nullable!double));
|}
|
|/// Attribute for deprecated API
|struct reflectDeprecated(string target)
|{
| string info;
|}
|
|/// Attribute to rename methods, types and functions
|template ReflectName(string target)
|{
| ///
| struct ReflectName(Args...)
| {
| ///
| string name;
| }
|}
|
|/// ditto
|template reflectName(string target = null, Args...)
|{
| ///
| auto reflectName(string name)
| {
| alias TargetName = ReflectName!target;
| return TargetName!Args(name);
| }
|}
|
|///
|version(mir_core_test) unittest
|{
| enum E { A, B, C }
|
| struct S
| {
| @reflectName("A")
| int a;
|
| @reflectName!"c++"("B")
| int b;
|
| @reflectName!("C", double)("cd")
| @reflectName!("C", float)("cf")
| F c(F)()
| {
| return b;
| }
| }
|
| import std.traits: hasUDA;
|
| alias UniName = ReflectName!null;
| alias CppName = ReflectName!"c++";
| alias CName = ReflectName!"C";
|
| static assert(hasUDA!(S.a, UniName!()("A")));
| static assert(hasUDA!(S.b, CppName!()("B")));
|
| // static assert(hasUDA!(S.c, ReflectName)); // doesn't work for now
| static assert(hasUDA!(S.c, CName));
| static assert(hasUDA!(S.c, CName!double));
| static assert(hasUDA!(S.c, CName!float));
| static assert(hasUDA!(S.c, CName!double("cd")));
| static assert(hasUDA!(S.c, CName!float("cf")));
|}
|
|/// Attribute to rename methods, types and functions
|template ReflectMeta(string target, string[] fields)
|{
| ///
| struct ReflectMeta(Args...)
| {
| ///
| Args args;
| static foreach(i, field; fields)
| mixin(`alias ` ~ field ~` = args[` ~ i.stringof ~`];`);
| }
|}
|
|/// ditto
|template reflectMeta(string target, string[] fields)
|{
| ///
| auto reflectMeta(Args...)(Args args)
| if (args.length <= fields.length)
| {
| alias TargetMeta = ReflectMeta!(target, fields);
| return TargetMeta!Args(args);
| }
|}
|
|///
|version(mir_core_test) unittest
|{
| enum E { A, B, C }
|
| struct S
| {
| int a;
| @reflectMeta!("c++", ["type"])(E.C)
| int b;
| }
|
| import std.traits: hasUDA;
|
| alias CppMeta = ReflectMeta!("c++", ["type"]);
|
| static assert(CppMeta!E(E.C).type == E.C);
| static assert(!hasUDA!(S.a, CppMeta!E(E.A)));
| static assert(hasUDA!(S.b, CppMeta!E(E.C)));
|}
|
|/++
|Attribute to ignore a reflection target
|+/
|template reflectIgnore(string target)
|{
| enum reflectIgnore;
|}
|
|///
|version(mir_core_test) unittest
|{
| struct S
| {
| @reflectIgnore!"c++"
| int a;
| }
|
| import std.traits: hasUDA;
| static assert(hasUDA!(S.a, reflectIgnore!"c++"));
|}
|
|/// Attribute for documentation and unittests
|struct ReflectDoc(string target)
|{
| ///
| string text;
| ///
| reflectUnittest!target test;
|
| ///
| @safe pure nothrow @nogc
| this(string text)
| {
| this.text = text;
| }
|
| ///
| @safe pure nothrow @nogc
| this(string text, reflectUnittest!target test)
| {
| this.text = text;
| this.test = test;
| }
|
| ///
| void toString(W)(scope ref W w) scope const
| {
| w.put(cast()this.text);
|
| if (this.test.text.length)
| {
| w.put("\nExample usage:\n");
| w.put(cast()this.test.text);
| }
| }
|
| ///
| @safe pure nothrow
| string toString()() scope const
| {
| return this.text ~ "\nExample usage:\n" ~ this.test.text;
| }
|}
|
|/++
|Attribute for documentation.
|+/
|template reflectDoc(string target = null)
|{
| ///
| ReflectDoc!target reflectDoc(string text)
| {
| return ReflectDoc!target(text);
| }
|
| ///
| ReflectDoc!target reflectDoc(string text, reflectUnittest!target test)
| {
| return ReflectDoc!target(text, test);
| }
|}
|
|/++
|+/
|template reflectGetDocs(string target, alias symbol)
|{
| static if (hasUDA!(symbol, ReflectDoc!target))
| static immutable(ReflectDoc!target[]) reflectGetDocs = [getUDAs!(symbol, ReflectDoc!target)];
| else
| static immutable(ReflectDoc!target[]) reflectGetDocs = null;
|}
|
|/// ditto
|template reflectGetDocs(string target)
|{
| ///
| alias reflectGetDocs(alias symbol) = .reflectGetDocs!(target, symbol);
|
| /// ditto
| immutable(ReflectDoc!target)[] reflectGetDocs(T)(T value)
| @safe pure nothrow @nogc
| if (is(T == enum))
| {
| foreach (i, member; EnumMembers!T)
| {{
| alias all = __traits(getAttributes, EnumMembers!T[i]);
| }}
| static immutable ReflectDoc!target[][EnumMembers!T.length] docs = [staticMap!(reflectGetDocs, EnumMembers!T)];
| import mir.enums: getEnumIndex;
| uint index = void;
| if (getEnumIndex(value, index))
| return docs[index];
| assert(0);
| }
|}
|
|///
|version(mir_core_test) unittest
|{
| enum E
| {
| @reflectDoc("alpha")
| a,
| @reflectDoc!"C#"("Beta", reflectUnittest!"C#"("some c# code"))
| @reflectDoc("beta")
| b,
| c,
| }
|
| alias Doc = ReflectDoc!null;
| alias CSDoc = ReflectDoc!"C#";
|
| static assert(reflectGetDocs!null(E.a) == [Doc("alpha")]);
| static assert(reflectGetDocs!"C#"(E.b) == [CSDoc("Beta", reflectUnittest!"C#"("some c# code"))]);
| static assert(reflectGetDocs!null(E.b) == [Doc("beta")]);
| static assert(reflectGetDocs!null(E.c) is null);
|
| struct S
| {
| @reflectDoc("alpha")
| @reflectDoc!"C#"("Alpha")
| int a;
| }
|
| static assert(reflectGetDocs!(null, S.a) == [Doc("alpha")]);
| static assert(reflectGetDocs!("C#", S.a) == [CSDoc("Alpha")]);
|
| import std.conv: to;
| static assert(CSDoc("Beta", reflectUnittest!"C#"("some c# code")).to!string == "Beta\nExample usage:\nsome c# code");
|}
|
|/++
|Attribute for extern unit-test.
|+/
|struct reflectUnittest(string target)
|{
| ///
| string text;
|
|@safe pure nothrow @nogc:
|
| this(string text)
| {
| this.text = text;
| }
|
| this(const typeof(this) other)
| {
| this.text = other.text;
| }
|}
|
|/++
|+/
|template reflectGetUnittest(string target, alias symbol)
|{
| static if (hasUDA!(symbol, reflectUnittest!target))
| enum string reflectGetUnittest = getUDA!(symbol, reflectUnittest).text;
| else
| enum string reflectGetUnittest = null;
|}
|
|/// ditto
|template reflectGetUnittest(string target)
|{
| ///
| alias reflectGetUnittest(alias symbol) = .reflectGetUnittest!(target, symbol);
|
| ///
| string reflectGetUnittest(T)(T value)
| if (is(T == enum))
| {
| foreach (i, member; EnumMembers!T)
| {{
| alias all = __traits(getAttributes, EnumMembers!T[i]);
| }}
| static immutable string[EnumMembers!T.length] tests = [staticMap!(reflectGetUnittest, EnumMembers!T)];
| import mir.enums: getEnumIndex;
| uint index = void;
| if (getEnumIndex(value, index))
| return tests[index];
| assert(0);
| }
|}
|
|///
|version(mir_core_test) unittest
|{
| enum E
| {
| @reflectUnittest!"c++"("assert(E::a == 0);")
| a,
| @reflectUnittest!"c++"("assert(E::b == 1);")
| b,
| c,
| }
|
| static assert(reflectGetUnittest!"c++"(E.a) == "assert(E::a == 0);");
| static assert(reflectGetUnittest!"c++"(E.b) == "assert(E::b == 1);");
| static assert(reflectGetUnittest!"c++"(E.c) is null);
|
| struct S
| {
| @reflectUnittest!"c++"("alpha")
| int a;
| }
|
| static assert(reflectGetUnittest!("c++", S.a) == "alpha");
|}
|
|/++
|Returns: single UDA.
|+/
|template getUDA(alias symbol, alias attribute)
|{
| private alias all = getUDAs!(symbol, attribute);
| static if (all.length != 1)
| static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof);
| else
| {
| static if (is(typeof(all[0])))
| enum getUDA = all[0];
| else
| alias getUDA = all[0];
| }
|}
|
|/++
|Checks if T has a field member.
|+/
|enum bool isOriginalMember(T, string member) = __traits(identifier, __traits(getMember, T, member)) == member;
|
|///
|version(mir_core_test) unittest
|{
| struct D
| {
| int a;
| alias b = a;
| }
|
| static assert(isOriginalMember!(D, "a"));
| static assert(!isOriginalMember!(D, "b"));
|}
|
|/++
|Checks if T has a field member.
|+/
|enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; });
|
|deprecated("use 'hasField' instead") alias isField = hasField;
|
|///
|version(mir_core_test) unittest
|{
| struct D
| {
| int gi;
| }
|
| struct I
| {
| int f;
|
| D base;
| alias base this;
|
| void gi(double ) @property {}
| void gi(uint ) @property {}
| }
|
| struct S
| {
| int d;
|
| I i;
| alias i this;
|
| int gm() @property {return 0;}
| int gc() const @property {return 0;}
| void gs(int) @property {}
| }
|
| static assert(!hasField!(S, "gi"));
| static assert(!hasField!(S, "gs"));
| static assert(!hasField!(S, "gc"));
| static assert(!hasField!(S, "gm"));
| static assert(!hasField!(S, "gi"));
| static assert(hasField!(S, "d"));
| static assert(hasField!(S, "f"));
| static assert(hasField!(S, "i"));
|}
|
|/// with classes
|version(mir_core_test) unittest
|{
| class I
| {
| int f;
|
| void gi(double ) @property {}
| void gi(uint ) @property {}
| }
|
| class S
| {
| int d;
|
| I i;
| alias i this;
|
| int gm() @property {return 0;}
| int gc() const @property {return 0;}
| void gs(int) @property {}
| }
|
| static assert(!hasField!(S, "gi"));
| static assert(!hasField!(S, "gs"));
| static assert(!hasField!(S, "gc"));
| static assert(!hasField!(S, "gm"));
| static assert(hasField!(S, "d"));
| static assert(hasField!(S, "f"));
| static assert(hasField!(S, "i"));
|}
|
|/++
|Checks if member is property.
|+/
|template isProperty(T, string member)
|{
| T* aggregate;
|
| static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
| {
| static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
| {
| enum bool isProperty = isPropertyImpl!(__traits(getMember, *aggregate, member));
| }
| else
| {
| enum bool isProperty = false;
| }
| }
| else
| enum bool isProperty = false;
|}
|
|///
|version(mir_core_test) unittest
|{
| struct D
| {
| int y;
|
| void gf(double ) @property {}
| void gf(uint ) @property {}
| }
|
| struct I
| {
| int f;
|
| D base;
| alias base this;
|
| void gi(double ) @property {}
| void gi(uint ) @property {}
| }
|
| struct S
| {
| int d;
|
| I i;
| alias i this;
|
| int gm() @property {return 0;}
| int gc() const @property {return 0;}
| void gs(int) @property {}
| }
|
| static assert(isProperty!(S, "gf"));
| static assert(isProperty!(S, "gi"));
| static assert(isProperty!(S, "gs"));
| static assert(isProperty!(S, "gc"));
| static assert(isProperty!(S, "gm"));
| static assert(!isProperty!(S, "d"));
| static assert(!isProperty!(S, "f"));
| static assert(!isProperty!(S, "y"));
|}
|
|/++
|Returns: list of the setter properties.
|
|Note: The implementation ignores templates.
|+/
|template getSetters(T, string member)
|{
| static if (__traits(hasMember, T, member))
| alias getSetters = Filter!(hasSingleArgument, Filter!(isPropertyImpl, __traits(getOverloads, T, member)));
| else
| alias getSetters = AliasSeq!();
|}
|
|///
|version(mir_core_test) unittest
|{
| struct I
| {
| int f;
|
| void gi(double ) @property {}
| void gi(uint ) @property {}
| }
|
| struct S
| {
| int d;
|
| I i;
| alias i this;
|
| int gm() @property {return 0;}
| int gc() const @property {return 0;}
| void gs(int) @property {}
| }
|
| static assert(getSetters!(S, "gi").length == 2);
| static assert(getSetters!(S, "gs").length == 1);
| static assert(getSetters!(S, "gc").length == 0);
| static assert(getSetters!(S, "gm").length == 0);
| static assert(getSetters!(S, "d").length == 0);
| static assert(getSetters!(S, "f").length == 0);
|}
|
|/++
|Returns: list of the serializable (public getters) members.
|+/
|enum string[] SerializableMembers(T) = [Filter!(ApplyLeft!(Serializable, T), FieldsAndProperties!T)];
|
|///
|version(mir_core_test) unittest
|{
| struct D
| {
| int y;
|
| int gf() @property {return 0;}
| }
|
| struct I
| {
| int f;
|
| D base;
| alias base this;
|
| int gi() @property {return 0;}
| }
|
| struct S
| {
| int d;
|
| package int p;
|
| int gm() @property {return 0;}
|
| private int q;
|
| I i;
| alias i this;
|
| int gc() const @property {return 0;}
| void gs(int) @property {}
| }
|
| static assert(SerializableMembers!S == ["y", "gf", "f", "gi", "d", "gm", "gc"]);
| static assert(SerializableMembers!(const S) == ["y", "f", "d", "gc"]);
|}
|
|/++
|Returns: list of the deserializable (public setters) members.
|+/
|enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), FieldsAndProperties!T)];
|
|///
|version(mir_core_test) unittest
|{
| struct I
| {
| int f;
| void ga(int) @property {}
| }
|
| struct S
| {
| int d;
| package int p;
|
| int gm() @property {return 0;}
| void gm(int) @property {}
|
| private int q;
|
| I i;
| alias i this;
|
|
| void gc(int, int) @property {}
| void gc(int) @property {}
| }
|
| S s;
| // s.gc(0);
|
| static assert (DeserializableMembers!S == ["f", "ga", "d", "gm", "gc"]);
| static assert (DeserializableMembers!(const S) == []);
|}
|
|// This trait defines what members should be serialized -
|// public members that are either readable and writable or getter properties
|private template Serializable(T, string member)
|{
| static if (!isPublic!(T, member))
| enum Serializable = false;
| else
| enum Serializable = isReadable!(T, member); // any readable is good
|}
|
|private enum bool hasSingleArgument(alias fun) = Parameters!fun.length == 1;
|private enum bool hasZeroArguments(alias fun) = Parameters!fun.length == 0;
|
|// This trait defines what members should be serialized -
|// public members that are either readable and writable or setter properties
|private template Deserializable(T, string member)
|{
| static if (!isPublic!(T, member))
| enum Deserializable = false;
| else
| static if (isReadableAndWritable!(T, member))
| enum Deserializable = true;
| else
| static if (getSetters!(T, member).length == 1)
| enum Deserializable = is(typeof((ref T val){ __traits(getMember, val, member) = Parameters!(getSetters!(T, member)[0])[0].init; }));
| else
| enum Deserializable = false;
|}
|
|private enum FieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(FieldsAndPropertiesImpl!T)));
|
|private template allMembers(T)
|{
| static if (isAggregateType!T)
| alias allMembers = __traits(allMembers, T);
| else
| alias allMembers = AliasSeq!();
|}
|
|private template FieldsAndPropertiesImpl(T)
|{
| alias isProperty = ApplyLeft!(.isProperty, T);
| alias hasField = ApplyLeft!(.hasField, T);
| alias isOriginalMember = ApplyLeft!(.isOriginalMember, T);
| alias isMember = templateAnd!(templateOr!(hasField, isProperty), isOriginalMember);
| static if (__traits(getAliasThis, T).length)
| {
| T* aggregate;
| alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
| static if (isAggregateType!T)
| alias baseMembers = FieldsAndPropertiesImpl!A;
| else
| alias baseMembers = AliasSeq!();
| alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
| alias FieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isMember, members));
| }
| else
| {
| import mir.algebraic;
| static if (isVariant!T)
| alias members = staticMap!(allMembers, T.AllowedTypes);
| else
| alias members = allMembers!T;
| alias FieldsAndPropertiesImpl = AliasSeq!(Filter!(isMember, members));
| }
|}
|
|// check if the member is readable
|private template isReadable(T, string member)
|{
| T* aggregate;
| enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, *aggregate, member)); });
|}
|
|// check if the member is readable/writeble?
|private template isReadableAndWritable(T, string member)
|{
| T* aggregate;
| enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, *aggregate, member) = __traits(getMember, *aggregate, member));
|}
|
|package template isPublic(T, string member)
|{
| T* aggregate;
| enum bool isPublic = !__traits(getProtection, __traits(getMember, *aggregate, member)).privateOrPackage;
|}
|
|// check if the member is property
|private template isSetter(T, string member)
|{
| T* aggregate;
| static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
| {
| static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
| {
| enum bool isSetter = getSetters!(T, member).length > 0;;
| }
| else
| {
| enum bool isSetter = false;
| }
| }
| else
| enum bool isSetter = false;
|}
|
|private template isGetter(T, string member)
|{
| T* aggregate;
| static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
| {
| static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
| {
| enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member))).length == 1;
| }
| else
| {
| enum bool isGetter = false;
| }
| }
| else
| enum bool isGetter = false;
|}
|
|private enum bool isPropertyImpl(alias member) = (functionAttributes!member & FunctionAttribute.property) != 0;
|
|private bool privateOrPackage()(string protection)
|{
| return protection == "private" || protection == "package";
|}
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/reflection.d has no code
<<<<<< EOF
# path=./source-mir-math-func-expdigamma.lst
|/**
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|
|Authors: Ilya Yaroshenko
|*/
|module mir.math.func.expdigamma;
|
|/++
|Optimized and more precise analog of `y = exp(digamma(x))`.
|
|Returns:
| `exp(digamma(x))`
|+/
|F expDigamma(F)(in F x)
|{
| import mir.math.common;
|
| static immutable F[7] c = [
| F(1.0 / 24),
| F(1.0L / 48),
| F(23.0L / 5760),
| F(17.0L / 3840),
| F(10_099.0L / 2_903_040),
| F(2501.0L / 1_161_216),
| F(795_697.0L / 199_065_600),
| ];
|
129| if (!(x >= 0))
0000000| return F.nan;
129| F s = x;
129| F w = 0;
859| while ( s < F(10) )
| {
730| w += 1 / s;
730| s += 1;
| }
129| F y = F(-0.5);
129| F t = 1;
| import mir.internal.utility;
| foreach (i; Iota!(0, c.length))
| {
903| t *= s;
903| y += c[i] / t;
| }
129| y += s;
129| y /= exp(w);
129| return y;
|}
|
|version(mir_test)
|unittest
|{
| import std.meta: AliasSeq;
| import std.mathspecial: digamma;
| import mir.math: approxEqual, exp, nextUp, nextDown;
1| assert(approxEqual(expDigamma(0.001), exp(digamma(0.001))));
1| assert(approxEqual(expDigamma(0.1), exp(digamma(0.1))));
1| assert(approxEqual(expDigamma(1.0), exp(digamma(1.0))));
1| assert(approxEqual(expDigamma(2.3), exp(digamma(2.3))));
1| assert(approxEqual(expDigamma(20.0), exp(digamma(20.0))));
1| assert(approxEqual(expDigamma(40.0), exp(digamma(40.0))));
| foreach (F; AliasSeq!(float, double, real))
| {
3| assert(expDigamma!F(0.0) == 0);
3| assert(expDigamma!F(0.0.nextUp) >= 0);
3| assert(expDigamma!F(0.0.min_normal) >= 0);
3| assert(expDigamma!F(0.5.nextUp) >= 0);
3| assert(expDigamma!F(0.5.nextDown) >= 0);
90| foreach (i; 1 .. 10)
| {
27| assert(expDigamma(F(i)) >= expDigamma(F(i).nextDown));
27| assert(expDigamma(F(i)) <= expDigamma(F(i).nextUp));
| }
| }
|}
source/mir/math/func/expdigamma.d is 96% covered
<<<<<< EOF
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-math-ieee.lst
|/**
| * Base floating point routines.
| *
| * Macros:
| * TABLE_SV =
| * Special Values
| * $0
| * SVH = $(TR $(TH $1) $(TH $2))
| * SV = $(TR $(TD $1) $(TD $2))
| * TH3 = $(TR $(TH $1) $(TH $2) $(TH $3))
| * TD3 = $(TR $(TD $1) $(TD $2) $(TD $3))
| * TABLE_DOMRG =
| * $(SVH Domain X, Range Y)
| $(SV $1, $2)
| *
| * DOMAIN=$1
| * RANGE=$1
| * NAN = $(RED NAN)
| * SUP = $0
| * GAMMA = Γ
| * THETA = θ
| * INTEGRAL = ∫
| * INTEGRATE = $(BIG ∫$(SMALL $1)$2)
| * POWER = $1$2
| * SUB = $1$2
| * BIGSUM = $(BIG Σ $2$(SMALL $1))
| * CHOOSE = $(BIG () $(SMALL $1)$(SMALL $2) $(BIG ))
| * PLUSMN = ±
| * INFIN = ∞
| * PLUSMNINF = ±∞
| * PI = π
| * LT = <
| * GT = >
| * SQRT = √
| * HALF = ½
| *
| * License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
| * Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, Ilya Yaroshenko
| */
|module mir.math.ieee;
|
|import mir.internal.utility: isFloatingPoint;
|
|/*********************************
| * Return `true` if sign bit of e is set, `false` if not.
| */
|bool signbit(T)(const T x) @nogc @trusted pure nothrow
|{
| if (__ctfe)
| {
| double dval = cast(double) x; // Precision can increase or decrease but sign won't change (even NaN).
| return 0 > *cast(long*) &dval;
| }
|
| mixin floatTraits!T;
|
| static if (realFormat == RealFormat.ieeeSingle)
| {
| return 0 > *cast(int*) &x;
| }
| else
| static if (realFormat == RealFormat.ieeeDouble)
| {
| return 0 > *cast(long*) &x;
| }
| else
| static if (realFormat == RealFormat.ieeeQuadruple)
| {
| return 0 > ((cast(long*)&x)[MANTISSA_MSB]);
| }
| else static if (realFormat == RealFormat.ieeeExtended)
| {
| version (LittleEndian)
| auto mp = cast(ubyte*)&x + 9;
| else
| auto mp = cast(ubyte*)&x;
|
| return (*mp & 0x80) != 0;
| }
| else static assert(0, "signbit is not implemented.");
|}
|
|///
|@nogc @safe pure nothrow version(mir_core_test) unittest
|{
| assert(!signbit(float.nan));
| assert(signbit(-float.nan));
| assert(!signbit(168.1234f));
| assert(signbit(-168.1234f));
| assert(!signbit(0.0f));
| assert(signbit(-0.0f));
| assert(signbit(-float.max));
| assert(!signbit(float.max));
|
| assert(!signbit(double.nan));
| assert(signbit(-double.nan));
| assert(!signbit(168.1234));
| assert(signbit(-168.1234));
| assert(!signbit(0.0));
| assert(signbit(-0.0));
| assert(signbit(-double.max));
| assert(!signbit(double.max));
|
| assert(!signbit(real.nan));
| assert(signbit(-real.nan));
| assert(!signbit(168.1234L));
| assert(signbit(-168.1234L));
| assert(!signbit(0.0L));
| assert(signbit(-0.0L));
| assert(signbit(-real.max));
| assert(!signbit(real.max));
|}
|
|/**************************************
| * To what precision is x equal to y?
| *
| * Returns: the number of mantissa bits which are equal in x and y.
| * eg, 0x1.F8p+60 and 0x1.F1p+60 are equal to 5 bits of precision.
| *
| * $(TABLE_SV
| * $(TR $(TH x) $(TH y) $(TH feqrel(x, y)))
| * $(TR $(TD x) $(TD x) $(TD real.mant_dig))
| * $(TR $(TD x) $(TD $(GT)= 2*x) $(TD 0))
| * $(TR $(TD x) $(TD $(LT)= x/2) $(TD 0))
| * $(TR $(TD $(NAN)) $(TD any) $(TD 0))
| * $(TR $(TD any) $(TD $(NAN)) $(TD 0))
| * )
| */
|int feqrel(T)(const T x, const T y) @trusted pure nothrow @nogc
| if (isFloatingPoint!T)
|{
| /* Public Domain. Author: Don Clugston, 18 Aug 2005.
| */
| mixin floatTraits!T;
| static if (realFormat == RealFormat.ieeeSingle
| || realFormat == RealFormat.ieeeDouble
| || realFormat == RealFormat.ieeeExtended
| || realFormat == RealFormat.ieeeQuadruple)
| {
| import mir.math.common: fabs;
|
| if (x == y)
| return T.mant_dig; // ensure diff != 0, cope with IN
|
| auto diff = fabs(x - y);
|
| int a = ((cast(U*)& x)[idx] & exp_mask) >>> exp_shft;
| int b = ((cast(U*)& y)[idx] & exp_mask) >>> exp_shft;
| int d = ((cast(U*)&diff)[idx] & exp_mask) >>> exp_shft;
|
|
| // The difference in abs(exponent) between x or y and abs(x-y)
| // is equal to the number of significand bits of x which are
| // equal to y. If negative, x and y have different exponents.
| // If positive, x and y are equal to 'bitsdiff' bits.
| // AND with 0x7FFF to form the absolute value.
| // To avoid out-by-1 errors, we subtract 1 so it rounds down
| // if the exponents were different. This means 'bitsdiff' is
| // always 1 lower than we want, except that if bitsdiff == 0,
| // they could have 0 or 1 bits in common.
|
| int bitsdiff = ((a + b - 1) >> 1) - d;
| if (d == 0)
| { // Difference is subnormal
| // For subnormals, we need to add the number of zeros that
| // lie at the start of diff's significand.
| // We do this by multiplying by 2^^real.mant_dig
| diff *= norm_factor;
| return bitsdiff + T.mant_dig - int(((cast(U*)&diff)[idx] & exp_mask) >>> exp_shft);
| }
|
| if (bitsdiff > 0)
| return bitsdiff + 1; // add the 1 we subtracted before
|
| // Avoid out-by-1 errors when factor is almost 2.
| if (bitsdiff == 0 && (a ^ b) == 0)
| return 1;
| else
| return 0;
| }
| else
| {
| static assert(false, "Not implemented for this architecture");
| }
|}
|
|///
|@safe pure version(mir_core_test) unittest
|{
| assert(feqrel(2.0, 2.0) == 53);
| assert(feqrel(2.0f, 2.0f) == 24);
| assert(feqrel(2.0, double.nan) == 0);
|
| // Test that numbers are within n digits of each
| // other by testing if feqrel > n * log2(10)
|
| // five digits
| assert(feqrel(2.0, 2.00001) > 16);
| // ten digits
| assert(feqrel(2.0, 2.00000000001) > 33);
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| void testFeqrel(F)()
| {
| // Exact equality
| assert(feqrel(F.max, F.max) == F.mant_dig);
| assert(feqrel!(F)(0.0, 0.0) == F.mant_dig);
| assert(feqrel(F.infinity, F.infinity) == F.mant_dig);
|
| // a few bits away from exact equality
| F w=1;
| for (int i = 1; i < F.mant_dig - 1; ++i)
| {
| assert(feqrel!(F)(1.0 + w * F.epsilon, 1.0) == F.mant_dig-i);
| assert(feqrel!(F)(1.0 - w * F.epsilon, 1.0) == F.mant_dig-i);
| assert(feqrel!(F)(1.0, 1 + (w-1) * F.epsilon) == F.mant_dig - i + 1);
| w*=2;
| }
|
| assert(feqrel!(F)(1.5+F.epsilon, 1.5) == F.mant_dig-1);
| assert(feqrel!(F)(1.5-F.epsilon, 1.5) == F.mant_dig-1);
| assert(feqrel!(F)(1.5-F.epsilon, 1.5+F.epsilon) == F.mant_dig-2);
|
|
| // Numbers that are close
| assert(feqrel!(F)(0x1.Bp+84, 0x1.B8p+84) == 5);
| assert(feqrel!(F)(0x1.8p+10, 0x1.Cp+10) == 2);
| assert(feqrel!(F)(1.5 * (1 - F.epsilon), 1.0L) == 2);
| assert(feqrel!(F)(1.5, 1.0) == 1);
| assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
|
| // Factors of 2
| assert(feqrel(F.max, F.infinity) == 0);
| assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
| assert(feqrel!(F)(1.0, 2.0) == 0);
| assert(feqrel!(F)(4.0, 1.0) == 0);
|
| // Extreme inequality
| assert(feqrel(F.nan, F.nan) == 0);
| assert(feqrel!(F)(0.0L, -F.nan) == 0);
| assert(feqrel(F.nan, F.infinity) == 0);
| assert(feqrel(F.infinity, -F.infinity) == 0);
| assert(feqrel(F.max, -F.max) == 0);
|
| assert(feqrel(F.min_normal / 8, F.min_normal / 17) == 3);
|
| const F Const = 2;
| immutable F Immutable = 2;
| auto Compiles = feqrel(Const, Immutable);
| }
|
| assert(feqrel(7.1824L, 7.1824L) == real.mant_dig);
|
| testFeqrel!(float)();
| testFeqrel!(double)();
| testFeqrel!(real)();
|}
|
|/++
|+/
|enum RealFormat
|{
| ///
| ieeeHalf,
| ///
| ieeeSingle,
| ///
| ieeeDouble,
| /// x87 80-bit real
| ieeeExtended,
| /// x87 real rounded to precision of double.
| ieeeExtended53,
| /// IBM 128-bit extended
| ibmExtended,
| ///
| ieeeQuadruple,
|}
|
|/**
| * Calculate the next largest floating point value after x.
| *
| * Return the least number greater than x that is representable as a real;
| * thus, it gives the next point on the IEEE number line.
| *
| * $(TABLE_SV
| * $(SVH x, nextUp(x) )
| * $(SV -$(INFIN), -real.max )
| * $(SV $(PLUSMN)0.0, real.min_normal*real.epsilon )
| * $(SV real.max, $(INFIN) )
| * $(SV $(INFIN), $(INFIN) )
| * $(SV $(NAN), $(NAN) )
| * )
| */
|T nextUp(T)(const T x) @trusted pure nothrow @nogc
| if (isFloatingPoint!T)
|{
| mixin floatTraits!T;
| static if (realFormat == RealFormat.ieeeSingle)
| {
| uint s = *cast(uint*)&x;
| if ((s & 0x7F80_0000) == 0x7F80_0000)
| {
| // First, deal with NANs and infinity
| if (x == -x.infinity) return -x.max;
|
| return x; // +INF and NAN are unchanged.
| }
| if (s > 0x8000_0000) // Negative number
| {
| --s;
| }
| else
| if (s == 0x8000_0000) // it was negative zero
| {
| s = 0x0000_0001; // change to smallest subnormal
| }
| else
| {
| // Positive number
| ++s;
| }
| R:
| return *cast(T*)&s;
| }
| else static if (realFormat == RealFormat.ieeeDouble)
| {
| ulong s = *cast(ulong*)&x;
|
| if ((s & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000)
| {
| // First, deal with NANs and infinity
| if (x == -x.infinity) return -x.max;
| return x; // +INF and NAN are unchanged.
| }
| if (s > 0x8000_0000_0000_0000) // Negative number
| {
| --s;
| }
| else
| if (s == 0x8000_0000_0000_0000) // it was negative zero
| {
| s = 0x0000_0000_0000_0001; // change to smallest subnormal
| }
| else
| {
| // Positive number
| ++s;
| }
| R:
| return *cast(T*)&s;
| }
| else static if (realFormat == RealFormat.ieeeQuadruple)
| {
| auto e = exp_mask & (cast(U *)&x)[idx];
| if (e == exp_mask)
| {
| // NaN or Infinity
| if (x == -real.infinity) return -real.max;
| return x; // +Inf and NaN are unchanged.
| }
|
| auto ps = cast(ulong *)&x;
| if (ps[MANTISSA_MSB] & 0x8000_0000_0000_0000)
| {
| // Negative number
| if (ps[MANTISSA_LSB] == 0 && ps[MANTISSA_MSB] == 0x8000_0000_0000_0000)
| {
| // it was negative zero, change to smallest subnormal
| ps[MANTISSA_LSB] = 1;
| ps[MANTISSA_MSB] = 0;
| return x;
| }
| if (ps[MANTISSA_LSB] == 0) --ps[MANTISSA_MSB];
| --ps[MANTISSA_LSB];
| }
| else
| {
| // Positive number
| ++ps[MANTISSA_LSB];
| if (ps[MANTISSA_LSB] == 0) ++ps[MANTISSA_MSB];
| }
| return x;
| }
| else static if (realFormat == RealFormat.ieeeExtended)
| {
| // For 80-bit reals, the "implied bit" is a nuisance...
| auto pe = cast(U*)&x + idx;
| version (LittleEndian)
| auto ps = cast(ulong*)&x;
| else
| auto ps = cast(ulong*)((cast(ushort*)&x) + 1);
|
| if ((*pe & exp_mask) == exp_mask)
| {
| // First, deal with NANs and infinity
| if (x == -real.infinity) return -real.max;
| return x; // +Inf and NaN are unchanged.
| }
| if (*pe & 0x8000)
| {
| // Negative number -- need to decrease the significand
| --*ps;
| // Need to mask with 0x7FF.. so subnormals are treated correctly.
| if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_FFFF_FFFF_FFFF)
| {
| if (*pe == 0x8000) // it was negative zero
| {
| *ps = 1;
| *pe = 0; // smallest subnormal.
| return x;
| }
|
| --*pe;
|
| if (*pe == 0x8000)
| return x; // it's become a subnormal, implied bit stays low.
|
| *ps = 0xFFFF_FFFF_FFFF_FFFF; // set the implied bit
| return x;
| }
| return x;
| }
| else
| {
| // Positive number -- need to increase the significand.
| // Works automatically for positive zero.
| ++*ps;
| if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0)
| {
| // change in exponent
| ++*pe;
| *ps = 0x8000_0000_0000_0000; // set the high bit
| }
| }
| return x;
| }
| else // static if (realFormat == RealFormat.ibmExtended)
| {
| assert(0, "nextUp not implemented");
| }
|}
|
|///
|@safe @nogc pure nothrow version(mir_core_test) unittest
|{
| assert(nextUp(1.0 - 1.0e-6).feqrel(0.999999) > 16);
| assert(nextUp(1.0 - real.epsilon).feqrel(1.0) > 16);
|}
|
|/**
| * Calculate the next smallest floating point value before x.
| *
| * Return the greatest number less than x that is representable as a real;
| * thus, it gives the previous point on the IEEE number line.
| *
| * $(TABLE_SV
| * $(SVH x, nextDown(x) )
| * $(SV $(INFIN), real.max )
| * $(SV $(PLUSMN)0.0, -real.min_normal*real.epsilon )
| * $(SV -real.max, -$(INFIN) )
| * $(SV -$(INFIN), -$(INFIN) )
| * $(SV $(NAN), $(NAN) )
| * )
| */
|T nextDown(T)(const T x) @safe pure nothrow @nogc
|{
| return -nextUp(-x);
|}
|
|///
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| assert( nextDown(1.0 + real.epsilon) == 1.0);
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import std.math: NaN, isIdentical;
|
| static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
| {
|
| // Tests for 80-bit reals
| assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC)));
| // negative numbers
| assert( nextUp(-real.infinity) == -real.max );
| assert( nextUp(-1.0L-real.epsilon) == -1.0 );
| assert( nextUp(-2.0L) == -2.0 + real.epsilon);
| // subnormals and zero
| assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) );
| assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) );
| assert( isIdentical(-0.0L, nextUp(-real.min_normal*real.epsilon)) );
| assert( nextUp(-0.0L) == real.min_normal*real.epsilon );
| assert( nextUp(0.0L) == real.min_normal*real.epsilon );
| assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal );
| assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) );
| // positive numbers
| assert( nextUp(1.0L) == 1.0 + real.epsilon );
| assert( nextUp(2.0L-real.epsilon) == 2.0 );
| assert( nextUp(real.max) == real.infinity );
| assert( nextUp(real.infinity)==real.infinity );
| }
|
| double n = NaN(0xABC);
| assert(isIdentical(nextUp(n), n));
| // negative numbers
| assert( nextUp(-double.infinity) == -double.max );
| assert( nextUp(-1-double.epsilon) == -1.0 );
| assert( nextUp(-2.0) == -2.0 + double.epsilon);
| // subnormals and zero
|
| assert( nextUp(-double.min_normal) == -double.min_normal*(1-double.epsilon) );
| assert( nextUp(-double.min_normal*(1-double.epsilon)) == -double.min_normal*(1-2*double.epsilon) );
| assert( isIdentical(-0.0, nextUp(-double.min_normal*double.epsilon)) );
| assert( nextUp(0.0) == double.min_normal*double.epsilon );
| assert( nextUp(-0.0) == double.min_normal*double.epsilon );
| assert( nextUp(double.min_normal*(1-double.epsilon)) == double.min_normal );
| assert( nextUp(double.min_normal) == double.min_normal*(1+double.epsilon) );
| // positive numbers
| assert( nextUp(1.0) == 1.0 + double.epsilon );
| assert( nextUp(2.0-double.epsilon) == 2.0 );
| assert( nextUp(double.max) == double.infinity );
|
| float fn = NaN(0xABC);
| assert(isIdentical(nextUp(fn), fn));
| float f = -float.min_normal*(1-float.epsilon);
| float f1 = -float.min_normal;
| assert( nextUp(f1) == f);
| f = 1.0f+float.epsilon;
| f1 = 1.0f;
| assert( nextUp(f1) == f );
| f1 = -0.0f;
| assert( nextUp(f1) == float.min_normal*float.epsilon);
| assert( nextUp(float.infinity)==float.infinity );
|
| assert(nextDown(1.0L+real.epsilon)==1.0);
| assert(nextDown(1.0+double.epsilon)==1.0);
| f = 1.0f+float.epsilon;
| assert(nextDown(f)==1.0);
|}
|
|/++
|Return the value that lies halfway between x and y on the IEEE number line.
|
|Formally, the result is the arithmetic mean of the binary significands of x
|and y, multiplied by the geometric mean of the binary exponents of x and y.
|x and y must not be NaN.
|Note: this function is useful for ensuring O(log n) behaviour in algorithms
|involving a 'binary chop'.
|
|Params:
| xx = x value
| yy = y value
|
|Special cases:
|If x and y not null and have opposite sign bits, then `copysign(T(0), y)` is returned.
|If x and y are within a factor of 2 and have the same sign, (ie, feqrel(x, y) > 0), the return value
|is the arithmetic mean (x + y) / 2.
|If x and y are even powers of 2 and have the same sign, the return value is the geometric mean,
|ieeeMean(x, y) = sgn(x) * sqrt(fabs(x * y)).
|+/
|T ieeeMean(T)(const T xx, const T yy) @trusted pure nothrow @nogc
|in
|{
| assert(xx == xx && yy == yy);
|}
|do
|{
| import mir.math.common: copysign;
| T x = xx;
| T y = yy;
|
| if (x == 0)
| {
| x = copysign(T(0), y);
| }
| else
| if (y == 0)
| {
| y = copysign(T(0), x);
| }
| else
| if (signbit(x) != signbit(y))
| {
| return copysign(T(0), y);
| }
|
| // The implementation is simple: cast x and y to integers,
| // average them (avoiding overflow), and cast the result back to a floating-point number.
|
| mixin floatTraits!(T);
| T u = 0;
| static if (realFormat == RealFormat.ieeeExtended)
| {
| // There's slight additional complexity because they are actually
| // 79-bit reals...
| ushort *ue = cast(ushort *)&u + idx;
| int ye = (cast(ushort *)&y)[idx];
| int xe = (cast(ushort *)&x)[idx];
|
| version (LittleEndian)
| {
| ulong *ul = cast(ulong *)&u;
| ulong xl = *cast(ulong *)&x;
| ulong yl = *cast(ulong *)&y;
| }
| else
| {
| ulong *ul = cast(ulong *)(cast(short *)&u + 1);
| ulong xl = *cast(ulong *)(cast(short *)&x + 1);
| ulong yl = *cast(ulong *)(cast(short *)&y + 1);
| }
|
| // Ignore the useless implicit bit. (Bonus: this prevents overflows)
| ulong m = (xl & 0x7FFF_FFFF_FFFF_FFFFL) + (yl & 0x7FFF_FFFF_FFFF_FFFFL);
|
| int e = ((xe & exp_mask) + (ye & exp_mask));
| if (m & 0x8000_0000_0000_0000L)
| {
| ++e;
| m &= 0x7FFF_FFFF_FFFF_FFFFL;
| }
| // Now do a multi-byte right shift
| const uint c = e & 1; // carry
| e >>= 1;
| m >>>= 1;
| if (c)
| m |= 0x4000_0000_0000_0000L; // shift carry into significand
| if (e)
| *ul = m | 0x8000_0000_0000_0000L; // set implicit bit...
| else
| *ul = m; // ... unless exponent is 0 (subnormal or zero).
|
| *ue = cast(ushort) (e | (xe & 0x8000)); // restore sign bit
| }
| else static if (realFormat == RealFormat.ieeeQuadruple)
| {
| // This would be trivial if 'ucent' were implemented...
| ulong *ul = cast(ulong *)&u;
| ulong *xl = cast(ulong *)&x;
| ulong *yl = cast(ulong *)&y;
|
| // Multi-byte add, then multi-byte right shift.
| import core.checkedint: addu;
| bool carry;
| ulong ml = addu(xl[MANTISSA_LSB], yl[MANTISSA_LSB], carry);
|
| ulong mh = carry + (xl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL) +
| (yl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL);
|
| ul[MANTISSA_MSB] = (mh >>> 1) | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000);
| ul[MANTISSA_LSB] = (ml >>> 1) | (mh & 1) << 63;
| }
| else static if (realFormat == RealFormat.ieeeDouble)
| {
| ulong *ul = cast(ulong *)&u;
| ulong *xl = cast(ulong *)&x;
| ulong *yl = cast(ulong *)&y;
| ulong m = (((*xl) & 0x7FFF_FFFF_FFFF_FFFFL)
| + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL)) >>> 1;
| m |= ((*xl) & 0x8000_0000_0000_0000L);
| *ul = m;
| }
| else static if (realFormat == RealFormat.ieeeSingle)
| {
| uint *ul = cast(uint *)&u;
| uint *xl = cast(uint *)&x;
| uint *yl = cast(uint *)&y;
| uint m = (((*xl) & 0x7FFF_FFFF) + ((*yl) & 0x7FFF_FFFF)) >>> 1;
| m |= ((*xl) & 0x8000_0000);
| *ul = m;
| }
| else
| {
| assert(0, "Not implemented");
| }
| return u;
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| assert(ieeeMean(-0.0,-1e-20)<0);
| assert(ieeeMean(0.0,1e-20)>0);
|
| assert(ieeeMean(1.0L,4.0L)==2L);
| assert(ieeeMean(2.0*1.013,8.0*1.013)==4*1.013);
| assert(ieeeMean(-1.0L,-4.0L)==-2L);
| assert(ieeeMean(-1.0,-4.0)==-2);
| assert(ieeeMean(-1.0f,-4.0f)==-2f);
| assert(ieeeMean(-1.0,-2.0)==-1.5);
| assert(ieeeMean(-1*(1+8*real.epsilon),-2*(1+8*real.epsilon))
| ==-1.5*(1+5*real.epsilon));
| assert(ieeeMean(0x1p60,0x1p-10)==0x1p25);
|
| static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
| {
| assert(ieeeMean(1.0L,real.infinity)==0x1p8192L);
| assert(ieeeMean(0.0L,real.infinity)==1.5);
| }
| assert(ieeeMean(0.5*real.min_normal*(1-4*real.epsilon),0.5*real.min_normal)
| == 0.5*real.min_normal*(1-2*real.epsilon));
|}
|
|/*********************************************************************
| * Separate floating point value into significand and exponent.
| *
| * Returns:
| * Calculate and return $(I x) and $(I exp) such that
| * value =$(I x)*2$(SUPERSCRIPT exp) and
| * .5 $(LT)= |$(I x)| $(LT) 1.0
| *
| * $(I x) has same sign as value.
| *
| * $(TABLE_SV
| * $(TR $(TH value) $(TH returns) $(TH exp))
| * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD 0))
| * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD unchenged))
| * $(TR $(TD -$(INFIN)) $(TD -$(INFIN)) $(TD unchenged))
| * $(TR $(TD $(PLUSMN)$(NAN)) $(TD $(PLUSMN)$(NAN)) $(TD unchenged))
| * )
| */
|T frexp(T)(const T value, ref int exp) @trusted pure nothrow @nogc
|if (isFloatingPoint!T)
|{
| import mir.utility: _expect;
| import mir.math.common: fabs;
|
| if (__ctfe)
| {
| // Handle special cases.
| if (value == 0) { exp = 0; return value; }
| if (value != value || fabs(value) == T.infinity) { return value; }
| // Handle ordinary cases.
| // In CTFE there is no performance advantage for having separate
| // paths for different floating point types.
| T absValue = value < 0 ? -value : value;
| int expCount;
| static if (T.mant_dig > double.mant_dig)
| {
| for (; absValue >= 0x1.0p+1024L; absValue *= 0x1.0p-1024L)
| expCount += 1024;
| for (; absValue < 0x1.0p-1021L; absValue *= 0x1.0p+1021L)
| expCount -= 1021;
| }
| const double dval = cast(double) absValue;
| int dexp = cast(int) (((*cast(const long*) &dval) >>> 52) & 0x7FF) + double.min_exp - 2;
| dexp++;
| expCount += dexp;
| absValue *= 2.0 ^^ -dexp;
| // If the original value was subnormal or if it was a real
| // then absValue can still be outside the [0.5, 1.0) range.
| if (absValue < 0.5)
| {
| assert(T.mant_dig > double.mant_dig || -T.min_normal < value && value < T.min_normal);
| do
| {
| absValue += absValue;
| expCount--;
| } while (absValue < 0.5);
| }
| else
| {
| assert(absValue < 1 || T.mant_dig > double.mant_dig);
| for (; absValue >= 1; absValue *= T(0.5))
| expCount++;
| }
| exp = expCount;
| return value < 0 ? -absValue : absValue;
| }
|
| with(floatTraits!T) static if (
| realFormat == RealFormat.ieeeExtended
| || realFormat == RealFormat.ieeeQuadruple
| || realFormat == RealFormat.ieeeDouble
| || realFormat == RealFormat.ieeeSingle)
| {
| T vf = value;
| S u = (cast(U*)&vf)[idx];
| int e = (u & exp_mask) >>> exp_shft;
| if (_expect(e, true)) // If exponent is non-zero
| {
| if (_expect(e == exp_msh, false))
| goto R;
| exp = e + (T.min_exp - 1);
| P:
| u &= ~exp_mask;
| u ^= exp_nrm;
| (cast(U*)&vf)[idx] = cast(U)u;
| R:
| return vf;
| }
| else
| {
| static if (realFormat == RealFormat.ieeeExtended)
| {
| version (LittleEndian)
| auto mp = cast(ulong*)&vf;
| else
| auto mp = cast(ulong*)((cast(ushort*)&vf) + 1);
| auto m = u & man_mask | *mp;
| }
| else
| {
| auto m = u & man_mask;
| static if (T.sizeof > U.sizeof)
| m |= (cast(U*)&vf)[MANTISSA_LSB];
| }
| if (!m)
| {
| exp = 0;
| goto R;
| }
| vf *= norm_factor;
| u = (cast(U*)&vf)[idx];
| e = (u & exp_mask) >>> exp_shft;
| exp = e + (T.min_exp - T.mant_dig);
| goto P;
| }
| }
| else // static if (realFormat == RealFormat.ibmExtended)
| {
| static assert(0, "frexp not implemented");
| }
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| import mir.math.common: pow, approxEqual;
| alias isNaN = x => x != x;
| int exp;
| real mantissa = frexp(123.456L, exp);
|
| assert(approxEqual(mantissa * pow(2.0L, cast(real) exp), 123.456L));
|
| // special cases, zero
| assert(frexp(-0.0, exp) == -0.0 && exp == 0);
| assert(frexp(0.0, exp) == 0.0 && exp == 0);
|
| // special cases, NaNs and INFs
| exp = 1234; // random number
| assert(isNaN(frexp(-real.nan, exp)) && exp == 1234);
| assert(isNaN(frexp(real.nan, exp)) && exp == 1234);
| assert(frexp(-real.infinity, exp) == -real.infinity && exp == 1234);
| assert(frexp(real.infinity, exp) == real.infinity && exp == 1234);
|}
|
|@safe @nogc nothrow version(mir_core_test) unittest
|{
| import mir.math.common: pow;
| int exp;
| real mantissa = frexp(123.456L, exp);
|
| assert(mantissa * pow(2.0L, cast(real) exp) == 123.456L);
|}
|
|@safe version(mir_core_test) unittest
|{
| import std.meta : AliasSeq;
| import std.typecons : tuple, Tuple;
|
| static foreach (T; AliasSeq!(float, double, real))
| {{
| enum randomNumber = 12345;
| Tuple!(T, T, int)[] vals = // x,frexp,exp
| [
| tuple(T(0.0), T( 0.0 ), 0),
| tuple(T(-0.0), T( -0.0), 0),
| tuple(T(1.0), T( .5 ), 1),
| tuple(T(-1.0), T( -.5 ), 1),
| tuple(T(2.0), T( .5 ), 2),
| tuple(T(float.min_normal/2.0f), T(.5), -126),
| tuple(T.infinity, T.infinity, randomNumber),
| tuple(-T.infinity, -T.infinity, randomNumber),
| tuple(T.nan, T.nan, randomNumber),
| tuple(-T.nan, -T.nan, randomNumber),
|
| // Phobos issue #16026:
| tuple(3 * (T.min_normal * T.epsilon), T( .75), (T.min_exp - T.mant_dig) + 2)
| ];
|
| foreach (i, elem; vals)
| {
| T x = elem[0];
| T e = elem[1];
| int exp = elem[2];
| int eptr = randomNumber;
| T v = frexp(x, eptr);
| assert(e == v || (e != e && v != v));
| assert(exp == eptr);
|
| }
|
| static if (floatTraits!(T).realFormat == RealFormat.ieeeExtended)
| {
| static T[3][] extendedvals = [ // x,frexp,exp
| [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal
| [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063],
| [T.min_normal, .5, -16381],
| [T.min_normal/2.0L, .5, -16382] // subnormal
| ];
| foreach (elem; extendedvals)
| {
| T x = elem[0];
| T e = elem[1];
| int exp = cast(int) elem[2];
| int eptr;
| T v = frexp(x, eptr);
| assert(e == v);
| assert(exp == eptr);
|
| }
| }
| }}
|}
|
|@safe version(mir_core_test) unittest
|{
| import std.meta : AliasSeq;
| void foo() {
| static foreach (T; AliasSeq!(real, double, float))
| {{
| int exp;
| const T a = 1;
| immutable T b = 2;
| auto c = frexp(a, exp);
| auto d = frexp(b, exp);
| }}
| }
|}
|
|/*******************************************
| * Returns: n * 2$(SUPERSCRIPT exp)
| * See_Also: $(LERF frexp)
| */
|T ldexp(T)(const T n, int exp) @nogc @trusted pure nothrow
| if (isFloatingPoint!T)
|{
| import core.math: ldexp;
| return ldexp(n, exp);
|}
|
|///
|@nogc @safe pure nothrow version(mir_core_test) unittest
|{
| import std.meta : AliasSeq;
| static foreach (T; AliasSeq!(float, double, real))
| {{
| T r = ldexp(cast(T) 3.0, cast(int) 3);
| assert(r == 24);
|
| T n = 3.0;
| int exp = 3;
| r = ldexp(n, exp);
| assert(r == 24);
| }}
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.common;
| {
| assert(ldexp(1.0, -1024) == 0x1p-1024);
| assert(ldexp(1.0, -1022) == 0x1p-1022);
| int x;
| double n = frexp(0x1p-1024L, x);
| assert(n == 0.5);
| assert(x==-1023);
| assert(ldexp(n, x)==0x1p-1024);
| }
| static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended ||
| floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
| {
| assert(ldexp(1.0L, -16384) == 0x1p-16384L);
| assert(ldexp(1.0L, -16382) == 0x1p-16382L);
| int x;
| real n = frexp(0x1p-16384L, x);
| assert(n == 0.5L);
| assert(x==-16383);
| assert(ldexp(n, x)==0x1p-16384L);
| }
|}
|
|/* workaround Issue 14718, float parsing depends on platform strtold
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| assert(ldexp(1.0, -1024) == 0x1p-1024);
| assert(ldexp(1.0, -1022) == 0x1p-1022);
| int x;
| double n = frexp(0x1p-1024, x);
| assert(n == 0.5);
| assert(x==-1023);
| assert(ldexp(n, x)==0x1p-1024);
|}
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| assert(ldexp(1.0f, -128) == 0x1p-128f);
| assert(ldexp(1.0f, -126) == 0x1p-126f);
| int x;
| float n = frexp(0x1p-128f, x);
| assert(n == 0.5f);
| assert(x==-127);
| assert(ldexp(n, x)==0x1p-128f);
|}
|*/
|
|@safe @nogc nothrow version(mir_core_test) unittest
|{
| import std.meta: AliasSeq;
| static F[3][] vals(F) = // value,exp,ldexp
| [
| [ 0, 0, 0],
| [ 1, 0, 1],
| [ -1, 0, -1],
| [ 1, 1, 2],
| [ 123, 10, 125952],
| [ F.max, int.max, F.infinity],
| [ F.max, -int.max, 0],
| [ F.min_normal, -int.max, 0],
| ];
| static foreach(F; AliasSeq!(double, real))
| {{
| int i;
|
| for (i = 0; i < vals!F.length; i++)
| {
| F x = vals!F[i][0];
| int exp = cast(int) vals!F[i][1];
| F z = vals!F[i][2];
| F l = ldexp(x, exp);
| assert(feqrel(z, l) >= 23);
| }
| }}
|}
|
|package(mir):
|
|// Constants used for extracting the components of the representation.
|// They supplement the built-in floating point properties.
|template floatTraits(T)
|{
| // EXPMASK is a ushort mask to select the exponent portion (without sign)
| // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort
| // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1).
| // EXPPOS_SHORT is the index of the exponent when represented as a ushort array.
| // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array.
| // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal
| enum norm_factor = 1 / T.epsilon;
| static if (T.mant_dig == 24)
| {
| enum realFormat = RealFormat.ieeeSingle;
| }
| else static if (T.mant_dig == 53)
| {
| static if (T.sizeof == 8)
| {
| enum realFormat = RealFormat.ieeeDouble;
| }
| else
| static assert(false, "No traits support for " ~ T.stringof);
| }
| else static if (T.mant_dig == 64)
| {
| enum realFormat = RealFormat.ieeeExtended;
| }
| else static if (T.mant_dig == 113)
| {
| enum realFormat = RealFormat.ieeeQuadruple;
| }
| else
| static assert(false, "No traits support for " ~ T.stringof);
|
| static if (realFormat == RealFormat.ieeeExtended)
| {
| alias S = int;
| alias U = ushort;
| enum sig_mask = U(1) << (U.sizeof * 8 - 1);
| enum exp_shft = 0;
| enum man_mask = 0;
| version (LittleEndian)
| enum idx = 4;
| else
| enum idx = 0;
| }
| else
| {
| static if (realFormat == RealFormat.ieeeQuadruple || realFormat == RealFormat.ieeeDouble && double.sizeof == size_t.sizeof)
| {
| alias S = long;
| alias U = ulong;
| }
| else
| {
| alias S = int;
| alias U = uint;
| }
| static if (realFormat == RealFormat.ieeeQuadruple)
| alias M = ulong;
| else
| alias M = U;
| enum sig_mask = U(1) << (U.sizeof * 8 - 1);
| enum uint exp_shft = T.mant_dig - 1 - (T.sizeof > U.sizeof ? U.sizeof * 8 : 0);
| enum man_mask = (U(1) << exp_shft) - 1;
| enum idx = T.sizeof > U.sizeof ? MANTISSA_MSB : 0;
| }
| enum exp_mask = (U.max >> (exp_shft + 1)) << exp_shft;
| enum int exp_msh = exp_mask >> exp_shft;
| enum intPartMask = man_mask + 1;
| enum exp_nrm = S(exp_msh - T.max_exp - 1) << exp_shft;
|}
|
|// These apply to all floating-point types
|version (LittleEndian)
|{
| enum MANTISSA_LSB = 0;
| enum MANTISSA_MSB = 1;
|}
|else
|{
| enum MANTISSA_LSB = 1;
| enum MANTISSA_MSB = 0;
|}
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/math/ieee.d has no code
<<<<<< EOF
# path=./source-mir-ndslice-slice.lst
|/++
|This is a submodule of $(MREF mir, ndslice).
|
|Safety_note:
| User-defined iterators should care about their safety except bounds checks.
| Bounds are checked in ndslice code.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|$(BOOKTABLE $(H2 Definitions),
|$(TR $(TH Name) $(TH Description))
|$(T2 Slice, N-dimensional slice.)
|$(T2 SliceKind, SliceKind of $(LREF Slice) enumeration.)
|$(T2 Universal, Alias for $(LREF .SliceKind.universal).)
|$(T2 Canonical, Alias for $(LREF .SliceKind.canonical).)
|$(T2 Contiguous, Alias for $(LREF .SliceKind.contiguous).)
|$(T2 sliced, Creates a slice on top of an iterator, a pointer, or an array's pointer.)
|$(T2 slicedField, Creates a slice on top of a field, a random access range, or an array.)
|$(T2 slicedNdField, Creates a slice on top of an ndField.)
|$(T2 kindOf, Extracts $(LREF SliceKind).)
|$(T2 isSlice, Checks if the type is `Slice` instance.)
|$(T2 Structure, A tuple of lengths and strides.)
|)
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
|STD = $(TD $(SMALL $0))
|+/
|module mir.ndslice.slice;
|
|import mir.internal.utility : Iota;
|import mir.math.common : optmath;
|import mir.ndslice.concatenation;
|import mir.ndslice.field;
|import mir.ndslice.internal;
|import mir.ndslice.iterator;
|import mir.ndslice.traits: isIterator;
|import mir.primitives;
|import mir.qualifier;
|import mir.utility;
|import std.meta;
|import std.traits;
|
|public import mir.primitives: DeepElementType;
|
|/++
|Checks if type T has asSlice property and its returns a slices.
|Aliases itself to a dimension count
|+/
|template hasAsSlice(T)
|{
| static if (__traits(hasMember, T, "asSlice"))
| enum size_t hasAsSlice = typeof(T.init.asSlice).N;
| else
| enum size_t hasAsSlice = 0;
|}
|
|///
|version(mir_test) unittest
|{
| import mir.series;
| static assert(!hasAsSlice!(int[]));
| static assert(hasAsSlice!(SeriesMap!(int, string)) == 1);
|}
|
|/++
|Check if $(LREF toConst) function can be called with type T.
|+/
|enum isConvertibleToSlice(T) = isSlice!T || isDynamicArray!T || hasAsSlice!T;
|
|///
|version(mir_test) unittest
|{
| import mir.series: SeriesMap;
| static assert(isConvertibleToSlice!(immutable int[]));
| static assert(isConvertibleToSlice!(string[]));
| static assert(isConvertibleToSlice!(SeriesMap!(string, int)));
| static assert(isConvertibleToSlice!(Slice!(int*)));
|}
|
|/++
|Reurns:
| Ndslice view in the same data.
|See_also: $(LREF isConvertibleToSlice).
|+/
|auto toSlice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) val)
|{
| import core.lifetime: move;
| return val.move;
|}
|
|/// ditto
|auto toSlice(Iterator, size_t N, SliceKind kind)(const Slice!(Iterator, N, kind) val)
|{
| return val[];
|}
|
|/// ditto
|auto toSlice(Iterator, size_t N, SliceKind kind)(immutable Slice!(Iterator, N, kind) val)
|{
| return val[];
|}
|
|/// ditto
|auto toSlice(T)(T[] val)
|{
6| return val.sliced;
|}
|
|/// ditto
|auto toSlice(T)(T val)
| if (hasAsSlice!T || __traits(hasMember, T, "moveToSlice"))
|{
| static if (__traits(hasMember, T, "moveToSlice"))
| return val.moveToSlice;
| else
| return val.asSlice;
|}
|
|/// ditto
|auto toSlice(T)(ref T val)
| if (hasAsSlice!T)
|{
| return val.asSlice;
|}
|
|///
|template toSlices(args...)
|{
| static if (args.length)
| {
| alias arg = args[0];
| alias Arg = typeof(arg);
| static if (isMutable!Arg && isSlice!Arg)
| alias slc = arg;
| else
| @optmath @property auto ref slc()()
| {
1| return toSlice(arg);
| }
| alias toSlices = AliasSeq!(slc, toSlices!(args[1..$]));
| }
| else
| alias toSlices = AliasSeq!();
|}
|
|/++
|Checks if the type is `Slice` instance.
|+/
|enum isSlice(T) = is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind);
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias A = uint[];
| alias S = Slice!(int*);
|
| static assert(isSlice!S);
| static assert(!isSlice!A);
|}
|
|/++
|SliceKind of $(LREF Slice).
|See_also:
| $(SUBREF topology, universal),
| $(SUBREF topology, canonical),
| $(SUBREF topology, assumeCanonical),
| $(SUBREF topology, assumeContiguous).
|+/
|enum mir_slice_kind
|{
| /// A slice has strides for all dimensions.
| universal,
| /// A slice has >=2 dimensions and row dimension is contiguous.
| canonical,
| /// A slice is a flat contiguous data without strides.
| contiguous,
|}
|/// ditto
|alias SliceKind = mir_slice_kind;
|
|/++
|Alias for $(LREF .SliceKind.universal).
|
|See_also:
| Internal Binary Representation section in $(LREF Slice).
|+/
|alias Universal = SliceKind.universal;
|/++
|Alias for $(LREF .SliceKind.canonical).
|
|See_also:
| Internal Binary Representation section in $(LREF Slice).
|+/
|alias Canonical = SliceKind.canonical;
|/++
|Alias for $(LREF .SliceKind.contiguous).
|
|See_also:
| Internal Binary Representation section in $(LREF Slice).
|+/
|alias Contiguous = SliceKind.contiguous;
|
|/// Extracts $(LREF SliceKind).
|enum kindOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind) = kind;
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| static assert(kindOf!(Slice!(int*, 1, Universal)) == Universal);
|}
|
|/// Extracts iterator type from a $(LREF Slice).
|alias IteratorOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind) = Iterator;
|
|private template SkipDimension(size_t dimension, size_t index)
|{
| static if (index < dimension)
| enum SkipDimension = index;
| else
| static if (index == dimension)
| static assert (0, "SkipInex: wrong index");
| else
| enum SkipDimension = index - 1;
|}
|
|/++
|Creates an n-dimensional slice-shell over an iterator.
|Params:
| iterator = An iterator, a pointer, or an array.
| lengths = A list of lengths for each dimension
|Returns:
| n-dimensional slice
|+/
|auto sliced(size_t N, Iterator)(Iterator iterator, size_t[N] lengths...)
| if (!__traits(isStaticArray, Iterator) && N
| && !is(Iterator : Slice!(_Iterator, _N, kind), _Iterator, size_t _N, SliceKind kind))
|{
| alias C = ImplicitlyUnqual!(typeof(iterator));
4243| size_t[N] _lengths;
| foreach (i; Iota!N)
7962| _lengths[i] = lengths[i];
4243| ptrdiff_t[1] _strides = 0;
| static if (isDynamicArray!Iterator)
| {
553| assert(lengthsProduct(_lengths) <= iterator.length,
| "array length should be greater or equal to the product of constructed ndslice lengths");
1106| auto ptr = iterator.length ? &iterator[0] : null;
553| return Slice!(typeof(C.init[0])*, N)(_lengths, ptr);
| }
| else
| {
| // break safety
3690| if (false)
| {
0000000| ++iterator;
0000000| --iterator;
0000000| iterator += 34;
0000000| iterator -= 34;
| }
| import core.lifetime: move;
3690| return Slice!(C, N)(_lengths, iterator.move);
| }
|}
|
|/// Random access range primitives for slices over user defined types
|@safe pure nothrow @nogc version(mir_test) unittest
|{
| struct MyIota
| {
| //`[index]` operator overloading
| auto opIndex(size_t index) @safe nothrow
| {
2| return index;
| }
|
0000000| auto lightConst()() const @property { return MyIota(); }
| auto lightImmutable()() immutable @property { return MyIota(); }
| }
|
| import mir.ndslice.iterator: FieldIterator;
| alias Iterator = FieldIterator!MyIota;
| alias S = Slice!(Iterator, 2);
| import std.range.primitives;
| static assert(hasLength!S);
| static assert(hasSlicing!S);
| static assert(isRandomAccessRange!S);
|
1| auto slice = Iterator().sliced(20, 10);
1| assert(slice[1, 2] == 12);
1| auto sCopy = slice.save;
1| assert(slice[1, 2] == 12);
|}
|
|/++
|Creates an 1-dimensional slice-shell over an array.
|Params:
| array = An array.
|Returns:
| 1-dimensional slice
|+/
|Slice!(T*) sliced(T)(T[] array) @trusted
|{
| version(LDC) pragma(inline, true);
56655| return Slice!(T*)([array.length], array.ptr);
|}
|
|/// Creates a slice from an array.
|@safe pure nothrow version(mir_test) unittest
|{
1| auto slice = new int[10].sliced;
1| assert(slice.length == 10);
| static assert(is(typeof(slice) == Slice!(int*)));
|}
|
|/++
|Creates an n-dimensional slice-shell over the 1-dimensional input slice.
|Params:
| slice = slice
| lengths = A list of lengths for each dimension.
|Returns:
| n-dimensional slice
|+/
|Slice!(Iterator, N, kind)
| sliced
| (Iterator, size_t N, SliceKind kind)
| (Slice!(Iterator, 1, kind) slice, size_t[N] lengths...)
| if (N)
|{
22| auto structure = typeof(return)._Structure.init;
22| structure[0] = lengths;
| static if (kind != Contiguous)
| {
| import mir.ndslice.topology: iota;
| structure[1] = structure[0].iota.strides;
| }
| import core.lifetime: move;
22| return typeof(return)(structure, slice._iterator.move);
|}
|
|///
|@safe pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
1| auto data = new int[24];
99| foreach (i, ref e; data)
24| e = cast(int)i;
1| auto a = data[0..10].sliced(10)[0..6].sliced(2, 3);
1| auto b = iota!int(10)[0..6].sliced(2, 3);
1| assert(a == b);
1| a[] += b;
27| foreach (i, e; data[0..6])
6| assert(e == 2*i);
75| foreach (i, e; data[6..$])
18| assert(e == i+6);
|}
|
|/++
|Creates an n-dimensional slice-shell over a field.
|Params:
| field = A field. The length of the
| array should be equal to or less then the product of
| lengths.
| lengths = A list of lengths for each dimension.
|Returns:
| n-dimensional slice
|+/
|Slice!(FieldIterator!Field, N)
|slicedField(Field, size_t N)(Field field, size_t[N] lengths...)
| if (N)
|{
| static if (hasLength!Field)
59| assert(lengths.lengthsProduct <= field.length, "Length product should be less or equal to the field length.");
65| return FieldIterator!Field(0, field).sliced(lengths);
|}
|
|///ditto
|auto slicedField(Field)(Field field)
| if(hasLength!Field)
|{
18| return .slicedField(field, field.length);
|}
|
|/// Creates an 1-dimensional slice over a field, array, or random access range.
|@safe @nogc pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
1| auto slice = 10.iota.slicedField;
1| assert(slice.length == 10);
|}
|
|/++
|Creates an n-dimensional slice-shell over an ndField.
|Params:
| field = A ndField. Lengths should fit into field's shape.
| lengths = A list of lengths for each dimension.
|Returns:
| n-dimensional slice
|See_also: $(SUBREF concatenation, concatenation) examples.
|+/
|Slice!(IndexIterator!(FieldIterator!(ndIotaField!N), ndField), N)
|slicedNdField(ndField, size_t N)(ndField field, size_t[N] lengths...)
| if (N)
|{
| static if(hasShape!ndField)
| {
20| auto shape = field.shape;
192| foreach (i; 0 .. N)
44| assert(lengths[i] <= shape[i], "Lengths should fit into ndfield's shape.");
| }
| import mir.ndslice.topology: indexed, ndiota;
20| return indexed(field, ndiota(lengths));
|}
|
|///ditto
|auto slicedNdField(ndField)(ndField field)
| if(hasShape!ndField)
|{
20| return .slicedNdField(field, field.shape);
|}
|
|/++
|Combination of coordinate(s) and value.
|+/
|struct CoordinateValue(T, size_t N = 1)
|{
| ///
| size_t[N] index;
|
| ///
| T value;
|
| ///
| int opCmp()(scope auto ref const typeof(this) rht) const
| {
| return cmpCoo(this.index, rht.index);
| }
|}
|
|private int cmpCoo(size_t N)(scope const auto ref size_t[N] a, scope const auto ref size_t[N] b)
|{
| foreach (i; Iota!(0, N))
| if (a[i] != b[i])
| return a[i] > b[i] ? 1 : -1;
| return 0;
|}
|
|/++
|Presents $(LREF .Slice.structure).
|+/
|struct Structure(size_t N)
|{
| ///
| size_t[N] lengths;
| ///
| sizediff_t[N] strides;
|}
|
|package(mir) alias LightConstOfLightScopeOf(Iterator) = LightConstOf!(LightScopeOf!Iterator);
|package(mir) alias LightImmutableOfLightConstOf(Iterator) = LightImmutableOf!(LightScopeOf!Iterator);
|package(mir) alias ImmutableOfUnqualOfPointerTarget(Iterator) = immutable(Unqual!(PointerTarget!Iterator))*;
|package(mir) alias ConstOfUnqualOfPointerTarget(Iterator) = const(Unqual!(PointerTarget!Iterator))*;
|
|package(mir) template allLightScope(args...)
|{
| static if (args.length)
| {
| alias arg = args[0];
| alias Arg = typeof(arg);
| static if(!isDynamicArray!Arg)
| {
| static if(!is(LightScopeOf!Arg == Arg))
| @optmath @property ls()()
| {
| import mir.qualifier: lightScope;
| return lightScope(arg);
| }
| else alias ls = arg;
| }
| else alias ls = arg;
| alias allLightScope = AliasSeq!(ls, allLightScope!(args[1..$]));
| }
| else
| alias allLightScope = AliasSeq!();
|}
|
|/++
|Presents an n-dimensional view over a range.
|
|$(H3 Definitions)
|
|In order to change data in a slice using
|overloaded operators such as `=`, `+=`, `++`,
|a syntactic structure of type
|`[]` must be used.
|It is worth noting that just like for regular arrays, operations `a = b`
|and `a[] = b` have different meanings.
|In the first case, after the operation is carried out, `a` simply points at the same data as `b`
|does, and the data which `a` previously pointed at remains unmodified.
|Here, `а` and `b` must be of the same type.
|In the second case, `a` points at the same data as before,
|but the data itself will be changed. In this instance, the number of dimensions of `b`
|may be less than the number of dimensions of `а`; and `b` can be a Slice,
|a regular multidimensional array, or simply a value (e.g. a number).
|
|In the following table you will find the definitions you might come across
|in comments on operator overloading.
|
|$(BOOKTABLE
|$(TR $(TH Operator Overloading) $(TH Examples at `N == 3`))
|$(TR $(TD An $(B interval) is a part of a sequence of type `i .. j`.)
| $(STD `2..$-3`, `0..4`))
|$(TR $(TD An $(B index) is a part of a sequence of type `i`.)
| $(STD `3`, `$-1`))
|$(TR $(TD A $(B partially defined slice) is a sequence composed of
| $(B intervals) and $(B indices) with an overall length strictly less than `N`.)
| $(STD `[3]`, `[0..$]`, `[3, 3]`, `[0..$,0..3]`, `[0..$,2]`))
|$(TR $(TD A $(B fully defined index) is a sequence
| composed only of $(B indices) with an overall length equal to `N`.)
| $(STD `[2,3,1]`))
|$(TR $(TD A $(B fully defined slice) is an empty sequence
| or a sequence composed of $(B indices) and at least one
| $(B interval) with an overall length equal to `N`.)
| $(STD `[]`, `[3..$,0..3,0..$-1]`, `[2,0..$,1]`))
|$(TR $(TD An $(B indexed slice) is syntax sugar for $(SUBREF topology, indexed) and $(SUBREF topology, cartesian).)
| $(STD `[anNdslice]`, `[$.iota, anNdsliceForCartesian1, $.iota]`))
|)
|
|See_also:
| $(SUBREF topology, iota).
|
|$(H3 Internal Binary Representation)
|
|Multidimensional Slice is a structure that consists of lengths, strides, and a iterator (pointer).
|
|$(SUBREF topology, FieldIterator) shell is used to wrap fields and random access ranges.
|FieldIterator contains a shift of the current initial element of a multidimensional slice
|and the field itself.
|
|With the exception of $(MREF mir,ndslice,allocation) module, no functions in this
|package move or copy data. The operations are only carried out on lengths, strides,
|and pointers. If a slice is defined over a range, only the shift of the initial element
|changes instead of the range.
|
|Mir n-dimensional Slices can be one of the three kinds.
|
|$(H4 Contiguous slice)
|
|Contiguous in memory (or in a user-defined iterator's field) row-major tensor that doesn't store strides because they can be computed on the fly using lengths.
|The row stride is always equaled 1.
|
|$(H4 Canonical slice)
|
|Canonical slice as contiguous in memory (or in a user-defined iterator's field) rows of a row-major tensor, it doesn't store the stride for row dimension because it is always equaled 1.
|BLAS/LAPACK matrices are Canonical but originally have column-major order.
|In the same time you can use 2D Canonical Slices with LAPACK assuming that rows are columns and columns are rows.
|
|$(H4 Universal slice)
|
|A row-major tensor that stores the strides for all dimensions.
|NumPy strides are Universal.
|
|$(H4 Internal Representation for Universal Slices)
|
|Type definition
|
|-------
|Slice!(Iterator, N, Universal)
|-------
|
|Schema
|
|-------
|Slice!(Iterator, N, Universal)
| size_t[N] _lengths
| sizediff_t[N] _strides
| Iterator _iterator
|-------
|
|$(H5 Example)
|
|Definitions
|
|-------
|import mir.ndslice;
|auto a = new double[24];
|Slice!(double*, 3, Universal) s = a.sliced(2, 3, 4).universal;
|Slice!(double*, 3, Universal) t = s.transposed!(1, 2, 0);
|Slice!(double*, 3, Universal) r = t.reversed!1;
|-------
|
|Representation
|
|-------
|s________________________
| lengths[0] ::= 2
| lengths[1] ::= 3
| lengths[2] ::= 4
|
| strides[0] ::= 12
| strides[1] ::= 4
| strides[2] ::= 1
|
| iterator ::= &a[0]
|
|t____transposed!(1, 2, 0)
| lengths[0] ::= 3
| lengths[1] ::= 4
| lengths[2] ::= 2
|
| strides[0] ::= 4
| strides[1] ::= 1
| strides[2] ::= 12
|
| iterator ::= &a[0]
|
|r______________reversed!1
| lengths[0] ::= 2
| lengths[1] ::= 3
| lengths[2] ::= 4
|
| strides[0] ::= 12
| strides[1] ::= -4
| strides[2] ::= 1
|
| iterator ::= &a[8] // (old_strides[1] * (lengths[1] - 1)) = 8
|-------
|
|$(H4 Internal Representation for Canonical Slices)
|
|Type definition
|
|-------
|Slice!(Iterator, N, Canonical)
|-------
|
|Schema
|
|-------
|Slice!(Iterator, N, Canonical)
| size_t[N] _lengths
| sizediff_t[N-1] _strides
| Iterator _iterator
|-------
|
|$(H4 Internal Representation for Contiguous Slices)
|
|Type definition
|
|-------
|Slice!(Iterator, N)
|-------
|
|Schema
|
|-------
|Slice!(Iterator, N, Contiguous)
| size_t[N] _lengths
| sizediff_t[0] _strides
| Iterator _iterator
|-------
|+/
1107|struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_...)
| if (0 < N_ && N_ < 255 && !(kind_ == Canonical && N_ == 1) && Labels_.length <= N_ && isIterator!Iterator_)
|{
|@optmath:
|
| /// $(LREF SliceKind)
| enum SliceKind kind = kind_;
|
| /// Dimensions count
| enum size_t N = N_;
|
| /// Strides count
| enum size_t S = kind == Universal ? N : kind == Canonical ? N - 1 : 0;
|
| /// Labels count.
| enum size_t L = Labels_.length;
|
| /// Data iterator type
| alias Iterator = Iterator_;
|
| /// This type
| alias This = Slice!(Iterator, N, kind);
|
| /// Data element type
| alias DeepElement = typeof(Iterator.init[size_t.init]);
|
| ///
| alias serdeKeysProxy = DeepElement;
|
| /// Label Iterators types
| alias Labels = Labels_;
|
| ///
| template Element(size_t dimension)
| if (dimension < N)
| {
| static if (N == 1)
| alias Element = DeepElement;
| else
| {
| static if (kind == Universal || dimension == N - 1)
| alias Element = mir_slice!(Iterator, N - 1, Universal);
| else
| static if (N == 2 || kind == Contiguous && dimension == 0)
| alias Element = mir_slice!(Iterator, N - 1);
| else
| alias Element = mir_slice!(Iterator, N - 1, Canonical);
| }
| }
|
|package(mir):
|
| enum doUnittest = is(Iterator == int*) && (N == 1 || N == 2) && kind == Contiguous;
|
| enum hasAccessByRef = __traits(compiles, &_iterator[0]);
|
| enum PureIndexLength(Slices...) = Filter!(isIndex, Slices).length;
|
| enum isPureSlice(Slices...) =
| Slices.length == 0
| || Slices.length <= N
| && PureIndexLength!Slices < N
| && Filter!(isIndex, Slices).length < Slices.length
| && allSatisfy!(templateOr!(isIndex, is_Slice), Slices);
|
|
| enum isFullPureSlice(Slices...) =
| Slices.length == 0
| || Slices.length == N
| && PureIndexLength!Slices < N
| && allSatisfy!(templateOr!(isIndex, is_Slice), Slices);
|
| enum isIndexedSlice(Slices...) =
| Slices.length
| && Slices.length <= N
| && allSatisfy!(isSlice, Slices)
| && anySatisfy!(templateNot!is_Slice, Slices);
|
| static if (S)
| {
| ///
| public alias _Structure = AliasSeq!(size_t[N], ptrdiff_t[S]);
| ///
| public _Structure _structure;
| ///
| public alias _lengths = _structure[0];
| ///
| public alias _strides = _structure[1];
| }
| else
| {
| ///
| public alias _Structure = AliasSeq!(size_t[N]);
| ///
| public _Structure _structure;
| ///
| public alias _lengths = _structure[0];
| ///
| public enum ptrdiff_t[S] _strides = ptrdiff_t[S].init;
| }
|
| /// Data Iterator
| public Iterator _iterator;
| /// Labels iterators
| public Labels _labels;
|
| sizediff_t backIndex(size_t dimension = 0)() @safe @property scope const
| if (dimension < N)
| {
7597| return _stride!dimension * (_lengths[dimension] - 1);
| }
|
| size_t indexStride(size_t I)(size_t[I] _indices) @safe scope const
| {
| static if (_indices.length)
| {
| static if (kind == Contiguous)
| {
| enum E = I - 1;
78455| assert(_indices[E] < _lengths[E], indexError!(E, N));
78455| ptrdiff_t ball = this._stride!E;
78455| ptrdiff_t stride = _indices[E] * ball;
| foreach_reverse (i; Iota!E) //static
| {
691| ball *= _lengths[i + 1];
691| assert(_indices[i] < _lengths[i], indexError!(i, N));
691| stride += ball * _indices[i];
| }
| }
| else
| static if (kind == Canonical)
| {
| enum E = I - 1;
10| assert(_indices[E] < _lengths[E], indexError!(E, N));
| static if (I == N)
7| size_t stride = _indices[E];
| else
3| size_t stride = _strides[E] * _indices[E];
| foreach_reverse (i; Iota!E) //static
| {
9| assert(_indices[i] < _lengths[i], indexError!(i, N));
9| stride += _strides[i] * _indices[i];
| }
| }
| else
| {
| enum E = I - 1;
3522| assert(_indices[E] < _lengths[E], indexError!(E, N));
3522| size_t stride = _strides[E] * _indices[E];
| foreach_reverse (i; Iota!E) //static
| {
26| assert(_indices[i] < _lengths[i], indexError!(i, N));
26| stride += _strides[i] * _indices[i];
| }
| }
81987| return stride;
| }
| else
| {
| return 0;
| }
| }
|
|public:
|
| // static if (S == 0)
| // {
| /// Defined for Contiguous Slice only
| // this()(size_t[N] lengths, in ptrdiff_t[] empty, Iterator iterator, Labels labels)
| // {
| // version(LDC) pragma(inline, true);
| // assert(empty.length == 0);
| // this._lengths = lengths;
| // this._iterator = iterator;
| // }
|
| // /// ditto
| // this()(size_t[N] lengths, Iterator iterator, Labels labels)
| // {
| // version(LDC) pragma(inline, true);
| // this._lengths = lengths;
| // this._iterator = iterator;
| // }
|
| // /// ditto
| // this()(size_t[N] lengths, in ptrdiff_t[] empty, Iterator iterator, Labels labels)
| // {
| // version(LDC) pragma(inline, true);
| // assert(empty.length == 0);
| // this._lengths = lengths;
| // this._iterator = iterator;
| // }
|
| // /// ditto
| // this()(size_t[N] lengths, Iterator iterator, Labels labels)
| // {
| // version(LDC) pragma(inline, true);
| // this._lengths = lengths;
| // this._iterator = iterator;
| // }
| // }
|
| // version(LDC)
| // private enum classicConstructor = true;
| // else
| // private enum classicConstructor = S > 0;
|
| // static if (classicConstructor)
| // {
| /// Defined for Canonical and Universal Slices (DMD, GDC, LDC) and for Contiguous Slices (LDC)
| // this()(size_t[N] lengths, ptrdiff_t[S] strides, Iterator iterator, Labels labels)
| // {
| // version(LDC) pragma(inline, true);
| // this._lengths = lengths;
| // this._strides = strides;
| // this._iterator = iterator;
| // this._labels = labels;
| // }
|
| // /// ditto
| // this()(size_t[N] lengths, ptrdiff_t[S] strides, ref Iterator iterator, Labels labels)
| // {
| // version(LDC) pragma(inline, true);
| // this._lengths = lengths;
| // this._strides = strides;
| // this._iterator = iterator;
| // this._labels = labels;
| // }
| // }
|
| // /// Construct from null
| // this()(typeof(null))
| // {
| // version(LDC) pragma(inline, true);
| // }
|
| // static if (doUnittest)
| // ///
| // @safe pure version(mir_test) unittest
| // {
| // import mir.ndslice.slice;
| // alias Array = Slice!(double*);
| // Array a = null;
| // auto b = Array(null);
| // assert(a.empty);
| // assert(b.empty);
|
| // auto fun(Array a = null)
| // {
|
| // }
| // }
|
| static if (doUnittest)
| /// Creates a 2-dimentional slice with custom strides.
| nothrow pure
| version(mir_test) unittest
| {
2| uint[8] array = [1, 2, 3, 4, 5, 6, 7, 8];
2| auto slice = Slice!(uint*, 2, Universal)([2, 2], [4, 1], array.ptr);
|
2| assert(&slice[0, 0] == &array[0]);
2| assert(&slice[0, 1] == &array[1]);
2| assert(&slice[1, 0] == &array[4]);
2| assert(&slice[1, 1] == &array[5]);
2| assert(slice == [[1, 2], [5, 6]]);
|
2| array[2] = 42;
2| assert(slice == [[1, 2], [5, 6]]);
|
2| array[1] = 99;
2| assert(slice == [[1, 99], [5, 6]]);
| }
|
| /++
| Returns: View with stripped out reference counted context.
| The lifetime of the result mustn't be longer then the lifetime of the original slice.
| +/
| auto lightScope()() scope return @property
| {
2070| auto ret = Slice!(LightScopeOf!Iterator, N, kind, staticMap!(LightScopeOf, Labels))
| (_structure, .lightScope(_iterator));
| foreach(i; Iota!L)
4| ret._labels[i] = .lightScope(_labels[i]);
2070| return ret;
| }
|
| /// ditto
| auto lightScope()() scope const return @property
| {
1119| auto ret = Slice!(LightConstOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightConstOfLightScopeOf, Labels))
| (_structure, .lightScope(_iterator));
| foreach(i; Iota!L)
| ret._labels[i] = .lightScope(_labels[i]);
1119| return ret;
| }
|
| /// ditto
| auto lightScope()() scope immutable return @property
| {
| auto ret = Slice!(LightImmutableOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightImmutableOfLightConstOf(Labels)))
| (_structure, .lightScope(_iterator));
| foreach(i; Iota!L)
| ret._labels[i] = .lightScope(_labels[i]);
| return ret;
| }
|
| /// Returns: Mutable slice over immutable data.
| Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightImmutable()() scope return immutable @property
| {
9| auto ret = typeof(return)(_structure, .lightImmutable(_iterator));
| foreach(i; Iota!L)
2| ret._labels[i] = .lightImmutable(_labels[i]);
9| return ret;
| }
|
| /// Returns: Mutable slice over const data.
| Slice!(LightConstOf!Iterator, N, kind, staticMap!(LightConstOf, Labels)) lightConst()() scope return const @property @trusted
| {
2149| auto ret = typeof(return)(_structure, .lightConst(_iterator));
| foreach(i; Iota!L)
2| ret._labels[i] = .lightConst(_labels[i]);
2149| return ret;
| }
|
| /// ditto
| Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightConst()() scope return immutable @property
| {
| return this.lightImmutable;
| }
|
| /// Label for the dimensions 'd'. By default returns the row label.
| Slice!(Labels[d])
| label(size_t d = 0)() @property
| if (d <= L)
| {
35| return typeof(return)(_lengths[d], _labels[d]);
| }
|
| /// ditto
| void label(size_t d = 0)(Slice!(Labels[d]) rhs) @property
| if (d <= L)
| {
| import core.lifetime: move;
1| assert(rhs.length == _lengths[d], "ndslice: labels dimension mismatch");
1| _labels[d] = rhs._iterator.move;
| }
|
| /// ditto
| Slice!(LightConstOf!(Labels[d]))
| label(size_t d = 0)() @property const
| if (d <= L)
| {
| return typeof(return)(_lengths[d].lightConst, _labels[d]);
| }
|
| /// ditto
| Slice!(LightImmutableOf!(Labels[d]))
| label(size_t d = 0)() @property immutable
| if (d <= L)
| {
| return typeof(return)(_lengths[d].lightImmutable, _labels[d]);
| }
|
| /// Strips label off the DataFrame
| auto values()() @property
| {
2| return Slice!(Iterator, N, kind)(_structure, _iterator);
| }
|
| /// ditto
| auto values()() @property const
| {
| return Slice!(LightConstOf!Iterator, N, kind)(_structure, .lightConst(_iterator));
| }
|
| /// ditto
| auto values()() @property immutable
| {
1| return Slice!(LightImmutableOf!Iterator, N, kind)(_structure, .lightImmutable(_iterator));
| }
|
| /// `opIndex` overload for const slice
| auto ref opIndex(Indexes...)(Indexes indices) const @trusted
| if (isPureSlice!Indexes || isIndexedSlice!Indexes)
| {
206| return lightConst.opIndex(indices);
| }
| /// `opIndex` overload for immutable slice
| auto ref opIndex(Indexes...)(Indexes indices) immutable @trusted
| if (isPureSlice!Indexes || isIndexedSlice!Indexes)
| {
6| return lightImmutable.opIndex(indices);
| }
|
| static if (allSatisfy!(isPointer, Iterator, Labels))
| {
| private alias ConstThis = Slice!(const(Unqual!(PointerTarget!Iterator))*, N, kind);
| private alias ImmutableThis = Slice!(immutable(Unqual!(PointerTarget!Iterator))*, N, kind);
|
| /++
| Cast to const and immutable slices in case of underlying range is a pointer.
| +/
| auto toImmutable()() scope return immutable @trusted pure nothrow @nogc
| {
1| return Slice!(ImmutableOfUnqualOfPointerTarget!Iterator, N, kind, staticMap!(ImmutableOfUnqualOfPointerTarget, Labels))
| (_structure, _iterator, _labels);
| }
|
| /// ditto
| auto toConst()() scope return const @trusted pure nothrow @nogc
| {
| version(LDC) pragma(inline, true);
441| return Slice!(ConstOfUnqualOfPointerTarget!Iterator, N, kind, staticMap!(ConstOfUnqualOfPointerTarget, Labels))
| (_structure, _iterator, _labels);
| }
|
| static if (!is(Slice!(const(Unqual!(PointerTarget!Iterator))*, N, kind) == This))
| /// ditto
| alias toConst this;
|
| static if (doUnittest)
| ///
| version(mir_test) unittest
| {
| static struct Foo
| {
| Slice!(int*) bar;
|
| int get(size_t i) immutable
| {
0000000| return bar[i];
| }
|
| int get(size_t i) const
| {
0000000| return bar[i];
| }
|
| int get(size_t i) inout
| {
0000000| return bar[i];
| }
| }
| }
|
| static if (doUnittest)
| ///
| version(mir_test) unittest
| {
2| Slice!(double*, 2, Universal) nn;
2| Slice!(immutable(double)*, 2, Universal) ni;
2| Slice!(const(double)*, 2, Universal) nc;
|
2| const Slice!(double*, 2, Universal) cn;
2| const Slice!(immutable(double)*, 2, Universal) ci;
2| const Slice!(const(double)*, 2, Universal) cc;
|
2| immutable Slice!(double*, 2, Universal) in_;
2| immutable Slice!(immutable(double)*, 2, Universal) ii;
2| immutable Slice!(const(double)*, 2, Universal) ic;
|
6| nc = nc; nc = cn; nc = in_;
6| nc = nc; nc = cc; nc = ic;
6| nc = ni; nc = ci; nc = ii;
|
| void fun(T, size_t N)(Slice!(const(T)*, N, Universal) sl)
| {
| //...
| }
|
6| fun(nn); fun(cn); fun(in_);
6| fun(nc); fun(cc); fun(ic);
6| fun(ni); fun(ci); fun(ii);
|
| static assert(is(typeof(cn[]) == typeof(nc)));
| static assert(is(typeof(ci[]) == typeof(ni)));
| static assert(is(typeof(cc[]) == typeof(nc)));
|
| static assert(is(typeof(in_[]) == typeof(ni)));
| static assert(is(typeof(ii[]) == typeof(ni)));
| static assert(is(typeof(ic[]) == typeof(ni)));
|
2| ni = ci[];
2| ni = in_[];
2| ni = ii[];
2| ni = ic[];
| }
| }
|
| /++
| Iterator
| Returns:
| Iterator (pointer) to the $(LREF .Slice.first) element.
| +/
| auto iterator()() inout scope return @property
| {
20| return _iterator;
| }
|
| static if (kind == Contiguous && isPointer!Iterator)
| /++
| `ptr` alias is available only if the slice kind is $(LREF Contiguous) contiguous and the $(LREF .Slice.iterator) is a pointers.
| +/
| alias ptr = iterator;
| else
| {
| import mir.rc.array: mir_rci;
| static if (kind == Contiguous && is(Iterator : mir_rci!ET, ET))
| auto ptr() scope return inout @property
| {
385| return _iterator._iterator;
| }
| }
|
| /++
| Field (array) data.
| Returns:
| Raw data slice.
| Constraints:
| Field is defined only for contiguous slices.
| +/
| auto field()() scope return @trusted @property
| {
| static assert(kind == Contiguous, "Slice.field is defined only for contiguous slices. Slice kind is " ~ kind.stringof);
| static if (is(typeof(_iterator[size_t(0) .. elementCount])))
| {
893| return _iterator[size_t(0) .. elementCount];
| }
| else
| {
| import mir.ndslice.topology: flattened;
| return this.flattened;
| }
| }
|
| /// ditto
| auto field()() scope const return @trusted @property
| {
2| return this.lightConst.field;
| }
|
| /// ditto
| auto field()() scope immutable return @trusted @property
| {
2| return this.lightImmutable.field;
| }
|
| static if (doUnittest)
| ///
| @safe version(mir_test) unittest
| {
2| auto arr = [1, 2, 3, 4];
2| auto sl0 = arr.sliced;
2| auto sl1 = arr.slicedField;
|
2| assert(sl0.field is arr);
2| assert(sl1.field is arr);
|
2| arr = arr[1 .. $];
2| sl0 = sl0[1 .. $];
2| sl1 = sl1[1 .. $];
|
2| assert(sl0.field is arr);
2| assert(sl1.field is arr);
2| assert((cast(const)sl1).field is arr);
4| ()@trusted{ assert((cast(immutable)sl1).field is arr); }();
| }
|
| /++
| Returns: static array of lengths
| See_also: $(LREF .Slice.structure)
| +/
| size_t[N] shape()() @trusted @property scope const
| {
3322| return _lengths[0 .. N];
| }
|
| static if (doUnittest)
| /// Regular slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| assert(iota(3, 4, 5).shape == cast(size_t[3])[3, 4, 5]);
| }
|
| static if (doUnittest)
| /// Packed slice
| @safe @nogc pure nothrow
| version(mir_test) unittest
| {
| import mir.ndslice.topology : pack, iota;
2| size_t[3] s = [3, 4, 5];
2| assert(iota(3, 4, 5, 6, 7).pack!2.shape == s);
| }
|
| /++
| Returns: static array of lengths
| See_also: $(LREF .Slice.structure)
| +/
| ptrdiff_t[N] strides()() @trusted @property scope const
| {
| static if (N <= S)
439| return _strides[0 .. N];
| else
| {
848| typeof(return) ret;
| static if (kind == Canonical)
| {
| foreach (i; Iota!S)
123| ret[i] = _strides[i];
110| ret[$-1] = 1;
| }
| else
| {
738| ret[$ - 1] = _stride!(N - 1);
| foreach_reverse (i; Iota!(N - 1))
471| ret[i] = ret[i + 1] * _lengths[i + 1];
| }
848| return ret;
| }
| }
|
| static if (doUnittest)
| /// Regular slice
| @safe @nogc pure nothrow
| version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| size_t[3] s = [20, 5, 1];
2| assert(iota(3, 4, 5).strides == s);
| }
|
| static if (doUnittest)
| /// Modified regular slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : pack, iota, universal;
| import mir.ndslice.dynamic : reversed, strided, transposed;
2| assert(iota(3, 4, 50)
| .universal
| .reversed!2 //makes stride negative
| .strided!2(6) //multiplies stride by 6 and changes corresponding length
| .transposed!2 //brings dimension `2` to the first position
| .strides == cast(ptrdiff_t[3])[-6, 200, 50]);
| }
|
| static if (doUnittest)
| /// Packed slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : pack, iota;
2| size_t[3] s = [20 * 42, 5 * 42, 1 * 42];
2| assert(iota(3, 4, 5, 6, 7)
| .pack!2
| .strides == s);
| }
|
| /++
| Returns: static array of lengths and static array of strides
| See_also: $(LREF .Slice.shape)
| +/
| Structure!N structure()() @safe @property scope const
| {
6| return typeof(return)(_lengths, strides);
| }
|
| static if (doUnittest)
| /// Regular slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| assert(iota(3, 4, 5)
| .structure == Structure!3([3, 4, 5], [20, 5, 1]));
| }
|
| static if (doUnittest)
| /// Modified regular slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : pack, iota, universal;
| import mir.ndslice.dynamic : reversed, strided, transposed;
2| assert(iota(3, 4, 50)
| .universal
| .reversed!2 //makes stride negative
| .strided!2(6) //multiplies stride by 6 and changes corresponding length
| .transposed!2 //brings dimension `2` to the first position
| .structure == Structure!3([9, 3, 4], [-6, 200, 50]));
| }
|
| static if (doUnittest)
| /// Packed slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : pack, iota;
2| assert(iota(3, 4, 5, 6, 7)
| .pack!2
| .structure == Structure!3([3, 4, 5], [20 * 42, 5 * 42, 1 * 42]));
| }
|
| /++
| Save primitive.
| +/
| auto save()() scope return inout @property
| {
6| return this;
| }
|
| static if (doUnittest)
| /// Save range
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| auto slice = iota(2, 3).save;
| }
|
| static if (doUnittest)
| /// Pointer type.
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
| //sl type is `Slice!(2, int*)`
2| auto sl = slice!int(2, 3).save;
| }
|
| /++
| Multidimensional `length` property.
| Returns: length of the corresponding dimension
| See_also: $(LREF .Slice.shape), $(LREF .Slice.structure)
| +/
| size_t length(size_t dimension = 0)() @safe @property scope const
| if (dimension < N)
| {
103349| return _lengths[dimension];
| }
|
| static if (doUnittest)
| ///
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| auto slice = iota(3, 4, 5);
2| assert(slice.length == 3);
2| assert(slice.length!0 == 3);
2| assert(slice.length!1 == 4);
2| assert(slice.length!2 == 5);
| }
|
| alias opDollar = length;
|
| /++
| Multidimensional `stride` property.
| Returns: stride of the corresponding dimension
| See_also: $(LREF .Slice.structure)
| +/
| sizediff_t _stride(size_t dimension = 0)() @safe @property scope const
| if (dimension < N)
| {
| static if (dimension < S)
| {
402| return _strides[dimension];
| }
| else
| static if (dimension + 1 == N)
| {
88331| return 1;
| }
| else
| {
2122| size_t ball = _lengths[$ - 1];
| foreach_reverse(i; Iota!(dimension + 1, N - 1))
48| ball *= _lengths[i];
2122| return ball;
| }
|
| }
|
| static if (doUnittest)
| /// Regular slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| auto slice = iota(3, 4, 5);
2| assert(slice._stride == 20);
2| assert(slice._stride!0 == 20);
2| assert(slice._stride!1 == 5);
2| assert(slice._stride!2 == 1);
| }
|
| static if (doUnittest)
| /// Modified regular slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.dynamic : reversed, strided, swapped;
| import mir.ndslice.topology : universal, iota;
2| assert(iota(3, 4, 50)
| .universal
| .reversed!2 //makes stride negative
| .strided!2(6) //multiplies stride by 6 and changes the corresponding length
| .swapped!(1, 2) //swaps dimensions `1` and `2`
| ._stride!1 == -6);
| }
|
| /++
| Multidimensional input range primitive.
| +/
| bool empty(size_t dimension = 0)() @safe @property scope const
| if (dimension < N)
| {
386094| return _lengths[dimension] == 0;
| }
|
| static if (N == 1)
| {
| ///ditto
| auto ref front(size_t dimension = 0)() scope return @trusted @property
| if (dimension == 0)
| {
300794| assert(!empty!dimension);
300794| return *_iterator;
| }
|
| ///ditto
| auto ref front(size_t dimension = 0)() scope return @trusted @property const
| if (dimension == 0)
| {
12| assert(!empty!dimension);
12| return *_iterator.lightScope;
| }
|
| ///ditto
| auto ref front(size_t dimension = 0)() scope return @trusted @property immutable
| if (dimension == 0)
| {
| assert(!empty!dimension);
| return *_iterator.lightScope;
| }
| }
| else
| {
| /// ditto
| Element!dimension front(size_t dimension = 0)() scope return @property
| if (dimension < N)
| {
3475| typeof(return)._Structure structure_ = typeof(return)._Structure.init;
|
| foreach (i; Iota!(typeof(return).N))
| {
| enum j = i >= dimension ? i + 1 : i;
3719| structure_[0][i] = _lengths[j];
| }
|
| static if (!typeof(return).S || typeof(return).S + 1 == S)
| alias s = _strides;
| else
25| auto s = strides;
|
| foreach (i; Iota!(typeof(return).S))
| {
| enum j = i >= dimension ? i + 1 : i;
982| structure_[1][i] = s[j];
| }
|
3475| return typeof(return)(structure_, _iterator);
| }
|
| ///ditto
| auto front(size_t dimension = 0)() scope return @trusted @property const
| if (dimension < N)
| {
| assert(!empty!dimension);
| return this.lightConst.front!dimension;
| }
|
| ///ditto
| auto front(size_t dimension = 0)() scope return @trusted @property immutable
| if (dimension < N)
| {
| assert(!empty!dimension);
| return this.lightImmutable.front!dimension;
| }
| }
|
| static if (N == 1 && isMutable!DeepElement && !hasAccessByRef)
| {
| ///ditto
| auto ref front(size_t dimension = 0, T)(T value) scope return @trusted @property
| if (dimension == 0)
| {
| // check assign safety
| static auto ref fun(ref DeepElement t, ref T v) @safe
| {
0000000| return t = v;
| }
617| assert(!empty!dimension);
| static if (__traits(compiles, *_iterator = value))
| return *_iterator = value;
| else
623| return _iterator[0] = value;
| }
| }
|
| ///ditto
| static if (N == 1)
| auto ref Element!dimension
| back(size_t dimension = 0)() scope return @trusted @property
| if (dimension < N)
| {
7576| assert(!empty!dimension);
7576| return _iterator[backIndex];
| }
| else
| auto ref Element!dimension
| back(size_t dimension = 0)() scope return @trusted @property
| if (dimension < N)
| {
13| assert(!empty!dimension);
13| auto structure_ = typeof(return)._Structure.init;
|
| foreach (i; Iota!(typeof(return).N))
| {
| enum j = i >= dimension ? i + 1 : i;
16| structure_[0][i] = _lengths[j];
| }
|
| static if (!typeof(return).S || typeof(return).S + 1 == S)
| alias s =_strides;
| else
5| auto s = strides;
|
| foreach (i; Iota!(typeof(return).S))
| {
| enum j = i >= dimension ? i + 1 : i;
9| structure_[1][i] = s[j];
| }
|
13| return typeof(return)(structure_, _iterator + backIndex!dimension);
| }
|
| static if (N == 1 && isMutable!DeepElement && !hasAccessByRef)
| {
| ///ditto
| auto ref back(size_t dimension = 0, T)(T value) scope return @trusted @property
| if (dimension == 0)
| {
| // check assign safety
| static auto ref fun(ref DeepElement t, ref T v) @safe
| {
| return t = v;
| }
| assert(!empty!dimension);
| return _iterator[backIndex] = value;
| }
| }
|
| ///ditto
| void popFront(size_t dimension = 0)() @trusted
| if (dimension < N && (dimension == 0 || kind != Contiguous))
| {
213793| assert(_lengths[dimension], __FUNCTION__ ~ ": length!" ~ dimension.stringof ~ " should be greater than 0.");
213793| _lengths[dimension]--;
| static if ((kind == Contiguous || kind == Canonical) && dimension + 1 == N)
200854| ++_iterator;
| else
| static if (kind == Canonical || kind == Universal)
10963| _iterator += _strides[dimension];
| else
1976| _iterator += _stride!dimension;
| }
|
| ///ditto
| void popBack(size_t dimension = 0)() @safe scope
| if (dimension < N && (dimension == 0 || kind != Contiguous))
| {
102| assert(_lengths[dimension], __FUNCTION__ ~ ": length!" ~ dimension.stringof ~ " should be greater than 0.");
102| --_lengths[dimension];
| }
|
| ///ditto
| void popFrontExactly(size_t dimension = 0)(size_t n) @trusted scope
| if (dimension < N && (dimension == 0 || kind != Contiguous))
| {
120| assert(n <= _lengths[dimension],
| __FUNCTION__ ~ ": n should be less than or equal to length!" ~ dimension.stringof);
120| _lengths[dimension] -= n;
120| _iterator += _stride!dimension * n;
| }
|
| ///ditto
| void popBackExactly(size_t dimension = 0)(size_t n) @safe scope
| if (dimension < N && (dimension == 0 || kind != Contiguous))
| {
29| assert(n <= _lengths[dimension],
| __FUNCTION__ ~ ": n should be less than or equal to length!" ~ dimension.stringof);
29| _lengths[dimension] -= n;
| }
|
| ///ditto
| void popFrontN(size_t dimension = 0)(size_t n) @trusted scope
| if (dimension < N && (dimension == 0 || kind != Contiguous))
| {
4| popFrontExactly!dimension(min(n, _lengths[dimension]));
| }
|
| ///ditto
| void popBackN(size_t dimension = 0)(size_t n) @safe scope
| if (dimension < N && (dimension == 0 || kind != Contiguous))
| {
| popBackExactly!dimension(min(n, _lengths[dimension]));
| }
|
| static if (doUnittest)
| ///
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import std.range.primitives;
| import mir.ndslice.topology : iota, canonical;
2| auto slice = iota(10, 20, 30).canonical;
|
| static assert(isRandomAccessRange!(typeof(slice)));
| static assert(hasSlicing!(typeof(slice)));
| static assert(hasLength!(typeof(slice)));
|
2| assert(slice.shape == cast(size_t[3])[10, 20, 30]);
2| slice.popFront;
2| slice.popFront!1;
2| slice.popBackExactly!2(4);
2| assert(slice.shape == cast(size_t[3])[9, 19, 26]);
|
2| auto matrix = slice.front!1;
2| assert(matrix.shape == cast(size_t[2])[9, 26]);
|
2| auto column = matrix.back!1;
2| assert(column.shape == cast(size_t[1])[9]);
|
2| slice.popFrontExactly!1(slice.length!1);
2| assert(slice.empty == false);
2| assert(slice.empty!1 == true);
2| assert(slice.empty!2 == false);
2| assert(slice.shape == cast(size_t[3])[9, 0, 26]);
|
2| assert(slice.back.front!1.empty);
|
2| slice.popFrontN!0(40);
2| slice.popFrontN!2(40);
2| assert(slice.shape == cast(size_t[3])[0, 0, 0]);
| }
|
| package(mir) ptrdiff_t lastIndex()() @safe @property scope const
| {
| static if (kind == Contiguous)
| {
660| return elementCount - 1;
| }
| else
| {
4| auto strides = strides;
4| ptrdiff_t shift = 0;
| foreach(i; Iota!N)
8| shift += strides[i] * (_lengths[i] - 1);
4| return shift;
| }
| }
|
| static if (N > 1)
| {
| /// Accesses the first deep element of the slice.
| auto ref first()() scope return @trusted @property
| {
5| assert(!anyEmpty);
5| return *_iterator;
| }
|
| static if (isMutable!DeepElement && !hasAccessByRef)
| ///ditto
| auto ref first(T)(T value) scope return @trusted @property
| {
| assert(!anyEmpty);
| static if (__traits(compiles, *_iterator = value))
| return *_iterator = value;
| else
| return _iterator[0] = value;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow @nogc version(mir_test) unittest
| {
| import mir.ndslice.topology: iota, universal, canonical;
1| auto f = 5;
1| assert([2, 3].iota(f).first == f);
| }
|
| /// Accesses the last deep element of the slice.
| auto ref last()() @trusted scope return @property
| {
1| assert(!anyEmpty);
1| return _iterator[lastIndex];
| }
|
| static if (isMutable!DeepElement && !hasAccessByRef)
| ///ditto
| auto ref last(T)(T value) @trusted scope return @property
| {
| assert(!anyEmpty);
| return _iterator[lastIndex] = value;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow @nogc version(mir_test) unittest
| {
| import mir.ndslice.topology: iota;
1| auto f = 5;
1| assert([2, 3].iota(f).last == f + 2 * 3 - 1);
| }
|
| static if (kind_ != SliceKind.contiguous)
| /// Peforms `popFrontAll` for all dimensions
| void popFrontAll()
| {
2| assert(!anyEmpty);
| foreach(d; Iota!N_)
4| popFront!d;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology: iota, canonical;
1| auto v = [2, 3].iota.canonical;
1| v.popFrontAll;
1| assert(v == [[4, 5]]);
| }
|
| static if (kind_ != SliceKind.contiguous)
| /// Peforms `popBackAll` for all dimensions
| void popBackAll()
| {
2| assert(!anyEmpty);
| foreach(d; Iota!N_)
4| popBack!d;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology: iota, canonical;
1| auto v = [2, 3].iota.canonical;
1| v.popBackAll;
1| assert(v == [[0, 1]]);
| }
| }
| else
| {
| alias first = front;
| alias last = back;
| alias popFrontAll = popFront;
| alias popBackAll = popBack;
| }
|
| /+
| Returns: `true` if for any dimension of completely unpacked slice the length equals to `0`, and `false` otherwise.
| +/
| private bool anyRUEmpty()() @trusted @property scope const
| {
| static if (isInstanceOf!(SliceIterator, Iterator))
| {
| import mir.ndslice.topology: unpack;
22| return this.lightScope.unpack.anyRUEmpty;
| }
| else
1295| return _lengths[0 .. N].anyEmptyShape;
| }
|
|
| /++
| Returns: `true` if for any dimension the length equals to `0`, and `false` otherwise.
| +/
| bool anyEmpty()() @trusted @property scope const
| {
2109| return _lengths[0 .. N].anyEmptyShape;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow @nogc version(mir_test) unittest
| {
| import mir.ndslice.topology : iota, canonical;
2| auto s = iota(2, 3).canonical;
2| assert(!s.anyEmpty);
2| s.popFrontExactly!1(3);
2| assert(s.anyEmpty);
| }
|
| /++
| Convenience function for backward indexing.
|
| Returns: `this[$-index[0], $-index[1], ..., $-index[N-1]]`
| +/
| auto ref backward()(size_t[N] index) scope return
| {
| foreach (i; Iota!N)
24| index[i] = _lengths[i] - index[i];
12| return this[index];
| }
|
| /// ditto
| auto ref backward()(size_t[N] index) scope return const
| {
| return this.lightConst.backward(index);
| }
|
| /// ditto
| auto ref backward()(size_t[N] index) scope return const
| {
| return this.lightConst.backward(index);
| }
|
| static if (doUnittest)
| ///
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| auto s = iota(2, 3);
2| assert(s[$ - 1, $ - 2] == s.backward([1, 2]));
| }
|
| /++
| Returns: Total number of elements in a slice
| +/
| size_t elementCount()() @safe @property scope const
| {
5525| size_t len = 1;
| foreach (i; Iota!N)
11363| len *= _lengths[i];
5525| return len;
| }
|
| static if (doUnittest)
| /// Regular slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| assert(iota(3, 4, 5).elementCount == 60);
| }
|
|
| static if (doUnittest)
| /// Packed slice
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : pack, evertPack, iota;
2| auto slice = iota(3, 4, 5, 6, 7, 8);
2| auto p = slice.pack!2;
2| assert(p.elementCount == 360);
2| assert(p[0, 0, 0, 0].elementCount == 56);
2| assert(p.evertPack.elementCount == 56);
| }
|
| /++
| Slice selected dimension.
| Params:
| begin = initial index of the sub-slice (inclusive)
| end = final index of the sub-slice (noninclusive)
| Returns: ndslice with `length!dimension` equal to `end - begin`.
| +/
| auto select(size_t dimension)(size_t begin, size_t end) @trusted
| {
| static if (kind == Contiguous && dimension)
| {
| import mir.ndslice.topology: canonical;
2| auto ret = this.canonical;
| }
| else
| {
25| auto ret = this;
| }
27| auto len = end - begin;
27| assert(len <= ret._lengths[dimension]);
27| ret._lengths[dimension] = len;
27| ret._iterator += ret._stride!dimension * begin;
27| return ret;
| }
|
| static if (doUnittest)
| ///
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| auto sl = iota(3, 4);
2| assert(sl.select!1(1, 3) == sl[0 .. $, 1 .. 3]);
| }
|
| /++
| Select the first n elements for the dimension.
| Params:
| dimension = Dimension to slice.
| n = count of elements for the dimension
| Returns: ndslice with `length!dimension` equal to `n`.
| +/
| auto selectFront(size_t dimension)(size_t n) scope return
| {
| static if (kind == Contiguous && dimension)
| {
| import mir.ndslice.topology: canonical;
2| auto ret = this.canonical;
| }
| else
| {
154| auto ret = this;
| }
156| assert(n <= ret._lengths[dimension]);
156| ret._lengths[dimension] = n;
156| return ret;
| }
|
| static if (doUnittest)
| ///
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| auto sl = iota(3, 4);
2| assert(sl.selectFront!1(2) == sl[0 .. $, 0 .. 2]);
| }
|
| /++
| Select the last n elements for the dimension.
| Params:
| dimension = Dimension to slice.
| n = count of elements for the dimension
| Returns: ndslice with `length!dimension` equal to `n`.
| +/
| auto selectBack(size_t dimension)(size_t n) scope return
| {
| static if (kind == Contiguous && dimension)
| {
| import mir.ndslice.topology: canonical;
2| auto ret = this.canonical;
| }
| else
| {
49| auto ret = this;
| }
51| assert(n <= ret._lengths[dimension]);
51| ret._iterator += ret._stride!dimension * (ret._lengths[dimension] - n);
51| ret._lengths[dimension] = n;
51| return ret;
| }
|
| static if (doUnittest)
| ///
| @safe @nogc pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : iota;
2| auto sl = iota(3, 4);
2| assert(sl.selectBack!1(2) == sl[0 .. $, $ - 2 .. $]);
| }
|
| ///ditto
| bool opEquals(IteratorR, SliceKind rkind)(auto ref const Slice!(IteratorR, N, rkind) rslice) @trusted scope const
| {
| static if (
| __traits(isPOD, Iterator)
| && __traits(isPOD, IteratorR)
| && __traits(compiles, this._iterator == rslice._iterator)
| )
| {
203| if (this._lengths != rslice._lengths)
2| return false;
201| if (anyEmpty)
0000000| return true;
201| if (this._iterator == rslice._iterator)
| {
184| auto ls = this.strides;
184| auto rs = rslice.strides;
| foreach (i; Iota!N)
| {
669| if (this._lengths[i] != 1 && ls[i] != rs[i])
0000000| goto L;
| }
184| return true;
| }
| }
| L:
|
| static if (is(Iterator == IotaIterator!UL, UL) && is(IteratorR == Iterator))
| {
0000000| return false;
| }
| else
| {
| import mir.algorithm.iteration : equal;
169| return equal(this.lightScope, rslice.lightScope);
| }
| }
|
| /// ditto
| bool opEquals(T)(scope const(T)[] arr) @trusted scope const
| {
1857| auto slice = this.lightConst;
1839| if (slice.length != arr.length)
2| return false;
1837| if (arr.length) do
| {
5818| if (slice.front != arr[0])
6| return false;
5797| slice.popFront;
5797| arr = arr[1 .. $];
| }
5797| while (arr.length);
1831| return true;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow
| version(mir_test) unittest
| {
2| auto a = [1, 2, 3, 4].sliced(2, 2);
|
2| assert(a != [1, 2, 3, 4, 5, 6].sliced(2, 3));
2| assert(a != [[1, 2, 3], [4, 5, 6]]);
|
2| assert(a == [1, 2, 3, 4].sliced(2, 2));
2| assert(a == [[1, 2], [3, 4]]);
|
2| assert(a != [9, 2, 3, 4].sliced(2, 2));
2| assert(a != [[9, 2], [3, 4]]);
| }
|
| static if (doUnittest)
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology : iota;
2| assert(iota(2, 3).slice[0 .. $ - 2] == iota([4, 3], 2)[0 .. $ - 4]);
| }
|
| /++
| `Slice!(IotaIterator!size_t)` is the basic type for `[a .. b]` syntax for all ndslice based code.
| +/
| Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j) @safe scope const
| if (dimension < N)
| in
| {
1860| assert(i <= j,
| "Slice.opSlice!" ~ dimension.stringof ~ ": the left opSlice boundary must be less than or equal to the right bound.");
| enum errorMsg = ": right opSlice boundary must be less than or equal to the length of the given dimension.";
1860| assert(j <= _lengths[dimension],
| "Slice.opSlice!" ~ dimension.stringof ~ errorMsg);
| }
| do
| {
1860| return typeof(return)(j - i, typeof(return).Iterator(i));
| }
|
| /++
| $(BOLD Fully defined index)
| +/
| auto ref opIndex()(size_t[N] _indices...) scope return @trusted
| {
40168| return _iterator[indexStride(_indices)];
| }
|
| /// ditto
| auto ref opIndex()(size_t[N] _indices...) scope return const @trusted
| {
| static if (is(typeof(_iterator[indexStride(_indices)])))
6544| return _iterator[indexStride(_indices)];
| else
| return .lightConst(.lightScope(_iterator))[indexStride(_indices)];
| }
|
| /// ditto
| auto ref opIndex()(size_t[N] _indices...) scope return immutable @trusted
| {
| static if (is(typeof(_iterator[indexStride(_indices)])))
0000000| return _iterator[indexStride(_indices)];
| else
| return .lightImmutable(.lightScope(_iterator))[indexStride(_indices)];
| }
|
| /++
| $(BOLD Partially defined index)
| +/
| auto opIndex(size_t I)(size_t[I] _indices...) scope return @trusted
| if (I && I < N)
| {
| enum size_t diff = N - I;
| alias Ret = Slice!(Iterator, diff, diff == 1 && kind == Canonical ? Contiguous : kind);
| static if (I < S)
7| return Ret(_lengths[I .. N], _strides[I .. S], _iterator + indexStride(_indices));
| else
76| return Ret(_lengths[I .. N], _iterator + indexStride(_indices));
| }
|
| /// ditto
| auto opIndex(size_t I)(size_t[I] _indices...) scope return const
| if (I && I < N)
| {
| return this.lightConst.opIndex(_indices);
| }
|
| /// ditto
| auto opIndex(size_t I)(size_t[I] _indices...) scope return immutable
| if (I && I < N)
| {
| return this.lightImmutable.opIndex(_indices);
| }
|
| /++
| $(BOLD Partially or fully defined slice.)
| +/
| auto opIndex(Slices...)(Slices slices) scope return @trusted
| if (isPureSlice!Slices)
| {
| static if (Slices.length)
| {
| enum size_t j(size_t n) = n - Filter!(isIndex, Slices[0 .. n]).length;
| enum size_t F = PureIndexLength!Slices;
| enum size_t S = Slices.length;
| static assert(N - F > 0);
1873| size_t stride;
| static if (Slices.length == 1)
| enum K = kind;
| else
| static if (kind == Universal || Slices.length == N && isIndex!(Slices[$-1]))
| enum K = Universal;
| else
| static if (Filter!(isIndex, Slices[0 .. $-1]).length == Slices.length - 1 || N - F == 1)
| enum K = Contiguous;
| else
| enum K = Canonical;
| alias Ret = Slice!(Iterator, N - F, K);
1873| auto structure_ = Ret._Structure.init;
|
| enum bool shrink = kind == Canonical && slices.length == N;
| static if (shrink)
| {
| {
| enum i = Slices.length - 1;
1| auto slice = slices[i];
| static if (isIndex!(Slices[i]))
| {
| assert(slice < _lengths[i], "Slice.opIndex: index must be less than length");
| stride += slice;
| }
| else
| {
1| stride += slice._iterator._index;
1| structure_[0][j!i] = slice._lengths[0];
| }
| }
| }
| static if (kind == Universal || kind == Canonical)
| {
6| foreach_reverse (i, slice; slices[0 .. $ - shrink]) //static
| {
| static if (isIndex!(Slices[i]))
| {
1| assert(slice < _lengths[i], "Slice.opIndex: index must be less than length");
1| stride += _strides[i] * slice;
| }
| else
| {
5| stride += _strides[i] * slice._iterator._index;
5| structure_[0][j!i] = slice._lengths[0];
5| structure_[1][j!i] = _strides[i];
| }
| }
| }
| else
| {
1869| ptrdiff_t ball = this._stride!(slices.length - 1);
1999| foreach_reverse (i, slice; slices) //static
| {
| static if (isIndex!(Slices[i]))
| {
51| assert(slice < _lengths[i], "Slice.opIndex: index must be less than length");
51| stride += ball * slice;
| }
| else
| {
1948| stride += ball * slice._iterator._index;
1948| structure_[0][j!i] = slice._lengths[0];
| static if (j!i < Ret.S)
88| structure_[1][j!i] = ball;
| }
| static if (i)
130| ball *= _lengths[i];
| }
| }
| foreach (i; Iota!(Slices.length, N))
42| structure_[0][i - F] = _lengths[i];
| foreach (i; Iota!(Slices.length, N))
| static if (Ret.S > i - F)
1| structure_[1][i - F] = _strides[i];
|
1873| return Ret(structure_, _iterator + stride);
| }
| else
| {
1460| return this;
| }
| }
|
| static if (doUnittest)
| ///
| pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto slice = slice!int(5, 3);
|
| /// Fully defined slice
2| assert(slice[] == slice);
2| auto sublice = slice[0..$-2, 1..$];
|
| /// Partially defined slice
2| auto row = slice[3];
2| auto col = slice[0..$, 1];
| }
|
| /++
| $(BOLD Indexed slice.)
| +/
| auto opIndex(Slices...)(scope return Slices slices) scope return
| if (isIndexedSlice!Slices)
| {
| import mir.ndslice.topology: indexed, cartesian;
| static if (Slices.length == 1)
| alias index = slices[0];
| else
2| auto index = slices.cartesian;
11| return this.indexed(index);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation: slice;
2| auto sli = slice!int(4, 3);
2| auto idx = slice!(size_t[2])(3);
2| idx[] = [
| cast(size_t[2])[0, 2],
| cast(size_t[2])[3, 1],
| cast(size_t[2])[2, 0]];
|
| // equivalent to:
| // import mir.ndslice.topology: indexed;
| // sli.indexed(indx)[] = 1;
2| sli[idx] = 1;
|
2| assert(sli == [
| [0, 0, 1],
| [0, 0, 0],
| [1, 0, 0],
| [0, 1, 0],
| ]);
|
16| foreach (row; sli[[1, 3].sliced])
4| row[] += 2;
|
2| assert(sli == [
| [0, 0, 1],
| [2, 2, 2], // <-- += 2
| [1, 0, 0],
| [2, 3, 2], // <-- += 2
| ]);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology: iota;
| import mir.ndslice.allocation: slice;
2| auto sli = slice!int(5, 6);
|
| // equivalent to
| // import mir.ndslice.topology: indexed, cartesian;
| // auto a = [0, sli.length!0 / 2, sli.length!0 - 1].sliced;
| // auto b = [0, sli.length!1 / 2, sli.length!1 - 1].sliced;
| // auto c = cartesian(a, b);
| // auto minor = sli.indexed(c);
2| auto minor = sli[[0, $ / 2, $ - 1].sliced, [0, $ / 2, $ - 1].sliced];
|
2| minor[] = iota!int([3, 3], 1);
|
2| assert(sli == [
| // ↓ ↓ ↓︎
| [1, 0, 0, 2, 0, 3], // <---
| [0, 0, 0, 0, 0, 0],
| [4, 0, 0, 5, 0, 6], // <---
| [0, 0, 0, 0, 0, 0],
| [7, 0, 0, 8, 0, 9], // <---
| ]);
| }
|
| /++
| Element-wise binary operator overloading.
| Returns:
| lazy slice of the same kind and the same structure
| Note:
| Does not allocate neither new slice nor a closure.
| +/
| auto opUnary(string op)() scope return
| if (op == "*" || op == "~" || op == "-" || op == "+")
| {
| import mir.ndslice.topology: map;
| static if (op == "+")
| return this;
| else
5| return this.map!(op ~ "a");
| }
|
| static if (doUnittest)
| ///
| version(mir_test) unittest
| {
| import mir.ndslice.topology;
|
2| auto payload = [1, 2, 3, 4];
2| auto s = iota([payload.length], payload.ptr); // slice of references;
2| assert(s[1] == payload.ptr + 1);
|
2| auto c = *s; // the same as s.map!"*a"
2| assert(c[1] == *s[1]);
|
2| *s[1] = 3;
2| assert(c[1] == *s[1]);
| }
|
| /++
| Element-wise operator overloading for scalars.
| Params:
| value = a scalar
| Returns:
| lazy slice of the same kind and the same structure
| Note:
| Does not allocate neither new slice nor a closure.
| +/
| auto opBinary(string op, T)(scope return T value) scope return
| if(!isSlice!T)
| {
| import mir.ndslice.topology: vmap;
35| return this.vmap(LeftOp!(op, ImplicitlyUnqual!T)(value));
| }
|
| /// ditto
| auto opBinaryRight(string op, T)(scope return T value) scope return
| if(!isSlice!T)
| {
| import mir.ndslice.topology: vmap;
36| return this.vmap(RightOp!(op, ImplicitlyUnqual!T)(value));
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow @nogc version(mir_test) unittest
| {
| import mir.ndslice.topology;
|
| // 0 1 2 3
2| auto s = iota([4]);
| // 0 1 2 0
2| assert(s % 3 == iota([4]).map!"a % 3");
| // 0 2 4 6
2| assert(2 * s == iota([4], 0, 2));
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow @nogc version(mir_test) unittest
| {
| import mir.ndslice.topology;
|
| // 0 1 2 3
2| auto s = iota([4]);
| // 0 1 4 9
2| assert(s ^^ 2.0 == iota([4]).map!"a ^^ 2.0");
| }
|
| /++
| Element-wise operator overloading for slices.
| Params:
| rhs = a slice of the same shape.
| Returns:
| lazy slice the same shape that has $(LREF Contiguous) kind
| Note:
| Binary operator overloading is allowed if both slices are contiguous or one-dimensional.
| $(BR)
| Does not allocate neither new slice nor a closure.
| +/
| auto opBinary(string op, RIterator, size_t RN, SliceKind rkind)
| (scope return Slice!(RIterator, RN, rkind) rhs) scope return
| if(N == RN && (kind == Contiguous && rkind == Contiguous || N == 1) && op != "~")
| {
| import mir.ndslice.topology: zip, map;
296| return zip(this, rhs).map!("a " ~ op ~ " b");
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow @nogc version(mir_test) unittest
| {
| import mir.ndslice.topology: iota, map, zip;
|
2| auto s = iota([2, 3]);
2| auto c = iota([2, 3], 5, 8);
2| assert(s * s + c == s.map!"a * a".zip(c).map!"a + b");
| }
|
| /++
| Duplicates slice.
| Returns: GC-allocated Contiguous mutable slice.
| See_also: $(LREF .Slice.idup)
| +/
| Slice!(Unqual!DeepElement*, N)
| dup()() scope @property
| {
188| if (__ctfe)
| {
| import mir.ndslice.topology: flattened;
| import mir.array.allocation: array;
0000000| return this.flattened.array.dup.sliced(this.shape);
| }
| else
| {
| import mir.ndslice.allocation: uninitSlice;
| import mir.conv: emplaceRef;
| alias E = this.DeepElement;
|
376| auto result = (() @trusted => this.shape.uninitSlice!(Unqual!E))();
|
| import mir.algorithm.iteration: each;
188| each!(emplaceRef!(Unqual!E))(result, this);
|
188| return result;
| }
| }
|
| /// ditto
| Slice!(immutable(DeepElement)*, N)
| dup()() scope const @property
| {
| this.lightScope.dup;
| }
|
| /// ditto
| Slice!(immutable(DeepElement)*, N)
| dup()() scope immutable @property
| {
| this.lightScope.dup;
| }
|
| static if (doUnittest)
| ///
| @safe pure version(mir_test) unittest
| {
| import mir.ndslice;
2| auto x = 3.iota!int;
2| Slice!(immutable(int)*) imm = x.idup;
2| Slice!(int*) mut = imm.dup;
2| assert(imm == x);
2| assert(mut == x);
| }
|
| /++
| Duplicates slice.
| Returns: GC-allocated Contiguous immutable slice.
| See_also: $(LREF .Slice.dup)
| +/
| Slice!(immutable(DeepElement)*, N)
| idup()() scope @property
| {
4| if (__ctfe)
| {
| import mir.ndslice.topology: flattened;
| import mir.array.allocation: array;
0000000| return this.flattened.array.idup.sliced(this.shape);
| }
| else
| {
| import mir.ndslice.allocation: uninitSlice;
| import mir.conv: emplaceRef;
| alias E = this.DeepElement;
|
8| auto result = (() @trusted => this.shape.uninitSlice!(Unqual!E))();
|
| import mir.algorithm.iteration: each;
4| each!(emplaceRef!(immutable E))(result, this);
| alias R = typeof(return);
8| return (() @trusted => cast(R) result)();
| }
| }
|
| /// ditto
| Slice!(immutable(DeepElement)*, N)
| idup()() scope const @property
| {
| this.lightScope.idup;
| }
|
| /// ditto
| Slice!(immutable(DeepElement)*, N)
| idup()() scope immutable @property
| {
| this.lightScope.idup;
| }
|
| static if (doUnittest)
| ///
| @safe pure version(mir_test) unittest
| {
| import mir.ndslice;
2| auto x = 3.iota!int;
2| Slice!(int*) mut = x.dup;
2| Slice!(immutable(int)*) imm = mut.idup;
2| assert(imm == x);
2| assert(mut == x);
| }
|
| /++
| Provides the index location of a slice, taking into account
| `Slice._strides`. Similar to `Slice.indexStride`, except the slice is
| indexed by a value. Called by `Slice.accessFlat`.
|
| Params:
| n = location in slice
| Returns:
| location in slice, adjusted for `Slice._strides`
| See_also:
| $(SUBREF topology, flattened),
| $(LREF .Slice.indexStride),
| $(LREF .Slice.accessFlat)
| +/
| private
| ptrdiff_t indexStrideValue(ptrdiff_t n) @safe scope const
| {
1335| assert(n < elementCount, "indexStrideValue: n must be less than elementCount");
1335| assert(n >= 0, "indexStrideValue: n must be greater than or equal to zero");
|
| static if (kind == Contiguous) {
1266| return n;
| } else {
69| ptrdiff_t _shift;
| foreach_reverse (i; Iota!(1, N))
| {
105| immutable v = n / ptrdiff_t(_lengths[i]);
105| n %= ptrdiff_t(_lengths[i]);
| static if (i == S)
27| _shift += n;
| else
78| _shift += n * _strides[i];
105| n = v;
| }
69| _shift += n * _strides[0];
69| return _shift;
| }
| }
|
| /++
| Provides access to a slice as if it were `flattened`.
|
| Params:
| index = location in slice
| Returns:
| value of flattened slice at `index`
| See_also: $(SUBREF topology, flattened)
| +/
| auto ref accessFlat(size_t index) scope return @trusted
| {
1335| return _iterator[indexStrideValue(index)];
| }
|
| ///
| version(mir_test)
| @safe pure @nogc nothrow
| unittest
| {
| import mir.ndslice.topology: iota, flattened;
|
1238| auto x = iota(2, 3, 4);
1238| assert(x.accessFlat(9) == x.flattened[9]);
| }
|
| static if (isMutable!DeepElement)
| {
| private void opIndexOpAssignImplSlice(string op, RIterator, size_t RN, SliceKind rkind)
| (Slice!(RIterator, RN, rkind) value) scope
| {
| static if (N > 1 && RN == N && kind == Contiguous && rkind == Contiguous)
| {
| import mir.ndslice.topology : flattened;
14| this.flattened.opIndexOpAssignImplSlice!op(value.flattened);
| }
| else
| {
1305| auto ls = this;
| do
| {
| static if (N > RN)
| {
98| ls.front.opIndexOpAssignImplSlice!op(value);
| }
| else
| {
| static if (ls.N == 1)
| {
| static if (isInstanceOf!(SliceIterator, Iterator))
| {
| static if (isSlice!(typeof(value.front)))
16| ls.front.opIndexOpAssignImplSlice!op(value.front);
| else
| static if (isDynamicArray!(typeof(value.front)))
| ls.front.opIndexOpAssignImplSlice!op(value.front);
| else
16| ls.front.opIndexOpAssignImplValue!op(value.front);
| }
| else
| static if (op == "^^" && isFloatingPoint!(typeof(ls.front)) && isFloatingPoint!(typeof(value.front)))
| {
| import mir.math.common: pow;
| ls.front = pow(ls.front, value.front);
| }
| else
| mixin("ls.front " ~ op ~ "= value.front;");
| }
| else
| static if (RN == 1)
| ls.front.opIndexOpAssignImplValue!op(value.front);
| else
109| ls.front.opIndexOpAssignImplSlice!op(value.front);
3130| value.popFront;
| }
3228| ls.popFront;
| }
3228| while (ls._lengths[0]);
| }
| }
|
| /++
| Assignment of a value of `Slice` type to a $(B fully defined slice).
| +/
| void opIndexAssign(RIterator, size_t RN, SliceKind rkind, Slices...)
| (Slice!(RIterator, RN, rkind) value, Slices slices) scope return
| if (isFullPureSlice!Slices || isIndexedSlice!Slices)
| {
1058| auto sl = this.lightScope.opIndex(slices);
1058| assert(_checkAssignLengths(sl, value));
1058| if(!sl.anyRUEmpty)
1058| sl.opIndexOpAssignImplSlice!""(value);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
2| auto b = [1, 2, 3, 4].sliced(2, 2);
|
2| a[0..$, 0..$-1] = b;
2| assert(a == [[1, 2, 0], [3, 4, 0]]);
|
| // fills both rows with b[0]
2| a[0..$, 0..$-1] = b[0];
2| assert(a == [[1, 2, 0], [1, 2, 0]]);
|
2| a[1, 0..$-1] = b[1];
2| assert(a[1] == [3, 4, 0]);
|
2| a[1, 0..$-1][] = b[0];
2| assert(a[1] == [1, 2, 0]);
| }
|
| static if (doUnittest)
| /// Left slice is packed
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : blocks, iota;
| import mir.ndslice.allocation : slice;
2| auto a = slice!int(4, 4);
2| a.blocks(2, 2)[] = iota!int(2, 2);
|
2| assert(a ==
| [[0, 0, 1, 1],
| [0, 0, 1, 1],
| [2, 2, 3, 3],
| [2, 2, 3, 3]]);
| }
|
| static if (doUnittest)
| /// Both slices are packed
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.topology : blocks, iota, pack;
| import mir.ndslice.allocation : slice;
2| auto a = slice!int(4, 4);
2| a.blocks(2, 2)[] = iota!int(2, 2, 2).pack!1;
|
2| assert(a ==
| [[0, 1, 2, 3],
| [0, 1, 2, 3],
| [4, 5, 6, 7],
| [4, 5, 6, 7]]);
| }
|
| void opIndexOpAssignImplArray(string op, T, Slices...)(T[] value) scope
| {
108| auto ls = this;
108| assert(ls.length == value.length, __FUNCTION__ ~ ": argument must have the same length.");
| static if (N == 1)
| {
| do
| {
| static if (ls.N == 1)
| {
| static if (isInstanceOf!(SliceIterator, Iterator))
| {
| static if (isSlice!(typeof(value[0])))
| ls.front.opIndexOpAssignImplSlice!op(value[0]);
| else
| static if (isDynamicArray!(typeof(value[0])))
| ls.front.opIndexOpAssignImplSlice!op(value[0]);
| else
16| ls.front.opIndexOpAssignImplValue!op(value[0]);
| }
| else
| static if (op == "^^" && isFloatingPoint!(typeof(ls.front)) && isFloatingPoint!(typeof(value[0])))
| {
| import mir.math.common: pow;
| ls.front = pow(ls.front, value[0]);
| }
| else
| mixin("ls.front " ~ op ~ "= value[0];");
| }
| else
| mixin("ls.front[] " ~ op ~ "= value[0];");
272| value = value[1 .. $];
272| ls.popFront;
| }
272| while (ls.length);
| }
| else
| static if (N == DynamicArrayDimensionsCount!(T[]))
| {
| do
| {
28| ls.front.opIndexOpAssignImplArray!op(value[0]);
28| value = value[1 .. $];
28| ls.popFront;
| }
28| while (ls.length);
| }
| else
| {
| do
| {
12| ls.front.opIndexOpAssignImplArray!op(value);
12| ls.popFront;
| }
12| while (ls.length);
| }
| }
|
| /++
| Assignment of a regular multidimensional array to a $(B fully defined slice).
| +/
| void opIndexAssign(T, Slices...)(T[] value, Slices slices) scope return
| if ((isFullPureSlice!Slices || isIndexedSlice!Slices)
| && (!isDynamicArray!DeepElement || isDynamicArray!T)
| && DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= typeof(this.opIndex(slices)).N)
| {
54| auto sl = this.lightScope.opIndex(slices);
54| sl.opIndexOpAssignImplArray!""(value);
| }
|
| static if (doUnittest)
| ///
| pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
2| auto b = [[1, 2], [3, 4]];
|
2| a[] = [[1, 2, 3], [4, 5, 6]];
2| assert(a == [[1, 2, 3], [4, 5, 6]]);
|
2| a[0..$, 0..$-1] = [[1, 2], [3, 4]];
2| assert(a == [[1, 2, 3], [3, 4, 6]]);
|
2| a[0..$, 0..$-1] = [1, 2];
2| assert(a == [[1, 2, 3], [1, 2, 6]]);
|
2| a[1, 0..$-1] = [3, 4];
2| assert(a[1] == [3, 4, 6]);
|
2| a[1, 0..$-1][] = [3, 4];
2| assert(a[1] == [3, 4, 6]);
| }
|
| static if (doUnittest)
| /// Packed slices
| pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : blocks;
2| auto a = slice!int(4, 4);
2| a.blocks(2, 2)[] = [[0, 1], [2, 3]];
|
2| assert(a ==
| [[0, 0, 1, 1],
| [0, 0, 1, 1],
| [2, 2, 3, 3],
| [2, 2, 3, 3]]);
| }
|
|
| private void opIndexOpAssignImplConcatenation(string op, T)(T value) scope
| {
77| auto sl = this;
| static if (concatenationDimension!T)
| {
13| if (!sl.empty) do
| {
| static if (op == "")
50| sl.front.opIndexAssign(value.front);
| else
| sl.front.opIndexOpAssign!op(value.front);
50| value.popFront;
50| sl.popFront;
| }
50| while(!sl.empty);
| }
| else
| {
154| foreach (ref slice; value._slices)
| {
| static if (op == "")
152| sl[0 .. slice.length].opIndexAssign(slice);
| else
2| sl[0 .. slice.length].opIndexOpAssign!op(slice);
|
154| sl = sl[slice.length .. $];
| }
64| assert(sl.empty);
| }
| }
|
| ///
| void opIndexAssign(T, Slices...)(T concatenation, Slices slices) scope return
| if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T)
| {
76| auto sl = this.lightScope.opIndex(slices);
| static assert(typeof(sl).N == T.N, "incompatible dimension count");
76| sl.opIndexOpAssignImplConcatenation!""(concatenation);
| }
|
| static if (!isNumeric!DeepElement)
| /++
| Assignment of a value (e.g. a number) to a $(B fully defined slice).
| +/
| void opIndexAssign(T, Slices...)(T value, Slices slices) scope
| if ((isFullPureSlice!Slices || isIndexedSlice!Slices)
| && (!isDynamicArray!T || isDynamicArray!DeepElement)
| && DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement
| && !isSlice!T
| && !isConcatenation!T)
| {
11| auto sl = this.lightScope.opIndex(slices);
11| if(!sl.anyRUEmpty)
11| sl.opIndexOpAssignImplValue!""(value);
| }
| else
| void opIndexAssign(Slices...)(DeepElement value, Slices slices) scope
| if (isFullPureSlice!Slices || isIndexedSlice!Slices)
| {
131| auto sl = this.lightScope.opIndex(slices);
131| if(!sl.anyRUEmpty)
74| sl.opIndexOpAssignImplValue!""(value);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow
| version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
|
2| a[] = 9;
2| assert(a == [[9, 9, 9], [9, 9, 9]]);
|
2| a[0..$, 0..$-1] = 1;
2| assert(a == [[1, 1, 9], [1, 1, 9]]);
|
2| a[0..$, 0..$-1] = 2;
2| assert(a == [[2, 2, 9], [2, 2, 9]]);
|
2| a[1, 0..$-1] = 3;
| //assert(a[1] == [3, 3, 9]);
|
2| a[1, 0..$-1] = 4;
| //assert(a[1] == [4, 4, 9]);
|
2| a[1, 0..$-1][] = 5;
|
2| assert(a[1] == [5, 5, 9]);
| }
|
| static if (doUnittest)
| /// Packed slices have the same behavior.
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
| import mir.ndslice.topology : pack;
2| auto a = slice!int(2, 3).pack!1;
|
2| a[] = 9;
| //assert(a == [[9, 9, 9], [9, 9, 9]]);
| }
|
| /++
| Assignment of a value (e.g. a number) to a $(B fully defined index).
| +/
| auto ref opIndexAssign(T)(T value, size_t[N] _indices...) scope return @trusted
| {
| // check assign safety
| static auto ref fun(ref DeepElement t, ref T v) @safe
| {
| return t = v;
| }
| return _iterator[indexStride(_indices)] = value;
| }
| ///ditto
| auto ref opIndexAssign()(DeepElement value, size_t[N] _indices...) scope return @trusted
| {
| import mir.functional: forward;
| // check assign safety
| static auto ref fun(ref DeepElement t, ref DeepElement v) @safe
| {
0000000| return t = v;
| }
34259| return _iterator[indexStride(_indices)] = forward!value;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
|
2| a[1, 2] = 3;
2| assert(a[1, 2] == 3);
| }
|
| static if (doUnittest)
| @safe pure nothrow version(mir_test) unittest
| {
2| auto a = new int[6].sliced(2, 3);
|
2| a[[1, 2]] = 3;
2| assert(a[[1, 2]] == 3);
| }
|
| /++
| Op Assignment `op=` of a value (e.g. a number) to a $(B fully defined index).
| +/
| auto ref opIndexOpAssign(string op, T)(T value, size_t[N] _indices...) scope return @trusted
| {
| // check op safety
| static auto ref fun(ref DeepElement t, ref T v) @safe
| {
0000000| return mixin(`t` ~ op ~ `= v`);
| }
918| auto str = indexStride(_indices);
| static if (op == "^^" && isFloatingPoint!DeepElement && isFloatingPoint!(typeof(value)))
| {
| import mir.math.common: pow;
| _iterator[str] = pow(_iterator[str], value);
| }
| else
930| return mixin (`_iterator[str] ` ~ op ~ `= value`);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
|
2| a[1, 2] += 3;
2| assert(a[1, 2] == 3);
| }
|
| static if (doUnittest)
| @safe pure nothrow version(mir_test) unittest
| {
2| auto a = new int[6].sliced(2, 3);
|
2| a[[1, 2]] += 3;
2| assert(a[[1, 2]] == 3);
| }
|
| /++
| Op Assignment `op=` of a value of `Slice` type to a $(B fully defined slice).
| +/
| void opIndexOpAssign(string op, RIterator, SliceKind rkind, size_t RN, Slices...)
| (Slice!(RIterator, RN, rkind) value, Slices slices) scope return
| if (isFullPureSlice!Slices || isIndexedSlice!Slices)
| {
24| auto sl = this.lightScope.opIndex(slices);
24| assert(_checkAssignLengths(sl, value));
24| if(!sl.anyRUEmpty)
24| sl.opIndexOpAssignImplSlice!op(value);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
2| auto b = [1, 2, 3, 4].sliced(2, 2);
|
2| a[0..$, 0..$-1] += b;
2| assert(a == [[1, 2, 0], [3, 4, 0]]);
|
2| a[0..$, 0..$-1] += b[0];
2| assert(a == [[2, 4, 0], [4, 6, 0]]);
|
2| a[1, 0..$-1] += b[1];
2| assert(a[1] == [7, 10, 0]);
|
2| a[1, 0..$-1][] += b[0];
2| assert(a[1] == [8, 12, 0]);
| }
|
| static if (doUnittest)
| /// Left slice is packed
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : blocks, iota;
2| auto a = slice!size_t(4, 4);
2| a.blocks(2, 2)[] += iota(2, 2);
|
2| assert(a ==
| [[0, 0, 1, 1],
| [0, 0, 1, 1],
| [2, 2, 3, 3],
| [2, 2, 3, 3]]);
| }
|
| static if (doUnittest)
| /// Both slices are packed
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : blocks, iota, pack;
2| auto a = slice!size_t(4, 4);
2| a.blocks(2, 2)[] += iota(2, 2, 2).pack!1;
|
2| assert(a ==
| [[0, 1, 2, 3],
| [0, 1, 2, 3],
| [4, 5, 6, 7],
| [4, 5, 6, 7]]);
| }
|
| /++
| Op Assignment `op=` of a regular multidimensional array to a $(B fully defined slice).
| +/
| void opIndexOpAssign(string op, T, Slices...)(T[] value, Slices slices) scope return
| if (isFullPureSlice!Slices
| && (!isDynamicArray!DeepElement || isDynamicArray!T)
| && DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= typeof(this.opIndex(slices)).N)
| {
14| auto sl = this.lightScope.opIndex(slices);
14| sl.opIndexOpAssignImplArray!op(value);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation : slice;
2| auto a = slice!int(2, 3);
|
2| a[0..$, 0..$-1] += [[1, 2], [3, 4]];
2| assert(a == [[1, 2, 0], [3, 4, 0]]);
|
2| a[0..$, 0..$-1] += [1, 2];
2| assert(a == [[2, 4, 0], [4, 6, 0]]);
|
2| a[1, 0..$-1] += [3, 4];
2| assert(a[1] == [7, 10, 0]);
|
2| a[1, 0..$-1][] += [1, 2];
2| assert(a[1] == [8, 12, 0]);
| }
|
| static if (doUnittest)
| /// Packed slices
| @safe pure nothrow
| version(mir_test) unittest
| {
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : blocks;
2| auto a = slice!int(4, 4);
2| a.blocks(2, 2)[] += [[0, 1], [2, 3]];
|
2| assert(a ==
| [[0, 0, 1, 1],
| [0, 0, 1, 1],
| [2, 2, 3, 3],
| [2, 2, 3, 3]]);
| }
|
| private void opIndexOpAssignImplValue(string op, T)(T value) scope return
| {
| static if (N > 1 && kind == Contiguous)
| {
| import mir.ndslice.topology : flattened;
12| this.flattened.opIndexOpAssignImplValue!op(value);
| }
| else
| {
317| auto ls = this;
| do
| {
| static if (N == 1)
| {
| static if (isInstanceOf!(SliceIterator, Iterator))
38| ls.front.opIndexOpAssignImplValue!op(value);
| else
| mixin (`ls.front ` ~ op ~ `= value;`);
| }
| else
125| ls.front.opIndexOpAssignImplValue!op(value);
2410| ls.popFront;
| }
2410| while(ls._lengths[0]);
| }
| }
|
| /++
| Op Assignment `op=` of a value (e.g. a number) to a $(B fully defined slice).
| +/
| void opIndexOpAssign(string op, T, Slices...)(T value, Slices slices) scope return
| if ((isFullPureSlice!Slices || isIndexedSlice!Slices)
| && (!isDynamicArray!T || isDynamicArray!DeepElement)
| && DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement
| && !isSlice!T
| && !isConcatenation!T)
| {
37| auto sl = this.lightScope.opIndex(slices);
37| if(!sl.anyRUEmpty)
37| sl.opIndexOpAssignImplValue!op(value);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
|
2| a[] += 1;
2| assert(a == [[1, 1, 1], [1, 1, 1]]);
|
2| a[0..$, 0..$-1] += 2;
2| assert(a == [[3, 3, 1], [3, 3, 1]]);
|
2| a[1, 0..$-1] += 3;
2| assert(a[1] == [6, 6, 1]);
| }
|
| ///
| void opIndexOpAssign(string op,T, Slices...)(T concatenation, Slices slices) scope return
| if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T)
| {
1| auto sl = this.lightScope.opIndex(slices);
| static assert(typeof(sl).N == concatenation.N);
1| sl.opIndexOpAssignImplConcatenation!op(concatenation);
| }
|
| static if (doUnittest)
| /// Packed slices have the same behavior.
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
| import mir.ndslice.topology : pack;
2| auto a = slice!int(2, 3).pack!1;
|
2| a[] += 9;
2| assert(a == [[9, 9, 9], [9, 9, 9]]);
| }
|
|
| /++
| Increment `++` and Decrement `--` operators for a $(B fully defined index).
| +/
| auto ref opIndexUnary(string op)(size_t[N] _indices...) scope return
| @trusted
| // @@@workaround@@@ for Issue 16473
| //if (op == `++` || op == `--`)
| {
| // check op safety
| static auto ref fun(DeepElement t) @safe
| {
0000000| return mixin(op ~ `t`);
| }
15| return mixin (op ~ `_iterator[indexStride(_indices)]`);
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
|
2| ++a[1, 2];
2| assert(a[1, 2] == 1);
| }
|
| // Issue 16473
| static if (doUnittest)
| @safe pure nothrow version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto sl = slice!double(2, 5);
2| auto d = -sl[0, 1];
| }
|
| static if (doUnittest)
| @safe pure nothrow version(mir_test) unittest
| {
2| auto a = new int[6].sliced(2, 3);
|
2| ++a[[1, 2]];
2| assert(a[[1, 2]] == 1);
| }
|
| private void opIndexUnaryImpl(string op, Slices...)(Slices slices) scope
| {
183| auto ls = this;
| do
| {
| static if (N == 1)
| {
| static if (isInstanceOf!(SliceIterator, Iterator))
| ls.front.opIndexUnaryImpl!op;
| else
| mixin (op ~ `ls.front;`);
| }
| else
149| ls.front.opIndexUnaryImpl!op;
1105| ls.popFront;
| }
1105| while(ls._lengths[0]);
| }
|
| /++
| Increment `++` and Decrement `--` operators for a $(B fully defined slice).
| +/
| void opIndexUnary(string op, Slices...)(Slices slices) scope return
| if (isFullPureSlice!Slices && (op == `++` || op == `--`))
| {
34| auto sl = this.lightScope.opIndex(slices);
34| if (!sl.anyRUEmpty)
34| sl.opIndexUnaryImpl!op;
| }
|
| static if (doUnittest)
| ///
| @safe pure nothrow
| version(mir_test) unittest
| {
| import mir.ndslice.allocation;
2| auto a = slice!int(2, 3);
|
2| ++a[];
2| assert(a == [[1, 1, 1], [1, 1, 1]]);
|
2| --a[1, 0..$-1];
|
2| assert(a[1] == [0, 0, 1]);
| }
| }
|}
|
|/// ditto
|alias Slice = mir_slice;
|
|/++
|Slicing, indexing, and arithmetic operations.
|+/
|pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.allocation;
| import mir.ndslice.dynamic : transposed;
| import mir.ndslice.topology : iota, universal;
1| auto tensor = iota(3, 4, 5).slice;
|
1| assert(tensor[1, 2] == tensor[1][2]);
1| assert(tensor[1, 2, 3] == tensor[1][2][3]);
|
1| assert( tensor[0..$, 0..$, 4] == tensor.universal.transposed!2[4]);
1| assert(&tensor[0..$, 0..$, 4][1, 2] is &tensor[1, 2, 4]);
|
1| tensor[1, 2, 3]++; //`opIndex` returns value by reference.
1| --tensor[1, 2, 3]; //`opUnary`
|
1| ++tensor[];
1| tensor[] -= 1;
|
| // `opIndexAssing` accepts only fully defined indices and slices.
| // Use an additional empty slice `[]`.
| static assert(!__traits(compiles, tensor[0 .. 2] *= 2));
|
1| tensor[0 .. 2][] *= 2; //OK, empty slice
1| tensor[0 .. 2, 3, 0..$] /= 2; //OK, 3 index or slice positions are defined.
|
| //fully defined index may be replaced by a static array
1| size_t[3] index = [1, 2, 3];
1| assert(tensor[index] == tensor[1, 2, 3]);
|}
|
|/++
|Operations with rvalue slices.
|+/
|pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.allocation;
| import mir.ndslice.topology: universal;
| import mir.ndslice.dynamic: transposed, everted;
|
1| auto tensor = slice!int(3, 4, 5).universal;
1| auto matrix = slice!int(3, 4).universal;
1| auto vector = slice!int(3);
|
12| foreach (i; 0..3)
3| vector[i] = i;
|
| // fills matrix columns
1| matrix.transposed[] = vector;
|
| // fills tensor with vector
| // transposed tensor shape is (4, 5, 3)
| // vector shape is ( 3)
1| tensor.transposed!(1, 2)[] = vector;
|
| // transposed tensor shape is (5, 3, 4)
| // matrix shape is ( 3, 4)
1| tensor.transposed!2[] += matrix;
|
| // transposed tensor shape is (5, 4, 3)
| // transposed matrix shape is ( 4, 3)
1| tensor.everted[] ^= matrix.transposed; // XOR
|}
|
|/++
|Creating a slice from text.
|See also $(MREF std, format).
|+/
|version(mir_test) unittest
|{
| import mir.algorithm.iteration: filter, all;
| import mir.array.allocation;
| import mir.exception;
| import mir.functional: not;
| import mir.ndslice.allocation;
| import mir.parse;
| import mir.primitives: empty;
|
| import std.algorithm: map;
| import std.string: lineSplitter, split;
|
| // std.functional, std.string, std.range;
|
| Slice!(int*, 2) toMatrix(string str)
| {
1| string[][] data = str.lineSplitter.filter!(not!empty).map!split.array;
|
1| size_t rows = data .length.enforce!"empty input";
1| size_t columns = data[0].length.enforce!"empty first row";
|
3| data.all!(a => a.length == columns).enforce!"rows have different lengths";
1| auto slice = slice!int(rows, columns);
11| foreach (i, line; data)
30| foreach (j, num; line)
6| slice[i, j] = num.fromString!int;
1| return slice;
| }
|
1| auto input = "\r1 2 3\r\n 4 5 6\n";
|
1| auto matrix = toMatrix(input);
1| assert(matrix == [[1, 2, 3], [4, 5, 6]]);
|
| // back to text
| import std.format;
1| auto text2 = format("%(%(%s %)\n%)\n", matrix);
1| assert(text2 == "1 2 3\n4 5 6\n");
|}
|
|// Slicing
|@safe @nogc pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
1| auto a = iota(10, 20, 30, 40);
1| auto b = a[0..$, 10, 4 .. 27, 4];
1| auto c = b[2 .. 9, 5 .. 10];
1| auto d = b[3..$, $-2];
1| assert(b[4, 17] == a[4, 10, 21, 4]);
1| assert(c[1, 2] == a[3, 10, 11, 4]);
1| assert(d[3] == a[6, 10, 25, 4]);
|}
|
|// Operator overloading. # 1
|pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.allocation;
| import mir.ndslice.topology : iota;
|
1| auto fun(ref sizediff_t x) { x *= 3; }
|
1| auto tensor = iota(8, 9, 10).slice;
|
1| ++tensor[];
1| fun(tensor[0, 0, 0]);
|
1| assert(tensor[0, 0, 0] == 3);
|
1| tensor[0, 0, 0] *= 4;
1| tensor[0, 0, 0]--;
1| assert(tensor[0, 0, 0] == 11);
|}
|
|// Operator overloading. # 2
|pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.topology: map, iota;
| import mir.array.allocation : array;
| //import std.bigint;
|
1| auto matrix = 72
| .iota
| //.map!(i => BigInt(i))
| .array
| .sliced(8, 9);
|
1| matrix[3 .. 6, 2] += 100;
27| foreach (i; 0 .. 8)
240| foreach (j; 0 .. 9)
144| if (i >= 3 && i < 6 && j == 2)
3| assert(matrix[i, j] >= 100);
| else
69| assert(matrix[i, j] < 100);
|}
|
|// Operator overloading. # 3
|pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.allocation;
| import mir.ndslice.topology : iota;
|
1| auto matrix = iota(8, 9).slice;
1| matrix[] = matrix;
1| matrix[] += matrix;
1| assert(matrix[2, 3] == (2 * 9 + 3) * 2);
|
1| auto vec = iota([9], 100);
1| matrix[] = vec;
26| foreach (v; matrix)
8| assert(v == vec);
|
1| matrix[] += vec;
26| foreach (vector; matrix)
232| foreach (elem; vector)
72| assert(elem >= 200);
|}
|
|// Type deduction
|version(mir_test) unittest
|{
| // Arrays
| foreach (T; AliasSeq!(int, const int, immutable int))
| static assert(is(typeof((T[]).init.sliced(3, 4)) == Slice!(T*, 2)));
|
| // Container Array
| import std.container.array;
2| Array!int ar;
1| ar.length = 12;
2| auto arSl = ar[].slicedField(3, 4);
|}
|
|// Test for map #1
|version(mir_test) unittest
|{
| import mir.ndslice.topology: map, byDim;
1| auto slice = [1, 2, 3, 4].sliced(2, 2);
|
17| auto r = slice.byDim!0.map!(a => a.map!(a => a * 6));
1| assert(r.front.front == 6);
1| assert(r.front.back == 12);
1| assert(r.back.front == 18);
1| assert(r.back.back == 24);
1| assert(r[0][0] == 6);
1| assert(r[0][1] == 12);
1| assert(r[1][0] == 18);
1| assert(r[1][1] == 24);
|
| import std.range.primitives;
| static assert(hasSlicing!(typeof(r)));
| static assert(isForwardRange!(typeof(r)));
| static assert(isRandomAccessRange!(typeof(r)));
|}
|
|// Test for map #2
|version(mir_test) unittest
|{
| import mir.ndslice.topology: map, byDim;
| import std.range.primitives;
1| auto data = [1, 2, 3, 4];
| static assert(hasSlicing!(typeof(data)));
| static assert(isForwardRange!(typeof(data)));
| static assert(isRandomAccessRange!(typeof(data)));
1| auto slice = data.sliced(2, 2);
| static assert(hasSlicing!(typeof(slice)));
| static assert(isForwardRange!(typeof(slice)));
| static assert(isRandomAccessRange!(typeof(slice)));
17| auto r = slice.byDim!0.map!(a => a.map!(a => a * 6));
| static assert(hasSlicing!(typeof(r)));
| static assert(isForwardRange!(typeof(r)));
| static assert(isRandomAccessRange!(typeof(r)));
1| assert(r.front.front == 6);
1| assert(r.front.back == 12);
1| assert(r.back.front == 18);
1| assert(r.back.back == 24);
1| assert(r[0][0] == 6);
1| assert(r[0][1] == 12);
1| assert(r[1][0] == 18);
1| assert(r[1][1] == 24);
|}
|
|private enum bool isType(alias T) = false;
|
|private enum bool isType(T) = true;
|
|private enum isStringValue(alias T) = is(typeof(T) : string);
|
|
|private bool _checkAssignLengths(
| LIterator, RIterator,
| size_t LN, size_t RN,
| SliceKind lkind, SliceKind rkind,
| )
| (Slice!(LIterator, LN, lkind) ls,
| Slice!(RIterator, RN, rkind) rs)
|{
| static if (isInstanceOf!(SliceIterator, LIterator))
| {
| import mir.ndslice.topology: unpack;
8| return _checkAssignLengths(ls.unpack, rs);
| }
| else
| static if (isInstanceOf!(SliceIterator, RIterator))
| {
| import mir.ndslice.topology: unpack;
4| return _checkAssignLengths(ls, rs.unpack);
| }
| else
| {
| foreach (i; Iota!(0, RN))
1134| if (ls._lengths[i + LN - RN] != rs._lengths[i])
3| return false;
1083| return true;
| }
|}
|
|@safe pure nothrow @nogc version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
|
1| assert(_checkAssignLengths(iota(2, 2), iota(2, 2)));
1| assert(!_checkAssignLengths(iota(2, 2), iota(2, 3)));
1| assert(!_checkAssignLengths(iota(2, 2), iota(3, 2)));
1| assert(!_checkAssignLengths(iota(2, 2), iota(3, 3)));
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto slice = new int[15].slicedField(5, 3);
|
| /// Fully defined slice
1| assert(slice[] == slice);
1| auto sublice = slice[0..$-2, 1..$];
|
| /// Partially defined slice
1| auto row = slice[3];
1| auto col = slice[0..$, 1];
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
1| auto b = [1, 2, 3, 4].sliced(2, 2);
|
1| a[0..$, 0..$-1] = b;
1| assert(a == [[1, 2, 0], [3, 4, 0]]);
|
1| a[0..$, 0..$-1] = b[0];
1| assert(a == [[1, 2, 0], [1, 2, 0]]);
|
1| a[1, 0..$-1] = b[1];
1| assert(a[1] == [3, 4, 0]);
|
1| a[1, 0..$-1][] = b[0];
1| assert(a[1] == [1, 2, 0]);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
1| auto b = [[1, 2], [3, 4]];
|
1| a[] = [[1, 2, 3], [4, 5, 6]];
1| assert(a == [[1, 2, 3], [4, 5, 6]]);
|
1| a[0..$, 0..$-1] = [[1, 2], [3, 4]];
1| assert(a == [[1, 2, 3], [3, 4, 6]]);
|
1| a[0..$, 0..$-1] = [1, 2];
1| assert(a == [[1, 2, 3], [1, 2, 6]]);
|
1| a[1, 0..$-1] = [3, 4];
1| assert(a[1] == [3, 4, 6]);
|
1| a[1, 0..$-1][] = [3, 4];
1| assert(a[1] == [3, 4, 6]);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| a[] = 9;
| //assert(a == [[9, 9, 9], [9, 9, 9]]);
|
1| a[0..$, 0..$-1] = 1;
| //assert(a == [[1, 1, 9], [1, 1, 9]]);
|
1| a[0..$, 0..$-1] = 2;
| //assert(a == [[2, 2, 9], [2, 2, 9]]);
|
1| a[1, 0..$-1] = 3;
| //assert(a[1] == [3, 3, 9]);
|
1| a[1, 0..$-1] = 4;
| //assert(a[1] == [4, 4, 9]);
|
1| a[1, 0..$-1][] = 5;
| //assert(a[1] == [5, 5, 9]);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| a[1, 2] = 3;
1| assert(a[1, 2] == 3);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| a[[1, 2]] = 3;
1| assert(a[[1, 2]] == 3);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| a[1, 2] += 3;
1| assert(a[1, 2] == 3);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| a[[1, 2]] += 3;
1| assert(a[[1, 2]] == 3);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
1| auto b = [1, 2, 3, 4].sliced(2, 2);
|
1| a[0..$, 0..$-1] += b;
1| assert(a == [[1, 2, 0], [3, 4, 0]]);
|
1| a[0..$, 0..$-1] += b[0];
1| assert(a == [[2, 4, 0], [4, 6, 0]]);
|
1| a[1, 0..$-1] += b[1];
1| assert(a[1] == [7, 10, 0]);
|
1| a[1, 0..$-1][] += b[0];
1| assert(a[1] == [8, 12, 0]);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| a[0..$, 0..$-1] += [[1, 2], [3, 4]];
1| assert(a == [[1, 2, 0], [3, 4, 0]]);
|
1| a[0..$, 0..$-1] += [1, 2];
1| assert(a == [[2, 4, 0], [4, 6, 0]]);
|
1| a[1, 0..$-1] += [3, 4];
1| assert(a[1] == [7, 10, 0]);
|
1| a[1, 0..$-1][] += [1, 2];
1| assert(a[1] == [8, 12, 0]);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| a[] += 1;
1| assert(a == [[1, 1, 1], [1, 1, 1]]);
|
1| a[0..$, 0..$-1] += 2;
1| assert(a == [[3, 3, 1], [3, 3, 1]]);
|
1| a[1, 0..$-1] += 3;
1| assert(a[1] == [6, 6, 1]);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| ++a[1, 2];
1| assert(a[1, 2] == 1);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| ++a[[1, 2]];
1| assert(a[[1, 2]] == 1);
|}
|
|pure nothrow version(mir_test) unittest
|{
1| auto a = new int[6].slicedField(2, 3);
|
1| ++a[];
1| assert(a == [[1, 1, 1], [1, 1, 1]]);
|
1| --a[1, 0..$-1];
1| assert(a[1] == [0, 0, 1]);
|}
|
|version(mir_test) unittest
|{
| import mir.ndslice.topology: iota, universal;
|
1| auto sl = iota(3, 4).universal;
1| assert(sl[0 .. $] == sl);
|}
|
|version(mir_test) unittest
|{
| import mir.ndslice.topology: canonical, iota;
| static assert(kindOf!(typeof(iota([1, 2]).canonical[1])) == Contiguous);
|}
|
|version(mir_test) unittest
|{
| import mir.ndslice.topology: iota;
1| auto s = iota(2, 3);
1| assert(s.front!1 == [0, 3]);
1| assert(s.back!1 == [2, 5]);
|}
|
|/++
|Assignment utility for generic code that works both with scalars and with ndslices.
|Params:
| op = assign operation (generic, optional)
| lside = left side
| rside = right side
|Returns:
| expression value
|+/
|auto ndassign(string op = "", L, R)(ref L lside, auto ref R rside) @property
| if (!isSlice!L && (op.length == 0 || op[$-1] != '='))
|{
1| return mixin(`lside ` ~ op ~ `= rside`);
|}
|
|/// ditto
|auto ndassign(string op = "", L, R)(L lside, auto ref R rside) @property
| if (isSlice!L && (op.length == 0 || op[$-1] != '='))
|{
| static if (op == "")
2| return lside.opIndexAssign(rside);
| else
2| return lside.opIndexOpAssign!op(rside);
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.topology: iota;
| import mir.ndslice.allocation: slice;
1| auto scalar = 3;
1| auto vector = 3.iota.slice; // [0, 1, 2]
|
| // scalar = 5;
1| scalar.ndassign = 5;
1| assert(scalar == 5);
|
| // vector[] = vector * 2;
1| vector.ndassign = vector * 2;
1| assert(vector == [0, 2, 4]);
|
| // vector[] += scalar;
1| vector.ndassign!"+"= scalar;
1| assert(vector == [5, 7, 9]);
|}
|
|version(mir_test) pure nothrow unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: universal;
|
1| auto df = slice!(double, int, int)(2, 3).universal;
1| df.label[] = [1, 2];
1| df.label!1[] = [1, 2, 3];
1| auto lsdf = df.lightScope;
1| assert(lsdf.label!0[0] == 1);
1| assert(lsdf.label!1[1] == 2);
|
1| auto immdf = (cast(immutable)df).lightImmutable;
1| assert(immdf.label!0[0] == 1);
1| assert(immdf.label!1[1] == 2);
|
1| auto constdf = df.lightConst;
1| assert(constdf.label!0[0] == 1);
1| assert(constdf.label!1[1] == 2);
|
1| auto constdf2 = df.toConst;
1| assert(constdf2.label!0[0] == 1);
1| assert(constdf2.label!1[1] == 2);
|
1| auto immdf2 = (cast(immutable)df).toImmutable;
1| assert(immdf2.label!0[0] == 1);
1| assert(immdf2.label!1[1] == 2);
|}
|
|version(mir_test) pure nothrow unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: universal;
|
1| auto df = slice!(double, int, int)(2, 3).universal;
1| df[] = 5;
|
1| Slice!(double*, 2, Universal) values = df.values;
1| assert(values[0][0] == 5);
1| Slice!(LightConstOf!(double*), 2, Universal) constvalues = df.values;
1| assert(constvalues[0][0] == 5);
1| Slice!(LightImmutableOf!(double*), 2, Universal) immvalues = (cast(immutable)df).values;
1| assert(immvalues[0][0] == 5);
|}
|
|version(mir_test) @safe unittest
|{
| import mir.ndslice.allocation;
2| auto a = rcslice!double([2, 3], 0);
2| auto b = rcslice!double([2, 3], 0);
1| a[1, 2] = 3;
1| b[] = a;
1| assert(a == b);
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened;
|
1| auto m = iota(2, 3, 4); // Contiguous Matrix
1| auto mFlat = m.flattened;
|
50| for (size_t i = 0; i < m.elementCount; i++) {
24| assert(m.accessFlat(i) == mFlat[i]);
| }
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened;
|
1| auto m = iota(3, 4); // Contiguous Matrix
1| auto x = m.front; // Contiguous Vector
|
10| for (size_t i = 0; i < x.elementCount; i++) {
4| assert(x.accessFlat(i) == m[0, i]);
| }
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened;
|
1| auto m = iota(3, 4); // Contiguous Matrix
1| auto x = m[0 .. $, 0 .. $ - 1]; // Canonical Matrix
1| auto xFlat = x.flattened;
|
20| for (size_t i = 0; i < x.elementCount; i++) {
9| assert(x.accessFlat(i) == xFlat[i]);
| }
|}
|
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened;
|
1| auto m = iota(2, 3, 4); // Contiguous Matrix
1| auto x = m[0 .. $, 0 .. $, 0 .. $ - 1]; // Canonical Matrix
1| auto xFlat = x.flattened;
|
38| for (size_t i = 0; i < x.elementCount; i++) {
18| assert(x.accessFlat(i) == xFlat[i]);
| }
|}
|
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened;
| import mir.ndslice.dynamic: transposed;
|
1| auto m = iota(2, 3, 4); // Contiguous Matrix
1| auto x = m.transposed!(2, 1, 0); // Universal Matrix
1| auto xFlat = x.flattened;
|
50| for (size_t i = 0; i < x.elementCount; i++) {
24| assert(x.accessFlat(i) == xFlat[i]);
| }
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened;
| import mir.ndslice.dynamic: transposed;
|
1| auto m = iota(3, 4); // Contiguous Matrix
1| auto x = m.transposed; // Universal Matrix
1| auto xFlat = x.flattened;
|
26| for (size_t i = 0; i < x.elementCount; i++) {
12| assert(x.accessFlat(i) == xFlat[i]);
| }
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened, diagonal;
|
1| auto m = iota(3, 4); // Contiguous Matrix
1| auto x = m.diagonal; // Universal Vector
|
8| for (size_t i = 0; i < x.elementCount; i++) {
3| assert(x.accessFlat(i) == m[i, i]);
| }
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| import mir.ndslice.topology: iota, flattened;
|
1| auto m = iota(3, 4); // Contiguous Matrix
1| auto x = m.front!1; // Universal Vector
|
8| for (size_t i = 0; i < x.elementCount; i++) {
3| assert(x.accessFlat(i) == m[i, 0]);
| }
|}
|
|version(mir_test)
|@safe pure @nogc nothrow
|unittest // check it can be compiled
|{
| import mir.algebraic;
| alias S = Slice!(double*, 2);
| alias D = Variant!S;
|}
|
|version(mir_test)
|unittest
|{
| import mir.ndslice;
|
1| auto matrix = slice!short(3, 4);
1| matrix[] = 0;
1| matrix.diagonal[] = 1;
|
1| auto row = matrix[2];
1| row[3] = 6;
1| assert(matrix[2, 3] == 6); // D & C index order
|}
source/mir/ndslice/slice.d is 97% covered
<<<<<< EOF
# path=./source-mir-interpolate-spline.lst
|/++
|$(H2 Cubic Spline Interpolation)
|
|The module provides common C2 splines, monotone (PCHIP) splines, Akima splines and others.
|
|See_also: $(LREF SplineType), $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.interpolate.spline;
|
|import core.lifetime: move;
|import mir.functional;
|import mir.internal.utility;
|import mir.interpolate;
|import mir.interpolate: Repeat;
|import mir.math.common;
|import mir.ndslice.slice;
|import mir.primitives;
|import mir.rc.array;
|import mir.utility: min, max;
|import std.meta: AliasSeq, staticMap;
|import std.traits: Unqual;
|public import mir.interpolate: atInterval;
|
|@fmamath:
|
|///
|@safe pure @nogc version(mir_test) unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.allocation: rcslice;
| import mir.ndslice.topology: vmap;
|
| static immutable xdata = [-1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22];
2| auto x = xdata.rcslice;
| static immutable ydata = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
1| auto y = ydata.sliced;
|
2| auto interpolant = spline!double(x, y); // constructs Spline
2| auto xs = x + 0.5; // input X values for cubic spline
|
| static immutable test_data0 = [
| -0.68361541, 7.28568719, 10.490694 , 0.36192032,
| 11.91572713, 16.44546433, 17.66699525, 4.52730869,
| 19.22825394, -2.3242592 ];
| /// not-a-knot (default)
1| assert(xs.vmap(interpolant).all!approxEqual(test_data0));
|
| static immutable test_data1 = [
| 10.85298372, 5.26255911, 10.71443229, 0.1824536 ,
| 11.94324989, 16.45633939, 17.59185094, 4.86340188,
| 17.8565408 , 2.81856494];
| /// natural cubic spline
1| interpolant = spline!double(x, y, SplineBoundaryType.secondDerivative);
1| assert(xs.vmap(interpolant).all!approxEqual(test_data1));
|
| static immutable test_data2 = [
| 9.94191781, 5.4223652 , 10.69666392, 0.1971149 , 11.93868415,
| 16.46378847, 17.56521661, 4.97656997, 17.39645585, 4.54316446];
| /// set both boundary second derivatives to 3
1| interpolant = spline!double(x, y, SplineBoundaryType.secondDerivative, 3);
1| assert(xs.vmap(interpolant).all!approxEqual(test_data2));
|
| static immutable test_data3 = [
| 16.45728263, 4.27981687, 10.82295092, 0.09610695,
| 11.95252862, 16.47583126, 17.49964521, 5.26561539,
| 16.21803478, 8.96104974];
| /// set both boundary derivatives to 3
1| interpolant = spline!double(x, y, SplineBoundaryType.firstDerivative, 3);
1| assert(xs.vmap(interpolant).all!approxEqual(test_data3));
|
| static immutable test_data4 = [
| 16.45730084, 4.27966112, 10.82337171, 0.09403945,
| 11.96265209, 16.44067375, 17.6374694 , 4.67438921,
| 18.6234452 , -0.05582876];
| /// different boundary conditions
1| interpolant = spline!double(x, y,
| SplineBoundaryCondition!double(SplineBoundaryType.firstDerivative, 3),
| SplineBoundaryCondition!double(SplineBoundaryType.secondDerivative, -5));
1| assert(xs.vmap(interpolant).all!approxEqual(test_data4));
|
|
| static immutable test_data5 = [
| 12.37135558, 4.99638066, 10.74362441, 0.16008641,
| 11.94073593, 16.47908148, 17.49841853, 5.26600921,
| 16.21796051, 8.96102894];
| // ditto
1| interpolant = spline!double(x, y,
| SplineBoundaryCondition!double(SplineBoundaryType.secondDerivative, -5),
| SplineBoundaryCondition!double(SplineBoundaryType.firstDerivative, 3));
1| assert(xs.vmap(interpolant).all!approxEqual(test_data5));
|
| static immutable test_data6 = [
| 11.40871379, 2.64278898, 9.55774317, 4.84791141, 11.24842121,
| 16.16794267, 18.58060557, 5.2531411 , 17.45509005, 1.86992521];
| /// Akima spline
1| interpolant = spline!double(x, y, SplineType.akima);
1| assert(xs.vmap(interpolant).all!approxEqual(test_data6));
|
| /// Double Quadratic spline
1| interpolant = spline!double(x, y, SplineType.doubleQuadratic);
| import mir.interpolate.utility: ParabolaKernel;
1| auto kernel1 = ParabolaKernel!double(x[2], x[3], x[4], y[2], y[3], y[4]);
1| auto kernel2 = ParabolaKernel!double( x[3], x[4], x[5], y[3], y[4], y[5]);
| // weighted sum of quadratic functions
1| auto c = 0.35; // from [0 .. 1]
1| auto xp = c * x[3] + (1 - c) * x[4];
1| auto yp = c * kernel1(xp) + (1 - c) * kernel2(xp);
1| assert(interpolant(xp).approxEqual(yp));
| // check parabolic extrapolation of the boundary intervals
1| kernel1 = ParabolaKernel!double(x[0], x[1], x[2], y[0], y[1], y[2]);
1| kernel2 = ParabolaKernel!double(x[$ - 3], x[$ - 2], x[$ - 1], y[$ - 3], y[$ - 2], y[$ - 1]);
1| assert(interpolant(x[0] - 23.421).approxEqual(kernel1(x[0] - 23.421)));
1| assert(interpolant(x[$ - 1] + 23.421).approxEqual(kernel2(x[$ - 1] + 23.421)));
|}
|
|///
|@safe pure version(mir_test) unittest
|{
| import mir.rc.array: rcarray;
| import mir.algorithm.iteration: all;
| import mir.functional: aliasCall;
| import mir.math.common: approxEqual;
| import mir.ndslice.allocation: uninitSlice;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: vmap, map;
|
2| auto x = rcarray!(immutable double)(-1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22).asSlice;
2| auto y = rcarray(
| 8.77842512,
| 7.96429686,
| 7.77074363,
| 1.10838032,
| 2.69925191,
| 1.84922654,
| 1.48167283,
| 2.8267636 ,
| 0.40200172,
| 7.78980608).asSlice;
|
2| auto interpolant = x.spline!double(y); // default boundary condition is 'not-a-knot'
|
2| auto xs = x + 0.5;
|
2| auto ys = xs.vmap(interpolant);
|
1| auto r =
| [5.56971848,
| 9.30342403,
| 4.44139761,
| -0.74740285,
| 3.00994108,
| 1.50750417,
| 1.73144979,
| 2.64860361,
| 0.64413911,
| 10.81768928];
|
1| assert(all!approxEqual(ys, r));
|
| // first derivative
2| auto d1 = xs.vmap(interpolant.aliasCall!"withDerivative").map!"a[1]";
1| auto r1 =
| [-4.51501279,
| 2.15715986,
| -7.28363308,
| -2.14050449,
| 0.03693092,
| -0.49618999,
| 0.58109933,
| -0.52926703,
| 0.7819035 ,
| 6.70632693];
1| assert(all!approxEqual(d1, r1));
|
| // second derivative
2| auto d2 = xs.vmap(interpolant.aliasCall!"withTwoDerivatives").map!"a[2]";
1| auto r2 =
| [7.07104751,
| -2.62293241,
| -0.01468508,
| 5.70609505,
| -2.02358911,
| 0.72142061,
| 0.25275483,
| -0.6133589 ,
| 1.26894416,
| 2.68067146];
1| assert(all!approxEqual(d2, r2));
|
| // third derivative (6 * a)
2| auto d3 = xs.vmap(interpolant.aliasCall!("opCall", 3)).map!"a[3]";
1| auto r3 =
| [-3.23132664,
| -3.23132664,
| 14.91047457,
| -3.46891432,
| 1.88520325,
| -0.16559031,
| -0.44056064,
| 0.47057577,
| 0.47057577,
| 0.47057577];
1| assert(all!approxEqual(d3, r3));
|}
|
|/// R -> R: Cubic interpolation
|version(mir_test)
|@safe unittest
|{
| import mir.algorithm.iteration: all;
| import mir.math.common: approxEqual;
| import mir.ndslice;
|
| static immutable x = [0, 1, 2, 3, 5.00274, 7.00274, 10.0055, 20.0137, 30.0192];
| static immutable y = [0.0011, 0.0011, 0.0030, 0.0064, 0.0144, 0.0207, 0.0261, 0.0329, 0.0356,];
1| auto xs = [1, 2, 3, 4.00274, 5.00274, 6.00274, 7.00274, 8.00548, 9.00548, 10.0055, 11.0055, 12.0082, 13.0082, 14.0082, 15.0082, 16.011, 17.011, 18.011, 19.011, 20.0137, 21.0137, 22.0137, 23.0137, 24.0164, 25.0164, 26.0164, 27.0164, 28.0192, 29.0192, 30.0192];
|
2| auto interpolation = spline!double(x.rcslice, y.sliced);
|
1| auto data =
| [ 0.0011 , 0.003 , 0.0064 , 0.01042622, 0.0144 ,
| 0.01786075, 0.0207 , 0.02293441, 0.02467983, 0.0261 ,
| 0.02732764, 0.02840225, 0.0293308 , 0.03012914, 0.03081002,
| 0.03138766, 0.03187161, 0.03227637, 0.03261468, 0.0329 ,
| 0.03314357, 0.03335896, 0.03355892, 0.03375674, 0.03396413,
| 0.03419436, 0.03446018, 0.03477529, 0.03515072, 0.0356 ];
|
1| assert(all!approxEqual(xs.sliced.vmap(interpolation), data));
|}
|
|/// R^2 -> R: Bicubic interpolation
|version(mir_test)
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice;
21| alias appreq = (a, b) => approxEqual(a, b, 10e-10, 10e-10);
|
| ///// set test function ////
1| const double y_x0 = 2;
1| const double y_x1 = -7;
1| const double y_x0x1 = 3;
|
| // this function should be approximated very well
41| alias f = (x0, x1) => y_x0 * x0 + y_x1 * x1 + y_x0x1 * x0 * x1 - 11;
|
| ///// set interpolant ////
| static immutable x0 = [-1.0, 2, 8, 15];
| static immutable x1 = [-4.0, 2, 5, 10, 13];
1| auto grid = cartesian(x0, x1);
|
2| auto interpolant = spline!(double, 2)(x0.rcslice, x1.rcslice, grid.map!f);
|
| ///// compute test data ////
1| auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23);
| // auto test_grid = cartesian(x0 + 0, x1 + 0);
1| auto real_data = test_grid.map!f;
2| auto interp_data = test_grid.vmap(interpolant);
|
| ///// verify result ////
1| assert(all!appreq(interp_data, real_data));
|
| //// check derivatives ////
1| auto z0 = 1.23;
1| auto z1 = 3.21;
| // writeln("-----------------");
| // writeln("-----------------");
1| auto d = interpolant.withDerivative(z0, z1);
1| assert(appreq(interpolant(z0, z1), f(z0, z1)));
| // writeln("d = ", d);
| // writeln("interpolant.withTwoDerivatives(z0, z1) = ", interpolant.withTwoDerivatives(z0, z1));
| // writeln("-----------------");
| // writeln("-----------------");
| // writeln("interpolant(z0, z1) = ", interpolant(z0, z1));
| // writeln("y_x0 + y_x0x1 * z1 = ", y_x0 + y_x0x1 * z1);
| // writeln("y_x1 + y_x0x1 * z0 = ", y_x1 + y_x0x1 * z0);
| // writeln("-----------------");
| // writeln("-----------------");
| // assert(appreq(d[0][0], f(z0, z1)));
| // assert(appreq(d[1][0], y_x0 + y_x0x1 * z1));
| // assert(appreq(d[0][1], y_x1 + y_x0x1 * z0));
| // assert(appreq(d[1][1], y_x0x1));
|}
|
|/// R^3 -> R: Tricubic interpolation
|version(mir_test)
|unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice;
63| alias appreq = (a, b) => approxEqual(a, b, 10e-10, 10e-10);
|
| ///// set test function ////
| enum y_x0 = 2;
| enum y_x1 = -7;
| enum y_x2 = 5;
| enum y_x0x1 = 10;
| enum y_x0x1x2 = 3;
|
| // this function should be approximated very well
123| alias f = (x0, x1, x2) => y_x0 * x0 + y_x1 * x1 + y_x2 * x2
| + y_x0x1 * x0 * x1 + y_x0x1x2 * x0 * x1 * x2 - 11;
|
| ///// set interpolant ////
| static immutable x0 = [-1.0, 2, 8, 15];
| static immutable x1 = [-4.0, 2, 5, 10, 13];
| static immutable x2 = [3, 3.7, 5];
1| auto grid = cartesian(x0, x1, x2);
|
2| auto interpolant = spline!(double, 3)(x0.rcslice, x1.rcslice, x2.rcslice, grid.map!f);
|
| ///// compute test data ////
1| auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23, x2.sliced - 3);
1| auto real_data = test_grid.map!f;
2| auto interp_data = test_grid.vmap(interpolant);
|
| ///// verify result ////
1| assert(all!appreq(interp_data, real_data));
|
| //// check derivatives ////
1| auto z0 = 1.23;
1| auto z1 = 3.23;
1| auto z2 = -3;
1| auto d = interpolant.withDerivative(z0, z1, z2);
1| assert(appreq(interpolant(z0, z1, z2), f(z0, z1, z2)));
1| assert(appreq(d[0][0][0], f(z0, z1, z2)));
|
| // writeln("-----------------");
| // writeln("-----------------");
| // auto d = interpolant.withDerivative(z0, z1);
1| assert(appreq(interpolant(z0, z1, z2), f(z0, z1, z2)));
| // writeln("interpolant(z0, z1, z2) = ", interpolant(z0, z1, z2));
| // writeln("d = ", d);
| // writeln("interpolant.withTwoDerivatives(z0, z1, z2) = ", interpolant.withTwoDerivatives(z0, z1, z2));
| // writeln("-----------------");
| // writeln("-----------------");
| // writeln("interpolant(z0, z1) = ", interpolant(z0, z1));
| // writeln("y_x0 + y_x0x1 * z1 = ", y_x0 + y_x0x1 * z1);
| // writeln("y_x1 + y_x0x1 * z0 = ", y_x1 + y_x0x1 * z0);
| // writeln("-----------------");
| // writeln("-----------------");
|
| // writeln("y_x0 + y_x0x1 * z1 + y_x0x1x2 * z1 * z2 = ", y_x0 + y_x0x1 * z1 + y_x0x1x2 * z1 * z2);
| // assert(appreq(d[1][0][0], y_x0 + y_x0x1 * z1 + y_x0x1x2 * z1 * z2));
| // writeln("y_x1 + y_x0x1 * z0 + y_x0x1x2 * z0 * z2 = ", y_x1 + y_x0x1 * z0 + y_x0x1x2 * z0 * z2);
| // assert(appreq(d[0][1][0], y_x1 + y_x0x1 * z0 + y_x0x1x2 * z0 * z2));
| // writeln("y_x0x1 + y_x0x1x2 * z2 = ", y_x0x1 + y_x0x1x2 * z2);
| // assert(appreq(d[1][1][0], y_x0x1 + y_x0x1x2 * z2));
| // writeln("y_x0x1x2 = ", y_x0x1x2);
| // assert(appreq(d[1][1][1], y_x0x1x2));
|}
|
|
|/// Monotone PCHIP
|version(mir_test)
|@safe unittest
|{
| import mir.math.common: approxEqual;
| import mir.algorithm.iteration: all;
| import mir.ndslice.allocation: rcslice;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: vmap;
|
| static immutable x = [1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22];
| static immutable y = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
2| auto interpolant = spline!double(x.rcslice, y.sliced, SplineType.monotone);
|
1| auto xs = x[0 .. $ - 1].sliced + 0.5;
|
2| auto ys = xs.vmap(interpolant);
|
1| assert(ys.all!approxEqual([
| 5.333333333333334,
| 2.500000000000000,
| 10.000000000000000,
| 4.288971807628524,
| 11.202580845771145,
| 16.250000000000000,
| 17.962962962962962,
| 5.558593750000000,
| 17.604662698412699,
| ]));
|}
|
|// Check direction equality
|version(mir_test)
|@safe unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.allocation: rcslice;
| import mir.ndslice.topology: retro, vmap;
|
| static immutable points = [1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22];
| static immutable values = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
|
1| auto results = [
| 5.333333333333334,
| 2.500000000000000,
| 10.000000000000000,
| 4.288971807628524,
| 11.202580845771145,
| 16.250000000000000,
| 17.962962962962962,
| 5.558593750000000,
| 17.604662698412699,
| ];
2| auto interpolant = spline!double(points.rcslice, values.sliced, SplineType.monotone);
|
2| auto pointsR = rcslice(-points.retro);
2| auto valuesR = values.retro.rcslice;
2| auto interpolantR = spline!double(pointsR, valuesR, SplineType.monotone);
|
| version(X86_64)
| assert(vmap(points[0 .. $ - 1].sliced + 0.5, interpolant) == vmap(pointsR.retro[0 .. $ - 1] - 0.5, interpolantR));
|}
|
|/++
|Cubic Spline types.
|
|The first derivatives are guaranteed to be continuous for all cubic splines.
|+/
|extern(C++, "mir", "interpolate")
|enum SplineType
|{
| /++
| Spline with contiguous second derivative.
| +/
| c2,
| /++
| $(HTTPS en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline, Cardinal) and Catmull–Rom splines.
| +/
| cardinal,
| /++
| The interpolant preserves monotonicity in the interpolation data and does not overshoot if the data is not smooth.
| It is also known as $(HTTPS docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.interpolate.PchipInterpolator.html, PCHIP)
| in numpy and Matlab.
| +/
| monotone,
| /++
| Weighted sum of two nearbor quadratic functions.
| It is used in $(HTTPS s3-eu-west-1.amazonaws.com/og-public-downloads/smile-interpolation-extrapolation.pdf, financial analysis).
| +/
| doubleQuadratic,
| /++
| $(HTTPS en.wikipedia.org/wiki/Akima_spline, Akima spline).
| +/
| akima,
|}
|
|/++
|Constructs multivariate cubic spline in symmetrical form with nodes on rectilinear grid.
|Result has continues second derivatives throughout the curve / nd-surface.
|+/
|template spline(T, size_t N = 1, X = T)
| if (isFloatingPoint!T && is(T == Unqual!T) && N <= 6)
|{
| /++
| Params:
| grid = immutable `x` values for interpolant
| values = `f(x)` values for interpolant
| typeOfBoundaries = $(LREF SplineBoundaryType) for both tails (optional).
| valueOfBoundaryConditions = value of the boundary type (optional).
| Constraints:
| `grid` and `values` must have the same length >= 3
| Returns: $(LREF Spline)
| +/
| Spline!(T, N, X) spline(yIterator, SliceKind ykind)(
| Repeat!(N, Slice!(RCI!(immutable X))) grid,
| Slice!(yIterator, N, ykind) values,
| SplineBoundaryType typeOfBoundaries = SplineBoundaryType.notAKnot,
| in T valueOfBoundaryConditions = 0,
| )
| {
9| return spline(grid, values, SplineType.c2, 0, typeOfBoundaries, valueOfBoundaryConditions);
| }
|
| Spline!(T, N, X) spline(yIterator, SliceKind ykind)(
| Repeat!(N, Slice!(RCI!(immutable X))) grid,
| Slice!(yIterator, N, ykind) values,
| SplineType kind,
| in T param = 0,
| SplineBoundaryType typeOfBoundaries = SplineBoundaryType.notAKnot,
| in T valueOfBoundaryConditions = 0,
| )
| {
15| return spline(grid, values, SplineBoundaryCondition!T(typeOfBoundaries, valueOfBoundaryConditions), kind, param);
| }
|
| /++
| Params:
| grid = immutable `x` values for interpolant
| values = `f(x)` values for interpolant
| boundaries = $(LREF SplineBoundaryCondition) for both tails.
| kind = $(LREF SplineType) type of cubic spline.
| param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
| Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
| Constraints:
| `grid` and `values` must have the same length >= 3
| Returns: $(LREF Spline)
| +/
| Spline!(T, N, X) spline(yIterator, SliceKind ykind)(
| Repeat!(N, Slice!(RCI!(immutable X))) grid,
| Slice!(yIterator, N, ykind) values,
| SplineBoundaryCondition!T boundaries,
| SplineType kind = SplineType.c2,
| in T param = 0,
| )
| {
15| return spline(grid, values, boundaries, boundaries, kind, param);
| }
|
| /++
| Params:
| grid = immutable `x` values for interpolant
| values = `f(x)` values for interpolant
| rBoundary = $(LREF SplineBoundaryCondition) for left tail.
| lBoundary = $(LREF SplineBoundaryCondition) for right tail.
| kind = $(LREF SplineType) type of cubic spline.
| param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
| Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
| Constraints:
| `grid` and `values` must have the same length >= 3
| Returns: $(LREF Spline)
| +/
| Spline!(T, N, X) spline(yIterator, SliceKind ykind)(
| Repeat!(N, Slice!(RCI!(immutable X))) grid,
| Slice!(yIterator, N, ykind) values,
| SplineBoundaryCondition!T rBoundary,
| SplineBoundaryCondition!T lBoundary,
| SplineType kind = SplineType.c2,
| in T param = 0,
| )
| {
17| auto ret = typeof(return)(forward!grid);
17| ret._values = values;
17| ret._computeDerivatives(kind, param, rBoundary, lBoundary);
17| return ret;
| }
|}
|
|/++
|Cubic Spline Boundary Condition Type.
|
|See_also: $(LREF SplineBoundaryCondition) $(LREF SplineType)
|+/
|extern(C++, "mir", "interpolate")
|enum SplineBoundaryType
|{
| /++
| Not implemented.
| +/
| periodic = -1,
| /++
| Not-a-knot (or cubic) boundary condition.
| It is an aggresive boundary condition that is used only for C2 splines and is default for all API calls.
| For other then C2 splines, `notAKnot` is changed internally to
| a default boundary type for used $(LREF SplineType).
| +/
| notAKnot,
| /++
| Set the first derivative.
| +/
| firstDerivative,
| /++
| Set the second derivative.
| +/
| secondDerivative,
| /++
| Default for Cardinal and Double-Quadratic splines.
| +/
| parabolic,
| /++
| Default for monotone (aka PHCIP ) splines.
| +/
| monotone,
| /++
| Default for Akima splines.
| +/
| akima,
|}
|
|/++
|Cubic Spline Boundary Condition
|
|See_also: $(LREF SplineBoundaryType)
|+/
|extern(C++, "mir", "interpolate")
|struct SplineBoundaryCondition(T)
|{
| /// type (default is $(LREF SplineBoundaryType.notAKnot))
| SplineBoundaryType type = SplineBoundaryType.notAKnot;
| /// value (default is 0)
| T value = 0;
|}
|
|/++
|Multivariate cubic spline with nodes on rectilinear grid.
|+/
41|struct Spline(F, size_t N = 1, X = F)
| if (N && N <= 6)
|{
| import mir.rc.array;
|
| /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
| Slice!(RCI!(F[2 ^^ N]), N) _data;
| /// Grid iterators. $(RED For internal use.)
| Repeat!(N, RCI!(immutable X)) _grid;
|
|@fmamath extern(D):
|
| /++
| +/
17| this(Repeat!(N, Slice!(RCI!(immutable X))) grid) @safe @nogc
| {
17| size_t length = 1;
17| size_t[N] shape;
| enum msg = "spline interpolant: minimal allowed length for the grid equals 2.";
| version(D_Exceptions)
| static immutable exc = new Exception(msg);
20| foreach(i, ref x; grid)
| {
20| if (x.length < 2)
| {
0000000| version(D_Exceptions) throw exc;
| else assert(0, msg);
| }
20| length *= shape[i] = x.length;
20| this._grid[i] = x._iterator.move;
| }
| import mir.ndslice.allocation: rcslice;
17| this._data = shape.rcslice!(F[2 ^^ N]);
| }
|
| package static auto pickDataSubslice(D)(auto scope ref D data, size_t index) @trusted
| {
283| auto strides = data.strides;
| foreach (i; Iota!(strides.length))
286| strides[i] *= DeepElementType!D.length;
283| return Slice!(F*, strides.length, Universal)(data.shape, strides, data._iterator.ptr + index);
| }
|
| /++
| Assigns function values to the internal memory.
| $(RED For internal use.)
| +/
| void _values(SliceKind kind, Iterator)(Slice!(Iterator, N, kind) values) scope @property @trusted
| {
17| assert(values.shape == _data.shape, "'values' should have the same shape as the .gridShape");
17| pickDataSubslice(_data.lightScope, 0)[] = values;
| }
|
| /++
| Computes derivatives and stores them in `_data`.
| `_data` is assumed to be preinitialized with function values filled in `F[2 ^^ N][0]`.
| Params:
| lbc = left boundary condition
| rbc = right boundary condition
| temp = temporal buffer length points count (optional)
|
| $(RED For internal use.)
| +/
| void _computeDerivatives(SplineType kind, F param, SplineBoundaryCondition!F lbc, SplineBoundaryCondition!F rbc) scope @trusted nothrow @nogc
| {
| import mir.algorithm.iteration: maxLength;
17| auto ml = this._data.maxLength;
34| auto temp = RCArray!F(ml);
17| auto tempSlice = temp[].sliced;
17| _computeDerivativesTemp(kind, param, lbc, rbc, tempSlice);
| }
|
| /// ditto
| pragma(inline, false)
| void _computeDerivativesTemp(SplineType kind, F param, SplineBoundaryCondition!F lbc, SplineBoundaryCondition!F rbc, Slice!(F*) temp) scope @system nothrow @nogc
| {
| import mir.algorithm.iteration: maxLength, each;
| import mir.ndslice.topology: map, byDim, evertPack;
|
15| assert(temp.length >= _data.maxLength);
|
| static if (N == 1)
| {
15| splineSlopes!(F, F)(_grid[0].lightConst.sliced(_data._lengths[0]), pickDataSubslice(_data.lightScope, 0), pickDataSubslice(_data.lightScope, 1), temp[0 .. _data._lengths[0]], kind, param, lbc, rbc);
| }
| else
| foreach_reverse(i; Iota!N)
| {
| // if (i == _grid.length - 1)
| _data
| .lightScope
| .byDim!i
| .evertPack
| .each!((d){
| enum L = 2 ^^ (N - 1 - i);
| foreach(l; Iota!L)
| {
| auto y = pickDataSubslice(d, l);
| auto s = pickDataSubslice(d, L + l);
| // debug printf("ptr = %ld, stride = %ld, stride = %ld, d = %ld i = %ld l = %ld\n", d.iterator, d._stride!0, y._stride!0, d.length, i, l);
| splineSlopes!(F, F)(_grid[i].sliced(_data._lengths[i]), y, s, temp[0 .. _data._lengths[i]], kind, param, lbc, rbc);
| // debug{
| // (cast(void delegate() @nogc)(){
| // writeln("y = ", y);
| // writeln("s = ", s);
| // })();
| // }
| }
| });
| }
| }
|
|@trusted:
|
| ///
0000000| Spline lightConst() const @property { return *cast(Spline*)&this; }
| ///
0000000| Spline lightImmutable() immutable @property { return *cast(Spline*)&this; }
|
| ///
| Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property
| if (dimension < N)
| {
| return _grid[dimension].lightConst.sliced(_data._lengths[dimension]);
| }
|
| ///
| immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
| if (dimension < N)
| {
387| return _grid[dimension]._iterator[0 .. _data._lengths[dimension]];
| }
|
| /++
| Returns: intervals count.
| +/
| size_t intervalCount(size_t dimension = 0)() scope const @property
| {
387| assert(_data._lengths[dimension] > 1);
387| return _data._lengths[dimension] - 1;
| }
|
| ///
| size_t[N] gridShape() scope const @property
| {
0000000| return _data.shape;
| }
|
| ///
| alias withDerivative = opCall!1;
| ///
| alias withTwoDerivatives = opCall!2;
|
| ///
| enum uint derivativeOrder = 3;
|
| ///
| template opCall(uint derivative : 2)
| {
| auto opCall(X...)(in X xs) scope const
| if (X.length == N)
| // @FUTURE@
| // X.length == N || derivative == 0 && X.length && X.length <= N
| {
10| auto d4 = this.opCall!3(xs);
10| SplineReturnType!(F, N, 3) d3;
| void fun(size_t d, A, B)(ref A a, ref B b)
| {
| static if (d)
| foreach(i; Iota!3)
30| fun!(d - 1)(a[i], b[i]);
| else
30| b = a;
| }
10| fun!N(d4, d3);
10| return d3;
| }
| }
|
| ///
| template opCall(uint derivative = 0)
| if (derivative == 0 || derivative == 1 || derivative == 3)
| {
| static if (N > 1 && derivative) pragma(msg, "Warning: multivariate cubic spline with derivatives was not tested!!!");
|
| /++
| `(x)` operator.
| Complexity:
| `O(log(points.length))`
| +/
| auto opCall(X...)(in X xs) scope const @trusted
| if (X.length == N)
| // @FUTURE@
| // X.length == N || derivative == 0 && X.length && X.length <= N
| {
| import mir.ndslice.topology: iota;
| alias Kernel = AliasCall!(SplineKernel!F, "opCall", derivative);
| enum rp2d = derivative == 3 ? 2 : derivative;
|
241| size_t[N] indices;
241| Kernel[N] kernels;
|
| foreach(i; Iota!N)
| {
| static if (isInterval!(typeof(xs[i])))
| {
2| indices[i] = xs[i][1];
2| auto x = xs[i][0];
| }
| else
| {
| alias x = xs[i];
387| indices[i] = this.findInterval!i(x);
| }
389| kernels[i] = SplineKernel!F(_grid[i][indices[i]], _grid[i][indices[i] + 1], x);
| }
|
| align(64) F[2 ^^ N * 2 ^^ N][2] local;
|
| void load(sizediff_t i)(const(F[2 ^^ N])* from, F[2 ^^ N]* to)
| {
| version(LDC) pragma(inline, true);
| static if (i == -1)
| {
| // copyvec(*from, *to);
| // not aligned:
904| *to = *from;
| }
| else
| {
663| from += strides[i] * indices[i];
663| load!(i - 1)(from, to);
663| from += strides[i];
| enum s = 2 ^^ (N - 1 - i);
663| to += s;
663| load!(i - 1)(from, to);
| }
| }
|
241| immutable strides = _data._lengths.iota.strides;
241| load!(N - 1)(_data.ptr, cast(F[2 ^^ N]*) local[0].ptr);
|
| // debug{
|
| // printf("0local[0] = ");
| // foreach(ref e; local[0][])
| // printf("%f ", e);
| // printf("\n");
| // }
|
| foreach(i; Iota!N)
| {
| enum P = 2 ^^ (N - 1 - i) * 2 ^^ (i * rp2d);
| enum L = (2 ^^ N) ^^ 2 / (2 ^^ (i * (2 - rp2d))) / 4;
389| shuffle2!P(local[0][0 * L .. 1 * L], local[0][1 * L .. 2 * L], local[1][0 * L .. 1 * L], local[1][1 * L .. 2 * L]);
389| shuffle2!P(local[0][2 * L .. 3 * L], local[0][3 * L .. 4 * L], local[1][2 * L .. 3 * L], local[1][3 * L .. 4 * L]);
| // debug
| // {
| // printf("0local[1] = ");
| // foreach(ref e; local[1][0 .. L* 4])
| // printf("%f ", e);
| // printf("\n");
| // }
389| local[0][] = F.init;
389| vectorize(
| kernels[i],
| local[1][0 * L .. 1 * L], local[1][2 * L .. 3 * L],
| local[1][1 * L .. 2 * L], local[1][3 * L .. 4 * L],
| *cast(F[L][2 ^^ rp2d]*) local[0].ptr,
| );
|
| // debug{
|
| // printf("1local[0] = ");
| // foreach(ref e; local[0][0 .. L* 2 ^^ rp2d])
| // printf("%f ", e);
| // printf("\n");
| // }
| // printf("local[0][0]", local[0][0]);
| static if (i + 1 == N)
| {
241| return *cast(SplineReturnType!(F, N, 2 ^^ rp2d)*) local[0].ptr;
| }
| else
| {
| static if (rp2d == 1)
| {
3| shuffle3!1(local[0][0 .. L], local[0][L .. 2 * L], local[1][0 .. L], local[1][L .. 2 * L]);
3| copyvec(local[1][0 .. 1 * L], local[0][0 .. 1 * L]);
3| copyvec(local[1][L .. 2 * L], local[0][L .. 2 * L]);
| }
| else
| static if (rp2d == 2)
| {
| shuffle3!1(local[0][0 * L .. 1 * L], local[0][1 * L .. 2 * L], local[1][0 * L .. 1 * L], local[1][1 * L .. 2 * L]);
| shuffle3!1(local[0][2 * L .. 3 * L], local[0][3 * L .. 4 * L], local[1][2 * L .. 3 * L], local[1][3 * L .. 4 * L]);
| shuffle3!2(local[1][0 * L .. 1 * L], local[1][2 * L .. 3 * L], local[0][0 * L .. 1 * L], local[0][2 * L .. 3 * L]);
| shuffle3!2(local[1][1 * L .. 2 * L], local[1][3 * L .. 4 * L], local[0][1 * L .. 2 * L], local[0][3 * L .. 4 * L]);
| }
|
| // debug{
|
| // printf("2local[0] = ");
| // foreach(ref e; local[0][0 .. L * 2 ^^ rp2d])
| // printf("%f ", e);
| // printf("\n");
| // }
|
| }
| }
| }
| }
|}
|
|/++
|Piecewise cubic hermite interpolating polynomial.
|Params:
| points = `x` values for interpolant
| values = `f(x)` values for interpolant
| slopes = uninitialized ndslice to write slopes into
| temp = uninitialized temporary ndslice
| kind = $(LREF SplineType) type of cubic spline.
| param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
| Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
| lbc = left boundary condition
| rbc = right boundary condition
|Constraints:
| `points`, `values`, and `slopes`, must have the same length > 3;
| `temp` must have length greater or equal to points less minus one.
|+/
|void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind skind)(
| Slice!(IP, 1, gkind) points,
| Slice!(IV, 1, vkind) values,
| Slice!(IS, 1, skind) slopes,
| Slice!(T*) temp,
| SplineType kind,
| F param,
| SplineBoundaryCondition!F lbc,
| SplineBoundaryCondition!F rbc,
| ) @trusted
|{
| import mir.ndslice.topology: diff, zip, slide;
|
133| assert (points.length >= 2);
133| assert (points.length == values.length);
133| assert (points.length == slopes.length);
133| assert (temp.length == points.length);
|
133| auto n = points.length;
|
266| typeof(slopes[0]) first, last;
|
266| auto xd = points.diff;
133| auto yd = values.diff;
266| auto dd = yd / xd;
266| auto dd2 = points.zip(values).slide!(3, "(c[1] - a[1]) / (c[0] - a[0])");
|
133| with(SplineType) final switch(kind)
| {
127| case c2:
127| break;
0000000| case cardinal:
0000000| if (lbc.type == SplineBoundaryType.notAKnot)
0000000| lbc.type = SplineBoundaryType.parabolic;
0000000| if (rbc.type == SplineBoundaryType.notAKnot)
0000000| rbc.type = SplineBoundaryType.parabolic;
0000000| break;
4| case monotone:
4| if (lbc.type == SplineBoundaryType.notAKnot)
4| lbc.type = SplineBoundaryType.monotone;
4| if (rbc.type == SplineBoundaryType.notAKnot)
4| rbc.type = SplineBoundaryType.monotone;
4| break;
1| case doubleQuadratic:
1| if (lbc.type == SplineBoundaryType.notAKnot)
1| lbc.type = SplineBoundaryType.parabolic;
1| if (rbc.type == SplineBoundaryType.notAKnot)
1| rbc.type = SplineBoundaryType.parabolic;
1| break;
1| case akima:
1| if (lbc.type == SplineBoundaryType.notAKnot)
1| lbc.type = SplineBoundaryType.akima;
1| if (rbc.type == SplineBoundaryType.notAKnot)
1| rbc.type = SplineBoundaryType.akima;
1| break;
| }
|
133| if (n <= 3)
| {
21| if (lbc.type == SplineBoundaryType.notAKnot)
21| lbc.type = SplineBoundaryType.parabolic;
21| if (rbc.type == SplineBoundaryType.notAKnot)
21| rbc.type = SplineBoundaryType.parabolic;
|
21| if (n == 2)
| {
0000000| if (lbc.type == SplineBoundaryType.monotone
0000000| || lbc.type == SplineBoundaryType.akima)
0000000| lbc.type = SplineBoundaryType.parabolic;
0000000| if (rbc.type == SplineBoundaryType.monotone
0000000| || rbc.type == SplineBoundaryType.akima)
0000000| rbc.type = SplineBoundaryType.parabolic;
| }
| /// special case
21| if (rbc.type == SplineBoundaryType.parabolic
21| && lbc.type == SplineBoundaryType.parabolic)
| {
| import mir.interpolate.utility;
21| if (n == 3)
| {
21| auto derivatives = parabolaDerivatives(points[0], points[1], points[2], values[0], values[1], values[2]);
21| slopes[0] = derivatives[0];
21| slopes[1] = derivatives[1];
21| slopes[2] = derivatives[2];
| }
| else
| {
0000000| assert(slopes.length == 2);
0000000| slopes.back = slopes.front = yd.front / xd.front;
| }
21| return;
| }
| }
|
112| with(SplineBoundaryType) final switch(lbc.type)
| {
| case periodic:
|
| assert(0);
|
101| case notAKnot:
|
101| auto dx0 = xd[0];
101| auto dx1 = xd[1];
101| auto dy0 = yd[0];
101| auto dy1 = yd[1];
101| auto dd0 = dy0 / dx0;
101| auto dd1 = dy1 / dx1;
|
101| slopes.front = dx1;
101| first = dx0 + dx1;
101| temp.front = ((dx0 + 2 * first) * dx1 * dd0 + dx0 ^^ 2 * dd1) / first;
101| break;
|
2| case firstDerivative:
|
2| slopes.front = 1;
2| first = 0;
2| temp.front = lbc.value;
2| break;
|
3| case secondDerivative:
|
3| slopes.front = 2;
3| first = 1;
3| temp.front = 3 * dd.front - 0.5 * lbc.value * xd.front;
3| break;
|
1| case parabolic:
|
1| slopes.front = 1;
1| first = 1;
1| temp.front = 2 * dd.front;
1| break;
|
4| case monotone:
|
4| slopes.front = 1;
4| first = 0;
4| temp.front = pchipTail(xd[0], xd[1], dd[0], dd[1]);
4| break;
|
1| case akima:
|
1| slopes.front = 1;
1| first = 0;
1| temp.front = akimaTail(dd[0], dd[1]);
1| break;
|
| }
|
112| with(SplineBoundaryType) final switch(rbc.type)
| {
| case periodic:
| assert(0);
|
101| case notAKnot:
|
101| auto dx0 = xd[$ - 1];
101| auto dx1 = xd[$ - 2];
101| auto dy0 = yd[$ - 1];
101| auto dy1 = yd[$ - 2];
101| auto dd0 = dy0 / dx0;
101| auto dd1 = dy1 / dx1;
101| slopes.back = dx1;
101| last = dx0 + dx1;
101| temp.back = ((dx0 + 2 * last) * dx1 * dd0 + dx0 ^^ 2 * dd1) / last;
101| break;
|
2| case firstDerivative:
|
2| slopes.back = 1;
2| last = 0;
2| temp.back = rbc.value;
2| break;
|
3| case secondDerivative:
|
3| slopes.back = 2;
3| last = 1;
3| temp.back = 3 * dd.back + 0.5 * rbc.value * xd.back;
3| break;
|
1| case parabolic:
|
1| slopes.back = 1;
1| last = 1;
1| temp.back = 2 * dd.back;
1| break;
|
4| case monotone:
|
4| slopes.back = 1;
4| last = 0;
4| temp.back = pchipTail(xd[$ - 1], xd[$ - 2], dd[$ - 1], dd[$ - 2]);
4| break;
|
1| case akima:
|
1| slopes.back = 1;
1| last = 0;
1| temp.back = akimaTail(dd[$ - 1], dd[$ - 2]);
1| break;
|
| }
|
112| with(SplineType) final switch(kind)
| {
106| case c2:
|
1179| foreach (i; 1 .. n - 1)
| {
287| auto dx0 = xd[i - 1];
287| auto dx1 = xd[i - 0];
287| auto dy0 = yd[i - 1];
287| auto dy1 = yd[i - 0];
287| slopes[i] = 2 * (dx0 + dx1);
287| temp[i] = 3 * (dy0 / dx0 * dx1 + dy1 / dx1 * dx0);
| }
106| break;
|
0000000| case cardinal:
|
0000000| foreach (i; 1 .. n - 1)
| {
0000000| slopes[i] = 1;
0000000| temp[i] = (1 - param) * dd2[i - 1];
| }
0000000| break;
|
4| case monotone:
| {
4| auto step0 = cast()xd[0];
4| auto step1 = cast()xd[1];
4| auto diff0 = cast()yd[0];
4| auto diff1 = cast()yd[1];
4| diff0 /= step0;
4| diff1 /= step1;
|
4| for(size_t i = 1;;)
| {
32| slopes[i] = 1;
96| if (diff0 && diff1 && copysign(1f, diff0) == copysign(1f, diff1))
| {
8| auto w0 = step1 * 2 + step0;
8| auto w1 = step0 * 2 + step1;
8| temp[i] = (w0 + w1) / (w0 / diff0 + w1 / diff1);
| }
| else
| {
24| temp[i] = 0;
| }
32| if (++i == n - 1)
| {
4| break;
| }
28| step0 = step1;
28| diff0 = diff1;
28| step1 = xd[i];
28| diff1 = yd[i];
28| diff1 /= step1;
| }
| }
4| break;
|
1| case doubleQuadratic:
|
27| foreach (i; 1 .. n - 1)
| {
8| slopes[i] = 1;
8| temp[i] = dd[i - 1] + dd[i] - dd2[i - 1];
| }
1| break;
|
1| case akima:
| {
1| auto d3 = dd[1];
1| auto d2 = dd[0];
1| auto d1 = 2 * d2 - d3;
1| auto d0 = d1;
27| foreach (i; 1 .. n - 1)
| {
8| d0 = d1;
8| d1 = d2;
8| d2 = d3;
16| d3 = i == n - 2 ? 2 * d2 - d1 : dd[i + 1];
8| slopes[i] = 1;
8| temp[i] = akimaSlope(d0, d1, d2, d3);
| }
1| break;
| }
| }
|
1677| foreach (i; 0 .. n - 1)
| {
1229| auto c = i == 0 ? first : kind == SplineType.c2 ? xd[i - 1] : 0;
1229| auto a = i == n - 2 ? last : kind == SplineType.c2 ? xd[i + 1] : 0;
894| auto w = slopes[i] == 1 ? a : a / slopes[i];
447| slopes[i + 1] -= w * c;
447| temp[i + 1] -= w * temp[i];
| }
|
112| slopes.back = temp.back / slopes.back;
|
1230| foreach_reverse (i; 0 .. n - 1)
| {
1229| auto c = i == 0 ? first : kind == SplineType.c2 ? xd[i - 1] : 0;
447| auto v = temp[i] - c * slopes[i + 1];
894| slopes[i] = slopes[i] == 1 ? v : v / slopes[i];
| }
|}
|
|private F akimaTail(F)(in F d2, in F d3)
|{
2| auto d1 = 2 * d2 - d3;
2| auto d0 = 2 * d1 - d2;
2| return akimaSlope(d0, d1, d2, d3);
|}
|
|private F akimaSlope(F)(in F d0, in F d1, in F d2, in F d3)
|{
10| if (d1 == d2)
0000000| return d1;
10| if (d0 == d1 && d2 == d3)
0000000| return (d1 + d2) * 0.5f;
10| if (d0 == d1)
0000000| return d1;
10| if (d2 == d3)
0000000| return d2;
10| auto w0 = fabs(d1 - d0);
10| auto w1 = fabs(d3 - d2);
10| auto ws = w0 + w1;
10| w0 /= ws;
10| w1 /= ws;
10| return w0 * d2 + w1 * d1;
|}
|
|///
|struct SplineKernel(X)
|{
| X step = 0;
| X w0 = 0;
| X w1 = 0;
| X wq = 0;
|
| ///
389| this(X x0, X x1, X x)
| {
389| step = x1 - x0;
389| auto c0 = x - x0;
389| auto c1 = x1 - x;
389| w0 = c0 / step;
389| w1 = c1 / step;
389| wq = w0 * w1;
| }
|
| ///
| template opCall(uint derivative = 0)
| if (derivative <= 3)
| {
| ///
| auto opCall(Y)(in Y y0, in Y y1, in Y s0, in Y s1) const
| {
1597| auto diff = y1 - y0;
1597| auto z0 = s0 * step - diff;
1597| auto z1 = s1 * step - diff;
1597| auto a0 = z0 * w1;
1597| auto a1 = z1 * w0;
1597| auto pr = a0 - a1;
1597| auto b0 = y0 * w1;
1597| auto b1 = y1 * w0;
1597| auto pl = b0 + b1;
1597| auto y = pl + wq * pr;
| static if (derivative)
| {
64| Y[derivative + 1] ret = 0;
64| ret[0] = y;
64| auto wd = w1 - w0;
64| auto zd = z1 + z0;
64| ret[1] = (diff + (wd * pr - wq * zd)) / step;
| static if (derivative > 1)
| {
20| auto astep = zd / (step * step);
20| ret[2] = -3 * wd * astep + (s1 - s0) / step;
| static if (derivative > 2)
20| ret[3] = 6 * astep / step;
| }
64| return ret;
| }
| else
| {
1533| return y;
| }
| }
| }
|
| ///
| alias withDerivative = opCall!1;
| ///
| alias withTwoDerivatives = opCall!2;
|}
|
|package T pchipTail(T)(in T step0, in T step1, in T diff0, in T diff1)
|{
| import mir.math.common: copysign, fabs;
8| if (!diff0)
| {
0000000| return 0;
| }
8| auto slope = ((step0 * 2 + step1) * diff0 - step0 * diff1) / (step0 + step1);
8| if (copysign(1f, slope) != copysign(1f, diff0))
| {
0000000| return 0;
| }
16| if ((copysign(1f, diff0) != copysign(1f, diff1)) && (fabs(slope) > fabs(diff0 * 3)))
| {
0000000| return diff0 * 3;
| }
8| return slope;
|}
source/mir/interpolate/spline.d is 92% covered
<<<<<< EOF
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-internal-utility.lst
|///
|module mir.internal.utility;
|
|private alias AliasSeq(T...) = T;
|
|///
|alias Iota(size_t j) = Iota!(0, j);
|
|///
|template Iota(size_t i, size_t j)
|{
| static assert(i <= j, "Iota: i should be less than or equal to j");
| static if (i == j)
| alias Iota = AliasSeq!();
| else
| alias Iota = AliasSeq!(i, Iota!(i + 1, j));
|}
|
|///
|template realType(C)
| if (__traits(isFloating, C) || isComplex!C)
|{
| import std.traits: Unqual;
| static if (isComplex!C)
| alias realType = typeof(Unqual!C.init.re);
| else
| alias realType = Unqual!C;
|}
|
|///
|template isComplex(C)
|{
| static if (is(C == struct) || is(C == enum))
| {
| static if (hasField!(C, "re") && hasField!(C, "im") && C.init.tupleof.length == 2)
| enum isComplex = isFloatingPoint!(typeof(C.init.tupleof[0]));
| else
| enum isComplex = false;
| }
| else
| {
| // for backward compatability with cfloat, cdouble and creal
| enum isComplex = __traits(isFloating, C) && !isFloatingPoint!C && !is(C : __vector(F[N]), F, size_t N);
| }
|}
|
|version(LDC)
|version(mir_core_test)
|unittest
|{
| static assert(!isComplex!(__vector(double[2])));
|}
|
|///
|template isComplexOf(C, F)
| if (isFloatingPoint!F)
|{
| static if (isComplex!C)
| enum isComplexOf = is(typeof(C.init.re) == F);
| else
| enum isComplexOf = false;
|}
|
|///
|template isFloatingPoint(C)
|{
| import std.traits: Unqual;
| alias U = Unqual!C;
| enum isFloatingPoint = is(U == double) || is(U == float) || is(U == real);
|}
|
|// copy to reduce imports
|enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; });
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/internal/utility.d has no code
<<<<<< EOF
# path=./source-mir-type_info.lst
|/++
|$(H1 Type Information)
|
|Type Information implementation compatible with BetterC mode.
|
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.type_info;
|
|/++
|+/
|struct mir_type_info
|{
| ///
| extern(C)
| void function(void*) @safe pure nothrow @nogc destructor;
| /++
| Note: Negative values are used for classes to indicate that
| +/
| int size;
|}
|
|/++
|+/
|ref immutable(mir_type_info) mir_get_type_info(T)() @trusted
|{
| import std.traits: Unqual, hasElaborateDestructor;
|
| static if (is(T == class))
| enum sizeof = __traits(classInstanceSize, T);
| else
| enum sizeof = T.sizeof;
|
| static if (!is(T == Unqual!T))
| {
6| return mir_get_type_info!(Unqual!T);
| }
| else
| static if (hasElaborateDestructor!T)
| {
| import std.traits: SetFunctionAttributes, functionAttributes;
| alias fun = void function(void*) @safe pure nothrow @nogc;
| extern(C)
| static void destroy_impl(void* ptr) nothrow
| {
| static if (is(T == class))
| T inst() return @trusted
| {
| return cast(T)ptr;
| }
| else
| ref T inst() return @trusted
| {
5| return *cast(T*)ptr;
| }
| version(assert)
5| destroy!true(inst());
| else
| destroy!false(inst());
| }
|
| static immutable ti = mir_type_info(cast(SetFunctionAttributes!(fun, "C", functionAttributes!fun))&destroy_impl, sizeof);
5| return ti;
| }
| else
| {
182| return .mir_get_type_info!sizeof;
| }
|}
|
|/++
|+/
|ref immutable(mir_type_info) mir_get_type_info(uint sizeof)()
|{
| static immutable ti = mir_type_info(null, sizeof);
182| return ti;
|}
|
|package template hasDestructor(T)
|{
| import std.traits: Unqual;
|
| static if (is(T == struct))
| {
| static if (__traits(hasMember, Unqual!T, "__xdtor"))
| enum hasDestructor = __traits(isSame, Unqual!T, __traits(parent, T.init.__xdtor));
| else
| enum hasDestructor = false;
| }
| else
| static if (is(T == class))
| {
| enum hasDestructor = __traits(hasMember, Unqual!T, "__xdtor");
| }
| else
| {
| enum hasDestructor = false;
| }
|}
|
|package const(void)* mir_get_payload_ptr(T)()
|{
| import std.traits: Unqual;
|
| static if (!is(T == Unqual!T))
| {
6| return mir_get_payload_ptr!(Unqual!T);
| }
| else
| static if (is(T == class))
| {
4| return typeid(T).initializer.ptr;
| }
| else
| static if (__traits(isZeroInit, T) || __traits(isFloating, T))
| {
157| return null;
| }
| else
| {
| static immutable payload = T.init;
26| return &payload;
| }
|}
source/mir/type_info.d is 100% covered
<<<<<< EOF
# path=./source-mir-small_string.lst
|/++
|$(H1 Small String)
|
|The module contains self-contained generic small string implementaton.
|
|$(LREF SmallString) supports ASDF - Json Serialisation Library.
|
|See also `include/mir/small_series.h` for a C++ version of this type.
|Both C++ and D implementations have the same ABI and name mangling.
|
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|+/
|module mir.small_string;
|
|import mir.serde: serdeScoped, serdeProxy;
|
|private extern (C) @system nothrow @nogc pure size_t strnlen_s(scope const char* s, size_t n);
|
|private static immutable errorMsg = "Cannot create SmallString: input string exceeds maximum allowed length.";
|version(D_Exceptions)
| private static immutable exception = new Exception(errorMsg);
|
|extern(C++, "mir"):
|
|/++
|Self-contained generic Small String implementaton.
|+/
|@serdeScoped @serdeProxy!(const(char)[])
|struct SmallString(uint maxLength)
| if (maxLength)
|{
|
| import core.stdc.string: memcmp, memcpy, strlen;
| import std.traits: Unqual, isIterable;
|
| // maxLength bytes
| char[maxLength] _data = '\0';
|
|extern(D) @safe pure @nogc:
|
| /// Constructor
0000000| this(typeof(null))
| {
| }
|
| /// ditto
11| this(scope const(char)[] str)
| {
11| this.opAssign(str);
| }
|
| /// ditto
| this(uint n)(auto ref scope const SmallString!n str)
| {
| this.opAssign(str);
| }
|
| /// ditto
| this(Range)(auto ref Range str)
| if (isIterable!Range)
| {
| size_t i = 0;
| foreach(char c; str)
| {
| if (i > _data.length)
| {
| version(D_Exceptions) throw exception;
| else assert(0, errorMsg);
| }
| _data[i++] = c;
| }
| }
|
| /// `=` operator
| ref typeof(this) opAssign(typeof(null)) return
| {
1| _data = '\0';
1| return this;
| }
|
| /// ditto
| ref typeof(this) opAssign(scope const(char)[] str) return @trusted
| {
11| if (str.length > _data.sizeof)
| {
0000000| version(D_Exceptions) throw exception;
| else assert(0, errorMsg);
| }
11| if (__ctfe)
0000000| _data[0 .. str.length] = str;
| else
11| memcpy(_data.ptr, str.ptr, str.length);
11| _data[str.length .. $] = '\0';
11| return this;
| }
|
| /// ditto
| ref typeof(this) opAssign(uint n)(auto ref scope const SmallString!n rhs) return
| if (n != maxLength)
| {
| static if (n < maxLength)
| {
| version (LDC)
| cast(char[n])(_data[0 .. n]) = rhs._data;
| else
1| _data[0 .. n] = rhs._data;
1| _data[n .. maxLength] = '\0';
| }
| else
| {
1| if (rhs._data[maxLength])
| {
0000000| version(D_Exceptions) throw exception;
| else assert(0, errorMsg);
| }
1| _data = cast(char[maxLength])(rhs._data[0 .. maxLength]);
| }
2| return this;
| }
|
| /// ditto
| ref typeof(this) opAssign(uint n)(const SmallString!n rhs) return
| if (n != maxLength)
| {
| static if (n < maxLength)
| {
| version (LDC)
| cast(char[n])(_data[0 .. n]) = rhs._data;
| else
| _data[0 .. n] = rhs._data;
| _data[n .. maxLength] = '\0';
| }
| else
| {
| if (rhs._data[maxLength])
| {
| version(D_Exceptions) throw exception;
| else assert(0, errorMsg);
| }
| _data = cast(char[maxLength])(rhs._data[0 .. maxLength]);
| }
| return this;
| }
|
| /// ditto
| void trustedAssign(scope const(char)[] str) return @trusted nothrow
| {
0000000| _data = '\0';
0000000| if (__ctfe)
0000000| _data[0 .. str.length] = str;
| else
0000000| memcpy(_data.ptr, str.ptr, str.length);
| }
|
| ///
| ref typeof(this) append(char c) @trusted
| {
1| auto length = opIndex.length;
1| if (length == maxLength)
| {
0000000| version(D_Exceptions) throw exception;
| else assert(0, errorMsg);
| }
1| _data[length] = c;
1| return this;
| }
|
| ///
| ref typeof(this) append(scope const(char)[] str) @trusted
| {
6| auto length = opIndex.length;
6| if (length + str.length > maxLength)
| {
0000000| version(D_Exceptions) throw exception;
| else assert(0, errorMsg);
| }
6| if (__ctfe)
0000000| _data[length .. str.length + length] = str;
| else
6| memcpy(_data.ptr + length, str.ptr, str.length);
6| return this;
| }
|
| /// ditto
| alias put = append;
|
| /// ditto
| alias opOpAssign(string op : "~") = append;
|
| ///
| SmallString concat(scope const(char)[] str) scope const
| {
1| SmallString c = this;
1| c.append(str);
1| return c;
| }
|
| /// ditto
| alias opBinary(string op : "~") = concat;
|
|
|scope nothrow:
|
| /++
| Returns an scope common string.
|
| The property is used as common string representation self alias.
|
| The alias helps with `[]`, `[i]`, `[i .. j]`, `==`, and `!=` operations and implicit conversion to strings.
| +/
| inout(char)[] opIndex() inout @trusted scope return
| {
30| size_t i;
30| if (__ctfe)
0000000| while (i < maxLength && _data[i]) i++;
| else
60| i = _data[$ - 1] ? _data.length : strlen(_data.ptr);
30| return _data[0 .. i];
| }
|
| ///
| ref inout(char) opIndex(size_t index) inout scope return
| {
1| return opIndex[index];
| }
|
|const:
|
| ///
| bool empty() @property
| {
2| return _data[0] == 0;
| }
|
| ///
| size_t length() @property
| {
0000000| return opIndex.length;
| }
|
| ///
| alias toString = opIndex;
|
| /// Hash implementation
| size_t toHash()
| {
0000000| return hashOf(opIndex);
| }
|
| /// Comparisons operator overloads
| bool opEquals(ref scope const SmallString rhs) scope
| {
1| return _data == rhs._data;
| }
|
| /// ditto
| bool opEquals(SmallString rhs) scope
| {
0000000| return _data == rhs._data;
| }
|
| /// ditto
| bool opEquals(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
| if (rhsMaxLength != maxLength)
| {
| return opIndex == rhs.opIndex;
| }
|
| /// ditto
| bool opEquals()(scope const(char)[] str) scope
| {
9| return opIndex == str;
| }
|
| /// ditto
| int opCmp(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
| {
1| return __cmp(opIndex, rhs.opIndex);
| }
|
| /// ditto
| int opCmp()(scope const(char)[] str) scope
| {
3| return __cmp(opIndex, str);
| }
|}
|
|///
|@safe pure @nogc version(mir_test) unittest
|{
1| SmallString!16 s16;
1| assert(s16.empty);
|
1| auto s8 = SmallString!8("Hellow!!");
1| assert(s8 == "Hellow!!", s8[]);
|
1| s16 = s8;
1| assert(s16 == "Hellow!!", s16[]);
1| s16[7] = '@';
1| s8 = null;
1| assert(s8.empty);
1| s8 = s16;
1| assert(s8 == "Hellow!@");
|
1| auto s8_2 = s8;
1| assert(s8_2 == "Hellow!@");
1| assert(s8_2 == s8);
|
1| assert(s8 < "Hey");
1| assert(s8 > "Hellow!");
|
1| assert(s8.opCmp("Hey") < 0);
1| assert(s8.opCmp(s8) == 0);
|}
|
|/// Concatenation
|@safe pure @nogc version(mir_test) unittest
|{
1| auto a = SmallString!16("asdf");
1| a ~= " ";
1| auto b = a ~ "qwerty";
| static assert(is(typeof(b) == SmallString!16));
1| assert(b == "asdf qwerty");
1| b.put('!');
1| b.put("!");
1| assert(b == "asdf qwerty!!");
|}
|
|@safe pure @nogc nothrow version(mir_test) unittest
|{
| import mir.conv: emplaceRef;
2| SmallString!32 a, b;
1| emplaceRef!(const SmallString!32)(a, cast(const)b);
|}
source/mir/small_string.d is 80% covered
<<<<<< EOF
# path=./source-mir-ndslice-allocation.lst
|/++
|This is a submodule of $(MREF mir,ndslice).
|
|It contains allocation utilities.
|
|
|$(BOOKTABLE $(H2 Common utilities),
|$(T2 shape, Returns a shape of a common n-dimensional array. )
|)
|
|$(BOOKTABLE $(H2 GC Allocation utilities),
|$(TR $(TH Function Name) $(TH Description))
|$(T2 slice, Allocates a slice using GC.)
|$(T2 bitSlice, GC-Allocates a bitwise packed n-dimensional boolean slice.)
|$(T2 ndarray, Allocates a common n-dimensional array from a slice. )
|$(T2 uninitSlice, Allocates an uninitialized slice using GC. )
|)
|
|$(BOOKTABLE $(H2 Ref counted allocation utilities),
|$(T2 rcslice, Allocates an n-dimensional reference-counted (thread-safe) slice.)
|$(T2 bitRcslice, Allocates a bitwise packed n-dimensional reference-counted (thread-safe) boolean slice.)
|$(T2 mininitRcslice, Allocates a minimally initialized n-dimensional reference-counted (thread-safe) slice.)
|)
|
|$(BOOKTABLE $(H2 Custom allocation utilities),
|$(TR $(TH Function Name) $(TH Description))
|$(T2 makeNdarray, Allocates a common n-dimensional array from a slice using an allocator. )
|$(T2 makeSlice, Allocates a slice using an allocator. )
|$(T2 makeUninitSlice, Allocates an uninitialized slice using an allocator. )
|)
|
|$(BOOKTABLE $(H2 CRuntime allocation utilities),
|$(TR $(TH Function Name) $(TH Description))
|$(T2 stdcSlice, Allocates a slice copy using `core.stdc.stdlib.malloc`)
|$(T2 stdcUninitSlice, Allocates an uninitialized slice using `core.stdc.stdlib.malloc`.)
|$(T2 stdcFreeSlice, Frees memory using `core.stdc.stdlib.free`)
|)
|
|$(BOOKTABLE $(H2 Aligned allocation utilities),
|$(TR $(TH Function Name) $(TH Description))
|$(T2 uninitAlignedSlice, Allocates an uninitialized aligned slice using GC. )
|$(T2 stdcUninitAlignedSlice, Allocates an uninitialized aligned slice using CRuntime.)
|$(T2 stdcFreeAlignedSlice, Frees memory using CRuntime)
|)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.ndslice.allocation;
|
|import mir.math.common: optmath;
|import mir.ndslice.concatenation;
|import mir.ndslice.field: BitField;
|import mir.ndslice.internal;
|import mir.ndslice.iterator: FieldIterator;
|import mir.ndslice.slice;
|import mir.rc.array;
|import std.traits;
|import std.meta: staticMap;
|
|@optmath:
|
|/++
|Allocates an n-dimensional reference-counted (thread-safe) slice.
|Params:
| lengths = List of lengths for each dimension.
| init = Value to initialize with (optional).
| slice = Slice to copy shape and data from (optional).
|Returns:
| n-dimensional slice
|+/
|Slice!(RCI!T, N)
| rcslice(T, size_t N)(size_t[N] lengths...)
|{
23| immutable len = lengths.lengthsProduct;
23| auto _lengths = lengths;
23| return typeof(return)(_lengths, RCI!T(RCArray!T(len)));
|}
|
|/// ditto
|Slice!(RCI!T, N)
| rcslice(T, size_t N)(size_t[N] lengths, T init)
|{
6| auto ret = (()@trusted => mininitRcslice!T(lengths))();
3| ret.lightScope.field[] = init;
3| return ret;
|}
|
|/// ditto
|auto rcslice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
|{
| import mir.conv: emplaceRef;
| alias E = slice.DeepElement;
|
219| auto result = (() @trusted => slice.shape.mininitRcslice!(Unqual!E))();
|
| import mir.algorithm.iteration: each;
73| each!(emplaceRef!E)(result.lightScope, slice.lightScope);
|
146| return *(() @trusted => cast(Slice!(RCI!E, N)*) &result)();
|}
|
|/// ditto
|auto rcslice(T)(T[] array)
|{
24| return rcslice(array.sliced);
|}
|
|/// ditto
|auto rcslice(T, I)(I[] array)
| if (!isImplicitlyConvertible!(I[], T[]))
|{
| import mir.ndslice.topology: as;
1| return rcslice(array.sliced.as!T);
|}
|
|///
|version(mir_test)
|@safe pure nothrow @nogc unittest
|{
| import mir.ndslice.slice: Slice;
| import mir.rc.array: RCI;
2| auto tensor = rcslice!int(5, 6, 7);
1| assert(tensor.length == 5);
1| assert(tensor.elementCount == 5 * 6 * 7);
| static assert(is(typeof(tensor) == Slice!(RCI!int, 3)));
|
| // creates duplicate using `rcslice`
2| auto dup = tensor.rcslice;
1| assert(dup == tensor);
|}
|
|///
|version(mir_test)
|@safe pure nothrow @nogc unittest
|{
| import mir.ndslice.slice: Slice;
| import mir.rc.array: RCI;
2| auto tensor = rcslice([2, 3], 5);
1| assert(tensor.elementCount == 2 * 3);
1| assert(tensor[1, 1] == 5);
|
| import mir.rc.array;
| static assert(is(typeof(tensor) == Slice!(RCI!int, 2)));
|}
|
|/// ditto
|auto rcslice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation)
|{
| alias T = Unqual!(concatenation.DeepElement);
2| auto ret = (()@trusted => mininitRcslice!T(concatenation.shape))();
1| ret.lightScope.opIndexAssign(concatenation);
1| return ret;
|}
|
|///
|version(mir_test)
|@safe pure nothrow @nogc unittest
|{
| import mir.rc.array: RCI;
| import mir.ndslice.slice: Slice;
| import mir.ndslice.topology : iota;
| import mir.ndslice.concatenation;
2| auto tensor = concatenation([2, 3].iota, [3].iota(6)).rcslice;
1| assert(tensor == [3, 3].iota);
|
| static assert(is(typeof(tensor) == Slice!(RCI!ptrdiff_t, 2)));
|}
|
|/++
|Allocates an n-dimensional reference-counted (thread-safe) slice without memory initialisation.
|Params:
| lengths = List of lengths for each dimension.
|Returns:
| n-dimensional slice
|+/
|Slice!(RCI!T, N)
| uninitRCslice(T, size_t N)(size_t[N] lengths...)
|{
3| immutable len = lengths.lengthsProduct;
3| auto _lengths = lengths;
3| return typeof(return)(_lengths, RCI!T(RCArray!T(len, false)));
|}
|
|///
|version(mir_test)
|@safe pure nothrow @nogc unittest
|{
| import mir.ndslice.slice: Slice;
| import mir.rc.array: RCI;
2| auto tensor = uninitRCslice!int(5, 6, 7);
1| tensor[] = 1;
1| assert(tensor.length == 5);
1| assert(tensor.elementCount == 5 * 6 * 7);
| static assert(is(typeof(tensor) == Slice!(RCI!int, 3)));
|}
|
|/++
|Allocates a bitwise packed n-dimensional reference-counted (thread-safe) boolean slice.
|Params:
| lengths = List of lengths for each dimension.
|Returns:
| n-dimensional bitwise rcslice
|See_also: $(SUBREF topology, bitwise).
|+/
|Slice!(FieldIterator!(BitField!(RCI!size_t)), N) bitRcslice(size_t N)(size_t[N] lengths...)
|{
| import mir.ndslice.topology: bitwise;
| enum elen = size_t.sizeof * 8;
2| immutable len = lengths.lengthsProduct;
2| immutable dlen = (len / elen + (len % elen != 0));
2| return RCArray!size_t(dlen).asSlice.bitwise[0 .. len].sliced(lengths);
|}
|
|/// 1D
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
2| auto bitarray = 100.bitRcslice; // allocates 16 bytes total (plus RC context)
1| assert(bitarray.shape == cast(size_t[1])[100]);
1| assert(bitarray[72] == false);
1| bitarray[72] = true;
1| assert(bitarray[72] == true);
|}
|
|/// 2D
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
2| auto bitmatrix = bitRcslice(20, 6); // allocates 16 bytes total (plus RC context)
1| assert(bitmatrix.shape == cast(size_t[2])[20, 6]);
1| assert(bitmatrix[3, 4] == false);
1| bitmatrix[3, 4] = true;
1| assert(bitmatrix[3, 4] == true);
|}
|
|/++
|Allocates a minimally initialized n-dimensional reference-counted (thread-safe) slice.
|Params:
| lengths = list of lengths for each dimension
|Returns:
| contiguous minimally initialized n-dimensional reference-counted (thread-safe) slice
|+/
|Slice!(RCI!T, N) mininitRcslice(T, size_t N)(size_t[N] lengths...)
|{
78| immutable len = lengths.lengthsProduct;
78| auto _lengths = lengths;
78| return Slice!(RCI!T, N)(_lengths, RCI!T(mininitRcarray!T(len)));
|}
|
|///
|version(mir_test)
|pure nothrow @nogc unittest
|{
| import mir.ndslice.slice: Slice;
| import mir.rc.array: RCI;
2| auto tensor = mininitRcslice!int(5, 6, 7);
1| assert(tensor.length == 5);
1| assert(tensor.elementCount == 5 * 6 * 7);
| static assert(is(typeof(tensor) == Slice!(RCI!int, 3)));
|}
|
|private alias Pointer(T) = T*;
|private alias Pointers(Args...) = staticMap!(Pointer, Args);
|
|/++
|GC-Allocates an n-dimensional slice.
|+/
|template slice(Args...)
| if (Args.length)
|{
| ///
| alias LabelTypes = Args[1 .. $];
| ///
| alias T = Args[0];
|
| /++
| Params:
| lengths = List of lengths for each dimension.
| init = Value to initialize with (optional).
| Returns:
| initialzed n-dimensional slice
| +/
| Slice!(T*, N, Contiguous, Pointers!LabelTypes)
| slice(size_t N)(size_t[N] lengths...)
| if (N >= LabelTypes.length)
| {
80| auto shape = lengths; // DMD variadic bug workaround
80| immutable len = shape.lengthsProduct;
239| auto ret = typeof(return)(shape, len == 0 ? null : (()@trusted=>new T[len].ptr)());
| foreach (i, L; LabelTypes) // static
28| ret._labels[i] = (()@trusted=>new L[shape[i]].ptr)();
80| return ret;
| }
|
| /// ditto
| Slice!(T*, N, Contiguous, Pointers!LabelTypes)
| slice(size_t N)(size_t[N] lengths, T init)
| if (N >= LabelTypes.length)
| {
| import mir.conv: emplaceRef;
| import std.array : uninitializedArray;
5| immutable len = lengths.lengthsProduct;
5| auto arr = uninitializedArray!(Unqual!T[])(len);
135| foreach (ref e; arr)
40| emplaceRef(e, init);
15| auto ret = typeof(return)(lengths, len == 0 ? null : (()@trusted=>cast(T*)arr.ptr)());
| foreach (i, L; LabelTypes) // static
| ret._labels[i] = (()@trusted=>new L[shape[i]].ptr)();
5| return ret;
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow unittest
|{
| import mir.ndslice.slice: Slice;
1| auto tensor = slice!int(5, 6, 7);
1| assert(tensor.length == 5);
1| assert(tensor.length!1 == 6);
1| assert(tensor.elementCount == 5 * 6 * 7);
| static assert(is(typeof(tensor) == Slice!(int*, 3)));
|}
|
|/// 2D DataFrame example
|version(mir_test)
|@safe pure unittest
|{
| import mir.ndslice.slice;
| import mir.ndslice.allocation: slice;
| import mir.date: Date;
|
1| auto dataframe = slice!(double, Date, string)(4, 3);
1| assert(dataframe.length == 4);
1| assert(dataframe.length!1 == 3);
1| assert(dataframe.elementCount == 4 * 3);
|
| static assert(is(typeof(dataframe) ==
| Slice!(double*, 2, Contiguous, Date*, string*)));
|
| // Dataframe labels are contiguous 1-dimensional slices.
|
| // Fill row labels
1| dataframe.label[] = [
| Date(2019, 1, 24),
| Date(2019, 2, 2),
| Date(2019, 2, 4),
| Date(2019, 2, 5),
| ];
|
1| assert(dataframe.label!0[2] == Date(2019, 2, 4));
|
| // Fill column labels
1| dataframe.label!1[] = ["income", "outcome", "balance"];
|
1| assert(dataframe.label!1[2] == "balance");
|
| // Change label element
1| dataframe.label!1[2] = "total";
1| assert(dataframe.label!1[2] == "total");
|
| // Attach a newly allocated label
1| dataframe.label!1 = ["Income", "Outcome", "Balance"].sliced;
|
1| assert(dataframe.label!1[2] == "Balance");
|}
|
|/++
|GC-Allocates an n-dimensional slice.
|Params:
| lengths = List of lengths for each dimension.
| init = Value to initialize with (optional).
|Returns:
| initialzed n-dimensional slice
|+/
|Slice!(T*, N)
| slice(size_t N, T)(size_t[N] lengths, T init)
|{
1| return .slice!T(lengths, init);
|}
|
|// TODO: make it a dataframe compatible. This function performs copy.
|/// ditto
|auto slice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
|{
108| if (__ctfe)
| {
| import mir.ndslice.topology: flattened;
| import mir.array.allocation: array;
0000000| return slice.flattened.array.sliced(slice.shape);
| }
| else
| {
| import mir.conv: emplaceRef;
| alias E = slice.DeepElement;
|
216| auto result = (() @trusted => slice.shape.uninitSlice!(Unqual!E))();
|
| import mir.algorithm.iteration: each;
108| each!(emplaceRef!E)(result, slice);
|
216| return (() @trusted => cast(Slice!(E*, N)) result)();
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow unittest
|{
1| auto tensor = slice([2, 3], 5);
1| assert(tensor.elementCount == 2 * 3);
1| assert(tensor[1, 1] == 5);
|
| // creates duplicate using `slice`
1| auto dup = tensor.slice;
1| assert(dup == tensor);
|}
|
|/// ditto
|auto slice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation)
|{
| alias T = Unqual!(concatenation.DeepElement);
| static if (hasElaborateAssign!T)
| alias fun = .slice;
| else
| alias fun = .uninitSlice;
42| auto ret = (()@trusted => fun!T(concatenation.shape))();
21| ret.opIndexAssign(concatenation);
21| return ret;
|}
|
|///
|version(mir_test)
|@safe pure nothrow unittest
|{
| import mir.ndslice.slice: Slice;
| import mir.ndslice.topology : iota;
| import mir.ndslice.concatenation;
1| auto tensor = concatenation([2, 3].iota, [3].iota(6)).slice;
1| assert(tensor == [3, 3].iota);
|
| static assert(is(typeof(tensor) == Slice!(ptrdiff_t*, 2)));
|}
|
|/++
|GC-Allocates a bitwise packed n-dimensional boolean slice.
|Params:
| lengths = List of lengths for each dimension.
|Returns:
| n-dimensional bitwise slice
|See_also: $(SUBREF topology, bitwise).
|+/
|Slice!(FieldIterator!(BitField!(size_t*)), N) bitSlice(size_t N)(size_t[N] lengths...)
|{
| import mir.ndslice.topology: bitwise;
| enum elen = size_t.sizeof * 8;
10| immutable len = lengths.lengthsProduct;
10| immutable dlen = (len / elen + (len % elen != 0));
10| return new size_t[dlen].sliced.bitwise[0 .. len].sliced(lengths);
|}
|
|/// 1D
|@safe pure version(mir_test) unittest
|{
1| auto bitarray = bitSlice(100); // allocates 16 bytes total
1| assert(bitarray.shape == [100]);
1| assert(bitarray[72] == false);
1| bitarray[72] = true;
1| assert(bitarray[72] == true);
|}
|
|/// 2D
|@safe pure version(mir_test) unittest
|{
1| auto bitmatrix = bitSlice(20, 6); // allocates 16 bytes total
1| assert(bitmatrix.shape == [20, 6]);
1| assert(bitmatrix[3, 4] == false);
1| bitmatrix[3, 4] = true;
1| assert(bitmatrix[3, 4] == true);
|}
|
|/++
|GC-Allocates an uninitialized n-dimensional slice.
|Params:
| lengths = list of lengths for each dimension
|Returns:
| contiguous uninitialized n-dimensional slice
|+/
|auto uninitSlice(T, size_t N)(size_t[N] lengths...)
|{
485| immutable len = lengths.lengthsProduct;
| import std.array : uninitializedArray;
485| auto arr = uninitializedArray!(T[])(len);
| version (mir_secure_memory)
| {()@trusted{
| (cast(ubyte[])arr)[] = 0;
| }();}
485| return arr.sliced(lengths);
|}
|
|///
|version(mir_test)
|@safe pure nothrow unittest
|{
| import mir.ndslice.slice: Slice;
1| auto tensor = uninitSlice!int(5, 6, 7);
1| assert(tensor.length == 5);
1| assert(tensor.elementCount == 5 * 6 * 7);
| static assert(is(typeof(tensor) == Slice!(int*, 3)));
|}
|
|/++
|GC-Allocates an uninitialized aligned an n-dimensional slice.
|Params:
| lengths = list of lengths for each dimension
| alignment = memory alignment (bytes)
|Returns:
| contiguous uninitialized n-dimensional slice
|+/
|auto uninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @system
|{
1| immutable len = lengths.lengthsProduct;
| import std.array : uninitializedArray;
2| assert((alignment != 0) && ((alignment & (alignment - 1)) == 0), "'alignment' must be a power of two");
2| size_t offset = alignment <= 16 ? 0 : alignment - 1;
1| void* basePtr = uninitializedArray!(byte[])(len * T.sizeof + offset).ptr;
1| T* alignedPtr = cast(T*)((cast(size_t)(basePtr) + offset) & ~offset);
1| return alignedPtr.sliced(lengths);
|}
|
|///
|version(mir_test)
|@system pure nothrow unittest
|{
| import mir.ndslice.slice: Slice;
1| auto tensor = uninitAlignedSlice!double([5, 6, 7], 64);
1| tensor[] = 0;
1| assert(tensor.length == 5);
1| assert(tensor.elementCount == 5 * 6 * 7);
1| assert(cast(size_t)(tensor.ptr) % 64 == 0);
| static assert(is(typeof(tensor) == Slice!(double*, 3)));
|}
|
|/++
|Allocates an array through a specified allocator and creates an n-dimensional slice over it.
|See also $(MREF std, experimental, allocator).
|Params:
| alloc = allocator
| lengths = list of lengths for each dimension
| init = default value for array initialization
| slice = slice to copy shape and data from
|Returns:
| a structure with fields `array` and `slice`
|Note:
| `makeSlice` always returns slice with mutable elements
|+/
|auto makeSlice(Allocator, size_t N, Iterator)(auto ref Allocator alloc, Slice!(N, Iterator) slice)
|{
| alias T = Unqual!(slice.DeepElement);
| return makeSlice!(T)(alloc, slice);
|}
|
|/// ditto
|Slice!(T*, N)
|makeSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...)
|{
| import std.experimental.allocator : makeArray;
1| return alloc.makeArray!T(lengths.lengthsProduct).sliced(lengths);
|}
|
|/// ditto
|Slice!(T*, N)
|makeSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths, T init)
|{
| import std.experimental.allocator : makeArray;
2| immutable len = lengths.lengthsProduct;
2| auto array = alloc.makeArray!T(len, init);
2| return array.sliced(lengths);
|}
|
|/// ditto
|auto makeSlice(Allocator, Iterator, size_t N, SliceKind kind)
| (auto ref Allocator allocator, Slice!(Iterator, N, kind) slice)
|{
| import mir.conv: emplaceRef;
| alias E = slice.DeepElement;
|
1| auto result = allocator.makeUninitSlice!(Unqual!E)(slice.shape);
|
| import mir.algorithm.iteration: each;
1| each!(emplaceRef!E)(result, slice);
|
1| return cast(Slice!(E*, N)) result;
|}
|
|/// Initialization with default value
|version(mir_test)
|@nogc unittest
|{
| import std.experimental.allocator;
| import std.experimental.allocator.mallocator;
| import mir.algorithm.iteration: all;
| import mir.ndslice.topology: map;
|
1| auto sl = Mallocator.instance.makeSlice([2, 3, 4], 10);
1| auto ar = sl.field;
1| assert(sl.all!"a == 10");
|
1| auto sl2 = Mallocator.instance.makeSlice(sl.map!"a * 2");
1| auto ar2 = sl2.field;
1| assert(sl2.all!"a == 20");
|
1| Mallocator.instance.dispose(ar);
1| Mallocator.instance.dispose(ar2);
|}
|
|version(mir_test)
|@nogc unittest
|{
| import std.experimental.allocator;
| import std.experimental.allocator.mallocator;
|
| // cast to your own type
1| auto sl = makeSlice!double(Mallocator.instance, [2, 3, 4], 10);
1| auto ar = sl.field;
1| assert(sl[1, 1, 1] == 10.0);
1| Mallocator.instance.dispose(ar);
|}
|
|/++
|Allocates an uninitialized array through a specified allocator and creates an n-dimensional slice over it.
|See also $(MREF std, experimental, allocator).
|Params:
| alloc = allocator
| lengths = list of lengths for each dimension
|Returns:
| a structure with fields `array` and `slice`
|+/
|Slice!(T*, N)
|makeUninitSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...)
| if (N)
|{
8| if (immutable len = lengths.lengthsProduct)
| {
8| auto mem = alloc.allocate(len * T.sizeof);
8| if (mem.length == 0) assert(0);
16| auto array = () @trusted { return cast(T[]) mem; }();
| version (mir_secure_memory)
| {() @trusted {
| (cast(ubyte[])array)[] = 0;
| }();}
8| return array.sliced(lengths);
| }
| else
| {
0000000| return T[].init.sliced(lengths);
| }
|}
|
|///
|version(mir_test)
|@system @nogc unittest
|{
| import std.experimental.allocator;
| import std.experimental.allocator.mallocator;
|
1| auto sl = makeUninitSlice!int(Mallocator.instance, 2, 3, 4);
1| auto ar = sl.field;
1| assert(ar.ptr is sl.iterator);
1| assert(ar.length == 24);
1| assert(sl.elementCount == 24);
|
1| Mallocator.instance.dispose(ar);
|}
|
|/++
|Allocates a common n-dimensional array from a slice.
|Params:
| slice = slice
|Returns:
| multidimensional D array
|+/
|auto ndarray(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
|{
| import mir.array.allocation : array;
| static if (slice.N == 1)
| {
3| return slice.array;
| }
| else
| {
| import mir.ndslice.topology: ipack, map;
1| return slice.ipack!1.map!(.ndarray).array;
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow unittest
|{
| import mir.ndslice.topology : iota;
1| auto slice = iota(3, 4);
1| auto m = slice.ndarray;
| static assert(is(typeof(m) == sizediff_t[][])); // sizediff_t is long for 64 bit platforms
1| assert(m == [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]);
|}
|
|/++
|Allocates a common n-dimensional array using data from a slice.
|Params:
| alloc = allocator (optional)
| slice = slice
|Returns:
| multidimensional D array
|+/
|auto makeNdarray(T, Allocator, Iterator, size_t N, SliceKind kind)(auto ref Allocator alloc, Slice!(Iterator, N, kind) slice)
|{
| import std.experimental.allocator : makeArray;
| static if (slice.N == 1)
| {
3| return makeArray!T(alloc, slice);
| }
| else
| {
| alias E = typeof(makeNdarray!T(alloc, slice[0]));
1| auto ret = makeArray!E(alloc, slice.length);
15| foreach (i, ref e; ret)
3| e = .makeNdarray!T(alloc, slice[i]);
1| return ret;
| }
|}
|
|///
|version(mir_test)
|@nogc unittest
|{
| import std.experimental.allocator;
| import std.experimental.allocator.mallocator;
| import mir.ndslice.topology : iota;
|
1| auto slice = iota(3, 4);
1| auto m = Mallocator.instance.makeNdarray!long(slice);
|
| static assert(is(typeof(m) == long[][]));
|
| static immutable ar = [[0L, 1, 2, 3], [4L, 5, 6, 7], [8L, 9, 10, 11]];
1| assert(m == ar);
|
12| foreach (ref row; m)
3| Mallocator.instance.dispose(row);
1| Mallocator.instance.dispose(m);
|}
|
|/++
|Shape of a common n-dimensional array.
|Params:
| array = common n-dimensional array
| err = error flag passed by reference
|Returns:
| static array of dimensions type of `size_t[n]`
|+/
|auto shape(T)(T[] array, ref int err)
|{
| static if (isDynamicArray!T)
| {
4| size_t[1 + typeof(shape(T.init, err)).length] ret;
|
4| if (array.length)
| {
3| ret[0] = array.length;
3| ret[1..$] = shape(array[0], err);
3| if (err)
0000000| goto L;
25| foreach (ar; array)
| {
6| if (shape(ar, err) != ret[1..$])
1| err = 1;
6| if (err)
1| goto L;
| }
| }
| }
| else
| {
9| size_t[1] ret;
9| ret[0] = array.length;
| }
12| err = 0;
|L:
13| return ret;
|}
|
|///
|version(mir_test)
|@safe pure unittest
|{
1| int err;
1| size_t[2] shape = [[1, 2, 3], [4, 5, 6]].shape(err);
1| assert(err == 0);
1| assert(shape == [2, 3]);
|
1| [[1, 2], [4, 5, 6]].shape(err);
1| assert(err == 1);
|}
|
|/// Slice from ndarray
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation: slice, shape;
1| int err;
1| auto array = [[1, 2, 3], [4, 5, 6]];
1| auto s = array.shape(err).slice!int;
1| s[] = [[1, 2, 3], [4, 5, 6]];
1| assert(s == array);
|}
|
|version(mir_test)
|@safe pure unittest
|{
1| int err;
1| size_t[2] shape = (int[][]).init.shape(err);
1| assert(shape[0] == 0);
1| assert(shape[1] == 0);
|}
|
|version(mir_test)
|nothrow unittest
|{
| import mir.ndslice.allocation;
| import mir.ndslice.topology : iota;
|
1| auto sl = iota([0, 0], 1);
|
1| assert(sl.empty!0);
1| assert(sl.empty!1);
|
1| auto gcsl1 = sl.slice;
1| auto gcsl2 = slice!double(0, 0);
|
| import std.experimental.allocator;
| import std.experimental.allocator.mallocator;
|
1| auto sl2 = makeSlice!double(Mallocator.instance, 0, 0);
|
1| Mallocator.instance.dispose(sl2.field);
|}
|
|/++
|Allocates an uninitialized array using `core.stdc.stdlib.malloc` and creates an n-dimensional slice over it.
|Params:
| lengths = list of lengths for each dimension
|Returns:
| contiguous uninitialized n-dimensional slice
|See_also:
| $(LREF stdcSlice), $(LREF stdcFreeSlice)
|+/
|Slice!(T*, N) stdcUninitSlice(T, size_t N)(size_t[N] lengths...)
|{
| import core.stdc.stdlib: malloc;
2| immutable len = lengths.lengthsProduct;
2| auto p = malloc(len * T.sizeof);
2| if (p is null) assert(0);
| version (mir_secure_memory)
| {
| (cast(ubyte*)p)[0 .. len * T.sizeof] = 0;
| }
4| auto ptr = len ? cast(T*) p : null;
2| return ptr.sliced(lengths);
|}
|
|/++
|Allocates a copy of a slice using `core.stdc.stdlib.malloc`.
|Params:
| slice = n-dimensional slice
|Returns:
| contiguous n-dimensional slice
|See_also:
| $(LREF stdcUninitSlice), $(LREF stdcFreeSlice)
|+/
|auto stdcSlice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
|{
| alias E = slice.DeepElement;
| alias T = Unqual!E;
| static assert (!hasElaborateAssign!T, "stdcSlice is not miplemented for slices that have elaborate assign");
1| auto ret = stdcUninitSlice!T(slice.shape);
|
| import mir.conv: emplaceRef;
| import mir.algorithm.iteration: each;
1| each!(emplaceRef!E)(ret, slice);
1| return ret;
|}
|
|/++
|Frees memory using `core.stdc.stdlib.free`.
|Params:
| slice = n-dimensional slice
|See_also:
| $(LREF stdcSlice), $(LREF stdcUninitSlice)
|+/
|void stdcFreeSlice(T, size_t N)(Slice!(T*, N) slice)
|{
| import core.stdc.stdlib: free;
| version (mir_secure_memory)
| {
| (cast(ubyte[])slice.field)[] = 0;
| }
2| slice._iterator.free;
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.topology: iota;
|
1| auto i = iota(3, 4);
1| auto s = i.stdcSlice;
1| auto t = s.shape.stdcUninitSlice!size_t;
|
1| t[] = s;
|
1| assert(t == i);
|
1| s.stdcFreeSlice;
1| t.stdcFreeSlice;
|}
|
|/++
|Allocates an uninitialized aligned array using `core.stdc.stdlib.malloc` and creates an n-dimensional slice over it.
|Params:
| lengths = list of lengths for each dimension
| alignment = memory alignment (bytes)
|Returns:
| contiguous uninitialized n-dimensional slice
|+/
|auto stdcUninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @system
|{
1| immutable len = lengths.lengthsProduct;
| import mir.internal.memory: alignedAllocate;
1| auto arr = (cast(T*)alignedAllocate(len * T.sizeof, alignment))[0 .. len];
| version (mir_secure_memory)
| {
| (cast(ubyte[])arr)[] = 0;
| }
1| return arr.sliced(lengths);
|}
|
|///
|version(mir_test)
|@system pure nothrow unittest
|{
| import mir.ndslice.slice: Slice;
1| auto tensor = stdcUninitAlignedSlice!double([5, 6, 7], 64);
1| assert(tensor.length == 5);
1| assert(tensor.elementCount == 5 * 6 * 7);
1| assert(cast(size_t)(tensor.ptr) % 64 == 0);
| static assert(is(typeof(tensor) == Slice!(double*, 3)));
1| stdcFreeAlignedSlice(tensor);
|}
|
|/++
|Frees aligned memory allocaged by CRuntime.
|Params:
| slice = n-dimensional slice
|See_also:
| $(LREF stdcSlice), $(LREF stdcUninitSlice)
|+/
|void stdcFreeAlignedSlice(T, size_t N)(Slice!(T*, N) slice)
|{
| import mir.internal.memory: alignedFree;
| version (mir_secure_memory)
| {
| (cast(ubyte[])slice.field)[] = 0;
| }
1| slice._iterator.alignedFree;
|}
source/mir/ndslice/allocation.d is 98% covered
<<<<<< EOF
# path=./source-mir-array-allocation.lst
|/**
|Functions and types that manipulate built-in arrays and associative arrays.
|
|This module provides all kinds of functions to create, manipulate or convert arrays:
|
|$(SCRIPT inhibitQuickIndex = 1;)
|$(BOOKTABLE ,
|$(TR $(TH Function Name) $(TH Description)
|)
| $(TR $(TD $(LREF _array))
| $(TD Returns a copy of the input in a newly allocated dynamic _array.
| ))
|)
|
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|
|Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis
|
|Source: $(PHOBOSSRC std/_array.d)
|*/
|module mir.array.allocation;
|
|import mir.functional;
|import mir.primitives;
|import std.traits;
|
|/**
| * Allocates an array and initializes it with copies of the elements
| * of range $(D r).
| *
| * Narrow strings are handled as a special case in an overload.
| *
| * Params:
| * r = range (or aggregate with $(D opApply) function) whose elements are copied into the allocated array
| * Returns:
| * allocated and initialized array
| */
|auto array(Range)(Range r)
|if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !__traits(isStaticArray, Range) || isPointer!Range && (isInputRange!(PointerTarget!Range) || isIterable!(PointerTarget!Range)))
|{
| static if (isIterable!Range)
| alias E = ForeachType!Range;
| else
| static if (isPointer!Range && isIterable!(PointerTarget!Range))
| alias E = ForeachType!(PointerTarget!Range);
| else
| alias E = ElementType!Range;
|
43| if (__ctfe)
| {
| // Compile-time version to avoid memcpy calls.
| // Also used to infer attributes of array().
0000000| E[] result;
| static if (isInputRange!Range)
0000000| for (; !r.empty; r.popFront)
0000000| result ~= r.front;
| else
| static if (isPointer!Range)
0000000| foreach (e; *r)
0000000| result ~= e;
| else
0000000| foreach (e; r)
0000000| result ~= e;
0000000| return result;
| }
|
| import mir.primitives: hasLength;
|
| static if (hasLength!Range)
| {
33| auto length = r.length;
33| if (length == 0)
0000000| return null;
|
| import mir.conv : emplaceRef;
| import std.array: uninitializedArray;
|
66| auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))();
|
| static if (isInputRange!Range)
| {
15663| foreach(ref e; result)
| {
5189| emplaceRef!E(e, r.front);
5097| r.popFront;
| }
| }
| else
| static if (isPointer!Range)
| {
1| auto it = result;
15| foreach(ref f; *r)
| {
4| emplaceRef!E(it[0], f);
4| it = it[1 .. $];
| }
| }
| else
| {
| auto it = result;
| foreach (f; r)
| {
| import mir.functional: forward;
| emplaceRef!E(it[0], forward!f);
| it = it[1 .. $];
| }
| }
|
66| return (() @trusted => cast(E[]) result)();
| }
| else
| {
| import mir.appender: scopedBuffer;
| import std.array: uninitializedArray;
|
20| auto a = scopedBuffer!(Unqual!E);
|
| static if (isInputRange!Range)
127| for (; !r.empty; r.popFront)
59| a.put(r.front);
| else
| static if (isPointer!Range)
| {
| foreach (e; *r)
| a.put(forward!e);
| }
| else
| {
11| foreach (e; r)
10| a.put(forward!e);
| }
|
10| return () @trusted {
10| auto ret = uninitializedArray!(Unqual!E[])(a.length);
10| a.moveDataAndEmplaceTo(ret);
10| return ret;
| } ();
| }
|}
|
|///
|@safe pure nothrow version(mir_test) unittest
|{
1| auto a = array([1, 2, 3, 4, 5][]);
1| assert(a == [ 1, 2, 3, 4, 5 ]);
|}
|
|@safe pure nothrow version(mir_test) unittest
|{
| import mir.algorithm.iteration : equal;
| struct Foo
| {
| int a;
| }
1| auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
1| assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
|}
|
|@safe pure nothrow version(mir_test) unittest
|{
| struct MyRange
| {
| enum front = 123;
| enum empty = true;
| void popFront() {}
| }
|
1| auto arr = (new MyRange).array;
1| assert(arr.empty);
|}
|
|@system pure nothrow version(mir_test) unittest
|{
1| immutable int[] a = [1, 2, 3, 4];
1| auto b = (&a).array;
1| assert(b == a);
|}
|
|@system version(mir_test) unittest
|{
| import mir.algorithm.iteration : equal;
| struct Foo
| {
| int a;
| void opAssign(Foo)
| {
| assert(0);
| }
| auto opEquals(Foo foo)
| {
5| return a == foo.a;
| }
| }
1| auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
1| assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
|}
|
|@safe version(mir_test) unittest
|{
| // Issue 12315
| static struct Bug12315 { immutable int i; }
| enum bug12315 = [Bug12315(123456789)].array();
| static assert(bug12315[0].i == 123456789);
|}
|
|@safe version(mir_test) unittest
|{
| import mir.ndslice.topology: repeat;
| static struct S{int* p;}
1| auto a = array(immutable(S).init.repeat(5));
1| assert(a.length == 5);
|}
|
|///
|@safe version(mir_test) unittest
|{
1| assert("Hello D".array == "Hello D");
1| assert("Hello D"w.array == "Hello D"w);
1| assert("Hello D"d.array == "Hello D"d);
|}
|
|@system version(mir_test) unittest
|{
| // @system due to array!string
| import std.conv : to;
|
0000000| static struct TestArray { int x; string toString() @safe { return to!string(x); } }
|
| static struct OpAssign
| {
| uint num;
4| this(uint num) { this.num = num; }
|
| // Templating opAssign to make sure the bugs with opAssign being
| // templated are fixed.
0000000| void opAssign(T)(T rhs) { this.num = rhs.num; }
| }
|
| static struct OpApply
| {
| int opApply(scope int delegate(ref int) dg)
| {
1| int res;
33| foreach (i; 0 .. 10)
| {
10| res = dg(i);
10| if (res) break;
| }
|
1| return res;
| }
| }
|
1| auto a = array([1, 2, 3, 4, 5][]);
1| assert(a == [ 1, 2, 3, 4, 5 ]);
|
1| auto b = array([TestArray(1), TestArray(2)][]);
1| assert(b == [TestArray(1), TestArray(2)]);
|
| class C
| {
| int x;
4| this(int y) { x = y; }
0000000| override string toString() const @safe { return to!string(x); }
| }
1| auto c = array([new C(1), new C(2)][]);
1| assert(c[0].x == 1);
1| assert(c[1].x == 2);
|
1| auto d = array([1.0, 2.2, 3][]);
1| assert(is(typeof(d) == double[]));
1| assert(d == [1.0, 2.2, 3]);
|
1| auto e = [OpAssign(1), OpAssign(2)];
1| auto f = array(e);
1| assert(e == f);
|
1| assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
1| assert(array("ABC") == "ABC");
1| assert(array("ABC".dup) == "ABC");
|}
|
|//Bug# 8233
|@safe version(mir_test) unittest
|{
1| assert(array("hello world"d) == "hello world"d);
1| immutable a = [1, 2, 3, 4, 5];
1| assert(array(a) == a);
1| const b = a;
1| assert(array(b) == a);
|
| //To verify that the opAssign branch doesn't get screwed up by using Unqual.
| //EDIT: array no longer calls opAssign.
| struct S
| {
| ref S opAssign(S)(const ref S rhs)
| {
| assert(0);
| }
|
| int i;
| }
|
| alias AliasSeq(T...) = T;
| foreach (T; AliasSeq!(S, const S, immutable S))
| {
3| auto arr = [T(1), T(2), T(3), T(4)];
3| assert(array(arr) == arr);
| }
|}
|
|@safe version(mir_test) unittest
|{
| //9824
| static struct S
| {
| @disable void opAssign(S);
| int i;
| }
1| auto arr = [S(0), S(1), S(2)];
1| arr.array;
|}
|
|// Bugzilla 10220
|@safe version(mir_test) unittest
|{
| import mir.algorithm.iteration : equal;
| import std.exception;
| import mir.ndslice.topology: repeat;
|
| static struct S
| {
| int val;
|
| @disable this();
4| this(int v) { val = v; }
| }
| static immutable r = S(1).repeat(2).array();
1| assert(equal(r, [S(1), S(1)]));
|}
|
|@safe version(mir_test) unittest
|{
| //Turn down infinity:
| static assert(!is(typeof(
| repeat(1).array()
| )));
|}
source/mir/array/allocation.d is 85% covered
<<<<<< EOF
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-complex.lst
|/++
|Complex numbers
|
|Authors: Ilya Yaroshenko
|+/
|module mir.complex;
|
|import mir.math.common: optmath;
|
|private alias CommonType(A, B) = typeof(A.init + B.init);
|
|@optmath:
|
|/++
|Generic complex number type
|+/
|struct Complex(T)
| if (is(T == float) || is(T == double) || is(T == real))
|{
| import mir.internal.utility: isComplex;
| import std.traits: isNumeric;
|
|@optmath:
|
| /++
| Real part. Default value is zero.
| +/
| T re = 0;
| /++
| Imaginary part. Default value is zero.
| +/
| T im = 0;
|
| ///
| ref Complex opAssign(R)(Complex!R rhs)
| if (!is(R == T))
| {
| this.re = rhs.re;
| this.im = rhs.im;
| return this;
| }
|
| ///
| ref Complex opAssign(F)(const F rhs)
| if (isNumeric!F)
| {
| this.re = rhs;
| this.im = 0;
| return this;
| }
|
| ///
| ref Complex opOpAssign(string op : "+", R)(Complex!R rhs) return
| {
| re += rhs.re;
| im += rhs.im;
| return this;
| }
|
| ///
| ref Complex opOpAssign(string op : "-", R)(Complex!R rhs) return
| {
| re -= rhs.re;
| im -= rhs.im;
| return this;
| }
|
| ///
| ref Complex opOpAssign(string op, R)(Complex!R rhs) return
| if (op == "*" || op == "/")
| {
| return this = this.opBinary!op(rhs);
| }
|
| ///
| ref Complex opOpAssign(string op : "+", R)(const R rhs) return
| if (isNumeric!R)
| {
| re += rhs;
| return this;
| }
|
| ///
| ref Complex opOpAssign(string op : "-", R)(const R rhs) return
| if (isNumeric!R)
| {
| re -= rhs;
| return this;
| }
|
| ///
| ref Complex opOpAssign(string op : "*", R)(const R rhs) return
| if (isNumeric!R)
| {
| re *= rhs;
| return this;
| }
|
| ///
| ref Complex opOpAssign(string op : "/", R)(const R rhs) return
| if (isNumeric!R)
| {
| re /= rhs;
| return this;
| }
|
|const:
|
| ///
| bool opEquals(const Complex rhs)
| {
0000000| return re == rhs.re && im == rhs.im;
| }
|
| ///
| size_t toHash()
| {
0000000| T[2] val = [re, im];
0000000| return hashOf(val) ;
| }
|
|scope:
|
| ///
| bool opEquals(R)(Complex!R rhs)
| if (!is(R == T))
| {
| return re == rhs.re && im == rhs.im;
| }
|
| ///
| bool opEquals(F)(const F rhs)
| if (isNumeric!F)
| {
| return re == rhs && im == 0;
| }
|
| ///
| Complex opUnary(string op : "+")()
| {
| return this;
| }
|
| ///
| Complex opUnary(string op : "-")()
| {
| return typeof(return)(-re, -im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(Complex!R rhs)
| {
| return typeof(return)(re + rhs.re, im + rhs.im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(Complex!R rhs)
| {
| return typeof(return)(re - rhs.re, im - rhs.im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(Complex!R rhs)
| {
| return typeof(return)(re * rhs.re - im * rhs.im, re * rhs.im + im * rhs.re);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(Complex!R rhs)
| {
| // TODO: use more precise algorithm
| auto norm = rhs.re * rhs.re + rhs.im * rhs.im;
| return typeof(return)(
| (re * rhs.re + im * rhs.im) / norm,
| (im * rhs.re - re * rhs.im) / norm,
| );
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(const R rhs)
| if (isNumeric!R)
| {
| return typeof(return)(re + rhs, im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(const R rhs)
| if (isNumeric!R)
| {
| return typeof(return)(re - rhs, im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(const R rhs)
| if (isNumeric!R)
| {
| return typeof(return)(re * rhs, im * rhs);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(const R rhs)
| if (isNumeric!R)
| {
| return typeof(return)(re / rhs, im / rhs);
| }
|
|
| ///
| Complex!(CommonType!(T, R)) opBinaryRight(string op : "+", R)(const R rhs)
| if (isNumeric!R)
| {
| return typeof(return)(rhs + re, im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinaryRight(string op : "-", R)(const R rhs)
| if (isNumeric!R)
| {
| return typeof(return)(rhs - re, -im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinaryRight(string op : "*", R)(const R rhs)
| if (isNumeric!R)
| {
| return typeof(return)(rhs * re, rhs * im);
| }
|
| ///
| Complex!(CommonType!(T, R)) opBinaryRight(string op : "/", R)(const R rhs)
| if (isNumeric!R)
| {
| // TODO: use more precise algorithm
| auto norm = this.re * this.re + this.im * this.im;
| return typeof(return)(
| rhs * (this.re / norm),
| -rhs * (this.im / norm),
| );
| }
|
| ///
| R opCast(R)()
| if (isNumeric!R || isComplex!R)
| {
| static if (isNumeric!R)
| return cast(R) re;
| else
| return R(re, im);
| }
|}
|
|/// ditto
|Complex!T complex(T)(const T re, const T im)
| if (is(T == float) || is(T == double) || is(T == real))
|{
| return typeof(return)(re, im);
|}
|
|private alias _cdouble_ = Complex!double;
|private alias _cfloat_ = Complex!float;
|private alias _creal_ = Complex!real;
|
|///
|unittest
|{
| auto a = complex(1.0, 3);
| auto b = a;
| b.re += 3;
| a = b;
| assert(a == b);
|
| a = Complex!float(5, 6);
| assert(a == Complex!real(5, 6));
|
| a += b;
| a -= b;
| a *= b;
| a /= b;
|
| a = a + b;
| a = a - b;
| a = a * b;
| a = a / b;
|
| a += 2;
| a -= 2;
| a *= 2;
| a /= 2;
|
| a = a + 2;
| a = a - 2;
| a = a * 2;
| a = a / 2;
|
| a = 2 + a;
| a = 2 - a;
| a = 2 * a;
| a = 2 / a;
|
| a = -a;
| a = +a;
|
| assert(a != 4.0);
| a = 4;
| assert(a == 4);
| assert(cast(int)a == 4);
| assert(cast(Complex!float)a == 4);
|
| import std.complex : StdComplex = Complex;
| assert(cast(StdComplex!double)a == StdComplex!double(4, 0));
|}
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/complex.d is 0% covered
<<<<<< EOF
# path=./source-mir-ndslice-traits.lst
|/++
|$(H2 Multidimensional traits)
|
|This is a submodule of $(MREF mir,ndslice).
|
|$(BOOKTABLE $(H2 Function),
|$(TR $(TH Function Name) $(TH Description))
|
|$(T2 isVector, Test if type is a one-dimensional slice.)
|$(T2 isMatrix, Test if type is a two-dimensional slice.)
|$(T2 isContiguousSlice, Test if type is a contiguous slice.)
|$(T2 isCanonicalSlice, Test if type is a canonical slice.)
|$(T2 isUniversalSlice, Test if type is a universal slice.)
|$(T2 isContiguousVector, Test if type is a contiguous one-dimensional slice.)
|$(T2 isUniversalVector, Test if type is a universal one-dimensional slice.)
|$(T2 isContiguousMatrix, Test if type is a contiguous two-dimensional slice.)
|$(T2 isCanonicalMatrix, Test if type is a canonical two-dimensional slice.)
|$(T2 isUniversalMatrix, Test if type is a universal two-dimensional slice.)
|$(T2 isIterator, Test if type is a random access iterator.)
|)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: John Hall
|
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|
|module mir.ndslice.traits;
|
|import mir.ndslice.slice : Slice, SliceKind, Contiguous, Universal, Canonical;
|
|/// Test if type is a one-dimensional slice.
|enum bool isVector(T) = is(T : Slice!(Iterator, 1, kind), SliceKind kind, Iterator);
|
|/// Test if type is a two-dimensional slice.
|enum bool isMatrix(T) = is(T : Slice!(Iterator, 2, kind), SliceKind kind, Iterator);
|
|/// Test if type is a contiguous slice.
|enum bool isContiguousSlice(T) = is(T : Slice!(Iterator, N, Contiguous), Iterator, size_t N);
|
|/// Test if type is a canonical slice.
|enum bool isCanonicalSlice(T) = is(T : Slice!(Iterator, N, Canonical), Iterator, size_t N);
|
|/// Test if type is a universal slice.
|enum bool isUniversalSlice(T) = is(T : Slice!(Iterator, N, Universal), Iterator, size_t N);
|
|/// Test if type is a contiguous one-dimensional slice.
|enum bool isContiguousVector(T) = is(T : Slice!(Iterator, 1, Contiguous), Iterator);
|
|/// Test if type is a universal one-dimensional slice.
|enum bool isUniversalVector(T) = is(T : Slice!(Iterator, 1, Universal), Iterator);
|
|/// Test if type is a contiguous two-dimensional slice.
|enum bool isContiguousMatrix(T) = is(T : Slice!(Iterator, 2, Contiguous), Iterator);
|
|/// Test if type is a canonical two-dimensional slice.
|enum bool isCanonicalMatrix(T) = is(T : Slice!(Iterator, 2, Canonical), Iterator);
|
|/// Test if type is a universal two-dimensional slice.
|enum bool isUniversalMatrix(T) = is(T : Slice!(Iterator, 2, Universal), Iterator);
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.slice : Slice;
|
| alias S1 = Slice!(int*);
| static assert(isContiguousVector!S1);
| static assert(!isUniversalVector!S1);
|
| static assert(!isContiguousMatrix!S1);
| static assert(!isCanonicalMatrix!S1);
| static assert(!isUniversalMatrix!S1);
|
| static assert(isVector!S1);
| static assert(!isMatrix!S1);
|
| static assert(isContiguousSlice!S1);
| static assert(!isCanonicalSlice!S1);
| static assert(!isUniversalSlice!S1);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias S2 = Slice!(float*, 1, Universal);
| static assert(!isContiguousVector!S2);
| static assert(isUniversalVector!S2);
|
| static assert(!isContiguousMatrix!S2);
| static assert(!isCanonicalMatrix!S2);
| static assert(!isUniversalMatrix!S2);
|
| static assert(isVector!S2);
| static assert(!isMatrix!S2);
|
| static assert(!isContiguousSlice!S2);
| static assert(!isCanonicalSlice!S2);
| static assert(isUniversalSlice!S2);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias S3 = Slice!(byte*, 2);
| static assert(!isContiguousVector!S3);
| static assert(!isUniversalVector!S3);
|
| static assert(isContiguousMatrix!S3);
| static assert(!isCanonicalMatrix!S3);
| static assert(!isUniversalMatrix!S3);
|
| static assert(!isVector!S3);
| static assert(isMatrix!S3);
|
| static assert(isContiguousSlice!S3);
| static assert(!isCanonicalSlice!S3);
| static assert(!isUniversalSlice!S3);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias S4 = Slice!(int*, 2, Canonical);
| static assert(!isContiguousVector!S4);
| static assert(!isUniversalVector!S4);
|
| static assert(!isContiguousMatrix!S4);
| static assert(isCanonicalMatrix!S4);
| static assert(!isUniversalMatrix!S4);
|
| static assert(!isVector!S4);
| static assert(isMatrix!S4);
|
| static assert(!isContiguousSlice!S4);
| static assert(isCanonicalSlice!S4);
| static assert(!isUniversalSlice!S4);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias S5 = Slice!(int*, 2, Universal);
| static assert(!isContiguousVector!S5);
| static assert(!isUniversalVector!S5);
|
| static assert(!isContiguousMatrix!S5);
| static assert(!isCanonicalMatrix!S5);
| static assert(isUniversalMatrix!S5);
|
| static assert(!isVector!S5);
| static assert(isMatrix!S5);
|
| static assert(!isContiguousSlice!S5);
| static assert(!isCanonicalSlice!S5);
| static assert(isUniversalSlice!S5);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias S6 = Slice!(int*, 3);
|
| static assert(!isContiguousVector!S6);
| static assert(!isUniversalVector!S6);
|
| static assert(!isContiguousMatrix!S6);
| static assert(!isCanonicalMatrix!S6);
| static assert(!isUniversalMatrix!S6);
|
| static assert(!isVector!S6);
| static assert(!isMatrix!S6);
|
| static assert(isContiguousSlice!S6);
| static assert(!isCanonicalSlice!S6);
| static assert(!isUniversalSlice!S6);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias S7 = Slice!(int*, 3, Canonical);
|
| static assert(!isContiguousVector!S7);
| static assert(!isUniversalVector!S7);
|
| static assert(!isContiguousMatrix!S7);
| static assert(!isCanonicalMatrix!S7);
| static assert(!isUniversalMatrix!S7);
|
| static assert(!isVector!S7);
| static assert(!isMatrix!S7);
|
| static assert(!isContiguousSlice!S7);
| static assert(isCanonicalSlice!S7);
| static assert(!isUniversalSlice!S7);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| alias S8 = Slice!(int*, 3, Universal);
|
| static assert(!isContiguousVector!S8);
| static assert(!isUniversalVector!S8);
|
| static assert(!isContiguousMatrix!S8);
| static assert(!isCanonicalMatrix!S8);
| static assert(!isUniversalMatrix!S8);
|
| static assert(!isVector!S8);
| static assert(!isMatrix!S8);
|
| static assert(!isContiguousSlice!S8);
| static assert(!isCanonicalSlice!S8);
| static assert(isUniversalSlice!S8);
|}
|
|///
|template isIterator(T)
|{
| enum isIterator = __traits(compiles, (T a, T b)
| {
| sizediff_t diff = a - b;
| ++a;
| ++b;
| --a;
| --b;
| void foo(V)(auto ref V v)
| {
|
| }
| foo(a[sizediff_t(3)]);
| auto c = a + sizediff_t(3);
| auto d = a - sizediff_t(3);
| a += sizediff_t(3);
| a -= sizediff_t(3);
| foo(*a);
| });
|}
source/mir/ndslice/traits.d has no code
<<<<<< EOF
# path=./source-mir-ndslice-mutation.lst
|/++
|$(H2 Multidimensional mutation algorithms)
|
|This is a submodule of $(MREF mir,ndslice).
|
|$(BOOKTABLE $(H2 Function),
|$(TR $(TH Function Name) $(TH Description))
|)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.ndslice.mutation;
|
|import mir.ndslice.slice: Slice, SliceKind;
|
|/++
|Copies n-dimensional minor.
|+/
|void copyMinor(size_t N, IteratorFrom, SliceKind KindFrom, IteratorTo, SliceKind KindTo, IndexIterator)(
| Slice!(IteratorFrom, N, KindFrom) from,
| Slice!(IteratorTo, N, KindTo) to,
| Slice!IndexIterator[N] indices...
|)
|in {
| import mir.internal.utility: Iota;
| static foreach (i; Iota!N)
4| assert(indices[i].length == to.length!i);
|}
|do {
| static if (N == 1)
2| to[] = from[indices[0]];
| else
9| foreach (i; 0 .. indices[0].length)
| {
2| copyMinor!(N - 1)(from[indices[0][i]], to[i], indices[1 .. N]);
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice;
| // 0 1 2 3
| // 4 5 6 7
| // 8 9 10 11
1| auto a = iota!int(3, 4);
1| auto b = slice!int(2, 2);
1| copyMinor(a, b, [2, 1].sliced, [0, 3].sliced);
1| assert(b == [[8, 11], [4, 7]]);
|}
|
|/++
|Reverses data in the 1D slice.
|+/
|void reverseInPlace(Iterator)(Slice!Iterator slice)
|{
| import mir.utility : swap;
9| foreach (i; 0 .. slice.length / 2)
2| swap(slice[i], slice[$ - (i + 1)]);
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice;
1| auto s = 5.iota.slice;
1| s.reverseInPlace;
1| assert([4, 3, 2, 1, 0]);
|}
source/mir/ndslice/mutation.d is 100% covered
<<<<<< EOF
# path=./source-mir-ndslice-ndfield.lst
|/++
|This is a submodule of $(MREF mir,ndslice).
|
|NdField is a type with `opIndex(size_t[N] index...)` primitive.
|An ndslice can be created on top of a ndField using $(SUBREF slice, slicedNdField).
|
|$(BOOKTABLE $(H2 NdFields),
|$(TR $(TH NdField Name) $(TH Used By))
|$(T2 Cartesian, $(SUBREF topology, cartesian))
|$(T2 Kronecker, $(SUBREF topology, kronecker))
|)
|
|See_also: $(SUBREF concatenation, concatenation).
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.ndslice.ndfield;
|
|import mir.qualifier;
|import mir.internal.utility;
|import mir.ndslice.internal;
|import mir.ndslice.slice;
|import mir.primitives;
|import std.meta;
|
|private template _indices(NdFields...)
|{
| static if (NdFields.length == 0)
| enum _indices = "";
| else
| {
| alias Next = NdFields[0 .. $ - 1];
| enum i = Next.length;
| enum _indices = ._indices!Next ~
| "_fields[" ~ i.stringof ~ "][" ~ _indices_range!([staticMap!(DimensionCount, Next)].sum, DimensionCount!(NdFields[$ - 1])) ~ "], ";
| }
|}
|
|private template _indices_range(size_t begin, size_t count)
|{
| static if (count == 0)
| enum _indices_range = "";
| else
| {
| enum next = count - 1;
| enum elem = begin + next;
| enum _indices_range = ._indices_range!(begin, next) ~ "indices[" ~ elem.stringof ~ "], ";
| }
|}
|
|///
|struct Cartesian(NdFields...)
| if (NdFields.length > 1)
|{
| ///
| NdFields _fields;
|
| package(mir) enum size_t M(size_t f) = [staticMap!(DimensionCount, NdFields[0..f])].sum;
| package(mir) enum size_t N = M!(NdFields.length);
|
| ///
| auto lightConst()() const @property
| {
| import std.format;
| import mir.ndslice.topology: iota;
35| return mixin("Cartesian!(staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota));
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| import std.format;
| import mir.ndslice.topology: iota;
| return mixin("Cartesian!(staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota));
| }
|
| ///
| size_t length(size_t d = 0)() @safe scope const @property
| {
| foreach(f, NdField; NdFields)
| static if (M!f <= d && M!(f + 1) > d)
| {
| enum d = d - M!f;
| static if (d)
| return _fields[f].length!(d - M!f);
| else
0000000| return _fields[f].length;
| }
| }
|
| ///
| size_t[N] shape()() @safe scope const @property
| {
30| typeof(return) ret;
| foreach(f, NdField; NdFields)
| {
| static if (hasShape!NdField)
| {
70| auto s = _fields[f].shape;
| foreach(j; Iota!(s.length))
72| ret[M!f + j] = s[j];
| }
| else
| {
| ret[M!f] = _fields[f].length;
| }
| }
30| return ret;
| }
|
| ///
| size_t elementCount()() @safe scope const @property
| {
0000000| size_t ret = 1;
| foreach (f, NdField; NdFields)
0000000| ret *= _fields[f].elementCount;
0000000| return ret;
| }
|
| ///
| auto opIndex(size_t[N] indices...)
| {
| import mir.functional : refTuple;
592| return mixin("refTuple(" ~ _indices!(NdFields) ~ ")");
| }
|}
|
|private template _kr_indices(size_t n)
|{
| static if (n == 0)
| enum _kr_indices = "";
| else
| {
| enum i = n - 1;
| enum _kr_indices = ._kr_indices!i ~ "_fields[" ~ i.stringof ~ "][ind[" ~ i.stringof ~ "]], ";
| }
|}
|
|///
|struct Kronecker(alias fun, NdFields...)
| if (NdFields.length > 1 && allSatisfy!(templateOr!(hasShape, hasLength), NdFields[1 .. $]))
|{
| ///
| NdFields _fields;
|
| ///
| auto lightConst()() const @property
| {
| import std.format;
| import mir.ndslice.topology: iota;
19| return mixin("Kronecker!(fun, staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota));
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| import std.format;
| import mir.ndslice.topology: iota;
| return mixin("Kronecker!(fun, staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota));
| }
|
| private enum N = DimensionCount!(NdFields[$-1]);
|
| ///
| size_t length(size_t d = 0)() scope const @property
| {
| static if (d == 0)
| {
| size_t ret = 1;
| foreach (f, NdField; NdFields)
| ret *= _fields[f].length;
| }
| else
| {
| size_t ret = 1;
| foreach (f, NdField; NdFields)
| ret *= _fields[f].length!d;
| }
| return ret;
| }
|
|
| ///
| size_t[N] shape()() scope const @property
| {
| static if (N > 1)
| {
4| size_t[N] ret = 1;
| foreach (f, NdField; NdFields)
| {
10| auto s = _fields[f].shape;
| foreach(i; Iota!N)
20| ret[i] *= s[i];
| }
4| return ret;
| }
| else
| {
2| size_t[1] ret = 1;
| foreach (f, NdField; NdFields)
4| ret[0] *= _fields[f].length;
2| return ret;
| }
| }
|
| ///
| size_t elementCount()() scope const @property
| {
| size_t ret = 1;
| foreach (f, NdField; NdFields)
| ret *= _fields[f].elementCount;
| ret;
| }
|
| ///
| auto ref opIndex()(size_t[N] indices...)
| {
| static if (N > 1)
128| size_t[N][NdFields.length] ind;
| else
6| size_t[NdFields.length] ind;
| foreach_reverse (f, NdField; NdFields)
| {
| static if (f)
| {
| static if (hasShape!(NdFields[f]))
| {
198| auto s = _fields[f].shape;
| }
| else
| {
| size_t[1] s;
| s[0] = _fields[f].length;
| }
| static if (N > 1)
| {
| foreach(i; Iota!N)
| {
384| ind[f][i] = indices[i] % s[i];
384| indices[i] /= s[i];
| }
| }
| else
| {
6| ind[f] = indices[0] % s[0];
6| indices[0] /= s[0];
| }
| }
| else
| {
| static if (N > 1)
| {
| foreach(i; Iota!N)
256| ind[f][i] = indices[i];
| }
| else
| {
6| ind[f] = indices[0];
| }
| }
| }
134| return mixin("fun(" ~ _kr_indices!(ind.length) ~ ")");
| }
|}
source/mir/ndslice/ndfield.d is 85% covered
<<<<<< EOF
# path=./source-mir-numeric.lst
|/++
|Base numeric algorithms.
|
|Reworked part of `std.numeric`.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot), Lars Tandle Kyllingstad (diff)
|+/
|module mir.numeric;
|
|import mir.internal.utility: isFloatingPoint;
|import mir.math.common;
|import mir.math.ieee;
|
|version(D_Exceptions)
|{
| private static immutable findRoot_badBounds = new Exception("findRoot/findLocalMin: f(ax) and f(bx) must have opposite signs to bracket the root.");
| private static immutable findRoot_nanX = new Exception("findRoot/findLocalMin: ax or bx is NaN.");
| private static immutable findRoot_nanY = new Exception("findRoot/findLocalMin: f(x) returned NaN.");
|}
|
|/++
|+/
|enum mir_find_root_status
|{
| /// Success
| success,
| ///
| badBounds,
| ///
| nanX,
| ///
| nanY,
|}
|
|/// ditto
|alias FindRootStatus = mir_find_root_status;
|
|/++
|+/
|struct mir_find_root_result(T)
|{
| /// Left bound
| T ax = 0;
| /// Rifht bound
| T bx = 0;
| /// `f(ax)` or `f(ax).fabs.fmin(T.max / 2).copysign(f(ax))`.
| T ay = 0;
| /// `f(bx)` or `f(bx).fabs.fmin(T.max / 2).copysign(f(bx))`.
| T by = 0;
| /// Amount of target function calls.
| uint iterations;
|
|@safe pure @nogc scope const @property:
|
| /++
| Returns: self
| Required_versions:`D_Exceptions`
| Throws: `Exception` if $(LREF FindRootResult.status) isn't $(LREF mir_find_root_status.success).
| +/
| version(D_Exceptions)
| ref validate() inout return
| {
80| with(FindRootStatus) final switch(status)
| {
160| case success: return this;
0000000| case badBounds: throw findRoot_badBounds;
0000000| case nanX: throw findRoot_nanX;
0000000| case nanY: throw findRoot_nanY;
| }
| }
|
|extern(C++) nothrow:
|
| /++
| Returns: $(LREF mir_find_root_status)
| +/
| FindRootStatus status()
| {
80| with(FindRootStatus) return
80| ax != ax || bx != bx ? nanX :
160| ay != ay || by != by ? nanY :
120| ay.signbit == by.signbit && ay != 0 && by != 0 ? badBounds :
80| success;
| }
|
| /++
| A bound that corresponds to the minimal absolute function value.
|
| Returns: `!(ay.fabs > by.fabs) ? ax : bx`
| +/
| T x()
| {
44| return !(ay.fabs > by.fabs) ? ax : bx;
| }
|
| /++
| The minimal of absolute function values.
|
| Returns: `!(ay.fabs > by.fabs) ? ay : by`
| +/
| T y()
| {
0000000| return !(ay.fabs > by.fabs) ? ay : by;
| }
|}
|
|/// ditto
|alias FindRootResult = mir_find_root_result;
|
|/++
|Find root of a real function f(x) by bracketing, allowing the
|termination condition to be specified.
|
|Given a function `f` and a range `[a .. b]` such that `f(a)`
|and `f(b)` have opposite signs or at least one of them equals ±0,
|returns the value of `x` in
|the range which is closest to a root of `f(x)`. If `f(x)`
|has more than one root in the range, one will be chosen
|arbitrarily. If `f(x)` returns NaN, NaN will be returned;
|otherwise, this algorithm is guaranteed to succeed.
|
|Uses an algorithm based on TOMS748, which uses inverse cubic
|interpolation whenever possible, otherwise reverting to parabolic
|or secant interpolation. Compared to TOMS748, this implementation
|improves worst-case performance by a factor of more than 100, and
|typical performance by a factor of 2. For 80-bit reals, most
|problems require 8 to 15 calls to `f(x)` to achieve full machine
|precision. The worst-case performance (pathological cases) is
|approximately twice the number of bits.
|
|References: "On Enclosing Simple Roots of Nonlinear Equations",
|G. Alefeld, F.A. Potra, Yixun Shi, Mathematics of Computation 61,
|pp733-744 (1993). Fortran code available from $(HTTP
|www.netlib.org,www.netlib.org) as algorithm TOMS478.
|
|Params:
|f = Function to be analyzed. `f(ax)` and `f(bx)` should have opposite signs, or `lowerBound` and/or `upperBound`
| should be defined to perform initial interval extension.
|tolerance = Defines an early termination condition. Receives the
| current upper and lower bounds on the root. The
| delegate must return `true` when these bounds are
| acceptable. If this function always returns `false` or
| it is null, full machine precision will be achieved.
|ax = Left inner bound of initial range of `f` known to contain the root.
|bx = Right inner bound of initial range of `f` known to contain the root. Can be equal to `ax`.
|fax = Value of `f(ax)` (optional).
|fbx = Value of `f(bx)` (optional).
|lowerBound = Lower outer bound for interval extension (optional)
|upperBound = Upper outer bound for interval extension (optional)
|maxIterations = Appr. maximum allowed number of function calls (inluciding function calls for grid).
|steps = Number of nodes in the left side and right side regular grids (optional). If it equals to `0` (default),
| then the algorithm uses `ieeeMean` for searching. The algorithm performs searching if
| `sgn(fax)` equals to `sgn(fbx)` and at least one outer bound allows to extend the inner initial range.
|
|Returns: $(LREF FindRootResult)
|+/
|@fmamath
|FindRootResult!T findRoot(alias f, alias tolerance = null, T)(
| const T ax,
| const T bx,
| const T fax = T.nan,
| const T fbx = T.nan,
| const T lowerBound = T.nan,
| const T upperBound = T.nan,
| uint maxIterations = T.sizeof * 16,
| uint steps = 0,
| )
| if (
| isFloatingPoint!T && __traits(compiles, T(f(T.init))) &&
| (
| is(typeof(tolerance) == typeof(null)) ||
| __traits(compiles, {auto _ = bool(tolerance(T.init, T.init));}
| )
| ))
|{
84| if (false) // break attributes
0000000| T y = f(T(1));
84| scope funInst = delegate(T x) {
1129| return T(f(x));
| };
84| scope fun = funInst.trustedAllAttr;
|
| static if (is(typeof(tolerance) == typeof(null)))
| {
| alias tol = tolerance;
| }
| else
| {
2| if (false) // break attributes
0000000| bool b = tolerance(T(1), T(1));
40| scope tolInst = delegate(T a, T b) { return bool(tolerance(a, b)); };
2| scope tol = tolInst.trustedAllAttr;
| }
84| return findRootImpl(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, fun, tol);
|}
|
|///
|// @nogc
|version(mir_test) @safe unittest
|{
| import mir.math.common: log, exp, fabs;
|
1| auto logRoot = findRoot!log(0, double.infinity).validate.x;
1| assert(logRoot == 1);
|
1| auto shift = 1;
4| auto expm1Root = findRoot!(x => exp(x) - shift)
| (-double.infinity, double.infinity).validate.x;
1| assert(expm1Root == 0);
|
15| auto approxLogRoot = findRoot!(log, (a, b) => fabs(a - b) < 1e-5)(0, double.infinity).validate.x;
1| assert(fabs(approxLogRoot - 1) < 1e-5);
|}
|
|/// With adaptive bounds
|version(mir_test) @safe unittest
|{
| import mir.math.common: log, exp, fabs;
|
1| auto logRoot = findRoot!log(
| 10, 10, // assume we have one initial point
| double.nan, double.nan, // fa, fb aren't provided by user
| -double.infinity, double.infinity, // all space is available for the bounds extension.
| ).validate.x;
1| assert(logRoot == 1);
|
1| auto shift = 1;
4| alias expm1Fun = (double x) => exp(x) - shift;
1| auto expm1RootRet = findRoot!expm1Fun
| (
| 11, 10, // reversed order for interval is always OK
| expm1Fun(11), expm1Fun(10), // the order must be the same as bounds
| 0, double.infinity, // space for the bounds extension.
| ).validate;
1| assert(expm1Fun(expm1RootRet.x) == 0);
|
25| auto approxLogRoot = findRoot!(log, (a, b) => fabs(a - b) < 1e-5)(
| -1e10, +1e10,
| double.nan, double.nan,
| 0, double.infinity,
| ).validate.x;
1| assert(fabs(approxLogRoot - 1) < 1e-5);
|}
|
|/// ditto
|unittest
|{
| import core.stdc.tgmath: atan;
| import mir.math;
| import std.meta: AliasSeq;
|
1| const double[2][3] boundaries = [
| [0.4, 0.6],
| [1.4, 1.6],
| [0.1, 2.1]];
|
| enum root = 1.0;
|
| foreach(fun; AliasSeq!(
40| (double x) => x ^^ 2 - root,
40| (double x) => root - x ^^ 2,
47| (double x) => atan(x - root),
| ))
| {
36| foreach(ref bounds; boundaries)
| {
9| auto result = findRoot!fun(
| bounds[0], bounds[1],
| double.nan, double.nan, // f(a) and f(b) not provided
| -double.max, double.max, // user provided outer bounds
| );
9| assert(result.validate.x == root);
| }
| }
|
12| foreach(ref bounds; boundaries)
| {
27| auto result = findRoot!(x => sin(x - root))(
| bounds[0], bounds[1],
| double.nan, double.nan, // f(a) and f(b) not provided
| -10, 10, // user provided outer bounds
| 100, // max iterations,
| 10, // steps for grid
| );
3| assert(result.validate.x == root);
| }
| // single initial point, grid, positive direction
7| auto result = findRoot!((double x ) => sin(x - root))(
| 0.1, 0.1, // initial point, a = b
| double.nan, double.nan, // f(a) = f(b) not provided
| -100.0, 100.0, // user provided outer bounds
| 150, // max iterations,
| 100, // number of steps for grid
| );
1| assert(result.validate.x == root);
|
| // single initial point, grid, negative direction
13| result = findRoot!((double x ) => sin(x - root))(
| 0.1, 0.1, // initial point a = b
| double.nan, double.nan, // f(a) = f(b) not provided
| 100.0, -100.0, // user provided outer bounds, reversed order
| 150, // max iterations,
| 100, // number of steps for grid
| );
1| assert(result.validate.x == double(root - PI)); // Left side root!
|}
|
|/++
|With adaptive bounds and single initial point.
|Reverse outer bound order controls first step direction
|in case of `f(a) == f(b)`.
|+/
|unittest
|{
| enum root = 1.0;
|
| // roots are +/- `root`
42| alias fun = (double x) => x * x - root;
|
1| double lowerBound = -10.0;
1| double upperBound = 10.0;
|
1| assert(
| findRoot!fun(
| 0, 0, // initial interval
| double.nan, double.nan,
| lowerBound, upperBound,
| // positive direction has higher priority
| ).validate.x == root
| );
|
1| assert(
| findRoot!fun(
| 0, 0, // initial interval
| double.nan, double.nan,
| upperBound, lowerBound,
| // reversed order
| ).validate.x == -root // other root
| );
|}
|
|/// $(LREF findRoot) implementations.
|export @fmamath FindRootResult!float findRootImpl(
| float ax,
| float bx,
| float fax,
| float fbx,
| float lowerBound,
| float upperBound,
| uint maxIterations,
| uint steps,
| scope float delegate(float) @safe pure nothrow @nogc f,
| scope bool delegate(float, float) @safe pure nothrow @nogc tolerance, //can be null
|) @safe pure nothrow @nogc
|{
| pragma(inline, false);
3| return findRootImplGen!float(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, f, tolerance);
|}
|
|/// ditto
|export @fmamath FindRootResult!double findRootImpl(
| double ax,
| double bx,
| double fax,
| double fbx,
| double lowerBound,
| double upperBound,
| uint maxIterations,
| uint steps,
| scope double delegate(double) @safe pure nothrow @nogc f,
| scope bool delegate(double, double) @safe pure nothrow @nogc tolerance, //can be null
|) @safe pure nothrow @nogc
|{
| pragma(inline, false);
23| return findRootImplGen!double(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, f, tolerance);
|}
|
|/// ditto
|export @fmamath FindRootResult!real findRootImpl(
| real ax,
| real bx,
| real fax,
| real fbx,
| real lowerBound,
| real upperBound,
| uint maxIterations,
| uint steps,
| scope real delegate(real) @safe pure nothrow @nogc f,
| scope bool delegate(real, real) @safe pure nothrow @nogc tolerance, //can be null
|) @safe pure nothrow @nogc
|{
| pragma(inline, false);
58| return findRootImplGen!real(ax, bx, fax, fbx, lowerBound, upperBound, maxIterations, steps, f, tolerance);
|}
|
|private @fmamath FindRootResult!T findRootImplGen(T)(
| T a,
| T b,
| T fa,
| T fb,
| T lb,
| T ub,
| uint maxIterations,
| uint steps,
| scope const T delegate(T) @safe pure nothrow @nogc f,
| scope const bool delegate(T, T) @safe pure nothrow @nogc tolerance, //can be null
|) @safe pure nothrow @nogc
| if (isFloatingPoint!T)
|{
| version(LDC) pragma(inline, true);
| // Author: Don Clugston. This code is (heavily) modified from TOMS748
| // (www.netlib.org). The changes to improve the worst-cast performance are
| // entirely original.
|
| // Author: Ilya Yaroshenko (Bounds extension logic,
| // API improvements, infinity and huge numbers handing, compiled code size reduction)
|
84| T d; // [a .. b] is our current bracket. d is the third best guess.
84| T fd; // Value of f at d.
84| bool done = false; // Has a root been found?
84| uint iterations;
|
| static void swap(ref T a, ref T b)
| {
12| T t = a; a = b; b = t;
| }
|
| bool exit()
| {
| pragma(inline, false);
1067| return done
1017| || iterations >= maxIterations
1017| || b == nextUp(a)
1022| || tolerance !is null && tolerance(a, b);
| }
|
| // Test the function at point c; update brackets accordingly
| void bracket(T c)
| {
| pragma(inline, false);
928| T fc = f(c);
928| iterations++;
1811| if (fc == 0 || fc != fc) // Exact solution, or NaN
| {
45| a = c;
45| fa = fc;
45| d = c;
45| fd = fc;
45| done = true;
45| return;
| }
|
883| fc = fc.fabs.fmin(T.max / 2).copysign(fc);
|
| // Determine new enclosing interval
883| if (signbit(fa) != signbit(fc))
| {
439| d = b;
439| fd = fb;
439| b = c;
439| fb = fc;
| }
| else
| {
444| d = a;
444| fd = fa;
444| a = c;
444| fa = fc;
| }
| }
|
| /* Perform a secant interpolation. If the result would lie on a or b, or if
| a and b differ so wildly in magnitude that the result would be meaningless,
| perform a bisection instead.
| */
| static T secantInterpolate(T a, T b, T fa, T fb)
| {
| pragma(inline, false);
186| if (a - b == a && b != 0
253| || b - a == b && a != 0)
| {
| // Catastrophic cancellation
13| return ieeeMean(a, b);
| }
| // avoid overflow
168| T m = fa - fb;
168| T wa = fa / m;
168| T wb = fb / m;
168| T c = b * wa - a * wb;
546| if (c == a || c == b || c != c || c.fabs == T.infinity)
61| c = a.half + b.half;
168| return c;
| }
|
| /* Uses 'numsteps' newton steps to approximate the zero in [a .. b] of the
| quadratic polynomial interpolating f(x) at a, b, and d.
| Returns:
| The approximate zero in [a .. b] of the quadratic polynomial.
| */
| T newtonQuadratic(int numsteps)
| {
| // Find the coefficients of the quadratic polynomial.
219| const T a0 = fa;
219| const T a1 = (fb - fa) / (b - a);
219| const T a2 = ((fd - fb) / (d - b) - a1) / (d - a);
|
| // Determine the starting point of newton steps.
438| T c = a2.signbit != fa.signbit ? a : b;
|
| // start the safeguarded newton steps.
2245| foreach (int i; 0 .. numsteps)
| {
530| const T pc = a0 + (a1 + a2 * (c - b))*(c - a);
530| const T pdc = a1 + a2*((2 * c) - (a + b));
530| if (pdc == 0)
1| return a - a0 / a1;
| else
529| c = c - pc / pdc;
| }
218| return c;
| }
|
| // Starting with the second iteration, higher-order interpolation can
| // be used.
84| int itnum = 1; // Iteration number
84| int baditer = 1; // Num bisections to take if an iteration is bad.
168| T c, e; // e is our fourth best guess
84| T fe;
84| bool left;
|
| // Allow a and b to be provided in reverse order
84| if (a > b)
| {
1| swap(a, b);
1| swap(fa, fb);
| }
|
168| if (a != a || b != b)
| {
0000000| done = true;
0000000| goto whileloop;
| }
|
84| if (lb != lb)
| {
65| lb = a;
| }
|
84| if (ub != ub)
| {
65| ub = b;
| }
|
84| if (lb > ub)
| {
2| swap(lb, ub);
2| left = true;
| }
|
84| if (lb == -T.infinity)
| {
2| lb = -T.max;
| }
|
84| if (ub == +T.infinity)
| {
6| ub = +T.max;
| }
|
84| if (!(lb <= a))
| {
2| a = lb;
2| fa = T.nan;
| }
|
84| if (!(b <= ub))
| {
3| a = lb;
3| fa = T.nan;
| }
|
84| if (fa != fa)
| {
83| fa = f(a);
83| iterations++;
| }
|
| // On the first iteration we take a secant step:
165| if (fa == 0 || fa != fa)
| {
3| done = true;
3| b = a;
3| fb = fa;
3| goto whileloop;
| }
|
81| if (fb != fb)
| {
80| if (a == b)
| {
5| fb = fa;
| }
| else
| {
75| fb = f(b);
75| iterations++;
| }
| }
|
161| if (fb == 0 || fb != fb)
| {
1| done = true;
1| a = b;
1| fa = fb;
1| goto whileloop;
| }
|
80| if (fa.fabs < fb.fabs)
| {
33| left = true;
| }
| else
47| if (fa.fabs > fb.fabs)
| {
20| left = false;
| }
|
| // extend inner boundaries
80| if (fa.signbit == fb.signbit)
| {
14| T lx = a;
14| T ux = b;
14| T ly = fa;
14| T uy = fb;
14| const sb = fa.signbit;
|
| import mir.ndslice.topology: linspace;
|
28| typeof(linspace!T([2], [lx, lb])) lgrid, ugrid;
14| if (steps)
| {
4| lgrid = linspace!T([steps + 1], [lx, lb]);
4| lgrid.popFront;
4| ugrid = linspace!T([steps + 1], [ux, ub]);
4| ugrid.popFront;
| }
|
14| for(T mx;;)
| {
43| bool lw = lb < lx;
43| bool uw = ux < ub;
|
86| if (!lw && !uw || iterations >= maxIterations)
| {
0000000| done = true;
0000000| goto whileloop;
| }
|
129| if (lw && (!uw || left))
| {
23| if (lgrid.empty)
| {
19| mx = ieeeMean(lb, lx);
19| if (mx == lx)
0000000| mx = lb;
| }
| else
| {
4| mx = lgrid.front;
4| lgrid.popFront;
4| if (mx == lx)
0000000| continue;
| }
23| T my = f(mx);
23| iterations++;
23| if (my == 0)
| {
1| a = b = mx;
1| fa = fb = my;
1| done = true;
1| goto whileloop;
| }
22| if (mx != mx)
| {
0000000| lb = mx;
0000000| continue;
| }
22| if (my.signbit == sb)
| {
15| lx = mx;
15| ly = my;
15| if (lgrid.empty)
| {
13| left = ly.fabs < uy.fabs;
| }
15| continue;
| }
7| a = mx;
7| fa = my;
7| b = lx;
7| fb = ly;
7| break;
| }
| else
| {
20| if (ugrid.empty)
| {
18| mx = ieeeMean(ub, ux);
18| if (mx == ux)
0000000| mx = ub;
| }
| else
| {
2| mx = ugrid.front;
2| ugrid.popFront;
2| if (mx == ux)
0000000| continue;
| }
20| T my = f(mx);
20| iterations++;
20| if (my == 0)
| {
0000000| a = b = mx;
0000000| fa = fb = my;
0000000| done = true;
0000000| goto whileloop;
| }
20| if (mx != mx)
| {
0000000| ub = mx;
0000000| continue;
| }
20| if (my.signbit == sb)
| {
14| ux = mx;
14| uy = my;
14| if (ugrid.empty)
| {
14| left = !(ly.fabs > uy.fabs);
| }
14| continue;
| }
6| b = mx;
6| fb = my;
6| a = ux;
6| fa = uy;
6| break;
| }
| }
| }
|
79| fa = fa.fabs.fmin(T.max / 2).copysign(fa);
79| fb = fb.fabs.fmin(T.max / 2).copysign(fb);
79| bracket(secantInterpolate(a, b, fa, fb));
|
|whileloop:
333| while (!exit)
| {
652| T a0 = a, b0 = b; // record the brackets
|
| // Do two higher-order (cubic or parabolic) interpolation steps.
2339| foreach (int QQ; 0 .. 2)
| {
| // Cubic inverse interpolation requires that
| // all four function values fa, fb, fd, and fe are distinct;
| // otherwise use quadratic interpolation.
1607| bool distinct = (fa != fb) && (fa != fd) && (fa != fe)
1496| && (fb != fd) && (fb != fe) && (fd != fe);
| // The first time, cubic interpolation is impossible.
622| if (itnum<2) distinct = false;
545| bool ok = distinct;
545| if (distinct)
| {
| // Cubic inverse interpolation of f(x) at a, b, d, and e
420| const q11 = (d - e) * fd / (fe - fd);
420| const q21 = (b - d) * fb / (fd - fb);
420| const q31 = (a - b) * fa / (fb - fa);
420| const d21 = (b - d) * fd / (fd - fb);
420| const d31 = (a - b) * fb / (fb - fa);
|
420| const q22 = (d21 - q11) * fb / (fe - fb);
420| const q32 = (d31 - q21) * fa / (fd - fa);
420| const d32 = (d31 - q21) * fd / (fd - fa);
420| const q33 = (d32 - q22) * fa / (fe - fa);
420| c = a + (q31 + q32 + q33);
1202| if (c != c || (c <= a) || (c >= b))
| {
| // DAC: If the interpolation predicts a or b, it's
| // probable that it's the actual root. Only allow this if
| // we're already close to the root.
114| if (c == a && (a - b != a || a - b != -b))
| {
10| auto down = !(a - b != a);
10| if (down)
0000000| c = -c;
10| c = c.nextUp;
10| if (down)
0000000| c = -c;
| }
| else
| {
94| ok = false;
| }
|
| }
| }
545| if (!ok)
| {
| // DAC: Alefeld doesn't explain why the number of newton steps
| // should vary.
219| c = newtonQuadratic(2 + distinct);
605| if (c != c || (c <= a) || (c >= b))
| {
| // Failure, try a secant step:
102| c = secantInterpolate(a, b, fa, fb);
| }
| }
545| ++itnum;
545| e = d;
545| fe = fd;
545| bracket(c);
545| if (exit)
60| break whileloop;
485| if (itnum == 2)
77| continue whileloop;
| }
|
| // Now we take a double-length secant step:
189| T u;
189| T fu;
189| if (fabs(fa) < fabs(fb))
| {
106| u = a;
106| fu = fa;
| }
| else
| {
83| u = b;
83| fu = fb;
| }
189| c = u - 2 * (fu / (fb - fa)) * (b - a);
|
| // DAC: If the secant predicts a value equal to an endpoint, it's
| // probably false.
708| if (c == a || c == b || c != c || fabs(c - u) > (b - a) * 0.5f)
| {
64| if ((a - b) == a || (b - a) == b)
| {
10| c = ieeeMean(a, b);
| }
| else
| {
22| c = a.half + b.half;
| }
| }
189| e = d;
189| fe = fd;
189| bracket(c);
189| if (exit)
17| break;
|
| // IMPROVE THE WORST-CASE PERFORMANCE
| // We must ensure that the bounds reduce by a factor of 2
| // in binary space! every iteration. If we haven't achieved this
| // yet, or if we don't yet know what the exponent is,
| // perform a binary chop.
|
172| if ((a == 0
170| || b == 0
170| || fabs(a) >= 0.5f * fabs(b)
136| && fabs(b) >= 0.5f * fabs(a))
134| && b - a < 0.25f * (b0 - a0))
| {
120| baditer = 1;
120| continue;
| }
|
| // DAC: If this happens on consecutive iterations, we probably have a
| // pathological function. Perform a number of bisections equal to the
| // total number of consecutive bad iterations.
|
52| if (b - a < 0.25f * (b0 - a0))
12| baditer = 1;
501| foreach (int QQ; 0 .. baditer)
| {
115| e = d;
115| fe = fd;
115| bracket(ieeeMean(a, b));
| }
52| ++baditer;
| }
84| return typeof(return)(a, b, fa, fb, iterations);
|}
|
|version(mir_test) @safe unittest
|{
| import mir.math.constant;
|
1| int numProblems = 0;
1| int numCalls;
|
| void testFindRoot(real delegate(real) @nogc @safe nothrow pure f , real x1, real x2, int line = __LINE__) //@nogc @safe nothrow pure
| {
| //numCalls=0;
| //++numProblems;
116| assert(x1 == x1 && x2 == x2);
58| auto result = findRoot!f(x1, x2).validate;
|
58| auto flo = f(result.ax);
58| auto fhi = f(result.bx);
58| if (flo != 0)
| {
32| assert(flo.signbit != fhi.signbit);
| }
| }
|
| // Test functions
| real cubicfn(real x) @nogc @safe nothrow pure
| {
| //++numCalls;
45| if (x>float.max)
1| x = float.max;
45| if (x<-float.max)
3| x = -float.max;
| // This has a single real root at -59.286543284815
45| return 0.386*x*x*x + 23*x*x + 15.7*x + 525.2;
| }
| // Test a function with more than one root.
34| real multisine(real x) { ++numCalls; return sin(x); }
1| testFindRoot( &multisine, 6, 90);
1| testFindRoot(&cubicfn, -100, 100);
1| testFindRoot( &cubicfn, -double.max, real.max);
|
|
|/* Tests from the paper:
| * "On Enclosing Simple Roots of Nonlinear Equations", G. Alefeld, F.A. Potra,
| * Yixun Shi, Mathematics of Computation 61, pp733-744 (1993).
| */
| // Parameters common to many alefeld tests.
1| int n;
2| real ale_a, ale_b;
|
1| int powercalls = 0;
|
| real power(real x)
| {
0000000| ++powercalls;
0000000| ++numCalls;
0000000| return pow(x, n) + double.min_normal;
| }
1| int [] power_nvals = [3, 5, 7, 9, 19, 25];
| // Alefeld paper states that pow(x, n) is a very poor case, where bisection
| // outperforms his method, and gives total numcalls =
| // 921 for bisection (2.4 calls per bit), 1830 for Alefeld (4.76/bit),
| // 0.5f624 for brent (6.8/bit)
| // ... but that is for double, not real80.
| // This poor performance seems mainly due to catastrophic cancellation,
| // which is avoided here by the use of ieeeMean().
| // I get: 231 (0.48/bit).
| // IE this is 10X faster in Alefeld's worst case
1| numProblems=0;
21| foreach (k; power_nvals)
| {
6| n = k;
| //testFindRoot(&power, -1, 10);
| }
|
1| int powerProblems = numProblems;
|
| // Tests from Alefeld paper
|
1| int [9] alefeldSums;
| real alefeld0(real x)
| {
174| ++alefeldSums[0];
174| ++numCalls;
174| real q = sin(x) - x/2;
6960| for (int i=1; i<20; ++i)
3306| q+=(2*i-5.0)*(2*i-5.0) / ((x-i*i)*(x-i*i)*(x-i*i));
174| return q;
| }
| real alefeld1(real x)
| {
36| ++numCalls;
36| ++alefeldSums[1];
36| return ale_a*x + exp(ale_b * x);
| }
| real alefeld2(real x)
| {
199| ++numCalls;
199| ++alefeldSums[2];
199| return pow(x, n) - ale_a;
| }
| real alefeld3(real x)
| {
60| ++numCalls;
60| ++alefeldSums[3];
60| return (1.0 +pow(1.0L-n, 2))*x - pow(1.0L-n*x, 2);
| }
| real alefeld4(real x)
| {
73| ++numCalls;
73| ++alefeldSums[4];
73| return x*x - pow(1-x, n);
| }
| real alefeld5(real x)
| {
80| ++numCalls;
80| ++alefeldSums[5];
80| return (1+pow(1.0L-n, 4))*x - pow(1.0L-n*x, 4);
| }
| real alefeld6(real x)
| {
86| ++numCalls;
86| ++alefeldSums[6];
86| return exp(-n*x)*(x-1.01L) + pow(x, n);
| }
| real alefeld7(real x)
| {
73| ++numCalls;
73| ++alefeldSums[7];
73| return (n*x-1) / ((n-1)*x);
| }
|
1| numProblems=0;
1| testFindRoot(&alefeld0, PI_2, PI);
22| for (n=1; n <= 10; ++n)
| {
10| testFindRoot(&alefeld0, n*n+1e-9L, (n+1)*(n+1)-1e-9L);
| }
2| ale_a = -40; ale_b = -1;
1| testFindRoot(&alefeld1, -9, 31);
2| ale_a = -100; ale_b = -2;
1| testFindRoot(&alefeld1, -9, 31);
2| ale_a = -200; ale_b = -3;
1| testFindRoot(&alefeld1, -9, 31);
1| int [] nvals_3 = [1, 2, 5, 10, 15, 20];
1| int [] nvals_5 = [1, 2, 4, 5, 8, 15, 20];
1| int [] nvals_6 = [1, 5, 10, 15, 20];
1| int [] nvals_7 = [2, 5, 15, 20];
|
10| for (int i=4; i<12; i+=2)
| {
4| n = i;
4| ale_a = 0.2;
4| testFindRoot(&alefeld2, 0, 5);
4| ale_a=1;
4| testFindRoot(&alefeld2, 0.95, 4.05);
4| testFindRoot(&alefeld2, 0, 1.5);
| }
21| foreach (i; nvals_3)
| {
6| n=i;
6| testFindRoot(&alefeld3, 0, 1);
| }
21| foreach (i; nvals_3)
| {
6| n=i;
6| testFindRoot(&alefeld4, 0, 1);
| }
24| foreach (i; nvals_5)
| {
7| n=i;
7| testFindRoot(&alefeld5, 0, 1);
| }
18| foreach (i; nvals_6)
| {
5| n=i;
5| testFindRoot(&alefeld6, 0, 1);
| }
15| foreach (i; nvals_7)
| {
4| n=i;
4| testFindRoot(&alefeld7, 0.01L, 1);
| }
| real worstcase(real x)
| {
111| ++numCalls;
222| return x<0.3*real.max? -0.999e-3: 1.0;
| }
1| testFindRoot(&worstcase, -real.max, real.max);
|
| // just check that the double + float cases compile
2| findRoot!(x => 0)(-double.max, double.max);
2| findRoot!(x => -0.0)(-float.max, float.max);
|/*
| int grandtotal=0;
| foreach (calls; alefeldSums)
| {
| grandtotal+=calls;
| }
| grandtotal-=2*numProblems;
| printf("\nALEFELD TOTAL = %d avg = %f (alefeld avg=19.3 for double)\n",
| grandtotal, (1.0*grandtotal)/numProblems);
| powercalls -= 2*powerProblems;
| printf("POWER TOTAL = %d avg = %f ", powercalls,
| (1.0*powercalls)/powerProblems);
|*/
| //Issue 14231
2| auto xp = findRoot!(x => x)(0f, 1f);
3| auto xn = findRoot!(x => x)(-1f, -0f);
|}
|
|/++
|+/
|struct FindLocalMinResult(T)
|{
| ///
| T x = 0;
| ///
| T y = 0;
| ///
| T error = 0;
|
|@safe pure @nogc scope const @property:
|
| /++
| Returns: self
| Required_versions:`D_Exceptions`
| Throws: `Exception` if $(LREF FindRootResult.status) isn't $(LREF mir_find_root_status.success).
| +/
| version(D_Exceptions)
| ref validate() return
| {
16| with(FindRootStatus) final switch(status)
| {
32| case success: return this;
0000000| case badBounds: throw findRoot_badBounds;
0000000| case nanX: throw findRoot_nanX;
0000000| case nanY: throw findRoot_nanY;
| }
| }
|
|extern(C++) nothrow:
|
| /++
| Returns: $(LREF mir_find_root_status)
| +/
| FindRootStatus status()
| {
19| with(FindRootStatus) return
0000000| x != x ? nanX :
22| y != y ? nanY :
16| success;
| }
|}
|
|/++
|Find a real minimum of a real function `f(x)` via bracketing.
|Given a function `f` and a range `(ax .. bx)`,
|returns the value of `x` in the range which is closest to a minimum of `f(x)`.
|`f` is never evaluted at the endpoints of `ax` and `bx`.
|If `f(x)` has more than one minimum in the range, one will be chosen arbitrarily.
|If `f(x)` returns NaN or -Infinity, `(x, f(x), NaN)` will be returned;
|otherwise, this algorithm is guaranteed to succeed.
|Params:
| f = Function to be analyzed
| ax = Left bound of initial range of f known to contain the minimum.
| bx = Right bound of initial range of f known to contain the minimum.
| relTolerance = Relative tolerance.
| absTolerance = Absolute tolerance.
|Preconditions:
| `ax` and `bx` shall be finite reals. $(BR)
| `relTolerance` shall be normal positive real. $(BR)
| `absTolerance` shall be normal positive real no less then `T.epsilon*2`.
|Returns:
| A $(LREF FindLocalMinResult) consisting of `x`, `y = f(x)` and `error = 3 * (absTolerance * fabs(x) + relTolerance)`.
| The method used is a combination of golden section search and
|successive parabolic interpolation. Convergence is never much slower
|than that for a Fibonacci search.
|References:
| "Algorithms for Minimization without Derivatives", Richard Brent, Prentice-Hall, Inc. (1973)
|See_Also: $(LREF findRoot), $(REF isNormal, std,math)
|+/
|FindLocalMinResult!T findLocalMin(alias f, T)(
| const T ax,
| const T bx,
| const T relTolerance = sqrt(T.epsilon),
| const T absTolerance = sqrt(T.epsilon))
| if (isFloatingPoint!T && __traits(compiles, T(f(T.init))))
|{
19| if (false) // break attributes
| {
0000000| T y = f(T(123));
| }
19| scope funInst = delegate(T x) {
98361| return T(f(x));
| };
19| scope fun = funInst.trustedAllAttr;
|
19| return findLocalMinImpl(ax, bx, relTolerance, absTolerance, fun);
|}
|
|@fmamath
|private FindLocalMinResult!float findLocalMinImpl(
| const float ax,
| const float bx,
| const float relTolerance,
| const float absTolerance,
| scope const float delegate(float) @safe pure nothrow @nogc f,
| ) @safe pure nothrow @nogc
|{
| pragma(inline, false);
6| return findLocalMinImplGen!float(ax, bx, relTolerance, absTolerance, f);
|}
|
|@fmamath
|private FindLocalMinResult!double findLocalMinImpl(
| const double ax,
| const double bx,
| const double relTolerance,
| const double absTolerance,
| scope const double delegate(double) @safe pure nothrow @nogc f,
| ) @safe pure nothrow @nogc
|{
| pragma(inline, false);
7| return findLocalMinImplGen!double(ax, bx, relTolerance, absTolerance, f);
|}
|
|@fmamath
|private FindLocalMinResult!real findLocalMinImpl(
| const real ax,
| const real bx,
| const real relTolerance,
| const real absTolerance,
| scope const real delegate(real) @safe pure nothrow @nogc f,
| ) @safe pure nothrow @nogc
|{
| pragma(inline, false);
6| return findLocalMinImplGen!real(ax, bx, relTolerance, absTolerance, f);
|}
|
|@fmamath
|private FindLocalMinResult!T findLocalMinImplGen(T)(
| const T ax,
| const T bx,
| const T relTolerance,
| const T absTolerance,
| scope const T delegate(T) @safe pure nothrow @nogc f,
| )
| if (isFloatingPoint!T && __traits(compiles, {T _ = f(T.init);}))
|in
|{
38| assert(relTolerance.fabs >= T.min_normal && relTolerance.fabs < T.infinity, "relTolerance is not normal floating point number");
38| assert(absTolerance.fabs >= T.min_normal && absTolerance.fabs < T.infinity, "absTolerance is not normal floating point number");
19| assert(relTolerance >= 0, "absTolerance is not positive");
19| assert(absTolerance >= T.epsilon*2, "absTolerance is not greater then `2*T.epsilon`");
|}
|do
|{
| version(LDC) pragma(inline, true);
| // c is the squared inverse of the golden ratio
| // (3 - sqrt(5))/2
| // Value obtained from Wolfram Alpha.
| enum T c = 0x0.61c8864680b583ea0c633f9fa31237p+0L;
| enum T cm1 = 0x0.9e3779b97f4a7c15f39cc0605cedc8p+0L;
19| T tolerance;
38| T a = ax > bx ? bx : ax;
38| T b = ax > bx ? ax : bx;
19| if (a < -T.max)
0000000| a = -T.max;
19| if (b > +T.max)
0000000| b = +T.max;
| // sequence of declarations suitable for SIMD instructions
19| T v = a * cm1 + b * c;
19| assert(v.fabs < T.infinity);
38| T fv = v == v ? f(v) : v;
35| if (fv != fv || fv == -T.infinity)
| {
3| return typeof(return)(v, fv, T.init);
| }
16| T w = v;
16| T fw = fv;
16| T x = v;
16| T fx = fv;
16| size_t i;
32| for (T d = 0, e = 0;;)
| {
98358| i++;
98358| T m = (a + b) * 0.5f;
| // This fix is not part of the original algorithm
98358| if (!((m.fabs < T.infinity))) // fix infinity loop. Issue can be reproduced in R.
| {
0000000| m = a.half + b.half;
| }
98358| tolerance = absTolerance * fabs(x) + relTolerance;
98358| const t2 = tolerance * 2;
| // check stopping criterion
98358| if (!(fabs(x - m) > t2 - (b - a) * 0.5f))
| {
16| break;
| }
98342| T p = 0;
98342| T q = 0;
98342| T r = 0;
| // fit parabola
98342| if (fabs(e) > tolerance)
| {
98326| const xw = x - w;
98326| const fxw = fx - fw;
98326| const xv = x - v;
98326| const fxv = fx - fv;
98326| const xwfxv = xw * fxv;
98326| const xvfxw = xv * fxw;
98326| p = xv * xvfxw - xw * xwfxv;
98326| q = (xvfxw - xwfxv) * 2;
98326| if (q > 0)
86943| p = -p;
| else
11383| q = -q;
98326| r = e;
98326| e = d;
| }
98342| T u;
| // a parabolic-interpolation step
107048| if (fabs(p) < fabs(q * r * 0.5f) && p > q * (a - x) && p < q * (b - x))
| {
4353| d = p / q;
4353| u = x + d;
| // f must not be evaluated too close to a or b
8701| if (u - a < t2 || b - u < t2)
14| d = x < m ? tolerance: -tolerance;
| }
| // a golden-section step
| else
| {
187978| e = (x < m ? b : a) - x;
93989| d = c * e;
| }
| // f must not be evaluated too close to x
196695| u = x + (fabs(d) >= tolerance ? d: d > 0 ? tolerance: -tolerance);
98342| const fu = f(u);
196684| if (fu != fu || fu == -T.infinity)
| {
0000000| return typeof(return)(u, fu, T.init);
| }
| // update a, b, v, w, and x
98342| if (fu <= fx)
| {
166674| (u < x ? b : a) = x;
166674| v = w; fv = fw;
166674| w = x; fw = fx;
166674| x = u; fx = fu;
| }
| else
| {
30010| (u < x ? a : b) = u;
21016| if (fu <= fw || w == x)
| {
18010| v = w; fv = fw;
18010| w = u; fw = fu;
| }
6347| else if (fu <= fv || v == x || v == w)
| { // do not remove this braces
11660| v = u; fv = fu;
| }
| }
| }
16| return typeof(return)(x, fx, tolerance * 3);
|}
|
|///
|//@nogc
|version(mir_test) @safe unittest
|{
| import mir.math.common: approxEqual;
|
1| double shift = 4;
|
7| auto ret = findLocalMin!(x => (x-shift)^^2)(-1e7, 1e7).validate;
1| assert(ret.x.approxEqual(shift));
1| assert(ret.y.approxEqual(0.0));
|}
|
|///
|version(mir_test) @safe unittest
|{
| import mir.math.common: approxEqual, log, fabs;
| alias AliasSeq(T...) = T;
| static foreach (T; AliasSeq!(double, float, real))
| {
| {
96| auto ret = findLocalMin!(x => (x-4)^^2)(T.min_normal, T(1e7)).validate;
3| assert(ret.x.approxEqual(T(4)));
3| assert(ret.y.approxEqual(T(0)));
| }
| {
22311| auto ret = findLocalMin!(x => fabs(x-1))(-T.max/4, T.max/4, T.min_normal, 2*T.epsilon).validate;
3| assert(approxEqual(ret.x, T(1)));
3| assert(approxEqual(ret.y, T(0)));
3| assert(ret.error <= 10 * T.epsilon);
| }
| {
6| auto ret = findLocalMin!(x => T.init)(0, 1, T.min_normal, 2*T.epsilon);
3| assert(ret.status == FindRootStatus.nanY);
| }
| {
3| auto ret = findLocalMin!log( 0, 1, T.min_normal, 2*T.epsilon).validate;
3| assert(ret.error < 3.00001 * ((2*T.epsilon)*fabs(ret.x)+ T.min_normal));
6| assert(ret.x >= 0 && ret.x <= ret.error);
| }
| {
3| auto ret = findLocalMin!log(0, T.max, T.min_normal, 2*T.epsilon).validate;
3| assert(ret.y < -18);
3| assert(ret.error < 5e-08);
6| assert(ret.x >= 0 && ret.x <= ret.error);
| }
| {
199| auto ret = findLocalMin!(x => -fabs(x))(-1, 1, T.min_normal, 2*T.epsilon).validate;
3| assert(ret.x.fabs.approxEqual(T(1)));
3| assert(ret.y.fabs.approxEqual(T(1)));
3| assert(ret.error.approxEqual(T(0)));
| }
| }
|}
|
|// force disabled FMA math
|private static auto half(T)(const T x)
|{
| pragma(inline, false);
166| return x * 0.5f;
|}
|
|private auto trustedAllAttr(T)(T t) @trusted
|{
| import std.traits;
| enum attrs = (functionAttributes!T & ~FunctionAttribute.system)
| | FunctionAttribute.pure_
| | FunctionAttribute.safe
| | FunctionAttribute.nogc
| | FunctionAttribute.nothrow_;
106| return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
|}
|
|/++
|Calculate the derivative of a function.
|This function uses Ridders' method of extrapolating the results
|of finite difference formulas for consecutively smaller step sizes,
|with an improved stopping criterion described in the Numerical Recipes
|books by Press et al.
|
|This method gives a much higher degree of accuracy in the answer
|compared to a single finite difference calculation, but requires
|more function evaluations; typically 6 to 12. The maximum number
|of function evaluations is $(D 24).
|
|Params:
| f = The function of which to take the derivative.
| x = The point at which to take the derivative.
| h = A "characteristic scale" over which the function changes.
| factor = Stepsize is decreased by factor at each iteration.
| safe = Return when error is SAFE worse than the best so far.
|
|References:
|$(UL
| $(LI
| C. J. F. Ridders,
| $(I Accurate computation of F'(x) and F'(x)F''(x)).
| Advances in Engineering Software, vol. 4 (1982), issue 2, p. 75.)
| $(LI
| W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery,
| $(I Numerical Recipes in C++) (2nd ed.).
| Cambridge University Press, 2003.)
|)
|+/
|@fmamath
|DiffResult!T diff(alias f, T)(const T x, const T h, const T factor = T(2).sqrt, const T safe = 2)
|{
1| if (false) // break attributes
0000000| T y = f(T(1));
1| scope funInst = delegate(T x) {
14| return T(f(x));
| };
1| scope fun = funInst.trustedAllAttr;
1| return diffImpl(fun, x, h, factor, safe);
|}
|
|///ditto
|DiffResult!T diffImpl(T)
| (scope const T delegate(T) @safe pure nothrow @nogc f, const T x, const T h, const T factor = T(2).sqrt, const T safe = 2)
| @safe pure nothrow @nogc
|in {
1| assert(h < T.max);
1| assert(h > T.min_normal);
|}
|do {
| // Set up Romberg tableau.
| import mir.ndslice.slice: sliced;
| pragma(inline, false);
|
| enum tableauSize = 16;
1| T[tableauSize ^^ 2] workspace = void;
1| auto tab = workspace[].sliced(tableauSize, tableauSize);
|
| // From the NR book: Stop when the difference between consecutive
| // approximations is bigger than SAFE*error, where error is an
| // estimate of the absolute error in the current (best) approximation.
|
| // First approximation: A_0
1| T result = void;
1| T error = T.max;
1| T hh = h;
|
1| tab[0,0] = (f(x + h) - f(x - h)) / (2 * h);
19| foreach (n; 1 .. tableauSize)
| {
| // Decrease h.
6| hh /= factor;
|
| // Compute A_n
6| tab[0, n] = (f(x + hh) - f(x - hh)) / (2 * hh);
|
6| T facm = 1;
81| foreach (m; 1 .. n + 1)
| {
21| facm *= factor ^^ 2;
|
| // Compute B_(n-1), C_(n-2), ...
21| T upLeft = tab[m - 1, n - 1];
21| T up = tab[m - 1, n];
21| T current = (facm * up - upLeft) / (facm - 1);
21| tab[m, n] = current;
|
| // Calculate and check error.
21| T currentError = fmax(fabs(current - upLeft), fabs(current - up));
21| if (currentError <= error)
| {
10| result = current;
10| error = currentError;
| }
| }
|
6| if (fabs(tab[n, n] - tab[n - 1, n - 1]) >= safe * error)
1| break;
| }
|
1| return typeof(return)(result, error);
|}
|
|///
|unittest
|{
| import mir.math.common;
|
14| auto f(double x) { return exp(x) / (sin(x) - x ^^ 2); }
1| auto d(double x) { return -(exp(x) * ((x - 2) * x - sin(x) + cos(x)))/(x^^2 - sin(x))^^2; }
1| auto r = diff!f(1.0, 0.01);
1| assert (approxEqual(r.value, d(1)));
|}
|
|/++
|+/
|struct DiffResult(T)
| if (__traits(isFloating, T))
|{
| ///
| T value = 0;
| ///
| T error = 0;
|}
source/mir/numeric.d is 93% covered
<<<<<< EOF
# path=./source-mir-string_map.lst
|/++
|$(H1 Ordered string-value associtaive array)
|Macros:
|AlgebraicREF = $(GREF_ALTTEXT mir-core, $(TT $1), $1, mir, algebraic)$(NBSP)
|+/
|
|module mir.string_map;
|
|import std.traits;
|
|/++
|Checks if the type is instance of $(LREF StringMap).
|+/
|enum isStringMap(T) = is(Unqual!T == StringMap!V, V);
|
|version(mir_test)
|///
|unittest
|{
| static assert(isStringMap!(StringMap!int));
| static assert(isStringMap!(const StringMap!int));
| static assert(!isStringMap!int);
|}
|
|/++
|Ordered string-value associtaive array with extremely fast lookup.
|
|Params:
| T = mutable value type, can be instance of $(AlgebraicREF Algebraic) for example.
| U = an unsigned type that can hold an index of keys. `U.max` must be less then the maximum possible number of struct members.
|+/
|struct StringMap(T, U = uint)
| if (isMutable!T && !__traits(hasMember, T, "opPostMove") && __traits(isUnsigned, U))
|{
| import mir.utility: _expect;
| import core.lifetime: move;
| import mir.conv: emplaceRef;
|
| private alias Impl = StructImpl!(T, U);
| private Impl* implementation;
|
| ///
| // current implementation is workaround for linking bugs when used in self referencing algebraic types
| bool opEquals(const StringMap rhs) const
| {
1| if (keys != rhs.keys)
0000000| return false;
1| if (implementation)
9| foreach (const i; 0 .. implementation._length)
2| if (implementation._values[i] != rhs.implementation._values[i])
0000000| return false;
1| return true;
| }
|
| // // linking bug
| // version(none)
| // {
| // /++
| // +/
| // bool opEquals()(typeof(null)) @safe pure nothrow @nogc const
| // {
| // return implementation is null;
| // }
|
| // version(mir_test) static if (is(T == int))
| // ///
| // @safe pure unittest
| // {
| // StringMap!int map;
| // assert(map == null);
| // map = StringMap!int(["key" : 1]);
| // assert(map != null);
| // map.remove("key");
| // assert(map != null);
| // }
| // }
|
| /++
| Reset the associtave array
| +/
| ref opAssign()(typeof(null)) return @safe pure nothrow @nogc
| {
1| implementation = null;
1| return this;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!int map = ["key" : 1];
1| map = null;
| }
|
| /++
| Initialize the associtave array with default value.
| +/
1| this()(typeof(null) aa) @safe pure nothrow @nogc
| {
1| implementation = null;
| }
|
| version(mir_test) static if (is(T == int))
| /// Usefull for default funcion argument.
| @safe pure unittest
| {
1| StringMap!int map = null; //
| }
|
| /++
| Constructs an associative array using keys and values from the builtin associative array
| Complexity: `O(n log(n))`
| +/
9| this()(T[string] aa) @trusted pure nothrow
| {
9| this(aa.keys, aa.values);
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!int map = ["key" : 1];
1| assert(map.findPos("key") == 0);
| }
|
| ///
| string toString()() const
| {
| import mir.format: stringBuf;
| stringBuf buffer;
| toString(buffer);
| return buffer.data.idup;
| }
|
| ///ditto
| void toString(W)(scope ref W w) const
| {
| bool next;
| w.put('[');
| import mir.format: printEscaped, EscapeFormat, print;
| foreach (i, ref value; values)
| {
| if (next)
| w.put(`, `);
| next = true;
| w.put('\"');
| printEscaped!(char, EscapeFormat.ion)(w, keys[i]);
| w.put(`": `);
| print(w, value);
| }
| w.put(']');
| }
|
| /++
| Constructs an associative array using keys and values.
| Params:
| keys = mutable array of keys
| values = mutable array of values
| Key and value arrays must have the same length.
|
| Complexity: `O(n log(n))`
| +/
10| this()(string[] keys, T[] values) @trusted pure nothrow
| {
10| assert(keys.length == values.length);
10| implementation = new Impl(keys, values);
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| auto keys = ["ba", "a"];
1| auto values = [1.0, 3.0];
1| auto map = StringMap!double(keys, values);
1| assert(map.keys is keys);
1| assert(map.values is values);
| }
|
| /++
| Returns: number of elements in the table.
| +/
| size_t length()() @safe pure nothrow @nogc const @property
| {
242| return implementation ? implementation.length : 0;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!double map;
1| assert(map.length == 0);
1| map["a"] = 3.0;
1| assert(map.length == 1);
1| map["c"] = 4.0;
1| assert(map.length == 2);
1| assert(map.remove("c"));
1| assert(map.length == 1);
1| assert(!map.remove("c"));
1| assert(map.length == 1);
1| assert(map.remove("a"));
1| assert(map.length == 0);
| }
|
| /++
| Returns a dynamic array, the elements of which are the keys in the associative array.
| Doesn't allocate a new copy.
|
| Complexity: `O(1)`
| +/
| const(string)[] keys()() @safe pure nothrow @nogc const @property
| {
32| return implementation ? implementation.keys : null;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!double map;
1| assert(map.keys == []);
1| map["c"] = 4.0;
1| assert(map.keys == ["c"]);
1| map["a"] = 3.0;
1| assert(map.keys == ["c", "a"]);
1| map.remove("c");
1| assert(map.keys == ["a"]);
1| map.remove("a");
1| assert(map.keys == []);
1| map["c"] = 4.0;
1| assert(map.keys == ["c"]);
| }
|
| /++
| Returns a dynamic array, the elements of which are the values in the associative array.
| Doesn't allocate a new copy.
|
| Complexity: `O(1)`
| +/
| inout(T)[] values()() @safe pure nothrow @nogc inout @property
| {
32| return implementation ? implementation.values : null;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!double map;
1| assert(map.values == []);
1| map["c"] = 4.0;
1| assert(map.values == [4.0]);
1| map["a"] = 3.0;
1| assert(map.values == [4.0, 3.0]);
1| map.values[0]++;
1| assert(map.values == [5.0, 3.0]);
1| map.remove("c");
1| assert(map.values == [3.0]);
1| map.remove("a");
1| assert(map.values == []);
1| map["c"] = 4.0;
1| assert(map.values == [4.0]);
| }
|
| /++
| (Property) Gets the current capacity of an associative array.
| The capacity is the size that the underlaynig slices can grow to before the underlying arrays may be reallocated or extended.
|
| Complexity: `O(1)`
| +/
| size_t capacity()() @safe pure nothrow const @property
| {
| import mir.utility: min;
|
30| return !implementation ? 0 : min(
| implementation.keys.capacity,
| implementation.values.capacity,
| implementation.indices.capacity,
| );
| }
|
| version(mir_test) static if (is(T == int))
| ///
| unittest
| {
1| StringMap!double map;
1| assert(map.capacity == 0);
1| map["c"] = 4.0;
1| assert(map.capacity >= 1);
1| map["a"] = 3.0;
1| assert(map.capacity >= 2);
1| map.remove("c");
1| map.assumeSafeAppend;
1| assert(map.capacity >= 2);
| }
|
| /++
| Reserves capacity for an associative array.
| The capacity is the size that the underlaying slices can grow to before the underlying arrays may be reallocated or extended.
| +/
| size_t reserve()(size_t newcapacity) @trusted pure nothrow
| {
| import mir.utility: min;
|
2| if (_expect(!implementation, false))
| {
1| implementation = new Impl;
| }
|
2| auto keysV = implementation.keys;
2| auto keysVCaacity = keysV.reserve(newcapacity);
2| implementation._keys = keysV.ptr;
|
2| auto valuesV = implementation.values;
2| auto valuesCapacity = valuesV.reserve(newcapacity);
2| implementation._values = valuesV.ptr;
|
2| auto indicesV = implementation.indices;
2| auto indicesCapacity = indicesV.reserve(newcapacity);
2| implementation._indices = indicesV.ptr;
|
2| return min(
| keysVCaacity,
| valuesCapacity,
| indicesCapacity,
| );
| }
|
| version(mir_test) static if (is(T == int))
| ///
| unittest
| {
1| StringMap!double map;
1| auto capacity = map.reserve(10);
1| assert(capacity >= 10);
1| assert(map.capacity == capacity);
1| map["c"] = 4.0;
1| assert(map.capacity == capacity);
1| map["a"] = 3.0;
1| assert(map.capacity >= 2);
1| assert(map.remove("c"));
1| capacity = map.reserve(20);
1| assert(capacity >= 20);
1| assert(map.capacity == capacity);
| }
|
| /++
| Assume that it is safe to append to this associative array.
| Appends made to this associative array after calling this function may append in place, even if the array was a slice of a larger array to begin with.
| Use this only when it is certain there are no elements in use beyond the associative array in the memory block. If there are, those elements will be overwritten by appending to this associative array.
|
| Warning: Calling this function, and then using references to data located after the given associative array results in undefined behavior.
|
| Returns: The input is returned.
| +/
| ref inout(typeof(this)) assumeSafeAppend()() @system nothrow inout return
| {
4| if (implementation)
| {
4| implementation.keys.assumeSafeAppend;
4| implementation.values.assumeSafeAppend;
4| implementation.indices.assumeSafeAppend;
| }
4| return this;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| unittest
| {
1| StringMap!double map;
1| map["c"] = 4.0;
1| map["a"] = 3.0;
1| assert(map.capacity >= 2);
1| map.remove("c");
1| assert(map.capacity == 0);
1| map.assumeSafeAppend;
1| assert(map.capacity >= 2);
| }
|
| /++
| Removes all remaining keys and values from an associative array.
|
| Complexity: `O(1)`
| +/
| void clear()() @safe pure nothrow @nogc
| {
1| if (implementation)
| {
1| implementation._length = 0;
1| implementation._lengthTable = implementation._lengthTable[0 .. 0];
| }
|
| }
|
| version(mir_test) static if (is(T == int))
| ///
| unittest
| {
1| StringMap!double map;
1| map["c"] = 4.0;
1| map["a"] = 3.0;
1| map.clear;
1| assert(map.length == 0);
1| assert(map.capacity == 0);
1| map.assumeSafeAppend;
1| assert(map.capacity >= 2);
| }
|
| /++
| `remove(key)` does nothing if the given key does not exist and returns false. If the given key does exist, it removes it from the AA and returns true.
|
| Complexity: `O(log(s))` (not exist) or `O(n)` (exist), where `s` is the count of the strings with the same length as they key.
| +/
| bool remove()(scope const(char)[] key) @trusted pure nothrow @nogc
| {
16| size_t index;
32| if (implementation && implementation.findIndex(key, index))
| {
14| implementation.removeAt(index);
14| return true;
| }
2| return false;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| unittest
| {
1| StringMap!double map;
1| map["a"] = 3.0;
1| map["c"] = 4.0;
1| assert(map.remove("c"));
1| assert(!map.remove("c"));
1| assert(map.remove("a"));
1| assert(map.length == 0);
1| assert(map.capacity == 0);
1| assert(map.assumeSafeAppend.capacity >= 2);
| }
|
| /++
| Finds position of the key in the associative array .
|
| Return: An index starting from `0` that corresponds to the key or `-1` if the associative array doesn't contain the key.
|
| Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
| +/
| ptrdiff_t findPos()(scope const(char)[] key) @trusted pure nothrow @nogc const
| {
8| if (!implementation)
0000000| return -1;
8| size_t index;
8| if (!implementation.findIndex(key, index))
2| return -1;
6| return implementation._indices[index];
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!double map;
1| map["c"] = 3.0;
1| map["La"] = 4.0;
1| map["a"] = 5.0;
|
1| assert(map.findPos("C") == -1);
1| assert(map.findPos("c") == 0);
1| assert(map.findPos("La") == 1);
1| assert(map.findPos("a") == 2);
|
1| map.remove("c");
|
1| assert(map.findPos("c") == -1);
1| assert(map.findPos("La") == 0);
1| assert(map.findPos("a") == 1);
| }
|
| /++
| Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
| +/
| inout(T)* opBinaryRight(string op : "in")(scope const(char)[] key) @system pure nothrow @nogc inout
| {
3| if (!implementation)
1| return null;
2| size_t index;
2| if (!implementation.findIndex(key, index))
0000000| return null;
2| assert (index < length);
2| index = implementation.indices[index];
2| assert (index < length);
2| return implementation._values + index;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @system nothrow pure unittest
| {
1| StringMap!double map;
1| assert(("c" in map) is null);
1| map["c"] = 3.0;
1| assert(*("c" in map) == 3.0);
| }
|
| /++
| Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
| +/
| ref inout(T) opIndex()(scope const(char)[] key) @trusted pure inout //@nogc
| {
14| size_t index;
28| if (implementation && implementation.findIndex(key, index))
| {
14| assert (index < length);
14| index = implementation._indices[index];
14| assert (index < length);
14| return implementation._values[index];
| }
| import mir.exception: MirException;
0000000| throw new MirException("No member: ", key);
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!double map;
1| map["c"] = 3.0;
1| map["La"] = 4.0;
1| map["a"] = 5.0;
|
1| map["La"] += 10;
1| assert(map["La"] == 14.0);
| }
|
| /++
| Complexity: `O(log(s))` (exist) or `O(n)` (not exist), where `s` is the count of the strings with the same length as they key.
| +/
| ref T opIndexAssign(R)(auto ref R value, string key) @trusted pure nothrow
| {
| import core.lifetime: forward, move;
10| T v;
10| v = forward!value;
10| return opIndexAssign(move(v), key);
| }
|
| /// ditto
| ref T opIndexAssign()(T value, string key) @trusted pure nothrow
| {
39| size_t index;
39| if (_expect(!implementation, false))
| {
14| implementation = new Impl;
| }
| else
| {
25| if (key.length + 1 < implementation.lengthTable.length)
| {
17| if (implementation.findIndex(key, index))
| {
1| assert (index < length);
1| index = implementation._indices[index];
1| assert (index < length);
1| implementation._values[index] = move(value);
1| return implementation._values[index];
| }
16| assert (index <= length);
| }
| else
| {
8| index = length;
| }
| }
38| assert (index <= length);
38| implementation.insertAt(key, move(value), index);
38| index = implementation._indices[index];
38| return implementation._values[index];
| }
|
| /++
| Looks up key; if it exists returns corresponding value else evaluates and returns defaultValue.
|
| Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
| +/
| inout(T) get()(scope const(char)[] key, lazy inout(T) defaultValue)
| {
2| size_t index;
4| if (implementation && implementation.findIndex(key, index))
| {
1| assert (index < length);
1| index = implementation.indices[index];
1| assert (index < length);
1| return implementation.values[index];
| }
1| return defaultValue;
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!int map;
1| map["c"] = 3;
1| assert(map.get("c", 1) == 3);
2| assert(map.get("C", 1) == 1);
| }
|
| /++
| Looks up key; if it exists returns corresponding value else evaluates value, adds it to the associative array and returns it.
|
| Complexity: `O(log(s))` (exist) or `O(n)` (not exist), where `s` is the count of the strings with the same length as they key.
| +/
| ref T require()(string key, lazy T value = T.init)
| {
| import std.stdio;
5| size_t index;
5| if (_expect(!implementation, false))
| {
0000000| implementation = new Impl;
| }
| else
| {
5| if (key.length + 1 < implementation.lengthTable.length)
| {
5| if (implementation.findIndex(key, index))
| {
3| assert (index < length);
3| index = implementation.indices[index];
3| assert (index < length);
3| return implementation.values[index];
| }
2| assert (index <= length);
| }
| else
| {
0000000| index = length;
| }
| }
2| assert (index <= length);
2| implementation.insertAt(key, value, index);
2| index = implementation.indices[index];
2| return implementation.values[index];
| }
|
| version(mir_test) static if (is(T == int))
| ///
| @safe pure unittest
| {
1| StringMap!int map = ["aa": 1];
4| int add3(ref int x) { x += 3; return x; }
1| assert(add3(map.require("aa", 10)) == 4);
2| assert(add3(map.require("bb", 10)) == 13);
2| assert(map.require("a", 100));
1| assert(map.require("aa") == 4);
1| assert(map.require("bb") == 13);
1| assert(map.keys == ["aa", "bb", "a"]);
| }
|
| /++
| Converts the associtave array to a common Dlang associative array.
|
| Complexity: `O(n)`.
| +/
| template toAA()
| {
| static if (__traits(compiles, (ref const T a) { T b; b = a;}))
| {
| ///
| T[string] toAA()() const
| {
5| T[string] ret;
42| foreach (i; 0 .. length)
| {
9| ret[implementation.keys[i]] = implementation.values[i];
| }
5| return ret;
| }
| }
| else
| {
| ///
| T[string] toAA()()
| {
| T[string] ret;
| foreach (i; 0 .. length)
| {
| ret[implementation.keys[i]] = implementation.values[i];
| }
| return ret;
| }
|
| ///
| const(T)[string] toAA()() const
| {
| const(T)[string] ret;
| foreach (i; 0 .. length)
| {
| ret[implementation.keys[i]] = implementation.values[i];
| }
| return ret;
| }
| }
| }
|
| ///
| @safe pure nothrow unittest
| {
4| StringMap!int map = ["k": 1];
4| int[string] aa = map.toAA;
4| assert(aa["k"] == 1);
| }
|}
|
|version(mir_test)
|///
|unittest
|{
1| StringMap!int table;
1| table["L"] = 3;
1| table["A"] = 2;
1| table["val"] = 1;
1| assert(table.keys == ["L", "A", "val"]);
1| assert(table.values == [3, 2, 1]);
1| assert(table["A"] == 2);
1| table.values[2] += 10;
1| assert(table["A"] == 2);
1| assert(table["L"] == 3);
1| assert(table["val"] == 11);
1| assert(table.keys == ["L", "A", "val"]);
1| assert(table.values == [3, 2, 11]);
1| table.remove("A");
1| assert(table.keys == ["L", "val"]);
1| assert(table.values == [3, 11]);
1| assert(table["L"] == 3);
1| assert(table["val"] == 11);
|
1| assert(table == table);
|}
|
|private struct StructImpl(T, U = uint)
| if (!__traits(hasMember, T, "opPostMove") && __traits(isUnsigned, U))
|{
| import core.lifetime: move;
| import mir.conv: emplaceRef;
|
| size_t _length;
| string* _keys;
| T* _values;
| U* _indices;
| U[] _lengthTable;
|
| /++
| +/
10| this()(string[] keys, T[] values) @trusted pure nothrow
| {
| import mir.array.allocation: array;
| import mir.ndslice.sorting: makeIndex;
| import mir.ndslice.topology: iota, indexed;
| import mir.string_table: smallerStringFirst;
|
10| assert(keys.length == values.length);
10| if (keys.length == 0)
0000000| return;
10| _length = keys.length;
10| _keys = keys.ptr;
10| _values = values.ptr;
10| _indices = keys.makeIndex!(U, smallerStringFirst).ptr;
10| auto sortedKeys = _keys.indexed(indices);
10| size_t maxKeyLength = sortedKeys[$ - 1].length;
10| _lengthTable = new U[maxKeyLength + 2];
|
10| size_t ski;
126| foreach (length; 0 .. maxKeyLength + 1)
| {
86| while(ski < sortedKeys.length && sortedKeys[ski].length == length)
16| ski++;
32| _lengthTable[length + 1] = cast(U)ski;
| }
| }
|
| void insertAt()(string key, T value, size_t i) @trusted
| {
| pragma(inline, false);
|
40| assert(i <= length);
| {
40| auto a = keys;
40| a ~= key;
40| _keys = a.ptr;
| }
| {
40| auto a = values;
40| a ~= move(value);
40| _values = a.ptr;
| }
| {
40| auto a = indices;
40| a ~= 0;
40| _indices = a.ptr;
|
40| if (__ctfe)
| {
0000000| foreach_reverse (idx; i .. length)
| {
0000000| _indices[idx + 1] = _indices[idx];
| }
| }
| else
| {
| import core.stdc.string: memmove;
40| memmove(_indices + i + 1, _indices + i, (length - i) * U.sizeof);
| }
40| assert(length <= U.max);
40| _indices[i] = cast(U)length;
40| _length++;
| }
| {
40| if (key.length + 2 <= lengthTable.length)
| {
18| ++lengthTable[key.length + 1 .. $];
| }
| else
| {
22| auto oldLen = _lengthTable.length;
22| _lengthTable.length = key.length + 2;
44| auto oldVal = oldLen ? _lengthTable[oldLen - 1] : 0;
22| _lengthTable[oldLen .. key.length + 1] = oldVal;
22| _lengthTable[key.length + 1] = oldVal + 1;
| }
| }
| }
|
| void removeAt()(size_t i)
| {
14| assert(i < length);
14| auto j = _indices[i];
14| assert(j < length);
| {
14| --_lengthTable[_keys[j].length + 1 .. $];
| }
| {
14| if (__ctfe)
| {
0000000| foreach (idx; i .. length)
| {
0000000| _indices[idx] = _indices[idx + 1];
0000000| _indices[idx] = _indices[idx + 1];
| }
| }
| else
| {
| import core.stdc.string: memmove;
14| memmove(_indices + i, _indices + i + 1, (length - 1 - i) * U.sizeof);
| }
90| foreach (ref elem; indices[0 .. $ - 1])
16| if (elem > j)
13| elem--;
| }
| {
14| if (__ctfe)
| {
0000000| foreach_reverse (idx; j .. length - 1)
| {
0000000| _keys[idx] = _keys[idx + 1];
0000000| _values[idx] = move(_values[idx + 1]);
| }
| }
| else
| {
| import core.stdc.string: memmove;
14| destroy!false(_values[j]);
14| memmove(_keys + j, _keys + j + 1, (length - 1 - j) * string.sizeof);
14| memmove(_values + j, _values + j + 1, (length - 1 - j) * T.sizeof);
14| emplaceRef(_values[length - 1]);
| }
| }
14| _length--;
28| _lengthTable = _lengthTable[0 .. length ? _keys[_indices[length - 1]].length + 2 : 0];
| }
|
| size_t length()() @safe pure nothrow @nogc const @property
| {
388| return _length;
| }
|
| inout(string)[] keys()() @trusted inout @property
| {
84| return _keys[0 .. _length];
| }
|
| inout(T)[] values()() @trusted inout @property
| {
90| return _values[0 .. _length];
| }
|
| inout(U)[] indices()() @trusted inout @property
| {
92| return _indices[0 .. _length];
| }
|
| inout(U)[] lengthTable()() @trusted inout @property
| {
88| return _lengthTable;
| }
|
| auto sortedKeys()() @trusted const @property
| {
| import mir.ndslice.topology: indexed;
| return _keys.indexed(indices);
| }
|
| bool findIndex()(scope const(char)[] key, ref size_t index) @trusted pure nothrow @nogc const
| {
| import mir.utility: _expect;
64| if (_expect(key.length + 1 < _lengthTable.length, true))
| {
|
| // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // 0 1 2 3 4 5 6 8 9 10 12 16
|
64| auto low = _lengthTable[key.length] + 0u;
64| auto high = _lengthTable[key.length + 1] + 0u;
95| while (low < high)
| {
72| auto mid = (low + high) / 2;
|
| import core.stdc.string: memcmp;
72| int r = void;
|
72| if (__ctfe)
0000000| r = __cmp(key, _keys[_indices[mid]]);
| else
72| r = memcmp(key.ptr, _keys[_indices[mid]].ptr, key.length);
|
72| if (r == 0)
| {
41| index = mid;
41| return true;
| }
31| if (r > 0)
8| low = mid + 1;
| else
23| high = mid;
| }
23| index = low;
| }
23| return false;
| }
|}
|
|version(mir_test)
|unittest
|{
| import mir.algebraic_alias.json: JsonAlgebraic;
| import mir.string_map: StringMap;
|
1| StringMap!JsonAlgebraic token;
1| token[`access_token`] = "secret-data";
1| token[`expires_in`] = 3599;
1| token[`token_type`] = "Bearer";
|
1| assert(token[`access_token`] == "secret-data");
1| assert(token[`expires_in`] == 3599);
1| assert(token[`token_type`] == "Bearer"); // mir/string_map.d(511): No member: token_type
|
1| const tkType = `token_type` in token;
|
1| assert((*tkType) == "Bearer"); // *tkType contains value 3599
|}
source/mir/string_map.d is 95% covered
<<<<<< EOF
# path=./source-mir-interpolate-package.lst
|/++
|$(H1 Interpolation Algorithms)
|
|$(BOOKTABLE $(H2 Interpolation modules),
|$(TR $(TH Module) $(TH Interpolation kind))
|$(T2M constant, Constant Interpolant)
|$(T2M generic, Generic Piecewise Interpolant)
|$(T2M linear, Linear Interpolant)
|$(T2M polynomial, Lagrange Barycentric Interpolant)
|$(T2M spline, Piecewise Cubic Hermite Interpolant Spline: C2 (with contiguous second derivative), cardinal, monotone (aka PCHIP), double-quadratic, Akima)
|)
|]
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir,interpolate, $1)$(NBSP)
|T2M=$(TR $(TDNW $(MREF mir,interpolate,$1)) $(TD $+))
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.interpolate;
|
|import mir.functional: RefTuple;
|import mir.math.common: optmath;
|import mir.ndslice.slice: Slice, Contiguous;
|import mir.primitives;
|import mir.qualifier;
|import std.traits: isInstanceOf;
|
|@optmath:
|
|package ref iter(alias s)() { return s._iterator; };
|package alias GridVector(It) = Slice!It;
|
|package enum bool isInterval(T) = isInstanceOf!(RefTuple, T);
|package enum bool isInterval(alias T) = isInstanceOf!(RefTuple, T);
|
|package template Repeat(ulong n, L...)
|{
| static if (n)
| {
| static import std.meta;
| alias Repeat = std.meta.Repeat!(n, L);
| }
| else
| alias Repeat = L[0 .. 0];
|}
|
|/++
|Interval index for x value given.
|+/
|template findInterval(size_t dimension = 0)
|{
| /++
| Interval index for x value given.
| Params:
| interpolant = interpolant
| x = X value
| +/
| size_t findInterval(Interpolant, X)(auto ref const Interpolant interpolant, in X x) @trusted
| {
| import mir.ndslice.slice: sliced;
| import mir.ndslice.sorting: transitionIndex;
| static if (dimension)
| {
294| immutable sizediff_t len = interpolant.intervalCount!dimension - 1;
294| auto grid = interpolant.gridScopeView!dimension[1 .. $][0 .. len];
| }
| else
| {
1090| immutable sizediff_t len = interpolant.intervalCount - 1;
1090| auto grid = interpolant.gridScopeView[1 .. $][0 .. len];
| }
1384| assert(len >= 0);
1384| return grid.transitionIndex!"a <= b"(x);
| }
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: rcslice;
| import mir.ndslice.topology: as;
| import mir.ndslice.slice: sliced;
| import mir.interpolate.linear;
|
| static immutable x = [0.0, 1, 2];
| static immutable y = [10.0, 2, 4];
2| auto interpolation = linear!double(x.rcslice, y.as!(const double).rcslice);
1| assert(interpolation.findInterval(1.0) == 1);
|}
|
|/++
|Lazy interpolation shell with linear complexity.
|
|Params:
| range = sorted range
| interpolant = interpolant structure with `.gridScopeView` method.
|Complexity:
| `O(range.length + interpolant.gridScopeView.length)` to evaluate all elements.
|Returns:
| Lazy input range.
|See_also:
| $(SUBREF linear, linear),
| $(SUBREF spline, spline).
|+/
|auto interp1(Range, Interpolant)(Range range, Interpolant interpolant, size_t interval = 0)
|{
2| return Interp1!(Range, Interpolant)(range, interpolant, interval);
|}
|
|/// ditto
1|struct Interp1(Range, Interpolant)
|{
| /// Sorted range (descending). $(RED For internal use.)
| private Range _range;
| /// Interpolant structure. $(RED For internal use.)
| private Interpolant _interpolant;
| /// Current interpolation interval. $(RED For internal use.)
| private size_t _interval;
|
| ///
| auto lightScope()
| {
0000000| return Interp1!(LightScopeOf!Range, LightScopeOf!Interpolant)(.lightScope(_range), .lightScope(_interpolant), _interval);
| }
|
| static if (hasLength!Range)
| /// Length (optional)
1| size_t length()() const @property { return _range.length; }
| /// Save primitive (optional)
| auto save()() @property
| {
| auto ret = this;
| ret._range = _range.save;
| return ret;
| }
| /// Input range primitives
61| bool empty () const @property { return _range.empty; }
| /// ditto
0000000| void popFront() { _range.popFront; }
| /// ditto
| auto front() @property
|
| {
30| assert(!empty);
30| auto x = _range.front;
30| return (x) @trusted {
30| auto points = _interpolant.gridScopeView;
30| sizediff_t len = _interpolant.intervalCount - 1;
30| assert(len >= 0);
44| while (x > points[_interval + 1] && _interval < len)
7| _interval++;
30| return _interpolant(x.atInterval(_interval));
| } (x);
| }
|}
|
|/++
|PCHIP interpolation.
|+/
|version(mir_test)
|@safe unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.allocation: rcslice;
| import mir.interpolate: interp1;
| import mir.interpolate.spline;
|
| static immutable x = [1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22];
| static immutable y = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
2| auto interpolation = spline!double(x.rcslice, y.sliced, SplineType.monotone);
|
1| auto xs = x[0 .. $ - 1].sliced + 0.5;
|
2| auto ys = xs.interp1(interpolation);
|}
|
|@safe pure @nogc version(mir_test) unittest
|{
| import mir.interpolate.linear;
| import mir.ndslice;
| import mir.math.common: approxEqual;
|
| static immutable x = [0, 1, 2, 3, 5.00274, 7.00274, 10.0055, 20.0137, 30.0192];
| static immutable y = [0.0011, 0.0011, 0.0030, 0.0064, 0.0144, 0.0207, 0.0261, 0.0329, 0.0356,];
| static immutable xs = [1, 2, 3, 4.00274, 5.00274, 6.00274, 7.00274, 8.00548, 9.00548, 10.0055, 11.0055, 12.0082, 13.0082, 14.0082, 15.0082, 16.011, 17.011, 18.011, 19.011, 20.0137, 21.0137, 22.0137, 23.0137, 24.0164, 25.0164, 26.0164, 27.0164, 28.0192, 29.0192, 30.0192];
|
2| auto interpolation = linear!double(x.rcslice, y.as!(const double).rcslice);
|
| static immutable data = [0.0011, 0.0030, 0.0064, 0.0104, 0.0144, 0.0176, 0.0207, 0.0225, 0.0243, 0.0261, 0.0268, 0.0274, 0.0281, 0.0288, 0.0295, 0.0302, 0.0309, 0.0316, 0.0322, 0.0329, 0.0332, 0.0335, 0.0337, 0.0340, 0.0342, 0.0345, 0.0348, 0.0350, 0.0353, 0.0356];
|
1| () @trusted {
31| assert(all!((a, b) => approxEqual(a, b, 1e-4, 1e-4))(xs.interp1(interpolation), data));
| }();
|}
|
|/++
|Optimization utility that can be used with interpolants if
|x should be extrapolated at interval given.
|
|By default interpolants uses binary search to find appropriate interval,
|it has `O(log(.gridScopeView.length))` complexity.
|If an interval is given, interpolant uses it instead of binary search.
|+/
|RefTuple!(T, size_t) atInterval(T)(in T value, size_t intervalIndex)
|{
32| return typeof(return)(value, intervalIndex);
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.allocation;
| import mir.ndslice.slice;
| import mir.interpolate.spline;
| static immutable x = [0.0, 1, 2];
| static immutable y = [3.0, 4, -10];
2| auto interpolant = spline!double(x.rcslice, y.sliced);
1| assert(interpolant(1.3) != interpolant(1.3.atInterval(0)));
1| assert(interpolant(1.3) == interpolant(1.3.atInterval(1)));
|}
|
|version(D_AVX2)
| enum _avx = true;
|else
|version(D_AVX)
| enum _avx = true;
|else
| enum _avx = false;
|
|version(LDC)
| enum LDC = true;
|else
| enum LDC = false;
|
|version(X86_64)
| enum x86_64 = true;
|else
| enum x86_64 = false;
|
|auto copyvec(F, size_t N)(ref const F[N] from, ref F[N] to)
|{
| import mir.internal.utility;
|
| static if (LDC && F.mant_dig != 64 && is(__vector(F[N])))
| {
| alias V = __vector(F[N]); // @FUTURE@ vector support
| *cast(V*) to.ptr = *cast(V*) from.ptr;
| }
| else
| static if (F.sizeof <= double.sizeof && F[N].sizeof >= (double[2]).sizeof && x86_64)
| {
| import mir.utility;
| enum S = _avx ? 32u : 16u;
| enum M = min(S, F[N].sizeof) / F.sizeof;
| alias V = __vector(F[M]); // @FUTURE@ vector support
| enum C = N / M;
| foreach(i; Iota!C)
| {
| *cast(V*)(to.ptr + i * M) = *cast(V*)(from.ptr + i * M);
| }
| }
| else
| {
2778| to = from;
| }
|}
|
|package template SplineReturnType(F, size_t N, size_t P)
|{
| static if (P <= 1 || N == 0)
| alias SplineReturnType = F;
| else
| alias SplineReturnType = .SplineReturnType!(F, N - 1, P)[P];
|}
|
|template generateShuffles3(size_t N, size_t P)
|{
| enum size_t[N][2] generateShuffles3 =
| ()
| {
| auto ret = new size_t[](2 * N);
| size_t[2] j;
| bool f = 1;
| foreach(i; 0 .. N)
| {
| ret[i * 2] = i;
| ret[i * 2 + 1] = i + N;
| }
| return [ret[0 .. $ / 2], ret[$ / 2 .. $]];
| }();
|}
|
|
|void shuffle3(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N] d)
| if (P <= N && N)
|{
| static if (P == 0 || N == 1)
| {
| copyvec(a, c);
| copyvec(b, d);
| }
| else
| version(LDC)
| {
| enum masks = generateShuffles3!(N, P);
| import std.meta: aliasSeqOf;
| import ldc.simd: shufflevector;
| alias V = __vector(F[N]); // @FUTURE@ vector support
| auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
| auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
| *cast(V*)c.ptr = as;
| *cast(V*)d.ptr = bs;
| }
| else
| {
| foreach(i; Iota!(N / P))
| {
| enum j = 2 * i * P;
| static if (j < N)
| {
24| copyvec(a[i * P .. i * P + P], c[j .. j + P]);
| static assert(j + 2 * P <= c.length);
24| copyvec(b[i * P .. i * P + P], c[j + P .. j + 2 * P]);
| }
| else
| {
24| copyvec(a[i * P .. i * P + P], d[j - N .. j - N + P]);
24| copyvec(b[i * P .. i * P + P], d[j - N + P .. j - N + 2 * P]);
| }
| }
| }
|}
|
|void shuffle2(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N] d)
| if (P <= N && N)
|{
| static if (P == 0 || N == 1)
| {
478| copyvec(a, c);
478| copyvec(b, d);
| }
| else
| version(LDC)
| {
| enum masks = generateShuffles2!(N, P);
| import std.meta: aliasSeqOf;
| import ldc.simd: shufflevector;
| alias V = __vector(F[N]); // @FUTURE@ vector support
| auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
| auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
| *cast(V*)c.ptr = as;
| *cast(V*)d.ptr = bs;
| }
| else
| {
| foreach(i; Iota!(N / P))
| {
| enum j = 2 * i * P;
| static if (j < N)
429| copyvec(a[j .. j + P], c[i * P .. i * P + P]);
| else
425| copyvec(b[j - N .. j - N + P], c[i * P .. i * P + P]);
| }
| foreach(i; Iota!(N / P))
| {
| enum j = 2 * i * P + P;
| static if (j < N)
425| copyvec(a[j .. j + P], d[i * P .. i * P + P]);
| else
429| copyvec(b[j - N .. j - N + P], d[i * P .. i * P + P]);
| }
| }
|}
|
|void shuffle1(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N] d)
| if (P <= N && N)
|{
| static if (P == 0 || N == 1)
| {
| copyvec(a, c);
| copyvec(b, d);
| }
| else
| version(LDC)
| {
| enum masks = generateShuffles1!(N, P);
| import std.meta: aliasSeqOf;
| import ldc.simd: shufflevector;
| alias V = __vector(F[N]); // @FUTURE@ vector support
| auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
| auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
| *cast(V*)c.ptr = as;
| *cast(V*)d.ptr = bs;
| }
| else
| {
| foreach(i; Iota!(N / P))
| {
| enum j = i * P;
| static if (i % 2 == 0)
3| copyvec(a[j .. j + P], c[i * P .. i * P + P]);
| else
3| copyvec(b[j - P .. j], c[i * P .. i * P + P]);
| }
| foreach(i; Iota!(N / P))
| {
| enum j = i * P + P;
| static if (i % 2 == 0)
3| copyvec(a[j .. j + P], d[i * P .. i * P + P]);
| else
3| copyvec(b[j - P .. j], d[i * P .. i * P + P]);
| }
| }
|}
|
|template generateShuffles2(size_t N, size_t P)
|{
| enum size_t[N][2] generateShuffles2 =
| ()
| {
| auto ret = new size_t[][](2, N);
| size_t[2] j;
| bool f = 1;
| foreach(i; 0 .. 2 * N)
| {
| if (i % P == 0)
| f = !f;
| ret[f][j[f]++] = i;
| }
| return ret;
| }();
|}
|
|
|template generateShuffles1(size_t N, size_t P)
|{
| enum size_t[N][2] generateShuffles1 =
| ()
| {
| auto ret = new size_t[][](2, N);
| foreach(i; 0 .. N)
| {
| ret[0][i] = (i / P + 1) % 2 ? i : i + N - P;
| ret[1][i] = ret[0][i] + P;
| }
| return ret;
| }();
|}
|
|unittest
|{
1| assert(generateShuffles1!(4, 1) == [[0, 4, 2, 6], [1, 5, 3, 7]]);
1| assert(generateShuffles1!(4, 2) == [[0, 1, 4, 5], [2, 3, 6, 7]]);
1| assert(generateShuffles1!(4, 4) == [[0, 1, 2, 3], [4, 5, 6, 7]]);
|}
|
|unittest
|{
1| assert(generateShuffles2!(4, 1) == [[0, 2, 4, 6], [1, 3, 5, 7]]);
1| assert(generateShuffles2!(4, 2) == [[0, 1, 4, 5], [2, 3, 6, 7]]);
1| assert(generateShuffles2!(4, 4) == [[0, 1, 2, 3], [4, 5, 6, 7]]);
|}
|
|unittest
|{
| enum ai = [0, 1, 2, 3];
| enum bi = [4, 5, 6, 7];
| align(32)
| double[4] a = [0, 1, 2, 3], b = [4, 5, 6, 7], c, d;
1| shuffle3!1(a, b, c, d);
1| assert([c, d] == [[0.0, 4, 1, 5], [2.0, 6, 3, 7]]);
|}
|
|unittest
|{
| enum ai = [0, 1, 2, 3];
| enum bi = [4, 5, 6, 7];
| align(32)
| double[4] a = [0, 1, 2, 3], b = [4, 5, 6, 7], c, d;
1| shuffle2!1(a, b, c, d);
1| assert([c, d] == [[0.0, 2, 4, 6], [1.0, 3, 5, 7]]);
1| shuffle2!2(a, b, c, d);
1| assert([c, d] == [[0.0, 1, 4, 5], [2.0, 3, 6, 7]]);
| // shuffle2!4(a, b, c, d);
| // assert([c, d] == [[0.0, 1, 2, 3], [4.0, 5, 6, 7]]);
|}
|
|unittest
|{
| enum ai = [0, 1, 2, 3];
| enum bi = [4, 5, 6, 7];
| align(32)
| double[4] a = [0, 1, 2, 3], b = [4, 5, 6, 7], c, d;
1| shuffle1!1(a, b, c, d);
1| assert([c, d] == [[0, 4, 2, 6], [1, 5, 3, 7]]);
1| shuffle1!2(a, b, c, d);
1| assert([c, d] == [[0, 1, 4, 5], [2, 3, 6, 7]]);
| // shuffle1!4(a, b, c, d);
| // assert([c, d] == [[0, 1, 2, 3], [4, 5, 6, 7]]);
|}
|
|import mir.internal.utility;
|
|auto vectorize(Kernel, F, size_t N, size_t R)(ref Kernel kernel, ref F[N] a0, ref F[N] b0, ref F[N] a1, ref F[N] b1, ref F[N][R] c)
|{
| // static if (LDC && F.mant_dig != 64)
| // {
| // alias V = __vector(F[N]); // @FUTURE@ vector support
| // *cast(V[R]*) c.ptr = kernel(
| // *cast(V*)a0.ptr, *cast(V*)b0.ptr,
| // *cast(V*)a1.ptr, *cast(V*)b1.ptr);
| // }
| // else
| // static if (F.sizeof <= double.sizeof && F[N].sizeof >= (double[2]).sizeof)
| // {
| // import mir.utility;
| // enum S = _avx ? 32u : 16u;
| // enum M = min(S, F[N].sizeof) / F.sizeof;
| // alias V = __vector(F[M]); // @FUTURE@ vector support
| // enum C = N / M;
| // foreach(i; Iota!C)
| // {
| // auto r = kernel(
| // *cast(V*)(a0.ptr + i * M), *cast(V*)(b0.ptr + i * M),
| // *cast(V*)(a1.ptr + i * M), *cast(V*)(b1.ptr + i * M),
| // );
| // static if (R == 1)
| // *cast(V*)(c[0].ptr + i * M) = r;
| // else
| // foreach(j; Iota!R)
| // *cast(V*)(c[j].ptr + i * M) = r[j];
| // }
| // }
| // else
| // {
| foreach(i; Iota!N)
| {
1597| auto r = kernel(a0[i], b0[i], a1[i], b1[i]);
| static if (R == 1)
1533| c[0][i] = r;
| else
| foreach(j; Iota!R)
168| c[j][i] = r[j];
| }
| // }
|}
|
|auto vectorize(Kernel, F, size_t N, size_t R)(ref Kernel kernel, ref F[N] a, ref F[N] b, ref F[N][R] c)
|{
| // static if (LDC && F.mant_dig != 64 && is(__vector(F[N])))
| // {
| // alias V = __vector(F[N]); // @FUTURE@ vector support
| // *cast(V[R]*) c.ptr = kernel(*cast(V*)a.ptr, *cast(V*)b.ptr);
| // }
| // else
| // static if (F.sizeof <= double.sizeof && F[N].sizeof >= (double[2]).sizeof && x86_64)
| // {
| // import mir.utility;
| // enum S = _avx ? 32u : 16u;
| // enum M = min(S, F[N].sizeof) / F.sizeof;
| // alias V = __vector(F[M]); // @FUTURE@ vector support
| // enum C = N / M;
| // foreach(i; Iota!C)
| // {
| // auto r = kernel(
| // *cast(V*)(a.ptr + i * M),
| // *cast(V*)(b.ptr + i * M),
| // );
| // static if (R == 1)
| // *cast(V*)(c[0].ptr + i * M) = r;
| // else
| // foreach(j; Iota!R)
| // *cast(V*)(c[j].ptr + i * M) = r[j];
| // }
| // }
| // else
| // {
290| F[N][R] _c;//Temporary array in case "c" overlaps "a" and/or "b".
| foreach(i; Iota!N)
| {
566| auto r = kernel(a[i], b[i]);
| static if (R == 1)
550| _c[0][i] = r;
| else
| foreach(j; Iota!R)
32| _c[j][i] = r[j];
| }
290| c = _c;
| // }
|}
|
|// version(unittest)
|// template _test_fun(size_t R)
|// {
|// auto _test_fun(T)(T a0, T b0, T a1, T b1)
|// {
|// static if (R == 4)
|// {
|// return cast(T[R])[a0, b0, a1, b1];
|// }
|// else
|// static if (R == 2)
|// {
|// return cast(T[R])[a0 + b0, a1 + b1];
|// }
|// else
|// return a0 + b0 + a1 + b1;
|// }
|// }
|
|// unittest
|// {
|// import std.meta;
|
|// foreach(F; AliasSeq!(float, double))
|// foreach(N; AliasSeq!(1, 2, 4, 8, 16))
|// {
|// align(F[N].sizeof) F[N] a0 = 4;
|// align(F[N].sizeof) F[N] b0 = 30;
|// align(F[N].sizeof) F[N] a1 = 200;
|// align(F[N].sizeof) F[N] b1 = 1000;
|// align(F[N].sizeof) F[N][1] c1;
|// align(F[N].sizeof) F[N][2] c2;
|// align(F[N].sizeof) F[N][4] c4;
|// vectorize!(_test_fun!(1))(a0, b0, a1, b1, c1);
|// vectorize!(_test_fun!(2))(a0, b0, a1, b1, c2);
|// vectorize!(_test_fun!(4))(a0, b0, a1, b1, c4);
|// }
|// }
source/mir/interpolate/package.d is 97% covered
<<<<<< EOF
# path=./source-mir-ndslice-field.lst
|/++
|This is a submodule of $(MREF mir,ndslice).
|
|Field is a type with `opIndex()(ptrdiff_t index)` primitive.
|An iterator can be created on top of a field using $(SUBREF iterator, FieldIterator).
|An ndslice can be created on top of a field using $(SUBREF slice, slicedField).
|
|$(BOOKTABLE $(H2 Fields),
|$(TR $(TH Field Name) $(TH Used By))
|$(T2 BitField, $(SUBREF topology, bitwise))
|$(T2 BitpackField, $(SUBREF topology, bitpack))
|$(T2 CycleField, $(SUBREF topology, cycle) (2 kinds))
|$(T2 LinspaceField, $(SUBREF topology, linspace))
|$(T2 MagicField, $(SUBREF topology, magic))
|$(T2 MapField, $(SUBREF topology, map) and $(SUBREF topology, mapField))
|$(T2 ndIotaField, $(SUBREF topology, ndiota))
|$(T2 OrthogonalReduceField, $(SUBREF topology, orthogonalReduceField))
|$(T2 RepeatField, $(SUBREF topology, repeat))
|$(T2 SparseField, Used for mutable DOK sparse matrixes )
|)
|
|
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.ndslice.field;
|
|import mir.internal.utility: Iota;
|import mir.math.common: optmath;
|import mir.ndslice.internal;
|import mir.qualifier;
|
|@optmath:
|
|package template ZeroShiftField(T)
|{
| static if (hasZeroShiftFieldMember!T)
| alias ZeroShiftField = typeof(T.init.assumeFieldsHaveZeroShift());
| else
| alias ZeroShiftField = T;
|}
|
|package enum hasZeroShiftFieldMember(T) = __traits(hasMember, T, "assumeFieldsHaveZeroShift");
|
|package auto applyAssumeZeroShift(Types...)()
|{
| import mir.ndslice.topology;
0000000| string str;
| foreach(i, T; Types)
| static if (hasZeroShiftFieldMember!T)
0000000| str ~= "_fields[" ~ i.stringof ~ "].assumeFieldsHaveZeroShift, ";
| else
0000000| str ~= "_fields[" ~ i.stringof ~ "], ";
0000000| return str;
|}
|
|auto MapField__map(Field, alias fun, alias fun1)(ref MapField!(Field, fun) f)
|{
| import core.lifetime: move;
| import mir.functional: pipe;
3| return MapField!(Field, pipe!(fun, fun1))(move(f._field));
|}
|
|
|/++
|`MapField` is used by $(SUBREF topology, map).
|+/
|struct MapField(Field, alias _fun)
|{
|@optmath:
| ///
| Field _field;
|
| ///
| auto lightConst()() const @property
| {
32| return MapField!(LightConstOf!Field, _fun)(.lightConst(_field));
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| return MapField!(LightImmutableOf!Field, _fun)(.lightImmutable(_field));
| }
|
| /++
| User defined constructor used by $(LREF mapField).
| +/
| static alias __map(alias fun1) = MapField__map!(Field, _fun, fun1);
|
| auto ref opIndex(T...)(auto ref T index)
| {
| import mir.functional: RefTuple, unref;
| static if (is(typeof(_field[index]) : RefTuple!K, K...))
| {
199| auto t = _field[index];
199| return mixin("_fun(" ~ _iotaArgs!(K.length, "t.expand[", "].unref, ") ~ ")");
| }
| else
772| return _fun(_field[index]);
| }
|
| static if (__traits(hasMember, Field, "length"))
| auto length() const @property
| {
0000000| return _field.length;
| }
|
| static if (__traits(hasMember, Field, "shape"))
| auto shape() const @property
| {
0000000| return _field.shape;
| }
|
| static if (__traits(hasMember, Field, "elementCount"))
| auto elementCount() const @property
| {
0000000| return _field.elementCount;
| }
|
| static if (hasZeroShiftFieldMember!Field)
| /// Defined if `Field` has member `assumeFieldsHaveZeroShift`.
| auto assumeFieldsHaveZeroShift() @property
| {
| return _mapField!_fun(_field.assumeFieldsHaveZeroShift);
| }
|}
|
|/++
|`VmapField` is used by $(SUBREF topology, map).
|+/
|struct VmapField(Field, Fun)
|{
|@optmath:
| ///
| Field _field;
| ///
| Fun _fun;
|
| ///
| auto lightConst()() const @property
| {
| return VmapField!(LightConstOf!Field, _fun)(.lightConst(_field));
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| return VmapField!(LightImmutableOf!Field, _fun)(.lightImmutable(_field));
| }
|
| auto ref opIndex(T...)(auto ref T index)
| {
| import mir.functional: RefTuple, unref;
| static if (is(typeof(_field[index]) : RefTuple!K, K...))
| {
| auto t = _field[index];
| return mixin("_fun(" ~ _iotaArgs!(K.length, "t.expand[", "].unref, ") ~ ")");
| }
| else
| return _fun(_field[index]);
| }
|
| static if (__traits(hasMember, Field, "length"))
| auto length() const @property
| {
| return _field.length;
| }
|
| static if (__traits(hasMember, Field, "shape"))
| auto shape() const @property
| {
| return _field.shape;
| }
|
| static if (__traits(hasMember, Field, "elementCount"))
| auto elementCount()const @property
| {
| return _field.elementCount;
| }
|
| static if (hasZeroShiftFieldMember!Field)
| /// Defined if `Field` has member `assumeFieldsHaveZeroShift`.
| auto assumeFieldsHaveZeroShift() @property
| {
| return _vmapField(_field.assumeFieldsHaveZeroShift, _fun);
| }
|}
|
|/+
|Creates a mapped field. Uses `__map` if possible.
|+/
|auto _mapField(alias fun, Field)(Field field)
|{
| import mir.functional: naryFun;
| static if ((
| __traits(isSame, fun, naryFun!"a|b") ||
| __traits(isSame, fun, naryFun!"a^b") ||
| __traits(isSame, fun, naryFun!"a&b") ||
| __traits(isSame, fun, naryFun!"a | b") ||
| __traits(isSame, fun, naryFun!"a ^ b") ||
| __traits(isSame, fun, naryFun!"a & b")) &&
| is(Field : ZipField!(BitField!(LeftField, I), BitField!(RightField, I)), LeftField, RightField, I))
| {
| import mir.ndslice.topology: bitwiseField;
1| auto f = ZipField!(LeftField, RightField)(field._fields[0]._field, field._fields[1]._field)._mapField!fun;
1| return f.bitwiseField!(typeof(f), I);
| }
| else
| static if (__traits(hasMember, Field, "__map"))
9| return Field.__map!fun(field);
| else
20| return MapField!(Field, fun)(field);
|}
|
|/+
|Creates a mapped field. Uses `__vmap` if possible.
|+/
|auto _vmapField(Field, Fun)(Field field, Fun fun)
|{
| static if (__traits(hasMember, Field, "__vmap"))
| return Field.__vmap(field, fun);
| else
| return VmapField!(Field, Fun)(field, fun);
|}
|
|/++
|Iterates multiple fields in lockstep.
|
|`ZipField` is used by $(SUBREF topology, zipFields).
|+/
|struct ZipField(Fields...)
| if (Fields.length > 1)
|{
|@optmath:
| import mir.functional: RefTuple, Ref, _ref;
| import std.meta: anySatisfy;
|
| ///
| Fields _fields;
|
| ///
| auto lightConst()() const @property
| {
| import std.format;
| import mir.ndslice.topology: iota;
| import std.meta: staticMap;
0000000| return mixin("ZipField!(staticMap!(LightConstOf, Fields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota));
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| import std.format;
| import mir.ndslice.topology: iota;
| import std.meta: staticMap;
| return mixin("ZipField!(staticMap!(LightImmutableOf, Fields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota));
| }
|
| auto opIndex()(ptrdiff_t index)
| {
| alias Iterators = Fields;
| alias _iterators = _fields;
| import mir.ndslice.iterator: _zip_types, _zip_index;
0000000| return mixin("RefTuple!(_zip_types!Fields)(" ~ _zip_index!Fields ~ ")");
| }
|
| auto opIndexAssign(Types...)(RefTuple!(Types) value, ptrdiff_t index)
| if (Types.length == Fields.length)
| {
| foreach(i, ref val; value.expand)
| {
| _fields[i][index] = val;
| }
| return opIndex(index);
| }
|
| static if (anySatisfy!(hasZeroShiftFieldMember, Fields))
| /// Defined if at least one of `Fields` has member `assumeFieldsHaveZeroShift`.
| auto assumeFieldsHaveZeroShift() @property
| {
| import std.meta: staticMap;
| return mixin("ZipField!(staticMap!(ZeroShiftField, Fields))(" ~ applyAssumeZeroShift!Fields ~ ")");
| }
|}
|
|/++
|`RepeatField` is used by $(SUBREF topology, repeat).
|+/
|struct RepeatField(T)
|{
| import std.traits: Unqual;
|
|@optmath:
| alias UT = Unqual!T;
|
| ///
| UT _value;
|
| ///
| auto lightConst()() const @property @trusted
| {
55| return RepeatField!(const T)(cast(UT) _value);
| }
|
| ///
| auto lightImmutable()() immutable @property @trusted
| {
| return RepeatField!(immutable T)(cast(UT) _value);
| }
|
| auto ref T opIndex()(ptrdiff_t) @trusted
906| { return cast(T) _value; }
|}
|
|/++
|`BitField` is used by $(SUBREF topology, bitwise).
|+/
2|struct BitField(Field, I = typeof(cast()Field.init[size_t.init]))
| if (__traits(isUnsigned, I))
|{
|@optmath:
| import mir.bitop: ctlz;
| package(mir) alias E = I;
| package(mir) enum shift = ctlz(I.sizeof) + 3;
|
| ///
| Field _field;
|
| /// optimization for bitwise operations
| auto __vmap(Fun : LeftOp!(op, bool), string op)(Fun fun)
| if (op == "|" || op == "&" || op == "^")
| {
| import mir.ndslice.topology: bitwiseField;
| return _vmapField(_field, RightOp!(op, I)(I(0) - fun.value)).bitwiseField;
| }
|
| /// ditto
| auto __vmap(Fun : RightOp!(op, bool), string op)(Fun fun)
| if (op == "|" || op == "&" || op == "^")
| {
| import mir.ndslice.topology: bitwiseField;
| return _vmapField(_field, RightOp!(op, I)(I(0) - fun.value)).bitwiseField;
| }
|
| /// ditto
| auto __vmap(Fun)(Fun fun)
| {
| return VmapField!(typeof(this), Fun)(this, fun);
| }
|
| /// ditto
| alias __map(alias fun) = BitField__map!(Field, I, fun);
|
| ///
| auto lightConst()() const @property
| {
8| return BitField!(LightConstOf!Field, I)(mir.qualifier.lightConst(_field));
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| return BitField!(LightImmutableOf!Field, I)(mir.qualifier.lightImmutable(_field));
| }
|
| bool opIndex()(size_t index)
| {
| import mir.bitop: bt;
2857| return bt!(Field, I)(_field, index) != 0;
| }
|
| bool opIndexAssign()(bool value, size_t index)
| {
| import mir.bitop: bta;
634| bta!(Field, I)(_field, index, value);
634| return value;
| }
|
| static if (hasZeroShiftFieldMember!Field)
| /// Defined if `Field` has member `assumeFieldsHaveZeroShift`.
| auto assumeFieldsHaveZeroShift() @property
| {
| return BitField!(ZeroShiftField!Field, I)(_field.assumeFieldsHaveZeroShift);
| }
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.iterator: FieldIterator;
1| ushort[10] data;
1| auto f = FieldIterator!(BitField!(ushort*))(0, BitField!(ushort*)(data.ptr));
1| f[123] = true;
1| f++;
1| assert(f[122]);
|}
|
|auto BitField__map(Field, I, alias fun)(BitField!(Field, I) field)
|{
| import core.lifetime: move;
| import mir.functional: naryFun;
| static if (__traits(isSame, fun, naryFun!"~a") || __traits(isSame, fun, naryFun!"!a"))
| {
| import mir.ndslice.topology: bitwiseField;
5| auto f = _mapField!(naryFun!"~a")(move(field._field));
5| return f.bitwiseField!(typeof(f), I);
| }
| else
| {
1| return MapField!(BitField!(Field, I), fun)(move(field));
| }
|}
|
|/++
|`BitpackField` is used by $(SUBREF topology, bitpack).
|+/
|struct BitpackField(Field, uint pack, I = typeof(cast()Field.init[size_t.init]))
| if (__traits(isUnsigned, I))
|{
| //static assert();
|@optmath:
| package(mir) alias E = I;
| package(mir) enum mask = (I(1) << pack) - 1;
| package(mir) enum bits = I.sizeof * 8;
|
| ///
| Field _field;
|
| ///
| auto lightConst()() const @property
| {
0000000| return BitpackField!(LightConstOf!Field, pack)(.lightConst(_field));
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| return BitpackField!(LightImmutableOf!Field, pack)(.lightImmutable(_field));
| }
|
| I opIndex()(size_t index)
| {
25| index *= pack;
25| size_t start = index % bits;
25| index /= bits;
25| auto ret = (_field[index] >>> start) & mask;
| static if (bits % pack)
| {
25| sizediff_t end = start - (bits - pack);
25| if (end > 0)
7| ret ^= cast(I)(_field[index + 1] << (bits - end)) >>> (bits - pack);
| }
25| return cast(I) ret;
| }
|
| I opIndexAssign()(I value, size_t index)
| {
| import std.traits: Unsigned;
18| assert(cast(Unsigned!I)value <= mask);
18| index *= pack;
18| size_t start = index % bits;
18| index /= bits;
18| _field[index] = cast(I)((_field[index] & ~(mask << start)) ^ (value << start));
| static if (bits % pack)
| {
18| sizediff_t end = start - (bits - pack);
18| if (end > 0)
5| _field[index + 1] = cast(I)((_field[index + 1] & ~((I(1) << end) - 1)) ^ (value >>> (pack - end)));
| }
18| return value;
| }
|
| static if (hasZeroShiftFieldMember!Field)
| /// Defined if `Field` has member `assumeFieldsHaveZeroShift`.
| auto assumeFieldsHaveZeroShift() @property
| {
| return BitpackField!(ZeroShiftField!Field, pack, I)(_field.assumeFieldsHaveZeroShift);
| }
|}
|
|///
|unittest
|{
| import mir.ndslice.iterator: FieldIterator;
1| ushort[10] data;
1| auto f = FieldIterator!(BitpackField!(ushort*, 6))(0, BitpackField!(ushort*, 6)(data.ptr));
1| f[0] = cast(ushort) 31;
1| f[1] = cast(ushort) 13;
1| f[2] = cast(ushort) 8;
1| f[3] = cast(ushort) 43;
1| f[4] = cast(ushort) 28;
1| f[5] = cast(ushort) 63;
1| f[6] = cast(ushort) 39;
1| f[7] = cast(ushort) 23;
1| f[8] = cast(ushort) 44;
|
1| assert(f[0] == 31);
1| assert(f[1] == 13);
1| assert(f[2] == 8);
1| assert(f[3] == 43);
1| assert(f[4] == 28);
1| assert(f[5] == 63);
1| assert(f[6] == 39);
1| assert(f[7] == 23);
1| assert(f[8] == 44);
1| assert(f[9] == 0);
1| assert(f[10] == 0);
1| assert(f[11] == 0);
|}
|
|unittest
|{
| import mir.ndslice.slice;
| import mir.ndslice.topology;
| import mir.ndslice.sorting;
1| uint[2] data;
1| auto packed = data[].sliced.bitpack!18;
1| assert(packed.length == 3);
1| packed[0] = 5;
1| packed[1] = 3;
1| packed[2] = 2;
1| packed.sort;
1| assert(packed[0] == 2);
1| assert(packed[1] == 3);
1| assert(packed[2] == 5);
|}
|
|///
|struct OrthogonalReduceField(FieldsIterator, alias fun, T)
|{
| import mir.ndslice.slice: Slice;
|
|@optmath:
| /// non empty slice
|
| Slice!FieldsIterator _fields;
|
| ///
| T _initialValue;
|
| ///
| auto lightConst()() const @property
| {
1| auto fields = _fields.lightConst;
1| return OrthogonalReduceField!(fields.Iterator, fun, T)(fields, _initialValue);
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| auto fields = _fields.lightImmutable;
| return OrthogonalReduceField!(fields.Iterator, fun, T)(fields, _initialValue);
| }
|
| /// `r = fun(r, fields[i][index]);` reduction by `i`
| auto opIndex()(size_t index)
| {
| import std.traits: Unqual;
100| auto fields = _fields;
100| T r = _initialValue;
100| if (!fields.empty) do
| {
300| r = cast(T) fun(r, fields.front[index]);
300| fields.popFront;
| }
300| while(!fields.empty);
100| return r;
| }
|}
|
|///
|struct CycleField(Field)
|{
| import mir.ndslice.slice: Slice;
|
|@optmath:
| /// Cycle length
| size_t _length;
| ///
| Field _field;
|
| ///
| auto lightConst()() const @property
| {
2| auto field = .lightConst(_field);
2| return CycleField!(typeof(field))(_length, field);
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| auto field = .lightImmutable(_field);
| return CycleField!(typeof(field))(_length, field);
| }
|
| ///
| auto ref opIndex()(size_t index)
| {
14| return _field[index % _length];
| }
|
| ///
| static if (!__traits(compiles, &opIndex(size_t.init)))
| {
| auto ref opIndexAssign(T)(auto ref T value, size_t index)
| {
| return _field[index % _length] = value;
| }
| }
|
| static if (hasZeroShiftFieldMember!Field)
| /// Defined if `Field` has member `assumeFieldsHaveZeroShift`.
| auto assumeFieldsHaveZeroShift() @property
| {
| return CycleField!(ZeroShiftField!Field)(_length, _field.assumeFieldsHaveZeroShift);
| }
|}
|
|///
|struct CycleField(Field, size_t length)
|{
| import mir.ndslice.slice: Slice;
|
|@optmath:
| /// Cycle length
| enum _length = length;
| ///
| Field _field;
|
| ///
| auto lightConst()() const @property
| {
2| auto field = .lightConst(_field);
2| return CycleField!(typeof(field), _length)(field);
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| auto field = .lightImmutable(_field);
| return CycleField!(typeof(field), _length)(field);
| }
|
| ///
| auto ref opIndex()(size_t index)
| {
14| return _field[index % _length];
| }
|
| ///
| static if (!__traits(compiles, &opIndex(size_t.init)))
| {
| auto ref opIndexAssign(T)(auto ref T value, size_t index)
| {
| return _field[index % _length] = value;
| }
| }
|
| static if (hasZeroShiftFieldMember!Field)
| /// Defined if `Field` has member `assumeFieldsHaveZeroShift`.
| auto assumeFieldsHaveZeroShift() @property
| {
| return CycleField!(ZeroShiftField!Field, _length)(_field.assumeFieldsHaveZeroShift);
| }
|}
|
|/++
|`ndIotaField` is used by $(SUBREF topology, ndiota).
|+/
|struct ndIotaField(size_t N)
| if (N)
|{
|@optmath:
| ///
| size_t[N - 1] _lengths;
|
| ///
| auto lightConst()() const @property
| {
59| return ndIotaField!N(_lengths);
| }
|
| ///
| auto lightImmutable()() const @property
| {
| return ndIotaField!N(_lengths);
| }
|
| ///
| size_t[N] opIndex()(size_t index) const
| {
777| size_t[N] indices;
| foreach_reverse (i; Iota!(N - 1))
| {
1151| indices[i + 1] = index % _lengths[i];
1151| index /= _lengths[i];
| }
777| indices[0] = index;
777| return indices;
| }
|}
|
|/++
|`LinspaceField` is used by $(SUBREF topology, linspace).
|+/
|struct LinspaceField(T)
|{
| ///
| size_t _length;
|
| ///
| T _start = cast(T) 0, _stop = cast(T) 0;
|
| ///
| auto lightConst()() scope const @property
| {
29| return LinspaceField!T(_length, _start, _stop);
| }
|
| ///
| auto lightImmutable()() scope const @property
| {
| return LinspaceField!T(_length, _start, _stop);
| }
|
| // no fastmath
| ///
| T opIndex()(sizediff_t index) scope const
| {
1148| sizediff_t d = _length - 1;
1148| auto v = typeof(T.init.re)(d - index);
1148| auto w = typeof(T.init.re)(index);
1148| v /= d;
1148| w /= d;
1148| auto a = v * _start;
1148| auto b = w * _stop;
1148| return a + b;
| }
|
|@optmath:
|
| ///
| size_t length(size_t dimension = 0)() scope const @property
| if (dimension == 0)
| {
26| return _length;
| }
|
| ///
| size_t[1] shape()() scope const @property @nogc
| {
8| return [_length];
| }
|}
|
|/++
|Magic square field.
|+/
|struct MagicField
|{
|@optmath:
|@safe pure nothrow @nogc:
|
| /++
| Magic Square size.
| +/
| size_t _n;
|
|scope const:
|
| ///
| MagicField lightConst()() @property
| {
45| return this;
| }
|
| ///
| MagicField lightImmutable()() @property
| {
| return this;
| }
|
| ///
| size_t length(size_t dimension = 0)() @property
| if(dimension <= 2)
| {
25| return _n * _n;
| }
|
| ///
| size_t[1] shape() @property
| {
0000000| return [_n * _n];
| }
|
| ///
| size_t opIndex(size_t index)
| {
| pragma(inline, false);
9224| auto d = index / _n;
9224| auto m = index % _n;
9224| if (_n & 1)
| {
| //d = _n - 1 - d; // MATLAB synchronization
| //index = d * _n + m; // ditto
4922| auto r = (index + 1 - d + (_n - 3) / 2) % _n;
4922| auto c = (_n * _n - index + 2 * d) % _n;
4922| return r * _n + c + 1;
| }
| else
4302| if ((_n & 2) == 0)
| {
1880| auto a = (d + 1) & 2;
1880| auto b = (m + 1) & 2;
3760| return a != b ? index + 1: _n * _n - index;
| }
| else
| {
2422| auto n = _n / 2 ;
2422| size_t shift;
2422| ptrdiff_t q;
2422| ptrdiff_t p = m - n;
2422| if (p >= 0)
| {
1211| m = p;
1211| shift = n * n;
1211| auto mul = m <= n / 2 + 1;
1211| q = d - n;
1211| if (q >= 0)
| {
605| d = q;
605| mul = !mul;
| }
1211| if (mul)
| {
606| shift *= 2;
| }
| }
| else
| {
1211| auto mul = m < n / 2;
1211| q = d - n;
1211| if (q >= 0)
| {
605| d = q;
605| mul = !mul;
| }
1492| if (d == n / 2 && (m == 0 || m == n / 2))
| {
51| mul = !mul;
| }
1211| if (mul)
| {
606| shift = n * n * 3;
| }
| }
2422| index = d * n + m;
2422| auto r = (index + 1 - d + (n - 3) / 2) % n;
2422| auto c = (n * n - index + 2 * d) % n;
2422| return r * n + c + 1 + shift;
| }
| }
|}
|
|/++
|`SparseField` is used to represent Sparse ndarrays in mutable DOK format.
|+/
|struct SparseField(T)
|{
| ///
| T[size_t] _table;
|
| ///
| auto lightConst()() const @trusted
| {
| return SparseField!(const T)(cast(const(T)[size_t])_table);
| }
|
| ///
| auto lightImmutable()() immutable @trusted
| {
| return SparseField!(immutable T)(cast(immutable(T)[size_t])_table);
| }
|
| ///
| T opIndex()(size_t index)
| {
| import std.traits: isScalarType;
| static if (isScalarType!T)
| return _table.get(index, cast(T)0);
| else
| return _table.get(index, null);
| }
|
| ///
| T opIndexAssign()(T value, size_t index)
| {
| import std.traits: isScalarType;
| static if (isScalarType!T)
| {
| if (value != 0)
| _table[index] = value;
| else
| _table.remove(index);
| }
| else
| {
| if (value !is null)
| _table[index] = value;
| else
| _table.remove(index);
| }
| return value;
| }
|
| ///
| T opIndexUnary(string op)(size_t index)
| if (op == `++` || op == `--`)
| {
| import std.traits: isScalarType;
| mixin (`auto value = ` ~ op ~ `_table[index];`);
| static if (isScalarType!T)
| {
| if (value == 0)
| _table.remove(index);
| }
| else
| {
| if (value is null)
| _table.remove(index);
| }
| return value;
| }
|
| ///
| T opIndexOpAssign(string op)(T value, size_t index)
| if (op == `+` || op == `-`)
| {
| import std.traits: isScalarType;
| mixin (`value = _table[index] ` ~ op ~ `= value;`); // this works
| static if (isScalarType!T)
| {
| if (value == 0)
| _table.remove(index);
| }
| else
| {
| if (value is null)
| _table.remove(index);
| }
| return value;
| }
|}
source/mir/ndslice/field.d is 92% covered
<<<<<< EOF
# path=./source-mir-ndslice-connect-cpython.lst
|/++
|Utilities for $(LINK2 https://docs.python.org/3/c-api/buffer.html, Python Buffer Protocol).
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
|STD = $(TD $(SMALL $0))
|PGB = $(LINK2 https://docs.python.org/3/c-api/buffer.html#c.PyObject_GetBuffer, PyObject_GetBuffer())
|+/
|module mir.ndslice.connect.cpython;
|
|import mir.ndslice.slice;
|import core.stdc.config;
|import std.traits;
|
|/++
|Construct flags for $(PGB).
|If `T` is not `const` or `immutable` then the flags require writable buffer.
|If slice kind is $(SUBREF slice, Contiguous) then the flags require $(LINK2 https://docs.python.org/3/c-api/buffer.html#contiguity-requests, c_contiguous) buffer.
|
|Params:
| kind = slice kind
| T = record type
|Returns:
| flags for $(LREF Py_buffer) request.
|+/
|enum int pythonBufferFlags(SliceKind kind, T) = (kind == Contiguous ? PyBuf_c_contiguous : PyBuf_strides) | (is(T == const) || is (T == immutable) ? PyBuf_records_ro : PyBuf_records);
|
|/++
|Fills the slice (structure) from the python `view`.
|The view should be created by $(PGB) that was called with $(LREF pythonBufferFlags).
|
|Params:
| slice = output ndslice
| view = $(LREF Py_buffer) requested
|Returns:
| one of the `input_buffer_*` $(LREF PythonBufferErrorCode) on failure and `success` otherwise.
|+/
|PythonBufferErrorCode fromPythonBuffer(T, size_t N, SliceKind kind)(ref Slice!(T*, N, kind) slice, ref const Py_buffer view) nothrow @nogc @trusted
| if (N <= PyBuf_max_ndim)
|{
| import core.stdc.string: strcmp;
| import mir.internal.utility: Iota;
|
| static if (!(is(T == const) || is(T == immutable)))
| assert(!view.readonly);
|
| enum N = slice.N;
| enum S = slice.S;
|
0000000| if (N != view.ndim)
0000000| return typeof(return).input_buffer_ndim_mismatch;
0000000| if (T.sizeof != view.itemsize)
0000000| return typeof(return).input_buffer_itemsize_mismatch;
0000000| if (pythonBufferFormat!(Unqual!T).ptr.strcmp(view.format))
0000000| return typeof(return).input_buffer_format_mismatch;
0000000| if (kind == Canonical && view.strides[N - 1] != T.sizeof)
0000000| return typeof(return).input_buffer_strides_mismatch;
|
| foreach(i; Iota!N)
0000000| slice._lengths[i] = view.shape[i];
| foreach(i; Iota!S)
| {
| assert(view.strides[i] % T.sizeof == 0);
| slice._strides[i] = view.strides[i] / T.sizeof;
| }
0000000| slice._iterator = cast(T*) view.buf;
|
0000000| return typeof(return).success;
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.slice: Slice;
| auto bar(ref const Py_buffer view)
| {
0000000| Slice!(const(double)*, 2) mat;
0000000| if (auto error = mat.fromPythonBuffer(view))
| {
| // has null pointer
| }
0000000| return mat;
| }
|}
|
|/++
|Fills the python view (structure) from the slice.
|Params:
| slice = input ndslice
| view = output $(LREF Py_buffer).
| $(LREF Py_buffer.internal) is initialized with null value,
| $(LREF Py_buffer.obj) is not initialized.
| Other $(LREF Py_buffer) fields are initialized according to the flags and slice.
| flags = requester flags
| structureBuffer = Single chunk of memory with the same alignment and size as $(SUBREF _slice, Structure).
| The buffer is used to store shape and strides for the view.
|Returns:
| one of the `cannot_create_*` $(LREF PythonBufferErrorCode) on failure and `success` otherwise.
|+/
|PythonBufferErrorCode toPythonBuffer(T, size_t N, SliceKind kind)(Slice!(T*, N, kind) slice, ref Py_buffer view, int flags, ref Structure!N structureBuffer) nothrow @nogc @trusted
| if (N <= PyBuf_max_ndim)
|{
0000000| structureBuffer.lengths = slice._lengths;
0000000| structureBuffer.strides = slice.strides;
|
0000000| foreach(ref stride; structureBuffer.strides)
0000000| stride *= T.sizeof;
|
| /////////////////////
| /// always filled ///
| /////////////////////
0000000| view.buf = slice._iterator;
| // skip view.obj
0000000| view.len = slice.elementCount * T.sizeof;
0000000| view.itemsize = T.sizeof;
0000000| view.ndim = N;
0000000| view.internal = null;
|
| static if (kind != Contiguous)
| {
0000000| bool check_single_memory_block;
| }
|
| /// shape ///
0000000| if ((flags & PyBuf_nd) == PyBuf_nd)
| {
0000000| view.shape = cast(sizediff_t*) structureBuffer.lengths.ptr;
| /// strides ///
0000000| if ((flags & PyBuf_strides) == PyBuf_strides)
0000000| view.strides = cast(sizediff_t*) structureBuffer.strides.ptr;
| else
| {
0000000| view.strides = null;
| static if (kind != Contiguous)
0000000| check_single_memory_block = true;
| }
| }
| else
| {
0000000| view.shape = null;
0000000| view.strides = null;
| static if (kind != Contiguous)
0000000| check_single_memory_block = true;
| }
0000000| view.suboffsets = null;
|
| /// ! structure verification ! ///
| static if (kind == Contiguous)
| {
| static if (N != 1)
| {
0000000| if ((flags & PyBuf_f_contiguous) == PyBuf_f_contiguous)
| {
| import mir.ndslice.dynamic: everted;
| import mir.ndslice.topology: iota;
0000000| if (slice.everted.shape.iota.everted.strides != slice.strides)
0000000| return typeof(return).cannot_create_f_contiguous_buffer;
| }
| }
| }
| else
| {
| import mir.ndslice.dynamic: everted, normalizeStructure;
| import mir.ndslice.topology: iota;
0000000| if ((flags & PyBuf_c_contiguous) == PyBuf_c_contiguous)
| {
0000000| if (slice.shape.iota.strides != slice.strides && slice.everted.shape.iota.everted.strides != slice.strides)
0000000| return typeof(return).cannot_create_c_contiguous_buffer;
| }
| else
0000000| if ((flags & PyBuf_any_contiguous) == PyBuf_any_contiguous)
| {
0000000| if (slice.shape.iota.strides != slice.strides && slice.everted.shape.iota.everted.strides != slice.strides)
0000000| return typeof(return).cannot_create_any_contiguous_buffer;
| }
| else
0000000| if (check_single_memory_block)
| {
0000000| if (!slice.normalizeStructure)
0000000| return typeof(return).cannot_create_a_buffer_without_strides;
| }
| }
|
| /// readonly ///
| static if (is(T == const) || is(T == immutable))
| {
| if (flags & PyBuf_writable)
| return typeof(return).cannot_create_writable_buffer;
| view.readonly = 1;
| }
| else
0000000| view.readonly = 0;
|
| /// format ///
0000000| if (flags & PyBuf_format)
| {
| enum fmt = pythonBufferFormat!(Unqual!T);
| static if (fmt is null)
| return typeof(return).cannot_create_format_string;
| else
0000000| view.format = cast(char*)fmt.ptr;
| }
| else
0000000| view.format = null;
|
0000000| return typeof(return).success;
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.slice : Slice, Structure, Universal, Contiguous, SliceKind;
| Py_buffer bar(SliceKind kind)(Slice!(double*, 2, kind) slice)
| {
| import core.stdc.stdlib;
| enum N = 2;
|
0000000| auto structurePtr = cast(Structure!N*) Structure!N.sizeof.malloc;
0000000| if (!structurePtr)
| assert(0);
0000000| Py_buffer view;
|
0000000| if (auto error = slice.toPythonBuffer(view, PyBuf_records_ro, *structurePtr))
| {
0000000| view = view.init; // null buffer
0000000| structurePtr.free;
| }
| else
| {
0000000| assert(cast(sizediff_t*)&structurePtr.lengths == view.shape);
0000000| assert(cast(sizediff_t*)&structurePtr.strides == view.strides);
| }
|
0000000| return view;
| }
|
| alias barUni = bar!Universal;
| alias barCon = bar!Contiguous;
|}
|
|/// Python $(LINK2 https://docs.python.org/3/c-api/buffer.html#buffer-structure, Buffer structure).
|extern(C)
|struct bufferinfo
|{
| ///
| void *buf;
| ///
| void *obj;
| ///
| sizediff_t len;
| ///
| sizediff_t itemsize;
| ///
| int readonly;
| ///
| int ndim;
| ///
| char *format;
| ///
| sizediff_t *shape;
| ///
| sizediff_t *strides;
| ///
| sizediff_t *suboffsets;
| ///
| void *internal;
|}
|/// ditto
|alias Py_buffer = bufferinfo;
|
|/++
|Error codes for ndslice - Py_buffer conversion.
|+/
|enum PythonBufferErrorCode
|{
| ///
| success,
| ///
| cannot_create_format_string,
| ///
| cannot_create_writable_buffer,
| ///
| cannot_create_f_contiguous_buffer,
| ///
| cannot_create_c_contiguous_buffer,
| ///
| cannot_create_any_contiguous_buffer,
| ///
| cannot_create_a_buffer_without_strides,
| ///
| input_buffer_ndim_mismatch,
| ///
| input_buffer_itemsize_mismatch,
| ///
| input_buffer_format_mismatch,
| ///
| input_buffer_strides_mismatch,
|}
|
|///
|enum PyBuf_max_ndim = 64;
|
|///
|enum PyBuf_simple = 0;
|///
|enum PyBuf_writable = 0x0001;
|///
|enum PyBuf_writeable = PyBuf_writable;
|///
|enum PyBuf_format = 0x0004;
|///
|enum PyBuf_nd = 0x0008;
|///
|enum PyBuf_strides = (0x0010 | PyBuf_nd);
|///
|enum PyBuf_c_contiguous = (0x0020 | PyBuf_strides);
|///
|enum PyBuf_f_contiguous = (0x0040 | PyBuf_strides);
|///
|enum PyBuf_any_contiguous = (0x0080 | PyBuf_strides);
|///
|enum PyBuf_indirect = (0x0100 | PyBuf_strides);
|
|///
|enum PyBuf_contig = (PyBuf_nd | PyBuf_writable);
|///
|enum PyBuf_contig_ro = (PyBuf_nd);
|
|///
|enum PyBuf_strided = (PyBuf_strides | PyBuf_writable);
|///
|enum PyBuf_strided_ro = (PyBuf_strides);
|
|///
|enum PyBuf_records = (PyBuf_strides | PyBuf_writable | PyBuf_format);
|///
|enum PyBuf_records_ro = (PyBuf_strides | PyBuf_format);
|
|/++
|Returns $(HTTPS docs.python.org/3/c-api/buffer.html#c.Py_buffer.format, python format (type)) string.
|For example, `"O"` for `PyObject` and "B" for ubyte.
|+/
|template pythonBufferFormat(T)
|{
| static if (is(T == struct) && __traits(identifier, A) == "PyObject")
| enum pythonBufferFormat = "O";
| else
| static if (is(Unqual!T == short))
| enum pythonBufferFormat = "h";
| else
| static if (is(Unqual!T == ushort))
| enum pythonBufferFormat = "H";
| else
| static if (is(Unqual!T == int))
| enum pythonBufferFormat = "i";
| else
| static if (is(Unqual!T == uint))
| enum pythonBufferFormat = "I";
| else
| static if (is(Unqual!T == float))
| enum pythonBufferFormat = "f";
| else
| static if (is(Unqual!T == double))
| enum pythonBufferFormat = "d";
| else
| static if (is(Unqual!T == long))
| enum pythonBufferFormat = "q";
| else
| static if (is(Unqual!T == ulong))
| enum pythonBufferFormat = "Q";
| else
| static if (is(Unqual!T == ubyte))
| enum pythonBufferFormat = "B";
| else
| static if (is(Unqual!T == byte))
| enum pythonBufferFormat = "b";
| else
| static if (is(Unqual!T == char))
| enum pythonBufferFormat = "c";
| else
| static if (is(Unqual!T == char*))
| enum pythonBufferFormat = "z";
| else
| static if (is(Unqual!T == void*))
| enum pythonBufferFormat = "P";
| else
| static if (is(Unqual!T == bool))
| enum pythonBufferFormat = "?";
| else
| static if (is(Unqual!T == wchar*))
| enum pythonBufferFormat = "Z";
| else
| static if (is(Unqual!T == wchar))
| enum pythonBufferFormat = "u";
| else
| {
| static if (is(cpp_long))
| {
| static if (is(Unqual!T == cpp_long))
| enum pythonBufferFormat = "l";
| else
| enum pythonBufferFormat = null;
| }
| else
| static if (is(cpp_ulong))
| {
| static if (is(Unqual!T == cpp_ulong))
| enum pythonBufferFormat = "L";
| else
| enum pythonBufferFormat = null;
| }
| else
| static if (is(c_long_double))
| {
| static if (is(Unqual!T == c_long_double))
| enum pythonBufferFormat = "g";
| else
| enum pythonBufferFormat = null;
| }
| else
| enum pythonBufferFormat = null;
| }
|}
source/mir/ndslice/connect/cpython.d is 0% covered
<<<<<< EOF
# path=./source-mir-rc-ptr.lst
|/++
|$(H1 Thread-safe reference-counted shared pointers).
|
|This implementation supports class and struct (`alias this`) polymorphism.
|+/
|module mir.rc.ptr;
|
|import mir.rc.context;
|import mir.type_info;
|import std.traits;
|
|package static immutable allocationExcMsg = "mir_rcptr: out of memory error.";
|package static immutable getExcMsg = "mir_rcptr: trying to use null value.";
|
|version (D_Exceptions)
|{
| import core.exception: OutOfMemoryError, InvalidMemoryOperationError;
| package static immutable allocationError = new OutOfMemoryError(allocationExcMsg);
|}
|
|/++
|Thread safe reference counting array.
|
|This implementation supports class and struct (`alias this`) polymorphism.
|
|`__xdtor` if any is used to destruct objects.
|
|The implementation never adds roots into the GC.
|+/
|struct mir_rcptr(T)
|{
| static if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union))
| static assert(!__traits(isNested, T), "mir_rcptr does not support nested types.");
|
| ///
| static if (is(T == class) || is(T == interface))
| package Unqual!T _value;
| else
| package T* _value;
| package mir_rc_context* _context;
|
| package ref mir_rc_context context() inout scope return @trusted @property
| {
32| return *cast(mir_rc_context*)_context;
| }
|
| package void _reset()
| {
15| _value = null;
15| _context = null;
| }
|
| inout(void)* _thisPtr() inout scope return @trusted @property
| {
70| return cast(inout(void)*) _value;
| }
|
| package alias ThisTemplate = .mir_rcptr;
|
| /// ditto
| alias opUnary(string op : "*") = _get_value;
| /// ditto
| alias _get_value this;
|
| static if (is(T == class) || is(T == interface))
| ///
| pragma(inline, true)
| inout(T) _get_value() scope inout @property
| {
2| assert(this, getExcMsg);
2| return _value;
| }
| else
| ///
| pragma(inline, true)
| ref inout(T) _get_value() scope inout @property
| {
3| assert(this, getExcMsg);
3| return *_value;
| }
|
| ///
| void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe
| {
0000000| auto t0 = this._value;
0000000| auto t1 = this._context;
0000000| this._value = rhs._value;
0000000| this._context = rhs._context;
0000000| rhs._value = t0;
0000000| rhs._context = t1;
| }
|
| ///
0000000| this(typeof(null))
| {
| }
|
| ///
| mixin CommonRCImpl;
|
|
| ///
| pragma(inline, true)
| bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc
| {
0000000| return !this;
| }
|
| /// ditto
| bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
| {
0000000| return _thisPtr == rhs._thisPtr;
| }
|
| ///
| sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
| {
0000000| return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
| }
|
| ///
| size_t toHash() @trusted scope const pure nothrow @nogc
| {
0000000| return cast(size_t) _thisPtr;
| }
|
| ///
| ~this() nothrow
| {
| static if (hasElaborateDestructor!T || hasDestructor!T)
| {
1| if (false) // break @safe and pure attributes
| {
0000000| Unqual!T* object;
0000000| (*object).__xdtor;
| }
| }
27| if (this)
| {
30| (() @trusted { mir_rc_decrease_counter(context); })();
15| debug _reset;
| }
| }
|
| static if (is(T == const) || is(T == immutable))
| this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc
| {
| if (rhs)
| {
| this._value = cast(typeof(this._value))rhs._value;
| this._context = cast(typeof(this._context))rhs._context;
| mir_rc_increase_counter(context);
| }
| }
|
| static if (is(T == immutable))
| this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc
| {
| if (rhs)
| {
| this._value = cast(typeof(this._value))rhs._value;
| this._context = cast(typeof(this._context))rhs._context;
| mir_rc_increase_counter(context);
| }
| }
|
| static if (is(T == immutable))
| this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc
| {
| if (rhs)
| {
| this._value = cast(typeof(this._value))rhs._value;
| this._context = cast(typeof(this._context))rhs._context;
| mir_rc_increase_counter(context);
| }
| }
|
7| this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
| {
7| if (rhs)
| {
7| this._value = rhs._value;
7| this._context = rhs._context;
7| mir_rc_increase_counter(context);
| }
| }
|
| ///
| ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc
| {
0000000| this = typeof(this).init;
| }
|
| ///
| ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc
| {
0000000| this.proxySwap(rhs);
0000000| return this;
| }
|
| ///
| ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc
| if (isImplicitlyConvertible!(Q*, T*))
| {
| this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
| return this;
| }
|}
|
|///
|alias RCPtr = mir_rcptr;
|
|/++
|Returns: shared pointer of the member and the context from the current pointer.
|+/
|auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref Args args)
|{
| import core.lifetime: move;
| void foo(A)(auto ref A) {}
3| assert(context != null);
| static if (args.length)
| {
| // breaks safaty
| if (false) foo(__traits(getMember, context._get_value, member)(forward!args));
| return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member)(forward!args), context.move))();
| }
| else
| {
| // breaks safaty
3| if (false) foo(__traits(getMember, context._get_value, member));
6| return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member), context.move))();
| }
|}
|
|/++
|Returns: shared pointer constructed with current context.
|+/
|@system .mir_rcptr!R createRCWithContext(R, F)(return R value, return const mir_rcptr!F context)
| if (is(R == class) || is(R == interface))
|{
2| typeof(return) ret;
2| ret._value = cast()value;
2| ret._context = cast(mir_rc_context*)context._context;
2| (*cast(mir_rcptr!F*)&context)._value = null;
2| (*cast(mir_rcptr!F*)&context)._context = null;
2| return ret;
|}
|
|///ditto
|@system .mir_rcptr!R createRCWithContext(R, F)(return ref R value, return const mir_rcptr!F context)
| if (!is(R == class) && !is(R == interface))
|{
4| typeof(return) ret;
4| ret._value = &value;
4| ret._context = cast(mir_rc_context*)context._context;
4| (*cast(mir_rcptr!F*)&context)._value = null;
4| (*cast(mir_rcptr!F*)&context)._context = null;
4| return ret;
|}
|
|/++
|Construct a shared pointer of a required type with a current context.
|Provides polymorphism abilities for classes and structures with `alias this` syntax.
|+/
|mir_rcptr!R castTo(R, T)(return mir_rcptr!T context) @trusted
| if (isImplicitlyConvertible!(T, R))
|{
| import core.lifetime: move;
3| return createRCWithContext(cast(R)context._get_value, move(context));
|}
|
|/// ditto
|mir_rcptr!(const R) castTo(R, T)(return const mir_rcptr!T context) @trusted
| if (isImplicitlyConvertible!(const T, const R))
|{
| import core.lifetime: move;
| return createRCWithContext(cast(const R)context._get_value, move(*cast(mir_rcptr!T*)&context));
|}
|
|/// ditto
|mir_rcptr!(immutable R) castTo(R, T)(return immutable mir_rcptr!T context) @trusted
| if (isImplicitlyConvertible!(immutable T, immutable R))
|{
| import core.lifetime: move;
| return createRCWithContext(cast(immutable R)context._get_value, move(*cast(mir_rcptr!T*)&context));
|}
|
|///
|template createRC(T)
| if (!is(T == interface) && !__traits(isAbstractClass, T))
|{
| ///
| mir_rcptr!T createRC(Args...)(auto ref Args args)
| {
5| typeof(return) ret;
10| with (ret) () @trusted {
5| _context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T);
5| if (!_context)
| {
| version(D_Exceptions)
0000000| throw allocationError;
| else
| assert(0, allocationExcMsg);
| }
5| _value = cast(typeof(_value))(_context + 1);
| } ();
| import core.lifetime: forward;
| import mir.conv: emplace;
5| cast(void) emplace!T(ret._value, forward!args);
5| return ret;
| }
|}
|
|///
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
2| auto a = createRC!double(10);
2| auto b = a;
1| assert(*b == 10);
1| *b = 100;
1| assert(*a == 100);
|}
|
|/// Classes with empty constructor
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| static class C
| {
| int index = 34;
| }
1| assert(createRC!C.index == 34);
|}
|
|///
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| static interface I { ref double bar() @safe pure nothrow @nogc; }
| static abstract class D { int index; }
| static class C : D, I
| {
| double value;
2| ref double bar() @safe pure nothrow @nogc { return value; }
2| this(double d) { value = d; }
| }
2| auto a = createRC!C(10);
1| assert(a._counter == 1);
2| auto b = a;
1| assert(a._counter == 2);
1| assert((*b).value == 10);
1| b.value = 100; // access via alias this syntax
1| assert(a.value == 100);
1| assert(a._counter == 2);
|
2| auto d = a.castTo!D; //RCPtr!D
1| assert(d._counter == 3);
1| d.index = 234;
1| assert(a.index == 234);
2| auto i = a.castTo!I; //RCPtr!I
1| assert(i.bar == 100);
1| assert(i._counter == 4);
|
2| auto v = a.shareMember!"value"; //RCPtr!double
2| auto w = a.shareMember!"bar"; //RCPtr!double
1| assert(i._counter == 6);
1| assert(*v == 100);
2| ()@trusted{assert(&*w is &*v);}();
|}
|
|/// 'Alias This' support
|version(mir_test)
|@safe pure @nogc nothrow
|unittest
|{
| struct S
| {
| double e;
| }
| struct C
| {
| int i;
| S s;
| // 'alias' should be accesable by reference
| // or a class/interface
| alias s this;
| }
|
2| auto a = createRC!C(10, S(3));
2| auto s = a.castTo!S; // RCPtr!S
1| assert(s._counter == 2);
1| assert(s.e == 3);
|}
|
|version(unittest):
|
|package struct _test_unpure_system_dest_s__ {
| static int numStructs;
| int i;
|
2| this(this This)(int i) {
2| this.i = i;
2| ++numStructs;
| }
|
| ~this() @system nothrow {
2| auto d = new int[2];
2| --numStructs;
| }
|}
|
|version(mir_test)
|@system nothrow
|unittest
|{
| import mir.rc.array;
2| auto ptr = createRC!_test_unpure_system_dest_s__(42);
2| auto arr = rcarray!_test_unpure_system_dest_s__(3);
|}
source/mir/rc/ptr.d is 82% covered
<<<<<< EOF
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-math-common.lst
|/++
|Common floating point math functions.
|
|This module has generic LLVM-oriented API compatible with all D compilers.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilya Yaroshenko, Phobos Team
|+/
|module mir.math.common;
|
|import mir.internal.utility: isComplex, isComplexOf, isFloatingPoint;
|
|version(LDC)
|{
| static import ldc.attributes;
|
| private alias AliasSeq(T...) = T;
|
| /++
| Functions attribute, an alias for `AliasSeq!(llvmFastMathFlag("contract"));`.
|
| $(UL
| $(LI 1. Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). )
| )
|
| Note: Can be used with all compilers.
| +/
| alias fmamath = AliasSeq!(ldc.attributes.llvmFastMathFlag("contract"));
|
| /++
| Functions attribute, an alias for `AliasSeq!(llvmFastMathFlag("fast"))`.
|
| It is similar to $(LREF fastmath), but does not allow unsafe-fp-math.
| This flag does NOT force LDC to use the reciprocal of an argument rather than perform division.
|
| This flag is default for string lambdas.
|
| Note: Can be used with all compilers.
| +/
| alias optmath = AliasSeq!(ldc.attributes.llvmFastMathFlag("fast"));
|
| /++
| Functions attribute, an alias for `ldc.attributes.fastmath` .
|
| $(UL
|
| $(LI 1. Enable optimizations that make unsafe assumptions about IEEE math (e.g. that addition is associative) or may not work for all input ranges.
| These optimizations allow the code generator to make use of some instructions which would otherwise not be usable (such as fsin on X86). )
|
| $(LI 2. Allow optimizations to assume the arguments and result are not NaN.
| Such optimizations are required to retain defined behavior over NaNs,
| but the value of the result is undefined. )
|
| $(LI 3. Allow optimizations to assume the arguments and result are not +$(BACKTICK)-inf.
| Such optimizations are required to retain defined behavior over +$(BACKTICK)-Inf,
| but the value of the result is undefined. )
|
| $(LI 4. Allow optimizations to treat the sign of a zero argument or result as insignificant. )
|
| $(LI 5. Allow optimizations to use the reciprocal of an argument rather than perform division. )
|
| $(LI 6. Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). )
|
| $(LI 7. Allow algebraically equivalent transformations that may dramatically change results in floating point (e.g. reassociate). )
| )
|
| Note: Can be used with all compilers.
| +/
| alias fastmath = ldc.attributes.fastmath;
|}
|else
|enum
|{
| /++
| Functions attribute, an alias for `AliasSeq!(llvmFastMathFlag("contract"));`.
|
| $(UL
| $(LI Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). )
| )
|
| Note: Can be used with all compilers.
| +/
| fmamath,
|
| /++
| Functions attribute, an alias for `AliasSeq!(llvmAttr("unsafe-fp-math", "false"), llvmFastMathFlag("fast"))`.
|
| It is similar to $(LREF fastmath), but does not allow unsafe-fp-math.
| This flag does NOT force LDC to use the reciprocal of an argument rather than perform division.
|
| This flag is default for string lambdas.
|
| Note: Can be used with all compilers.
| +/
| optmath,
|
| /++
| Functions attribute, an alias for `ldc.attributes.fastmath = AliasSeq!(llvmAttr("unsafe-fp-math", "true"), llvmFastMathFlag("fast"))` .
|
| $(UL
|
| $(LI Enable optimizations that make unsafe assumptions about IEEE math (e.g. that addition is associative) or may not work for all input ranges.
| These optimizations allow the code generator to make use of some instructions which would otherwise not be usable (such as fsin on X86). )
|
| $(LI Allow optimizations to assume the arguments and result are not NaN.
| Such optimizations are required to retain defined behavior over NaNs,
| but the value of the result is undefined. )
|
| $(LI Allow optimizations to assume the arguments and result are not +$(BACKTICK)-inf.
| Such optimizations are required to retain defined behavior over +$(BACKTICK)-Inf,
| but the value of the result is undefined. )
|
| $(LI Allow optimizations to treat the sign of a zero argument or result as insignificant. )
|
| $(LI Allow optimizations to use the reciprocal of an argument rather than perform division. )
|
| $(LI Allow floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-and-add). )
|
| $(LI Allow algebraically equivalent transformations that may dramatically change results in floating point (e.g. reassociate). )
| )
|
| Note: Can be used with all compilers.
| +/
| fastmath
|}
|
|version(LDC)
|{
| nothrow @nogc pure @safe:
|
| pragma(LDC_intrinsic, "llvm.sqrt.f#")
| ///
| T sqrt(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.sin.f#")
| ///
| T sin(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.cos.f#")
| ///
| T cos(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.powi.f#")
| ///
| T powi(T)(in T val, int power) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.pow.f#")
| ///
| T pow(T)(in T val, in T power) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.exp.f#")
| ///
| T exp(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.log.f#")
| ///
| T log(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.fma.f#")
| ///
| T fma(T)(T vala, T valb, T valc) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.fabs.f#")
| ///
| T fabs(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.floor.f#")
| ///
| T floor(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.exp2.f#")
| ///
| T exp2(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.log10.f#")
| ///
| T log10(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.log2.f#")
| ///
| T log2(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.ceil.f#")
| ///
| T ceil(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.trunc.f#")
| ///
| T trunc(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.rint.f#")
| ///
| T rint(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.nearbyint.f#")
| ///
| T nearbyint(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.copysign.f#")
| ///
| T copysign(T)(in T mag, in T sgn) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.round.f#")
| ///
| T round(T)(in T val) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.fmuladd.f#")
| ///
| T fmuladd(T)(in T vala, in T valb, in T valc) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.minnum.f#")
| ///
| T fmin(T)(in T vala, in T valb) if (isFloatingPoint!T);
|
| pragma(LDC_intrinsic, "llvm.maxnum.f#")
| ///
| T fmax(T)(in T vala, in T valb) if (isFloatingPoint!T);
|}
|else version(GNU)
|{
| static import gcc.builtins;
|
| // Calls GCC builtin for either float (suffix "f"), double (no suffix), or real (suffix "l").
| private enum mixinGCCBuiltin(string fun) =
| `static if (T.mant_dig == float.mant_dig) return gcc.builtins.__builtin_`~fun~`f(x);`~
| ` else static if (T.mant_dig == double.mant_dig) return gcc.builtins.__builtin_`~fun~`(x);`~
| ` else static if (T.mant_dig == real.mant_dig) return gcc.builtins.__builtin_`~fun~`l(x);`~
| ` else static assert(0);`;
|
| // As above but for two-argument function.
| private enum mixinGCCBuiltin2(string fun) =
| `static if (T.mant_dig == float.mant_dig) return gcc.builtins.__builtin_`~fun~`f(x, y);`~
| ` else static if (T.mant_dig == double.mant_dig) return gcc.builtins.__builtin_`~fun~`(x, y);`~
| ` else static if (T.mant_dig == real.mant_dig) return gcc.builtins.__builtin_`~fun~`l(x, y);`~
| ` else static assert(0);`;
|
| ///
| T sqrt(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`sqrt`); }
| ///
| T sin(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`sin`); }
| ///
| T cos(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`cos`); }
| ///
| T pow(T)(in T x, in T power) if (isFloatingPoint!T) { alias y = power; mixin(mixinGCCBuiltin2!`pow`); }
| ///
| T powi(T)(in T x, int power) if (isFloatingPoint!T) { alias y = power; mixin(mixinGCCBuiltin2!`powi`); }
| ///
| T exp(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`exp`); }
| ///
| T log(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`log`); }
| ///
| T fabs(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`fabs`); }
| ///
| T floor(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`floor`); }
| ///
| T exp2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`exp2`); }
| ///
| T log10(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`log10`); }
| ///
| T log2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`log2`); }
| ///
| T ceil(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`ceil`); }
| ///
| T trunc(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`trunc`); }
| ///
| T rint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`rint`); }
| ///
| T nearbyint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`nearbyint`); }
| ///
| T copysign(T)(in T mag, in T sgn) if (isFloatingPoint!T) { alias y = sgn; mixin(mixinGCCBuiltin2!`copysign`); }
| ///
| T round(T)(in T x) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin!`round`); }
| ///
| T fmuladd(T)(in T a, in T b, in T c) if (isFloatingPoint!T)
| {
| static if (T.mant_dig == float.mant_dig)
| return gcc.builtins.__builtin_fmaf(a, b, c);
| else static if (T.mant_dig == double.mant_dig)
| return gcc.builtins.__builtin_fma(a, b, c);
| else static if (T.mant_dig == real.mant_dig)
| return gcc.builtins.__builtin_fmal(a, b, c);
| else
| static assert(0);
| }
| version(mir_core_test)
| unittest { assert(fmuladd!double(2, 3, 4) == 2 * 3 + 4); }
| ///
| T fmin(T)(in T x, in T y) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin2!`fmin`); }
| ///
| T fmax(T)(in T x, in T y) if (isFloatingPoint!T) { mixin(mixinGCCBuiltin2!`fmax`); }
|}
|else static if (__VERSION__ >= 2082) // DMD 2.082 onward.
|{
| static import std.math;
| static import core.stdc.math;
|
| // Calls either std.math or cmath function for either float (suffix "f")
| // or double (no suffix). std.math will always be used during CTFE or for
| // arguments with greater than double precision or if the cmath function
| // is impure.
| private enum mixinCMath(string fun) =
| `pragma(inline, true);
| static if (!is(typeof(std.math.`~fun~`(0.5f)) == float)
| && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f))))
| if (!__ctfe)
| {
| static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x);
| else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x);
| }
| return std.math.`~fun~`(x);`;
|
| // As above but for two-argument function (both arguments must be floating point).
| private enum mixinCMath2(string fun) =
| `pragma(inline, true);
| static if (!is(typeof(std.math.`~fun~`(0.5f, 0.5f)) == float)
| && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f, 0.5f))))
| if (!__ctfe)
| {
| static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x, y);
| else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x, y);
| }
| return std.math.`~fun~`(x, y);`;
|
| // Some std.math functions have appropriate return types (float,
| // double, real) without need for a wrapper. We can alias them
| // directly but we leave the templates afterwards for documentation
| // purposes and so explicit template instantiation still works.
| // The aliases will always match before the templates.
| // Note that you cannot put any "static if" around the aliases or
| // compilation will fail due to conflict with the templates!
| alias sqrt = std.math.sqrt;
| alias sin = std.math.sin;
| alias cos = std.math.cos;
| alias exp = std.math.exp;
| //alias fabs = std.math.fabs;
| alias floor = std.math.floor;
| alias exp2 = std.math.exp2;
| alias ceil = std.math.ceil;
| alias rint = std.math.rint;
|
| ///
| T sqrt(T)(in T x) if (isFloatingPoint!T) { return std.math.sqrt(x); }
| ///
| T sin(T)(in T x) if (isFloatingPoint!T) { return std.math.sin(x); }
| ///
| T cos(T)(in T x) if (isFloatingPoint!T) { return std.math.cos(x); }
| ///
| T pow(T)(in T x, in T power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); }
| ///
| T powi(T)(in T x, int power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); }
| ///
| T exp(T)(in T x) if (isFloatingPoint!T) { return std.math.exp(x); }
| ///
| T log(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log`); }
| ///
| T fabs(T)(in T x) if (isFloatingPoint!T) { return std.math.fabs(x); }
| ///
| T floor(T)(in T x) if (isFloatingPoint!T) { return std.math.floor(x); }
| ///
| T exp2(T)(in T x) if (isFloatingPoint!T) { return std.math.exp2(x); }
| ///
| T log10(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log10`); }
| ///
| T log2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log2`); }
| ///
| T ceil(T)(in T x) if (isFloatingPoint!T) { return std.math.ceil(x); }
| ///
| T trunc(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`trunc`); }
| ///
| T rint(T)(in T x) if (isFloatingPoint!T) { return std.math.rint(x); }
| ///
| T nearbyint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`nearbyint`); }
| ///
| T copysign(T)(in T mag, in T sgn) if (isFloatingPoint!T)
| {
| alias x = mag;
| alias y = sgn;
| mixin(mixinCMath2!`copysign`);
| }
| ///
| T round(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`round`); }
| ///
| T fmuladd(T)(in T a, in T b, in T c) if (isFloatingPoint!T) { return a * b + c; }
| version(mir_core_test)
| unittest { assert(fmuladd!double(2, 3, 4) == 2 * 3 + 4); }
| ///
| T fmin(T)(in T x, in T y) if (isFloatingPoint!T)
| {
| version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798
| {
| version (CRuntime_Microsoft)
| mixin(mixinCMath2!`fmin`);
| else
| return std.math.fmin(x, y);
| }
| else
| mixin(mixinCMath2!`fmin`);
| }
| ///
| T fmax(T)(in T x, in T y) if (isFloatingPoint!T)
| {
| version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798
| {
| version (CRuntime_Microsoft)
| mixin(mixinCMath2!`fmax`);
| else
| return std.math.fmax(x, y);
| }
| else
| mixin(mixinCMath2!`fmax`);
| }
|
| version (mir_core_test) @nogc nothrow pure @safe unittest
| {
| // Check the aliases are correct.
| static assert(is(typeof(sqrt(1.0f)) == float));
| static assert(is(typeof(sin(1.0f)) == float));
| static assert(is(typeof(cos(1.0f)) == float));
| static assert(is(typeof(exp(1.0f)) == float));
| static assert(is(typeof(fabs(1.0f)) == float));
| static assert(is(typeof(floor(1.0f)) == float));
| static assert(is(typeof(exp2(1.0f)) == float));
| static assert(is(typeof(ceil(1.0f)) == float));
| static assert(is(typeof(rint(1.0f)) == float));
|
| auto x = sqrt!float(2.0f); // Explicit template instantiation still works.
| auto fp = &sqrt!float; // Can still take function address.
|
| // Test for DMD linker problem with fmin on Windows.
| static assert(is(typeof(fmin!float(1.0f, 1.0f))));
| static assert(is(typeof(fmax!float(1.0f, 1.0f))));
| }
|}
|else // DMD version prior to 2.082
|{
| static import std.math;
| static import core.stdc.math;
|
| // Calls either std.math or cmath function for either float (suffix "f")
| // or double (no suffix). std.math will always be used during CTFE or for
| // arguments with greater than double precision or if the cmath function
| // is impure.
| private enum mixinCMath(string fun) =
| `pragma(inline, true);
| static if (!is(typeof(std.math.`~fun~`(0.5f)) == float)
| && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f))))
| if (!__ctfe)
| {
| static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x);
| else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x);
| }
| return std.math.`~fun~`(x);`;
|
| // As above but for two-argument function (both arguments must be floating point).
| private enum mixinCMath2(string fun) =
| `pragma(inline, true);
| static if (!is(typeof(std.math.`~fun~`(0.5f, 0.5f)) == float)
| && is(typeof(() pure => core.stdc.math.`~fun~`f(0.5f, 0.5f))))
| if (!__ctfe)
| {
| static if (T.mant_dig == float.mant_dig) return core.stdc.math.`~fun~`f(x, y);
| else static if (T.mant_dig == double.mant_dig) return core.stdc.math.`~fun~`(x, y);
| }
| return std.math.`~fun~`(x, y);`;
|
| // Some std.math functions have appropriate return types (float,
| // double, real) without need for a wrapper.
| alias sqrt = std.math.sqrt;
|
| ///
| T sqrt(T)(in T x) if (isFloatingPoint!T) { return std.math.sqrt(x); }
| ///
| T sin(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`sin`); }
| ///
| T cos(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`cos`); }
| ///
| T pow(T)(in T x, in T power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); }
| ///
| T powi(T)(in T x, int power) if (isFloatingPoint!T) { alias y = power; mixin(mixinCMath2!`pow`); }
| ///
| T exp(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`exp`); }
| ///
| T log(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log`); }
| ///
| T fabs(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`fabs`); }
| ///
| T floor(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`floor`); }
| ///
| T exp2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`exp2`); }
| ///
| T log10(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log10`); }
| ///
| T log2(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`log2`); }
| ///
| T ceil(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`ceil`); }
| ///
| T trunc(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`trunc`); }
| ///
| T rint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`rint`); }
| ///
| T nearbyint(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`nearbyint`); }
| ///
| T copysign(T)(in T mag, in T sgn) if (isFloatingPoint!T)
| {
| alias x = mag;
| alias y = sgn;
| mixin(mixinCMath2!`copysign`);
| }
| ///
| T round(T)(in T x) if (isFloatingPoint!T) { mixin(mixinCMath!`round`); }
| ///
| T fmuladd(T)(in T a, in T b, in T c) if (isFloatingPoint!T) { return a * b + c; }
| version(mir_core_test)
| unittest { assert(fmuladd!double(2, 3, 4) == 2 * 3 + 4); }
| ///
| T fmin(T)(in T x, in T y) if (isFloatingPoint!T)
| {
| version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798
| {
| version (CRuntime_Microsoft)
| mixin(mixinCMath2!`fmin`);
| else
| return std.math.fmin(x, y);
| }
| else
| mixin(mixinCMath2!`fmin`);
| }
| ///
| T fmax(T)(in T x, in T y) if (isFloatingPoint!T)
| {
| version (Windows) // https://issues.dlang.org/show_bug.cgi?id=19798
| {
| version (CRuntime_Microsoft)
| mixin(mixinCMath2!`fmax`);
| else
| return std.math.fmax(x, y);
| }
| else
| mixin(mixinCMath2!`fmax`);
| }
|
| version (mir_core_test) @nogc nothrow pure @safe unittest
| {
| // Check the aliases are correct.
| static assert(is(typeof(sqrt(1.0f)) == float));
| auto x = sqrt!float(2.0f); // Explicit template instantiation still works.
| auto fp = &sqrt!float; // Can still take function address.
|
| // Test for DMD linker problem with fmin on Windows.
| static assert(is(typeof(fmin!float(1.0f, 1.0f))));
| static assert(is(typeof(fmax!float(1.0f, 1.0f))));
| }
|}
|
|version (mir_core_test)
|@nogc nothrow pure @safe unittest
|{
| import mir.math: PI, feqrel;
| assert(feqrel(pow(2.0L, -0.5L), cos(PI / 4)) >= real.mant_dig - 1);
|}
|
|/// Overload for cdouble, cfloat and creal
|@optmath auto fabs(T)(in T x)
| if (isComplex!T)
|{
| return x.re * x.re + x.im * x.im;
|}
|
|///
|version(mir_core_test) unittest
|{
| import mir.complex;
| assert(fabs(Complex!double(3, 4)) == 25);
|}
|
|/++
|Computes whether two values are approximately equal, admitting a maximum
|relative difference, and a maximum absolute difference.
|Params:
| lhs = First item to compare.
| rhs = Second item to compare.
| maxRelDiff = Maximum allowable difference relative to `rhs`. Defaults to `0.5 ^^ 20`.
| maxAbsDiff = Maximum absolute difference. Defaults to `0.5 ^^ 20`.
|
|Returns:
| `true` if the two items are equal or approximately equal under either criterium.
|+/
|bool approxEqual(T)(const T lhs, const T rhs, const T maxRelDiff = T(0x1p-20f), const T maxAbsDiff = T(0x1p-20f))
| if (isFloatingPoint!T)
|{
| if (rhs == lhs) // infs
| return true;
| auto diff = fabs(lhs - rhs);
| if (diff <= maxAbsDiff)
| return true;
| diff /= fabs(rhs);
| return diff <= maxRelDiff;
|}
|
|///
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| assert(approxEqual(1.0, 1.0000001));
| assert(approxEqual(1.0f, 1.0000001f));
| assert(approxEqual(1.0L, 1.0000001L));
|
| assert(approxEqual(10000000.0, 10000001));
| assert(approxEqual(10000000f, 10000001f));
| assert(!approxEqual(100000.0L, 100001L));
|}
|
|/// ditto
|bool approxEqual(T)(const T lhs, const T rhs, float maxRelDiff = 0x1p-20f, float maxAbsDiff = 0x1p-20f)
| if (isComplexOf!(T, float))
|{
| return approxEqual(lhs.re, rhs.re, maxRelDiff, maxAbsDiff)
| && approxEqual(lhs.im, rhs.im, maxRelDiff, maxAbsDiff);
|}
|
|/// ditto
|bool approxEqual(T)(const T lhs, const T rhs, double maxRelDiff = 0x1p-20f, double maxAbsDiff = 0x1p-20f)
| if (isComplexOf!(T, double))
|{
| return approxEqual(lhs.re, rhs.re, maxRelDiff, maxAbsDiff)
| && approxEqual(lhs.im, rhs.im, maxRelDiff, maxAbsDiff);
|}
|
|/// ditto
|bool approxEqual(T)(const T lhs, const T rhs, real maxRelDiff = 0x1p-20f, real maxAbsDiff = 0x1p-20f)
| if (isComplexOf!(T, real))
|{
| return approxEqual(lhs.re, rhs.re, maxRelDiff, maxAbsDiff)
| && approxEqual(lhs.im, rhs.im, maxRelDiff, maxAbsDiff);
|}
|
|/// Complex types works as `approxEqual(l.re, r.re) && approxEqual(l.im, r.im)`
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.internal.utility: isComplexOf;
| static struct UserComplex(T) { T re, im; }
| alias _cdouble = UserComplex!double;
|
| static assert(isComplexOf!(_cdouble, double));
|
| assert(approxEqual(_cdouble(1.0, 1), _cdouble(1.0000001, 1), 1.0000001));
| assert(!approxEqual(_cdouble(100000.0L, 0), _cdouble(100001L, 0)));
|}
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/math/common.d has no code
<<<<<< EOF
# path=./source-mir-bignum-decimal.lst
|/++
|Stack-allocated decimal type.
|
|Note:
| The module doesn't provide full arithmetic API for now.
|+/
|module mir.bignum.decimal;
|
|import mir.serde: serdeProxy, serdeScoped;
|import std.traits: isSomeChar;
|public import mir.bignum.low_level_view: DecimalExponentKey;
|import mir.bignum.low_level_view: ceilLog10Exp2;
|
|private enum expBufferLength = 2 + ceilLog10Exp2(size_t.sizeof * 8);
|private static immutable C[9] zerosImpl(C) = "0.00000.0";
|
|/++
|Stack-allocated decimal type.
|Params:
| maxSize64 = count of 64bit words in coefficient
|+/
|@serdeScoped @serdeProxy!(const(char)[])
|struct Decimal(size_t maxSize64)
| if (maxSize64 && maxSize64 <= ushort.max)
|{
| import mir.format: NumericSpec;
| import mir.bignum.integer;
| import mir.bignum.low_level_view;
| import std.traits: isMutable, isFloatingPoint;
|
| ///
| sizediff_t exponent;
| ///
| BigInt!maxSize64 coefficient;
|
| ///
| DecimalView!size_t view()
| {
1| return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
| }
|
| /// ditto
| DecimalView!(const size_t) view() const
| {
60| return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
| }
|
| ///
| this(C)(scope const(C)[] str, int exponentShift = 0) @safe pure @nogc
| if (isSomeChar!C)
| {
12| DecimalExponentKey key;
12| if (fromStringImpl(str, key, exponentShift) || key == DecimalExponentKey.nan || key == DecimalExponentKey.infinity)
12| return;
| static if (__traits(compiles, () @nogc { throw new Exception("Can't parse Decimal."); }))
| {
| import mir.exception: MirException;
| throw new MirException("Can't parse Decimal!" ~ maxSize64.stringof ~ " from string `", str , "`");
| }
| else
| {
| static immutable exception = new Exception("Can't parse Decimal!" ~ maxSize64.stringof ~ ".");
0000000| throw exception;
| }
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_test) @safe pure @nogc unittest
| {
| import mir.math.constant: PI;
1| Decimal!2 decimal = "3.141592653589793378e-40"; // constructor
1| assert(cast(double) decimal == double(PI) / 1e40);
| }
|
| /++
| Constructs Decimal from the floating point number using the $(HTTPS github.com/ulfjack/ryu, Ryu algorithm).
|
| The number is the shortest decimal representation that being converted back would result the same floating-point number.
| +/
72| this(T)(const T x)
| if (isFloatingPoint!T && maxSize64 >= 1 + (T.mant_dig >= 64))
| {
| import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
72| this = genericBinaryToDecimal(x);
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_bignum_test)
| @safe pure nothrow @nogc
| unittest
| {
| // float and double can be used to construct Decimal of any length
1| auto decimal64 = Decimal!1(-1.235e-7);
1| assert(decimal64.exponent == -10);
1| assert(decimal64.coefficient == -1235);
|
| // real number may need Decimal at least length of 2
1| auto decimal128 = Decimal!2(-1.235e-7L);
1| assert(decimal128.exponent == -10);
1| assert(decimal128.coefficient == -1235);
|
1| decimal128 = Decimal!2(1234e3f);
1| assert(decimal128.exponent == 3);
1| assert(decimal128.coefficient == 1234);
| }
|
| ///
| ref opAssign(size_t rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return
| if (rhsMaxSize64 < maxSize64)
| {
4| this.exponent = rhs.exponent;
4| this.coefficient = rhs.coefficient;
4| return this;
| }
|
| /++
| Handle thousand separators for non exponential numbers.
|
| Returns: false in case of overflow or incorrect string.
| +/
| bool fromStringWithThousandsSeparatorImpl(C,
| bool allowSpecialValues = true,
| bool allowStartingPlus = true,
| bool allowLeadingZeros = true,
| )(
| scope const(C)[] str,
| const C thousandsSeparator,
| const C fractionSeparator,
| out DecimalExponentKey key,
| int exponentShift = 0,
| )
| if (isSomeChar!C)
| {
| import mir.algorithm.iteration: find;
| import mir.format: stringBuf;
| import mir.ndslice.chunks: chunks;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: retro;
|
| stringBuf buffer;
| assert(thousandsSeparator != fractionSeparator);
| if (str.length && (str[0] == '+' || str[0] == '-'))
| {
| buffer.put(cast(char)str[0]);
| str = str[1 .. $];
| }
| auto integer = str[0 .. $ - str.find!(a => a == fractionSeparator)];
| if (integer.length % 4 == 0)
| return false;
| foreach_reverse (chunk; integer.sliced.retro.chunks(4))
| {
| auto s = chunk.retro.field;
| if (s.length == 4)
| {
| if (s[0] != thousandsSeparator)
| return false;
| s = s[1 .. $];
| }
| do
| {
| if (s[0] < '0' || s[0] > '9')
| return false;
| buffer.put(cast(char)s[0]);
| s = s[1 .. $];
| }
| while(s.length);
| }
| if (str.length > integer.length)
| {
| buffer.put('.');
| str = str[integer.length + 1 .. $];
| if (str.length == 0)
| return false;
| do
| {
| buffer.put(cast(char)str[0]);
| str = str[1 .. $];
| }
| while(str.length);
| }
| return fromStringImpl!(char,
| allowSpecialValues,
| false, // allowDotOnBounds
| false, // allowDExponent
| allowStartingPlus,
| false, // allowUnderscores
| allowLeadingZeros, // allowLeadingZeros
| false, // allowExponent
| false, // checkEmpty
| )(buffer.data, key, exponentShift);
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_bignum_test)
| @safe pure nothrow @nogc
| unittest
| {
1| Decimal!3 decimal;
1| DecimalExponentKey key;
|
1| assert(decimal.fromStringWithThousandsSeparatorImpl("12,345.678", ',', '.', key));
1| assert(cast(double) decimal == 12345.678);
1| assert(key == DecimalExponentKey.dot);
|
1| assert(decimal.fromStringWithThousandsSeparatorImpl("12,345,678", ',', '.', key, -3));
1| assert(cast(double) decimal == 12345.678);
1| assert(key == DecimalExponentKey.none);
|
1| assert(decimal.fromStringWithThousandsSeparatorImpl("021 345,678", ' ', ',', key));
1| assert(cast(double) decimal == 21345.678);
1| assert(key == DecimalExponentKey.dot);
| }
|
| /++
| Returns: false in case of overflow or incorrect string.
| +/
| bool fromStringImpl(C,
| bool allowSpecialValues = true,
| bool allowDotOnBounds = true,
| bool allowDExponent = true,
| bool allowStartingPlus = true,
| bool allowUnderscores = true,
| bool allowLeadingZeros = true,
| bool allowExponent = true,
| bool checkEmpty = true,
| )
| (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
| scope @trusted pure @nogc nothrow
| if (isSomeChar!C)
| {
| enum optimize = size_t.sizeof == 8 && maxSize64 == 1;
| version(LDC)
| {
| static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false)
| pragma(inline, true);
| }
| static if (optimize)
| {
| import mir.utility: _expect;
| static if (checkEmpty)
| {
| if (_expect(str.length == 0, false))
| return false;
| }
|
| coefficient.sign = str[0] == '-';
| if (coefficient.sign)
| {
| str = str[1 .. $];
| if (_expect(str.length == 0, false))
| return false;
| }
| else
| static if (allowStartingPlus)
| {
| if (_expect(str[0] == '+', false))
| {
| str = str[1 .. $];
| if (_expect(str.length == 0, false))
| return false;
| }
| }
|
| uint d = str[0] - '0';
| str = str[1 .. $];
| exponent = 0;
|
| ulong v;
| bool dot;
| static if (allowUnderscores)
| {
| bool recentUnderscore;
| }
| static if (!allowLeadingZeros)
| {
| if (d == 0)
| {
| if (str.length == 0)
| goto R;
| if (str[0] >= '0' && str[0] <= '9')
| return false;
| goto S;
| }
| }
|
| if (d < 10)
| {
| goto S;
| }
|
| static if (allowDotOnBounds)
| {
| if (d == '.' - '0')
| {
| if (str.length == 0)
| return false;
| key = DecimalExponentKey.dot;
| dot = true;
| goto F;
| }
| }
|
| static if (allowSpecialValues)
| {
| goto NI;
| }
| else
| {
| return false;
| }
|
| F: for(;;)
| {
| d = str[0] - '0';
| str = str[1 .. $];
|
| if (_expect(d <= 10, true))
| {
| static if (allowUnderscores)
| {
| recentUnderscore = false;
| }
| {
| import mir.checkedint: mulu;
| bool overflow;
| v = mulu(v, cast(uint)10, overflow);
| if (overflow)
| break;
| }
| S:
| v += d;
| exponentShift -= dot;
| if (str.length)
| continue;
| E:
| exponent += exponentShift;
| R:
| coefficient.data[0] = v;
| coefficient.length = v != 0;
| static if (allowUnderscores)
| {
| return !recentUnderscore;
| }
| else
| {
| return true;
| }
| }
| static if (allowUnderscores)
| {
| if (recentUnderscore)
| return false;
| }
| switch (d)
| {
| case DecimalExponentKey.dot:
| key = DecimalExponentKey.dot;
| if (_expect(dot, false))
| break;
| dot = true;
| if (str.length)
| {
| static if (allowUnderscores)
| {
| recentUnderscore = true;
| }
| continue;
| }
| static if (allowDotOnBounds)
| {
| goto R;
| }
| else
| {
| break;
| }
| static if (allowExponent)
| {
| static if (allowDExponent)
| {
| case DecimalExponentKey.d:
| case DecimalExponentKey.D:
| goto case DecimalExponentKey.e;
| }
| case DecimalExponentKey.e:
| case DecimalExponentKey.E:
| import mir.parse: parse;
| key = cast(DecimalExponentKey)d;
| if (parse(str, exponent) && str.length == 0)
| goto E;
| break;
| }
| static if (allowUnderscores)
| {
| case '_' - '0':
| recentUnderscore = true;
| if (str.length)
| continue;
| break;
| }
| default:
| }
| break;
| }
| return false;
| static if (allowSpecialValues)
| {
| NI:
| exponent = exponent.max;
| if (str.length == 2)
| {
| auto stail = cast(C[2])str[0 .. 2];
| if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
| {
| key = DecimalExponentKey.infinity;
| goto R;
| }
| if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
| {
| v = 1;
| key = DecimalExponentKey.nan;
| goto R;
| }
| }
| return false;
| }
| }
| else
| {
| import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
86| auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
86| auto ret = work.fromStringImpl!(C,
| allowSpecialValues,
| allowDotOnBounds,
| allowDExponent,
| allowStartingPlus,
| allowUnderscores,
| allowLeadingZeros,
| allowExponent,
| checkEmpty,
| )(str, key, exponentShift);
86| coefficient.length = cast(uint) work.coefficient.coefficients.length;
86| coefficient.sign = work.sign;
86| exponent = work.exponent;
86| return ret;
| }
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_bignum_test)
| @safe pure nothrow @nogc
| unittest
| {
| import mir.conv: to;
1| Decimal!3 decimal;
1| DecimalExponentKey key;
|
| // Check precise percentate parsing
1| assert(decimal.fromStringImpl("71.7", key, -2));
1| assert(key == DecimalExponentKey.dot);
| // The result is exact value instead of 0.7170000000000001 = 71.7 / 100
1| assert(cast(double) decimal == 0.717);
|
1| assert(decimal.fromStringImpl("+0.334e-5"w, key));
1| assert(key == DecimalExponentKey.e);
1| assert(cast(double) decimal == 0.334e-5);
|
1| assert(decimal.fromStringImpl("100_000_000"w, key));
1| assert(key == DecimalExponentKey.none);
1| assert(cast(double) decimal == 1e8);
|
1| assert(decimal.fromStringImpl("-334D-5"d, key));
1| assert(key == DecimalExponentKey.D);
1| assert(cast(double) decimal == -334e-5);
|
1| assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
1| assert(key == DecimalExponentKey.none);
1| assert(cast(double) decimal == 2482734692817364218734682973648217364981273648923423.0);
|
1| assert(decimal.fromStringImpl(".023", key));
1| assert(key == DecimalExponentKey.dot);
1| assert(cast(double) decimal == .023);
|
1| assert(decimal.fromStringImpl("0E100", key));
1| assert(key == DecimalExponentKey.E);
1| assert(cast(double) decimal == 0);
|
12| foreach (str; ["-nan", "-NaN", "-NAN"])
| {
3| assert(decimal.fromStringImpl(str, key));
3| assert(decimal.coefficient.length > 0);
3| assert(decimal.exponent == decimal.exponent.max);
3| assert(decimal.coefficient.sign);
3| assert(key == DecimalExponentKey.nan);
3| assert(cast(double) decimal != cast(double) decimal);
| }
|
12| foreach (str; ["inf", "Inf", "INF"])
| {
3| assert(decimal.fromStringImpl(str, key));
3| assert(decimal.coefficient.length == 0);
3| assert(decimal.exponent == decimal.exponent.max);
3| assert(key == DecimalExponentKey.infinity);
3| assert(cast(double) decimal == double.infinity);
| }
|
1| assert(decimal.fromStringImpl("-inf", key));
1| assert(decimal.coefficient.length == 0);
1| assert(decimal.exponent == decimal.exponent.max);
1| assert(key == DecimalExponentKey.infinity);
1| assert(cast(double) decimal == -double.infinity);
|
1| assert(!decimal.fromStringImpl("3.3.4", key));
1| assert(!decimal.fromStringImpl("3.4.", key));
1| assert(decimal.fromStringImpl("4.", key));
1| assert(!decimal.fromStringImpl(".", key));
1| assert(decimal.fromStringImpl("0.", key));
1| assert(decimal.fromStringImpl("00", key));
1| assert(!decimal.fromStringImpl("0d", key));
| }
|
| static if (maxSize64 == 3)
| version(mir_bignum_test)
| @safe pure nothrow @nogc
| unittest
| {
| import mir.conv: to;
1| Decimal!1 decimal;
1| DecimalExponentKey key;
|
1| assert(decimal.fromStringImpl("1.334", key));
1| assert(key == DecimalExponentKey.dot);
1| assert(cast(double) decimal == 1.334);
|
1| assert(decimal.fromStringImpl("+0.334e-5"w, key));
1| assert(key == DecimalExponentKey.e);
1| assert(cast(double) decimal == 0.334e-5);
|
1| assert(decimal.fromStringImpl("-334D-5"d, key));
1| assert(key == DecimalExponentKey.D);
1| assert(cast(double) decimal == -334e-5);
|
1| assert(!decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
|
1| assert(decimal.fromStringImpl(".023", key));
1| assert(key == DecimalExponentKey.dot);
1| assert(cast(double) decimal == .023);
|
1| assert(decimal.fromStringImpl("0E100", key));
1| assert(key == DecimalExponentKey.E);
1| assert(cast(double) decimal == 0);
|
12| foreach (str; ["-nan", "-NaN", "-NAN"])
| {
3| assert(decimal.fromStringImpl(str, key));
3| assert(decimal.coefficient.length > 0);
3| assert(decimal.exponent == decimal.exponent.max);
3| assert(decimal.coefficient.sign);
3| assert(key == DecimalExponentKey.nan);
3| assert(cast(double) decimal != cast(double) decimal);
| }
|
12| foreach (str; ["inf", "Inf", "INF"])
| {
3| assert(decimal.fromStringImpl(str, key));
3| assert(decimal.coefficient.length == 0);
3| assert(decimal.exponent == decimal.exponent.max);
3| assert(key == DecimalExponentKey.infinity);
3| assert(cast(double) decimal == double.infinity);
| }
|
1| assert(decimal.fromStringImpl("-inf", key));
1| assert(decimal.coefficient.length == 0);
1| assert(decimal.exponent == decimal.exponent.max);
1| assert(key == DecimalExponentKey.infinity);
1| assert(cast(double) decimal == -double.infinity);
|
1| assert(!decimal.fromStringImpl("3.3.4", key));
1| assert(!decimal.fromStringImpl("3.4.", key));
1| assert(decimal.fromStringImpl("4.", key));
1| assert(!decimal.fromStringImpl(".", key));
1| assert(decimal.fromStringImpl("0.", key));
1| assert(decimal.fromStringImpl("00", key));
1| assert(!decimal.fromStringImpl("0d", key));
| }
|
| private enum coefficientBufferLength = 2 + ceilLog10Exp2(coefficient.data.length * (size_t.sizeof * 8)); // including dot and sign
| private enum eDecimalLength = coefficientBufferLength + expBufferLength;
|
| ///
| immutable(C)[] toString(C = char)(NumericSpec spec = NumericSpec.init) const @safe pure nothrow
| if(isSomeChar!C && isMutable!C)
| {
| import mir.appender: UnsafeArrayBuffer;
2| C[eDecimalLength] data = void;
2| auto buffer = UnsafeArrayBuffer!C(data);
2| toString(buffer, spec);
2| return buffer.data.idup;
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_bignum_test) @safe pure unittest
| {
1| auto str = "-3.4010447314490204552169750449563978034784726557588085989975288830070948234680e-13245";
1| auto decimal = Decimal!4(str);
1| assert(decimal.toString == str, decimal.toString);
|
1| decimal = Decimal!4.init;
1| assert(decimal.toString == "0.0");
| }
|
| ///
| void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
| if(isSomeChar!C && isMutable!C)
| {
146| assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human);
| import mir.utility: _expect;
| // handle special values
73| if (_expect(exponent == exponent.max, false))
| {
| static immutable C[3] nan = "nan";
| static immutable C[4] ninf = "-inf";
| static immutable C[4] pinf = "+inf";
20| w.put(coefficient.length == 0 ? coefficient.sign ? ninf[] : pinf[] : nan[]);
8| return;
| }
|
65| C[coefficientBufferLength + 16] buffer0 = void;
65| auto buffer = buffer0[0 .. $ - 16];
|
65| size_t coefficientLength;
| static if (size_t.sizeof == 8)
| {
| if (__ctfe)
| {
| uint[coefficient.data.length * 2] data;
| foreach (i; 0 .. coefficient.length)
| {
| auto l = cast(uint)coefficient.data[i];
| auto h = cast(uint)(coefficient.data[i] >> 32);
| version (LittleEndian)
| {
| data[i * 2 + 0] = l;
| data[i * 2 + 1] = h;
| }
| else
| {
| data[$ - 1 - (i * 2 + 0)] = l;
| data[$ - 1 - (i * 2 + 1)] = h;
| }
| }
| auto work = BigUIntView!uint(data);
| work = work.topLeastSignificantPart(coefficient.length * 2).normalized;
| coefficientLength = work.toStringImpl(buffer);
| }
| else
| {
| BigInt!maxSize64 work = coefficient;
| coefficientLength = work.view.unsigned.toStringImpl(buffer);
| }
| }
| else
| {
65| BigInt!maxSize64 work = coefficient;
65| coefficientLength = work.view.unsigned.toStringImpl(buffer);
| }
|
130| C[1] sign = coefficient.sign ? "-" : "+";
116| bool addSign = coefficient.sign || spec.plus;
65| sizediff_t s = this.exponent + coefficientLength;
|
| alias zeros = zerosImpl!C;
|
65| if (spec.format == NumericSpec.Format.human)
| {
65| if (!spec.separatorCount)
0000000| spec.separatorCount = 3;
| void putL(scope const(C)[] b)
| {
11| assert(b.length);
|
11| if (addSign)
2| w.put(sign[]);
|
11| auto r = b.length % spec.separatorCount;
11| if (r == 0)
4| r = spec.separatorCount;
11| C[1] sep = spec.separatorChar;
11| goto LS;
| do
| {
18| w.put(sep[]);
| LS:
29| w.put(b[0 .. r]);
29| b = b[r .. $];
29| r = spec.separatorCount;
| }
29| while(b.length);
| }
|
| // try print decimal form without exponent
| // up to 6 digits exluding leading 0. or final .0
65| if (s <= 0)
| {
| //0.001....
| //0.0001
| //0.00001
| //0.000001
| //If separatorChar is defined lets be less greed for space.
60| if (this.exponent >= -6 || s >= -2 - (spec.separatorChar != 0) * 3)
| {
30| if (addSign)
8| w.put(sign[]);
30| w.put(zeros[0 .. -s + 2]);
30| w.put(buffer[$ - coefficientLength .. $]);
30| return;
| }
| }
| else
28| if (this.exponent >= 0)
| {
| ///dddddd.0
18| if (!spec.separatorChar)
| {
10| if (s <= 6)
| {
8| buffer[$ - coefficientLength - 1] = sign[0];
8| w.put(buffer[$ - coefficientLength - addSign .. $]);
8| w.put(zeros[$ - (this.exponent + 2) .. $]);
8| return;
| }
| }
| else
| {
8| if (s <= 12)
| {
7| buffer0[$ - 16 .. $] = '0';
7| putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + this.exponent]);
7| w.put(zeros[$ - 2 .. $]);
7| return;
| }
| }
| }
| else
| {
| ///dddddd.0
10| if (!spec.separatorChar)
| {
| ///dddddd.d....
7| if (s <= 6 || coefficientLength <= 6)
| {
5| buffer[$ - coefficientLength - 1] = sign[0];
5| w.put(buffer[$ - coefficientLength - addSign .. $ - coefficientLength + s]);
| T2:
9| buffer[$ - coefficientLength + s - 1] = '.';
9| w.put(buffer[$ - coefficientLength + s - 1 .. $]);
9| return;
| }
| }
| else
| {
4| if (s <= 12 || coefficientLength <= 12)
| {
4| putL(buffer[$ - coefficientLength .. $ - coefficientLength + s]);
4| goto T2;
| }
| }
| }
| }
|
11| assert(coefficientLength);
|
11| sizediff_t exponent = s - 1;
|
11| if (coefficientLength > 1)
| {
9| auto c = buffer[$ - coefficientLength];
9| buffer[$ - coefficientLength] = '.';
9| buffer[$ - ++coefficientLength] = c;
| }
|
11| buffer[$ - coefficientLength - 1] = sign[0];
11| w.put(buffer[$ - coefficientLength - addSign .. $]);
|
| import mir.format_impl: printSignedToTail;
|
| static if (sizediff_t.sizeof == 8)
| enum N = 21;
| else
| enum N = 11;
|
| // prints e+/-exponent
11| auto expLength = printSignedToTail(exponent, buffer0[$ - N - 16 .. $ - 16], '+');
11| buffer[$ - ++expLength] = spec.exponentChar;
11| w.put(buffer[$ - expLength .. $]);
| }
|
| static if (maxSize64 == 3)
| /// Check @nogc toString impl
| version(mir_bignum_test) @safe pure @nogc unittest
| {
| import mir.format: stringBuf;
1| auto str = "5.28238923728e-876543210";
1| auto decimal = Decimal!1(str);
2| stringBuf buffer;
1| buffer << decimal;
1| assert(buffer.data == str, buffer.data);
| }
|
| /++
| Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers.
| Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP. +/
| T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
| if (isFloatingPoint!T && isMutable!T)
| {
|
| enum optimize = maxSize64 == 1 && size_t.sizeof == 8 && T.mant_dig < 64;
|
| version(LDC)
| {
| static if (optimize || wordNormalized)
| pragma(inline, true);
| }
|
| static if (optimize)
| {
| import mir.bignum.fixed: UInt;
| import mir.bignum.fp: Fp, extendedMul;
| import mir.bignum.internal.dec2flt_table;
| import mir.bignum.low_level_view: MaxWordPow5, MaxFpPow5;
| import mir.math.common: floor;
| import mir.utility: _expect;
|
| T ret = 0;
| size_t length = coefficient.length;
|
|
| static if (!wordNormalized)
| {
| if (coefficient.data[0] == 0)
| length = 0;
| }
|
| if (_expect(exponent == exponent.max, false))
| {
| ret = length ? T.nan : T.infinity;
| goto R;
| }
|
| static if (!nonZero)
| if (length == 0)
| goto R;
| enum S = 9;
|
| Fp!64 load(typeof(exponent) e)
| {
| auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
| auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
| return Fp!64(false, p10exp, UInt!64(p10coeff));
| }
| {
| auto expSign = exponent < 0;
| if (_expect((expSign ? -exponent : exponent) >>> S == 0, true))
| {
| enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
| enum ulong half = (1UL << (64 - T.mant_dig - 1));
| enum ulong bound = ulong(1) << T.mant_dig;
|
| auto c = Fp!64(UInt!64(coefficient.data[0]));
| auto z = c.extendedMul(load(exponent));
| ret = cast(T) z;
| long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
| if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > 3 * expSign, true))
| goto R;
| if (!expSign && exponent <= MaxWordPow5!ulong || exponent == 0)
| goto R;
| if (expSign && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
| {
| auto e = load(-exponent);
| ret = c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
| goto R;
| }
| ret = algoR!T(ret, view.coefficient, cast(int) exponent);
| goto R;
| }
| ret = expSign ? 0 : T.infinity;
| }
| R:
| if (coefficient.sign)
| ret = -ret;
| return ret;
| }
| else
| {
60| return view.opCast!(T, wordNormalized, nonZero);
| }
| }
|
| ///
| bool isNaN() const @property
| {
0000000| return exponent == exponent.max && coefficient.length;
| }
|
| ///
| bool isInfinity() const @property
| {
0000000| return exponent == exponent.max && !coefficient.length;
| }
|
| ///
| ref opOpAssign(string op, size_t rhsMaxSize64)(ref const Decimal!rhsMaxSize64 rhs) @safe pure return
| if (op == "+" || op == "-")
| {
20| BigInt!rhsMaxSize64 rhsCopy;
20| BigIntView!(const size_t) rhsView;
20| auto expDiff = exponent - rhs.exponent;
20| if (expDiff >= 0)
| {
11| exponent = rhs.exponent;
11| coefficient.mulPow5(expDiff);
11| coefficient.opOpAssign!"<<"(expDiff);
11| rhsView = rhs.coefficient.view;
| }
| else
| {
9| rhsCopy.copyFrom(rhs.coefficient.view);
9| rhsCopy.mulPow5(-expDiff);
9| rhsCopy.opOpAssign!"<<"(-expDiff);
9| rhsView = rhsCopy.view;
| }
20| coefficient.opOpAssign!op(rhsView);
20| return this;
| }
|
| static if (maxSize64 == 3)
| ///
| version(mir_bignum_test) @safe pure @nogc unittest
| {
| import std.stdio;
1| auto a = Decimal!1("777.7");
1| auto b = Decimal!1("777");
| import mir.format;
1| assert(stringBuf() << cast(double)a - cast(double)b << getData == "0.7000000000000455");
1| a -= b;
1| assert(stringBuf() << a << getData == "0.7");
|
1| a = Decimal!1("-777.7");
1| b = Decimal!1("777");
1| a += b;
1| assert(stringBuf() << a << getData == "-0.7");
|
1| a = Decimal!1("777.7");
1| b = Decimal!1("-777");
1| a += b;
1| assert(stringBuf() << a << getData == "0.7");
|
1| a = Decimal!1("777");
1| b = Decimal!1("777.7");
1| a -= b;
1| assert(stringBuf() << a << getData == "-0.7");
| }
|}
|
|///
|version(mir_bignum_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.conv: to;
1| Decimal!3 decimal;
1| DecimalExponentKey key;
|
| import mir.math.constant: PI;
1| assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
1| assert(cast(double) decimal == double(PI) / 1e10);
1| assert(key == DecimalExponentKey.e);
|}
|
|
|///
|version(mir_bignum_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.conv: to;
1| Decimal!3 decimal;
1| DecimalExponentKey key;
|
1| assert(decimal.fromStringImpl("0", key));
1| assert(key == DecimalExponentKey.none);
1| assert(decimal.exponent == 0);
1| assert(decimal.coefficient.length == 0);
1| assert(!decimal.coefficient.sign);
1| assert(cast(double) decimal.coefficient == 0);
|
1| assert(decimal.fromStringImpl("-0.0", key));
1| assert(key == DecimalExponentKey.dot);
1| assert(decimal.exponent == -1);
1| assert(decimal.coefficient.length == 0);
1| assert(decimal.coefficient.sign);
1| assert(cast(double) decimal.coefficient == 0);
|
1| assert(decimal.fromStringImpl("0e0", key));
1| assert(key == DecimalExponentKey.e);
1| assert(decimal.exponent == 0);
1| assert(decimal.coefficient.length == 0);
1| assert(!decimal.coefficient.sign);
1| assert(cast(double) decimal.coefficient == 0);
|}
|
|deprecated("use decimal.fromStringImpl insteade")
|@trusted @nogc pure nothrow
|bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
| if (isSomeChar!C)
|{
| return decimal.fromStringImpl(str, key);
|}
|
|
|version(mir_bignum_test)
|@safe pure @nogc unittest
|{
1| Decimal!4 i = "-0";
|
1| assert(i.view.coefficient.coefficients.length == 0);
1| assert(i.coefficient.view.unsigned.coefficients.length == 0);
1| assert(i.coefficient.view == 0L);
1| assert(cast(long) i.coefficient == 0);
|}
source/mir/bignum/decimal.d is 98% covered
<<<<<< EOF
# path=./source-mir-combinatorics-package.lst
|/**
|This module contains various combinatorics algorithms.
|
|Authors: Sebastian Wilzbach, Ilya Yaroshenko
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|*/
|module mir.combinatorics;
|
|import mir.primitives: hasLength;
|import mir.qualifier;
|import std.traits;
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.fuse;
|
1| assert(['a', 'b'].permutations.fuse == [['a', 'b'], ['b', 'a']]);
1| assert(['a', 'b'].cartesianPower(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'a'], ['b', 'b']]);
1| assert(['a', 'b'].combinations(2).fuse == [['a', 'b']]);
1| assert(['a', 'b'].combinationsRepeat(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'b']]);
|
1| assert(permutations!ushort(2).fuse == [[0, 1], [1, 0]]);
1| assert(cartesianPower!ushort(2, 2).fuse == [[0, 0], [0, 1], [1, 0], [1, 1]]);
1| assert(combinations!ushort(2, 2).fuse == [[0, 1]]);
1| assert(combinationsRepeat!ushort(2, 2).fuse == [[0, 0], [0, 1], [1, 1]]);
|
1| assert([3, 1].permutations!ubyte.fuse == [[3, 1], [1, 3]]);
1| assert([3, 1].cartesianPower!ubyte(2).fuse == [[3, 3], [3, 1], [1, 3], [1, 1]]);
1| assert([3, 1].combinations!ubyte(2).fuse == [[3, 1]]);
1| assert([3, 1].combinationsRepeat!ubyte(2).fuse == [[3, 3], [3, 1], [1, 1]]);
|}
|
|/**
|Checks whether we can do basic arithmetic operations, comparisons, modulo and
|assign values to the type.
|*/
|private template isArithmetic(R)
|{
| enum bool isArithmetic = is(typeof(
| (inout int = 0)
| {
| R r = 1;
| R test = (r * r / r + r - r) % r;
| if (r < r && r > r) {}
| }));
|}
|
|/**
|Checks whether we can do basic arithmetic operations, comparison and modulo
|between two types. R needs to support item assignment of S (it includes S).
|Both R and S need to be arithmetic types themselves.
|*/
|private template isArithmetic(R, S)
|{
| enum bool isArithmetic = is(typeof(
| (inout int = 0)
| {
| if (isArithmetic!R && isArithmetic!S) {}
| S s = 1;
| R r = 1;
| R test = r * s + r * s;
| R test2 = r / s + r / s;
| R test3 = r - s + r - s;
| R test4 = r % s + r % s;
| if (r < s && s > r) {}
| if (s < r && r > s) {}
| }));
|}
|
|/**
|Computes the $(WEB en.wikipedia.org/wiki/Binomial_coefficient, binomial coefficient)
|of n and k.
|It is also known as "n choose k" or more formally as `_n!/_k!(_n-_k)`.
|If a fixed-length integer type is used and an overflow happens, `0` is returned.
|
|Uses the generalized binomial coefficient for negative integers and floating
|point number
|
|Params:
| n = arbitrary arithmetic type
| k = arbitrary arithmetic type
|
|Returns:
| Binomial coefficient
|*/
|R binomial(R = ulong, T)(T n, T k)
| if (isArithmetic!(R, T) &&
| ((is(typeof(T.min < 0)) && is(typeof(T.init & 1))) || !is(typeof(T.min < 0))) )
|{
102| R result = 1;
|
| enum hasMinProperty = is(typeof(T.min < 0));
| // only add negative support if possible
| static if ((hasMinProperty && T.min < 0) || !hasMinProperty)
| {
24| if (n < 0)
| {
3| if (k >= 0)
| {
4| return (k & 1 ? -1 : 1) * binomial!(R, T)(-n + k-1, k);
| }
1| else if (k <= n)
| {
2| return ((n-k) & 1 ? -1 : 1) * binomial!(R, T)(-k-1, n-k);
| }
| }
21| if (k < 0)
| {
2| result = 0;
2| return result;
| }
| }
|
97| if (k > n)
| {
9| result = 0;
9| return result;
| }
88| if (k > n - k)
| {
38| k = n - k;
| }
| // make a copy of n (could be a custom type)
714| for (T i = 1, m = n; i <= k; i++, m--)
| {
| // check whether an overflow can happen
| // hasMember!(Result, "max") doesn't work with dmd2.068 and ldc 0.17
| static if (is(typeof(0 > R.max)))
| {
203| if (result / i > R.max / m) return 0;
201| result = result / i * m + result % i * m / i;
| }
| else
| {
24| result = result * m / i;
| }
| }
87| return result;
|}
|
|///
|pure version(mir_test) unittest
|{
1| assert(binomial(5, 2) == 10);
1| assert(binomial(6, 4) == 15);
1| assert(binomial(3, 1) == 3);
|
| import std.bigint: BigInt;
1| assert(binomial!BigInt(1000, 10) == BigInt("263409560461970212832400"));
|}
|
|pure nothrow @safe @nogc version(mir_test) unittest
|{
1| assert(binomial(5, 1) == 5);
1| assert(binomial(5, 0) == 1);
1| assert(binomial(1, 2) == 0);
1| assert(binomial(1, 0) == 1);
1| assert(binomial(1, 1) == 1);
1| assert(binomial(2, 1) == 2);
1| assert(binomial(2, 1) == 2);
|
| // negative
1| assert(binomial!long(-5, 3) == -35);
1| assert(binomial!long(5, -3) == 0);
|}
|
|version(mir_test) unittest
|{
| import std.bigint;
|
| // test larger numbers
1| assert(binomial(100, 10) == 17_310_309_456_440);
1| assert(binomial(999, 5) == 82_09_039_793_949);
1| assert(binomial(300, 10) == 1_398_320_233_241_701_770LU);
1| assert(binomial(300LU, 10LU) == 1_398_320_233_241_701_770LU);
|
| // test overflow
1| assert(binomial(500, 10) == 0);
|
| // all parameters as custom types
2| BigInt n = 1010, k = 9;
1| assert(binomial!BigInt(n, k) == BigInt("2908077120956865974260"));
|
| // negative
1| assert(binomial!BigInt(-5, 3) == -35);
1| assert(binomial!BigInt(5, -3) == 0);
1| assert(binomial!BigInt(-5, -7) == 15);
|}
|
|/**
|Creates a projection of a generalized `Collection` range for the numeric case
|case starting from `0` onto a custom `range` of any type.
|
|Params:
| collection = range to be projected from
| range = random access range to be projected to
|
|Returns:
| Range with a projection to range for every element of collection
|
|See_Also:
| $(LREF permutations), $(LREF cartesianPower), $(LREF combinations),
| $(LREF combinationsRepeat)
|*/
|IndexedRoR!(Collection, Range) indexedRoR(Collection, Range)(Collection collection, Range range)
| if (__traits(compiles, Range.init[size_t.init]))
|{
72| return IndexedRoR!(Collection, Range)(collection, range);
|}
|
|/// ditto
|struct IndexedRoR(Collection, Range)
| if (__traits(compiles, Range.init[size_t.init]))
|{
| private Collection c;
| private Range r;
|
| ///
| alias DeepElement = ForeachType!Range;
|
| ///
77| this()(Collection collection, Range range)
| {
77| this.c = collection;
77| this.r = range;
| }
|
| ///
| auto lightScope()()
| {
| return IndexedRoR!(LightScopeOf!Collection, LightScopeOf!Range)(.lightScope(c), .lightScope(r));
| }
|
| ///
| auto lightScope()() const
| {
| return IndexedRoR!(LightConstOf!(LightScopeOf!Collection), LightConstOf!(LightScopeOf!Range))(.lightScope(c), .lightScope(r));
| }
|
| ///
| auto lightConst()() const
| {
| return IndexedRoR!(LightConstOf!Collection, LightConstOf!Range)(.lightConst(c), .lightConst(r));
| }
|
| /// Input range primitives
| auto front()() @property
| {
| import mir.ndslice.slice: isSlice, sliced;
| import mir.ndslice.topology: indexed;
| import std.traits: ForeachType;
| static if (isSlice!(ForeachType!Collection))
390| return r.indexed(c.front);
| else
| return r.indexed(c.front.sliced);
| }
|
| /// ditto
| void popFront() scope
| {
381| c.popFront;
| }
|
| /// ditto
| bool empty()() @property scope const
| {
50| return c.empty;
| }
|
| static if (hasLength!Collection)
| {
| /// ditto
| @property size_t length()() scope const
| {
13| return c.length;
| }
|
| ///
| @property size_t[2] shape()() scope const
| {
109| return c.shape;
| }
| }
|
| static if (__traits(hasMember, Collection, "save"))
| {
| /// Forward range primitive. Calls `collection.save`.
| typeof(this) save()() @property
| {
5| return IndexedRoR!(Collection, Range)(c.save, r);
| }
| }
|}
|
|///
|@safe pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.fuse;
|
1| auto perms = 2.permutations;
1| assert(perms.save.fuse == [[0, 1], [1, 0]]);
|
1| auto projection = perms.indexedRoR([1, 2]);
1| assert(projection.fuse == [[1, 2], [2, 1]]);
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| // import mir.ndslice.topology: only;
|
1| auto projectionD = 2.permutations.indexedRoR("ab"d);
1| assert(projectionD.fuse == [['a', 'b'], ['b', 'a']]);
|
| // auto projectionC = 2.permutations.indexedRoR(only('a', 'b'));
| // assert(projectionC.fuse == [['a', 'b'], ['b', 'a']]);
|}
|
|@safe pure nothrow version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import std.range: dropOne;
|
1| auto perms = 2.permutations;
1| auto projection = perms.indexedRoR([1, 2]);
1| assert(projection.length == 2);
|
| // can save
1| assert(projection.save.dropOne.front == [2, 1]);
1| assert(projection.front == [1, 2]);
|}
|
|@safe nothrow @nogc version(mir_test) unittest
|{
| import mir.algorithm.iteration: all;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.fuse;
| static perms = 2.permutations;
| static immutable projectionArray = [1, 2];
1| auto projection = perms.indexedRoR(projectionArray);
|
| static immutable result = [1, 2,
| 2, 1];
1| assert(result.sliced(2, 2).all!"a == b"(projection));
|}
|
|/**
|Lazily computes all _permutations of `r` using $(WEB
|en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
|
|While generating a new item is in `O(k)` (amortized `O(1)`),
|the number of permutations is `|n|!`.
|
|Params:
| n = number of elements (`|r|`)
| r = random access field. A field may not have iteration primitivies.
| alloc = custom Allocator
|
|Returns:
| Forward range, which yields the permutations
|
|See_Also:
| $(LREF Permutations)
|*/
|Permutations!T permutations(T = uint)(size_t n) @safe pure nothrow
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
15| assert(n, "must have at least one item");
14| return Permutations!T(new T[n-1], new T[n]);
|}
|
|/// ditto
|IndexedRoR!(Permutations!T, Range) permutations(T = uint, Range)(Range r) @safe pure nothrow
| if (__traits(compiles, Range.init[size_t.init]))
|{
4| return permutations!T(r.length).indexedRoR(r);
|}
|
|/// ditto
|Permutations!T makePermutations(T = uint, Allocator)(auto ref Allocator alloc, size_t n)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
2| assert(n, "must have at least one item");
| import std.experimental.allocator: makeArray;
1| auto state = alloc.makeArray!T(n - 1);
1| auto indices = alloc.makeArray!T(n);
1| return Permutations!T(state, indices);
|}
|
|/**
|Lazy Forward range of permutations using $(WEB
|en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
|
|It always generates the permutations from 0 to `n - 1`,
|use $(LREF indexedRoR) to map it to your range.
|
|Generating a new item is in `O(k)` (amortized `O(1)`),
|the total number of elements is `n^k`.
|
|See_Also:
| $(LREF permutations), $(LREF makePermutations)
|*/
|struct Permutations(T)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
| import mir.ndslice.slice: sliced, Slice;
|
| private T[] indices, state;
| private bool _empty;
| private size_t _max_states = 1, _pos;
|
| ///
| alias DeepElement = const T;
|
| /**
| state should have the length of `n - 1`,
| whereas the length of indices should be `n`
| */
15| this()(T[] state, T[] indices) @safe pure nothrow @nogc
| {
15| assert(state.length + 1 == indices.length);
| // iota
213| foreach (i, ref index; indices)
42| index = cast(T)i;
15| state[] = 0;
|
15| this.indices = indices;
15| this.state = state;
|
15| _empty = indices.length == 0;
|
| // factorial
171| foreach (i; 1..indices.length + 1)
42| _max_states *= i;
| }
|
| /// Input range primitives
| @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
| {
| import mir.ndslice.slice: sliced;
33| return indices.sliced;
| }
|
| /// ditto
| void popFront()() scope @safe pure nothrow @nogc
| {
| import std.algorithm.mutation : swapAt;
|
30| assert(!empty);
30| _pos++;
|
46| for (T h = 0;;h++)
| {
46| if (h + 2 > indices.length)
| {
10| _empty = true;
10| break;
| }
|
72| indices.swapAt((h & 1) ? 0 : state[h], h + 1);
|
36| if (state[h] == h + 1)
| {
16| state[h] = 0;
16| continue;
| }
20| state[h]++;
20| break;
| }
| }
|
| /// ditto
| @property bool empty()() @safe pure nothrow @nogc scope const
| {
38| return _empty;
| }
|
| /// ditto
| @property size_t length()() @safe pure nothrow @nogc scope const
| {
25| return _max_states - _pos;
| }
|
| ///
| @property size_t[2] shape()() scope const
| {
19| return [length, indices.length];
| }
|
| /// Forward range primitive. Allocates using GC.
| @property Permutations save()() @safe pure nothrow
| {
3| typeof(this) c = this;
3| c.indices = indices.dup;
3| c.state = state.dup;
3| return c;
| }
|}
|
|///
|pure @safe nothrow version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology : iota;
|
1| auto expectedRes = [[0, 1, 2],
| [1, 0, 2],
| [2, 0, 1],
| [0, 2, 1],
| [1, 2, 0],
| [2, 1, 0]];
|
1| auto r = iota(3);
1| auto rp = permutations(r.length).indexedRoR(r);
1| assert(rp.fuse == expectedRes);
|
| // direct style
1| auto rp2 = iota(3).permutations;
1| assert(rp2.fuse == expectedRes);
|}
|
|///
|@nogc version(mir_test) unittest
|{
| import mir.algorithm.iteration: equal;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology : iota;
|
| import std.experimental.allocator.mallocator;
|
| static immutable expected2 = [0, 1, 1, 0];
1| auto r = iota(2);
1| auto rp = makePermutations(Mallocator.instance, r.length);
1| assert(expected2.sliced(2, 2).equal(rp.indexedRoR(r)));
1| dispose(Mallocator.instance, rp);
|}
|
|pure @safe nothrow version(mir_test) unittest
|{
| // is copyable?
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
| import std.range: dropOne;
1| auto a = iota(2).permutations;
1| assert(a.front == [0, 1]);
1| assert(a.save.dropOne.front == [1, 0]);
1| assert(a.front == [0, 1]);
|
| // length
1| assert(1.permutations.length == 1);
1| assert(2.permutations.length == 2);
1| assert(3.permutations.length == 6);
1| assert(4.permutations.length == 24);
1| assert(10.permutations.length == 3_628_800);
|}
|
|version (assert)
|version(mir_test) unittest
|{
| // check invalid
| import std.exception: assertThrown;
| import core.exception: AssertError;
| import std.experimental.allocator.mallocator: Mallocator;
|
2| assertThrown!AssertError(0.permutations);
2| assertThrown!AssertError(Mallocator.instance.makePermutations(0));
|}
|
|/**
|Disposes a Permutations object. It destroys and then deallocates the
|Permutations object pointed to by a pointer.
|It is assumed the respective entities had been allocated with the same allocator.
|
|Params:
| alloc = Custom allocator
| perm = Permutations object
|
|See_Also:
| $(LREF makePermutations)
|*/
|void dispose(T, Allocator)(auto ref Allocator alloc, auto ref Permutations!T perm)
|{
| import std.experimental.allocator: dispose;
1| dispose(alloc, perm.state);
1| dispose(alloc, perm.indices);
|}
|
|/**
|Lazily computes the Cartesian power of `r` with itself
|for a number of repetitions `D repeat`.
|If the input is sorted, the product is in lexicographic order.
|
|While generating a new item is in `O(k)` (amortized `O(1)`),
|the total number of elements is `n^k`.
|
|Params:
| n = number of elements (`|r|`)
| r = random access field. A field may not have iteration primitivies.
| repeat = number of repetitions
| alloc = custom Allocator
|
|Returns:
| Forward range, which yields the product items
|
|See_Also:
| $(LREF CartesianPower)
|*/
|CartesianPower!T cartesianPower(T = uint)(size_t n, size_t repeat = 1) @safe pure nothrow
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
26| assert(repeat >= 1, "Invalid number of repetitions");
25| return CartesianPower!T(n, new T[repeat]);
|}
|
|/// ditto
|IndexedRoR!(CartesianPower!T, Range) cartesianPower(T = uint, Range)(Range r, size_t repeat = 1)
|if (isUnsigned!T && __traits(compiles, Range.init[size_t.init]))
|{
13| assert(repeat >= 1, "Invalid number of repetitions");
13| return cartesianPower!T(r.length, repeat).indexedRoR(r);
|}
|
|/// ditto
|CartesianPower!T makeCartesianPower(T = uint, Allocator)(auto ref Allocator alloc, size_t n, size_t repeat)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
2| assert(repeat >= 1, "Invalid number of repetitions");
| import std.experimental.allocator: makeArray;
1| return CartesianPower!T(n, alloc.makeArray!T(repeat));
|}
|
|/**
|Lazy Forward range of Cartesian Power.
|It always generates Cartesian Power from 0 to `n - 1`,
|use $(LREF indexedRoR) to map it to your range.
|
|Generating a new item is in `O(k)` (amortized `O(1)`),
|the total number of elements is `n^k`.
|
|See_Also:
| $(LREF cartesianPower), $(LREF makeCartesianPower)
|*/
|struct CartesianPower(T)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
| import mir.ndslice.slice: Slice;
|
| private T[] _state;
| private size_t n;
| private size_t _max_states, _pos;
|
| ///
| alias DeepElement = const T;
|
| /// state should have the length of `repeat`
26| this()(size_t n, T[] state) @safe pure nothrow @nogc
| {
26| assert(state.length >= 1, "Invalid number of repetitions");
|
| import std.math: pow;
26| this.n = n;
26| assert(n <= T.max);
26| this._state = state;
|
26| _max_states = pow(n, state.length);
| }
|
| /// Input range primitives
| @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
| {
| import mir.ndslice.slice: sliced;
79| return _state.sliced;
| }
|
| /// ditto
| void popFront()() scope @safe pure nothrow @nogc
| {
77| assert(!empty);
77| _pos++;
|
| /*
| * Bitwise increment - starting from back
| * It works like adding 1 in primary school arithmetic.
| * If a block has reached the number of elements, we reset it to
| * 0, and continue to increment, e.g. for n = 2:
| *
| * [0, 0, 0] -> [0, 0, 1]
| * [0, 1, 1] -> [1, 0, 0]
| */
483| foreach_reverse (i, ref el; _state)
| {
106| ++el;
106| if (el < n)
66| break;
|
40| el = 0;
| }
| }
|
| /// ditto
| @property size_t length()() @safe pure nothrow @nogc scope const
| {
36| return _max_states - _pos;
| }
|
| /// ditto
| @property bool empty()() @safe pure nothrow @nogc scope const
| {
87| return _pos == _max_states;
| }
|
| ///
| @property size_t[2] shape()() scope const
| {
22| return [length, _state.length];
| }
|
| /// Forward range primitive. Allocates using GC.
| @property CartesianPower save()() @safe pure nothrow
| {
1| typeof(this) c = this;
1| c._state = _state.dup;
1| return c;
| }
|}
|
|///
|pure nothrow @safe version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
1| assert(iota(2).cartesianPower.fuse == [[0], [1]]);
1| assert(iota(2).cartesianPower(2).fuse == [[0, 0], [0, 1], [1, 0], [1, 1]]);
|
1| auto three_nums_two_bins = [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]];
1| assert(iota(3).cartesianPower(2).fuse == three_nums_two_bins);
|
1| assert("AB"d.cartesianPower(2).fuse == ["AA"d, "AB"d, "BA"d, "BB"d]);
|}
|
|///
|@nogc version(mir_test) unittest
|{
| import mir.ndslice.topology: iota;
| import mir.algorithm.iteration: equal;
| import mir.ndslice.slice: sliced;
|
| import std.experimental.allocator.mallocator: Mallocator;
1| auto alloc = Mallocator.instance;
|
| static immutable expected2r2 = [
| 0, 0,
| 0, 1,
| 1, 0,
| 1, 1];
1| auto r = iota(2);
1| auto rc = alloc.makeCartesianPower(r.length, 2);
1| assert(expected2r2.sliced(4, 2).equal(rc.indexedRoR(r)));
1| alloc.dispose(rc);
|}
|
|pure nothrow @safe version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.array.allocation: array;
| import mir.ndslice.topology: iota;
| import std.range: dropOne;
|
1| assert(iota(0).cartesianPower.length == 0);
1| assert("AB"d.cartesianPower(3).fuse == ["AAA"d, "AAB"d, "ABA"d, "ABB"d, "BAA"d, "BAB"d, "BBA"d, "BBB"d]);
1| auto expected = ["AA"d, "AB"d, "AC"d, "AD"d,
| "BA"d, "BB"d, "BC"d, "BD"d,
| "CA"d, "CB"d, "CC"d, "CD"d,
| "DA"d, "DB"d, "DC"d, "DD"d];
1| assert("ABCD"d.cartesianPower(2).fuse == expected);
| // verify with array too
1| assert("ABCD"d.cartesianPower(2).fuse == expected);
|
1| assert(iota(2).cartesianPower.front == [0]);
|
| // is copyable?
1| auto a = iota(2).cartesianPower;
1| assert(a.front == [0]);
1| assert(a.save.dropOne.front == [1]);
1| assert(a.front == [0]);
|
| // test length shrinking
1| auto d = iota(2).cartesianPower;
1| assert(d.length == 2);
1| d.popFront;
1| assert(d.length == 1);
|}
|
|version(assert)
|version(mir_test) unittest
|{
| // check invalid
| import std.exception: assertThrown;
| import core.exception: AssertError;
| import std.experimental.allocator.mallocator : Mallocator;
|
2| assertThrown!AssertError(0.cartesianPower(0));
2| assertThrown!AssertError(Mallocator.instance.makeCartesianPower(0, 0));
|}
|
|// length
|pure nothrow @safe version(mir_test) unittest
|{
1| assert(1.cartesianPower(1).length == 1);
1| assert(1.cartesianPower(2).length == 1);
1| assert(2.cartesianPower(1).length == 2);
1| assert(2.cartesianPower(2).length == 4);
1| assert(2.cartesianPower(3).length == 8);
1| assert(3.cartesianPower(1).length == 3);
1| assert(3.cartesianPower(2).length == 9);
1| assert(3.cartesianPower(3).length == 27);
1| assert(3.cartesianPower(4).length == 81);
1| assert(4.cartesianPower(10).length == 1_048_576);
1| assert(14.cartesianPower(7).length == 105_413_504);
|}
|
|/**
|Disposes a CartesianPower object. It destroys and then deallocates the
|CartesianPower object pointed to by a pointer.
|It is assumed the respective entities had been allocated with the same allocator.
|
|Params:
| alloc = Custom allocator
| cartesianPower = CartesianPower object
|
|See_Also:
| $(LREF makeCartesianPower)
|*/
|void dispose(T = uint, Allocator)(auto ref Allocator alloc, auto ref CartesianPower!T cartesianPower)
|{
| import std.experimental.allocator: dispose;
1| dispose(alloc, cartesianPower._state);
|}
|
|/**
|Lazily computes all k-combinations of `r`.
|Imagine this as the $(LREF cartesianPower) filtered for only strictly ordered items.
|
|While generating a new combination is in `O(k)`,
|the number of combinations is `binomial(n, k)`.
|
|Params:
| n = number of elements (`|r|`)
| r = random access field. A field may not have iteration primitivies.
| k = number of combinations
| alloc = custom Allocator
|
|Returns:
| Forward range, which yields the k-combinations items
|
|See_Also:
| $(LREF Combinations)
|*/
|Combinations!T combinations(T = uint)(size_t n, size_t k = 1) @safe pure nothrow
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
41| assert(k >= 1, "Invalid number of combinations");
40| return Combinations!T(n, new T[k]);
|}
|
|/// ditto
|IndexedRoR!(Combinations!T, Range) combinations(T = uint, Range)(Range r, size_t k = 1)
|if (isUnsigned!T && __traits(compiles, Range.init[size_t.init]))
|{
25| assert(k >= 1, "Invalid number of combinations");
25| return combinations!T(r.length, k).indexedRoR(r);
|}
|
|/// ditto
|Combinations!T makeCombinations(T = uint, Allocator)(auto ref Allocator alloc, size_t n, size_t repeat)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
2| assert(repeat >= 1, "Invalid number of repetitions");
| import std.experimental.allocator: makeArray;
1| return Combinations!T(cast(T) n, alloc.makeArray!T(cast(T) repeat));
|}
|
|/**
|Lazy Forward range of Combinations.
|It always generates combinations from 0 to `n - 1`,
|use $(LREF indexedRoR) to map it to your range.
|
|Generating a new combination is in `O(k)`,
|the number of combinations is `binomial(n, k)`.
|
|See_Also:
| $(LREF combinations), $(LREF makeCombinations)
|*/
|struct Combinations(T)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
| import mir.ndslice.slice: Slice;
|
| private T[] state;
| private size_t n;
| private size_t max_states, pos;
|
| ///
| alias DeepElement = const T;
|
| /// state should have the length of `repeat`
41| this()(size_t n, T[] state) @safe pure nothrow @nogc
| {
| import mir.ndslice.topology: iota;
|
41| assert(state.length <= T.max);
41| this.n = n;
41| assert(n <= T.max);
41| this.max_states = cast(size_t) binomial(n, state.length);
41| this.state = state;
|
| // set initial state and calculate max possibilities
41| if (n > 0)
| {
| // skip first duplicate
75| if (n > 1 && state.length > 1)
| {
29| auto iotaResult = iota(state.length);
547| foreach (i, ref el; state)
| {
115| el = cast(T) iotaResult[i];
| }
| }
| }
| }
|
| /// Input range primitives
| @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
| {
| import mir.ndslice.slice: sliced;
122| return state.sliced;
| }
|
| /// ditto
| void popFront()() scope @safe pure nothrow @nogc
| {
120| assert(!empty);
120| pos++;
| // we might have bumped into the end state now
140| if (empty) return;
|
| // Behaves like: do _getNextState(); while (!_state.isStrictlySorted);
100| size_t i = state.length - 1;
| /* Go from the back to next settable block
| * - A must block must be lower than it's previous
| * - A state i is not settable if it's maximum height is reached
| *
| * Think of it as a backwords search on state with
| * iota(_repeat + d, _repeat + d) as search mask.
| * (d = _nrElements -_repeat)
| *
| * As an example n = 3, r = 2, iota is [1, 2] and hence:
| * [0, 1] -> i = 2
| * [0, 2] -> i = 1
| */
180| while (state[i] == n - state.length + i)
| {
80| i--;
| }
100| state[i] = cast(T)(state[i] + 1);
|
| /* Starting from our changed block, we need to take the change back
| * to the end of the state array and update them by their new diff.
| * [0, 1, 4] -> [0, 2, 3]
| * [0, 3, 4] -> [1, 2, 3]
| */
540| foreach (j; i + 1 .. state.length)
| {
80| state[j] = cast(T)(state[i] + j - i);
| }
| }
|
| /// ditto
| @property size_t length()() @safe pure nothrow @nogc scope const
| {
60| return max_states - pos;
| }
|
| /// ditto
| @property bool empty()() @safe pure nothrow @nogc scope const
| {
259| return pos == max_states;
| }
|
| ///
| @property size_t[2] shape()() scope const
| {
40| return [length, state.length];
| }
|
| /// Forward range primitive. Allocates using GC.
| @property Combinations save()() @safe pure nothrow
| {
1| typeof(this) c = this;
1| c.state = state.dup;
1| return c;
| }
|}
|
|///
|pure nothrow @safe version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
1| assert(iota(3).combinations(2).fuse == [[0, 1], [0, 2], [1, 2]]);
1| assert("AB"d.combinations(2).fuse == ["AB"d]);
1| assert("ABC"d.combinations(2).fuse == ["AB"d, "AC"d, "BC"d]);
|}
|
|///
|@nogc version(mir_test) unittest
|{
| import mir.algorithm.iteration: equal;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: iota;
|
| import std.experimental.allocator.mallocator;
1| auto alloc = Mallocator.instance;
|
| static immutable expected3r2 = [
| 0, 1,
| 0, 2,
| 1, 2];
1| auto r = iota(3);
1| auto rc = alloc.makeCombinations(r.length, 2);
1| assert(expected3r2.sliced(3, 2).equal(rc.indexedRoR(r)));
1| alloc.dispose(rc);
|}
|
|pure nothrow @safe version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.array.allocation: array;
| import mir.ndslice.topology: iota;
| import std.range: dropOne;
|
1| assert(iota(0).combinations.length == 0);
1| assert(iota(2).combinations.fuse == [[0], [1]]);
|
1| auto expected = ["AB"d, "AC"d, "AD"d, "BC"d, "BD"d, "CD"d];
1| assert("ABCD"d.combinations(2).fuse == expected);
| // verify with array too
1| assert("ABCD"d.combinations(2).fuse == expected);
1| assert(iota(2).combinations.front == [0]);
|
| // is copyable?
1| auto a = iota(2).combinations;
1| assert(a.front == [0]);
1| assert(a.save.dropOne.front == [1]);
1| assert(a.front == [0]);
|
| // test length shrinking
1| auto d = iota(2).combinations;
1| assert(d.length == 2);
1| d.popFront;
1| assert(d.length == 1);
|
| // test larger combinations
1| auto expected5 = [[0, 1, 2], [0, 1, 3], [0, 1, 4],
| [0, 2, 3], [0, 2, 4], [0, 3, 4],
| [1, 2, 3], [1, 2, 4], [1, 3, 4],
| [2, 3, 4]];
1| assert(iota(5).combinations(3).fuse == expected5);
1| assert(iota(4).combinations(3).fuse == [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]);
1| assert(iota(3).combinations(3).fuse == [[0, 1, 2]]);
1| assert(iota(2).combinations(3).length == 0);
1| assert(iota(1).combinations(3).length == 0);
|
1| assert(iota(3).combinations(2).fuse == [[0, 1], [0, 2], [1, 2]]);
1| assert(iota(2).combinations(2).fuse == [[0, 1]]);
1| assert(iota(1).combinations(2).length == 0);
|
1| assert(iota(1).combinations(1).fuse == [[0]]);
|}
|
|pure nothrow @safe version(mir_test) unittest
|{
| // test larger combinations
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
|
1| auto expected6r4 = [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 2, 5],
| [0, 1, 3, 4], [0, 1, 3, 5], [0, 1, 4, 5],
| [0, 2, 3, 4], [0, 2, 3, 5], [0, 2, 4, 5],
| [0, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5],
| [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]];
1| assert(iota(6).combinations(4).fuse == expected6r4);
|
1| auto expected6r3 = [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 1, 5],
| [0, 2, 3], [0, 2, 4], [0, 2, 5], [0, 3, 4],
| [0, 3, 5], [0, 4, 5], [1, 2, 3], [1, 2, 4],
| [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5],
| [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]];
1| assert(iota(6).combinations(3).fuse == expected6r3);
|
1| auto expected6r2 = [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5],
| [1, 2], [1, 3], [1, 4], [1, 5], [2, 3],
| [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]];
1| assert(iota(6).combinations(2).fuse == expected6r2);
|
1| auto expected7r5 = [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5], [0, 1, 2, 3, 6],
| [0, 1, 2, 4, 5], [0, 1, 2, 4, 6], [0, 1, 2, 5, 6],
| [0, 1, 3, 4, 5], [0, 1, 3, 4, 6], [0, 1, 3, 5, 6],
| [0, 1, 4, 5, 6], [0, 2, 3, 4, 5], [0, 2, 3, 4, 6],
| [0, 2, 3, 5, 6], [0, 2, 4, 5, 6], [0, 3, 4, 5, 6],
| [1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 5, 6],
| [1, 2, 4, 5, 6], [1, 3, 4, 5, 6], [2, 3, 4, 5, 6]];
1| assert(iota(7).combinations(5).fuse == expected7r5);
|}
|
|// length
|pure nothrow @safe version(mir_test) unittest
|{
1| assert(1.combinations(1).length == 1);
1| assert(1.combinations(2).length == 0);
1| assert(2.combinations(1).length == 2);
1| assert(2.combinations(2).length == 1);
1| assert(2.combinations(3).length == 0);
1| assert(3.combinations(1).length == 3);
1| assert(3.combinations(2).length == 3);
1| assert(3.combinations(3).length == 1);
1| assert(3.combinations(4).length == 0);
1| assert(4.combinations(10).length == 0);
1| assert(14.combinations(11).length == 364);
1| assert(20.combinations(7).length == 77_520);
1| assert(30.combinations(10).length == 30_045_015);
1| assert(30.combinations(15).length == 155_117_520);
|}
|
|version(assert)
|version(mir_test) unittest
|{
| // check invalid
| import std.exception: assertThrown;
| import core.exception: AssertError;
| import std.experimental.allocator.mallocator: Mallocator;
|
2| assertThrown!AssertError(0.combinations(0));
2| assertThrown!AssertError(Mallocator.instance.makeCombinations(0, 0));
|}
|
|/**
|Disposes a Combinations object. It destroys and then deallocates the
|Combinations object pointed to by a pointer.
|It is assumed the respective entities had been allocated with the same allocator.
|
|Params:
| alloc = Custom allocator
| perm = Combinations object
|
|See_Also:
| $(LREF makeCombinations)
|*/
|void dispose(T, Allocator)(auto ref Allocator alloc, auto ref Combinations!T perm)
|{
| import std.experimental.allocator: dispose;
1| dispose(alloc, perm.state);
|}
|
|/**
|Lazily computes all k-combinations of `r` with repetitions.
|A k-combination with repetitions, or k-multicombination,
|or multisubset of size k from a set S is given by a sequence of k
|not necessarily distinct elements of S, where order is not taken into account.
|Imagine this as the cartesianPower filtered for only ordered items.
|
|While generating a new combination with repeats is in `O(k)`,
|the number of combinations with repeats is `binomial(n + k - 1, k)`.
|
|Params:
| n = number of elements (`|r|`)
| r = random access field. A field may not have iteration primitivies.
| k = number of combinations
| alloc = custom Allocator
|
|Returns:
| Forward range, which yields the k-multicombinations items
|
|See_Also:
| $(LREF CombinationsRepeat)
|*/
|CombinationsRepeat!T combinationsRepeat(T = uint)(size_t n, size_t k = 1) @safe pure nothrow
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
37| assert(k >= 1, "Invalid number of combinations");
36| return CombinationsRepeat!T(n, new T[k]);
|}
|
|/// ditto
|IndexedRoR!(CombinationsRepeat!T, Range) combinationsRepeat(T = uint, Range)(Range r, size_t k = 1)
| if (isUnsigned!T && __traits(compiles, Range.init[size_t.init]))
|{
21| assert(k >= 1, "Invalid number of combinations");
21| return combinationsRepeat!T(r.length, k).indexedRoR(r);
|}
|
|/// ditto
|CombinationsRepeat!T makeCombinationsRepeat(T = uint, Allocator)(auto ref Allocator alloc, size_t n, size_t repeat)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
2| assert(repeat >= 1, "Invalid number of repetitions");
| import std.experimental.allocator: makeArray;
1| return CombinationsRepeat!T(n, alloc.makeArray!T(repeat));
|}
|
|/**
|Lazy Forward range of combinations with repeats.
|It always generates combinations with repeats from 0 to `n - 1`,
|use $(LREF indexedRoR) to map it to your range.
|
|Generating a new combination with repeats is in `O(k)`,
|the number of combinations with repeats is `binomial(n, k)`.
|
|See_Also:
| $(LREF combinationsRepeat), $(LREF makeCombinationsRepeat)
|*/
|struct CombinationsRepeat(T)
| if (isUnsigned!T && T.sizeof <= size_t.sizeof)
|{
| import mir.ndslice.slice: Slice;
|
| private T[] state;
| private size_t n;
| private size_t max_states, pos;
|
| ///
| alias DeepElement = const T;
|
| /// state should have the length of `repeat`
37| this()(size_t n, T[] state) @safe pure nothrow @nogc
| {
37| this.n = n;
37| assert(n <= T.max);
37| this.state = state;
37| size_t repeatLen = state.length;
|
| // set initial state and calculate max possibilities
37| if (n > 0)
| {
36| max_states = cast(size_t) binomial(n + repeatLen - 1, repeatLen);
| }
| }
|
| /// Input range primitives
| @property Slice!(const(T)*) front()() @safe pure nothrow @nogc
| {
| import mir.ndslice.slice: sliced;
168| return state.sliced;
| }
|
| /// ditto
| void popFront()() scope @safe pure nothrow @nogc
| {
166| assert(!empty);
166| pos++;
|
166| immutable repeat = state.length;
|
| // behaves like: do _getNextState(); while (!_state.isSorted);
166| size_t i = repeat - 1;
| // go to next settable block
| // a block is settable if its not in the end state (=nrElements - 1)
385| while (state[i] == n - 1 && i != 0)
| {
100| i--;
| }
166| state[i] = cast(T)(state[i] + 1);
|
| // if we aren't at the last block, we need to set all blocks
| // to equal the current one
| // e.g. [0, 2] -> (upper block: [1, 2]) -> [1, 1]
166| if (i != repeat - 1)
| {
354| for (size_t j = i + 1; j < repeat; j++)
100| state[j] = state[i];
| }
| }
|
| /// ditto
| @property size_t length()() @safe pure nothrow @nogc scope const
| {
55| return max_states - pos;
| }
|
| /// ditto
| @property bool empty()() @safe pure nothrow @nogc scope const
| {
184| return pos == max_states;
| }
|
| ///
| @property size_t[2] shape()() scope const
| {
38| return [length, state.length];
| }
|
| /// Forward range primitive. Allocates using GC.
| @property CombinationsRepeat save()() @safe pure nothrow
| {
1| typeof(this) c = this;
1| c.state = state.dup;
1| return c;
| }
|}
|
|///
|pure nothrow @safe version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
|
1| assert(iota(2).combinationsRepeat.fuse == [[0], [1]]);
1| assert(iota(2).combinationsRepeat(2).fuse == [[0, 0], [0, 1], [1, 1]]);
1| assert(iota(3).combinationsRepeat(2).fuse == [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]);
1| assert("AB"d.combinationsRepeat(2).fuse == ["AA"d, "AB"d, "BB"d]);
|}
|
|///
|@nogc version(mir_test) unittest
|{
| import mir.algorithm.iteration: equal;
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: iota;
|
| import std.experimental.allocator.mallocator;
1| auto alloc = Mallocator.instance;
|
| static immutable expected3r1 = [
| 0,
| 1,
| 2];
1| auto r = iota(3);
1| auto rc = alloc.makeCombinationsRepeat(r.length, 1);
1| assert(expected3r1.sliced(3, 1).equal(rc.indexedRoR(r)));
1| alloc.dispose(rc);
|}
|
|version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.array.allocation: array;
| import mir.ndslice.topology: iota;
| import std.range: dropOne;
|
1| assert(iota(0).combinationsRepeat.length == 0);
1| assert("AB"d.combinationsRepeat(3).fuse == ["AAA"d, "AAB"d, "ABB"d,"BBB"d]);
|
1| auto expected = ["AA"d, "AB"d, "AC"d, "AD"d, "BB"d, "BC"d, "BD"d, "CC"d, "CD"d, "DD"d];
1| assert("ABCD"d.combinationsRepeat(2).fuse == expected);
| // verify with array too
1| assert("ABCD"d.combinationsRepeat(2).fuse == expected);
|
1| assert(iota(2).combinationsRepeat.front == [0]);
|
| // is copyable?
1| auto a = iota(2).combinationsRepeat;
1| assert(a.front == [0]);
1| assert(a.save.dropOne.front == [1]);
1| assert(a.front == [0]);
|
| // test length shrinking
1| auto d = iota(2).combinationsRepeat;
1| assert(d.length == 2);
1| d.popFront;
1| assert(d.length == 1);
|}
|
|// length
|pure nothrow @safe version(mir_test) unittest
|{
1| assert(1.combinationsRepeat(1).length == 1);
1| assert(1.combinationsRepeat(2).length == 1);
1| assert(2.combinationsRepeat(1).length == 2);
1| assert(2.combinationsRepeat(2).length == 3);
1| assert(2.combinationsRepeat(3).length == 4);
1| assert(3.combinationsRepeat(1).length == 3);
1| assert(3.combinationsRepeat(2).length == 6);
1| assert(3.combinationsRepeat(3).length == 10);
1| assert(3.combinationsRepeat(4).length == 15);
1| assert(4.combinationsRepeat(10).length == 286);
1| assert(11.combinationsRepeat(14).length == 1_961_256);
1| assert(20.combinationsRepeat(7).length == 657_800);
1| assert(20.combinationsRepeat(10).length == 20_030_010);
1| assert(30.combinationsRepeat(10).length == 635_745_396);
|}
|
|pure nothrow @safe version(mir_test) unittest
|{
| // test larger combinations
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
|
1| auto expected3r1 = [[0], [1], [2]];
1| assert(iota(3).combinationsRepeat(1).fuse == expected3r1);
|
1| auto expected3r2 = [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]];
1| assert(iota(3).combinationsRepeat(2).fuse == expected3r2);
|
1| auto expected3r3 = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1],
| [0, 1, 2], [0, 2, 2], [1, 1, 1], [1, 1, 2],
| [1, 2, 2], [2, 2, 2]];
1| assert(iota(3).combinationsRepeat(3).fuse == expected3r3);
|
1| auto expected3r4 = [[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 2],
| [0, 0, 1, 1], [0, 0, 1, 2], [0, 0, 2, 2],
| [0, 1, 1, 1], [0, 1, 1, 2], [0, 1, 2, 2],
| [0, 2, 2, 2], [1, 1, 1, 1], [1, 1, 1, 2],
| [1, 1, 2, 2], [1, 2, 2, 2], [2, 2, 2, 2]];
1| assert(iota(3).combinationsRepeat(4).fuse == expected3r4);
|
1| auto expected4r3 = [[0, 0, 0], [0, 0, 1], [0, 0, 2],
| [0, 0, 3], [0, 1, 1], [0, 1, 2],
| [0, 1, 3], [0, 2, 2], [0, 2, 3],
| [0, 3, 3], [1, 1, 1], [1, 1, 2],
| [1, 1, 3], [1, 2, 2], [1, 2, 3],
| [1, 3, 3], [2, 2, 2], [2, 2, 3],
| [2, 3, 3], [3, 3, 3]];
1| assert(iota(4).combinationsRepeat(3).fuse == expected4r3);
|
1| auto expected4r2 = [[0, 0], [0, 1], [0, 2], [0, 3],
| [1, 1], [1, 2], [1, 3], [2, 2],
| [2, 3], [3, 3]];
1| assert(iota(4).combinationsRepeat(2).fuse == expected4r2);
|
1| auto expected5r3 = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4],
| [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 2],
| [0, 2, 3], [0, 2, 4], [0, 3, 3], [0, 3, 4], [0, 4, 4],
| [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4], [1, 2, 2],
| [1, 2, 3], [1, 2, 4], [1, 3, 3], [1, 3, 4], [1, 4, 4],
| [2, 2, 2], [2, 2, 3], [2, 2, 4], [2, 3, 3], [2, 3, 4],
| [2, 4, 4], [3, 3, 3], [3, 3, 4], [3, 4, 4], [4, 4, 4]];
1| assert(iota(5).combinationsRepeat(3).fuse == expected5r3);
|
1| auto expected5r2 = [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
| [1, 1], [1, 2], [1, 3], [1, 4], [2, 2],
| [2, 3], [2, 4], [3, 3], [3, 4], [4, 4]];
1| assert(iota(5).combinationsRepeat(2).fuse == expected5r2);
|}
|
|version(assert)
|version(mir_test) unittest
|{
| // check invalid
| import std.exception: assertThrown;
| import core.exception: AssertError;
| import std.experimental.allocator.mallocator: Mallocator;
|
2| assertThrown!AssertError(0.combinationsRepeat(0));
2| assertThrown!AssertError(Mallocator.instance.makeCombinationsRepeat(0, 0));
|}
|
|/**
|Disposes a CombinationsRepeat object. It destroys and then deallocates the
|CombinationsRepeat object pointed to by a pointer.
|It is assumed the respective entities had been allocated with the same allocator.
|
|Params:
| alloc = Custom allocator
| perm = CombinationsRepeat object
|
|See_Also:
| $(LREF makeCombinationsRepeat)
|*/
|void dispose(T, Allocator)(auto ref Allocator alloc, auto ref CombinationsRepeat!T perm)
|{
| import std.experimental.allocator: dispose;
1| dispose(alloc, perm.state);
|}
source/mir/combinatorics/package.d is 100% covered
<<<<<< EOF
# path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-math-package.lst
|/++
|$(H1 Math Functionality)
|$(BOOKTABLE $(H2 Math modules),
|$(TR $(TH Module) $(TH Math kind))
|$(T2M common, Common math functions)
|$(T2M constant, Constants)
|$(T2M ieee, Basic IEEE-754 numerical functions)
|)
|Macros:
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, math, $1)$(NBSP)
|T2M=$(TR $(TDNW $(MREF mir,math,$1)) $(TD $+))
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.math;
|
|public import mir.math.common;
|public import mir.math.constant;
|public import mir.math.ieee;
../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/math/package.d has no code
<<<<<< EOF
# path=./source-mir-ndslice-fuse.lst
|/++
|This is a submodule of $(MREF mir,ndslice).
|
|Allocation routines that construct ndslices from ndranges.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilya Yaroshenko
|
|See_also: $(SUBMODULE concatenation) submodule.
|
|Macros:
|SUBMODULE = $(MREF_ALTTEXT $1, mir, ndslice, $1)
|SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.ndslice.fuse;
|
|import mir.internal.utility;
|import mir.ndslice.slice;
|import mir.primitives;
|import mir.qualifier;
|import std.meta;
|import std.traits;
|
|/++
|Fuses ndrange `r` into GC-allocated ($(LREF fuse)) or RC-allocated ($(LREF rcfuse)) ndslice.
|Can be used to join rows or columns into a matrix.
|
|Params:
| Dimensions = (optional) indices of dimensions to be brought to the first position
|Returns:
| ndslice
|+/
|alias fuse(Dimensions...) = fuseImpl!(false, void, Dimensions);
|/// ditto
|alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions);
|
|///
|@safe pure version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.slice : Contiguous, Slice;
| import mir.ndslice.topology: iota;
| import mir.rc.array: RCI;
|
| enum ror = [
| [0, 1, 2, 3],
| [4, 5, 6, 7],
| [8, 9,10,11]];
|
| // 0 1 2 3
| // 4 5 6 7
| // 8 9 10 11
1| auto matrix = ror.fuse;
|
2| auto rcmatrix = ror.rcfuse; // nogc version
|
1| assert(matrix == [3, 4].iota);
1| assert(rcmatrix == [3, 4].iota);
| static assert(ror.fuse == [3, 4].iota); // CTFE-able
|
| // matrix is contiguos
| static assert(is(typeof(matrix) == Slice!(int*, 2)));
| static assert(is(typeof(rcmatrix) == Slice!(RCI!int, 2)));
|}
|
|/// Transposed
|@safe pure version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
| import mir.ndslice.dynamic: transposed;
| import mir.ndslice.slice : Contiguous, Slice;
|
| enum ror = [
| [0, 1, 2, 3],
| [4, 5, 6, 7],
| [8, 9,10,11]];
|
| // 0 4 8
| // 1 5 9
| // 2 6 10
| // 3 7 11
|
| // `!1` brings dimensions under index 1 to the front (0 index).
1| auto matrix = ror.fuse!1;
|
1| assert(matrix == [3, 4].iota.transposed!1);
| // TODO: CTFE
| // static assert(ror.fuse!1 == [3, 4].iota.transposed!1); // CTFE-able
| // matrix is contiguos
| static assert(is(typeof(matrix) == Slice!(int*, 2)));
|}
|
|/// 3D
|@safe pure version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: iota;
| import mir.ndslice.dynamic: transposed;
|
1| auto ror =
| [[[ 0, 1, 2, 3],
| [ 4, 5, 6, 7]],
| [[ 8, 9,10,11],
| [12,13,14,15]]];
|
1| auto nd = [2, 2, 4].iota;
|
1| assert(ror.fuse == nd);
1| assert(ror.fuse!2 == nd.transposed!2);
1| assert(ror.fuse!(1, 2) == nd.transposed!(1, 2));
1| assert(ror.fuse!(2, 1) == nd.transposed!(2, 1));
|}
|
|/// Work with RC Arrays of RC Arrays
|@safe pure version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.slice;
| import mir.ndslice.topology: map;
| import mir.rc.array;
|
| Slice!(const(double)*, 2) conv(RCArray!(const RCArray!(const double)) a)
| {
0000000| return a[].map!"a[]".fuse;
| }
|}
|
|/++
|Fuses ndrange `r` into GC-allocated ($(LREF fuseAs)) or RC-allocated ($(LREF rcfuseAs)) ndslice.
|Can be used to join rows or columns into a matrix.
|
|Params:
| T = output type of ndslice elements
| Dimensions = (optional) indices of dimensions to be brought to the first position
|Returns:
| ndslice
|+/
|alias fuseAs(T, Dimensions...) = fuseImpl!(false, T, Dimensions);
|/// ditto
|alias rcfuseAs(T, Dimensions...) = fuseImpl!(true, T, Dimensions);
|
|///
|@safe pure version(mir_test) unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.slice : Contiguous, Slice;
| import mir.ndslice.topology: iota;
| import mir.rc.array: RCI;
|
| enum ror = [
| [0, 1, 2, 3],
| [4, 5, 6, 7],
| [8, 9,10,11]];
|
| // 0 1 2 3
| // 4 5 6 7
| // 8 9 10 11
1| auto matrix = ror.fuseAs!double;
|
2| auto rcmatrix = ror.rcfuseAs!double; // nogc version
|
1| assert(matrix == [3, 4].iota);
1| assert(rcmatrix == [3, 4].iota);
| static assert(ror.fuseAs!double == [3, 4].iota); // CTFE-able
|
| // matrix is contiguos
| static assert(is(typeof(matrix) == Slice!(double*, 2)));
| static assert(is(typeof(rcmatrix) == Slice!(RCI!double, 2)));
|}
|
|///
|template fuseImpl(bool RC, T_, Dimensions...)
|{
| import mir.ndslice.internal: isSize_t, toSize_t;
| static if (allSatisfy!(isSize_t, Dimensions))
| /++
| Params:
| r = parallelotope (ndrange) with length/shape and input range primitives.
| +/
| auto fuseImpl(NDRange)(NDRange r)
| if (hasShape!NDRange)
| {
| import mir.conv: emplaceRef;
| import mir.algorithm.iteration: each;
| import mir.ndslice.allocation;
108| auto shape = fuseShape(r);
| static if (is(T_ == void))
| alias T = FuseElementType!NDRange;
| else
| alias T = T_;
| alias UT = Unqual!T;
| static if (RC)
| {
| import mir.rc.array: RCI;
| alias R = Slice!(RCI!T, fuseDimensionCount!NDRange);
4| Slice!(RCI!UT, fuseDimensionCount!NDRange) ret;
| }
| else
| {
| alias R = Slice!(T*, fuseDimensionCount!NDRange);
106| Slice!(UT*, fuseDimensionCount!NDRange) ret;
| }
| static if (Dimensions.length)
| {
| import mir.ndslice.topology: iota;
| import mir.ndslice.dynamic: transposed, completeTranspose;
| enum perm = completeTranspose!(shape.length)([Dimensions]);
10| size_t[shape.length] shapep;
| foreach(i; Iota!(shape.length))
23| shapep[i] = shape[perm[i]];
| // enum iperm = perm.length.iota[completeTranspose!(shape.length)([Dimensions])[].sliced].slice;
| alias InverseDimensions = aliasSeqOf!(
| (size_t[] perm){
| auto ar = new size_t[perm.length];
| ar.sliced[perm.sliced] = perm.length.iota;
| return ar;
| }(perm)
| );
| static if (RC)
| {
| ret = shapep.uninitRcslice!UT;
| ret.lightScope.transposed!InverseDimensions.each!(emplaceRef!T)(r);
| }
| else
| {
10| if (__ctfe)
| {
0000000| ret = shapep.slice!UT;
0000000| ret.transposed!InverseDimensions.each!"a = b"(r);
| }
| else
| {
10| ret = shapep.uninitSlice!UT;
10| ret.transposed!InverseDimensions.each!(emplaceRef!T)(r);
| }
|
| }
| }
| else
| {
| static if (RC)
| {
2| ret = shape.uninitRCslice!UT;
2| ret.lightScope.each!(emplaceRef!T)(r);
| }
| else
| {
96| if (__ctfe)
| {
0000000| ret = shape.slice!UT;
0000000| ret.each!"a = b"(r);
| }
| else
| {
96| ret = shape.uninitSlice!UT;
96| ret.each!(emplaceRef!T)(r);
| }
| }
| }
| static if (RC)
| {
| import core.lifetime: move;
4| return move(*(() @trusted => cast(R*)&ret)());
| }
| else
| {
212| return *(() @trusted => cast(R*)&ret)();
| }
| }
| else
| alias fuseImpl = .fuseImpl!(RC, T_, staticMap!(toSize_t, Dimensions));
|}
|
|private template fuseDimensionCount(R)
|{
| static if (is(typeof(R.init.shape) : size_t[N], size_t N) && (isDynamicArray!R || __traits(hasMember, R, "front")))
| {
| import mir.ndslice.topology: repeat;
| enum size_t fuseDimensionCount = N + fuseDimensionCount!(DeepElementType!R);
| }
| else
| enum size_t fuseDimensionCount = 0;
|}
|
|private static immutable shapeExceptionMsg = "fuseShape Exception: elements have different shapes/lengths";
|
|version(D_Exceptions)
| static immutable shapeException = new Exception(shapeExceptionMsg);
|
|/+
|TODO docs
|+/
|size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r)
| if (hasShape!Range)
|{
| // auto outerShape = r.shape;
| enum N = r.shape.length;
| enum RN = typeof(return).length;
| enum M = RN - N;
| static if (M == 0)
| {
466| return r.shape;
| }
| else
| {
| import mir.ndslice.topology: repeat;
| typeof(return) ret;
| ret[0 .. N] = r.shape;
| if (!ret[0 .. N].anyEmptyShape)
| {
| ret[N .. $] = fuseShape(mixin("r" ~ ".front".repeat(N).fuseCells.field));
| import mir.algorithm.iteration: all;
| if (!all!((a) => cast(size_t[M]) ret[N .. $] == .fuseShape(a))(r))
| {
| version (D_Exceptions)
| throw shapeException;
| else
| assert(0, shapeExceptionMsg);
| }
| }
| return ret;
| }
|}
|
|private template FuseElementType(NDRange)
|{
| import mir.ndslice.topology: repeat;
| alias FuseElementType = typeof(mixin("NDRange.init" ~ ".front".repeat(fuseDimensionCount!NDRange).fuseCells.field));
|}
|
|/++
|Fuses `cells` into GC-allocated ndslice.
|
|Params:
| cells = ndrange of ndcells, ndrange and ndcell should have `shape` and multidimensional input range primivies (`front!d`, `empty!d`, `popFront!d`).
|Returns: ndslice composed of fused cells.
|See_also: $(SUBREF chunks, chunks)
|+/
|auto fuseCells(S)(S cells)
|{
| alias T = DeepElementType!(DeepElementType!S);
| alias UT = Unqual!T;
2| if (__ctfe)
| {
| import mir.ndslice.allocation: slice;
0000000| auto ret = cells.fuseCellsShape.slice!UT;
0000000| ret.fuseCellsAssign!"a = b" = cells;
| static if (is(T == immutable))
0000000| return (() @trusted => cast(immutable) ret)()[];
| else
| static if (is(T == const))
| return (() @trusted => cast(const) ret)()[];
| else
0000000| return ret;
| }
| else
| {
| import mir.ndslice.allocation: uninitSlice;
| import mir.conv;
2| auto ret = cells.fuseCellsShape.uninitSlice!UT;
2| ret.fuseCellsAssign!(emplaceRef!T) = cells;
| alias R = Slice!(T*, ret.N);
4| return R(ret._structure, (() @trusted => cast(T*)ret._iterator)());
| }
|}
|
|/// 1D
|@safe pure version(mir_test) unittest
|{
| import mir.ndslice.topology: iota;
| enum ar = [[0, 1], [], [2, 3, 4, 5], [6], [7, 8, 9]];
| static assert ([[0, 1], [], [2, 3, 4, 5], [6], [7, 8, 9]].fuseCells == 10.iota);
1| assert (ar.fuseCells == 10.iota);
|}
|
|/// 2D
|@safe pure version(mir_test) unittest
|{
| import mir.ndslice.topology: iota;
| import mir.ndslice.chunks;
|
1| auto sl = iota(11, 17);
1| assert(sl.chunks!(0, 1)(3, 4).fuseCells == sl);
|}
|
|/+
|TODO docs
|+/
|auto fuseCellsAssign(alias fun = "a = b", Iterator, size_t N, SliceKind kind, S)(Slice!(Iterator, N, kind) to, S cells)
|{
2| assert(to.shape == cells.fuseCellsShape, "'cells.fuseCellsShape' should be equal to 'to.shape'");
|
2| if (cells.anyEmpty)
0000000| goto R;
|
| import mir.functional: naryFun;
| import mir.ndslice.topology: canonical;
| static if (kind == Contiguous)
2| fuseCellsEmplaceImpl!(naryFun!fun, 0, N)(to.canonical, cells);
| else
| fuseCellsEmplaceImpl!(naryFun!fun, 0, N)(to, cells);
2| R: return to;
|}
|
|/+
|TODO docs
|+/
|size_t[S.init.shape.length] fuseCellsShape(S)(S cells) @property
|{
4| typeof(return) ret;
| enum N = ret.length;
| static if (N == 1)
| {
36| foreach (ref e; cells)
10| ret[0] += e.length;
| }
| else
| {
| import mir.ndslice.topology: repeat;
| enum expr = "e" ~ ".front".repeat(N).fuseCells.field;
| foreach (i; Iota!N)
44| for (auto e = cells.save; !e.empty!i; e.popFront!i)
18| ret[i] += mixin(expr).length!i;
| }
4| return ret;
|}
|
|private auto fuseCellsEmplaceImpl(alias fun, size_t i, size_t M, Iterator, size_t N, SliceKind kind, S)(Slice!(Iterator, N, kind) to, S cells)
|{
| do
| {
29| auto from = cells.front;
| static if (M == 1)
| {
5| auto n = from.length!i;
| }
| else
| {
| import mir.ndslice.topology: repeat;
| enum expr = "from" ~ ".front".repeat(N - 1 - i).fuseCells.field;
24| auto n = mixin(expr).length!i;
| }
29| assert (to.length!i >= n);
| static if (i + 1 == M)
| {
| import mir.algorithm.iteration: each;
25| each!fun(to.selectFront!i(n), from);
| }
| else
| {
4| .fuseCellsEmplaceImpl!(fun, i + 1, N)(to.selectFront!i(n), from);
| }
29| to.popFrontExactly!i(n);
29| cells.popFront;
| }
29| while(!cells.empty);
6| return to;
|}
source/mir/ndslice/fuse.d is 85% covered
<<<<<< EOF
# path=./source-mir-bignum-internal-ryu-generic_128.lst
|// Converted and then optimised from generic_128.h and generic_128.c
|// Copyright 2018 Ulf Adams (original code https://github.com/ulfjack/ryu)
|// Copyright 2020 Ilya Yaroshenko (2020 D conversion and optimisation)
|// License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|
|// This is a generic 128-bit implementation of float to shortest conversion
|// using the Ryu algorithm. It can handle any IEEE-compatible floating-point
|// type up to 128 bits. In order to use this correctly, you must use the
|// appropriate *_to_fd128 function for the underlying type - DO NOT CAST your
|// input to another floating-point type, doing so will result in incorrect
|// output!
|//
|// For any floating-point type that is not natively defined by the compiler,
|// you can use genericBinaryToDecimal to work directly on the underlying bit
|// representation.
|
|module mir.bignum.internal.ryu.generic_128;
|
|version(BigEndian)
| static assert (0, "Let us know if you are using Mir on BigEndian target and we will add support for this module.");
|
|debug(ryu) import core.stdc.stdio;
|
|import mir.bignum.decimal: Decimal;
|import mir.bignum.fixed: UInt, extendedMulHigh, extendedMul;
|
|@safe pure nothrow @nogc:
|
|// Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 32768.
|uint pow5bits(const int e)
|{
| version(LDC) pragma(inline, true);
253| assert(e >= 0);
253| assert(e <= 1 << 15);
253| return cast(uint) (((e * 163391164108059UL) >> 46) + 1);
|}
|
|void mul_128_256_shift(const UInt!128 a, const UInt!256 b, const uint shift, const uint corr, ref UInt!256 result)
|{
| version(LDC) pragma(inline, true);
90| assert(shift > 0);
90| assert(shift < 256);
90| result = (extendedMul(a, b) >> shift).toSize!256 + corr;
|}
|
|// Computes 5^i in the form required by Ryu, and stores it in the given pointer.
|void generic_computePow5(const uint i, ref UInt!256 result)
|{
| version(LDC) pragma(inline, true);
81| const uint base = i / POW5_TABLE_SIZE;
81| const uint base2 = base * POW5_TABLE_SIZE;
81| const mul = UInt!256(GENERIC_POW5_SPLIT[base]);
81| if (i == base2)
| {
1| result = mul;
| }
| else
| {
80| const uint offset = i - base2;
80| const m = UInt!128(GENERIC_POW5_TABLE[offset]);
80| const uint delta = pow5bits(i) - pow5bits(base2);
80| const uint corr = cast(uint) ((POW5_ERRORS[i / 32] >> (2 * (i % 32))) & 3);
80| mul_128_256_shift(m, mul, delta, corr, result);
| }
|}
|
|version(mir_bignum_test) unittest
|{
| // We only test a few entries - we could test the fUL table instead, but should we?
| static immutable uint[10] EXACT_POW5_IDS = [1, 10, 55, 56, 300, 1000, 2345, 3210, 4968 - 3, 4968 - 1];
|
| static immutable ulong[4][10] EXACT_POW5 = [
| [ 0u, 0u, 0u, 90071992547409920u],
| [ 0u, 0u, 0u, 83886080000000000u],
| [ 0u, 15708555500268290048u, 14699724349295723422u, 117549435082228750u],
| [ 0u, 5206161169240293376u, 4575641699882439235u, 73468396926392969u],
| [ 2042133660145364371u, 9702060195405861314u, 6467325284806654637u, 107597969523956154u],
| [15128847313296546509u, 11916317791371073891u, 788593023170869613u, 137108429762886488u],
| [10998857860460266920u, 858411415306315808u, 12732466392391605111u, 136471991906002539u],
| [ 5404652432687674341u, 18039986361557197657u, 2228774284272261859u, 94370442653226447u],
| [15313487127299642753u, 9780770376910163681u, 15213531439620567348u, 93317108016191349u],
| [ 7928436552078881485u, 723697829319983520u, 932817143438521969u, 72903990637649492u],
| ];
|
22| for (int i = 0; i < 10; i++)
| {
10| UInt!256 result;
10| generic_computePow5(EXACT_POW5_IDS[i], result);
10| assert(UInt!256(EXACT_POW5[i]) == result);
| }
|}
|
|// Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
|void generic_computeInvPow5(const uint i, ref UInt!256 result)
|{
| version(LDC) pragma(inline, true);
11| const uint base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE;
11| const uint base2 = base * POW5_TABLE_SIZE;
11| const mul = UInt!256(GENERIC_POW5_INV_SPLIT[base]); // 1/5^base2
11| if (i == base2)
| {
1| result = mul + 1;
| }
| else
| {
10| const uint offset = base2 - i;
10| const m = UInt!128(GENERIC_POW5_TABLE[offset]); // 5^offset
10| const uint delta = pow5bits(base2) - pow5bits(i);
10| const uint corr = cast(uint) ((POW5_INV_ERRORS[i / 32] >> (2 * (i % 32))) & 3) + 1;
10| mul_128_256_shift(m, mul, delta, corr, result);
| }
|}
|
|version(mir_bignum_test) unittest
|{
| static immutable uint[9] EXACT_INV_POW5_IDS = [10, 55, 56, 300, 1000, 2345, 3210, 4897 - 3, 4897 - 1];
|
| static immutable ulong[4][10] EXACT_INV_POW5 = [
| [13362655651931650467u, 3917988799323120213u, 9037289074543890586u, 123794003928538027u],
| [ 983662216614650042u, 15516934687640009097u, 8839031818921249472u, 88342353238919216u],
| [ 1573859546583440066u, 2691002611772552616u, 6763753280790178510u, 141347765182270746u],
| [ 1607391579053635167u, 943946735193622172u, 10726301928680150504u, 96512915280967053u],
| [ 7238603269427345471u, 17319296798264127544u, 14852913523241959878u, 75740009093741608u],
| [ 2734564866961569744u, 13277212449690943834u, 17231454566843360565u, 76093223027199785u],
| [ 5348945211244460332u, 14119936934335594321u, 15647307321253222579u, 110040743956546595u],
| [ 2848579222248330872u, 15087265905644220040u, 4449739884766224405u, 100774177495370196u],
| [ 1432572115632717323u, 9719393440895634811u, 3482057763655621045u, 128990947194073851u],
| ];
|
20| for (int i = 0; i < 9; i++)
| {
9| UInt!256 result;
9| generic_computeInvPow5(EXACT_