.circleci/config.yml
.github/workflows/ci.yml
.github/workflows/compilers.json
.gitignore
.gitmodules
LICENSE
NOTICE
README.md
bigint_benchmark/dub.sdl
bigint_benchmark/source/app.d
cpp_example/eye.d
cpp_example/init_rcarray.d
cpp_example/main.cpp
cpp_example/meson.build
dub.sdl
images/kaleidic.jpeg
images/symmetry.png
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/ion.d
source/mir/algebraic_alias/json.d
source/mir/algebraic_alias/transform.d
source/mir/algorithm/iteration.d
source/mir/algorithm/setops.d
source/mir/annotated.d
source/mir/appender.d
source/mir/array/allocation.d
source/mir/base64.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/dec2float.d
source/mir/bignum/internal/dec2float_table.d
source/mir/bignum/internal/kernel.d
source/mir/bignum/internal/parse.d
source/mir/bignum/internal/phobos_kernel.d
source/mir/bignum/internal/ryu/generic_128.d
source/mir/bignum/internal/ryu/table.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/ediff.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/extrapolate.d
source/mir/interpolate/generic.d
source/mir/interpolate/linear.d
source/mir/interpolate/mod.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/func/hermite.d
source/mir/math/func/normal.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/stdio.d
source/mir/string.d
source/mir/string_map.d
source/mir/test.d
source/mir/timestamp.d
source/mir/type_info.d
subprojects/mir-core.wrap
<<<<<< network
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-algebraic.lst
|/++
|$(H2 Variant and Nullable types)
|
|This module implements a
|$(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union)
|type (a.k.a.
|$(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union),
|$(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)).
|Such types are useful
|for type-uniform binary interfaces, interfacing with scripting
|languages, and comfortable exploratory programming.
|
|The module defines generic $(LREF Algebraic) type that contains a payload.
|The allowed types of the paylad are defined by the unordered $(LREF TypeSet).
|
|$(LREF Algebraic) template accepts two arguments: self type set id and a list of type sets.
|
|$(BOOKTABLE $(H3 $(LREF Algebraic) Aliases),
|$(TR $(TH Name) $(TH Description))
|$(T2 Variant, an algebraic type)
|$(T2 TaggedVariant, a tagged algebraic type)
|$(T2 Nullable, an algebraic type with at least `typeof(null)`)
|)
|
|$(BOOKTABLE $(H3 Visitor Handlers),
|$(TR $(TH Name) $(TH Ensures can match) $(TH Throws if no match) $(TH Returns $(LREF Nullable)) $(TH Multiple dispatch) $(TH Argumments count) $(TH Fuses Algebraic types on return))
|$(LEADINGROWN 8, Classic handlers)
|$(T8 visit, Yes, N/A, No, No, 1+, No)
|$(T8 optionalVisit, No, No, Yes, No, 1+, No)
|$(T8 autoVisit, No, No, auto, No, 1+, No)
|$(T8 tryVisit, No, Yes, No, No, 1+, No)
|$(LEADINGROWN 8, Multiple dispatch and algebraic fusion on return)
|$(T8 match, Yes, N/A, No, Yes, 0+, Yes)
|$(T8 optionalMatch, No, No, Yes, Yes, 0+, Yes)
|$(T8 autoMatch, No, No, auto, Yes, 0+, Yes)
|$(T8 tryMatch, No, Yes, No, Yes, 0+, Yes)
|$(LEADINGROWN 8, Inner handlers. Multiple dispatch and algebraic fusion on return.)
|$(T8 suit, N/A(Yes), N/A, No, Yes, ?, Yes)
|$(T8 some, N/A(Yes), N/A, No, Yes, 0+, Yes)
|$(T8 none, N/A(Yes), N/A, No, Yes, 1+, Yes)
|$(T8 assumeOk, Yes(No), No(Yes), No(Yes), Yes(No), 0+, Yes(No))
|$(LEADINGROWN 8, Member access)
|$(T8 getMember, Yes, N/A, No, No, 1+, No)
|$(T8 optionalGetMember, No, No, Yes, No, 1+, No)
|$(T8 autoGetMember, No, No, auto, No, 1+, No)
|$(T8 tryGetMember, No, Yes, No, No, 1+, No)
|$(LEADINGROWN 8, Member access with algebraic fusion on return)
|$(T8 matchMember, Yes, N/A, No, No, 1+, Yes)
|$(T8 optionalMatchMember, No, No, Yes, No, 1+, Yes)
|$(T8 autoMatchMember, No, No, auto, No, 1+, Yes)
|$(T8 tryMatchMember, No, Yes, No, No, 1+, Yes)
|)
|
|$(BOOKTABLE $(H3 Special Types),
|$(TR $(TH Name) $(TH Description))
|$(T2plain `void`, It is usefull to indicate a possible return type of the visitor. Can't be accesed by reference. )
|$(T2plain `typeof(null)`, It is usefull for nullable types. Also, it is used to indicate that a visitor can't match the current value of the algebraic. Can't be accesed by reference. )
|$(T2 This, Dummy structure that is used to construct self-referencing algebraic types. Example: `Variant!(int, double, string, This*[2])`)
|$(T2plain $(LREF SetAlias)`!setId`, Dummy structure that is used to construct cyclic-referencing lists of algebraic types. )
|$(T2 Err, Wrapper to denote an error value type. )
|$(T2 reflectErr, Attribute that denotes that the type is an error value type. )
|)
|
|$(BOOKTABLE $(H3 $(LREF Algebraic) Traits),
|$(TR $(TH Name) $(TH Description))
|$(T2 isVariant, Checks if the type is instance of $(LREF Algebraic).)
|$(T2 isNullable, Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. )
|$(T2 isTypeSet, Checks if the types are the same as $(LREF TypeSet) of them. )
|$(T2 ValueTypeOfNullable, Gets type of $(LI $(LREF .Algebraic.get.2)) method. )
|$(T2 SomeVariant, Gets subtype of algebraic without types for which $(LREF isErr) is true.)
|$(T2 NoneVariant, Gets subtype of algebraic with types for which $(LREF isErr) is true.)
|$(T2 isErr, Checks if T is a instance of $(LREF Err) or if it is annotated with $(LREF reflectErr).)
|$(T2 isResultVariant, Checks if T is a Variant with at least one allowed type that satisfy $(LREF isErr) traits.)
|
|)
|
|
|$(H3 Type Set)
|$(UL
|$(LI Type set is unordered. Example:`TypeSet!(int, double)` and `TypeSet!(double, int)` are the same. )
|$(LI Duplicats are ignored. Example: `TypeSet!(float, int, float)` and `TypeSet!(int, float)` are the same. )
|$(LI Types are automatically unqualified if this operation can be performed implicitly. Example: `TypeSet!(const int) and `TypeSet!int` are the same. )
|$(LI Non trivial `TypeSet!(A, B, ..., etc)` is allowed.)
|$(LI Trivial `TypeSet!T` is allowed.)
|$(LI Empty `TypeSet!()` is allowed.)
|)
|
|$(H3 Visitors)
|$(UL
|$(LI Visitors are allowed to return values of different types If there are more then one return type then the an $(LREF Algebraic) type is returned. )
|$(LI Visitors are allowed to accept additional arguments. The arguments can be passed to the visitor handler. )
|$(LI Multiple visitors can be passes to the visitor handler. )
|$(LI Visitors are matched according to the common $(HTTPS dlang.org/spec/function.html#function-overloading, Dlang Function Overloading) rules. )
|$(LI Visitors are allowed accept algebraic value by reference except the value of `typeof(null)`. )
|$(LI Visitors are called without algebraic value if its algebraic type is `void`. )
|$(LI If the visitors arguments has known types, then such visitors should be passed to a visitor handler before others to make the compiler happy. This includes visitors with no arguments, which is used to match `void` type. )
|)
|
|$(H3 Implementation Features)
|$(UL
|$(LI BetterC support. Runtime `TypeInfo` is not used.)
|$(LI Copy-constructors and postblit constructors are supported. )
|$(LI `toHash`, `opCmp`. `opEquals`, and `toString` support. )
|$(LI No string or template mixins are used. )
|$(LI Optimised for fast execution. )
|$(LI $(LREF some) / $(LREF none) idiom. )
|)
|
|See_also: $(HTTPS en.wikipedia.org/wiki/Algebra_of_sets, Algebra of sets).
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki
|
|Macros:
|T2plain=$(TR $(TDNW $1) $(TD $+))
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
|T8=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4) $(TD $5) $(TD $6) $(TD $7) $(TD $8))
|
|+/
|module mir.algebraic;
|
|import mir.internal.meta;
|import mir.functional: naryFun;
|
|/++
|The attribute is used to define a permanent member field in an anlgebraic type.
|Should applied to a field of the union passed to $(LREF TaggedVariant).
|+/
|enum algMeta;
|/++
|The attribute is used in pair with $(LREF algMeta) to exclude the field
|from compression in `toHash`, `opEquals`, and `opCmp` methods.
|+/
|enum algTransp;
|/++
|The attribute is used in pair with $(LREF algMeta) to use the field
|as an error infomration. Usually it is a position marker in a file.
|The type should have `scope const` `toString` method.
|+/
|enum algVerbose;
|
|private static immutable variantExceptionMsg = "mir.algebraic: the algebraic stores other type then requested.";
|private static immutable variantNullExceptionMsg = "mir.algebraic: the algebraic is empty and doesn't store any value.";
|private static immutable variantMemberExceptionMsg = "mir.algebraic: the algebraic stores a type that isn't compatible with the user provided visitor and arguments.";
|
|version (D_Exceptions)
|{
| private static immutable variantException = new Exception(variantExceptionMsg);
| private static immutable variantNullException = new Exception(variantNullExceptionMsg);
| private static immutable variantMemberException = new Exception(variantMemberExceptionMsg);
|}
|
|private static struct _Null()
|{
|@safe pure nothrow @nogc const:
| int opCmp(_Null) { return 0; }
| this(typeof(null)) inout {}
| string toString() { return "null"; }
|}
|
|private static struct _Void()
|{
| @safe pure nothrow @nogc const:
| int opCmp(_Void) { return 0; }
| string toString() { return "void"; }
|}
|
|/++
|Checks if the type is instance of $(LREF Algebraic).
|+/
|enum bool isVariant(T) = is(immutable T == immutable Algebraic!Types, Types...);
|
|///
|@safe pure version(mir_core_test) unittest
|{
| static assert(isVariant!(Variant!(int, string)));
| static assert(isVariant!(const Variant!(int[], string)));
| static assert(isVariant!(Nullable!(int, string)));
| static assert(!isVariant!int);
|}
|
|/++
|Same as $(LREF isVariant), but matches for `alias this` variant types (requires
|DMD FE 2.100.0 or later)
|+/
|enum bool isLikeVariant(T) = !is(immutable T == immutable noreturn)
| && is(immutable T : immutable Algebraic!Types, Types...);
|
|
|static if (__VERSION__ >= 2_100)
|{
| ///
| @safe pure version(mir_core_test) unittest
| {
| static struct CustomVariant
| {
| Variant!(int, string) data;
| alias data this;
| this(T)(T v) { data = v; }
| ref typeof(this) opAssign(T)(T v)
| {
| data = v;
| return this;
| }
| }
|
| static assert(isLikeVariant!(Variant!(int, string)));
| static assert(isLikeVariant!(const Variant!(int[], string)));
| static assert(isLikeVariant!(Nullable!(int, string)));
| static assert(!isLikeVariant!int);
|
| static assert(!isVariant!CustomVariant);
| static assert(isLikeVariant!CustomVariant);
|
| CustomVariant customVariant = 5;
| assert(customVariant.match!(
| (string s) => false,
| (int n) => true
| ));
| }
|}
|
|/++
|Checks if the type is instance of tagged $(LREF Algebraic).
|
|Tagged algebraics can be defined with $(LREF TaggedVariant).
|+/
|enum bool isTaggedVariant(T) = is(immutable T == immutable Algebraic!U, U) && is(U == union);
|
|///
|@safe pure version(mir_core_test) unittest
|{
| static union MyUnion
| {
| int integer;
| immutable(char)[] string;
| }
|
| alias MyAlgebraic = Algebraic!MyUnion;
| static assert(isTaggedVariant!MyAlgebraic);
|
| static assert(!isTaggedVariant!int);
| static assert(!isTaggedVariant!(Variant!(int, string)));
|}
|
|/++
|Same as $(LREF isTaggedVariant), but with support for custom `alias this`
|variants.
|
|Only works since DMD FE 2.100, see $(LREF isLikeVariant).
|+/
|enum bool isLikeTaggedVariant(T) = isLikeVariant!T && is(T.Kind == enum);
|
|/++
|Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`.
|+/
|enum bool isNullable(T) = is(immutable T == immutable Algebraic!(typeof(null), Types), Types...);
|
|///
|@safe pure version(mir_core_test) unittest
|{
| static assert(isNullable!(const Nullable!(int, string)));
| static assert(isNullable!(Nullable!()));
|
| static assert(!isNullable!(Variant!()));
| static assert(!isNullable!(Variant!string));
| static assert(!isNullable!int);
| static assert(!isNullable!string);
|}
|
|/++
|Same as $(LREF isNullable), but with support for custom `alias this` variants.
|
|Only works since DMD FE 2.100, see $(LREF isLikeVariant).
|+/
|enum bool isLikeNullable(T) = !is(immutable T == immutable noreturn)
| && is(immutable T : immutable Algebraic!(typeof(null), Types), Types...);
|
|/++
|Gets type of $(LI $(LREF .Algebraic.get.2)) method.
|+/
|template ValueTypeOfNullable(T : Algebraic!(typeof(null), Types), Types...)
|{
| static if (Types.length == 1)
| alias ValueTypeOfNullable = Types[0];
| else
| alias ValueTypeOfNullable = Algebraic!Types;
|}
|
|///
|@safe pure version(mir_core_test) unittest
|{
| static assert(is(ValueTypeOfNullable!(const Nullable!(int, string)) == Algebraic!(int, string)));
| static assert(is(ValueTypeOfNullable!(Nullable!()) == Algebraic!()));
| static assert(is(typeof(Nullable!().get()) == Algebraic!()));
|}
|
|/++
|Dummy type for $(LREF Variant) and $(LREF Nullable) self-referencing.
|+/
|struct This
|{
|@safe pure nothrow @nogc scope const:
0000000| int opCmp(typeof(this)) { return 0; }
0000000| string toString() { return typeof(this).stringof; }
|}
|
|private template TagInfo(T, string name, udas...)
| if (udas.length <= 3)
|{
| import std.meta: staticIndexOf;
| alias Type = T;
| enum tag = name;
| enum meta = staticIndexOf!(algMeta, udas) >= 0;
| enum transparent = staticIndexOf!(algTransp, udas) >= 0;
| enum verbose = staticIndexOf!(algVerbose, udas) >= 0;
|}
|
|// example from std.variant
|/++
|$(H4 Self-Referential Types)
|A useful and popular use of algebraic data structures is for defining
|$(LUCKY self-referential data structures), i.e. structures that embed references to
|values of their own type within.
|This is achieved with $(LREF Variant) by using $(LREF This) as a placeholder whenever a
|reference to the type being defined is needed. The $(LREF Variant) instantiation
|will perform
|$(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial,
|alpha renaming) on its constituent types, replacing $(LREF This)
|with the self-referenced type. The structure of the type involving $(LREF This) may
|be arbitrarily complex.
|+/
|@safe pure version(mir_core_test) unittest
|{
| import mir.functional: Tuple;
|
| // A tree is either a leaf or a branch of two others
| alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*));
| alias Leafs = Tuple!(Tree!int*, Tree!int*);
|
| Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43));
| Tree!int* right = tree.get!Leafs[1];
| assert(*right == 43);
|}
|
|///
|@safe pure version(mir_core_test) unittest
|{
| // An object is a double, a string, or a hash of objects
| alias Obj = Variant!(double, string, This[string], This[]);
| alias Map = Obj[string];
|
| Obj obj = "hello";
| assert(obj._is!string);
| assert(obj.trustedGet!string == "hello");
| obj = 42.0;
| assert(obj.get!double == 42);
| obj = ["customer": Obj("John"), "paid": Obj(23.95)];
| assert(obj.get!Map["customer"] == "John");
|}
|
|/++
|Type set resolution template used to construct $(LREF Algebraic) .
|+/
|template TypeSet(T...)
|{
| import std.meta: staticSort, staticMap, allSatisfy, anySatisfy;
| // sort types by sizeof and them mangleof
| // but typeof(null) goes first
| static if (is(staticMap!(TryRemoveConst, T) == T))
| static if (is(NoDuplicates!T == T))
| static if (staticIsSorted!(TypeCmp, T))
| {
| alias TypeSet = T;
| }
| else
| alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T));
| else
| alias TypeSet = TypeSet!(NoDuplicates!T);
| else
| alias TypeSet = TypeSet!(staticMap!(TryRemoveConst, T));
|}
|
|// IonNull goes first as well
|private template isIonNull(T)
|{
| static if (is(T == TagInfo!(U, name), U, string name))
| enum isIonNull = .isIonNull!U;
| else
| enum isIonNull = T.stringof == "IonNull";
|}
|
|private template TypeCmp(A, B)
|{
| enum bool TypeCmp = is(A == B) ? false:
| is(A == typeof(null)) ? true:
| is(B == typeof(null)) ? false:
| isIonNull!A ? true:
| isIonNull!B ? false:
| is(A == void) || is(A == TagInfo!(void, vaname), string vaname) ? true:
| is(B == void) || is(A == TagInfo!(void, vbname), string vbname) ? false:
| A.sizeof < B.sizeof ? true:
| A.sizeof > B.sizeof ? false:
| A.mangleof < B.mangleof;
|}
|
|///
|version(mir_core_test) unittest
|{
| static struct S {}
| alias C = S;
| alias Int = int;
| static assert(is(TypeSet!(S, int) == TypeSet!(Int, C)));
| static assert(is(TypeSet!(S, int, int) == TypeSet!(Int, C)));
| static assert(!is(TypeSet!(uint, S) == TypeSet!(int, S)));
|}
|
|private template applyTags(string[] tagNames, T...)
| if (tagNames.length == T.length)
|{
| import std.meta: AliasSeq;
| static if (tagNames.length == 0)
| alias applyTags = AliasSeq!();
| else
| alias applyTags = AliasSeq!(TagInfo!(T[0], tagNames[0]), .applyTags!(tagNames[1 .. $], T[1 .. $]));
|}
|
|/++
|Checks if the type list is $(LREF TypeSet).
|+/
|enum bool isTypeSet(T...) = is(T == TypeSet!T);
|
|///
|@safe pure version(mir_core_test) unittest
|{
| static assert(isTypeSet!(TypeSet!()));
| static assert(isTypeSet!(TypeSet!void));
| static assert(isTypeSet!(TypeSet!(void, int, typeof(null))));
|}
|
|/++
|Variant Type (aka Algebraic Type).
|
|The impllementation is defined as
|----
|alias Variant(T...) = Algebraic!(TypeSet!T);
|----
|
|Compatible with BetterC mode.
|+/
|alias Variant(T...) = Algebraic!(TypeSet!T);
|
|///
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| Variant!(int, double, string) v = 5;
| assert(v.get!int == 5);
| v = 3.14;
| assert(v == 3.14);
| // auto x = v.get!long; // won't compile, type long not allowed
| // v = '1'; // won't compile, type char not allowed
|}
|
|/// Single argument Variant
|// and Type with copy constructor
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| static struct S
| {
| int n;
| this(ref return scope inout S rhs) inout
| {
| this.n = rhs.n + 1;
| }
| }
|
| Variant!S a = S();
| auto b = a;
|
| import mir.conv;
| assert(a.get!S.n == 0);
| assert(b.n == 1); //direct access of a member in case of all algebraic types has this member
|}
|
|/// Empty type set
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| Variant!() a;
| auto b = a;
| assert(a.toHash == 0);
| assert(a == b);
| assert(a <= b && b >= a);
| static assert(typeof(a).sizeof == 1);
|}
|
|/// Small types
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| static struct S { ubyte d; }
| static assert(Nullable!(byte, char, S).sizeof == 2);
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| static struct S { ubyte[3] d; }
| static assert(Nullable!(ushort, wchar, S).sizeof == 6);
|}
|
|// /// opPostMove support
|// @safe pure @nogc nothrow
|// version(mir_core_test) unittest
|// {
|// import std.algorithm.mutation: move;
|
|// static struct S
|// {
|// uint s;
|
|// void opPostMove(const ref S old) nothrow
|// {
|// this.s = old.s + 1;
|// }
|// }
|
|// Variant!S a;
|
|// auto b = a.move;
|// assert(b.s == 1);
|// }
|
|/++
|Tagged Variant Type (aka Tagged Algebraic Type).
|
|Compatible with BetterC mode.
|
|Template has two declarations:
|----
|// and
|template TaggedVariant(T)
| if (is(T == union))
|{
| ...
|}
|----
|
|See_also: $(LREF Variant), $(LREF isTaggedVariant).
|+/
|deprecated ("Use Algebraic!Union instead")
|template TaggedVariant(T)
| if (is(T == union))
|{
| alias TaggedVariant = Algebraic!T;
|}
|
|/// Json Value with styles
|@safe pure
|version(mir_core_test) unittest
|{
| enum Style { block, flow }
|
| static struct SomeMetadata {
| int a;
| @safe pure nothrow @nogc scope
| int opCmp(scope const SomeMetadata rhs) const { return a - rhs.a; }
| }
|
| static struct ParsePosition
| {
| string file, line, column;
|
| void toString()(scope ref W w) scope const
| {
| w.put(file);
| if (line) {
| w.put("("); w.put(line);
| if (column) { w.put(","); w.put(column); }
| w.put(")");
| }
| }
| }
|
| static union Json_
| {
| typeof(null) null_;
| bool boolean;
| long integer;
| double floating;
| // Not, that `string` is't builtin type but an alias in `object.d`
| // So we can use `string` as a name of the string field
| immutable(char)[] string;
| This[] array;
| // commented out to test `opCmp` primitive
| // This[immutable(char)[]] object;
|
| @algMeta:
| bool active;
| SomeMetadata metadata;
| @algTransp:
| Style style;
| @algVerbose ParsePosition position;
| }
|
| alias JsonAlgebraic = Algebraic!Json_;
|
| // typeof(null) has priority
| static assert(JsonAlgebraic.Kind.init == JsonAlgebraic.Kind.null_);
| static assert(JsonAlgebraic.Kind.null_ == 0);
|
| // Kind and AllowedTypes has the same order
| static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.array] == JsonAlgebraic[]));
| static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.boolean] == bool));
| static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.floating] == double));
| static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.integer] == long));
| static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.null_] == typeof(null)));
| // static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.object] == JsonAlgebraic[string]));
|
| JsonAlgebraic v;
| assert(v.kind == JsonAlgebraic.Kind.null_);
|
| v = 1;
| assert(v.kind == JsonAlgebraic.Kind.integer);
| assert(v == 1);
| v = JsonAlgebraic(1);
| assert(v == 1);
| v = v.get!(long, double);
|
| v = "Tagged!";
| // member-based access. Simple!
| assert(v.string == "Tagged!");
| // type-based access
| assert(v.get!string == "Tagged!");
| assert(v.trustedGet!string == "Tagged!");
|
| assert(v.kind == JsonAlgebraic.Kind.string);
|
| assert(v.get!"string" == "Tagged!"); // string-based get
| assert(v.trustedGet!"string" == "Tagged!"); // string-based trustedGet
|
| assert(v.get!(JsonAlgebraic.Kind.string) == "Tagged!"); // Kind-based get
| assert(v.trustedGet!(JsonAlgebraic.Kind.string) == "Tagged!"); // Kind-based trustedGet
|
| // checks
| assert(v._is!string); // type based
| assert(v._is!"string"); // string based
| assert(v._is!(JsonAlgebraic.Kind.string)); //
|
| v = null;
| assert(v.kind == JsonAlgebraic.Kind.null_);
|
| v = [JsonAlgebraic("str"), JsonAlgebraic(4.3)];
|
| assert(v.kind == JsonAlgebraic.Kind.array);
| assert(v.trustedGet!(JsonAlgebraic[])[1].kind == JsonAlgebraic.Kind.floating);
|
| JsonAlgebraic w = v;
| w.style = Style.flow;
| assert(v.style != w.style);
| assert(v == w);
| assert(v <= w);
| assert(v >= w);
| assert(v.toHash == w.toHash);
| w.active = true;
| assert(v != w);
| assert(v.toHash != w.toHash);
| assert(v.get!"array" == w.get!"array");
| assert(v < w);
|}
|
|/// Wrapped algebraic with propogated primitives
|@safe pure
|version(mir_core_test) unittest
|{
| static struct Response
| {
| private union Response_
| {
| double float_;
| immutable(char)[] string;
| Response[] array;
| Response[immutable(char)[]] table;
| }
|
| alias ResponseAlgebraic = Algebraic!Response_;
|
| ResponseAlgebraic data;
| alias Tag = ResponseAlgebraic.Kind;
|
| // propogates opEquals, opAssign, and other primitives
| alias data this;
|
| static foreach (T; ResponseAlgebraic.AllowedTypes)
| this(T v) @safe pure nothrow @nogc { data = v; }
| }
|
| Response v = 3.0;
| assert(v.kind == Response.Tag.float_);
| v = "str";
| assert(v == "str");
|}
|
|/++
|Nullable $(LREF Variant) Type (aka Algebraic Type).
|
|The impllementation is defined as
|----
|alias Nullable(T...) = Variant!(typeof(null), T);
|----
|
|In additional to common algebraic API the following members can be accesssed:
|$(UL
|$(LI $(LREF .Algebraic.isNull))
|$(LI $(LREF .Algebraic.nullify))
|$(LI $(LREF .Algebraic.get.2))
|)
|
|Compatible with BetterC mode.
|+/
|alias Nullable(T...) = Variant!(typeof(null), T);
|
|/// ditto
|Nullable!T nullable(T)(T t)
|{
| import core.lifetime: forward;
| return Nullable!T(forward!t);
|}
|
|/++
|Single type `Nullable`
|+/
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| static assert(is(Nullable!int == Variant!(typeof(null), int)));
|
| Nullable!int a = 5;
| assert(a.get!int == 5);
|
| a.nullify;
| assert(a.isNull);
|
| a = 4;
| assert(!a.isNull);
| assert(a.get == 4);
| assert(a == 4);
| a = 4;
|
| a = null;
| assert(a == null);
|}
|
|/// Empty nullable type set support
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| Nullable!() a;
| auto b = a;
| assert(a.toHash == 0);
| assert(a == b);
| assert(a <= b && b >= a);
| static assert(typeof(a).sizeof == 1);
|}
|
|private bool contains(scope const char[][] names, scope const char[] member)
|@safe pure nothrow @nogc
|{
0000000| foreach (name; names)
0000000| if (name == member)
0000000| return true;
0000000| return false;
|}
|
|/++
|Algebraic implementation.
|For more portable code, it is higly recommeded to don't use this template directly.
|Instead, please use of $(LREF Variant) and $(LREF Nullable), which sort types.
|+/
|struct Algebraic(T__...)
|{
| import mir.internal.meta: getUDAs;
| import core.lifetime: moveEmplace;
| import mir.conv: emplaceRef;
| import mir.reflection: isPublic, hasField, isProperty;
| import std.meta: Filter, AliasSeq, ApplyRight, anySatisfy, allSatisfy, staticMap, templateOr, templateNot, templateAnd;
| import std.traits:
| hasElaborateAssign,
| hasElaborateCopyConstructor,
| hasElaborateDestructor,
| hasMember,
| hasUDA,
| isAggregateType,
| isAssociativeArray,
| isDynamicArray,
| isEqualityComparable,
| isOrderingComparable,
| Largest,
| Unqual
| ;
|
| static if (T__.length != 1)
| {
| private alias Types__ = T__;
| private alias MetaInfo__ = T__[0 .. 0];
| enum immutable(char[][]) metaFieldNames__ = null;
| enum immutable(char[][]) typeFieldNames__ = null;
| }
| else
| static if (!is(T__[0] == union))
| {
| private alias Types__ = T__;
| private alias MetaInfo__ = T__[0 .. 0];
| enum immutable(char[][]) metaFieldNames__ = null;
| enum immutable(char[][]) typeFieldNames__ = null;
| }
| else
| {
| private alias UMTypeInfoOf__(immutable(char)[] member) = TagInfo!(
| typeof(__traits(getMember, T__[0], member)),
| member,
| getUDAs!(T__[0], member, algMeta),
| getUDAs!(T__[0], member, algTransp),
| getUDAs!(T__[0], member, algVerbose),
| );
|
| private alias UMGetType__(alias TI) = TI.Type;
| private enum bool UMGetMeta(alias TI) = TI.meta;
|
| private alias AllInfo__ = staticMap!(UMTypeInfoOf__, __traits(allMembers, T__[0]));
| private alias TypesInfo__ = Filter!(templateNot!UMGetMeta, AllInfo__);
| private alias MetaInfo__ = Filter!(UMGetMeta, AllInfo__);
| private alias Types__ = staticMap!(UMGetType__, TypesInfo__);
|
| /++
| +/
| static immutable char[][] metaFieldNames__ = () {
| immutable(char)[][] ret;
| foreach (T; MetaInfo__)
| ret ~= T.tag;
| return ret;
| } ();
|
| /++
| +/
| static immutable char[][] typeFieldNames__ = () {
| immutable(char)[][] ret;
| foreach (T; TypesInfo__)
| ret ~= T.tag;
| return ret;
| } ();
| }
|
| private enum bool variant_test__ = is(Types__ == AliasSeq!(typeof(null), double));
|
| /++
| Allowed types list
| See_also: $(LREF TypeSet)
| +/
| alias AllowedTypes = AliasSeq!(ReplaceTypeUnless!(.isVariant, .This, Algebraic!T__, Types__));
|
| version(mir_core_test)
| static if (variant_test__)
| ///
| unittest
| {
| import std.meta: AliasSeq;
|
| alias V = Nullable!
| (
| This*,
| string,
| double,
| bool,
| );
|
| static assert(is(V.AllowedTypes == TypeSet!(
| typeof(null),
| bool,
| string,
| double,
| V*)));
| }
|
| static foreach (i, T; MetaInfo__)
| mixin ("MetaInfo__[" ~ i.stringof ~ "].Type " ~ T.tag ~";");
|
| private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes));
|
| private static union Storage__
| {
| _Payload payload;
|
| static foreach (int i, P; _Payload)
| mixin(`alias _member_` ~ i.stringof ~ ` = payload[` ~ i.stringof ~ `];`);
|
| static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))) || is(AllowedTypes == AliasSeq!void))
| ubyte[0] bytes;
| else
| ubyte[Largest!_Payload.sizeof] bytes;
| }
|
| private Storage__ storage__;
|
| static if (AllowedTypes.length > 1)
| {
| static if ((Storage__.alignof & 1) && _Payload.length <= ubyte.max)
| private alias ID__ = ubyte;
| else
| static if ((Storage__.alignof & 2) && _Payload.length <= ushort.max)
| private alias ID__ = ushort;
| else
| // static if (Storage__.alignof & 3)
| private alias ID__ = uint;
| // else
| // private alias ID__ = ulong;
|
| ID__ identifier__;
| }
| else
| {
| private alias ID__ = uint;
| enum ID__ identifier__ = 0;
| }
|
| version (D_Ddoc)
| {
| /++
| Algebraic Kind.
|
| Defined as enum for tagged algebraics and as unsigned for common algebraics.
|
| The Kind enum contains the members defined using tag names.
|
| If the algebraic type is $(LREF Nullable) then the default Kind enum member has zero value and corresponds to `typeof(null)`.
|
| See_also: $(LREF TaggedVariant).
| +/
| enum Kind { _not_me_but_tags_name_list_ }
| }
|
| static if (typeFieldNames__.length)
| {
| version (D_Ddoc){}
| else
| {
| mixin(enumKindText(typeFieldNames__));
|
| }
| }
| else
| {
| version (D_Ddoc){}
| else
| {
| alias Kind = ID__;
| }
| }
|
| /++
| Returns: $(LREF .Algebraic.Kind).
|
| Defined as enum for tagged algebraics and as unsigned for common algebraics.
| See_also: $(LREF TaggedVariant).
| +/
| Kind kind() const @safe pure nothrow @nogc @property
| {
| assert(identifier__ <= Kind.max);
| return cast(Kind) identifier__;
| }
|
| static if (anySatisfy!(hasElaborateDestructor, _Payload))
| ~this() @trusted
| {
| S: switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| static if (hasElaborateDestructor!T)
| {
| case i:
| (*cast(Unqual!(_Payload[i])*)&storage__.payload[i]).__xdtor;
| break S;
| }
| default:
| }
| version(mir_secure_memory)
| storage__.bytes = 0xCC;
| }
|
| // static if (anySatisfy!(hasOpPostMove, _Payload))
| // void opPostMove(const ref typeof(this) old)
| // {
| // S: switch (identifier__)
| // {
| // static foreach (i, T; AllowedTypes)
| // static if (hasOpPostMove!T)
| // {
| // case i:
| // this.storage__.payload[i].opPostMove(old.storage__.payload[i]);
| // return;
| // }
| // default: return;
| // }
| // }
|
| static if (AllowedTypes.length)
| {
| static if (!__traits(compiles, (){ _Payload[0] arg; }))
| {
| @disable this();
| }
|
| static if (allSatisfy!(isDynamicArray, AllowedTypes))
| {
| auto length()() const @property
| {
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| return trustedGet!T().length;
| }
| default: assert(0);
| }
| }
|
| auto length()(size_t length) @property
| {
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| return trustedGet!T().length = length;
| }
| default: assert(0);
| }
| }
|
| alias opDollar(size_t pos : 0) = length;
|
| /// Returns: slice type of `Slice!(IotaIterator!size_t)`
| size_t[2] opSlice(size_t dimension)(size_t i, size_t j) @safe scope const
| if (dimension == 0)
| in(i <= j, "Algebraic.opSlice: the left opSlice boundary must be less than or equal to the right bound.")
| {
| return [i, j];
| }
|
| auto opIndex()(size_t index)
| {
| return this.visit!(a => a[index]);
| }
|
| auto opIndex()(size_t index) const
| {
| return this.visit!(a => a[index]);
| }
|
| auto opIndex()(size_t[2] index)
| {
| auto ret = this;
| S: switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| ret.trustedGet!T() = ret.trustedGet!T()[index[0] .. index[1]];
| break S;
| }
| default: assert(0);
| }
| return ret;
| }
|
| auto opIndexAssign(T)(T value, size_t index)
| {
| return this.tryMatch!((ref array, ref value) => array[index] = value)(value);
| }
| }
| }
|
| /// Construct an algebraic type from its subset.
| this(RhsTypes...)(Algebraic!RhsTypes rhs)
| if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes))
| {
| import core.lifetime: move;
| static if (is(RhsTypes == Types__))
| this = move(rhs);
| else
| {
| static foreach (member; metaFieldNames__)
| static if (Algebraic!RhsTypes.metaFieldNames__.contains(member))
| __traits(getMember, this, member) = move(__traits(getMember, rhs, member));
|
| switch (rhs.identifier__)
| {
| static foreach (i, T; Algebraic!RhsTypes.AllowedTypes)
| {
| case i:
| static if (__traits(compiles, __ctor(move(rhs.trustedGet!T))))
| __ctor(move(rhs.trustedGet!T));
| else
| __ctor(rhs.trustedGet!T);
| return;
| }
| default:
| assert(0, variantMemberExceptionMsg);
| }
| }
| }
|
| version(mir_core_test)
| static if (variant_test__)
| ///
| unittest
| {
| alias Float = Variant!(float, double);
| alias Int = Variant!(long, int);
| alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
|
| Float fp = 3.0;
| Number number = fp; // constructor call
| assert(number == 3.0);
|
| Int integer = 12L;
| number = Number(integer);
| assert(number == 12L);
| }
|
| static if (!allSatisfy!(isCopyable, AllowedTypes))
| @disable this(this);
| else
| static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
| {
| import std.meta: Filter;
| private alias CC_AllowedTypes = Filter!(hasElaborateCopyConstructor, AllowedTypes);
| // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes);
| // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes);
| // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction;
|
| static if (__VERSION__ < 2094)
| private static union _StorageI(uint i)
| {
| _Payload[i] payload;
| ubyte[Storage__.bytes.length] bytes;
| }
|
| static if (allSatisfy!(hasInoutConstruction, CC_AllowedTypes))
| this(return ref scope inout Algebraic rhs) inout
| {
| static foreach (member; metaFieldNames__)
| __traits(getMember, this, member) = __traits(getMember, rhs, member);
|
| static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
| static foreach (int i, T; AllowedTypes)
| static if (!is(T == typeof(null)) && !is(T == void))
| {
| if (identifier__ == i)
| {
| static if (__VERSION__ < 2094)
| {
| storage__.bytes = () inout @trusted {
| auto ret = inout _StorageI!i(rhs.trustedGet!T);
| return ret.bytes;
| } ();
| return;
| }
| else
| {
| storage__ = () inout {
| mixin(`inout Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
| return ret;
| } ();
| return;
| }
| }
| }
| }
| else
| {
| static if (allSatisfy!(hasMutableConstruction, CC_AllowedTypes))
| this(return ref scope Algebraic rhs)
| {
| static foreach (member; metaFieldNames__)
| __traits(getMember, this, member) = __traits(getMember, rhs, member);
|
| static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
| static foreach (int i, T; AllowedTypes)
| static if (!is(T == typeof(null)) && !is(T == void))
| {
| if (identifier__ == i)
| {
| storage__ = () {
| mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
| return ret;
| } ();
| return;
| }
| }
| }
|
| static if (allSatisfy!(hasConstConstruction, CC_AllowedTypes))
| this(return ref scope const Algebraic rhs) const
| {
| static foreach (member; metaFieldNames__)
| __traits(getMember, this, member) = __traits(getMember, rhs, member);
|
| static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
| static foreach (int i, T; AllowedTypes)
| static if (!is(T == typeof(null)) && !is(T == void))
| {
| if (identifier__ == i)
| {
| storage__ = () const {
| mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
| return ret;
| } ();
| return;
| }
| }
| }
|
| static if (allSatisfy!(hasImmutableConstruction, CC_AllowedTypes))
| this(return ref scope immutable Algebraic rhs) immutable
| {
| static foreach (member; metaFieldNames__)
| __traits(getMember, this, member) = __traits(getMember, rhs, member);
|
| static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
| static foreach (int i, T; AllowedTypes)
| static if (!is(T == typeof(null)) && !is(T == void))
| {
| if (identifier__ == i)
| {
| storage__ = () immutable {
| mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
| return ret;
| } ();
| return;
| }
| }
| }
|
| static if (allSatisfy!(hasSemiImmutableConstruction, CC_AllowedTypes))
| this(return ref scope const Algebraic rhs) immutable
| {
| static foreach (member; metaFieldNames__)
| __traits(getMember, this, member) = __traits(getMember, rhs, member);
|
| static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
| static foreach (int i, T; AllowedTypes)
| static if (!is(T == typeof(null)) && !is(T == void))
| {
| if (identifier__ == i)
| {
| storage__ = () const {
| mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
| return ret;
| } ();
| return;
| }
| }
| }
|
| static if (allSatisfy!(hasSemiMutableConstruction, CC_AllowedTypes))
| this(return ref scope const Algebraic rhs)
| {
| static foreach (member; metaFieldNames__)
| __traits(getMember, this, member) = __traits(getMember, rhs, member);
|
| static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
| static foreach (int i, T; AllowedTypes)
| static if (!is(T == typeof(null)) && !is(T == void))
| {
| if (identifier__ == i)
| {
| storage__ = () const {
| mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
| return ret;
| } ();
| return;
| }
| }
| }
| }
| }
|
| /++
| +/
| size_t toHash() scope @trusted const pure nothrow @nogc
| {
| size_t hash;
|
|
| static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
| {
| }
| else{S:
| switch (identifier__)
| {
| import std.traits: isArray;
| static foreach (i, T; AllowedTypes)
| {
| case i: {
| static if (is(T == void))
| hash = i;
| else
| static if (is(T == typeof(null)))
| hash = i;
| else
| static if (typeFieldNames__.length) // force for tagged types
| {
| static if (__traits(hasMember, T, "toHash"))
| hash = trustedGet!T.toHash;
| else
| static if (isArray!T)
| foreach (ref e; trustedGet!T)
| static if (__traits(hasMember, typeof(e), "toHash"))
| hash = hashOf(e.toHash, hash);
| else
| hash = hashOf(e, hash);
| else
| hash = hashOf(trustedGet!T);
| }
| else
| static if (__traits(compiles, hashOf(trustedGet!T.hashOf, i ^ hash)))
| hash = hashOf(trustedGet!T.hashOf, i ^ hash);
| else
| {
| debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof);
| hash = i;
| }
| break S;
| }
| }
| default: assert(0);
| }}
|
| static foreach (i, T; MetaInfo__)
| static if (!T.transparent)
| {
| static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface))
| {{
| scope eqfun = delegate() {
| hash = hashOf(__traits(getMember, this, T.tag), hash);
| };
| trustedAllAttr(eqfun)();
| }}
| else
| hash = hashOf(__traits(getMember, this, T.tag), hash);
| }
| return hash;
| }
|
| ///
| bool opEquals()(scope const Algebraic rhs) scope @trusted const pure nothrow @nogc
| {
| return opEquals(rhs);
| }
|
| /// ditto
| bool opEquals()(scope ref const Algebraic rhs) scope @trusted const pure nothrow @nogc
| {
| static foreach (i, T; MetaInfo__)
| static if (!T.transparent)
| {
| static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface))
| {{
| scope eqfun = delegate() {
| return __traits(getMember, this, T.tag) != __traits(getMember, rhs, T.tag);
| };
| if (trustedAllAttr(eqfun)())
| return false;
| }}
| else
| if (__traits(getMember, this, T.tag) != __traits(getMember, rhs, T.tag))
| return false;
| }
|
| static if (AllowedTypes.length == 0)
| {
| return true;
| }
| else
| {
| if (this.identifier__ != rhs.identifier__)
| return false;
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| static if (is(T == void))
| return rhs._is!void;
| else
| static if (is(T == class) || is(T == interface))
| {{
| scope eqfun = delegate() {
| return this.trustedGet!T == rhs.trustedGet!T;
| };
| return trustedAllAttr(eqfun)();
| }}
| else
| static if (__traits(isFloating, T))
| return this.trustedGet!T == rhs.trustedGet!T || (this.trustedGet!T != this.trustedGet!T && rhs.trustedGet!T != rhs.trustedGet!T);
| else
| return this.trustedGet!T == rhs.trustedGet!T;
| }
| default: assert(0);
| }
| }
| }
|
| /++
| +/
| static if (!anySatisfy!(templateOr!(isAssociativeArray, templateAnd!(isAggregateType, templateNot!hasOpCmp)), staticMap!(basicElementType, AllowedTypes)))
| int opCmp()(auto ref scope const typeof(this) rhs) scope @trusted const pure nothrow @nogc
| {
| static foreach (i, T; MetaInfo__)
| static if (!T.transparent)
| {
| static if (__traits(compiles, __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag))))
| {
| if (auto d = __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag)))
| return d;
| }
| else
| static if (__traits(hasMember, __traits(getMember, this, T.tag), "opCmp") && !is(MetaFieldsTypes[i] == U*, U))
| {
| if (auto d = __traits(getMember, this, T.tag).opCmp(__traits(getMember, rhs, T.tag)))
| return d;
| }
| else
| {
| if (auto d = __traits(getMember, this, T.tag) < __traits(getMember, rhs, T.tag) ? -1 : __traits(getMember, this, T.tag) > __traits(getMember, rhs, T.tag) ? +1 : 0)
| return d;
| }
| }
|
|
| static if (AllowedTypes.length == 0)
| {
| return 0;
| }
| else
| {
| import std.traits: isArray;
| if (auto d = int(this.identifier__) - int(rhs.identifier__))
| return d;
| import std.traits: isArray, isPointer;
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| static if (__traits(hasMember, T, "opCmp") && !isPointer!T)
| {{
| auto ret = this.trustedGet!T.opCmp(rhs.trustedGet!T);
| static if (is(typeof(ret) == int))
| return ret;
| else
| return ret < 0 ? -1 : ret > 0 ? 1 : 0;
| }}
| else
| static if (!isArray!T)
| return this.trustedGet!T < rhs.trustedGet!T ? -1 :
| this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
| else
| return __cmp(trustedGet!T, rhs.trustedGet!T);
| }
| default: assert(0);
| }
| }
| }
|
| /// Requires mir-algorithm package
| immutable(char)[] toString()() @trusted pure scope const
| {
| static if (AllowedTypes.length == 0)
| {
| return "Algebraic";
| }
| else
| {
| import mir.conv: to;
| immutable(char)[] ret;
| static foreach (i, member; metaFieldNames__)
| {
| static if (__traits(compiles, { auto s = to!(immutable(char)[])(__traits(getMember, this, member));}))
| // should be passed by value to workaround compiler bug
| ret ~= to!(immutable(char)[])(__traits(getMember, this, member));
| else
| ret ~= AllowedTypes[i].stringof;
| ret ~= ", ";
| }
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| static if (is(T == void))
| ret ~= "void";
| else
| static if (is(T == typeof(null)))
| ret ~= "null";
| else
| static if (__traits(compiles, { auto s = to!(immutable(char)[])(trustedGet!T);}))
| // should be passed by value to workaround compiler bug
| ret ~= to!(immutable(char)[])(trustedGet!T);
| else
| ret ~= AllowedTypes[i].stringof;
| return ret;
| }
| default: assert(0);
| }
| }
| }
|
| ///ditto
| void toString(W)(ref scope W w) scope const @trusted pure
| if (__traits(compiles, ()pure{ w.put("Algebraic"); }))
| {
| if (false)
| return w.put("Algebraic");
| static if (AllowedTypes.length == 0)
| {
| return w.put("Algebraic");
| }
| else
| {
| import mir.format: print;
| static foreach (i, member; metaFieldNames__)
| {
| static if (__traits(compiles, { import mir.format: print; print(w, __traits(getMember, this, member)); }))
| { import mir.format: print; print(w, __traits(getMember, this, member)); }
| else
| w.put(AllowedTypes[i].stringof);
| w.put(", ");
| }
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| static if (is(T == void))
| w.put("void");
| else
| static if (is(T == typeof(null)))
| w.put("null");
| else
| static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); }))
| toStringImpl!T(w);
| else
| w.put(AllowedTypes[i].stringof);
| return;
| }
| default: assert(0);
| }
| }
| }
|
| ///ditto
| void toString(W)(ref scope W w) scope const @trusted
| if (!__traits(compiles, ()pure{ w.put("Algebraic"); }))
| {
| if (false)
| return w.put("Algebraic");
| static if (AllowedTypes.length == 0)
| {
| return w.put("Algebraic");
| }
| else
| {
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| static if (is(T == void))
| return w.put("void");
| else
| static if (is(T == typeof(null)))
| return w.put("null");
| else
| static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); }))
| return toStringImpl!T(w);
| else
| return w.put(AllowedTypes[i].stringof);
| }
| default: assert(0);
| }
| }
| }
|
| private void toStringImpl(T, W)(ref scope W w) @safe scope const pure nothrow @nogc
| {
| import mir.format: print;
| scope pfun = delegate() {
| print(w, trustedGet!T);
| };
| trustedAllAttr(pfun)();
| }
|
| static if (is(AllowedTypes[0] == typeof(null)))
| {
| ///
| bool opCast(C)() const
| if (is(C == bool))
| {
| return identifier__ != 0;
| }
|
| ///
| Algebraic opCast(C)() const
| if (is(C == Algebraic))
| {
| return this;
| }
|
| /// Defined if the first type is `typeof(null)`
| bool isNull() const @property { return identifier__ == 0; }
| /// ditto
| void nullify() { this = null; }
|
| /// ditto
| auto get()()
| if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2 && is(AllowedTypes[0] == typeof(null)))
| {
| import mir.utility: _expect;
| if (_expect(!identifier__, false))
| {
| throw variantNullException;
| }
| static if (AllowedTypes.length != 2)
| {
| Algebraic!(AllowedTypes[1 .. $]) ret;
| S: switch (identifier__)
| {
| static foreach (i, T; AllowedTypes[1 .. $])
| {
| {
| case i + 1:
| if (!hasElaborateCopyConstructor!T && !__ctfe)
| goto default;
| static if (is(T == void))
| ret = ret._void;
| else
| ret = this.trustedGet!T;
| break S;
| }
| }
| default:
| ret.storage__.bytes = this.storage__.bytes;
| static if (ret.AllowedTypes.length > 1)
| ret.identifier__ = cast(typeof(ret.identifier__))(this.identifier__ - 1);
| }
| return ret;
| }
| }
|
| static if (AllowedTypes.length == 2)
| {
| /++
| Gets the value if not null. If `this` is in the null state, and the optional
| parameter `fallback` was provided, it will be returned. Without `fallback`,
| calling `get` with a null state is invalid.
|
| When the fallback type is different from the Nullable type, `get(T)` returns
| the common type.
|
| Params:
| fallback = the value to return in case the `Nullable` is null.
|
| Returns:
| The value held internally by this `Nullable`.
| +/
| auto ref inout(AllowedTypes[1]) get() return inout
| {
| assert(identifier__, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ").");
| return trustedGet!(AllowedTypes[1]);
| }
|
| version(mir_core_test)
| static if (variant_test__)
| ///
| @safe pure nothrow @nogc
| unittest
| {
| enum E { a = "a", b = "b" }
| Nullable!E f = E.a;
| auto e = f.get();
| static assert(is(typeof(e) == E), Nullable!E.AllowedTypes.stringof);
| assert(e == E.a);
|
| assert(f.get(E.b) == E.a);
|
| f = null;
| assert(f.get(E.b) == E.b);
| }
|
| /// ditto
| @property auto ref inout(AllowedTypes[1]) get()(auto ref inout(AllowedTypes[1]) fallback) return inout
| {
| return isNull ? fallback : get();
| }
| }
| }
|
| /++
| Checks if the underlaying type is an element of a user provided type set.
| +/
| bool _is(R : Algebraic!RetTypes, RetTypes...)() @safe pure nothrow @nogc const @property
| if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
| {
| static if (is(RetTypes == Types__))
| return true;
| else
| {
| import std.meta: staticIndexOf;
| import std.traits: CopyTypeQualifiers;
| alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
| alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
| // uint rhsTypeId;
| switch (identifier__)
| {
| foreach (i, T; AllowedTypes)
| static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
| {
| case i:
| return true;
| }
| default:
| return false;
| }
| }
| }
|
| /// ditto
| bool _is(RetTypes...)() @safe pure nothrow @nogc const @property
| if (RetTypes.length > 1)
| {
| return this._is!(Variant!RetTypes);
| }
|
| /++
| `nothrow` $(LREF .Algebraic.get) alternative that returns an algebraic subset.
| +/
| auto ref trustedGet(R : Algebraic!RetTypes, this This, RetTypes...)() return @property
| if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
| {
| static if (is(RetTypes == Types__))
| return this;
| else
| {
| import std.meta: staticIndexOf;
| import std.traits: CopyTypeQualifiers;
| alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
| alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
| // uint rhsTypeId;
| switch (identifier__)
| {
| foreach (i, T; AllowedTypes)
| static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
| {
| case i:
| static if (is(T == void))
| return (()@trusted => cast(Ret) Ret._void)();
| else
| return Ret(trustedGet!T);
| }
| default:
| assert(0, variantMemberExceptionMsg);
| }
| }
| }
|
| /// ditto
| template trustedGet(RetTypes...)
| if (RetTypes.length > 1)
| {
| ///
| auto ref trustedGet(this This)() return
| {
| return this.trustedGet!(Variant!RetTypes);
| }
| }
|
| version(mir_core_test)
| static if (variant_test__)
| ///
| @safe pure nothrow @nogc
| unittest
| {
| alias Float = Variant!(float, double);
| alias Int = Variant!(long, int);
| alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
|
| Number number = 3.0;
| assert(number._is!Float);
| auto fp = number.trustedGet!Float;
| static assert(is(typeof(fp) == Float));
| assert(fp == 3.0);
|
| // type list overload
| number = 12L;
| assert(number._is!(int, long));
| auto integer = number.trustedGet!(int, long);
| static assert(is(typeof(integer) == Int));
| assert(integer == 12L);
| }
|
| static if (typeFieldNames__.length)
| {
| /// `trustedGet` overload that accept $(LREF .Algebraic.Kind).
| alias trustedGet(Kind kind) = trustedGet!(AllowedTypes[kind]);
| /// ditto
| alias trustedGet(immutable(char)[] kind) = trustedGet!(__traits(getMember, Kind, kind));
| }
|
| /++
| Gets an algebraic subset.
|
| Throws: Exception if the storage contains value of the type that isn't represented in the allowed type set of the requested algebraic.
| +/
| auto ref get(R : Algebraic!RetTypes, this This, RetTypes...)() return @property
| if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
| {
| static if (is(RetTypes == Types__))
| return this;
| else
| {
| import std.meta: staticIndexOf;
| import std.traits: CopyTypeQualifiers;
| alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
| alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
| // uint rhsTypeId;
| switch (identifier__)
| {
| foreach (i, T; AllowedTypes)
| static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
| {
| case i:
| static if (is(T == void))
| return (()@trusted => cast(Ret) Ret._void)();
| else
| return Ret(trustedGet!T);
| }
| default:
| throw variantMemberException;
| }
| }
| }
|
| /// ditto
| template get(RetTypes...)
| if (RetTypes.length > 1)
| {
| ///
| auto ref get(this This)() return
| {
| return this.get!(Variant!RetTypes);
| }
| }
|
| version(mir_core_test)
| static if (variant_test__)
| ///
| @safe pure @nogc
| unittest
| {
| alias Float = Variant!(float, double);
| alias Int = Variant!(long, int);
| alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
|
| Number number = 3.0;
| auto fp = number.get!Float;
| static assert(is(typeof(fp) == Float));
| assert(fp == 3.0);
|
| // type list overload
| number = 12L;
| auto integer = number.get!(int, long);
| static assert(is(typeof(integer) == Int));
| assert(integer == 12L);
| }
|
| static if (typeFieldNames__.length)
| {
| /// `get` overload that accept $(LREF .Algebraic.Kind).
| alias get(Kind kind) = get!(AllowedTypes[kind]);
| /// ditto
| alias get(immutable(char)[] kind) = get!(__traits(getMember, Kind, kind));
|
| /// `_is` overload that accept $(LREF .Algebraic.Kind).
| alias _is(Kind kind) = _is!(AllowedTypes[kind]);
| /// ditto
| alias _is(immutable(char)[] kind) = _is!(__traits(getMember, Kind, kind));
|
| static foreach (member; typeFieldNames__)
| mixin ("alias " ~ member ~ `() = get!"` ~ member ~ `";`);
| }
|
| private alias _ReflectionTypes = AllowedTypes[is(AllowedTypes[0] == typeof(null)) .. $];
|
| static if (_ReflectionTypes.length)
| this(this This, Args...)(auto ref Args args)
| if (Args.length && (Args.length > 1 || !isLikeVariant!(Args[0])))
| {
| import std.traits: CopyTypeQualifiers;
| import core.lifetime: forward;
|
| template CanCompile(T)
| {
| alias Q = CopyTypeQualifiers!(This, T);
| enum CanCompile = __traits(compiles, new Q(forward!args));
| }
|
| alias TargetType = Filter!(CanCompile, _ReflectionTypes);
| static if (TargetType.length == 0)
| static assert(0, typeof(this).stringof ~ ".this: no types can be constructed with arguments " ~ Args.stringof);
| static assert(TargetType.length == 1, typeof(this).stringof ~ ".this: multiple types " ~ TargetType.stringof ~ " can be constructed with arguments " ~ Args.stringof);
| alias TT = TargetType[0];
| static if (is(TT == struct) || is(TT == union))
| this(CopyTypeQualifiers!(This, TT)(forward!args));
| else
| this(new CopyTypeQualifiers!(This, TT)(forward!args));
| }
|
| static if (_ReflectionTypes.length && allSatisfy!(isSimpleAggregateType, _ReflectionTypes))
| {
| static foreach (member; AllMembersRec!(_ReflectionTypes[0]))
| static if (
| !.algebraicMembers.contains(member) &&
| !metaFieldNames__.contains(member) &&
| !typeFieldNames__.contains(member) &&
| !(member.length >= 2 && (member[0 .. 2] == "__" || member[$ - 2 .. $] == "__")))
| static if (allSatisfy!(ApplyRight!(hasMember, member), _ReflectionTypes))
| static if (!anySatisfy!(ApplyRight!(isMemberType, member), _ReflectionTypes))
| static if (allSatisfy!(ApplyRight!(isSingleMember, member), _ReflectionTypes))
| static if (allSatisfy!(ApplyRight!(isPublic, member), _ReflectionTypes))
| {
| static if (allSatisfy!(ApplyRight!(hasField, member), _ReflectionTypes) && NoDuplicates!(staticMap!(ApplyRight!(memberTypeOf, member), _ReflectionTypes)).length == 1)
| {
| mixin(`ref ` ~ member ~q{()() inout return @trusted pure nothrow @nogc @property { return this.getMember!member; }});
| }
| else
| static if (allSatisfy!(ApplyRight!(templateOr!(hasField, isProperty), member), _ReflectionTypes))
| {
| mixin(`auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) @property { static if (args.length) { import core.lifetime: forward; return this.getMember!member = forward!args; } else return this.getMember!member; }});
| }
| static if (allSatisfy!(ApplyRight!(templateNot!(templateOr!(hasField, isProperty)), member), _ReflectionTypes))
| {
| mixin(`template ` ~ member ~`(TArgs...) { auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) { static if (args.length) { import core.lifetime: forward; return this.getMember!(member, TArgs)(forward!args); } else return this.getMember!(member, TArgs); }} ~ `}`);
| }
| }
| }
|
| ///
| ref opAssign(RhsTypes...)(Algebraic!RhsTypes rhs) return @trusted
| if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes) && !is(Algebraic == Algebraic!RhsTypes))
| {
| import core.lifetime: forward;
| this = this.init;
| __ctor(forward!rhs);
| return this;
| }
|
| // pragma(msg, AllowedTypes);
|
| static foreach (int i, T; AllowedTypes)
| {
| /// Zero cost always nothrow `get` alternative
| auto ref trustedGet(E)() @trusted @property return inout nothrow
| if (is(E == T))
| {
| assert (i == identifier__);
| static if (is(T == typeof(null)))
| return null;
| else
| static if (is(T == void))
| return;
| else
| return storage__.payload[i];
| }
|
| /++
| Throws: Exception if the storage contains value of other type
| +/
| auto ref get(E)() @property return inout
| if (is(E == T))
| {
| import mir.utility: _expect;
| static if (AllowedTypes.length > 1)
| {
| if (_expect(i != identifier__, false))
| {
| throw variantException;
| }
| }
| return trustedGet!T;
| }
|
| /++
| Checks if the storage stores an allowed type.
| +/
| bool _is(E)() const @property nothrow @nogc
| if (is(E == T))
| {
| return identifier__ == i;
| }
|
| static if (is(T == void))
| {
| /// Defined if `AllowedTypes` contains `void`
| static Algebraic _void()
| {
| Algebraic ret;
| ret.storage__ = () {
| import core.lifetime: forward;
| mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : _Void!().init };`);
| return ret;
| } ();
| ret.identifier__ = i;
| return ret;
| }
| }
| else
| {
| ///
| static if (isCopyable!(const T) || is(Unqual!T == T))
| this(T value)
| {
| import core.lifetime: forward;
| static if (is(T == typeof(null)))
| auto rhs = _Null!()();
| else
| alias rhs = forward!value;
|
| static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
| {
| storage__.bytes = () @trusted {
| auto ret = _StorageI!i(rhs);
| return ret.bytes;
| } ();
| }
| else
| {
| storage__ = () {
| mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
| return ret;
| } ();
| }
| static if (_Payload.length > 1)
| identifier__ = i;
| }
|
| /// ditto
| static if (isCopyable!(const T))
| this(const T value) const
| {
| static if (is(T == typeof(null)))
| auto rhs = _Null!()();
| else
| alias rhs = value;
| static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
| {
| storage__.bytes = () const @trusted {
| auto ret = const _StorageI!i(rhs);
| return ret.bytes;
| } ();
| }
| else
| {
| storage__ = () {
| mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
| return ret;
| } ();
| }
| static if (_Payload.length > 1)
| identifier__ = i;
| }
|
| /// ditto
| static if (isCopyable!(immutable T))
| this(immutable T value) immutable
| {
| static if (is(T == typeof(null)))
| auto rhs = _Null!()();
| else
| alias rhs = value;
| static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
| {
| storage__.bytes = () const @trusted {
| auto ret = immutable _StorageI!i(rhs);
| return ret.bytes;
| } ();
| }
| else
| {
| storage__ = () {
| mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
| return ret;
| } ();
| }
| static if (_Payload.length > 1)
| identifier__ = i;
| }
|
| static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); }))
| ///
| ref opAssign(T rhs) return @trusted
| {
| static foreach (T; MetaInfo__)
| __traits(getMember, this, T.tag) = T.Type.init;
|
| import core.lifetime: forward;
| this = this.init;
| __ctor(forward!rhs);
| return this;
| }
|
| /++
| +/
| bool opEquals()(scope ref const UnqualRec!T rhs) scope @trusted const //pure nothrow @nogc
| {
| static if (AllowedTypes.length > 1)
| if (identifier__ != i)
| return false;
| return trustedGet!T == rhs;
| }
|
| ///ditto
| bool opEquals()(scope const UnqualRec!T rhs) scope @trusted const //pure nothrow @nogc
| {
| return opEquals(rhs);
| }
|
| /++
| +/
| auto opCmp()(auto ref scope const UnqualRec!T rhs) scope @trusted const pure nothrow @nogc
| {
| static if (AllowedTypes.length > 1)
| if (auto d = int(identifier__) - int(i))
| return d;
| static if (__traits(compiles, __cmp(trustedGet!T, rhs)))
| return __cmp(trustedGet!T, rhs);
| else
| static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U))
| return trustedGet!T.opCmp(rhs);
| else
| return trustedGet!T < rhs ? -1 : trustedGet!T > rhs ? +1 : 0;
| }
|
| static if (is(Unqual!T == bool))
| {
| private alias contains = Contains!AllowedTypes;
| static if (contains!long && !contains!int)
| {
| this(int value)
| {
| this(long(value));
| }
|
| this(int value) const
| {
| this(long(value));
| }
|
| this(int value) immutable
| {
| this(long(value));
| }
|
| ref opAssign(int rhs) return @trusted
| {
| return opAssign(long(rhs));
| }
|
| auto opEquals()(int rhs) const
| {
| return opEquals(long(rhs));
| }
|
| auto opCmp()(int rhs) const
| {
| return opCmp(long(rhs));
| }
| }
|
| static if (contains!ulong && !contains!uint)
| {
| this(uint value)
| {
| this(ulong(value));
| }
|
| this(uint value) const
| {
| this(ulong(value));
| }
|
| this(uint value) immutable
| {
| this(ulong(value));
| }
|
| ref opAssign(uint rhs) return @trusted
| {
| return opAssign(ulong(rhs));
| }
|
| auto opEquals()(uint rhs) const
| {
| return opEquals(ulong(rhs));
| }
|
| auto opCmp()(uint rhs) const
| {
| return opCmp(ulong(rhs));
| }
| }
| }
| }
| }
|
| static if (anySatisfy!(isErr, AllowedTypes))
| {
| /++
| Determines if the variant holds value of some none-$(LREF isVariant) type.
| The property is avaliable only for $(ResultVariant)
| +/
| bool isOk() @safe pure nothrow @nogc const @property
| {
| switch (identifier__)
| {
| static foreach (i, T; AllowedTypes)
| {
| case i:
| return !.isErr!T;
| }
| default: assert(0);
| }
| }
| }
|}
|
|/++
|Constructor and methods propagation.
|+/
|version(mir_core_test)
|unittest
|{
| static struct Base
| {
| double d;
| }
|
| static class Cc
| {
| // alias this members are supported
| Base base;
| alias base this;
|
| int a;
| private string _b;
|
| @safe pure nothrow @nogc:
|
| override size_t toHash() scope const { return hashOf(_b) ^ a; }
|
| string b() const @property { return _b; }
| void b(string b) @property { _b = b; }
|
| int retArg(int v) { return v; }
| string retArgT(TArgs...)(int v) { return TArgs.stringof; }
|
| this(int a, string b)
| {
| this.a = a;
| this._b = b;
| }
| }
|
| static struct S
| {
| string b;
| int a;
|
| double retArg(double v) { return v; }
| double retArgT(TArgs...)(int v) { return v * TArgs.length; }
|
| // alias this members are supported
| Base base;
| alias base this;
| }
|
| static void inc(ref int a) { a++; }
|
| alias V = Nullable!(Cc, S); // or Variant!
|
| auto v = V(2, "str");
| assert(v._is!Cc);
| assert(v.a == 2);
| assert(v.b == "str");
| // members are returned by reference if possible
| inc(v.a);
| assert(v.a == 3);
| v.b = "s";
| assert(v.b == "s");
| // alias this members are supported
| v.d = 10;
| assert(v.d == 10);
| // method call support
| assert(v.retArg(100)._is!int);
| assert(v.retArg(100) == 100);
|
| // method with template args support
| assert(v.retArgT!dchar(100)._is!string);
| assert(v.retArgT!dchar(100) == "(dchar)");
|
| v = V("S", 5);
| assert(v._is!S);
| assert(v.a == 5);
| assert(v.b == "S");
| // members are returned by reference if possible
| inc(v.a);
| assert(v.a == 6);
| v.b = "s";
| assert(v.b == "s");
| // alias this members are supported
| v.d = 15;
| assert(v.d == 15);
| // method call support
| assert(v.retArg(300)._is!double);
| assert(v.retArg(300) == 300.0);
|}
|
|// test CTFE
|unittest
|{
| static struct S { string s;}
| alias V = Nullable!(double, S);
| enum a = V(1.9);
| static assert (a == 1.9);
| enum b = V(S("str"));
| static assert(b == S("str"));
| static auto foo(int r)
| {
| auto s = V(S("str"));
| s = r;
| return s;
| }
|
| static assert(foo(3) == 3);
| static auto bar(int r)
| {
| auto s = V(S("str"));
| s = r;
| return s.visit!((double d) => d, (_)=> 0.0)();
| }
| assert(bar(3) == 3);
| static assert(bar(3) == 3);
|
| static auto bar3(int r)
| {
| auto s = V(S("str"));
| s = r;
| return s.match!((double d) => d, (_)=> "3")();
| }
| assert(bar(3) == 3);
| static assert(bar(3) == 3);
|}
|
|@safe pure @nogc nothrow
|version(mir_core_test) unittest
|{
| import core.stdc.string: memcmp;
|
| static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true)
| {
| ubyte[payloadSize] _payload;
|
| const:
|
| static if (!isPOD)
| {
| this(this) {}
| ~this() {}
| }
|
| @safe pure nothrow @nogc:
|
|
| static if (hasToHash)
| size_t toHash() scope { return hashOf(_payload); }
|
| static if (hasOpEquals)
| auto opEquals(ref const scope typeof(this) rhs) scope { return _payload == rhs._payload; }
| auto opCmp(ref const scope typeof(this) rhs) @trusted scope { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); }
| }
|
| static foreach (size1; [1, 2, 4, 8, 10, 16, 20])
| static foreach (size2; [1, 2, 4, 8, 10, 16, 20])
| static if (size1 != size2)
| static foreach (isPOD; [true, false])
| static foreach (hasToHash; [true, false])
| static foreach (hasOpEquals; [true, false])
| {{
| alias T = Variant!(
| double,
| C!(size1, isPOD, hasToHash, hasOpEquals),
| C!(size2, isPOD, hasToHash, hasOpEquals));
| // static assert (__traits(compiles, T.init <= T.init));
| }}
|}
|
|// const propogation
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| static struct S1 { immutable(ubyte)* value; }
| static struct C1 { immutable(uint)* value; }
|
| alias V = Variant!(S1, C1);
| const V v = S1();
| assert(v._is!S1);
| V w = v;
| w = v;
|
| immutable f = V(S1());
| auto t = immutable V(S1());
| // auto j = immutable V(t);
| // auto i = const V(t);
|}
|
|// ditto
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| static struct S2 {
| uint* value;
| this(return ref scope const typeof(this) rhs) {}
| ref opAssign(typeof(this) rhs) return { return this; }
| }
| static struct C2 { const(uint)* value; }
|
| alias V = Variant!(S2, C2);
| const V v = S2();
| V w = v;
| w = S2();
| w = v;
| w = cast(const) V.init;
|
| const f = V(S2());
| auto t = const V(f);
|}
|
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| static struct S3 {
| uint* value;
| this(return ref scope typeof(this) rhs) {}
| this(return ref scope const typeof(this) rhs) const {}
| this(return ref scope immutable typeof(this) rhs) immutable {}
| }
| static struct C3 { immutable(uint)* value; }
|
| S3 s;
| S3 r = s;
| r = s;
| r = S3.init;
|
| alias V = Variant!(S3, C3);
| V v = S3();
| V w = v;
| w = S3();
| w = V.init;
| w = v;
|
| immutable V e = S3();
| auto t = immutable V(S3());
| auto j = const V(t);
| auto h = t;
|
| immutable V l = C3();
| auto g = immutable V(C3());
|}
|
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| static struct S4 {
| uint* value;
| this(return ref scope const typeof(this) rhs) pure immutable {}
| }
| static struct C4 { immutable(uint)* value; }
|
|
| S4 s;
| S4 r = s;
| r = s;
| r = S4.init;
|
| alias V = Variant!(S4, C4);
| V v = S4();
| V w = v;
| w = S4();
| w = V.init;
| w = v;
|
| {
| const V e = S4();
| const k = w;
| auto t = const V(k);
| auto j = immutable V(k);
| }
|
| immutable V e = S4();
| immutable k = w;
| auto t = immutable V(S4());
| auto j = const V(t);
| auto h = t;
|
| immutable V l = C4();
| import core.lifetime;
| auto g = immutable V(C4());
| immutable b = immutable V(s);
|}
|
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| import core.lifetime: move;
|
| static struct S5 {
| immutable(uint)* value;
| this(return ref scope typeof(this) rhs) {}
| this(return ref scope const typeof(this) rhs) immutable {}
| }
| static struct C5 { immutable(uint)* value; }
|
| S5 s;
| S5 r = s;
| r = s;
| r = S5.init;
|
| alias V = Variant!(S5, C5);
| V v = S5();
| V w = v;
| w = S5();
| w = V.init;
| w = v;
|
| immutable V e = S5();
| immutable f = V(S5());
| immutable k = w;
| auto t = immutable V(S5());
| auto j = const V(t);
| auto h = t;
|
| immutable V l = C5();
| import core.lifetime;
| immutable n = w.move;
| auto g = immutable V(C5());
| immutable b = immutable V(s);
|}
|
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| static struct S {
| uint* value;
| this(this) @safe pure nothrow @nogc {}
| // void opAssign(typeof(this) rhs) {}
| }
| static struct C1 { const(uint)* value; }
|
| S s;
| S r = s;
| r = s;
| r = S.init;
|
| alias V = Variant!(S, C1);
| V v = S();
| V w = v;
| w = S();
| w = V.init;
| w = v;
|}
|
|/++
|Applies a delegate or function to the given Variant depending on the held type,
|ensuring that all types are handled by the visiting functions.
|+/
|alias visit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, false);
|
|///
|@safe pure @nogc nothrow
|version(mir_core_test) unittest
|{
| alias Number = Variant!(int, double);
|
| Number x = 23;
| Number y = 1.0;
|
| assert(x.visit!((int v) => true, (float v) => false));
| assert(y.visit!((int v) => false, (float v) => true));
|}
|
|///
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| alias Number = Nullable!(int, double);
|
| Number z = null; // default
| Number x = 23;
| Number y = 1.0;
|
| () nothrow {
| assert(x.visit!((int v) => true, (float v) => false));
| assert(y.visit!((int v) => false, (v) => true));
| assert(z.visit!((typeof(null) v) => true, (v) => false));
| } ();
|
| auto xx = x.get;
| static assert (is(typeof(xx) == Variant!(int, double)));
| assert(xx.visit!((int v) => v, (float v) => 0) == 23);
| assert(xx.visit!((ref v) => v) == 23);
|
| x = null;
| y.nullify;
|
| assert(x.isNull);
| assert(y.isNull);
| assert(z.isNull);
| assert(z == y);
|}
|
|/// Array primitives propagation
|@safe pure version(mir_core_test) unittest
|{
| Variant!(long[], double[]) array;
| array = new long[3];
| array[2] = 100;
| assert(array == [0L, 0, 100]);
| assert(array.length == 3);
| assert(array[2] == 100);
| array.length = 4;
| assert(array == [0L, 0, 100, 0]);
| array = array[2 .. 3];
| assert(array.length == 1);
| assert(array[0] == 100);
| array[0] = 10.Variant!(long, double);
| assert(array[0] == 10);
|}
|
|/++
|Checks $(LREF .Algebraic.toString) and `void`
|$(LREF Algerbraic)`.toString` requries `mir-algorithm` package
|+/
|@safe pure nothrow version(mir_core_test) unittest
|{
| import mir.conv: to;
| enum MIR_ALGORITHM = __traits(compiles, { import mir.format; });
|
| alias visitorHandler = visit!(
| (typeof(null)) => "NULL",
| () => "VOID",
| (ref r) {r += 1;}, // returns void
| );
|
| alias secondOrderVisitorHandler = visit!(
| () => "SO VOID", // void => to "RV VOID"
| (str) => str, // string to => it self
| );
|
| alias V = Nullable!(void, int);
| static assert(is(V == Variant!(typeof(null), void, int)));
|
| V variant;
|
| assert(secondOrderVisitorHandler(visitorHandler(variant)) == "NULL");
| assert(variant.toString == "null");
|
| variant = V._void;
| assert(variant._is!void);
| assert(is(typeof(variant.get!void()) == void));
|
| assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID");
| assert(variant.toString == "void");
|
| variant = 5;
|
| assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID");
| assert(variant == 6);
| assert(variant.toString == (MIR_ALGORITHM ? "6" : "int"));
|}
|
|version(mir_core_test)
|unittest
|{
| Nullable!() value;
| alias visitHandler = visit!((typeof(null)) => null, err);
| auto d = visitHandler(value);
| assert(d == value);
|}
|
|/++
|Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|Throws: Exception if `naryFun!visitors` can't be called with provided arguments
|+/
|alias tryVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, false);
|
|///
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| alias Number = Variant!(int, double);
|
| Number x = 23;
|
| assert(x.tryVisit!((int v) => true));
|}
|
|/++
|Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments.
|+/
|alias optionalVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, false);
|
|///
|@safe pure @nogc nothrow
|version(mir_core_test) unittest
|{
| static struct S { int a; }
|
| Variant!(S, double) variant;
|
| alias optionalVisitInst = optionalVisit!((ref value) => value + 0);
|
| // do nothing because of variant isn't initialized
| Nullable!double result = optionalVisitInst(variant);
| assert(result.isNull);
|
| variant = S(2);
| // do nothing because of lambda can't compile
| result = optionalVisitInst(variant);
| assert(result == null);
|
| variant = 3.0;
| result = optionalVisitInst(variant);
| assert (result == 3.0);
|}
|
|/++
|Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments.
|+/
|alias autoVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, false);
|
|
|/++
|Applies a delegate or function to the given arguments depending on the held type,
|ensuring that all types are handled by the visiting functions.
|
|The handler supports multiple dispatch or multimethods: a feature of handler in which
|a function or method can be dynamically dispatched based on the run time (dynamic) type or,
|in the more general case, some other attribute of more than one of its arguments.
|
|Fuses algebraic types on return.
|
|See_also: $(HTTPS en.wikipedia.org/wiki/Multiple_dispatch, Multiple dispatch)
|+/
|alias match(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true);
|
|///
|version(mir_core_test)
|unittest
|{
| static struct Asteroid { uint size; }
| static struct Spaceship { uint size; }
| alias SpaceObject = Variant!(Asteroid, Spaceship);
|
| alias collideWith = match!(
| (Asteroid x, Asteroid y) => "a/a",
| (Asteroid x, Spaceship y) => "a/s",
| (Spaceship x, Asteroid y) => "s/a",
| (Spaceship x, Spaceship y) => "s/s",
| );
|
| import mir.utility: min;
|
| // Direct access of a member in case of all algebraic types has this member
| alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
|
| alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
|
| auto ea = Asteroid(1);
| auto es = Spaceship(2);
| auto oa = SpaceObject(ea);
| auto os = SpaceObject(es);
|
| // Asteroid-Asteroid
| assert(collide(ea, ea) == "a/a");
| assert(collide(ea, oa) == "a/a");
| assert(collide(oa, ea) == "a/a");
| assert(collide(oa, oa) == "a/a");
|
| // Asteroid-Spaceship
| assert(collide(ea, es) == "a/s");
| assert(collide(ea, os) == "a/s");
| assert(collide(oa, es) == "a/s");
| assert(collide(oa, os) == "a/s");
|
| // Spaceship-Asteroid
| assert(collide(es, ea) == "s/a");
| assert(collide(es, oa) == "s/a");
| assert(collide(os, ea) == "s/a");
| assert(collide(os, oa) == "s/a");
|
| // Spaceship-Spaceship
| assert(collide(es, es) == "big-boom");
| assert(collide(es, os) == "big-boom");
| assert(collide(os, es) == "big-boom");
| assert(collide(os, os) == "big-boom");
|}
|
|/++
|Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|Throws: Exception if `naryFun!visitors` can't be called with provided arguments
|
|Fuses algebraic types on return.
|+/
|alias tryMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, true);
|
|///
|version(mir_core_test)
|unittest
|{
| import std.exception: assertThrown;
| static struct Asteroid { uint size; }
| static struct Spaceship { uint size; }
| alias SpaceObject = Variant!(Asteroid, Spaceship);
|
| alias collideWith = tryMatch!(
| (Asteroid x, Asteroid y) => "a/a",
| // No visitor for A/S pair
| // (Asteroid x, Spaceship y) => "a/s",
| (Spaceship x, Asteroid y) => "s/a",
| (Spaceship x, Spaceship y) => "s/s",
| );
|
| import mir.utility: min;
| // Direct access of a member in case of all algebraic types has this member
| alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
|
| alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
|
| auto ea = Asteroid(1);
| auto es = Spaceship(2);
| auto oa = SpaceObject(ea);
| auto os = SpaceObject(es);
|
| // Asteroid-Asteroid
| assert(collide(ea, ea) == "a/a");
| assert(collide(ea, oa) == "a/a");
| assert(collide(oa, ea) == "a/a");
| assert(collide(oa, oa) == "a/a");
|
| // Asteroid-Spaceship
| assertThrown!Exception(collide(ea, es));
| assertThrown!Exception(collide(ea, os));
| assertThrown!Exception(collide(oa, es));
| assertThrown!Exception(collide(oa, os));
|
| // can deduce the type based on other return values
| static assert(is(typeof(collide(ea, os)) == string));
| static assert(is(typeof(collide(oa, es)) == string));
| static assert(is(typeof(collide(oa, os)) == string));
|
| // Also allows newer compilers to detect combinations which always throw an exception
| static if (is(typeof(collideWith(ea, es)) == noreturn))
| {
| static assert(is(typeof(collide(ea, es)) == string));
| }
| else
| {
| // not enough information to deduce the type from (ea, es) pair
| static assert(is(typeof(collide(ea, es)) == void));
| }
|
| // Spaceship-Asteroid
| assert(collide(es, ea) == "s/a");
| assert(collide(es, oa) == "s/a");
| assert(collide(os, ea) == "s/a");
| assert(collide(os, oa) == "s/a");
|
| // Spaceship-Spaceship
| assert(collide(es, es) == "big-boom");
| assert(collide(es, os) == "big-boom");
| assert(collide(os, es) == "big-boom");
| assert(collide(os, os) == "big-boom");
|}
|
|/++
|Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments.
|
|Fuses algebraic types on return.
|+/
|alias optionalMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, true);
|
|///
|version(mir_core_test)
|unittest
|{
| static struct Asteroid { uint size; }
| static struct Spaceship { uint size; }
| alias SpaceObject = Variant!(Asteroid, Spaceship);
|
| alias collideWith = optionalMatch!(
| (Asteroid x, Asteroid y) => "a/a",
| // No visitor for A/S pair
| // (Asteroid x, Spaceship y) => "a/s",
| (Spaceship x, Asteroid y) => "s/a",
| (Spaceship x, Spaceship y) => "s/s",
| );
|
| import mir.utility: min;
| // Direct access of a member in case of all algebraic types has this member
| alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
|
| alias collide = (x, y) => oops(x, y) ? "big-boom".nullable : collideWith(x, y);
|
| auto ea = Asteroid(1);
| auto es = Spaceship(2);
| auto oa = SpaceObject(ea);
| auto os = SpaceObject(es);
|
| // Asteroid-Asteroid
| assert(collide(ea, ea) == "a/a");
| assert(collide(ea, oa) == "a/a");
| assert(collide(oa, ea) == "a/a");
| assert(collide(oa, oa) == "a/a");
|
| // Asteroid-Spaceship
| // assert(collide(ea, es).isNull); // Compiler error: incompatible types
| assert(collideWith(ea, es).isNull); // OK
| assert(collide(ea, os).isNull);
| assert(collide(oa, es).isNull);
| assert(collide(oa, os).isNull);
|
|
| // Spaceship-Asteroid
| assert(collide(es, ea) == "s/a");
| assert(collide(es, oa) == "s/a");
| assert(collide(os, ea) == "s/a");
| assert(collide(os, oa) == "s/a");
|
| // Spaceship-Spaceship
| assert(collide(es, es) == "big-boom");
| assert(collide(es, os) == "big-boom");
| assert(collide(os, es) == "big-boom");
| assert(collide(os, os) == "big-boom");
|
| // check types
|
| static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
| static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
|
| static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == Nullable!string));
| static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
| static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == Nullable!string));
| static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
| static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
| static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == Nullable!string));
| static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == Nullable!string));
| static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == Nullable!string));
|}
|
|/++
|Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments.
|
|Fuses algebraic types on return.
|+/
|alias autoMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, true);
|
|///
|version(mir_core_test)
|unittest
|{
| static struct Asteroid { uint size; }
| static struct Spaceship { uint size; }
| alias SpaceObject = Variant!(Asteroid, Spaceship);
|
| alias collideWith = autoMatch!(
| (Asteroid x, Asteroid y) => "a/a",
| // No visitor for A/S pair
| // (Asteroid x, Spaceship y) => "a/s",
| (Spaceship x, Asteroid y) => "s/a",
| (Spaceship x, Spaceship y) => "s/s",
| );
|
| import mir.utility: min;
| // Direct access of a member in case of all algebraic types has this member
| alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
|
| import mir.conv: to;
| alias collide = (x, y) => oops(x, y) ? "big-boom".to!(typeof(collideWith(x, y))) : collideWith(x, y);
|
| auto ea = Asteroid(1);
| auto es = Spaceship(2);
| auto oa = SpaceObject(ea);
| auto os = SpaceObject(es);
|
| // Asteroid-Asteroid
| assert(collide(ea, ea) == "a/a");
| assert(collide(ea, oa) == "a/a");
| assert(collide(oa, ea) == "a/a");
| assert(collide(oa, oa) == "a/a");
|
| // Asteroid-Spaceship
| // assert(collide(ea, es).isNull); // Compiler error: incompatible types
| assert(collideWith(ea, es).isNull); // OK
| assert(collide(ea, os).isNull);
| assert(collide(oa, es).isNull);
| assert(collide(oa, os).isNull);
|
| // Spaceship-Asteroid
| assert(collide(es, ea) == "s/a");
| assert(collide(es, oa) == "s/a");
| assert(collide(os, ea) == "s/a");
| assert(collide(os, oa) == "s/a");
|
| // Spaceship-Spaceship
| assert(collide(es, es) == "big-boom");
| assert(collide(es, os) == "big-boom");
| assert(collide(os, es) == "big-boom");
| assert(collide(os, os) == "big-boom");
|
| // check types
|
| static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
| static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
|
| static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == string));
| static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == string));
| static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == string));
| static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == string));
| static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == string));
|
| static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
| static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
| static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
|}
|
|/++
|Applies a member handler to the given Variant depending on the held type,
|ensuring that all types are handled by the visiting handler.
|+/
|alias getMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.compileTime, false);
|
|///
|@safe pure @nogc nothrow
|version(mir_core_test) unittest
|{
| static struct S { auto bar(int a) { return a; } enum boolean = true; }
| static struct C2 { alias bar = (double a) => a * 2; enum boolean = false; }
|
| alias V = Variant!(S, C2);
|
| V x = S();
| V y = C2();
|
| static assert(is(typeof(x.getMember!"bar"(2)) == Variant!(int, double)));
| assert(x.getMember!"bar"(2) == 2);
| assert(y.getMember!"bar"(2) != 4);
| assert(y.getMember!"bar"(2) == 4.0);
|
| // direct implementation
| assert(x.bar(2) == 2);
| assert(y.bar(2) != 4);
| assert(y.bar(2) == 4.0);
| assert(x.boolean);
| assert(!y.boolean);
|}
|
|/++
|Applies a member handler to the given Variant depending on the held type,
|ensuring that all types are handled by the visiting handler.
|
|Fuses algebraic types on return.
|+/
|alias matchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.compileTime, true);
|
|///
|@safe pure @nogc nothrow
|version(mir_core_test) unittest
|{
| static struct S
| {
| Nullable!int m;
| }
|
| static struct C1
| {
| Variant!(float, double) m;
| }
|
| alias V = Variant!(S, C1);
|
| V x = S(2.nullable);
| V y = C1(Variant!(float, double)(4.0));
|
| // getMember returns an algebraic of algebaics
| static assert(is(typeof(x.getMember!"m") == Variant!(Variant!(float, double), Nullable!int)));
| // matchMember returns a fused algebraic
| static assert(is(typeof(x.matchMember!"m") == Nullable!(int, float, double)));
| assert(x.matchMember!"m" == 2);
| assert(y.matchMember!"m" != 4);
| assert(y.matchMember!"m" == 4.0);
|}
|
|/++
|Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
|Throws: Exception if member can't be accessed with provided arguments
|+/
|alias tryGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, false);
|
|///
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| static struct S { int bar(int a) { return a; }}
| static struct C3 { alias Bar = (double a) => a * 2; }
|
| alias V = Variant!(S, C3);
|
| V x = S();
| V y = C3();
|
| static assert(is(typeof(x.tryGetMember!"bar"(2)) == int));
| static assert(is(typeof(y.tryGetMember!"Bar"(2)) == double));
| assert(x.tryGetMember!"bar"(2) == 2);
| assert(y.tryGetMember!"Bar"(2) == 4.0);
|}
|
|///
|@safe pure @nogc nothrow
|version(mir_core_test) unittest
|{
| alias Number = Variant!(int, double);
|
| Number x = Number(23);
| Number y = Number(1.0);
|
| assert(x.visit!((int v) => true, (float v) => false));
| assert(y.visit!((int v) => false, (float v) => true));
|}
|
|/++
|Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
|Throws: Exception if member can't be accessed with provided arguments
|
|Fuses algebraic types on return.
|+/
|alias tryMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.exception, true);
|
|/++
|Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
|Returns: nullable variant, null value is used if the member can't be called with provided arguments.
|+/
|alias optionalGetMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.nullable, false);
|
|/++
|Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
|Returns: nullable variant, null value is used if the member can't be called with provided arguments.
|
|Fuses algebraic types on return.
|+/
|alias optionalMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.nullable, true);
|
|/++
|Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
|Returns: optionally nullable type, null value is used if the member can't be called with provided arguments.
|+/
|alias autoGetMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.auto_, false);
|
|/++
|Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
|Returns: optionally nullable type, null value is used if the member can't be called with provided arguments.
|
|Fuses algebraic types on return.
|+/
|alias autoMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.auto_, true);
|
|private template getMemberHandler(string member, TArgs...)
|{
| ///
| auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args)
| {
| static if (Args.length == 0)
| {
| static if (TArgs.length)
| {
| return mixin(`value.` ~ member ~ `!TArgs`);
| }
| else
| {
| return __traits(getMember, value, member);
| }
| }
| else
| {
| import core.lifetime: forward;
| import mir.reflection: hasField;
| static if (TArgs.length)
| {
| static if (hasField!(V, member) && Args.length == 1)
| return mixin(`value.` ~ member ~ `!TArgs`) = forward!args;
| else
| return mixin(`value.` ~ member ~ `!TArgs(forward!args)`);
| }
| else
| {
| static if (hasField!(V, member) && Args.length == 1)
| return __traits(getMember, value, member) = forward!args;
| else
| return __traits(getMember, value, member)(forward!args);
| }
| }
| }
|}
|
|private template VariantReturnTypes(T...)
|{
| import std.meta: staticMap;
|
| alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T));
|}
|
|private enum Exhaustive
|{
| compileTime,
| exception,
| nullable,
| auto_,
|}
|
|private template nextVisitor(T, alias visitor, alias arg)
|{
| static if (is(T == void))
| {
| alias nextVisitor = visitor;
| }
| else
| auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs)
| {
| import core.lifetime: forward;
|
| static if (__traits(isRef, arg))
| return visitor(arg.trustedGet!T, forward!nextArgs);
| else
| static if (is(typeof(move(arg.trustedGet!T))))
| return visitor(move(arg.trustedGet!T), forward!nextArgs);
| else
| return visitor((() => arg.trustedGet!T)(), forward!nextArgs);
| }
|}
|
|private template nextVisitor(alias visitor, alias arg)
|{
| auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs)
| {
| import core.lifetime: forward;
| return visitor(forward!arg, forward!nextArgs);
| }
|}
|
|private template visitThis(alias visitor, Exhaustive nextExhaustive)
|{
| template visitThis(T)
| {
| auto ref visitThis(Args...)(auto ref Args args)
| {
| import core.lifetime: forward;
| return .visitImpl!(nextVisitor!(T, visitor, forward!(args[0])), nextExhaustive, true)(forward!(args[1 .. $]));
| }
| }
|}
|
|private template visitLast(alias visitor)
|{
| template visitLast(T)
| {
| static if (is(T == void))
| {
| auto ref visitLast(Args...)(auto ref Args args)
| {
| import core.lifetime: forward;
| return visitor(forward!(args[1 .. $]));
| }
| }
| else
| {
| auto ref visitLast(Args...)(auto ref Args args)
| {
| import core.lifetime: forward, move;
| static if (__traits(isRef, args[0]))
| return visitor(args[0].trustedGet!T, forward!(args[1 .. $]));
| else
| static if (is(typeof(move(args[0].trustedGet!T))))
| return visitor(move(args[0].trustedGet!T), forward!(args[1 .. $]));
| else
| return visitor((() => args[0].trustedGet!T)(), forward!(args[1 .. $]));
| }
| }
| }
|}
|
|private enum _AcceptAll(Args...) = true;
|
|template visitImpl(alias visitor, Exhaustive exhaustive, bool fused, alias Filter = _AcceptAll)
|{
| ///
| auto ref visitImpl(Args...)(auto ref Args args)
| if (Filter!Args)
| {
| import std.meta: anySatisfy, staticMap, AliasSeq;
| import core.lifetime: forward;
|
| static if (!anySatisfy!(isLikeVariant, Args))
| {
| static if (exhaustive == Exhaustive.compileTime)
| {
| return visitor(forward!args);
| }
| else
| static if (exhaustive == Exhaustive.exception)
| {
| static if (__traits(compiles, visitor(forward!args)))
| return visitor(forward!args);
| else
| return throwMe(variantMemberException);
| }
| else
| static if (exhaustive == Exhaustive.nullable)
| {
| static if (__traits(compiles, visitor(forward!args)))
| return Nullable!(typeof(visitor(forward!args)))(visitor(forward!args));
| else
| return Nullable!().init;
| }
| else
| static if (exhaustive == Exhaustive.auto_)
| {
| static if (__traits(compiles, visitor(forward!args)))
| return visitor(forward!args);
| else
| return Nullable!().init;
| }
| else
| static assert(0, "not implemented");
| }
| else
| static if (!isLikeVariant!(Args[0]))
| {
| return .visitImpl!(nextVisitor!(visitor, args[0]), exhaustive, fused)(forward!(args[1 .. $]));
| }
| else
| {
| static if (fused && anySatisfy!(isLikeVariant, Args[1 .. $]))
| {
| alias fun = visitThis!(visitor, exhaustive);
| }
| else
| {
| static assert (isLikeVariant!(Args[0]), "First argument should be a Mir Algebraic type");
| alias fun = visitLast!visitor;
| }
|
| template VariantReturnTypesImpl(T)
| {
| static if (__traits(compiles, fun!T(forward!args)))
| {
| alias R = typeof(fun!T(forward!args));
| static if (fused && isLikeVariant!R)
| alias VariantReturnTypesImpl = staticMap!(TryRemoveConst, R.AllowedTypes);
| else
| static if (is(immutable R == immutable noreturn))
| alias VariantReturnTypesImpl = AliasSeq!();
| else
| alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!R);
| }
| else
| static if (exhaustive == Exhaustive.auto_)
| alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
| else
| alias VariantReturnTypesImpl = AliasSeq!();
| }
|
| static if (exhaustive == Exhaustive.nullable)
| alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes));
| else
| alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes));
|
| switch (args[0].identifier__)
| {
| static foreach (i, T; Args[0].AllowedTypes)
| {
| case i:
| static if (__traits(compiles, fun!T(forward!args)) || exhaustive == Exhaustive.compileTime && !is(T == typeof(null)))
| {
| static if (AllReturnTypes.length == 1)
| {
| return fun!T(forward!args);
| }
| else
| static if (is(VariantReturnTypesImpl!T == AliasSeq!void))
| {
| fun!T(forward!args);
| return Variant!AllReturnTypes._void;
| }
| else
| static if (is(typeof(fun!T(forward!args)) == Variant!AllReturnTypes))
| {
| return fun!T(forward!args);
| }
| else
| {
| return Variant!AllReturnTypes(fun!T(forward!args));
| }
| }
| else
| static if (exhaustive == Exhaustive.compileTime && is(T == typeof(null)))
| {
| assert(0, "Null " ~ Args[0].stringof);
| }
| else
| static if (exhaustive == Exhaustive.nullable || exhaustive == Exhaustive.auto_)
| {
| return Variant!AllReturnTypes(null);
| }
| else
| {
| return throwMe(variantMemberException);
| }
| }
| default: assert(0);
| }
| }
| }
|}
|
|private string enumKindText()(scope const char[][] strs)
|{
| auto r = "enum Kind {";
| foreach (s; strs)
| {
| r ~= s;
| r ~= ", ";
| }
| r ~= "}";
| return r;
|}
|
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| static struct S { int a; }
|
| Variant!(S, double) variant;
| variant = 1.0;
| variant.tryVisit!((ref value, b) => value += b)(2);
| assert (variant.get!double == 3);
|
| alias fun = (ref value) {
| static if (is(typeof(value) == S))
| value.a += 2;
| else
| value += 2;
| };
|
| variant.tryVisit!fun;
| assert (variant.get!double == 5);
|
| variant = S(4);
| variant.tryVisit!fun;
| assert (variant.get!S.a == 6);
|
| alias D = Variant!(Variant!(S, double));
|}
|
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| import std.meta: AliasSeq;
|
| static struct PODWithLongPointer {
| long* x;
| this(long l) pure
| {
| x = new long(l);
| }
|
| @property:
| long a() const {
| return x ? *x : 0;
| }
|
| void a(long l) {
| if (x) {
| *x = l;
| } else {
| x = new long(l);
| }
| }
| }
| static assert(is(TypeSet!(byte, immutable PODWithLongPointer) == AliasSeq!(byte, immutable PODWithLongPointer)));
|}
|
|private enum isSimpleAggregateType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface);
|
|unittest
|{
| static struct Test
| {
| alias Value = void;
| }
|
| alias B = Nullable!Test;
|}
|
|/++
|Wrapper to denote an error value type.
|
|The wrapper is autostripped by $(LREF none).
|
|See_also: $(LREF reflectErr).
|+/
|template Err(T)
|{
| static if (!isErr!T)
| {
| ///
| struct Err
| {
| ///
| T value;
| }
| }
| else
| {
| alias Err = T;
| }
|}
|
|/// ditto
|auto err(T)(T value) {
| import core.lifetime: move;
| static if (isErr!T)
| return move(value);
| else
| return Err!T(move(value));
|}
|
|///
|unittest
|{
| @reflectErr static struct E {}
|
| static assert(is(Err!string == Err!string));
| static assert(is(Err!(Err!string) == Err!string));
| static assert(is(Err!E == E));
| static assert(is(Err!Exception == Exception));
|
| static assert(is(typeof("str".err) == Err!string));
| static assert(is(typeof(E().err) == E));
| static assert(is(typeof(new Exception("str").err) == Exception));
|}
|
|/// Strips out $(LREF Err) wrapper from the type.
|template stripErr(T)
|{
| static if (is(immutable T : immutable Err!U, U))
| alias stripErr = U;
| else
| alias stripErr = T;
|}
|
|///
|version(mir_core_test)
|unittest
|{
| static assert(is(stripErr!Exception == Exception));
| static assert(is(stripErr!string == string));
| static assert(is(stripErr!(Err!string) == string));
|}
|
|/++
|
|See_also: $(LREF some) and $(LREF none).
|
|Params:
| visitors = visitors to $(LREF match) with.
|+/
|alias suit(alias filter, visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true, filter);
|
|///
|version(mir_core_test)
|@safe pure nothrow @nogc unittest
|{
| import std.traits: isDynamicArray, Unqual;
| import std.meta: templateNot;
| alias V = Variant!(long, int, string, long[], int[]);
| alias autoGetElementType = match!(
| (string s) => "string", // we override the suit handler below for string
| suit!(isDynamicArray, a => Unqual!(typeof(a[0])).stringof),
| suit!(templateNot!isDynamicArray, a => Unqual!(typeof(a)).stringof),
| );
| assert(autoGetElementType(V(string.init)) == "string");
| assert(autoGetElementType(V((long[]).init)) == "long");
| assert(autoGetElementType(V((int[]).init)) == "int");
| assert(autoGetElementType(V(long.init)) == "long");
| assert(autoGetElementType(V(int.init)) == "int");
|}
|
|///
|version(mir_core_test)
|@safe pure nothrow @nogc unittest
|{
| import std.traits: allSameType;
| import std.meta: templateNot;
|
| static struct Asteroid { uint size; }
| static struct Spaceship { uint size; }
| alias SpaceObject = Variant!(Asteroid, Spaceship);
|
| auto errorMsg = "can't unite an asteroid with a spaceship".err;
|
| alias unite = match!(
| suit!(allSameType, (a, b) => typeof(a)(a.size + b.size)),
| suit!(templateNot!allSameType, (a, b) => errorMsg),
| );
|
| auto ea = Asteroid(10);
| auto es = Spaceship(1);
| auto oa = SpaceObject(ea);
| auto os = SpaceObject(es);
|
| static assert(is(typeof(unite(oa, oa)) == Variant!(Err!string, Asteroid, Spaceship)));
|
| // Asteroid-Asteroid
| assert(unite(ea, ea) == Asteroid(20));
| assert(unite(ea, oa) == Asteroid(20));
| assert(unite(oa, ea) == Asteroid(20));
| assert(unite(oa, oa) == Asteroid(20));
|
| // Asteroid-Spaceship
| assert(unite(ea, es) == errorMsg);
| assert(unite(ea, os) == errorMsg);
| assert(unite(oa, es) == errorMsg);
| assert(unite(oa, os) == errorMsg);
|
| // Spaceship-Asteroid
| assert(unite(es, ea) == errorMsg);
| assert(unite(es, oa) == errorMsg);
| assert(unite(os, ea) == errorMsg);
| assert(unite(os, oa) == errorMsg);
|
| // Spaceship-Spaceship
| assert(unite(es, es) == Spaceship(2));
| assert(unite(es, os) == Spaceship(2));
| assert(unite(os, es) == Spaceship(2));
| assert(unite(os, os) == Spaceship(2));
|}
|
|private template unwrapErrImpl(alias arg)
|{
| static if (is(immutable typeof(arg) == immutable Err!V, V))
| auto ref unwrapErrImpl() @property { return arg.value; }
| else
| alias unwrapErrImpl = arg;
|}
|
|private template unwrapErr(alias fun)
|{
| auto ref unwrapErr(Args...)(auto ref return Args args)
| {
| import std.meta: staticMap;
| import std.format: format;
| enum expr = () {
| string ret = `fun(`;
| foreach(i, T; Args)
| {
| static if (is(immutable T == immutable Err!V, V))
| ret ~= `args[` ~ i.stringof ~ `].value, `;
| else
| ret ~= `args[` ~ i.stringof ~ `], `;
| }
| ret ~= `)`;
| return ret;
| }();
| return mixin(expr);
| }
|}
|
|/++
|$(LREF some) is a variant of $(LREF suit) that forces that type of any argument doesn't satisfy $(LREF isErr) template.
|
|$(LREF none) is a variant of $(LREF suit) that forces that type of all arguments satisfy $(LREF isErr) template. The handler automatically strips the $(LREF Err) wrapper.
|
|See_also: $(LREF suit), $(LREF Err), $(LREF isErr), $(LREF isResultVariant), and $(LREF reflectErr).
|
|Params:
| visitors = visitors to $(LREF match) with.
|+/
|alias some(visitors...) = suit!(allArgumentsIsNotInstanceOfErr, naryFun!visitors);
|
|/// ditto
|alias none(visitors...) = suit!(anyArgumentIsInstanceOfErr, unwrapErr!(naryFun!visitors));
|
|///
|version(mir_core_test)
|unittest
|{
| import mir.conv: to;
|
| alias orElse(alias fun) = visit!(some!"a", none!fun);
| alias errToString = orElse!(to!string);
|
| // can any other type including integer enum
| @reflectErr
| static struct ErrorInfo {
| string msg;
| auto toString() const { return msg; }
| }
|
| alias V = Variant!(Err!string, ErrorInfo, Exception, long, double);
| alias R = typeof(errToString(V.init));
|
| static assert(is(R == Variant!(string, long, double)), R.stringof);
|
| {
| V v = 1;
| assert(v.isOk);
| assert(errToString(v) == 1);
| }
|
| {
| V v = 1.0;
| assert(v.isOk);
| assert(errToString(v) == 1.0);
| }
|
| {
| V v = ErrorInfo("msg");
| assert(!v.isOk);
| assert(errToString(v) == "msg");
| }
|
| {
| V v = "msg".err;
| assert(!v.isOk);
| assert(errToString(v) == "msg");
| }
|
| {
| V v = new Exception("msg"); enum line = __LINE__;
| assert(!v.isOk);
| assert(errToString(v) == "object.Exception@" ~ __FILE__ ~ "(" ~ line.stringof ~ "): msg");
| }
|}
|
|/++
|Attribute that denotes an error type. Can be used with $(LREF some) and $(LREF none).
|
|See_also: $(LREF Err).
|+/
|enum reflectErr;
|
|/++
|Checks if T is a instance of $(LREF Err) or if it is annotated with $(LREF reflectErr).
|+/
|template isErr(T)
|{
| import std.traits: isAggregateType, hasUDA;
| static if (is(T == enum) || isAggregateType!T)
| {
| static if (is(immutable T == immutable Err!V, V))
| {
| enum isErr = true;
| }
| else
| static if (hasUDA!(T, reflectErr))
| {
| enum isErr = true;
| }
| else
| version (D_Exceptions)
| {
| enum isErr = is(immutable T : immutable Throwable);
| }
| else
| {
| enum isErr = false;
| }
| }
| else
| {
| enum isErr = false;
| }
|}
|
|/++
|Checks if T is a Variant with at least one allowed type that satisfy $(LREF isErr) traits.
|+/
|template isResultVariant(T)
|{
| static if (is(immutable T == immutable Algebraic!Types, Types...))
| {
| import std.meta: anySatisfy;
| enum isResultVariant = anySatisfy!(isErr, Types);
| }
| else
| {
| enum isResultVariant = false;
| }
|}
|
|deprecated("Use isResultVariant instead") alias isErrVariant = isResultVariant;
|
|private template anyArgumentIsInstanceOfErr(Args...)
|{
| import std.meta: anySatisfy;
| enum anyArgumentIsInstanceOfErr = anySatisfy!(isErr, Args);
|}
|
|private template allArgumentsIsNotInstanceOfErr(Args...)
|{
| import std.meta: anySatisfy;
| enum allArgumentsIsNotInstanceOfErr = !anySatisfy!(isErr, Args);
|}
|
|/++
|Gets subtype of algebraic without types for which $(LREF isErr) is true.
|+/
|template SomeVariant(T : Algebraic!Types, Types...)
|{
| import std.meta: Filter, templateNot;
| alias SomeVariant = Algebraic!(Filter!(templateNot!isErr, Types));
|}
|
|///
|@safe pure version(mir_core_test) unittest
|{
| @reflectErr static struct ErrorS { }
| alias V = Variant!(ErrorS, Err!string, long, double, This[]);
| static assert(is(SomeVariant!V == Variant!(long, double, This[])));
|}
|
|/++
|Gets subtype of algebraic with types for which $(LREF isErr) is true.
|+/
|template NoneVariant(T : Algebraic!Types, Types...)
|{
| import std.meta: Filter;
| alias NoneVariant = Algebraic!(Filter!(isErr, Types));
|}
|
|///
|@safe pure version(mir_core_test) unittest
|{
| @reflectErr static struct ErrorS { }
| alias V = Variant!(ErrorS, Err!string, long, double, This[]);
| static assert(is(NoneVariant!V == Variant!(ErrorS, Err!string)));
|}
|
|private template withNewLine(alias arg)
|{
| import std.meta: AliasSeq;
| alias withNewLine = AliasSeq!("\n", arg);
|}
|
|private noreturn throwMe(T...)(auto ref T args) {
| static if (T.length == 1)
| enum simpleThrow = is(immutable T[0] : immutable Throwable);
| else
| enum simpleThrow = false;
| static if (simpleThrow)
| {
| throw args[0];
| }
| else
| {
| import mir.exception: MirException;
| static if (__traits(compiles, { import mir.format: print; }))
| {
| import std.meta: staticMap;
| throw new MirException("assumeOk failure:", staticMap!(withNewLine, args));
| }
| else
| {
| import mir.conv: to;
| auto msg = "assumeOk failure:";
| foreach(ref arg; args)
| {
| msg ~= "\n";
| msg ~= arg.to!string;
| }
| throw new MirException(msg);
| }
| }
|}
|
|version(D_Exceptions)
|/++
|Validates that the result doesn't contain an error value.
|
|Params:
| visitor = (compiletime) visitor function. Default value is `naryFun!("", "a")`.
| handler = (compiletime) visitor handler to use. Default value is $(LREF match).
|Throws:
| Throws an exception if at least one parameter passed to
| `visitor` satisfies $(LREF isErr) traits.
| If there is only one paramter (common case) and its value is `Throwable`, throws it.
| Otherwise, _all_ paramters will be printed to the exception message using `mir.format.print`.
|+/
|alias assumeOk(alias visitor = naryFun!("", "a"), alias handler = .match) = handler!(some!visitor, none!throwMe);
|
|///
|version(mir_core_test) version(D_Exceptions)
|unittest
|{
| import std.exception: collectExceptionMsg;
| import mir.exception: MirException;
|
| alias SingleTypeValue = typeof(assumeOk(Variant!(Exception, long).init));
| static assert(is(SingleTypeValue == long), SingleTypeValue.stringof);
|
|
| // can any other type including integer enum
| @reflectErr
| static struct ErrorInfo {
| string msg;
| auto toString() const { return msg; }
| }
|
| alias V = Variant!(Err!string, ErrorInfo, Exception, long, double);
| alias R = typeof(assumeOk(V.init));
|
| static assert(is(R == Variant!(long, double)), R.stringof);
|
| {
| V v = 1;
| assert(v.isOk);
| assert(v.assumeOk == 1);
| }
|
| {
| V v = 1.0;
| assert(v.isOk);
| assert(v.assumeOk == 1.0);
| }
|
| {
| V v = ErrorInfo("msg");
| assert(!v.isOk);
| assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg");
| }
|
| {
| V v = "msg".err;
| assert(!v.isOk);
| assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg");
| }
|
| {
| V v = new Exception("msg");
| assert(!v.isOk);
| assert(v.assumeOk.collectExceptionMsg == "msg");
| }
|}
|
|version(mir_core_test)
|unittest
|{
| static struct RequestToken
| {
| Variant!(long, string) value;
| alias value this;
|
| this(T)(T v)
| {
| value = typeof(value)(v);
| }
| }
|
| Variant!(int, RequestToken) v = RequestToken.init;
|
| auto r = v.match!(
| (int v) {
| return assert(false);
| },
| ret => ret
| );
|
| static assert(is(typeof(r) == Variant!(long, string)));
|}
|
|package auto trustedAllAttr(T)(scope T t) @trusted
|{
| import std.traits;
| enum attrs = (functionAttributes!T & ~FunctionAttribute.system)
| | FunctionAttribute.pure_
| | FunctionAttribute.safe
| | FunctionAttribute.nogc
| | FunctionAttribute.nothrow_;
| return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
|}
|
|private static immutable algebraicMembers = [
| "_is",
| "_void",
| "AllowedTypes",
| "MetaFieldsTypes",
| "get",
| "isNull",
| "kind",
| "Kind",
| "nullify",
| "opAssign",
| "opCast",
| "opCmp",
| "opEquals",
| "opPostMove",
| "toHash",
| "toString",
| "trustedGet",
| "deserializeFromAsdf",
| "deserializeFromIon",
|];
|
|private template UnqualRec(T)
|{
| import std.traits: Unqual, isDynamicArray, ForeachType;
| static if (isDynamicArray!T)
| alias UnqualRec = UnqualRec!(ForeachType!T)[];
| else
| alias UnqualRec = Unqual!T;
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/algebraic.d is 0% covered
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-bitop.lst
|/++
|This module contains a collection of bit-level operations.
|
|Authors: Ilia Ki, Phobos & LDC Authors (original Phobos unittests, docs, conventions).
|+/
|module mir.bitop;
|
|version(LDC)
| import ldc.intrinsics;
|version(GNU)
| import gcc.builtins;
|
|import mir.math.common: fastmath;
|
|/// Right shift vallue for bit index to get element's index (5 for `uint`).
|enum uint bitElemShift(T : ubyte) = 3;
|/// ditto
|enum uint bitElemShift(T : byte) = 3;
|/// ditto
|enum uint bitElemShift(T : ushort) = 4;
|/// ditto
|enum uint bitElemShift(T : short) = 4;
|/// ditto
|enum uint bitElemShift(T : uint) = 5;
|/// ditto
|enum uint bitElemShift(T : int) = 5;
|/// ditto
|enum uint bitElemShift(T : ulong) = 6;
|/// ditto
|enum uint bitElemShift(T : long) = 6;
|static if (is(ucent))
|/// ditto
|enum uint bitElemShift(T : ucent) = 7;
|/// ditto
|static if (is(cent))
|enum uint bitElemShift(T : cent) = 7;
|
|/// Bit mask for bit index to get element's bit shift (31 for uint).
|enum uint bitShiftMask(T : ubyte) = 7;
|/// ditto
|enum uint bitShiftMask(T : byte) = 7;
|/// ditto
|enum uint bitShiftMask(T : ushort) = 15;
|/// ditto
|enum uint bitShiftMask(T : short) = 15;
|/// ditto
|enum uint bitShiftMask(T : uint) = 31;
|/// ditto
|enum uint bitShiftMask(T : int) = 31;
|/// ditto
|enum uint bitShiftMask(T : ulong) = 63;
|/// ditto
|enum uint bitShiftMask(T : long) = 63;
|static if (is(ucent))
|/// ditto
|enum uint bitShiftMask(T : ucent) = 127;
|static if (is(cent))
|/// ditto
|enum uint bitShiftMask(T : cent) = 127;
|
|// no effect on this function, but better for optimization of other @fastmath code that uses this
|@fastmath:
|
|
|/++
|+/
|T nTrailingBitsToCount(T)(in T value, in T popcnt)
| if (__traits(isUnsigned, T))
|{
| import std.traits;
| import mir.internal.utility: Iota;
| alias S = Signed!(CommonType!(int, T));
| S mask = S(-1) << T.sizeof * 4;
| foreach_reverse (s; Iota!(bitElemShift!T - 1))
| {{
| enum shift = 1 << s;
| if (S(popcnt) > S(ctpop(cast(T)(value & ~mask))))
| mask <<= shift;
| else
| mask >>= shift;
| }}
| return cttz(cast(T)mask) + (S(popcnt) != ctpop(cast(T)(value & ~mask)));
|}
|
|///
|version(mir_core_test) unittest
|{
| assert(nTrailingBitsToCount(0xF0u, 3u) == 7);
| assert(nTrailingBitsToCount(0xE00u, 3u) == 12);
|
| foreach(uint i; 1 .. 32)
| assert(nTrailingBitsToCount(uint.max, i) == i);
|}
|
|/++
|+/
|T nLeadingBitsToCount(T)(in T value, in T popcnt)
| if (__traits(isUnsigned, T))
|{
| import std.traits;
| import mir.internal.utility: Iota;
| alias S = Signed!(CommonType!(int, T));
| S mask = S(-1) << T.sizeof * 4;
| foreach_reverse (s; Iota!(bitElemShift!T - 1))
| {{
| enum shift = 1 << s;
| if (S(popcnt) > S(ctpop(cast(T)(value & mask))))
| mask >>= shift;
| else
| mask <<= shift;
| }}
| return ctlz(cast(T)~mask) + (S(popcnt) != ctpop(cast(T)(value & mask)));
|}
|
|///
|version(mir_core_test) unittest
|{
| assert(nLeadingBitsToCount(0xF0u, 3u) == 32 - 5);
| assert(nLeadingBitsToCount(0x700u, 3u) == 32 - 8);
|
| foreach(uint i; 1 .. 32)
| assert(nLeadingBitsToCount(uint.max, i) == i);
|}
|
|/++
|Tests the bit.
|Returns:
| A non-zero value if the bit was set, and a zero
| if it was clear.
|+/
|auto bt(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
| if (__traits(isUnsigned, T))
|{
| auto index = bitnum >> bitElemShift!T;
| auto mask = T(1) << (bitnum & bitShiftMask!T);
| return p[index] & mask;
|}
|
|///
|@system pure version(mir_core_test) unittest
|{
| size_t[2] array;
|
| array[0] = 2;
| array[1] = 0x100;
|
| assert(bt(array.ptr, 1));
| assert(array[0] == 2);
| assert(array[1] == 0x100);
|}
|
|/++
|Tests and assign the bit.
|Returns:
| A non-zero value if the bit was set, and a zero if it was clear.
|+/
|auto bta(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum, bool value)
| if (__traits(isUnsigned, T))
|{
| auto index = bitnum >> bitElemShift!T;
| auto shift = bitnum & bitShiftMask!T;
| auto mask = T(1) << shift;
| static if (__traits(compiles, &p[size_t.init]))
| {
| auto qp = &p[index];
| auto q = *qp;
| auto ret = q & mask;
| *qp = cast(T)((q & ~mask) ^ (T(value) << shift));
| }
| else
| {
| auto q = p[index];
| auto ret = q & mask;
| p[index] = cast(T)((q & ~mask) ^ (T(value) << shift));
| }
| return ret;
|}
|
|/++
|Tests and complements the bit.
|Returns:
| A non-zero value if the bit was set, and a zero if it was clear.
|+/
|auto btc(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
| if (__traits(isUnsigned, T))
|{
| auto index = bitnum >> bitElemShift!T;
| auto mask = T(1) << (bitnum & bitShiftMask!T);
| static if (__traits(compiles, &p[size_t.init]))
| {
| auto qp = &p[index];
| auto q = *qp;
| auto ret = q & mask;
| *qp = cast(T)(q ^ mask);
| }
| else
| {
| auto q = p[index];
| auto ret = q & mask;
| p[index] = cast(T)(q ^ mask);
| }
| return ret;
|}
|
|/++
|Tests and resets (sets to 0) the bit.
|Returns:
| A non-zero value if the bit was set, and a zero if it was clear.
|+/
|auto btr(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
| if (__traits(isUnsigned, T))
|{
| auto index = bitnum >> bitElemShift!T;
| auto mask = T(1) << (bitnum & bitShiftMask!T);
| static if (__traits(compiles, &p[size_t.init]))
| {
| auto qp = &p[index];
| auto q = *qp;
| auto ret = q & mask;
| *qp = cast(T)(q & ~mask);
| }
| else
| {
| auto q = p[index];
| auto ret = q & mask;
| p[index] = cast(T)(q & ~mask);
| }
| return ret;
|}
|
|/++
|Tests and sets the bit.
|Params:
|p = a non-NULL field / pointer to an array of unsigned integers.
|bitnum = a bit number, starting with bit 0 of p[0],
|and progressing. It addresses bits like the expression:
|---
|p[index / (T.sizeof*8)] & (1 << (index & ((T.sizeof*8) - 1)))
|---
|Returns:
| A non-zero value if the bit was set, and a zero if it was clear.
|+/
|auto bts(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
| if (__traits(isUnsigned, T))
|{
| auto index = bitnum >> bitElemShift!T;
| auto mask = T(1) << (bitnum & bitShiftMask!T);
| static if (__traits(compiles, &p[size_t.init]))
| {
| auto qp = &p[index];
| auto q = *qp;
| auto ret = q & mask;
| *qp = cast(T)(q | mask);
| }
| else
| {
| auto q = p[index];
| auto ret = q & mask;
| p[index] = cast(T)(q | mask);
| }
| return ret;
|}
|
|///
|@system pure version(mir_core_test) unittest
|{
| size_t[2] array;
|
| array[0] = 2;
| array[1] = 0x100;
|
| assert(btc(array.ptr, 35) == 0);
| if (size_t.sizeof == 8)
| {
| assert(array[0] == 0x8_0000_0002);
| assert(array[1] == 0x100);
| }
| else
| {
| assert(array[0] == 2);
| assert(array[1] == 0x108);
| }
|
| assert(btc(array.ptr, 35));
| assert(array[0] == 2);
| assert(array[1] == 0x100);
|
| assert(bts(array.ptr, 35) == 0);
| if (size_t.sizeof == 8)
| {
| assert(array[0] == 0x8_0000_0002);
| assert(array[1] == 0x100);
| }
| else
| {
| assert(array[0] == 2);
| assert(array[1] == 0x108);
| }
|
| assert(btr(array.ptr, 35));
| assert(array[0] == 2);
| assert(array[1] == 0x100);
|}
|
|/// The 'ctpop' family of intrinsics counts the number of bits set in a value.
|T ctpop(T)(in T src)
| if (__traits(isUnsigned, T))
|{
| version(LDC) if (!__ctfe)
| return llvm_ctpop(src);
| version(GNU) if (!__ctfe)
| {
| static if (T.sizeof < __builtin_clong.sizeof)
| return cast(T) __builtin_popcount(src);
| else static if (T.sizeof <= __builtin_clong.sizeof)
| return cast(T) __builtin_popcountl(src);
| else
| return cast(T) __builtin_popcountll(src);
| }
| import core.bitop: popcnt;
| return cast(T) popcnt(src);
|}
|
|/++
|The 'ctlz' family of intrinsic functions counts the number of leading zeros in a variable.
|Result is undefined if the argument is zero.
|+/
|T ctlz(T)(in T src)
| if (__traits(isUnsigned, T))
|{
| version(LDC) if (!__ctfe)
| return llvm_ctlz(src, true);
| version(GNU) if (!__ctfe)
| {
| // Do not zero-extend when counting leading zeroes.
| static if (T.sizeof < __builtin_clong.sizeof && T.sizeof >= uint.sizeof)
| return cast(T) __builtin_clz(src);
| else static if (T.sizeof == __builtin_clong.sizeof)
| return cast(T) __builtin_clzl(src);
| else static if (T.sizeof > __builtin_clong.sizeof)
| return cast(T) __builtin_clzll(src);
| }
| import core.bitop: bsr;
0000000| return cast(T)(T.sizeof * 8 - 1 - bsr(src));
|}
|
|///
|version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
|{
| assert(ctlz(cast(ubyte) 0b0011_1111) == 2);
| assert(ctlz(cast(ushort) 0b0000_0001_1111_1111) == 7);
|}
|
|/++
|The 'ctlzp' family of intrinsic functions counts the number of leading zeros in a variable.
|Result is properly defined if the argument is zero.
|+/
|T ctlzp(T)(in T src)
| if (__traits(isUnsigned, T))
|{
| version(LDC) if (!__ctfe)
| return llvm_ctlz(src, false);
| return src ? ctlz(src) : T.sizeof * 8;
|}
|
|///
|version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
|{
| assert(ctlzp(cast(ubyte) 0b0000_0000) == 8);
| assert(ctlzp(cast(ubyte) 0b0011_1111) == 2);
| assert(ctlzp(cast(ushort) 0b0000_0001_1111_1111) == 7);
| assert(ctlzp(cast(ushort) 0) == 16);
| assert(ctlzp(cast(ulong) 0) == 64);
|}
|
|/++
|The 'cttz' family of intrinsic functions counts the number of trailing zeros.
|Result is undefined if the argument is zero.
|+/
|T cttz(T)(in T src)
| if (__traits(isUnsigned, T))
|{
| version(LDC) if (!__ctfe)
| return llvm_cttz(src, true);
| version(GNU) if (!__ctfe)
| {
| static if (T.sizeof <__builtin_clong.sizeof)
| return cast(T) __builtin_ctz(src);
| else static if (T.sizeof <=__builtin_clong.sizeof)
| return cast(T) __builtin_ctzl(src);
| else
| return cast(T) __builtin_ctzll(src);
| }
| import core.bitop: bsf;
| return cast(T) bsf(src);
|}
|
|///
|version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
|{
| assert(cttzp(cast(ubyte) 0b11111100) == 2);
| assert(cttzp(cast(ushort) 0b1111111110000000) == 7);
|}
|
|/++
|The 'cttz' family of intrinsic functions counts the number of trailing zeros.
|Result is properly defined if the argument is zero.
|+/
|T cttzp(T)(in T src)
| if (__traits(isUnsigned, T))
|{
| version(LDC) if (!__ctfe)
| return llvm_cttz(src, false);
| return src ? cttz(src) : T.sizeof * 8;
|}
|
|///
|version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
|{
| assert(cttzp(cast(ubyte) 0b0000_0000) == 8);
| assert(cttzp(cast(ubyte) 0b11111100) == 2);
| assert(cttzp(cast(ushort) 0b1111111110000000) == 7);
| assert(cttzp(cast(ushort) 0) == 16);
| assert(cttzp(cast(ulong) 0) == 64);
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/bitop.d is 0% covered
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-checkedint.lst
|
|/**********************************************
| * This module implements integral arithmetic primitives that check
| * for out-of-range results.
| *
| * Integral arithmetic operators operate on fixed width types.
| * Results that are not representable in those fixed widths are silently
| * truncated to fit.
| * This module offers integral arithmetic primitives that produce the
| * same results, but set an 'overflow' flag when such truncation occurs.
| * The setting is sticky, meaning that numerous operations can be cascaded
| * and then the flag need only be checked at the end.
| * Whether the operation is signed or unsigned is indicated by an 's' or 'u'
| * suffix, respectively. While this could be achieved without such suffixes by
| * using overloading on the signedness of the types, the suffix makes it clear
| * which is happening without needing to examine the types.
| *
| * While the generic versions of these functions are computationally expensive
| * relative to the cost of the operation itself, compiler implementations are free
| * to recognize them and generate equivalent and faster code.
| *
| * References: $(LINK2 http://blog.regehr.org/archives/1139, Fast Integer Overflow Checks)
| * License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
| * Authors: Walter Bright
| * Source: $(DRUNTIMESRC core/_checkedint.d)
| */
|
|module mir.checkedint;
|
|version (GNU)
|{
| // GDC uses intrinsics for core.checkedint functions.
| public import core.checkedint : adds, addu, subs, subu, muls, mulu, negs;
|}
|else:
|
|version (LDC)
|{
| import ldc.intrinsics;
|
| // llvm.(u)mul.with.overflow.i64 might fall back to a software implementation
| // in the form of __mulodi4, which only exists in compiler-rt and not
| // libgcc. Thus, we need to be sure not to emit it for now (see GitHub #818).
| version (X86_64)
| version = LDC_HasNativeI64Mul;
|}
|
|nothrow:
|@safe:
|@nogc:
|pure:
|
|/*******************************
| * Add two signed integers, checking for overflow.
| *
| * The overflow is sticky, meaning a sequence of operations can
| * be done and overflow need only be checked at the end.
| * Params:
| * x = left operand
| * y = right operand
| * overflow = set if an overflow occurs, is not affected otherwise
| * Returns:
| * the sum
| */
|
|pragma(inline, true)
|int adds(int x, int y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_sadd_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| long r = cast(long)x + cast(long)y;
0000000| if (r < int.min || r > int.max)
0000000| overflow = true;
0000000| return cast(int)r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(adds(2, 3, overflow) == 5);
| assert(!overflow);
| assert(adds(1, int.max - 1, overflow) == int.max);
| assert(!overflow);
| assert(adds(int.min + 1, -1, overflow) == int.min);
| assert(!overflow);
| assert(adds(int.max, 1, overflow) == int.min);
| assert(overflow);
| overflow = false;
| assert(adds(int.min, -1, overflow) == int.max);
| assert(overflow);
| assert(adds(0, 0, overflow) == 0);
| assert(overflow); // sticky
|}
|
|/// ditto
|pragma(inline, true)
|long adds(long x, long y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_sadd_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| long r = cast(ulong)x + cast(ulong)y;
0000000| if (x < 0 && y < 0 && r >= 0 ||
0000000| x >= 0 && y >= 0 && r < 0)
0000000| overflow = true;
0000000| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(adds(2L, 3L, overflow) == 5);
| assert(!overflow);
| assert(adds(1L, long.max - 1, overflow) == long.max);
| assert(!overflow);
| assert(adds(long.min + 1, -1, overflow) == long.min);
| assert(!overflow);
| assert(adds(long.max, 1, overflow) == long.min);
| assert(overflow);
| overflow = false;
| assert(adds(long.min, -1, overflow) == long.max);
| assert(overflow);
| assert(adds(0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|
|static if (is(cent))
|{
|/// ditto
|pragma(inline, true)
|cent adds(cent x, cent y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_sadd_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
| cent r = cast(ucent)x + cast(ucent)y;
| if (x < 0 && y < 0 && r >= 0 ||
| x >= 0 && y >= 0 && r < 0)
| overflow = true;
| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(adds(cast(cent)2L, 3L, overflow) == 5);
| assert(!overflow);
| assert(adds(1L, cent.max - 1, overflow) == cent.max);
| assert(!overflow);
| assert(adds(cent.min + 1, -1, overflow) == cent.min);
| assert(!overflow);
| assert(adds(cent.max, 1, overflow) == cent.min);
| assert(overflow);
| overflow = false;
| assert(adds(cent.min, -1, overflow) == cent.max);
| assert(overflow);
| assert(adds(cast(cent)0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|}
|
|
|/*******************************
| * Add two unsigned integers, checking for overflow (aka carry).
| *
| * The overflow is sticky, meaning a sequence of operations can
| * be done and overflow need only be checked at the end.
| * Params:
| * x = left operand
| * y = right operand
| * overflow = set if an overflow occurs, is not affected otherwise
| * Returns:
| * the sum
| */
|
|pragma(inline, true)
|uint addu(uint x, uint y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_uadd_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| immutable uint r = x + y;
0000000| if (r < x || r < y)
0000000| overflow = true;
0000000| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(addu(2, 3, overflow) == 5);
| assert(!overflow);
| assert(addu(1, uint.max - 1, overflow) == uint.max);
| assert(!overflow);
| assert(addu(uint.min, -1, overflow) == uint.max);
| assert(!overflow);
| assert(addu(uint.max, 1, overflow) == uint.min);
| assert(overflow);
| overflow = false;
| assert(addu(uint.min + 1, -1, overflow) == uint.min);
| assert(overflow);
| assert(addu(0, 0, overflow) == 0);
| assert(overflow); // sticky
|}
|
|/// ditto
|pragma(inline, true)
|ulong addu(ulong x, ulong y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_uadd_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
15| immutable ulong r = x + y;
30| if (r < x || r < y)
0000000| overflow = true;
15| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(addu(2L, 3L, overflow) == 5);
| assert(!overflow);
| assert(addu(1, ulong.max - 1, overflow) == ulong.max);
| assert(!overflow);
| assert(addu(ulong.min, -1L, overflow) == ulong.max);
| assert(!overflow);
| assert(addu(ulong.max, 1, overflow) == ulong.min);
| assert(overflow);
| overflow = false;
| assert(addu(ulong.min + 1, -1L, overflow) == ulong.min);
| assert(overflow);
| assert(addu(0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|
|static if (is(ucent))
|{
|/// ditto
|pragma(inline, true)
|ucent addu(ucent x, ucent y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_uadd_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
| immutable ucent r = x + y;
| if (r < x || r < y)
| overflow = true;
| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(addu(cast(ucent)2L, 3L, overflow) == 5);
| assert(!overflow);
| assert(addu(1, ucent.max - 1, overflow) == ucent.max);
| assert(!overflow);
| assert(addu(ucent.min, -1L, overflow) == ucent.max);
| assert(!overflow);
| assert(addu(ucent.max, 1, overflow) == ucent.min);
| assert(overflow);
| overflow = false;
| assert(addu(ucent.min + 1, -1L, overflow) == ucent.min);
| assert(overflow);
| assert(addu(cast(ucent)0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|}
|
|
|/*******************************
| * Subtract two signed integers, checking for overflow.
| *
| * The overflow is sticky, meaning a sequence of operations can
| * be done and overflow need only be checked at the end.
| * Params:
| * x = left operand
| * y = right operand
| * overflow = set if an overflow occurs, is not affected otherwise
| * Returns:
| * the difference
| */
|
|pragma(inline, true)
|int subs(int x, int y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_ssub_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| immutable long r = cast(long)x - cast(long)y;
0000000| if (r < int.min || r > int.max)
0000000| overflow = true;
0000000| return cast(int)r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(subs(2, -3, overflow) == 5);
| assert(!overflow);
| assert(subs(1, -int.max + 1, overflow) == int.max);
| assert(!overflow);
| assert(subs(int.min + 1, 1, overflow) == int.min);
| assert(!overflow);
| assert(subs(int.max, -1, overflow) == int.min);
| assert(overflow);
| overflow = false;
| assert(subs(int.min, 1, overflow) == int.max);
| assert(overflow);
| assert(subs(0, 0, overflow) == 0);
| assert(overflow); // sticky
|}
|
|/// ditto
|pragma(inline, true)
|long subs(long x, long y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_ssub_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| immutable long r = cast(ulong)x - cast(ulong)y;
0000000| if (x < 0 && y >= 0 && r >= 0 ||
0000000| x >= 0 && y < 0 && (r < 0 || y == long.min))
0000000| overflow = true;
0000000| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(subs(2L, -3L, overflow) == 5);
| assert(!overflow);
| assert(subs(1L, -long.max + 1, overflow) == long.max);
| assert(!overflow);
| assert(subs(long.min + 1, 1, overflow) == long.min);
| assert(!overflow);
| assert(subs(-1L, long.min, overflow) == long.max);
| assert(!overflow);
| assert(subs(long.max, -1, overflow) == long.min);
| assert(overflow);
| overflow = false;
| assert(subs(long.min, 1, overflow) == long.max);
| assert(overflow);
| assert(subs(0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|
|static if (is(cent))
|{
|/// ditto
|pragma(inline, true)
|cent subs(cent x, cent y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_ssub_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
| immutable cent r = cast(ucent)x - cast(ucent)y;
| if (x < 0 && y >= 0 && r >= 0 ||
| x >= 0 && y < 0 && (r < 0 || y == long.min))
| overflow = true;
| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(subs(cast(cent)2L, -3L, overflow) == 5);
| assert(!overflow);
| assert(subs(1L, -cent.max + 1, overflow) == cent.max);
| assert(!overflow);
| assert(subs(cent.min + 1, 1, overflow) == cent.min);
| assert(!overflow);
| assert(subs(-1L, cent.min, overflow) == cent.max);
| assert(!overflow);
| assert(subs(cent.max, -1, overflow) == cent.min);
| assert(overflow);
| overflow = false;
| assert(subs(cent.min, 1, overflow) == cent.max);
| assert(overflow);
| assert(subs(cast(cent)0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|}
|
|
|/*******************************
| * Subtract two unsigned integers, checking for overflow (aka borrow).
| *
| * The overflow is sticky, meaning a sequence of operations can
| * be done and overflow need only be checked at the end.
| * Params:
| * x = left operand
| * y = right operand
| * overflow = set if an overflow occurs, is not affected otherwise
| * Returns:
| * the difference
| */
|
|pragma(inline, true)
|uint subu(uint x, uint y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_usub_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| if (x < y)
0000000| overflow = true;
0000000| return x - y;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(subu(3, 2, overflow) == 1);
| assert(!overflow);
| assert(subu(uint.max, 1, overflow) == uint.max - 1);
| assert(!overflow);
| assert(subu(1, 1, overflow) == uint.min);
| assert(!overflow);
| assert(subu(0, 1, overflow) == uint.max);
| assert(overflow);
| overflow = false;
| assert(subu(uint.max - 1, uint.max, overflow) == uint.max);
| assert(overflow);
| assert(subu(0, 0, overflow) == 0);
| assert(overflow); // sticky
|}
|
|
|/// ditto
|pragma(inline, true)
|ulong subu(ulong x, ulong y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_usub_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| if (x < y)
0000000| overflow = true;
0000000| return x - y;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(subu(3UL, 2UL, overflow) == 1);
| assert(!overflow);
| assert(subu(ulong.max, 1, overflow) == ulong.max - 1);
| assert(!overflow);
| assert(subu(1UL, 1UL, overflow) == ulong.min);
| assert(!overflow);
| assert(subu(0UL, 1UL, overflow) == ulong.max);
| assert(overflow);
| overflow = false;
| assert(subu(ulong.max - 1, ulong.max, overflow) == ulong.max);
| assert(overflow);
| assert(subu(0UL, 0UL, overflow) == 0);
| assert(overflow); // sticky
|}
|
|static if (is(ucent))
|{
|/// ditto
|pragma(inline, true)
|ucent subu(ucent x, ucent y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_usub_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
| if (x < y)
| overflow = true;
| return x - y;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(subu(cast(ucent)3UL, 2UL, overflow) == 1);
| assert(!overflow);
| assert(subu(ucent.max, 1, overflow) == ucent.max - 1);
| assert(!overflow);
| assert(subu(1UL, 1UL, overflow) == ucent.min);
| assert(!overflow);
| assert(subu(cast(ucent)0UL, 1UL, overflow) == ucent.max);
| assert(overflow);
| overflow = false;
| assert(subu(ucent.max - 1, ucent.max, overflow) == ucent.max);
| assert(overflow);
| assert(subu(cast(ucent)0UL, 0UL, overflow) == 0);
| assert(overflow); // sticky
|}
|}
|
|
|/***********************************************
| * Negate an integer.
| *
| * Params:
| * x = operand
| * overflow = set if x cannot be negated, is not affected otherwise
| * Returns:
| * the negation of x
| */
|
|pragma(inline, true)
|int negs(int x, scope ref bool overflow)
|{
0000000| if (x == int.min)
0000000| overflow = true;
0000000| return -x;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(negs(0, overflow) == -0);
| assert(!overflow);
| assert(negs(1234, overflow) == -1234);
| assert(!overflow);
| assert(negs(-5678, overflow) == 5678);
| assert(!overflow);
| assert(negs(int.min, overflow) == -int.min);
| assert(overflow);
| assert(negs(0, overflow) == -0);
| assert(overflow); // sticky
|}
|
|/// ditto
|pragma(inline, true)
|long negs(long x, scope ref bool overflow)
|{
0000000| if (x == long.min)
0000000| overflow = true;
0000000| return -x;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(negs(0L, overflow) == -0);
| assert(!overflow);
| assert(negs(1234L, overflow) == -1234);
| assert(!overflow);
| assert(negs(-5678L, overflow) == 5678);
| assert(!overflow);
| assert(negs(long.min, overflow) == -long.min);
| assert(overflow);
| assert(negs(0L, overflow) == -0);
| assert(overflow); // sticky
|}
|
|static if (is(cent))
|{
|/// ditto
|pragma(inline, true)
|cent negs(cent x, scope ref bool overflow)
|{
| if (x == cent.min)
| overflow = true;
| return -x;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(negs(cast(cent)0L, overflow) == -0);
| assert(!overflow);
| assert(negs(cast(cent)1234L, overflow) == -1234);
| assert(!overflow);
| assert(negs(cast(cent)-5678L, overflow) == 5678);
| assert(!overflow);
| assert(negs(cent.min, overflow) == -cent.min);
| assert(overflow);
| assert(negs(cast(cent)0L, overflow) == -0);
| assert(overflow); // sticky
|}
|}
|
|
|/*******************************
| * Multiply two signed integers, checking for overflow.
| *
| * The overflow is sticky, meaning a sequence of operations can
| * be done and overflow need only be checked at the end.
| * Params:
| * x = left operand
| * y = right operand
| * overflow = set if an overflow occurs, is not affected otherwise
| * Returns:
| * the product
| */
|
|pragma(inline, true)
|int muls(int x, int y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_smul_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| long r = cast(long)x * cast(long)y;
0000000| if (r < int.min || r > int.max)
0000000| overflow = true;
0000000| return cast(int)r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(muls(2, 3, overflow) == 6);
| assert(!overflow);
| assert(muls(-200, 300, overflow) == -60_000);
| assert(!overflow);
| assert(muls(1, int.max, overflow) == int.max);
| assert(!overflow);
| assert(muls(int.min, 1, overflow) == int.min);
| assert(!overflow);
| assert(muls(int.max, 2, overflow) == (int.max * 2));
| assert(overflow);
| overflow = false;
| assert(muls(int.min, -1, overflow) == int.min);
| assert(overflow);
| assert(muls(0, 0, overflow) == 0);
| assert(overflow); // sticky
|}
|
|/// ditto
|pragma(inline, true)
|long muls(long x, long y, scope ref bool overflow)
|{
| version (LDC_HasNativeI64Mul)
| {
| if (!__ctfe)
| {
| auto res = llvm_smul_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| immutable long r = cast(ulong)x * cast(ulong)y;
| enum not0or1 = ~1L;
0000000| if ((x & not0or1) && ((r == y)? r : (r / x) != y))
0000000| overflow = true;
0000000| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(muls(2L, 3L, overflow) == 6);
| assert(!overflow);
| assert(muls(-200L, 300L, overflow) == -60_000);
| assert(!overflow);
| assert(muls(1, long.max, overflow) == long.max);
| assert(!overflow);
| assert(muls(long.min, 1L, overflow) == long.min);
| assert(!overflow);
| assert(muls(long.max, 2L, overflow) == (long.max * 2));
| assert(overflow);
| overflow = false;
| assert(muls(-1L, long.min, overflow) == long.min);
| assert(overflow);
| overflow = false;
| assert(muls(long.min, -1L, overflow) == long.min);
| assert(overflow);
| assert(muls(0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|
|static if (is(cent))
|{
|/// ditto
|pragma(inline, true)
|cent muls(cent x, cent y, scope ref bool overflow)
|{
| version (LDC_HasNativeI64Mul)
| {
| if (!__ctfe)
| {
| auto res = llvm_smul_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
| immutable cent r = cast(ucent)x * cast(ucent)y;
| enum not0or1 = ~1L;
| if ((x & not0or1) && ((r == y)? r : (r / x) != y))
| overflow = true;
| return r;
|}
|
|version(mir_core_test) unittest
|{
| bool overflow;
| assert(muls(cast(cent)2L, 3L, overflow) == 6);
| assert(!overflow);
| assert(muls(cast(cent)-200L, 300L, overflow) == -60_000);
| assert(!overflow);
| assert(muls(1, cent.max, overflow) == cent.max);
| assert(!overflow);
| assert(muls(cent.min, 1L, overflow) == cent.min);
| assert(!overflow);
| assert(muls(cent.max, 2L, overflow) == (cent.max * 2));
| assert(overflow);
| overflow = false;
| assert(muls(-1L, cent.min, overflow) == cent.min);
| assert(overflow);
| overflow = false;
| assert(muls(cent.min, -1L, overflow) == cent.min);
| assert(overflow);
| assert(muls(cast(cent)0L, 0L, overflow) == 0);
| assert(overflow); // sticky
|}
|}
|
|
|/*******************************
| * Multiply two unsigned integers, checking for overflow (aka carry).
| *
| * The overflow is sticky, meaning a sequence of operations can
| * be done and overflow need only be checked at the end.
| * Params:
| * x = left operand
| * y = right operand
| * overflow = set if an overflow occurs, is not affected otherwise
| * Returns:
| * the product
| */
|
|pragma(inline, true)
|uint mulu(uint x, uint y, scope ref bool overflow)
|{
| version (LDC)
| {
| if (!__ctfe)
| {
| auto res = llvm_umul_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
0000000| immutable ulong r = ulong(x) * ulong(y);
0000000| if (r >> 32)
0000000| overflow = true;
0000000| return cast(uint) r;
|}
|
|version(mir_core_test) unittest
|{
| void test(uint x, uint y, uint r, bool overflow) @nogc nothrow
| {
| bool o;
| assert(mulu(x, y, o) == r);
| assert(o == overflow);
| }
| test(2, 3, 6, false);
| test(1, uint.max, uint.max, false);
| test(0, 1, 0, false);
| test(0, uint.max, 0, false);
| test(uint.max, 2, 2 * uint.max, true);
| test(1 << 16, 1U << 16, 0, true);
|
| bool overflow = true;
| assert(mulu(0, 0, overflow) == 0);
| assert(overflow); // sticky
|}
|
|/// ditto
|pragma(inline, true)
|ulong mulu(ulong x, uint y, scope ref bool overflow)
|{
0000000| ulong r = x * y;
0000000| if (x >> 32 &&
0000000| r / x != y)
0000000| overflow = true;
0000000| return r;
|}
|
|/// ditto
|pragma(inline, true)
|ulong mulu(ulong x, ulong y, scope ref bool overflow)
|{
| version (LDC_HasNativeI64Mul)
| {
| if (!__ctfe)
| {
| auto res = llvm_umul_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
15| immutable ulong r = x * y;
15| if ((x | y) >> 32 &&
1| x &&
1| r / x != y)
0000000| overflow = true;
15| return r;
|}
|
|version(mir_core_test) unittest
|{
| void test(T, U)(T x, U y, ulong r, bool overflow) @nogc nothrow
| {
| bool o;
| assert(mulu(x, y, o) == r);
| assert(o == overflow);
| }
| // One operand is zero
| test(0, 3, 0, false);
| test(0UL, 3, 0, false);
| test(0UL, 3UL, 0, false);
| test(3, 0, 0, false);
| test(3UL, 0, 0, false);
| test(3UL, 0UL, 0, false);
| // Small numbers
| test(2, 3, 6, false);
| test(2UL, 3, 6, false);
| test(2UL, 3UL, 6, false);
| // At the 32/64 border
| test(1, ulong(uint.max), uint.max, false);
| test(1UL, ulong(uint.max), uint.max, false);
| test(ulong(uint.max), 1, uint.max, false);
| test(ulong(uint.max), 1UL, uint.max, false);
| test(1, 1 + ulong(uint.max), 1 + ulong(uint.max), false);
| test(1UL, 1 + ulong(uint.max), 1 + ulong(uint.max), false);
| test(1 + ulong(uint.max), 1, 1 + ulong(uint.max), false);
| test(1 + ulong(uint.max), 1UL, 1 + ulong(uint.max), false);
| // At the limit
| test(1, ulong.max, ulong.max, false);
| test(1UL, ulong.max, ulong.max, false);
| test(ulong.max, 1, ulong.max, false);
| test(ulong.max, 1UL, ulong.max, false);
| // Miscellaneous
| test(0, 1, 0, false);
| test(0, ulong.max, 0, false);
| test(ulong.max, 2, 2 * ulong.max, true);
| test(1UL << 32, 1UL << 32, 0, true);
| // Must be sticky
| bool overflow = true;
| assert(mulu(0UL, 0UL, overflow) == 0);
| assert(overflow); // sticky
|}
|
|static if (is(ucent))
|{
|/// ditto
|pragma(inline, true)
|ucent mulu(ucent x, ucent y, scope ref bool overflow)
|{
| version (LDC_HasNativeI64Mul)
| {
| if (!__ctfe)
| {
| auto res = llvm_umul_with_overflow(x, y);
| overflow |= res.overflow;
| return res.result;
| }
| }
| immutable ucent r = x * y;
| if (x && (r / x) != y)
| overflow = true;
| return r;
|}
|
|version(mir_core_test) unittest
|{
| void test(ucent x, ucent y, ucent r, bool overflow) @nogc nothrow
| {
| bool o;
| assert(mulu(x, y, o) == r);
| assert(o == overflow);
| }
| test(2, 3, 6, false);
| test(1, ucent.max, ucent.max, false);
| test(0, 1, 0, false);
| test(0, ucent.max, 0, false);
| test(ucent.max, 2, 2 * ucent.max, true);
| test(cast(ucent)1UL << 64, cast(ucent)1UL << 64, 0, true);
|
| bool overflow = true;
| assert(mulu(0UL, 0UL, overflow) == 0);
| assert(overflow); // sticky
|}
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/checkedint.d is 13% covered
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-complex-math.lst
|
|/++
|Complex math
|
|Copyright: Ilia Ki; 2010, Lars T. Kyllingstad (original Phobos code)
|Authors: Ilia Ki, Lars Tandle Kyllingstad, Don Clugston
|+/
|module mir.complex.math;
|
|public import mir.complex;
|
|/++
|Params: z = A complex number.
|Returns: The square root of `z`.
|+/
|Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc
|{
| import mir.math.common: fabs, fmin, fmax, sqrt;
|
| if (z == 0)
| return typeof(return)(0, 0);
| auto x = fabs(z.re);
| auto y = fabs(z.im);
| auto n = fmin(x, y);
| auto m = fmax(x, y);
| auto r = n / m;
| auto w = sqrt(m) * sqrt(0.5f * ((x >= y ? 1 : r) + sqrt(1 + r * r)));
| auto s = typeof(return)(w, z.im / (w + w));
| if (z.re < 0)
| {
| s = typeof(return)(s.im, s.re);
| if (z.im < 0)
| s = -s;
| }
| return s;
|}
|
|///
|@safe pure nothrow unittest
|{
| assert(sqrt(complex(0.0)) == 0.0);
| assert(sqrt(complex(1.0, 0)) == 1.0);
| assert(sqrt(complex(-1.0, 0)) == complex(0, 1.0));
| assert(sqrt(complex(-8.0, -6.0)) == complex(1.0, -3.0));
|}
|
|@safe pure nothrow unittest
|{
| assert(complex(1.0, 1.0).sqrt.approxEqual(complex(1.098684113467809966, 0.455089860562227341)));
| assert(complex(0.5, 2.0).sqrt.approxEqual(complex(1.131713924277869410, 0.883615530875513265)));
|}
|
|/**
| * Calculate the natural logarithm of x.
| * The branch cut is along the negative axis.
| * Params:
| * x = A complex number
| * Returns:
| * The complex natural logarithm of `x`
| *
| * $(TABLE_SV
| * $(TR $(TH x) $(TH log(x)))
| * $(TR $(TD (-0, +0)) $(TD (-$(INFIN), $(PI))))
| * $(TR $(TD (+0, +0)) $(TD (-$(INFIN), +0)))
| * $(TR $(TD (any, +$(INFIN))) $(TD (+$(INFIN), $(PI)/2)))
| * $(TR $(TD (any, $(NAN))) $(TD ($(NAN), $(NAN))))
| * $(TR $(TD (-$(INFIN), any)) $(TD (+$(INFIN), $(PI))))
| * $(TR $(TD (+$(INFIN), any)) $(TD (+$(INFIN), +0)))
| * $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD (+$(INFIN), 3$(PI)/4)))
| * $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD (+$(INFIN), $(PI)/4)))
| * $(TR $(TD ($(PLUSMN)$(INFIN), $(NAN))) $(TD (+$(INFIN), $(NAN))))
| * $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN))))
| * $(TR $(TD ($(NAN), +$(INFIN))) $(TD (+$(INFIN), $(NAN))))
| * $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN))))
| * )
| */
|Complex!T log(T)(Complex!T x) @safe pure nothrow @nogc
|{
| import mir.math.constant: PI, PI_4, PI_2;
| import mir.math.common: log, fabs, copysign;
| alias isNaN = x => x != x;
| alias isInfinity = x => x.fabs == T.infinity;
|
| // Handle special cases explicitly here for better accuracy.
| // The order here is important, so that the correct path is chosen.
| if (isNaN(x.re))
| {
| if (isInfinity(x.im))
| return Complex!T(T.infinity, T.nan);
| else
| return Complex!T(T.nan, T.nan);
| }
| if (isInfinity(x.re))
| {
| if (isNaN(x.im))
| return Complex!T(T.infinity, T.nan);
| else if (isInfinity(x.im))
| {
| if (copysign(1, x.re) < 0)
| return Complex!T(T.infinity, copysign(3.0 * PI_4, x.im));
| else
| return Complex!T(T.infinity, copysign(PI_4, x.im));
| }
| else
| {
| if (copysign(1, x.re) < 0)
| return Complex!T(T.infinity, copysign(PI, x.im));
| else
| return Complex!T(T.infinity, copysign(0.0, x.im));
| }
| }
| if (isNaN(x.im))
| return Complex!T(T.nan, T.nan);
| if (isInfinity(x.im))
| return Complex!T(T.infinity, copysign(PI_2, x.im));
| if (x.re == 0.0 && x.im == 0.0)
| {
| if (copysign(1, x.re) < 0)
| return Complex!T(-T.infinity, copysign(PI, x.im));
| else
| return Complex!T(-T.infinity, copysign(0.0, x.im));
| }
|
| return Complex!T(log(cabs(x)), arg(x));
|}
|
|///
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.common: sqrt;
| import mir.math.constant: PI;
| import mir.math.common: approxEqual;
|
| auto a = complex(2.0, 1.0);
| assert(log(conj(a)) == conj(log(a)));
|
| assert(log(complex(-1.0L, 0.0L)) == complex(0.0L, PI));
| assert(log(complex(-1.0L, -0.0L)) == complex(0.0L, -PI));
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.common: fabs;
| import mir.math.constant: PI, PI_2, PI_4;
| alias isNaN = x => x != x;
| alias isInfinity = x => x.fabs == x.infinity;
|
| auto a = log(complex(-0.0L, 0.0L));
| assert(a == complex(-real.infinity, PI));
| auto b = log(complex(0.0L, 0.0L));
| assert(b == complex(-real.infinity, +0.0L));
| auto c = log(complex(1.0L, real.infinity));
| assert(c == complex(real.infinity, PI_2));
| auto d = log(complex(1.0L, real.nan));
| assert(isNaN(d.re) && isNaN(d.im));
|
| auto e = log(complex(-real.infinity, 1.0L));
| assert(e == complex(real.infinity, PI));
| auto f = log(complex(real.infinity, 1.0L));
| assert(f == complex(real.infinity, 0.0L));
| auto g = log(complex(-real.infinity, real.infinity));
| assert(g == complex(real.infinity, 3.0 * PI_4));
| auto h = log(complex(real.infinity, real.infinity));
| assert(h == complex(real.infinity, PI_4));
| auto i = log(complex(real.infinity, real.nan));
| assert(isInfinity(i.re) && isNaN(i.im));
|
| auto j = log(complex(real.nan, 1.0L));
| assert(isNaN(j.re) && isNaN(j.im));
| auto k = log(complex(real.nan, real.infinity));
| assert(isInfinity(k.re) && isNaN(k.im));
| auto l = log(complex(real.nan, real.nan));
| assert(isNaN(l.re) && isNaN(l.im));
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.constant: PI;
|
| auto a = log(fromPolar(1.0, PI / 6.0));
| assert(approxEqual(a, complex(0.0L, 0.523598775598298873077L), 0.0, 1e-15));
|
| auto b = log(fromPolar(1.0, PI / 3.0));
| assert(approxEqual(b, complex(0.0L, 1.04719755119659774615L), 0.0, 1e-15));
|
| auto c = log(fromPolar(1.0, PI / 2.0));
| assert(approxEqual(c, complex(0.0L, 1.57079632679489661923L), 0.0, 1e-15));
|
| auto d = log(fromPolar(1.0, 2.0 * PI / 3.0));
| assert(approxEqual(d, complex(0.0L, 2.09439510239319549230L), 0.0, 1e-15));
|
| auto e = log(fromPolar(1.0, 5.0 * PI / 6.0));
| assert(approxEqual(e, complex(0.0L, 2.61799387799149436538L), 0.0, 1e-15));
|
| auto f = log(complex(-1.0L, 0.0L));
| assert(approxEqual(f, complex(0.0L, PI), 0.0, 1e-15));
|}
|
|/++
|Calculates e$(SUPERSCRIPT x).
|Params:
| x = A complex number
|Returns:
| The complex base e exponential of `x`
| $(TABLE_SV
| $(TR $(TH x) $(TH exp(x)))
| $(TR $(TD ($(PLUSMN)0, +0)) $(TD (1, +0)))
| $(TR $(TD (any, +$(INFIN))) $(TD ($(NAN), $(NAN))))
| $(TR $(TD (any, $(NAN)) $(TD ($(NAN), $(NAN)))))
| $(TR $(TD (+$(INFIN), +0)) $(TD (+$(INFIN), +0)))
| $(TR $(TD (-$(INFIN), any)) $(TD ($(PLUSMN)0, cis(x.im))))
| $(TR $(TD (+$(INFIN), any)) $(TD ($(PLUSMN)$(INFIN), cis(x.im))))
| $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)0, $(PLUSMN)0)))
| $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)$(INFIN), $(NAN))))
| $(TR $(TD (-$(INFIN), $(NAN))) $(TD ($(PLUSMN)0, $(PLUSMN)0)))
| $(TR $(TD (+$(INFIN), $(NAN))) $(TD ($(PLUSMN)$(INFIN), $(NAN))))
| $(TR $(TD ($(NAN), +0)) $(TD ($(NAN), +0)))
| $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN))))
| $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN))))
| )
|+/
|Complex!T exp(T)(Complex!T x) @trusted pure nothrow @nogc // TODO: @safe
|{
| import mir.math.common: exp, fabs, copysign;
| alias isNaN = x => x != x;
| alias isInfinity = x => x.fabs == T.infinity;
|
| // Handle special cases explicitly here, as fromPolar will otherwise
| // cause them to return Complex!T(NaN, NaN), or with the wrong sign.
| if (isInfinity(x.re))
| {
| if (isNaN(x.im))
| {
| if (copysign(1, x.re) < 0)
| return Complex!T(0, copysign(0, x.im));
| else
| return x;
| }
| if (isInfinity(x.im))
| {
| if (copysign(1, x.re) < 0)
| return Complex!T(0, copysign(0, x.im));
| else
| return Complex!T(T.infinity, -T.nan);
| }
| if (x.im == 0)
| {
| if (copysign(1, x.re) < 0)
| return Complex!T(0);
| else
| return Complex!T(T.infinity);
| }
| }
| if (isNaN(x.re))
| {
| if (isNaN(x.im) || isInfinity(x.im))
| return Complex!T(T.nan, T.nan);
| if (x.im == 0)
| return x;
| }
| if (x.re == 0)
| {
| if (isNaN(x.im) || isInfinity(x.im))
| return Complex!T(T.nan, T.nan);
| if (x.im == 0)
| return Complex!T(1, 0);
| }
|
| return fromPolar!T(exp(x.re), x.im);
|}
|
|///
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.constant: PI;
|
| assert(exp(complex(0.0, 0.0)) == complex(1.0, 0.0));
|
| auto a = complex(2.0, 1.0);
| assert(exp(conj(a)) == conj(exp(a)));
|
| auto b = exp(complex(0.0, 1.0) * double(PI));
| assert(approxEqual(b, complex(-1.0), 0.0, 1e-15));
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.common: fabs;
|
| alias isNaN = x => x != x;
| alias isInfinity = x => x.fabs == x.infinity;
|
| auto a = exp(complex(0.0, double.infinity));
| assert(isNaN(a.re) && isNaN(a.im));
| auto b = exp(complex(0.0, double.infinity));
| assert(isNaN(b.re) && isNaN(b.im));
| auto c = exp(complex(0.0, double.nan));
| assert(isNaN(c.re) && isNaN(c.im));
|
| auto d = exp(complex(+double.infinity, 0.0));
| assert(d == complex(double.infinity, 0.0));
| auto e = exp(complex(-double.infinity, 0.0));
| assert(e == complex(0.0));
| auto f = exp(complex(-double.infinity, 1.0));
| assert(f == complex(0.0));
| auto g = exp(complex(+double.infinity, 1.0));
| assert(g == complex(double.infinity, double.infinity));
| auto h = exp(complex(-double.infinity, +double.infinity));
| assert(h == complex(0.0));
| auto i = exp(complex(+double.infinity, +double.infinity));
| assert(isInfinity(i.re) && isNaN(i.im));
| auto j = exp(complex(-double.infinity, double.nan));
| assert(j == complex(0.0));
| auto k = exp(complex(+double.infinity, double.nan));
| assert(isInfinity(k.re) && isNaN(k.im));
|
| auto l = exp(complex(double.nan, 0));
| assert(isNaN(l.re) && l.im == 0.0);
| auto m = exp(complex(double.nan, 1));
| assert(isNaN(m.re) && isNaN(m.im));
| auto n = exp(complex(double.nan, double.nan));
| assert(isNaN(n.re) && isNaN(n.im));
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.constant : PI;
|
| auto a = exp(complex(0.0, -PI));
| assert(approxEqual(a, complex(-1.0L), 0.0, 1e-15));
|
| auto b = exp(complex(0.0, -2.0 * PI / 3.0));
| assert(approxEqual(b, complex(-0.5L, -0.866025403784438646763L)));
|
| auto c = exp(complex(0.0, PI / 3.0));
| assert(approxEqual(c, complex(0.5L, 0.866025403784438646763L)));
|
| auto d = exp(complex(0.0, 2.0 * PI / 3.0));
| assert(approxEqual(d, complex(-0.5L, 0.866025403784438646763L)));
|
| auto e = exp(complex(0.0, PI));
| assert(approxEqual(e, complex(-1.0L), 0.0, 1e-15));
|}
|
|/++
|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)(Complex!T lhs, Complex!T rhs, const T maxRelDiff = 0x1p-20f, const T maxAbsDiff = 0x1p-20f)
|{
| import mir.math.common: approxEqual;
| 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
|{
| assert(approxEqual(complex(1.0, 1), complex(1.0000001, 1), 1.0000001));
| assert(!approxEqual(complex(100000.0, 0), complex(100001.0, 0)));
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/complex/math.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-complex-package.lst
|/++
|Complex numbers
|
|Copyright: Ilia Ki; 2010, Lars T. Kyllingstad (original Phobos code)
|Authors: Ilia Ki, Lars Tandle Kyllingstad, Don Clugston
|+/
|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;
| }
|
|scope 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) ;
| }
|
| ///
| 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 = 0)
| 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));
|}
|
|/**
| Constructs a complex number given its absolute value and argument.
| Params:
| modulus = The modulus
| argument = The argument
| Returns: The complex number with the given modulus and argument.
|*/
|Complex!T fromPolar(T)(const T modulus, const T argument)
| @safe pure nothrow @nogc
| if (__traits(isFloating, T))
|{
| import mir.math.common: sin, cos;
| return typeof(return)(modulus * cos(argument), modulus * sin(argument));
|}
|
|///
|@safe pure nothrow version(mir_core_test) unittest
|{
| import mir.math : approxEqual, PI, sqrt;
| auto z = fromPolar(sqrt(2.0), double(PI / 4));
| assert(approxEqual(z.re, 1.0));
| assert(approxEqual(z.im, 1.0));
|}
|
|/++
|Params: z = A complex number.
|Returns: The complex conjugate of `z`.
|+/
|Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc
|{
| return Complex!T(z.re, -z.im);
|}
|
|///
|@safe pure nothrow version(mir_core_test) unittest
|{
| assert(conj(complex(1.0)) == complex(1.0));
| assert(conj(complex(1.0, 2.0)) == complex(1.0, -2.0));
|}
|
|/++
|Params: z = A complex number.
|Returns: The argument (or phase) of `z`.
|+/
|T arg(T)(Complex!T z) @safe pure nothrow @nogc
|{
| import std.math.trigonometry : atan2;
| return atan2(z.im, z.re);
|}
|
|///
|@safe pure nothrow version(mir_core_test) unittest
|{
| import mir.math.constant: PI_2, PI_4;
| assert(arg(complex(1.0)) == 0.0);
| assert(arg(complex(0.0L, 1.0L)) == PI_2);
| assert(arg(complex(1.0L, 1.0L)) == PI_4);
|}
|
|
|/**
|Params: z = A complex number.
|Returns: The absolute value (or modulus) of `z`.
|*/
|T cabs(T)(Complex!T z) @safe pure nothrow @nogc
|{
| import std.math.algebraic : hypot;
| return hypot(z.re, z.im);
|}
|
|///
|@safe pure nothrow version(mir_core_test) unittest
|{
| import mir.math.common: sqrt;
| assert(cabs(complex(1.0)) == 1.0);
| assert(cabs(complex(0.0, 1.0)) == 1.0);
| assert(cabs(complex(1.0L, -2.0L)) == sqrt(5.0L));
|}
|
|@safe pure nothrow @nogc version(mir_core_test) unittest
|{
| import mir.math.common: sqrt;
| assert(cabs(complex(0.0L, -3.2L)) == 3.2L);
| assert(cabs(complex(0.0L, 71.6L)) == 71.6L);
| assert(cabs(complex(-1.0L, 1.0L)) == sqrt(2.0L));
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/complex/package.d is 0% covered
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-conv.lst
|/++
|Conversion utilities.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki
|+/
|module mir.conv;
|
|public import core.lifetime: emplace;
|
|import std.traits;
|
|private template isMirString(T)
|{
| static if (isSomeString!T)
| {
| enum isMirString = true;
| }
| else
| {
| static if (__traits(compiles, {import mir.small_string;}))
| {
| import mir.small_string;
| enum isMirString = is(T : SmallString!size, size_t size);
| }
| else
| {
| enum isMirString = false;
| }
| }
|}
|
|/++
|The `to` template converts a value from one type _to another.
|The source type is deduced and the target type must be specified, for example the
|expression `to!int(42.0)` converts the number 42 from
|`double` _to `int`. The conversion is "unsafe", i.e.,
|it does not check for overflow.
|+/
|template to(T)
|{
| ///
| auto ref T to(A...)(auto ref A args)
| if (A.length > 0)
| {
| import mir.utility;
| import mir.functional: forward;
| static if (A.length == 1 && isImplicitlyConvertible!(A[0], T))
| return args[0];
| else
| static if (is(T == class) && is(typeof(new T(forward!args))))
| return new T(forward!args);
| else
| static if (is(typeof(T(args))))
| return T(forward!args);
| else
| static if (A.length == 1)
| {
| alias I = A[0];
| alias arg = args[0];
| static if (is(typeof(cast(T) arg)) && !(isDynamicArray!T && isDynamicArray!I) && !isSomeString!T)
| return cast(T) forward!arg;
| else
| static if (isSomeString!I && is(T == enum))
| {
| import mir.enums;
| uint index = void;
| if (getEnumIndexFromKey!T(arg, index)._expect(true))
| return index.unsafeEnumFromIndex!T;
| static immutable msg = "Can not convert string to the enum " ~ T.stringof;
| version (D_Exceptions)
| {
| static immutable Exception exc = new Exception(msg);
| throw exc;
| }
| else
| {
| assert(0, msg);
| }
| }
| else
| static if (is(I == enum) && isSomeString!T)
| {
| import mir.enums;
| uint id = void;
| if (getEnumIndex(arg, id)._expect(true))
| return enumStrings!I[id];
| assert(0);
| }
| else
| static if (isMirString!I && !isSomeString!T)
| {
| static assert (__traits(compiles, { import mir.parse: fromString; }));
| import mir.parse: fromString;
| return fromString!(Unqual!T)(arg[]);
| }
| else
| static if (!isSomeString!I && isMirString!T)
| {
| // static if (is(Unqual!I == typeof(null)))
| // {
| // enum immutable(T) ret = "null";
| // static if (isImplicitlyConvertible!(immutable T, T))
| // return ret;
| // else
| // return .to!T(ret[]);
| // }
| // else
| static if (is(Unqual!I == bool))
| {
| enum immutable(T) t = "true";
| enum immutable(T) f = "false";
| auto ret = arg ? t : f;
| static if (isImplicitlyConvertible!(immutable T, T))
| return ret;
| else
| return .to!T(ret[]);
| }
| else
| {
| static if (isImplicitlyConvertible!(T, string) && __traits(compiles, () {const(char)[] s = arg.toString; return s;}))
| {
| auto ret = arg.toString;
| static if (is(typeof(ret) == string))
| return ret;
| else
| return ret.idup;
| }
| else
| {
| static assert (__traits(compiles, { import mir.format: print; }));
| import mir.format: print;
| static if (isSomeString!T)
| {
| static if (isNumeric!I)
| {
| import mir.appender: UnsafeArrayBuffer;
| alias C = Unqual!(ForeachType!T);
| C[64] array = '\0';
| auto buffer = UnsafeArrayBuffer!C(array);
| }
| else
| {
| import mir.appender: scopedBuffer;
| auto buffer = scopedBuffer!(Unqual!(ForeachType!T));
| }
| buffer.print(arg);
| static if (isMutable!(ForeachType!(T)))
| return buffer.data.dup;
| else
| return buffer.data.idup;
| }
| else
| {
| Unqual!T buffer;
| buffer.print(arg);
| return buffer;
| }
| }
| }
| }
| else
| static if (is(I : const(C)[], C) && is(T : immutable(C)[]))
| {
| static if (is(I : immutable(C)[]))
| return arg;
| else
| return idup(arg);
| }
| else
| static if (is(I : const(D)[], D) && is(T : D[]))
| {
| static if (is(I : D[]))
| return arg;
| else
| return dup(arg);
| }
| else
| static assert(0, T.stringof);
| }
| else
| static assert(0, T.stringof);
| }
|}
|
|///
|@safe pure @nogc
|version(mir_core_test) unittest
|{
| enum E
| {
| A,
| B,
| C,
| }
|
| assert(to!E("B") == E.B);
| assert(to!string(E.B) == "B");
| assert(to!string(null) is null);
| assert(to!string(true) == "true");
| assert(to!string(false) == "false");
|
| enum S : wstring
| {
| a = "A",
| b = "B",
| }
|
| assert(to!wstring(S.b) == "B"w);
| assert(to!S("B"w) == S.b);
|}
|
|/++
|Emplace helper function.
|+/
|void emplaceInitializer(T)(scope ref T chunk) @trusted pure nothrow
|
|{
| // Emplace T.init.
| // Previously, an immutable static and memcpy were used to hold an initializer.
| // With improved unions, this is no longer needed.
| union UntypedInit
| {
| T dummy;
| }
| static struct UntypedStorage
| {
| align(T.alignof) void[T.sizeof] dummy;
| }
|
| () @trusted {
| *cast(UntypedStorage*) &chunk = cast(UntypedStorage) UntypedInit.init;
| } ();
|}
|
|/++
|+/
|T[] uninitializedFillDefault(T)(return scope T[] array) nothrow @nogc
|{
| static if (__VERSION__ < 2083)
| {
| static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1))
| {
| import core.stdc.string : memset;
| memset(array.ptr, 0xff, T.sizeof * array.length);
| return array;
| }
| else
| {
| pragma(inline, false);
| foreach(ref e; array)
| emplaceInitializer(e);
| return array;
| }
| }
| else
| {
| static if (__traits(isZeroInit, T))
| {
| import core.stdc.string : memset;
| memset(array.ptr, 0, T.sizeof * array.length);
| return array;
| }
| else static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1))
| {
| import core.stdc.string : memset;
| memset(array.ptr, 0xff, T.sizeof * array.length);
| return array;
| }
| else
| {
| pragma(inline, false);
| foreach(ref e; array)
| emplaceInitializer(e);
| return array;
| }
| }
|}
|
|///
|pure nothrow @nogc @system
|version(mir_core_test) unittest
|{
| static struct S { int x = 42; @disable this(this); }
|
| int[5] expected = [42, 42, 42, 42, 42];
| S[5] arr = void;
| uninitializedFillDefault(arr);
| assert((cast(int*) arr.ptr)[0 .. arr.length] == expected);
|}
|
|///
|@system
|version(mir_core_test) unittest
|{
| int[] a = [1, 2, 4];
| uninitializedFillDefault(a);
| assert(a == [0, 0, 0]);
|}
|
|/++
|Destroy structs and unions usnig `__xdtor` member if any.
|Do nothing for other types.
|+/
|void xdestroy(T)(scope T[] ar)
|{
| static if ((is(T == struct) || is(T == union)) && __traits(hasMember, T, "__xdtor"))
| {
| static if (__traits(isSame, T, __traits(parent, ar[0].__xdtor)))
| {
| pragma(inline, false);
| foreach_reverse (ref e; ar)
| e.__xdtor();
| }
| }
|}
|
|///
|nothrow @nogc version(mir_core_test) unittest
|{
| __gshared int d;
| __gshared int c;
| struct D { ~this() nothrow @nogc {d++;} }
| extern(C++)
| struct C { ~this() nothrow @nogc {c++;} }
| C[2] carray;
| D[2] darray;
| carray.xdestroy;
| darray.xdestroy;
| assert(c == 2);
| assert(d == 2);
| c = 0;
| d = 0;
|}
|
|
|template emplaceRef(T)
|{
| void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
| {
| import core.lifetime: forward;
| import core.internal.lifetime: emplaceRef;
| return emplaceRef!T(chunk, forward!args);
| }
|}
|
|void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
|if (is(UT == Unqual!UT))
|{
| import core.lifetime: forward;
| emplaceRef!UT(chunk, forward!args);
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/conv.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-enums.lst
|/++
|Enum utilities.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki
|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 + enumMembers!T[0]);
| }
| else
| {
| return enumMembers!T[index];
| }
|}
|
|///
|version(mir_core_test)
|unittest
|{
| enum Linear
| {
| one = 1,
| two = 2
| }
|
| static assert(is(typeof(unsafeEnumFromIndex!Linear(0)) == Linear));
| assert(unsafeEnumFromIndex!Linear(0) == Linear.one);
| assert(unsafeEnumFromIndex!Linear(1) == Linear.two);
|
| enum Mixed
| {
| one = 1,
| oneAgain = 1,
| two = 2
| }
|
| assert(unsafeEnumFromIndex!Mixed(0) == Mixed.one);
| assert(unsafeEnumFromIndex!Mixed(1) == Mixed.one);
| assert(unsafeEnumFromIndex!Mixed(2) == Mixed.two);
|}
|
|/++
|Params:
| T = enum type to introspect
| key = some string that corresponds to some key name of the given enum
| index = resulting enum index if this method returns true.
|Returns:
| boolean whether the key was found in the enum keys and if so, index is set.
|+/
|template getEnumIndexFromKey(T, bool caseInsensitive = 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, caseInsensitive);
| static immutable indices = ()
| {
| minimalSignedIndexType!indexLength[indexLength] indices;
|
| foreach (i, member; EnumMembers!T)
| foreach (key; keysOf!(EnumMembers!T[i]))
| {
| static if (caseInsensitive)
| {
| key = key.dup.fastToUpperInPlace;
| }
| indices[table[key]] = i;
| }
|
| return indices;
| } ();
|
| uint stringId = void;
| if (_expect(table.get(key, stringId), true))
| {
| index = indices[stringId];
| return true;
| }
| return false;
| }
| }
|}
|
|///
|unittest
|{
| enum Short
| {
| hello,
| world
| }
|
| enum Long
| {
| This,
| Is,
| An,
| Enum,
| With,
| Lots,
| Of,
| Very,
| Long,
| EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tm
| }
|
| uint i;
| assert(getEnumIndexFromKey!Short("hello", i));
| assert(i == 0);
| assert(getEnumIndexFromKey!Short("world", i));
| assert(i == 1);
| assert(!getEnumIndexFromKey!Short("foo", i));
|
| assert(getEnumIndexFromKey!Short("HeLlO", i));
| assert(i == 0);
| assert(getEnumIndexFromKey!Short("WoRLd", i));
| assert(i == 1);
|
| assert(!getEnumIndexFromKey!(Short, false)("HeLlO", i));
| assert(!getEnumIndexFromKey!(Short, false)("WoRLd", i));
|
| assert(getEnumIndexFromKey!Long("Is", i));
| assert(i == 1);
| assert(getEnumIndexFromKey!Long("Long", i));
| assert(i == 8);
| assert(getEnumIndexFromKey!Long("EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tm", i));
| assert(i == 9);
| assert(!getEnumIndexFromKey!Long("EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCodeatm", i));
|
| assert(!getEnumIndexFromKey!(Long, false)("EntriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tM", i));
| assert(!getEnumIndexFromKey!(Long, false)("entriesThatArePartiallyAlsoVeryLongInStringLengthAsWeNeedToTestALotOfDifferentCasesThatCouldHappenInRealWorldCode_tm", i));
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/enums.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-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;
| auto buffer = stringBuf();
| 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.
| +/
| this(Args...)(auto ref scope const Args args, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure
| if (Args.length > 1)
| {
| static assert (__traits(compiles, {import mir.format;}), "MirThrowableImpl needs mir-algorithm for mir.format and exception formatting.");
| import mir.format;
| auto buf = stringBuf();
| foreach(ref arg; args)
| buf.print(arg);
| this(buf.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.3.6/mir-core/source/mir/exception.d is 0% covered
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-functional.lst
|/++
|Functions that manipulate other functions.
|This module provides functions for compile time function composition. These
|functions are helpful when constructing predicates for the algorithms in
|$(MREF mir, ndslice).
|$(BOOKTABLE $(H2 Functions),
|$(TR $(TH Function Name) $(TH Description))
| $(TR $(TD $(LREF naryFun))
| $(TD Create a unary, binary or N-nary function from a string. Most often
| used when defining algorithms on ranges and slices.
| ))
| $(TR $(TD $(LREF pipe))
| $(TD Join a couple of functions into one that executes the original
| functions one after the other, using one function's result for the next
| function's argument.
| ))
| $(TR $(TD $(LREF not))
| $(TD Creates a function that negates another.
| ))
| $(TR $(TD $(LREF reverseArgs))
| $(TD Predicate that reverses the order of its arguments.
| ))
| $(TR $(TD $(LREF forward))
| $(TD Forwards function arguments with saving ref-ness.
| ))
| $(TR $(TD $(LREF tuple))
| $(TD Removes $(LREF Ref) shell.
| ))
| $(TR $(TD $(LREF unref))
| $(TD Creates a $(LREF Tuple) structure.
| ))
| $(TR $(TD $(LREF __ref))
| $(TD Creates a $(LREF Ref) structure.
| ))
|)
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki, $(HTTP erdani.org, Andrei Alexandrescu (some original code from std.functional))
|
|Macros:
|NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
|+/
|module mir.functional;
|
|private enum isRef(T) = is(T : Ref!T0, T0);
|
|import mir.math.common: optmath;
|
|public import core.lifetime : forward;
|
|@optmath:
|
|/++
|Constructs static array.
|+/
|T[N] staticArray(T, size_t N)(T[N] a...)
|{
| return a;
|}
|
|/++
|Simple wrapper that holds a pointer.
|It is used for as workaround to return multiple auto ref values.
|+/
|struct Ref(T)
| if (!isRef!T)
|{
| @optmath:
|
| @disable this();
| ///
| this(ref T value) @trusted
| {
| __ptr = &value;
| }
| ///
| T* __ptr;
| ///
| ref inout(T) __value() inout @property { return *__ptr; }
| ///
| alias __value this;
|
| ///
| bool opEquals(ref scope const T rhs) const scope
| {
| return __value == rhs;
| }
|
| ///
| bool opEquals(scope const T rhs) const scope
| {
| return __value == rhs;
| }
|
| static if (__traits(hasMember, T, "toHash") || __traits(isScalar, T))
| ///
| size_t toHash() const
| {
| return hashOf(__value);
| }
|}
|
|/// Creates $(LREF Ref) wrapper.
|Ref!T _ref(T)(ref T value)
|{
| return Ref!T(value);
|}
|
|private mixin template _RefTupleMixin(T...)
| if (T.length <= 26)
|{
| static if (T.length)
| {
| enum i = T.length - 1;
| static if (isRef!(T[i]))
| mixin(`@optmath @property ref ` ~ cast(char)('a' + i) ~ `() { return *expand[` ~ i.stringof ~ `].__ptr; }` );
| else
| mixin(`alias ` ~ cast(char)('a' + i) ~ ` = expand[` ~ i.stringof ~ `];`);
| mixin ._RefTupleMixin!(T[0 .. $-1]);
| }
|}
|
|/++
|Simplified tuple structure. Some fields may be type of $(LREF Ref).
|Ref stores a pointer to a values.
|+/
|struct Tuple(T...)
|{
| @optmath:
| T expand;
| alias expand this;
| mixin _RefTupleMixin!T;
|}
|
|deprecated("Use 'Tuple' instead")
|alias RefTuple = Tuple;
|
|/// Removes $(LREF Ref) shell.
|alias Unref(V : Ref!T, T) = T;
|/// ditto
|template Unref(V : Tuple!T, T...)
|{
| import std.meta: staticMap;
| alias Unref = Tuple!(staticMap!(.Unref, T));
|}
|
|/// ditto
|alias Unref(V) = V;
|
|/++
|Returns: a $(LREF Tuple) structure.
|+/
|Tuple!Args tuple(Args...)(auto ref Args args)
|{
| return Tuple!Args(args);
|}
|
|deprecated("Use 'tuple' instead")
|alias refTuple = tuple;
|
|/// Removes $(LREF Ref) shell.
|ref T unref(V : Ref!T, T)(scope return V value)
|{
| return *value.__ptr;
|}
|
|/// ditto
|Unref!(Tuple!T) unref(V : Tuple!T, T...)(V value)
|{
| typeof(return) ret;
| foreach(i, ref elem; ret.expand)
| elem = unref(value.expand[i]);
| return ret;
|}
|
|/// ditto
|ref V unref(V)(scope return ref V value)
|{
| return value;
|}
|
|/// ditto
|V unref(V)(V value)
|{
| import std.traits: hasElaborateAssign;
| static if (hasElaborateAssign!V)
| {
| import core.lifetime: move;
| return move(value);
| }
| else
| return value;
|}
|
|private template autoExpandAndForwardElem(alias value)
|{
|
|}
|
|template autoExpandAndForward(alias value)
| if (is(typeof(value) : Tuple!Types, Types...))
|{
|
| import core.lifetime: move;
| enum isLazy = __traits(isRef, value) || __traits(isOut, value) || __traits(isLazy, value);
| template autoExpandAndForwardElem(size_t i)
| {
| alias T = typeof(value.expand[i]);
| static if (isRef!T)
| {
| ref autoExpandAndForwardElem()
| {
| return *value.expand[i].__ptr;
| }
| }
| else
| {
| static if (isLazy)
| @property ref autoExpandAndForwardElem(){ pragma(inline, true); return value.expand[i]; }
| else
| static if (is(typeof(move(value.expand[i]))))
| @property auto autoExpandAndForwardElem(){ pragma(inline, true); return move(value.expand[i]); }
| else
| @property auto autoExpandAndForwardElem(){ pragma(inline, true); return value.expand[i]; }
| }
| }
|
| import mir.internal.utility: Iota;
| import std.meta: staticMap;
| alias autoExpandAndForward = staticMap!(autoExpandAndForwardElem, Iota!(value.expand.length));
|}
|
|version(mir_core_test) unittest
|{
| long v;
| auto tup = tuple(v._ref, 2.3);
|
| auto f(ref long a, double b)
| {
| assert(b == 2.3);
| assert(a == v);
| assert(&a == &v);
| }
|
| f(autoExpandAndForward!tup);
|}
|
|private string joinStrings()(string[] strs)
|{
| if (strs.length)
| {
| auto ret = strs[0];
| foreach(s; strs[1 .. $])
| ret ~= s;
| return ret;
| }
| return null;
|}
|
|private auto copyArg(alias a)()
|{
| return a;
|}
|
|/++
|Takes multiple functions and adjoins them together. The result is a
|$(LREF Tuple) with one element per passed-in function. Upon
|invocation, the returned tuple is the adjoined results of all
|functions.
|Note: In the special case where only a single function is provided
|(`F.length == 1`), adjoin simply aliases to the single passed function
|(`F[0]`).
|+/
|template adjoin(fun...) if (fun.length && fun.length <= 26)
|{
| static if (fun.length != 1)
| {
| import std.meta: staticMap, Filter;
| static if (Filter!(_needNary, fun).length == 0)
| {
| ///
| @optmath auto adjoin(Args...)(auto ref Args args)
| {
| template _adjoin(size_t i)
| {
| static if (__traits(compiles, &(fun[i](forward!args))))
| enum _adjoin = "Ref!(typeof(fun[" ~ i.stringof ~ "](forward!args)))(fun[" ~ i.stringof ~ "](args)), ";
| else
| enum _adjoin = "fun[" ~ i.stringof ~ "](args), ";
| }
|
| import mir.internal.utility;
| mixin("return tuple(" ~ [staticMap!(_adjoin, Iota!(fun.length))].joinStrings ~ ");");
| }
| }
| else alias adjoin = .adjoin!(staticMap!(naryFun, fun));
| }
| else alias adjoin = naryFun!(fun[0]);
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| static bool f1(int a) { return a != 0; }
| static int f2(int a) { return a / 2; }
| auto x = adjoin!(f1, f2)(5);
| assert(is(typeof(x) == Tuple!(bool, int)));
| assert(x.a == true && x.b == 2);
|}
|
|@safe version(mir_core_test) unittest
|{
| alias f = pipe!(adjoin!("a", "a * a"), "a[0]");
| static assert(is(typeof(f(3)) == int));
| auto d = 4;
| static assert(is(typeof(f(d)) == Ref!int));
|}
|
|@safe version(mir_core_test) unittest
|{
| static bool F1(int a) { return a != 0; }
| auto x1 = adjoin!(F1)(5);
| static int F2(int a) { return a / 2; }
| auto x2 = adjoin!(F1, F2)(5);
| assert(is(typeof(x2) == Tuple!(bool, int)));
| assert(x2.a && x2.b == 2);
| auto x3 = adjoin!(F1, F2, F2)(5);
| assert(is(typeof(x3) == Tuple!(bool, int, int)));
| assert(x3.a && x3.b == 2 && x3.c == 2);
|
| bool F4(int a) { return a != x1; }
| alias eff4 = adjoin!(F4);
| static struct S
| {
| bool delegate(int) @safe store;
| int fun() { return 42 + store(5); }
| }
| S s;
| s.store = (int a) { return eff4(a); };
| auto x4 = s.fun();
| assert(x4 == 43);
|}
|
|//@safe
|version(mir_core_test) unittest
|{
| import std.meta: staticMap;
| alias funs = staticMap!(naryFun, "a", "a * 2", "a * 3", "a * a", "-a");
| alias afun = adjoin!funs;
| int a = 5, b = 5;
| assert(afun(a) == tuple(Ref!int(a), 10, 15, 25, -5));
| assert(afun(a) == tuple(Ref!int(b), 10, 15, 25, -5));
|
| static class C{}
| alias IC = immutable(C);
| IC foo(){return typeof(return).init;}
| Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)();
|
| static struct S{int* p;}
| alias IS = immutable(S);
| IS bar(){return typeof(return).init;}
| enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)();
|}
|
|private template needOpCallAlias(alias fun)
|{
| /* Determine whether or not naryFun need to alias to fun or
| * fun.opCall. Basically, fun is a function object if fun(...) compiles. We
| * want is(naryFun!fun) (resp., is(naryFun!fun)) to be true if fun is
| * any function object. There are 4 possible cases:
| *
| * 1) fun is the type of a function object with static opCall;
| * 2) fun is an instance of a function object with static opCall;
| * 3) fun is the type of a function object with non-static opCall;
| * 4) fun is an instance of a function object with non-static opCall.
| *
| * In case (1), is(naryFun!fun) should compile, but does not if naryFun
| * aliases itself to fun, because typeof(fun) is an error when fun itself
| * is a type. So it must be aliased to fun.opCall instead. All other cases
| * should be aliased to fun directly.
| */
| static if (is(typeof(fun.opCall) == function))
| {
| import std.traits: Parameters;
| enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () {
| return fun(Parameters!fun.init);
| });
| }
| else
| enum needOpCallAlias = false;
|}
|
|private template _naryAliases(size_t n)
| if (n <= 26)
|{
| static if (n == 0)
| enum _naryAliases = "";
| else
| {
| enum i = n - 1;
| enum _naryAliases = _naryAliases!i ~ "alias " ~ cast(char)('a' + i) ~ " = args[" ~ i.stringof ~ "];\n";
| }
|}
|
|private template stringFun(string fun)
|{
| /// Specialization for string lambdas
| @optmath auto ref stringFun(Args...)(auto ref Args args)
| if (args.length <= 26 && (Args.length == 0) == (fun.length == 0))
| {
| import mir.math.common;
| static if (fun.length)
| {
| mixin(_naryAliases!(Args.length));
| return mixin(fun);
| }
| else
| {
| return;
| }
| }
|}
|
|/++
|Aliases itself to a set of functions.
|
|Transforms strings representing an expression into a binary function. The
|strings must use symbol names `a`, `b`, ..., `z` as the parameters.
|If `functions[i]` is not a string, `naryFun` aliases itself away to `functions[i]`.
|+/
|template naryFun(functions...)
| if (functions.length >= 1)
|{
| static foreach (fun; functions)
| {
| static if (is(typeof(fun) : string))
| {
| alias naryFun = stringFun!fun;
| }
| else static if (needOpCallAlias!fun)
| alias naryFun = fun.opCall;
| else
| alias naryFun = fun;
| }
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| // Strings are compiled into functions:
| alias isEven = naryFun!("(a & 1) == 0");
| assert(isEven(2) && !isEven(1));
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| alias less = naryFun!("a < b");
| assert(less(1, 2) && !less(2, 1));
| alias greater = naryFun!("a > b");
| assert(!greater("1", "2") && greater("2", "1"));
|}
|
|/// `naryFun` accepts up to 26 arguments.
|@safe version(mir_core_test) unittest
|{
| assert(naryFun!("a * b + c")(2, 3, 4) == 10);
|}
|
|/// `naryFun` can return by reference.
|version(mir_core_test) unittest
|{
| int a;
| assert(&naryFun!("a")(a) == &a);
|}
|
|/// `args` parameter tuple
|version(mir_core_test) unittest
|{
| assert(naryFun!("args[0] + args[1]")(2, 3) == 5);
|}
|
|/// Multiple functions
|@safe pure nothrow @nogc
|version(mir_core_test) unittest
|{
| alias fun = naryFun!(
| (uint a) => a,
| (ulong a) => a * 2,
| a => a * 3,
| );
|
| int a = 10;
| long b = 10;
| float c = 10;
|
| assert(fun(a) == 10);
| assert(fun(b) == 20);
| assert(fun(c) == 30);
|}
|
|@safe version(mir_core_test) unittest
|{
| static int f1(int a) { return a + 1; }
| static assert(is(typeof(naryFun!(f1)(1)) == int));
| assert(naryFun!(f1)(41) == 42);
| int f2(int a) { return a + 1; }
| static assert(is(typeof(naryFun!(f2)(1)) == int));
| assert(naryFun!(f2)(41) == 42);
| assert(naryFun!("a + 1")(41) == 42);
|
| int num = 41;
| assert(naryFun!"a + 1"(num) == 42);
|
| // Issue 9906
| struct Seen
| {
| static bool opCall(int n) { return true; }
| }
| static assert(needOpCallAlias!Seen);
| static assert(is(typeof(naryFun!Seen(1))));
| assert(naryFun!Seen(1));
|
| Seen s;
| static assert(!needOpCallAlias!s);
| static assert(is(typeof(naryFun!s(1))));
| assert(naryFun!s(1));
|
| struct FuncObj
| {
| bool opCall(int n) { return true; }
| }
| FuncObj fo;
| static assert(!needOpCallAlias!fo);
| static assert(is(typeof(naryFun!fo)));
| assert(naryFun!fo(1));
|
| // Function object with non-static opCall can only be called with an
| // instance, not with merely the type.
| static assert(!is(typeof(naryFun!FuncObj)));
|}
|
|@safe version(mir_core_test) unittest
|{
| static int f1(int a, string b) { return a + 1; }
| static assert(is(typeof(naryFun!(f1)(1, "2")) == int));
| assert(naryFun!(f1)(41, "a") == 42);
| string f2(int a, string b) { return b ~ "2"; }
| static assert(is(typeof(naryFun!(f2)(1, "1")) == string));
| assert(naryFun!(f2)(1, "4") == "42");
| assert(naryFun!("a + b")(41, 1) == 42);
| //@@BUG
| //assert(naryFun!("return a + b;")(41, 1) == 42);
|
| // Issue 9906
| struct Seen
| {
| static bool opCall(int x, int y) { return true; }
| }
| static assert(is(typeof(naryFun!Seen)));
| assert(naryFun!Seen(1,1));
|
| struct FuncObj
| {
| bool opCall(int x, int y) { return true; }
| }
| FuncObj fo;
| static assert(!needOpCallAlias!fo);
| static assert(is(typeof(naryFun!fo)));
| assert(naryFun!fo(1,1));
|
| // Function object with non-static opCall can only be called with an
| // instance, not with merely the type.
| static assert(!is(typeof(naryFun!FuncObj)));
|}
|
|
|/++
|N-ary predicate that reverses the order of arguments, e.g., given
|`pred(a, b, c)`, returns `pred(c, b, a)`.
|+/
|template reverseArgs(alias fun)
|{
| import std.meta: Reverse;
| ///
| @optmath auto ref reverseArgs(Args...)(auto ref Args args)
| if (is(typeof(fun(Reverse!args))))
| {
| return fun(Reverse!args);
| }
|
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| int abc(int a, int b, int c) { return a * b + c; }
| alias cba = reverseArgs!abc;
| assert(abc(91, 17, 32) == cba(32, 17, 91));
|}
|
|@safe version(mir_core_test) unittest
|{
| int a(int a) { return a * 2; }
| alias _a = reverseArgs!a;
| assert(a(2) == _a(2));
|}
|
|@safe version(mir_core_test) unittest
|{
| int b() { return 4; }
| alias _b = reverseArgs!b;
| assert(b() == _b());
|}
|
|@safe version(mir_core_test) unittest
|{
| alias gt = reverseArgs!(naryFun!("a < b"));
| assert(gt(2, 1) && !gt(1, 1));
| int x = 42;
| bool xyz(int a, int b) { return a * x < b / x; }
| auto foo = &xyz;
| foo(4, 5);
| alias zyx = reverseArgs!(foo);
| assert(zyx(5, 4) == foo(4, 5));
|}
|
|/++
|Negates predicate `pred`.
|+/
|template not(alias pred)
|{
| static if (!is(typeof(pred) : string) && !needOpCallAlias!pred)
| ///
| @optmath bool not(T...)(auto ref T args)
| {
| return !pred(args);
| }
| else
| alias not = .not!(naryFun!pred);
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| import std.algorithm.searching : find;
| import std.uni : isWhite;
| string a = " Hello, world!";
| assert(find!(not!isWhite)(a) == "Hello, world!");
|}
|
|@safe version(mir_core_test) unittest
|{
| assert(not!"a != 5"(5));
| assert(not!"a != b"(5, 5));
|
| assert(not!(() => false)());
| assert(not!(a => a != 5)(5));
| assert(not!((a, b) => a != b)(5, 5));
| assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5));
|}
|
|private template _pipe(size_t n)
|{
| static if (n)
| {
| enum i = n - 1;
| enum _pipe = "f[" ~ i.stringof ~ "](" ~ ._pipe!i ~ ")";
| }
| else
| enum _pipe = "forward!args";
|}
|
|private template _unpipe(alias fun)
|{
| import std.traits: TemplateArgsOf, TemplateOf;
| static if (__traits(compiles, TemplateOf!fun))
| static if (__traits(isSame, TemplateOf!fun, .pipe))
| alias _unpipe = TemplateArgsOf!fun;
| else
| alias _unpipe = fun;
| else
| alias _unpipe = fun;
|
|}
|
|private enum _needNary(alias fun) = is(typeof(fun) : string) || needOpCallAlias!fun;
|
|/++
|Composes passed-in functions `fun[0], fun[1], ...` returning a
|function `f(x)` that in turn returns
|`...(fun[1](fun[0](x)))...`. Each function can be a regular
|functions, a delegate, a lambda, or a string.
|+/
|template pipe(fun...)
|{
| static if (fun.length != 1)
| {
| import std.meta: staticMap, Filter;
| alias f = staticMap!(_unpipe, fun);
| static if (f.length == fun.length && Filter!(_needNary, f).length == 0)
| {
| ///
| @optmath auto ref pipe(Args...)(auto ref Args args)
| {
| return mixin (_pipe!(fun.length));
| }
| }
| else alias pipe = .pipe!(staticMap!(naryFun, f));
| }
| else alias pipe = naryFun!(fun[0]);
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| assert(pipe!("a + b", a => a * 10)(2, 3) == 50);
|}
|
|/// `pipe` can return by reference.
|version(mir_core_test) unittest
|{
| int a;
| assert(&pipe!("a", "a")(a) == &a);
|}
|
|/// Template bloat reduction
|version(mir_core_test) unittest
|{
| enum a = "a * 2";
| alias b = e => e + 2;
|
| alias p0 = pipe!(pipe!(a, b), pipe!(b, a));
| alias p1 = pipe!(a, b, b, a);
|
| static assert(__traits(isSame, p0, p1));
|}
|
|@safe version(mir_core_test) unittest
|{
| import std.algorithm.comparison : equal;
| import std.algorithm.iteration : map;
| import std.array : split;
| import std.conv : to;
|
| // First split a string in whitespace-separated tokens and then
| // convert each token into an integer
| assert(pipe!(split, map!(to!(int)))("1 2 3").equal([1, 2, 3]));
|}
|
|
|struct AliasCall(T, string methodName, TemplateArgs...)
|{
| T __this;
| alias __this this;
|
| ///
| auto lightConst()() const @property
| {
| import mir.qualifier;
| return AliasCall!(LightConstOf!T, methodName, TemplateArgs)(__this.lightConst);
| }
|
| ///
| auto lightImmutable()() immutable @property
| {
| import mir.qualifier;
| return AliasCall!(LightImmutableOf!T, methodName, TemplateArgs)(__this.lightImmutable);
| }
|
| this()(auto ref T value)
| {
| __this = value;
| }
| auto ref opCall(Args...)(auto ref Args args)
| {
| import std.traits: TemplateArgsOf;
| mixin("return __this." ~ methodName ~ (TemplateArgs.length ? "!TemplateArgs" : "") ~ "(forward!args);");
| }
|}
|
|/++
|Replaces call operator (`opCall`) for the value using its method.
|The funciton is designed to use with $(NDSLICE, topology, vmap) or $(NDSLICE, topology, map).
|Params:
| methodName = name of the methods to use for opCall and opIndex
| TemplateArgs = template arguments
|+/
|template aliasCall(string methodName, TemplateArgs...)
|{
| /++
| Params:
| value = the value to wrap
| Returns:
| wrapped value with implemented opCall and opIndex methods
| +/
| AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(T value) @property
| {
| return typeof(return)(value);
| }
|
| /// ditto
| ref AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(return ref T value) @property @trusted
| {
| return *cast(typeof(return)*) &value;
| }
|}
|
|///
|@safe pure nothrow version(mir_core_test) unittest
|{
| static struct S
| {
| auto lightConst()() const @property { return S(); }
|
| auto fun(size_t ct_param = 1)(size_t rt_param) const
| {
| return rt_param + ct_param;
| }
| }
|
| S s;
|
| auto sfun = aliasCall!"fun"(s);
| assert(sfun(3) == 4);
|
| auto sfun10 = aliasCall!("fun", 10)(s); // uses fun!10
| assert(sfun10(3) == 13);
|}
|
|/++
|+/
|template recurseTemplatePipe(alias Template, size_t N, Args...)
|{
| static if (N == 0)
| alias recurseTemplatePipe = Args;
| else
| {
| alias recurseTemplatePipe = Template!(.recurseTemplatePipe!(Template, N - 1, Args));
| }
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| // import mir.ndslice.topology: map;
| alias map(alias fun) = a => a; // some template
| static assert (__traits(isSame, recurseTemplatePipe!(map, 2, "a * 2"), map!(map!"a * 2")));
|}
|
|/++
|+/
|template selfAndRecurseTemplatePipe(alias Template, size_t N, Args...)
|{
| static if (N == 0)
| alias selfAndRecurseTemplatePipe = Args;
| else
| {
| alias selfAndRecurseTemplatePipe = Template!(.selfAndRecurseTemplatePipe!(Template, N - 1, Args));
| }
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| // import mir.ndslice.topology: map;
| alias map(alias fun) = a => a; // some template
| static assert (__traits(isSame, selfAndRecurseTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2"))));
|}
|
|/++
|+/
|template selfTemplatePipe(alias Template, size_t N, Args...)
|{
| static if (N == 0)
| alias selfTemplatePipe = Args;
| else
| {
| alias selfTemplatePipe = Template!(.selfTemplatePipe!(Template, N - 1, Args));
| }
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| // import mir.ndslice.topology: map;
| alias map(alias fun) = a => a; // some template
| static assert (__traits(isSame, selfTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2"))));
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/functional.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-internal-meta.lst
|module mir.internal.meta;
|
|/++
| Gets the matching $(DDSUBLINK spec/attribute, uda, user-defined attributes)
| from the given symbol.
| If the UDA is a type, then any UDAs of the same type on the symbol will
| match. If the UDA is a template for a type, then any UDA which is an
| instantiation of that template will match. And if the UDA is a value,
| then any UDAs on the symbol which are equal to that value will match.
| See_Also:
| $(LREF hasUDA)
| +/
|template getUDAs(T, string member, alias attribute)
|{
| import std.meta : Filter, AliasSeq;
| T* aggregate;
| static if (__traits(compiles, __traits(getAttributes, __traits(getMember, *aggregate, member))))
| alias getUDAs = Filter!(isDesiredUDA!attribute, __traits(getAttributes, __traits(getMember, *aggregate, member)));
| else
| alias getUDAs = AliasSeq!();
|}
|
|/++
| Determine if a symbol has a given
| $(DDSUBLINK spec/attribute, uda, user-defined attribute).
| See_Also:
| $(LREF getUDAs)
| +/
|enum hasUDA(T, string member, alias attribute) = getUDAs!(T, member, attribute).length != 0;
|
|
|private template isDesiredUDA(alias attribute)
|{
| template isDesiredUDA(alias toCheck)
| {
| static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
| {
| static if (__traits(compiles, toCheck == attribute))
| enum isDesiredUDA = toCheck == attribute;
| else
| enum isDesiredUDA = false;
| }
| else static if (is(typeof(toCheck)))
| {
| static if (__traits(isTemplate, attribute))
| enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck));
| else
| enum isDesiredUDA = is(typeof(toCheck) == attribute);
| }
| else static if (__traits(isTemplate, attribute))
| enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
| else
| enum isDesiredUDA = is(toCheck == attribute);
| }
|}
|
|template memberTypeOf(T, string member)
|{
| T* aggregate;
| alias memberTypeOf = typeof(__traits(getMember, aggregate, member));
|}
|
|template isMemberType(T, string member)
|{
| enum isMemberType = is(typeof((ref __traits(getMember, T, member) v){})) || is(__traits(getMember, T, member) : void);
|}
|
|template isSingleMember(T, string member)
|{
| import std.meta: AliasSeq;
| enum isSingleMember = AliasSeq!(__traits(getMember, T, member)).length == 1;
|}
|
|template AllMembersRec(T)
|{
| static if (is(T == class) || is(T == struct) || is(T == union) || is(T == interface))
| {
| static if (__traits(getAliasThis, T).length)
| {
| T* aggregate;
| static if (is(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)))))
| {
| import std.meta: Filter, AliasSeq;
| alias baseMembers = AllMembersRec!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))));
| alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
| alias AllMembersRec = NoDuplicates!(AliasSeq!(baseMembers, members));
| }
| else
| {
| alias AllMembersRec = __traits(allMembers, T);
| }
| }
| else
| {
| alias AllMembersRec = __traits(allMembers, T);
| }
| }
| else
| {
| import std.meta: AliasSeq;
| alias AllMembersRec = AliasSeq!();
| }
|}
|
|alias ConstOf(T) = const T;
|enum Alignof(T) = T.alignof;
|enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a; } );
|enum canImplicitlyRemoveConst(T) = __traits(compiles, {static T _function_(ref const T a) { return a; }} );
|enum canRemoveConst(T) = canConstructWith!(const T, T);
|enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
|enum hasOpPostMove(T) = __traits(hasMember, T, "opPostMove");
|enum hasOpCmp(T) = __traits(hasMember, T, "opCmp");
|enum hasToHash(T) = __traits(hasMember, T, "toHash");
|static if (__VERSION__ < 2094)
| enum isCopyable(S) = is(typeof({ S foo = S.init; S copy = foo; }));
|else
| enum isCopyable(S) = __traits(isCopyable, S);
|enum isPOD(T) = __traits(isPOD, T);
|enum Sizeof(T) = T.sizeof;
|
|enum hasInoutConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope inout S rhs) inout { this.a = rhs.a; } }} );
|enum hasConstConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) const { this.a = rhs.a; } }} );
|enum hasImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope immutable S rhs) immutable { this.a = rhs.a; } }} );
|enum hasMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope S rhs) { this.a = rhs.a; } }} );
|enum hasSemiImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) immutable { this.a = rhs.a; } }} );
|enum hasSemiMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) { this.a = rhs.a; } }} );
|
|@safe version(mir_core_test) unittest
|{
| static struct S { this(ref return scope inout S) inout {} }
| static inout(S) _function_(ref inout S a) { return a; }
| static struct C2 { uint* a; this(ref return scope const S) const {} }
| static assert(hasInoutConstruction!uint);
| static assert(hasInoutConstruction!(immutable(uint)[]));
| static assert(hasInoutConstruction!(typeof(null)));
| static assert(hasInoutConstruction!S);
|}
|
|template staticIsSorted(alias cmp, Seq...)
|{
| static if (Seq.length <= 1)
| enum staticIsSorted = true;
| else static if (Seq.length == 2)
| enum staticIsSorted = cmp!(Seq[0], Seq[1]);
| else
| {
| enum staticIsSorted =
| cmp!(Seq[($ / 2) - 1], Seq[$ / 2]) &&
| staticIsSorted!(cmp, Seq[0 .. $ / 2]) &&
| staticIsSorted!(cmp, Seq[$ / 2 .. $]);
| }
|}
|
|template TryRemoveConst(T)
|{
| import std.traits: Unqual;
| alias U = Unqual!T;
| static if (canImplicitlyRemoveConst!U)
| {
| alias TryRemoveConst = U;
| }
| else
| {
| alias TryRemoveConst = T;
| }
|}
|
|
|template TypeCmp(A, B)
|{
| enum bool TypeCmp = is(A == B) ? false:
| is(A == typeof(null)) ? true:
| is(B == typeof(null)) ? false:
| is(A == void) ? true:
| is(B == void) ? false:
| A.sizeof < B.sizeof ? true:
| A.sizeof > B.sizeof ? false:
| A.mangleof < B.mangleof;
|}
|
|template isInstanceOf(alias S)
|{
| enum isInstanceOf(T) = is(T == S!Args, Args...);
|}
|
|version(mir_core_test) unittest
|{
| static assert(is(TryRemoveConst!(const int) == int));
|}
|
|
|// taken from std.meta.allSatisfy
|template allSatisfy(alias F, T...)
|{
| static foreach (Ti; T)
| {
| static if (!is(typeof(allSatisfy) == bool) && // not yet defined
| !F!(Ti))
| {
| enum allSatisfy = false;
| }
| }
| static if (!is(typeof(allSatisfy) == bool)) // if not yet defined
| {
| enum allSatisfy = true;
| }
|}
|
|template Erase(T, TList...)
|{
| alias Erase = GenericErase!(T, TList).result;
|}
|
|template Erase(alias T, TList...)
|{
| alias Erase = GenericErase!(T, TList).result;
|}
|
|template GenericErase(args...)
|if (args.length >= 1)
|{
| import std.meta: AliasSeq;
|
| alias e = OldAlias!(args[0]);
| alias tuple = args[1 .. $] ;
|
| static if (tuple.length)
| {
| alias head = OldAlias!(tuple[0]);
| alias tail = tuple[1 .. $];
|
| static if (isSame!(e, head))
| alias result = tail;
| else
| alias result = AliasSeq!(head, GenericErase!(e, tail).result);
| }
| else
| {
| alias result = AliasSeq!();
| }
|}
|
|template Pack(T...)
|{
| alias Expand = T;
| enum equals(U...) = isSame!(Pack!T, Pack!U);
|}
|
|
|template EraseAll(T, TList...)
|{
| alias EraseAll = GenericEraseAll!(T, TList).result;
|}
|
|template EraseAll(alias T, TList...)
|{
| alias EraseAll = GenericEraseAll!(T, TList).result;
|}
|
|template GenericEraseAll(args...)
|if (args.length >= 1)
|{
| import std.meta: AliasSeq;
|
| alias e = OldAlias!(args[0]);
| alias tuple = args[1 .. $];
|
| static if (tuple.length)
| {
| alias head = OldAlias!(tuple[0]);
| alias tail = tuple[1 .. $];
| alias next = AliasSeq!(
| GenericEraseAll!(e, tail[0..$/2]).result,
| GenericEraseAll!(e, tail[$/2..$]).result
| );
|
| static if (isSame!(e, head))
| alias result = next;
| else
| alias result = AliasSeq!(head, next);
| }
| else
| {
| alias result = AliasSeq!();
| }
|}
|
|template OldAlias(T)
|{
| alias OldAlias = T;
|}
|
|template OldAlias(alias T)
|{
| alias OldAlias = T;
|}
|
|template EraseAllN(uint N, TList...)
|{
| static if (N == 1)
| {
| alias EraseAllN = EraseAll!(TList[0], TList[1 .. $]);
| }
| else
| {
| static if (N & 1)
| alias EraseAllN = EraseAllN!(N / 2, TList[N / 2 + 1 .. N],
| EraseAllN!(N / 2 + 1, TList[0 .. N / 2 + 1], TList[N .. $]));
| else
| alias EraseAllN = EraseAllN!(N / 2, TList[N / 2 .. N],
| EraseAllN!(N / 2, TList[0 .. N / 2], TList[N .. $]));
| }
|}
|
|template NoDuplicates(TList...)
|{
| static if (TList.length >= 2)
| {
| import std.meta: AliasSeq;
|
| alias fst = NoDuplicates!(TList[0 .. $/2]);
| alias snd = NoDuplicates!(TList[$/2 .. $]);
| alias NoDuplicates = AliasSeq!(fst, EraseAllN!(fst.length, fst, snd));
| }
| else
| {
| alias NoDuplicates = TList;
| }
|}
|
|
|template isSame(ab...)
|if (ab.length == 2)
|{
| static if (is(ab[0]) && is(ab[1]))
| {
| enum isSame = is(ab[0] == ab[1]);
| }
| else static if (!is(ab[0]) && !is(ab[1]) &&
| !(is(typeof(&ab[0])) && is(typeof(&ab[1]))) &&
| __traits(compiles, { enum isSame = ab[0] == ab[1]; }))
| {
| enum isSame = (ab[0] == ab[1]);
| }
| else
| {
| enum isSame = __traits(isSame, ab[0], ab[1]);
| }
|}
|
|template Mod(From, To)
|{
| template Mod(T)
| {
| static if (is(T == From))
| alias Mod = To;
| else
| alias Mod = T;
| }
|}
|
|template Replace(From, To, T...)
|{
| import std.meta: staticMap;
| alias Replace = staticMap!(Mod!(From, To), T);
|}
|
|template ReplaceTypeUnless(alias pred, From, To, T...)
|{
| static if (T.length == 1)
| {
| import std.meta: staticMap;
| static if (pred!(T[0]))
| alias ReplaceTypeUnless = T[0];
| else static if (is(T[0] == From))
| alias ReplaceTypeUnless = To;
| else static if (is(T[0] == const(U), U))
| alias ReplaceTypeUnless = const(ReplaceTypeUnless!(pred, From, To, U));
| else static if (is(T[0] == immutable(U), U))
| alias ReplaceTypeUnless = immutable(ReplaceTypeUnless!(pred, From, To, U));
| else static if (is(T[0] == shared(U), U))
| alias ReplaceTypeUnless = shared(ReplaceTypeUnless!(pred, From, To, U));
| else static if (is(T[0] == U*, U))
| {
| static if (is(U == function))
| alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
| else
| alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)*;
| }
| else static if (is(T[0] == delegate))
| {
| alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
| }
| else static if (is(T[0] == function))
| {
| static assert(0, "Function types not supported," ~
| " use a function pointer type instead of " ~ T[0].stringof);
| }
| else static if (is(T[0] == U!V, alias U, V...))
| {
| template replaceTemplateArgs(T...)
| {
| static if (is(typeof(T[0])))
| static if (__traits(compiles, {alias replaceTemplateArgs = T[0];}))
| alias replaceTemplateArgs = T[0];
| else
| enum replaceTemplateArgs = T[0];
| else
| alias replaceTemplateArgs = ReplaceTypeUnless!(pred, From, To, T[0]);
| }
| alias ReplaceTypeUnless = U!(staticMap!(replaceTemplateArgs, V));
| }
| else static if (is(T[0] == struct))
| // don't match with alias this struct below
| // https://issues.dlang.org/show_bug.cgi?id=15168
| alias ReplaceTypeUnless = T[0];
| else static if (is(T[0] == enum))
| alias ReplaceTypeUnless = T[0];
| else static if (is(T[0] == U[], U))
| alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[];
| else static if (is(T[0] == U[n], U, size_t n))
| alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[n];
| else static if (is(T[0] == U[V], U, V))
| alias ReplaceTypeUnless =
| ReplaceTypeUnless!(pred, From, To, U)[ReplaceTypeUnless!(pred, From, To, V)];
| else
| alias ReplaceTypeUnless = T[0];
| }
| else static if (T.length > 1)
| {
| import std.meta: AliasSeq;
| alias ReplaceTypeUnless = AliasSeq!(ReplaceTypeUnless!(pred, From, To, T[0]),
| ReplaceTypeUnless!(pred, From, To, T[1 .. $]));
| }
| else
| {
| import std.meta: AliasSeq;
| alias ReplaceTypeUnless = AliasSeq!();
| }
|}
|
|@safe version(mir_core_test) unittest
|{
| import std.typecons: Tuple;
| import std.traits : isArray;
| static assert(
| is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) &&
| is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) &&
| is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[]))
| == Tuple!(string, int[]))
| );
|}
|
|template Contains(Types...)
|{
| import std.meta: staticIndexOf;
| enum Contains(T) = staticIndexOf!(T, Types) >= 0;
|}
|
|template replaceTypeInFunctionTypeUnless(alias pred, From, To, fun)
|{
| import std.meta;
| import std.traits;
| alias RX = ReplaceTypeUnless!(pred, From, To, ReturnType!fun);
| alias PX = AliasSeq!(ReplaceTypeUnless!(pred, From, To, Parameters!fun));
| // Wrapping with AliasSeq is neccesary because ReplaceType doesn't return
| // tuple if Parameters!fun.length == 1
| string gen()
| {
| enum linkage = functionLinkage!fun;
| alias attributes = functionAttributes!fun;
| enum variadicStyle = variadicFunctionStyle!fun;
| alias storageClasses = ParameterStorageClassTuple!fun;
| string result;
| result ~= "extern(" ~ linkage ~ ") ";
| static if (attributes & FunctionAttribute.ref_)
| {
| result ~= "ref ";
| }
| result ~= "RX";
| static if (is(fun == delegate))
| result ~= " delegate";
| else
| result ~= " function";
| result ~= "(";
| static foreach (i; 0 .. PX.length)
| {
| if (i)
| result ~= ", ";
| if (storageClasses[i] & ParameterStorageClass.scope_)
| result ~= "scope ";
| if (storageClasses[i] & ParameterStorageClass.out_)
| result ~= "out ";
| if (storageClasses[i] & ParameterStorageClass.ref_)
| result ~= "ref ";
| if (storageClasses[i] & ParameterStorageClass.lazy_)
| result ~= "lazy ";
| if (storageClasses[i] & ParameterStorageClass.return_)
| result ~= "return ";
| result ~= "PX[" ~ i.stringof ~ "]";
| }
| static if (variadicStyle == Variadic.typesafe)
| result ~= " ...";
| else static if (variadicStyle != Variadic.no)
| result ~= ", ...";
| result ~= ")";
| static if (attributes & FunctionAttribute.pure_)
| result ~= " pure";
| static if (attributes & FunctionAttribute.nothrow_)
| result ~= " nothrow";
| static if (attributes & FunctionAttribute.property)
| result ~= " @property";
| static if (attributes & FunctionAttribute.trusted)
| result ~= " @trusted";
| static if (attributes & FunctionAttribute.safe)
| result ~= " @safe";
| static if (attributes & FunctionAttribute.nogc)
| result ~= " @nogc";
| static if (attributes & FunctionAttribute.system)
| result ~= " @system";
| static if (attributes & FunctionAttribute.const_)
| result ~= " const";
| static if (attributes & FunctionAttribute.immutable_)
| result ~= " immutable";
| static if (attributes & FunctionAttribute.inout_)
| result ~= " inout";
| static if (attributes & FunctionAttribute.shared_)
| result ~= " shared";
| static if (attributes & FunctionAttribute.return_)
| result ~= " return";
| return result;
| }
| mixin("alias replaceTypeInFunctionTypeUnless = " ~ gen() ~ ";");
|}
|
|enum false_(T) = false;
|
|alias ReplaceType(From, To, T...) = ReplaceTypeUnless!(false_, From, To, T);
|
|version(mir_core_test) @safe unittest
|{
| import std.typecons: Unique, Tuple;
| template Test(Ts...)
| {
| static if (Ts.length)
| {
| //pragma(msg, "Testing: ReplaceType!("~Ts[0].stringof~", "
| // ~Ts[1].stringof~", "~Ts[2].stringof~")");
| static assert(is(ReplaceType!(Ts[0], Ts[1], Ts[2]) == Ts[3]),
| "ReplaceType!("~Ts[0].stringof~", "~Ts[1].stringof~", "
| ~Ts[2].stringof~") == "
| ~ReplaceType!(Ts[0], Ts[1], Ts[2]).stringof);
| alias Test = Test!(Ts[4 .. $]);
| }
| else alias Test = void;
| }
| //import core.stdc.stdio;
| alias RefFun1 = ref int function(float, long);
| alias RefFun2 = ref float function(float, long);
| extern(C) int printf(const char*, ...) nothrow @nogc @system;
| extern(C) float floatPrintf(const char*, ...) nothrow @nogc @system;
| int func(float);
| int x;
| struct S1 { void foo() { x = 1; } }
| struct S2 { void bar() { x = 2; } }
| alias Pass = Test!(
| int, float, typeof(&func), float delegate(float),
| int, float, typeof(&printf), typeof(&floatPrintf),
| int, float, int function(out long, ...),
| float function(out long, ...),
| int, float, int function(ref float, long),
| float function(ref float, long),
| int, float, int function(ref int, long),
| float function(ref float, long),
| int, float, int function(out int, long),
| float function(out float, long),
| int, float, int function(lazy int, long),
| float function(lazy float, long),
| int, float, int function(out long, ref const int),
| float function(out long, ref const float),
| int, int, int, int,
| int, float, int, float,
| int, float, const int, const float,
| int, float, immutable int, immutable float,
| int, float, shared int, shared float,
| int, float, int*, float*,
| int, float, const(int)*, const(float)*,
| int, float, const(int*), const(float*),
| const(int)*, float, const(int*), const(float),
| int*, float, const(int)*, const(int)*,
| int, float, int[], float[],
| int, float, int[42], float[42],
| int, float, const(int)[42], const(float)[42],
| int, float, const(int[42]), const(float[42]),
| int, float, int[int], float[float],
| int, float, int[double], float[double],
| int, float, double[int], double[float],
| int, float, int function(float, long), float function(float, long),
| int, float, int function(float), float function(float),
| int, float, int function(float, int), float function(float, float),
| int, float, int delegate(float, long), float delegate(float, long),
| int, float, int delegate(float), float delegate(float),
| int, float, int delegate(float, int), float delegate(float, float),
| int, float, Unique!int, Unique!float,
| int, float, Tuple!(float, int), Tuple!(float, float),
| int, float, RefFun1, RefFun2,
| S1, S2,
| S1[1][][S1]* function(),
| S2[1][][S2]* function(),
| int, string,
| int[3] function( int[] arr, int[2] ...) pure @trusted,
| string[3] function(string[] arr, string[2] ...) pure @trusted,
| );
| // https://issues.dlang.org/show_bug.cgi?id=15168
| static struct T1 { string s; alias s this; }
| static struct T2 { char[10] s; alias s this; }
| static struct T3 { string[string] s; alias s this; }
| alias Pass2 = Test!(
| ubyte, ubyte, T1, T1,
| ubyte, ubyte, T2, T2,
| ubyte, ubyte, T3, T3,
| );
|}
|// https://issues.dlang.org/show_bug.cgi?id=17116
|version(mir_core_test) @safe unittest
|{
| alias ConstDg = void delegate(float) const;
| alias B = void delegate(int) const;
| alias A = ReplaceType!(float, int, ConstDg);
| static assert(is(B == A));
|}
| // https://issues.dlang.org/show_bug.cgi?id=19696
|version(mir_core_test) @safe unittest
|{
| static struct T(U) {}
| static struct S { T!int t; alias t this; }
| static assert(is(ReplaceType!(float, float, S) == S));
|}
| // https://issues.dlang.org/show_bug.cgi?id=19697
|version(mir_core_test) @safe unittest
|{
| class D(T) {}
| class C : D!C {}
| static assert(is(ReplaceType!(float, float, C)));
|}
|// https://issues.dlang.org/show_bug.cgi?id=16132
|version(mir_core_test) @safe unittest
|{
| interface I(T) {}
| class C : I!int {}
| static assert(is(ReplaceType!(int, string, C) == C));
|}
|
|template basicElementType(T)
|{
| import std.traits: isArray, ForeachType;
| static if (isArray!T)
| alias basicElementType = ForeachType!T;
| else
| alias basicElementType = T;
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/internal/meta.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-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
| static if (__traits(getAliasThis, C).length == 1)
| enum isComplex = .isComplex!(typeof(__traits(getMember, C, __traits(getAliasThis, C)[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(mir_core_test)
|unittest
|{
| static assert(!isComplex!double);
| import mir.complex: Complex;
| static assert(isComplex!(Complex!double));
| import std.complex: PhobosComplex = Complex;
| static assert(isComplex!(PhobosComplex!double));
|
| static struct C
| {
| Complex!double value;
| alias value this;
| }
|
| static assert(isComplex!C);
|}
|
|///
|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.3.6/mir-core/source/mir/internal/utility.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-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: Ilia Ki, 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)
|{
| import ldc.intrinsics: LLVM_version;
| 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);
|
| static if (LLVM_version >= 1300)
| pragma(LDC_intrinsic, "llvm.powi.f#.i32")
| ///
| T powi(T)(in T val, int power) if (isFloatingPoint!T);
| else
| pragma(LDC_intrinsic, "llvm.powi.f#")
| ///
| T powi(T)(in T val, int power) if (isFloatingPoint!T);
|
| version(mir_core_test)
| unittest
| {
| assert(powi(3.0, int(2)) == 9);
| float f = 3;
| assert(powi(f, int(2)) == 9);
| }
|
| 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);
|}
|
|deprecated("Use mir.complex.approxEqual instead"):
|
|/// 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.3.6/mir-core/source/mir/math/common.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-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, Ilia Ki
| */
|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.3.6/mir-core/source/mir/math/ieee.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-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.3.6/mir-core/source/mir/math/package.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-primitives.lst
|/++
|Templates used to check primitives and
|range primitives for arrays with multi-dimensional like API support.
|
|Note:
|UTF strings behaves like common arrays in Mir.
|`std.uni.byCodePoint` can be used to create a range of characters.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki, $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and
| $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas
| in building this module goes to
| $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi)
|+/
|module mir.primitives;
|
|import mir.internal.utility;
|import mir.math.common: optmath;
|import std.traits;
|
|@optmath:
|
|/++
|Returns: `true` if `R` has a `length` member that returns an
|integral type implicitly convertible to `size_t`.
|
|`R` does not have to be a range.
|+/
|enum bool hasLength(R) = is(typeof(
|(const R r, inout int = 0)
|{
| size_t l = r.length;
|}));
|
|///
|@safe version(mir_core_test) unittest
|{
| static assert(hasLength!(char[]));
| static assert(hasLength!(int[]));
| static assert(hasLength!(inout(int)[]));
|
| struct B { size_t length() const { return 0; } }
| struct C { @property size_t length() const { return 0; } }
| static assert(hasLength!(B));
| static assert(hasLength!(C));
|}
|
|/++
|Returns: `true` if `R` has a `shape` member that returns an static array type of size_t[N].
|+/
|enum bool hasShape(R) = is(typeof(
|(const R r, inout int = 0)
|{
| auto l = r.shape;
| alias F = typeof(l);
| import std.traits;
| static assert(isStaticArray!F);
| static assert(is(ForeachType!F == size_t));
|}));
|
|///
|@safe version(mir_core_test) unittest
|{
| static assert(hasShape!(char[]));
| static assert(hasShape!(int[]));
| static assert(hasShape!(inout(int)[]));
|
| struct B { size_t length() const { return 0; } }
| struct C { @property size_t length() const { return 0; } }
| static assert(hasShape!(B));
| static assert(hasShape!(C));
|}
|
|///
|auto shape(Range)(scope const auto ref Range range) @property
| if (hasLength!Range || hasShape!Range)
|{
| static if (__traits(hasMember, Range, "shape"))
| {
| return range.shape;
| }
| else
| {
| size_t[1] ret;
| ret[0] = range.length;
| return ret;
| }
|}
|
|///
|version(mir_core_test) unittest
|{
| static assert([2, 2, 2].shape == [3]);
|}
|
|///
|template DimensionCount(T)
|{
| import mir.ndslice.slice: Slice, SliceKind;
| /// Extracts dimension count from a $(LREF Slice). Alias for $(LREF isSlice).
| static if(is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind))
| enum size_t DimensionCount = N;
| else
| static if (hasShape!T)
| enum size_t DimensionCount = typeof(T.init.shape).length;
| else
| enum size_t DimensionCount = 1;
|}
|
|package(mir) bool anyEmptyShape(size_t N)(scope const auto ref size_t[N] shape) @property
|{
| foreach (i; Iota!N)
| if (shape[i] == 0)
| return true;
| return false;
|}
|
|///
|bool anyEmpty(Range)(scope const auto ref Range range) @property
| if (hasShape!Range || __traits(hasMember, Range, "anyEmpty"))
|{
| static if (__traits(hasMember, Range, "anyEmpty"))
| {
| return range.anyEmpty;
| }
| else
| static if (__traits(hasMember, Range, "shape"))
| {
| return anyEmptyShape(range.shape);
| }
| else
| {
| return range.empty;
| }
|}
|
|///
|size_t elementCount(Range)(scope const auto ref Range range) @property
| if (hasShape!Range || __traits(hasMember, Range, "elementCount"))
|{
| static if (__traits(hasMember, Range, "elementCount"))
| {
| return range.elementCount;
| }
| else
| {
| auto sh = range.shape;
| size_t ret = sh[0];
| foreach(i; Iota!(1, sh.length))
| {
| ret *= sh[i];
| }
| return ret;
| }
|}
|
|deprecated("use elementCount instead")
|alias elementsCount = elementCount;
|
|
|/++
|Returns the element type of a struct with `.DeepElement` inner alias or a type of common array.
|Returns `ForeachType` if struct does not have `.DeepElement` member.
|+/
|template DeepElementType(S)
| if (is(S == struct) || is(S == class) || is(S == interface))
|{
| static if (__traits(hasMember, S, "DeepElement"))
| alias DeepElementType = S.DeepElement;
| else
| alias DeepElementType = ForeachType!S;
|}
|
|/// ditto
|alias DeepElementType(S : T[], T) = T;
|
|/+ ARRAY PRIMITIVES +/
|pragma(inline, true):
|
|///
|bool empty(size_t dim = 0, T)(scope const T[] ar)
| if (!dim)
|{
| return !ar.length;
|}
|
|///
|version(mir_core_test) unittest
|{
| assert((int[]).init.empty);
| assert(![1].empty!0); // Slice-like API
|}
|
|///
|ref inout(T) front(size_t dim = 0, T)(scope return inout(T)[] ar)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| assert(ar.length, "Accessing front of an empty array.");
| return ar[0];
|}
|
|///
|version(mir_core_test) unittest
|{
| assert(*&[3, 4].front == 3); // access be ref
| assert([3, 4].front!0 == 3); // Slice-like API
|}
|
|
|///
|ref inout(T) back(size_t dim = 0, T)(scope return inout(T)[] ar)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| assert(ar.length, "Accessing back of an empty array.");
| return ar[$ - 1];
|}
|
|///
|version(mir_core_test) unittest
|{
| assert(*&[3, 4].back == 4); // access be ref
| assert([3, 4].back!0 == 4); // Slice-like API
|}
|
|///
|void popFront(size_t dim = 0, T)(scope ref inout(T)[] ar)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| assert(ar.length, "Evaluating popFront() on an empty array.");
| ar = ar[1 .. $];
|}
|
|///
|version(mir_core_test) unittest
|{
| auto ar = [3, 4];
| ar.popFront;
| assert(ar == [4]);
| ar.popFront!0; // Slice-like API
| assert(ar == []);
|}
|
|///
|void popBack(size_t dim = 0, T)(scope ref inout(T)[] ar)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| assert(ar.length, "Evaluating popBack() on an empty array.");
| ar = ar[0 .. $ - 1];
|}
|
|///
|version(mir_core_test) unittest
|{
| auto ar = [3, 4];
| ar.popBack;
| assert(ar == [3]);
| ar.popBack!0; // Slice-like API
| assert(ar == []);
|}
|
|///
|size_t popFrontN(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| n = ar.length < n ? ar.length : n;
| ar = ar[n .. $];
| return n;
|}
|
|///
|version(mir_core_test) unittest
|{
| auto ar = [3, 4];
| ar.popFrontN(1);
| assert(ar == [4]);
| ar.popFrontN!0(10); // Slice-like API
| assert(ar == []);
|}
|
|///
|size_t popBackN(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| n = ar.length < n ? ar.length : n;
| ar = ar[0 .. $ - n];
| return n;
|}
|
|///
|version(mir_core_test) unittest
|{
| auto ar = [3, 4];
| ar.popBackN(1);
| assert(ar == [3]);
| ar.popBackN!0(10); // Slice-like API
| assert(ar == []);
|}
|
|///
|void popFrontExactly(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| assert(ar.length >= n, "Evaluating *.popFrontExactly(n) on an array with length less then n.");
| ar = ar[n .. $];
|}
|
|///
|version(mir_core_test) unittest
|{
| auto ar = [3, 4, 5];
| ar.popFrontExactly(2);
| assert(ar == [5]);
| ar.popFrontExactly!0(1); // Slice-like API
| assert(ar == []);
|}
|
|///
|void popBackExactly(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n)
| if (!dim && !is(Unqual!T[] == void[]))
|{
| assert(ar.length >= n, "Evaluating *.popBackExactly(n) on an array with length less then n.");
| ar = ar[0 .. $ - n];
|}
|
|///
|version(mir_core_test) unittest
|{
| auto ar = [3, 4, 5];
| ar.popBackExactly(2);
| assert(ar == [3]);
| ar.popBackExactly!0(1); // Slice-like API
| assert(ar == []);
|}
|
|///
|size_t length(size_t d : 0, T)(in T[] array)
| if (d == 0)
|{
| return array.length;
|}
|
|///
|version(mir_core_test) unittest
|{
| assert([1, 2].length!0 == 2);
| assert([1, 2].elementCount == 2);
|}
|
|///
|inout(T)[] save(T)(scope return inout(T)[] array)
|{
| return array;
|}
|
|///
|version(mir_core_test) unittest
|{
| auto a = [1, 2];
| assert(a is a.save);
|}
|
|/**
|Returns `true` if `R` is an input range. An input range must
|define the primitives `empty`, `popFront`, and `front`. The
|following code should compile for any input range.
|----
|R r; // can define a range object
|if (r.empty) {} // can test for empty
|r.popFront(); // can invoke popFront()
|auto h = r.front; // can get the front of the range of non-void type
|----
|The following are rules of input ranges are assumed to hold true in all
|Phobos code. These rules are not checkable at compile-time, so not conforming
|to these rules when writing ranges or range based code will result in
|undefined behavior.
|$(UL
| $(LI `r.empty` returns `false` if and only if there is more data
| available in the range.)
| $(LI `r.empty` evaluated multiple times, without calling
| `r.popFront`, or otherwise mutating the range object or the
| underlying data, yields the same result for every evaluation.)
| $(LI `r.front` returns the current element in the range.
| It may return by value or by reference.)
| $(LI `r.front` can be legally evaluated if and only if evaluating
| `r.empty` has, or would have, equaled `false`.)
| $(LI `r.front` evaluated multiple times, without calling
| `r.popFront`, or otherwise mutating the range object or the
| underlying data, yields the same result for every evaluation.)
| $(LI `r.popFront` advances to the next element in the range.)
| $(LI `r.popFront` can be called if and only if evaluating `r.empty`
| has, or would have, equaled `false`.)
|)
|Also, note that Phobos code assumes that the primitives `r.front` and
|`r.empty` are $(BIGOH 1) time complexity wise or "cheap" in terms of
|running time. $(BIGOH) statements in the documentation of range functions
|are made with this assumption.
|Params:
| R = type to be tested
|Returns:
| `true` if R is an input range, `false` if not
| */
|enum bool isInputRange(R) =
| is(typeof(R.init) == R)
| && is(ReturnType!((R r) => r.empty) == bool)
| && is(typeof((return ref R r) => r.front))
| && !is(ReturnType!((R r) => r.front) == void)
| && is(typeof((R r) => r.popFront));
|
|/**
|Returns `true` if `R` is an infinite input range. An
|infinite input range is an input range that has a statically-defined
|enumerated member called `empty` that is always `false`,
|for example:
|----
|struct MyInfiniteRange
|{
| enum bool empty = false;
| ...
|}
|----
| */
|
|template isInfinite(R)
|{
| static if (isInputRange!R && __traits(compiles, { enum e = R.empty; }))
| enum bool isInfinite = !R.empty;
| else
| enum bool isInfinite = false;
|}
|
|
|/**
|The element type of `R`. `R` does not have to be a range. The
|element type is determined as the type yielded by `r.front` for an
|object `r` of type `R`. For example, `ElementType!(T[])` is
|`T` if `T[]` isn't a narrow string; if it is, the element type is
|`dchar`. If `R` doesn't have `front`, `ElementType!R` is
|`void`.
| */
|template ElementType(R)
|{
| static if (is(typeof(R.init.front.init) T))
| alias ElementType = T;
| else
| alias ElementType = void;
|}
|
|/++
|This is a best-effort implementation of `length` for any kind of
|range.
|If `hasLength!Range`, simply returns `range.length` without
|checking `upTo` (when specified).
|Otherwise, walks the range through its length and returns the number
|of elements seen. Performes $(BIGOH n) evaluations of `range.empty`
|and `range.popFront()`, where `n` is the effective length of $(D
|range).
|+/
|auto walkLength(Range)(Range range)
|if (isIterable!Range && !isInfinite!Range)
|{
| static if (hasLength!Range)
| return range.length;
| else
| static if (__traits(hasMember, Range, "walkLength"))
| return range.walkLength;
| static if (isInputRange!Range)
| {
| size_t result;
| for ( ; !range.empty ; range.popFront() )
| ++result;
| return result;
| }
| else
| {
| size_t result;
| foreach (ref e; range)
| ++result;
| return result;
| }
|}
|
|/++
|Returns `true` if `R` is an output range for elements of type
|`E`. An output range is defined functionally as a range that
|supports the operation $(D r.put(e)).
| +/
|enum bool isOutputRange(R, E) =
| is(typeof(R.init.put(E.init)));
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/primitives.d has no code
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-reflection.lst
|/++
|Base reflection utilities.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki
|Macros:
|+/
|module mir.reflection;
|
|import std.meta;
|import std.traits: hasUDA, getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers, isAggregateType;
|import mir.internal.meta: hasUDA;
|import mir.functional: Tuple;
|
|deprecated
|package alias isSomeStruct = isAggregateType;
|
|/++
|Attribute to force member serialization for static fields, compiletime `enum` members and non-property methods.
|+/
|enum reflectSerde;
|
|/++
|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 msg;
|
| /++
| Number in an issue tracker. Not mandatory.
| +/
| uint issueNumber = uint.max;
| /++
| Should be kind of version number if one can be given.
| Can be something else if that's not possible. Not mandatory.
| +/
| string removalTime;
|}
|
|/// 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"));
|}
|
|version(mir_core_test) unittest
|{
| struct S
| {
| @reflectSerde enum s = "str";
| enum t = "str";
| }
| static assert(hasUDA!(S, "s", reflectSerde));
| static assert(!hasUDA!(S, "t", reflectSerde));
|}
|
|/++
|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), SerdeFieldsAndProperties!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;
|
| enum s = "str";
| @reflectSerde enum t = "str";
|
| 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", "t", "gm", "gc"]);
| static assert(SerializableMembers!(const S) == ["y", "f", "d", "t", "gc"]);
|}
|
|/++
|Returns: list of the deserializable (public setters) members.
|+/
|enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), SerdeFieldsAndProperties!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 SerdeFieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(SerdeFieldsAndPropertiesImpl!T)));
|
|
|private static immutable exlMembers = [
| "opAssign",
| "opCast",
| "opCmp",
| "opEquals",
| "opPostMove",
| "toHash",
| "toString",
| "trustedGet",
| "deserializeFromAsdf",
| "deserializeFromIon",
|];
|
|private auto filterMembers(string[] members)
|{
0000000| string[] ret;
|
0000000| L: foreach(member; members)
| {
0000000| if (member.length >= 2 && (member[0 .. 2] == "__" || member[$ - 2 .. $] == "__"))
0000000| continue;
0000000| foreach(exlMember; exlMembers)
0000000| if (exlMember == member)
0000000| continue L;
0000000| ret ~= member;
| }
0000000| return ret;
|};
|
|private template allMembers(T)
|{
| import std.meta: aliasSeqOf;
| static if (isAggregateType!T)
| alias allMembers = aliasSeqOf!(filterMembers([__traits(allMembers, T)]));
| else
| alias allMembers = AliasSeq!();
|}
|
|private template SerdeFieldsAndPropertiesImpl(T)
|{
| alias isProperty = ApplyLeft!(.isProperty, T);
| alias hasField = ApplyLeft!(.hasField, T);
| alias isOriginalMember = ApplyLeft!(.isOriginalMember, T);
| T* aggregate;
| template hasReflectSerde(string member)
| {
| static if (is(typeof(__traits(getMember, *aggregate, member))))
| enum hasReflectSerde = hasUDA!(T, member, reflectSerde);
| else
| enum hasReflectSerde = false;
| }
| alias isMember = templateAnd!(templateOr!(hasField, isProperty, hasReflectSerde), isOriginalMember);
| static if (!is(immutable T == Tuple!Types, Types...) && __traits(getAliasThis, T).length)
| {
| alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
| static if (isAggregateType!T)
| alias baseMembers = SerdeFieldsAndPropertiesImpl!A;
| else
| alias baseMembers = AliasSeq!();
| alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
| alias SerdeFieldsAndPropertiesImpl = 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 SerdeFieldsAndPropertiesImpl = 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.3.6/mir-core/source/mir/reflection.d is 0% covered
<<<<<< EOF
# path=..-..-..-.dub-packages-mir-core-1.3.6-mir-core-source-mir-utility.lst
|/++
|Generic utilities.
|
|$(BOOKTABLE Cheat Sheet,
|$(TR $(TH Function Name) $(TH Description))
|$(T2 swap, Swaps two values.)
|$(T2 extMul, Extended unsigned multiplications.)
|$(T2 min, Minimum value.)
|$(T2 max, Maximum value.)
|)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki, $(HTTP erdani.com, Andrei Alexandrescu) (original std.* modules),
|Macros:
|T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
|+/
|module mir.utility;
|
|import std.traits;
|
|import mir.math.common: optmath;
|
|version(LDC)
|pragma(LDC_inline_ir) R inlineIR(string s, R, P...)(P) @safe pure nothrow @nogc;
|
|@optmath:
|
|version(LDC)
|{
| ///
| public import ldc.intrinsics: _expect = llvm_expect;
|}
|else version(GNU)
|{
| import gcc.builtins: __builtin_expect, __builtin_clong;
|
| ///
| T _expect(T)(in T val, in T expected_val) if (__traits(isIntegral, T))
| {
| static if (T.sizeof <= __builtin_clong.sizeof)
| return cast(T) __builtin_expect(val, expected_val);
| else
| return val;
| }
|}
|else
|{
| ///
| T _expect(T)(in T val, in T expected_val) if (__traits(isIntegral, T))
| {
| return val;
| }
|}
|
|public import std.algorithm.mutation: swap;
|
|void swapStars(I1, I2)(auto ref I1 i1, auto ref I2 i2)
|{
| static if (__traits(compiles, swap(*i1, *i2)))
| {
| swap(*i1, *i2);
| }
| else
| {
| import mir.functional: unref;
| auto e = unref(*i1);
| i1[0] = *i2;
| i2[0] = e;
| }
|}
|
|/++
|Iterates the passed arguments and returns the minimum value.
|Params: args = The values to select the minimum from. At least two arguments
| must be passed, and they must be comparable with `<`.
|Returns: The minimum of the passed-in values.
|+/
|auto min(T...)(T args)
| if (T.length >= 2)
|{
| //Get "a"
| static if (T.length <= 2)
| alias a = args[0];
| else
| auto a = min(args[0 .. ($+1)/2]);
| alias T0 = typeof(a);
|
| //Get "b"
| static if (T.length <= 3)
| alias b = args[$-1];
| else
| auto b = min(args[($+1)/2 .. $]);
| alias T1 = typeof(b);
|
| static assert (is(typeof(a < b)), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ ".");
|
| static if ((isFloatingPoint!T0 && isNumeric!T1) || (isFloatingPoint!T1 && isNumeric!T0))
| {
| import mir.math.common: fmin;
| return fmin(a, b);
| }
| else
| {
| static if (isIntegral!T0 && isIntegral!T1)
| static assert(isSigned!T0 == isSigned!T1,
| "mir.utility.min is not defined for signed + unsigned pairs because of security reasons."
| ~ "Please unify type or use a Phobos analog.");
| //Do the "min" proper with a and b
| return a < b ? a : b;
| }
|}
|
|@safe version(mir_core_test) unittest
|{
| int a = 5;
| short b = 6;
| double c = 2;
| auto d = min(a, b);
| static assert(is(typeof(d) == int));
| assert(d == 5);
| auto e = min(a, b, c);
| static assert(is(typeof(e) == double));
| assert(e == 2);
|}
|
|/++
|`min` is not defined for arguments of mixed signedness because of security reasons.
|Please unify type or use a Phobos analog.
|+/
|version(mir_core_test) unittest
|{
| int a = -10;
| uint b = 10;
| static assert(!is(typeof(min(a, b))));
|}
|
|
|/++
|Iterates the passed arguments and returns the minimum value.
|Params: args = The values to select the minimum from. At least two arguments
| must be passed, and they must be comparable with `<`.
|Returns: The minimum of the passed-in values.
|+/
|auto max(T...)(T args)
| if (T.length >= 2)
|{
| //Get "a"
| static if (T.length <= 2)
| alias a = args[0];
| else
| auto a = max(args[0 .. ($+1)/2]);
| alias T0 = typeof(a);
|
| //Get "b"
| static if (T.length <= 3)
| alias b = args[$-1];
| else
| auto b = max(args[($+1)/2 .. $]);
| alias T1 = typeof(b);
|
| static assert (is(typeof(a < b)), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ ".");
|
| static if ((isFloatingPoint!T0 && isNumeric!T1) || (isFloatingPoint!T1 && isNumeric!T0))
| {
| import mir.math.common: fmax;
| return fmax(a, b);
| }
| else
| {
| static if (isIntegral!T0 && isIntegral!T1)
| static assert(isSigned!T0 == isSigned!T1,
| "mir.utility.max is not defined for signed + unsigned pairs because of security reasons."
| ~ "Please unify type or use a Phobos analog.");
| //Do the "max" proper with a and b
| return a > b ? a : b;
| }
|}
|
|///
|@safe version(mir_core_test) unittest
|{
| int a = 5;
| short b = 6;
| double c = 2;
| auto d = max(a, b);
| static assert(is(typeof(d) == int));
| assert(d == 6);
| auto e = min(a, b, c);
| static assert(is(typeof(e) == double));
| assert(e == 2);
|}
|
|/++
|`max` is not defined for arguments of mixed signedness because of security reasons.
|Please unify type or use a Phobos analog.
|+/
|version(mir_core_test) unittest
|{
| int a = -10;
| uint b = 10;
| static assert(!is(typeof(max(a, b))));
|}
|
|/++
|Return type for $(LREF extMul);
|
|The payload order of `low` and `high` parts depends on the endianness.
|+/
|struct ExtMulResult(I)
| if (isUnsigned!I)
|{
| version (LittleEndian)
| {
| /// Lower I.sizeof * 8 bits
| I low;
| /// Higher I.sizeof * 8 bits
| I high;
| }
| else
| {
| /// Higher I.sizeof * 8 bits
| I high;
| /// Lower I.sizeof * 8 bits
| I low;
| }
|
| T opCast(T : ulong)()
| {
| static if (is(I == ulong))
| {
| return cast(T)low;
| }
| else
| {
| return cast(T)(low | (ulong(high) << (I.sizeof * 8)));
| }
| }
|}
|
|private struct ExtDivResult(I)
| if (isUnsigned!I)
|{
| version (LittleEndian)
| {
| /// Quotient
| I quotient;
| /// Remainder
| I remainder;
| }
| else
| {
| /// Remainder
| I remainder;
| /// Quotient
| I quotient;
| }
|}
|
|/++
|Extended unsigned multiplications.
|Performs U x U multiplication and returns $(LREF ExtMulResult)!U that contains extended result.
|Params:
| a = unsigned integer
| b = unsigned integer
|Returns:
| 128bit result if U is ulong or 256bit result if U is ucent.
|Optimization:
| Algorithm is optimized for LDC (LLVM IR, any target) and for DMD (X86_64).
|+/
|ExtMulResult!U extMul(U)(in U a, in U b) @nogc nothrow pure @trusted
| if(isUnsigned!U)
|{
| static if (is(U == ulong))
| alias H = uint;
| else // ucent
| alias H = ulong;
|
| enum hbc = H.sizeof * 8;
|
| static if (U.sizeof < 4)
| {
| auto ret = uint(a) * b;
| version (LittleEndian)
| return typeof(return)(cast(U) ret, cast(U)(ret >>> (U.sizeof * 8)));
| else
| return typeof(return)(cast(U)(ret >>> (U.sizeof * 8)), cast(U) ret);
| }
| else
| static if (is(U == uint))
| {
| auto ret = ulong(a) * b;
| version (LittleEndian)
| return typeof(return)(cast(uint) ret, cast(uint)(ret >>> 32));
| else
| return typeof(return)(cast(uint)(ret >>> 32), cast(uint) ret);
| }
| else
| static if (is(U == ulong) && __traits(compiles, ucent.init))
| {
| auto ret = ucent(a) * b;
| version (LittleEndian)
| return typeof(return)(cast(ulong) ret, cast(ulong)(ret >>> 64));
| else
| return typeof(return)(cast(ulong)(ret >>> 64), cast(ulong) ret);
| }
| else
| {
| if (!__ctfe)
| {
| static if (size_t.sizeof == 4)
| {
| // https://github.com/ldc-developers/ldc/issues/2391
| }
| else
| version(LDC)
| {
| // LLVM IR by n8sh
| pragma(inline, true);
| static if (is(U == ulong))
| {
| auto r = inlineIR!(`
| %a = zext i64 %0 to i128
| %b = zext i64 %1 to i128
| %m = mul i128 %a, %b
| %n = lshr i128 %m, 64
| %h = trunc i128 %n to i64
| %l = trunc i128 %m to i64
| %agg1 = insertvalue [2 x i64] undef, i64 %l, 0
| %agg2 = insertvalue [2 x i64] %agg1, i64 %h, 1
| ret [2 x i64] %agg2`, ulong[2])(a, b);
| version (LittleEndian)
| return ExtMulResult!U(r[0], r[1]);
| else
| return ExtMulResult!U(r[1], r[0]);
| }
| else
| static if (false)
| {
| auto r = inlineIR!(`
| %a = zext i128 %0 to i256
| %b = zext i128 %1 to i256
| %m = mul i256 %a, %b
| %n = lshr i256 %m, 128
| %h = trunc i256 %n to i128
| %l = trunc i256 %m to i128
| %agg1 = insertvalue [2 x i128] undef, i128 %l, 0
| %agg2 = insertvalue [2 x i128] %agg1, i128 %h, 1
| ret [2 x i128] %agg2`, ucent[2])(a, b);
| version (LittleEndian)
| return ExtMulResult!U(r[0], r[1]);
| else
| return ExtMulResult!U(r[1], r[0]);
| }
| }
| else
| version(D_InlineAsm_X86_64)
| {
| static if (is(U == ulong))
| {
| return extMul_X86_64(a, b);
| }
| }
| }
|
| U al = cast(H)a;
| U ah = a >>> hbc;
| U bl = cast(H)b;
| U bh = b >>> hbc;
|
| U p0 = al * bl;
| U p1 = al * bh;
| U p2 = ah * bl;
| U p3 = ah * bh;
|
| H cy = cast(H)(((p0 >>> hbc) + cast(H)p1 + cast(H)p2) >>> hbc);
| U lo = p0 + (p1 << hbc) + (p2 << hbc);
| U hi = p3 + (p1 >>> hbc) + (p2 >>> hbc) + cy;
|
| version(LittleEndian)
| return typeof(return)(lo, hi);
| else
| return typeof(return)(hi, lo);
| }
|}
|
|/// 64bit x 64bit -> 128bit
|version(mir_core_test) unittest
|{
| immutable a = 0x93_8d_28_00_0f_50_a5_56;
| immutable b = 0x54_c3_2f_e8_cc_a5_97_10;
| enum c = extMul(a, b); // Compile time algorithm
| assert(extMul(a, b) == c); // Fast runtime algorithm
| static assert(c.high == 0x30_da_d1_42_95_4a_50_78);
| static assert(c.low == 0x27_9b_4b_b4_9e_fe_0f_60);
|}
|
|/// 32bit x 32bit -> 64bit
|version(mir_core_test) unittest
|{
| immutable a = 0x0f_50_a5_56;
| immutable b = 0xcc_a5_97_10;
| static assert(cast(ulong)extMul(a, b) == ulong(a) * b);
|}
|
|///
|version(mir_core_test) unittest
|{
| immutable ushort a = 0xa5_56;
| immutable ushort b = 0x97_10;
| static assert(cast(uint)extMul(a, b) == a * b);
|}
|
|///
|version(mir_core_test) unittest
|{
| immutable ubyte a = 0x56;
| immutable ubyte b = 0x10;
| static assert(cast(ushort)extMul(a, b) == a * b);
|}
|
|version(D_InlineAsm_X86_64)
|{
| version(Windows)
| {
| private ulong[2] extMul_X86_64_impl()(ulong a, ulong b)
| {
| asm @safe pure nothrow @nogc
| {
| naked;
| mov RAX, RCX;
| mul RDX;
| ret;
| }
| }
|
| private ExtMulResult!ulong extMul_X86_64()(ulong a, ulong b)
| {
| auto res = extMul_X86_64_impl(a, b);
| return ExtMulResult!ulong(res[0], res[1]);
| }
| }
| else
| private ExtMulResult!ulong extMul_X86_64()(ulong a, ulong b)
| {
| asm @safe pure nothrow @nogc
| {
| naked;
| mov RAX, RDI;
| mul RSI;
| ret;
| }
| }
|
| version(Windows)
| {
| private ulong[2] extDiv_X86_64_impl()(ulong high, ulong low, ulong d)
| {
| asm @safe pure nothrow @nogc
| {
| naked;
| mov RAX, RCX;
| div RDX;
| ret;
| }
| }
|
| private ExtDivResult!ulong extDiv_X86_64()(ExtMulResult!ulong pair, ulong d)
| {
| auto res = extDiv_X86_64_impl(pair.high, pair.low);
| return ExtDivResult!ulong(res[0], res[1]);
| }
| }
| else
| private ExtDivResult!ulong extDiv_X86_64()(ExtMulResult!ulong pair, ulong d)
| {
| asm @safe pure nothrow @nogc
| {
| naked;
| mov RAX, RDI;
| div RSI;
| ret;
| }
| }
|}
|
|version(LDC) {} else version(D_InlineAsm_X86_64)
|@nogc nothrow pure @safe version(mir_core_test) unittest
|{
| immutable a = 0x93_8d_28_00_0f_50_a5_56;
| immutable b = 0x54_c3_2f_e8_cc_a5_97_10;
|
| immutable ExtMulResult!ulong c = extMul_X86_64(a, b);
|
| assert(c.high == 0x30_da_d1_42_95_4a_50_78);
| assert(c.low == 0x27_9b_4b_b4_9e_fe_0f_60);
|}
|
|// draft
|// https://www.codeproject.com/Tips/785014/UInt-Division-Modulus
|private ulong divmod128by64(const ulong u1, const ulong u0, ulong v, out ulong r)
|{
0000000| const ulong b = 1L << 32;
0000000| ulong un1, un0, vn1, vn0, q1, q0, un32, un21, un10, rhat, left, right;
|
| import mir.bitop;
|
0000000| auto s = ctlz(v);
0000000| v <<= s;
0000000| vn1 = v >> 32;
0000000| vn0 = v & 0xffffffff;
|
0000000| un32 = (u1 << s) | (u0 >> (64 - s));
0000000| un10 = u0 << s;
|
0000000| un1 = un10 >> 32;
0000000| un0 = un10 & 0xffffffff;
|
0000000| q1 = un32 / vn1;
0000000| rhat = un32 % vn1;
|
0000000| left = q1 * vn0;
0000000| right = (rhat << 32) + un1;
|
0000000| while ((q1 >= b) || (left > right))
| {
0000000| --q1;
0000000| rhat += vn1;
0000000| if (rhat >= b)
0000000| break;
0000000| left -= vn0;
0000000| right = (rhat << 32) | un1;
| }
|
0000000| un21 = (un32 << 32) + (un1 - (q1 * v));
|
0000000| q0 = un21 / vn1;
0000000| rhat = un21 % vn1;
|
0000000| left = q0 * vn0;
0000000| right = (rhat << 32) | un0;
|
0000000| while ((q0 >= b) || (left > right))
| {
0000000| --q0;
0000000| rhat += vn1;
0000000| if (rhat >= b)
0000000| break;
0000000| left -= vn0;
0000000| right = (rhat << 32) | un0;
| }
|
0000000| r = ((un21 << 32) + (un0 - (q0 * v))) >> s;
0000000| return (q1 << 32) | q0;
|}
|
|/++
|Simple sort algorithm usefull for CTFE code.
|+/
|template simpleSort(alias cmp = "a < b")
|{
| ///
| T[] simpleSort(T)(return T[] array)
| {
| size_t i = 1;
| while (i < array.length)
| {
| size_t j = i;
| import mir.functional: naryFun;
| while (j > 0 && !naryFun!cmp(array[j - 1], array[j]))
| {
| swap(array[j - 1], array[j]);
| j--;
| }
| i++;
| }
| return array;
| }
|}
../../../.dub/packages/mir-core-1.3.6/mir-core/source/mir/utility.d is 0% covered
<<<<<< EOF
# path=.dub-code-mir-algorithm-test-default-unittest-cov-linux.posix-x86-dmd_v2.100.2-beta.1-F747A2D2B2FAE6DA964CC7BEBF13B66D_dub_test_root.lst
|module dub_test_root;
|import std.typetuple;
|static import mir.algebraic_alias.ion;
|static import mir.algebraic_alias.json;
|static import mir.algebraic_alias.transform;
|static import mir.algorithm.iteration;
|static import mir.algorithm.setops;
|static import mir.annotated;
|static import mir.appender;
|static import mir.array.allocation;
|static import mir.base64;
|static import mir.bignum.decimal;
|static import mir.bignum.fixed;
|static import mir.bignum.fp;
|static import mir.bignum.integer;
|static import mir.bignum.internal.dec2float;
|static import mir.bignum.internal.dec2float_table;
|static import mir.bignum.internal.kernel;
|static import mir.bignum.internal.parse;
|static import mir.bignum.internal.phobos_kernel;
|static import mir.bignum.internal.ryu.generic_128;
|static import mir.bignum.internal.ryu.table;
|static import mir.bignum.low_level_view;
|static import mir.container.binaryheap;
|static import mir.cpp_export.numeric;
|static import mir.date;
|static import mir.ediff;
|static import mir.format;
|static import mir.format_impl;
|static import mir.graph.tarjan;
|static import mir.interpolate.constant;
|static import mir.interpolate.extrapolate;
|static import mir.interpolate.generic;
|static import mir.interpolate.linear;
|static import mir.interpolate.mod;
|static import mir.interpolate.polynomial;
|static import mir.interpolate.spline;
|static import mir.interpolate.utility;
|static import mir.lob;
|static import mir.math.func.expdigamma;
|static import mir.math.func.hermite;
|static import mir.math.func.normal;
|static import mir.math.numeric;
|static import mir.math.stat;
|static import mir.math.sum;
|static import mir.ndslice.allocation;
|static import mir.ndslice.chunks;
|static import mir.ndslice.concatenation;
|static import mir.ndslice.connect.cpython;
|static import mir.ndslice.dynamic;
|static import mir.ndslice.field;
|static import mir.ndslice.filling;
|static import mir.ndslice.fuse;
|static import mir.ndslice.internal;
|static import mir.ndslice.iterator;
|static import mir.ndslice.mutation;
|static import mir.ndslice.ndfield;
|static import mir.ndslice.slice;
|static import mir.ndslice.sorting;
|static import mir.ndslice.topology;
|static import mir.ndslice.traits;
|static import mir.numeric;
|static import mir.parse;
|static import mir.polynomial;
|static import mir.range;
|static import mir.rc.array;
|static import mir.rc.context;
|static import mir.rc.ptr;
|static import mir.rc.slim_ptr;
|static import mir.serde;
|static import mir.series;
|static import mir.small_array;
|static import mir.small_string;
|static import mir.stdio;
|static import mir.string;
|static import mir.string_map;
|static import mir.test;
|static import mir.timestamp;
|static import mir.type_info;
|alias allModules = TypeTuple!(mir.algebraic_alias.ion, mir.algebraic_alias.json, mir.algebraic_alias.transform, mir.algorithm.iteration, mir.algorithm.setops, mir.annotated, mir.appender, mir.array.allocation, mir.base64, mir.bignum.decimal, mir.bignum.fixed, mir.bignum.fp, mir.bignum.integer, mir.bignum.internal.dec2float, mir.bignum.internal.dec2float_table, mir.bignum.internal.kernel, mir.bignum.internal.parse, mir.bignum.internal.phobos_kernel, mir.bignum.internal.ryu.generic_128, mir.bignum.internal.ryu.table, mir.bignum.low_level_view, mir.container.binaryheap, mir.cpp_export.numeric, mir.date, mir.ediff, mir.format, mir.format_impl, mir.graph.tarjan, mir.interpolate.constant, mir.interpolate.extrapolate, mir.interpolate.generic, mir.interpolate.linear, mir.interpolate.mod, mir.interpolate.polynomial, mir.interpolate.spline, mir.interpolate.utility, mir.lob, mir.math.func.expdigamma, mir.math.func.hermite, mir.math.func.normal, mir.math.numeric, mir.math.stat, mir.math.sum, mir.ndslice.allocation, mir.ndslice.chunks, mir.ndslice.concatenation, mir.ndslice.connect.cpython, mir.ndslice.dynamic, mir.ndslice.field, mir.ndslice.filling, mir.ndslice.fuse, mir.ndslice.internal, mir.ndslice.iterator, mir.ndslice.mutation, mir.ndslice.ndfield, mir.ndslice.slice, mir.ndslice.sorting, mir.ndslice.topology, mir.ndslice.traits, mir.numeric, mir.parse, mir.polynomial, mir.range, mir.rc.array, mir.rc.context, mir.rc.ptr, mir.rc.slim_ptr, mir.serde, mir.series, mir.small_array, mir.small_string, mir.stdio, mir.string, mir.string_map, mir.test, mir.timestamp, mir.type_info);
|
| import std.stdio;
| import core.runtime;
|
0000000| void main() { writeln("All unit tests have been run successfully."); }
| shared static this() {
| version (Have_tested) {
| import tested;
| import core.runtime;
| import std.exception;
| Runtime.moduleUnitTester = () => true;
| enforce(runUnitTests!allModules(new ConsoleTestResultWriter), "Unit tests failed.");
| }
| }
|
.dub/code/mir-algorithm-test-default-unittest-cov-linux.posix-x86-dmd_v2.100.2-beta.1-F747A2D2B2FAE6DA964CC7BEBF13B66D_dub_test_root.d is 0% covered
<<<<<< EOF
# path=source-mir-algebraic_alias-ion.lst
|/++
|$(H1 Mutable Ion value)
|
|This module contains a single alias definition and doesn't provide Ion serialization API.
|
|See_also: Ion library $(MIR_PACKAGE mir-ion)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki
|Macros:
|+/
|module mir.algebraic_alias.ion;
|
|import mir.algebraic: Algebraic, This;
|///
|public import mir.annotated: Annotated;
|///
|public import mir.lob: Clob, Blob;
|///
|public import mir.string_map: StringMap;
|///
|public import mir.timestamp: Timestamp;
|
|
|/++
|Definition union for $(LREF IonAlgebraic).
|+/
|union Ion_
|{
| ///
| typeof(null) null_;
| ///
| bool boolean;
| ///
| long integer;
| ///
| double float_;
| ///
| immutable(char)[] string;
| ///
| Blob blob;
| ///
| Clob clob;
| ///
| Timestamp timestamp;
| /// Self alias in array.
| This[] array;
| /// Self alias in $(MREF mir,string_map).
| StringMap!This object;
| /// Self alias in $(MREF mir,annotated).
| Annotated!This annotated;
|}
|
|/++
|Ion 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 IonAlgebraic = Algebraic!Ion_;
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.topology: map;
| import mir.array.allocation: array;
|
1| IonAlgebraic value;
|
1| StringMap!IonAlgebraic object;
|
| // Default
1| assert(value.isNull);
1| assert(value.kind == IonAlgebraic.Kind.null_);
|
| // Boolean
1| value = object["bool"] = true;
1| assert(!value.isNull);
1| assert(value == true);
1| assert(value.kind == IonAlgebraic.Kind.boolean);
| // access
1| assert(value.boolean == true);
1| assert(value.get!bool == true);
1| assert(value.get!"boolean" == true);
1| assert(value.get!(IonAlgebraic.Kind.boolean) == true);
| // nothrow access
1| assert(value.trustedGet!bool == true);
1| assert(value.trustedGet!"boolean" == true);
1| assert(value.trustedGet!(IonAlgebraic.Kind.boolean) == true);
| // checks
1| assert(!value._is!string);
1| assert(value._is!bool);
1| assert(value._is!"boolean");
1| assert(value._is!(IonAlgebraic.Kind.boolean));
|
| // Null
1| value = object["null"] = null;
1| assert(value.isNull);
1| assert(value == null);
1| assert(value.kind == IonAlgebraic.Kind.null_);
| // access
1| assert(value.null_ == null);
1| assert(value.get!(typeof(null)) == null);
1| assert(value.get!(IonAlgebraic.Kind.null_) == null);
|
| // String
1| value = object["string"] = "s";
1| assert(value.kind == IonAlgebraic.Kind.string);
1| assert(value == "s");
| // access
| // Yep, `string` here is an alias to `get!(immutable(char)[])` method
1| assert(value.string == "s");
| // `string` here is an alias of type `immutable(char)[]`
1| assert(value.get!string == "s");
1| assert(value.get!"string" == "s");
| // finally, `string` here is an enum meber
1| assert(value.get!(IonAlgebraic.Kind.string) == "s");
|
| // Integer
1| value = object["integer"] = 4;
1| assert(value.kind == IonAlgebraic.Kind.integer);
1| assert(value == 4);
1| assert(value != 4.0);
1| assert(value.integer == 4);
|
| // Float
1| value = object["float"] = 3.0;
1| assert(value.kind == IonAlgebraic.Kind.float_);
1| assert(value != 3);
1| assert(value == 3.0);
1| assert(value.float_ == 3.0);
|
| // Array
1| IonAlgebraic[] arr = [0, 1, 2, 3, 4].map!IonAlgebraic.array;
|
1| value = object["array"] = arr;
1| assert(value.kind == IonAlgebraic.Kind.array);
1| assert(value == arr);
1| assert(value.array[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 == IonAlgebraic.Kind.object);
1| assert(value.object.keys is object.keys);
|
1| IonAlgebraic[string] aa = object.toAA;
1| object = aa.StringMap!IonAlgebraic;
|
1| IonAlgebraic fromAA = ["a" : IonAlgebraic(3), "b" : IonAlgebraic("b")];
1| assert(fromAA.object["a"] == 3);
1| assert(fromAA.object["b"] == "b");
|
1| auto annotated = Annotated!IonAlgebraic(["birthday"], Timestamp("2001-01-01"));
1| value = annotated;
1| assert(value == annotated);
1| value = annotated.IonAlgebraic;
1| assert(value == annotated);
|}
source/mir/algebraic_alias/ion.d is 100% 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: Ilia Ki
|Macros:
|+/
|module mir.algebraic_alias.json;
|
|import mir.algebraic: Algebraic, This;
|///
|public import mir.string_map: StringMap;
|
|/++
|Definition union for $(LREF JsonAlgebraic).
|+/
|union Json_
|{
| ///
| 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 = Algebraic!Json_;
|
|///
|version(mir_test)
|@safe pure
|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);
| // access
1| assert(value.boolean == true);
1| assert(value.get!bool == true);
1| assert(value.get!"boolean" == true);
1| assert(value.get!(JsonAlgebraic.Kind.boolean) == true);
| // nothrow access
1| assert(value.trustedGet!bool == true);
1| assert(value.trustedGet!"boolean" == true);
1| assert(value.trustedGet!(JsonAlgebraic.Kind.boolean) == true);
| // checks
1| assert(!value._is!string);
1| assert(value._is!bool);
1| assert(value._is!"boolean");
1| assert(value._is!(JsonAlgebraic.Kind.boolean));
|
| // Null
1| value = object["null"] = null;
1| assert(value.isNull);
1| assert(value == null);
1| assert(value.kind == JsonAlgebraic.Kind.null_);
| // access
1| assert(value.null_ == 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");
| // access
| // Yep, `string` here is an alias to `get!(immutable(char)[])` method
1| assert(value.string == "s");
| // `string` here is an alias of type `immutable(char)[]`
1| assert(value.get!string == "s");
1| assert(value.get!"string" == "s");
| // finally, `string` here is an enum meber
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.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.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.array[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.object.keys is object.keys);
|
1| JsonAlgebraic[string] aa = object.toAA;
1| object = aa.StringMap!JsonAlgebraic;
|
1| JsonAlgebraic fromAA = ["a" : JsonAlgebraic(3), "b" : JsonAlgebraic("b")];
1| assert(fromAA.object["a"] == 3);
1| assert(fromAA.object["b"] == "b");
|}
source/mir/algebraic_alias/json.d is 100% covered
<<<<<< EOF
# path=source-mir-algebraic_alias-transform.lst
|/++
|$(H1 Transformation utilities for JSON-like values)
|
|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: Ilia Ki
|Macros:
|+/
|module mir.algebraic_alias.transform;
|
|import mir.algebraic: Algebraic, tryVisit, visit, optionalVisit;
|import mir.functional: naryFun;
|private alias AliasSeq(T...) = T;
|
|/++
|Transforms algebraics leafs recursively in place,
|ensuring that all leaf types are handled by the visiting functions.
|
|Recursion is done for `This[]`, `StringMap!This`, `This[string]`, and `Annotated!This` types.
|+/
|alias transformLeafs(visitors...) = transformLeafsImpl!(visit, naryFun!visitors);
|
|///
|version(mir_test)
|unittest
|{
| import mir.format: text;
| import mir.algebraic_alias.json;
1| JsonAlgebraic value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, null.JsonAlgebraic].JsonAlgebraic];
|
| // converts all leavs to a text form
1| value.transformLeafs!text;
1| assert(value == ["key" : ["str".JsonAlgebraic, "2.32".JsonAlgebraic, "null".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
|
1| value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, true.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic;
|
| /// converts only bool values
1| value.transformLeafs!(
1| (bool b) => b.text,
2| v => v, // other values are copied as is
| );
|
1| assert(value == ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, "true".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
|}
|
|/++
|Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|
|Throws: Exception if `naryFun!visitors` can't be called with provided arguments
|+/
|alias tryTransformLeafs(visitors...) = transformLeafsImpl!(tryVisit, naryFun!visitors);
|
|///
|version(mir_test)
|unittest
|{
| import mir.format: text;
| import mir.algebraic_alias.json;
1| JsonAlgebraic value = ["key" : [true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic];
|
| // converts long and double numbers to a text form, bool values is converted to `long`
4| value.tryTransformLeafs!((double v) => v.text, (long v) => v.text);
1| assert(value == ["key" : ["1".JsonAlgebraic, "100".JsonAlgebraic, "2.32".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
|}
|
|/++
|Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions.
|
|The function ignores leafs that can't be handled by the visiting functions.
|+/
|alias optionalTransformLeafs(visitors...) = transformLeafsImpl!(optionalVisit, naryFun!visitors);
|
|///
|version(mir_test)
|unittest
|{
| import mir.format: text;
| import mir.algebraic_alias.json;
1| JsonAlgebraic value = ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic];
|
| // converts long numbers to a text form, ignores other types
1| value.optionalTransformLeafs!(
1| (long v) => v.text,
1| (bool b) => b, // needs special overload for bool to get rid of implicit converion to long/double
| );
1| assert(value == ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, "100".JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
|}
|
|///
|template transformLeafsImpl(alias handler, alias visitor)
|{
| ///
| ref Algebraic!Types transformLeafsImpl(Types...)(ref return Algebraic!Types value)
| {
| import core.lifetime: move;
| import mir.algebraic: visit;
| import mir.annotated: Annotated;
| import mir.string_map: StringMap;
| alias T = Algebraic!Types;
| static if (is(T.AllowedTypes[0] == typeof(null)))
| {
| enum nullCompiles = __traits(compiles, value = visitor(null));
| static if (nullCompiles || __traits(isSame, handler, visit))
| {
| alias nullHandler = (typeof(null)) {
| static if (nullCompiles)
1| value = visitor(null);
| else
| assert(0, "Null " ~ T.stringof);
| };
| }
| else
| {
| alias nullHandler = AliasSeq!();
| }
| }
| else
| {
| alias nullHandler = AliasSeq!();
| }
21| handler!(
| (T[] v) {
51| foreach (ref e; v)
13| transformLeafsImpl(e);
| },
| (StringMap!T v) {
24| foreach (ref e; v.values)
4| transformLeafsImpl(e);
| },
| (T[string] v) {
| foreach (key, ref e; v)
| transformLeafsImpl(e);
| },
| (Annotated!T v) {
| transformLeafsImpl(v.value);
| },
| nullHandler,
| (ref v) { // auto for typeof(null) support
| static if (__traits(compiles, value = visitor(move(v))))
10| value = visitor(move(v));
| else
| value = visitor(v);
| }
| )(value);
21| return value;
| }
|
| /// ditto
| Algebraic!Types transformLeafsImpl(Types...)(Algebraic!Types value)
| {
| import core.lifetime: move;
| transformLeafsImpl(value);
| return move(value);
| }
|}
source/mir/algebraic_alias/transform.d is 100% covered
<<<<<< EOF
# path=source-mir-algorithm-iteration.lst
|// Written in the D programming language.
|/**
|This module contains generic _iteration algorithms.
|$(SCRIPT inhibitQuickIndex = 1;)
|
|$(BOOKTABLE $(H2 Function),
|$(TR $(TH Function Name) $(TH Description))
|$(T2 all, Checks if all elements satisfy to a predicate.)
|$(T2 any, Checks if at least one element satisfy to a predicate.)
|$(T2 cmp, Compares two slices.)
|$(T2 count, Counts elements in a slices according to a predicate.)
|$(T2 each, Iterates elements.)
|$(T2 eachLower, Iterates lower triangle of matrix.)
|$(T2 eachOnBorder, Iterates elementes on tensors borders and corners.)
|$(T2 eachUploPair, Iterates upper and lower pairs of elements in square matrix.)
|$(T2 eachUpper, Iterates upper triangle of matrix.)
|$(T2 equal, Compares two slices for equality.)
|$(T2 filter, Filters elements in a range or an ndslice.)
|$(T2 find, Finds backward index.)
|$(T2 findIndex, Finds index.)
|$(T2 fold, Accumulates all elements (different parameter order than `reduce`).)
|$(T2 isSymmetric, Checks if the matrix is symmetric.)
|$(T2 maxIndex, Finds index of the maximum.)
|$(T2 maxPos, Finds backward index of the maximum.)
|$(T2 minIndex, Finds index of the minimum.)
|$(T2 minmaxIndex, Finds indices of the minimum and the maximum.)
|$(T2 minmaxPos, Finds backward indices of the minimum and the maximum.)
|$(T2 minPos, Finds backward index of the minimum.)
|$(T2 nBitsToCount, Сount bits until set bit count is reached.)
|$(T2 reduce, Accumulates all elements.)
|$(T2 Chequer, Chequer color selector to work with $(LREF each) .)
|$(T2 uniq, Iterates over the unique elements in a range or an ndslice, which is assumed sorted.)
|)
|
|Transform function is represented by $(NDSLICEREF topology, map).
|
|All operators are suitable to change slices using `ref` argument qualification in a function declaration.
|Note, that string lambdas in Mir are `auto ref` functions.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
|Authors: Ilia Ki, John Michael Hall, Andrei Alexandrescu (original Phobos code)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
|
|Authors: , Ilia Ki (Mir & BetterC rework).
|Source: $(PHOBOSSRC std/algorithm/_iteration.d)
|Macros:
| NDSLICEREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
| T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
| */
|module mir.algorithm.iteration;
|
|import mir.functional: naryFun;
|import mir.internal.utility;
|import mir.math.common: optmath;
|import mir.ndslice.field: BitField;
|import mir.ndslice.internal;
|import mir.ndslice.iterator: FieldIterator, RetroIterator;
|import mir.ndslice.slice;
|import mir.primitives;
|import mir.qualifier;
|import std.meta;
|import std.traits;
|
|/++
|Chequer color selector to work with $(LREF each)
|+/
|enum Chequer : bool
|{
| /// Main diagonal color
| black,
| /// First sub-diagonal color
| red,
|}
|
|///
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
1| auto s = [5, 4].slice!int;
|
1| Chequer.black.each!"a = 1"(s);
1| assert(s == [
| [1, 0, 1, 0],
| [0, 1, 0, 1],
| [1, 0, 1, 0],
| [0, 1, 0, 1],
| [1, 0, 1, 0],
| ]);
|
11| Chequer.red.each!((ref b) => b = 2)(s);
1| assert(s == [
| [1, 2, 1, 2],
| [2, 1, 2, 1],
| [1, 2, 1, 2],
| [2, 1, 2, 1],
| [1, 2, 1, 2],
| ]);
|
|}
|
|@optmath:
|
|/+
|Bitslice representation for accelerated bitwise algorithm.
|1-dimensional contiguousitslice can be split into three chunks: head bits, body chunks, and tail bits.
|
|Bitslice can have head bits because it has slicing and the zero bit may not be aligned to the zero of a body chunk.
|+/
|private struct BitSliceAccelerator(Field, I = typeof(Field.init[size_t.init]))
| if (__traits(isUnsigned, I))
|{
| import mir.bitop;
| import mir.qualifier: lightConst;
| import mir.ndslice.traits: isIterator;
| import mir.ndslice.iterator: FieldIterator;
| import mir.ndslice.field: BitField;
|
| ///
| alias U = typeof(I + 1u);
| /// body bits chunks
| static if (isIterator!Field)
| Slice!Field bodyChunks;
| else
| Slice!(FieldIterator!Field) bodyChunks;
| /// head length
| int headLength;
| /// tail length
| int tailLength;
|
|@optmath:
|
9| this(Slice!(FieldIterator!(BitField!(Field, I))) slice)
| {
| enum mask = bitShiftMask!I;
| enum shift = bitElemShift!I;
9| size_t length = slice.length;
9| size_t index = slice._iterator._index;
9| if (auto hlen = index & mask)
| {
4| auto l = I.sizeof * 8 - hlen;
4| if (l > length)
| {
| // central problem
1| headLength = -cast(int) length;
1| tailLength = cast(int) hlen;
1| goto F;
| }
| else
| {
3| headLength = cast(uint) l;
3| length -= l;
3| index += l;
| }
| }
8| tailLength = cast(int) (length & mask);
| F:
9| length >>= shift;
9| index >>= shift;
9| bodyChunks._lengths[0] = length;
| static if (isIterator!Field)
| {
9| bodyChunks._iterator = slice._iterator._field._field;
9| bodyChunks._iterator += index;
| }
| else
| {
| bodyChunks._iterator._index = index;
| bodyChunks._iterator._field = slice._iterator._field._field;
| }
| }
|
|scope const:
|
| bool isCentralProblem()
| {
24| return headLength < 0;
| }
|
| U centralBits()
| {
1| assert(isCentralProblem);
1| return *bodyChunks._iterator.lightConst >>> tailLength;
| }
|
| uint centralLength()
| {
1| assert(isCentralProblem);
1| return -headLength;
| }
|
| /// head bits (last `headLength` bits are valid).
| U headBits()
| {
4| assert(!isCentralProblem);
4| if (headLength == 0)
1| return U.init;
| static if (isIterator!Field)
3| return bodyChunks._iterator.lightConst[-1];
| else
| return bodyChunks._iterator._field.lightConst[bodyChunks._iterator._index - 1];
| }
|
| /// tail bits (first `tailLength` bits are valid).
| U tailBits()
| {
9| assert(!isCentralProblem);
9| if (tailLength == 0)
4| return U.init;
| static if (isIterator!Field)
5| return bodyChunks._iterator.lightConst[bodyChunks.length];
| else
| return bodyChunks._iterator._field.lightConst[bodyChunks._iterator._index + bodyChunks.length];
| }
|
| U negCentralMask()
| {
1| return U.max << centralLength;
| }
|
| U negHeadMask()
| {
0000000| return U.max << headLength;
| }
|
| U negTailMask()
| {
2| return U.max << tailLength;
| }
|
| U negCentralMaskS()
| {
0000000| return U.max >> centralLength;
| }
|
| U negHeadMaskS()
| {
0000000| return U.max >> headLength;
| }
|
| U negTailMaskS()
| {
0000000| return U.max >> tailLength;
| }
|
| U centralBitsWithRemainingZeros()
| {
1| return centralBits & ~negCentralMask;
| }
|
| U centralBitsWithRemainingZerosS()
| {
0000000| return centralBits << (U.sizeof * 8 - centralLength);
| }
|
| U headBitsWithRemainingZeros()
| {
4| return headBits >>> (I.sizeof * 8 - headLength);
| }
|
| U headBitsWithRemainingZerosS()
| {
| static if (U.sizeof > I.sizeof)
0000000| return (headBits << (U.sizeof - I.sizeof) * 8) & ~negTailMaskS;
| else
0000000| return headBits & ~negTailMaskS;
| }
|
| U tailBitsWithRemainingZeros()
| {
2| return tailBits & ~negTailMask;
| }
|
| U tailBitsWithRemainingZerosS()
| {
1| return tailBits << (U.sizeof * 8 - tailLength);
| }
|
| U centralBitsWithRemainingOnes()
| {
0000000| return centralBits | negCentralMask;
| }
|
| U centralBitsWithRemainingOnesS()
| {
0000000| return centralBitsWithRemainingZerosS | negCentralMaskS;
| }
|
| U headBitsWithRemainingOnes()
| {
0000000| return headBitsWithRemainingZeros | negHeadMask;
| }
|
| U headBitsWithRemainingOnesS()
| {
0000000| return headBitsWithRemainingZerosS | negHeadMaskS;
| }
|
| U tailBitsWithRemainingOnes()
| {
0000000| return tailBits | negTailMask;
| }
|
| U tailBitsWithRemainingOnesS()
| {
0000000| return tailBitsWithRemainingZerosS | negTailMaskS;
| }
|
| size_t ctpop()
| {
| import mir.bitop: ctpop;
7| if (isCentralProblem)
1| return centralBitsWithRemainingZeros.ctpop;
6| size_t ret;
6| if (headLength)
3| ret = cast(size_t) headBitsWithRemainingZeros.ctpop;
6| if (bodyChunks.length)
| {
6| auto bc = bodyChunks.lightConst;
| do
| {
31| ret += cast(size_t) bc.front.ctpop;
31| bc.popFront;
| }
31| while(bc.length);
| }
6| if (tailBits)
2| ret += cast(size_t) tailBitsWithRemainingZeros.ctpop;
6| return ret;
| }
|
| bool any()
| {
0000000| if (isCentralProblem)
0000000| return centralBitsWithRemainingZeros != 0;
0000000| if (headBitsWithRemainingZeros != 0)
0000000| return true;
0000000| if (bodyChunks.length)
| {
0000000| auto bc = bodyChunks.lightConst;
| do
| {
0000000| if (bc.front != 0)
0000000| return true;
0000000| bc.popFront;
| }
0000000| while(bc.length);
| }
0000000| if (tailBitsWithRemainingZeros != 0)
0000000| return true;
0000000| return false;
| }
|
| bool all()
| {
0000000| if (isCentralProblem)
0000000| return centralBitsWithRemainingOnes != U.max;
0000000| size_t ret;
0000000| if (headBitsWithRemainingOnes != U.max)
0000000| return false;
0000000| if (bodyChunks.length)
| {
0000000| auto bc = bodyChunks.lightConst;
| do
| {
0000000| if (bc.front != I.max)
0000000| return false;
0000000| bc.popFront;
| }
0000000| while(bc.length);
| }
0000000| if (tailBitsWithRemainingOnes != U.max)
0000000| return false;
0000000| return true;
| }
|
| size_t cttz()
| {
0000000| U v;
0000000| size_t ret;
0000000| if (isCentralProblem)
| {
0000000| v = centralBitsWithRemainingOnes;
0000000| if (v)
0000000| goto R;
0000000| ret = centralLength;
0000000| goto L;
| }
0000000| v = headBitsWithRemainingOnes;
0000000| if (v)
0000000| goto R;
0000000| ret = headLength;
0000000| if (bodyChunks.length)
| {
0000000| auto bc = bodyChunks.lightConst;
| do
| {
0000000| v = bc.front;
0000000| if (v)
0000000| goto R;
0000000| ret += I.sizeof * 8;
0000000| bc.popFront;
| }
0000000| while(bc.length);
| }
0000000| v = tailBitsWithRemainingOnes;
0000000| if (v)
0000000| goto R;
0000000| ret += tailLength;
0000000| goto L;
| R:
0000000| ret += v.cttz;
| L:
0000000| return ret;
| }
|
| size_t ctlz()
| {
0000000| U v;
0000000| size_t ret;
0000000| if (isCentralProblem)
| {
0000000| v = centralBitsWithRemainingOnes;
0000000| if (v)
0000000| goto R;
0000000| ret = centralLength;
0000000| goto L;
| }
0000000| v = tailBitsWithRemainingOnesS;
0000000| if (v)
0000000| goto R;
0000000| ret = tailLength;
0000000| if (bodyChunks.length)
| {
0000000| auto bc = bodyChunks.lightConst;
| do
| {
0000000| v = bc.back;
0000000| if (v)
0000000| goto R;
0000000| ret += I.sizeof * 8;
0000000| bc.popBack;
| }
0000000| while(bc.length);
| }
0000000| v = headBitsWithRemainingOnesS;
0000000| if (v)
0000000| goto R;
0000000| ret += headLength;
0000000| goto L;
| R:
0000000| ret += v.ctlz;
| L:
0000000| return ret;
| }
|
| sizediff_t nBitsToCount(size_t count)
| {
1| size_t ret;
1| if (count == 0)
0000000| return count;
2| U v, cnt;
1| if (isCentralProblem)
| {
0000000| v = centralBitsWithRemainingZeros;
0000000| goto E;
| }
1| v = headBitsWithRemainingZeros;
1| cnt = v.ctpop;
1| if (cnt >= count)
0000000| goto R;
1| ret += headLength;
1| count -= cast(size_t) cnt;
1| if (bodyChunks.length)
| {
1| auto bc = bodyChunks.lightConst;
| do
| {
10| v = bc.front;
10| cnt = v.ctpop;
10| if (cnt >= count)
1| goto R;
9| ret += I.sizeof * 8;
9| count -= cast(size_t) cnt;
9| bc.popFront;
| }
9| while(bc.length);
| }
0000000| v = tailBitsWithRemainingZeros;
| E:
0000000| cnt = v.ctpop;
0000000| if (cnt >= count)
0000000| goto R;
0000000| return -1;
| R:
1| return ret + v.nTrailingBitsToCount(count);
| }
|
| sizediff_t retroNBitsToCount(size_t count)
| {
1| if (count == 0)
0000000| return count;
1| size_t ret;
2| U v, cnt;
1| if (isCentralProblem)
| {
0000000| v = centralBitsWithRemainingZerosS;
0000000| goto E;
| }
1| v = tailBitsWithRemainingZerosS;
1| cnt = v.ctpop;
1| if (cnt >= count)
0000000| goto R;
1| ret += tailLength;
1| count -= cast(size_t) cnt;
1| if (bodyChunks.length)
| {
1| auto bc = bodyChunks.lightConst;
| do
| {
28| v = bc.back;
28| cnt = v.ctpop;
28| if (cnt >= count)
1| goto R;
27| ret += I.sizeof * 8;
27| count -= cast(size_t) cnt;
27| bc.popBack;
| }
27| while(bc.length);
| }
0000000| v = headBitsWithRemainingZerosS;
| E:
0000000| cnt = v.ctpop;
0000000| if (cnt >= count)
0000000| goto R;
0000000| return -1;
| R:
1| return ret + v.nLeadingBitsToCount(count);
| }
|}
|
|/++
|Сount bits until set bit count is reached. Works with ndslices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
|Returns: bit count if set bit count is reached or `-1` otherwise.
|+/
|sizediff_t nBitsToCount(Field, I)(Slice!(FieldIterator!(BitField!(Field, I))) bitSlice, size_t count)
|{
1| return BitSliceAccelerator!(Field, I)(bitSlice).nBitsToCount(count);
|}
|
|///ditto
|sizediff_t nBitsToCount(Field, I)(Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))) bitSlice, size_t count)
|{
| import mir.ndslice.topology: retro;
1| return BitSliceAccelerator!(Field, I)(bitSlice.retro).retroNBitsToCount(count);
|}
|
|///
|version(mir_test)
|pure unittest
|{
| import mir.ndslice.allocation: bitSlice;
| import mir.ndslice.topology: retro;
1| auto s = bitSlice(1000);
1| s[50] = true;
1| s[100] = true;
1| s[200] = true;
1| s[300] = true;
1| s[400] = true;
1| assert(s.nBitsToCount(4) == 301);
1| assert(s.retro.nBitsToCount(4) == 900);
|}
|
|private void checkShapesMatch(
| string fun = __FUNCTION__,
| string pfun = __PRETTY_FUNCTION__,
| Slices...)
| (Slices slices)
| if (Slices.length > 1)
|{
| enum msgShape = "all slices must have the same shape" ~ tailErrorMessage!(fun, pfun);
| enum N = slices[0].shape.length;
| foreach (i, Slice; Slices)
| {
| static if (i == 0)
1504| continue;
| else
| static if (slices[i].shape.length == N)
1447| assert(slices[i].shape == slices[0].shape, msgShape);
| else
| {
| import mir.ndslice.fuse: fuseShape;
| static assert(slices[i].fuseShape.length >= N);
59| assert(cast(size_t[N])slices[i].fuseShape[0 .. N] == slices[0].shape, msgShape);
| }
| }
|}
|
|
|package(mir) template allFlattened(args...)
|{
| static if (args.length)
| {
| alias arg = args[0];
| @optmath @property allFlattenedMod()()
| {
| import mir.ndslice.topology: flattened;
304| return flattened(arg);
| }
| alias allFlattened = AliasSeq!(allFlattenedMod, allFlattened!(args[1..$]));
| }
| else
| alias allFlattened = AliasSeq!();
|}
|
|private template areAllContiguousSlices(Slices...)
|{
| import mir.ndslice.traits: isContiguousSlice;
| static if (allSatisfy!(isContiguousSlice, Slices))
| enum areAllContiguousSlices = Slices[0].N > 1 && areAllContiguousSlicesImpl!(Slices[0].N, Slices[1 .. $]);
| else
| enum areAllContiguousSlices = false;
|}
|
|private template areAllContiguousSlicesImpl(size_t N, Slices...)
|{
| static if (Slices.length == 0)
| enum areAllContiguousSlicesImpl = true;
| else
| enum areAllContiguousSlicesImpl = Slices[0].N == N && areAllContiguousSlicesImpl!(N, Slices[1 .. $]);
|}
|
|version(LDC) {}
|else version(GNU) {}
|else version (Windows) {}
|else version (X86_64)
|{
| //Compiling with DMD for x86-64 for Linux & OS X with optimizations enabled,
| //"Tensor mutation on-the-fly" unittest was failing. Disabling inlining
| //caused it to succeed.
| //TODO: Rework so this is unnecessary!
| version = Mir_disable_inlining_in_reduce;
|}
|
|version(Mir_disable_inlining_in_reduce)
|{
| private enum Mir_disable_inlining_in_reduce = true;
|
| private template _naryAliases(size_t n)
| {
| static if (n == 0)
| enum _naryAliases = "";
| else
| {
| enum i = n - 1;
| enum _naryAliases = _naryAliases!i ~ "alias " ~ cast(char)('a' + i) ~ " = args[" ~ i.stringof ~ "];\n";
| }
| }
|
| private template nonInlinedNaryFun(alias fun)
| {
| import mir.math.common : optmath;
| static if (is(typeof(fun) : string))
| {
| /// Specialization for string lambdas
| @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
| if (args.length <= 26)
| {
| pragma(inline,false);
| mixin(_naryAliases!(Args.length));
| return mixin(fun);
| }
| }
| else static if (is(typeof(fun.opCall) == function))
| {
| @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
| if (is(typeof(fun.opCall(args))))
| {
| pragma(inline,false);
| return fun.opCall(args);
| }
| }
| else
| {
| @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
| if (is(typeof(fun(args))))
| {
| pragma(inline,false);
| return fun(args);
| }
| }
| }
|}
|else
|{
| private enum Mir_disable_inlining_in_reduce = false;
|}
|
|S reduceImpl(alias fun, S, Slices...)(S seed, Slices slices)
|{
| do
| {
| static if (DimensionCount!(Slices[0]) == 1)
| mixin(`seed = fun(seed,` ~ frontOf!(Slices.length) ~ `);`);
| else
| mixin(`seed = .reduceImpl!fun(seed,` ~ frontOf!(Slices.length) ~ `);`);
10597| foreach_reverse(ref slice; slices)
10597| slice.popFront;
| }
10591| while(!slices[0].empty);
758| return seed;
|}
|
|/++
|Implements the homonym function (also known as `accumulate`,
|`compress`, `inject`, or `fold`) present in various programming
|languages of functional flavor. The call `reduce!(fun)(seed, slice1, ..., sliceN)`
|first assigns `seed` to an internal variable `result`,
|also called the accumulator. Then, for each set of element `x1, ..., xN` in
|`slice1, ..., sliceN`, `result = fun(result, x1, ..., xN)` gets evaluated. Finally,
|`result` is returned.
|
|`reduce` allows to iterate multiple slices in the lockstep.
|
|Note:
| $(NDSLICEREF topology, pack) can be used to specify dimensions.
|Params:
| fun = A function.
|See_Also:
| $(HTTP llvm.org/docs/LangRef.html#fast-math-flags, LLVM IR: Fast Math Flags)
|
| $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
|+/
|template reduce(alias fun)
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!fun, fun)
| && !Mir_disable_inlining_in_reduce)
| /++
| Params:
| seed = An initial accumulation value.
| slices = One or more slices, range, and arrays.
| Returns:
| the accumulated `result`
| +/
| @optmath auto reduce(S, Slices...)(S seed, Slices slices)
| if (Slices.length)
| {
| static if (Slices.length > 1)
2| slices.checkShapesMatch;
| static if (areAllContiguousSlices!Slices)
| {
| import mir.ndslice.topology: flattened;
6| return .reduce!fun(seed, allFlattened!(allLightScope!slices));
| }
| else
| {
767| if (slices[0].anyEmpty)
9| return cast(Unqual!S) seed;
| static if (is(S : Unqual!S))
| alias UT = Unqual!S;
| else
| alias UT = S;
758| return reduceImpl!(fun, UT, Slices)(seed, allLightScope!slices);
| }
| }
| else version(Mir_disable_inlining_in_reduce)
| //As above, but with inlining disabled.
| @optmath auto reduce(S, Slices...)(S seed, Slices slices)
| if (Slices.length)
| {
| static if (Slices.length > 1)
| slices.checkShapesMatch;
| static if (areAllContiguousSlices!Slices)
| {
| import mir.ndslice.topology: flattened;
| return .reduce!fun(seed, allFlattened!(allLightScope!slices));
| }
| else
| {
| if (slices[0].anyEmpty)
| return cast(Unqual!S) seed;
| static if (is(S : Unqual!S))
| alias UT = Unqual!S;
| else
| alias UT = S;
| return reduceImpl!(nonInlinedNaryFun!fun, UT, Slices)(seed, allLightScope!slices);
| }
| }
| else
| alias reduce = .reduce!(naryFun!fun);
|}
|
|/// Ranges and arrays
|version(mir_test)
|unittest
|{
1| auto ar = [1, 2, 3];
1| auto s = 0.reduce!"a + b"(ar);
1| assert (s == 6);
|}
|
|/// Single slice
|version(mir_test)
|unittest
|{
| import mir.ndslice.topology : iota;
|
| //| 0 1 2 | => 3 |
| //| 3 4 5 | => 12 | => 15
1| auto sl = iota(2, 3);
|
| // sum of all element in the slice
1| auto res = size_t(0).reduce!"a + b"(sl);
|
1| assert(res == 15);
|}
|
|/// Multiple slices, dot product
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
1| auto a = iota([2, 3], 0).as!double.slice;
| //| 1 2 3 |
| //| 4 5 6 |
1| auto b = iota([2, 3], 1).as!double.slice;
|
| alias dot = reduce!"a + b * c";
1| auto res = dot(0.0, a, b);
|
| // check the result:
| import mir.ndslice.topology : flattened;
| import std.numeric : dotProduct;
1| assert(res == dotProduct(a.flattened, b.flattened));
|}
|
|/// Zipped slices, dot product
|pure
|version(mir_test) unittest
|{
| import std.typecons : Yes;
| import std.numeric : dotProduct;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota, zip, universal;
| import mir.math.common : optmath;
|
| static @optmath T fmuladd(T, Z)(const T a, Z z)
| {
6| return a + z.a * z.b;
| }
|
| // 0 1 2
| // 3 4 5
1| auto sl1 = iota(2, 3).as!double.slice.universal;
| // 1 2 3
| // 4 5 6
1| auto sl2 = iota([2, 3], 1).as!double.slice;
|
| // slices must have the same strides for `zip!true`.
1| assert(sl1.strides == sl2.strides);
|
1| auto z = zip!true(sl1, sl2);
|
1| auto dot = reduce!fmuladd(0.0, z);
|
1| assert(dot == dotProduct(iota(6), iota([6], 1)));
|}
|
|/// Tensor mutation on-the-fly
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
| import mir.math.common : optmath;
|
| static @optmath T fun(T)(const T a, ref T b)
| {
6| return a + b++;
| }
|
| //| 0 1 2 |
| //| 3 4 5 |
1| auto sl = iota(2, 3).as!double.slice;
|
1| auto res = reduce!fun(double(0), sl);
|
1| assert(res == 15);
|
| //| 1 2 3 |
| //| 4 5 6 |
1| assert(sl == iota([2, 3], 1));
|}
|
|/++
|Packed slices.
|
|Computes minimum value of maximum values for each row.
|+/
|version(mir_test)
|unittest
|{
| import mir.math.common;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.dynamic : transposed;
| import mir.ndslice.topology : as, iota, pack, map, universal;
|
5| alias maxVal = (a) => reduce!fmax(-double.infinity, a);
2| alias minVal = (a) => reduce!fmin(double.infinity, a);
2| alias minimaxVal = (a) => minVal(a.pack!1.map!maxVal);
|
1| auto sl = iota(2, 3).as!double.slice;
|
| // Vectorized computation: row stride equals 1.
| //| 0 1 2 | => | 2 |
| //| 3 4 5 | => | 5 | => 2
1| auto res = minimaxVal(sl);
1| assert(res == 2);
|
| // Common computation: row stride does not equal 1.
| //| 0 1 2 | | 0 3 | => | 3 |
| //| 3 4 5 | => | 1 4 | => | 4 |
| // | 2 5 | => | 5 | => 3
1| auto resT = minimaxVal(sl.universal.transposed);
1| assert(resT == 3);
|}
|
|/// Dlang Range API support.
|version(mir_test)
|unittest
|{
| import mir.algorithm.iteration: each;
| import std.range: phobos_iota = iota;
|
1| int s;
| // 0 1 2 3
5| 4.phobos_iota.each!(i => s += i);
1| assert(s == 6);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
1| auto a = reduce!"a + b"(size_t(7), iota([0, 1], 1));
1| assert(a == 7);
|}
|
|void eachImpl(alias fun, Slices...)(Slices slices)
|{
2433| foreach(ref slice; slices)
2433| assert(!slice.empty);
| do
| {
| static if (DimensionCount!(Slices[0]) == 1)
| mixin(`fun(`~ frontOf!(Slices.length) ~ `);`);
| else
612| .eachImpl!fun(frontOf2!slices);
| foreach_reverse(i; Iota!(Slices.length))
16410| slices[i].popFront;
| }
8385| while(!slices[0].empty);
|}
|
|void chequerEachImpl(alias fun, Slices...)(Chequer color, Slices slices)
|{
12| foreach(ref slice; slices)
12| assert(!slice.empty);
| static if (DimensionCount!(Slices[0]) == 1)
| {
10| if (color)
| {
| foreach_reverse(i; Iota!(Slices.length))
5| slices[i].popFront;
5| if (slices[0].empty)
0000000| return;
| }
10| eachImpl!fun(strideOf!slices);
| }
| else
| {
| do
| {
| mixin(`.chequerEachImpl!fun(color,` ~ frontOf!(Slices.length) ~ `);`);
10| color = cast(Chequer)!color;
| foreach_reverse(i; Iota!(Slices.length))
10| slices[i].popFront;
| }
10| while(!slices[0].empty);
| }
|}
|
|/++
|The call `each!(fun)(slice1, ..., sliceN)`
|evaluates `fun` for each set of elements `x1, ..., xN` in
|the borders of `slice1, ..., sliceN` respectively.
|
|`each` allows to iterate multiple slices in the lockstep.
|
|Params:
| fun = A function.
|Note:
| $(NDSLICEREF dynamic, transposed) and
| $(NDSLICEREF topology, pack) can be used to specify dimensions.
|+/
|template eachOnBorder(alias fun)
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!fun, fun))
| /++
| Params:
| slices = One or more slices.
| +/
| @optmath void eachOnBorder(Slices...)(Slices slices)
| if (allSatisfy!(isSlice, Slices))
| {
| import mir.ndslice.traits: isContiguousSlice;
| static if (Slices.length > 1)
2| slices.checkShapesMatch;
5| if (!slices[0].anyEmpty)
| {
| alias N = DimensionCount!(Slices[0]);
| static if (N == 1)
| {
| mixin(`fun(`~ frontOf!(Slices.length) ~ `);`);
1| if (slices[0].length > 1)
1| fun(backOf!slices);
| }
| else
| static if (anySatisfy!(isContiguousSlice, Slices))
| {
| import mir.ndslice.topology: canonical;
| template f(size_t i)
| {
| static if (isContiguousSlice!(Slices[i]))
2| auto f () { return canonical(slices[i]); }
| else
| alias f = slices[i];
| }
2| eachOnBorder(staticMap!(f, Iota!(Slices.length)));
| }
| else
| {
| foreach (dimension; Iota!N)
| {
4| eachImpl!fun(frontOfD!(dimension, slices));
6| foreach_reverse(ref slice; slices)
6| slice.popFront!dimension;
4| if (slices[0].empty!dimension)
0000000| return;
4| eachImpl!fun(backOfD!(dimension, slices));
6| foreach_reverse(ref slice; slices)
6| slice.popBack!dimension;
4| if (slices[0].empty!dimension)
0000000| return;
| }
| }
| }
| }
| else
| alias eachOnBorder = .eachOnBorder!(naryFun!fun);
|}
|
|///
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : repeat, iota;
|
1| auto sl = [3, 4].iota.slice;
1| auto zeros = repeat(0, [3, 4]);
|
1| sl.eachOnBorder!"a = b"(zeros);
|
1| assert(sl ==
| [[0, 0, 0 ,0],
| [0, 5, 6, 0],
| [0, 0, 0 ,0]]);
|
1| sl.eachOnBorder!"a = 1";
1| sl[0].eachOnBorder!"a = 2";
|
1| assert(sl ==
| [[2, 1, 1, 2],
| [1, 5, 6, 1],
| [1, 1, 1 ,1]]);
|}
|
|/++
|The call `each!(fun)(slice1, ..., sliceN)`
|evaluates `fun` for each set of elements `x1, ..., xN` in
|`slice1, ..., sliceN` respectively.
|
|`each` allows to iterate multiple slices in the lockstep.
|Params:
| fun = A function.
|Note:
| $(NDSLICEREF dynamic, transposed) and
| $(NDSLICEREF topology, pack) can be used to specify dimensions.
|See_Also:
| This is functionally similar to $(LREF reduce) but has not seed.
|+/
|template each(alias fun)
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!fun, fun))
| {
| /++
| Params:
| slices = One or more slices, ranges, and arrays.
| +/
| @optmath auto each(Slices...)(Slices slices)
| if (Slices.length && !is(Slices[0] : Chequer))
| {
| static if (Slices.length > 1)
633| slices.checkShapesMatch;
| static if (areAllContiguousSlices!Slices)
| {
| import mir.ndslice.topology: flattened;
95| .each!fun(allFlattened!(allLightScope!slices));
| }
| else
| {
553| if (slices[0].anyEmpty)
3| return;
550| eachImpl!fun(allLightScope!slices);
| }
| }
|
| /++
| Iterates elements of selected $(LREF Chequer) color.
| Params:
| color = $(LREF Chequer).
| slices = One or more slices.
| +/
| @optmath auto each(Slices...)(Chequer color, Slices slices)
| if (Slices.length && allSatisfy!(isSlice, Slices))
| {
| static if (Slices.length > 1)
| slices.checkShapesMatch;
2| if (slices[0].anyEmpty)
0000000| return;
2| chequerEachImpl!fun(color, allLightScope!slices);
| }
| }
| else
| alias each = .each!(naryFun!fun);
|}
|
|/// Ranges and arrays
|version(mir_test)
|unittest
|{
1| auto ar = [1, 2, 3];
1| ar.each!"a *= 2";
1| assert (ar == [2, 4, 6]);
|}
|
|/// Single slice, multiply-add
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
1| auto sl = iota(2, 3).as!double.slice;
|
7| sl.each!((ref a) { a = a * 10 + 5; });
|
1| assert(sl ==
| [[ 5, 15, 25],
| [35, 45, 55]]);
|}
|
|/// Swap two slices
|version(mir_test)
|unittest
|{
| import mir.utility : swap;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
1| auto a = iota([2, 3], 0).as!double.slice;
| //| 10 11 12 |
| //| 13 14 15 |
1| auto b = iota([2, 3], 10).as!double.slice;
|
1| each!swap(a, b);
|
1| assert(a == iota([2, 3], 10));
1| assert(b == iota([2, 3], 0));
|}
|
|/// Swap two zipped slices
|version(mir_test)
|unittest
|{
| import mir.utility : swap;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, zip, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
1| auto a = iota([2, 3], 0).as!double.slice;
| //| 10 11 12 |
| //| 13 14 15 |
1| auto b = iota([2, 3], 10).as!double.slice;
|
1| auto z = zip(a, b);
|
13| z.each!(z => swap(z.a, z.b));
|
1| assert(a == iota([2, 3], 10));
1| assert(b == iota([2, 3], 0));
|}
|
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
1| size_t i;
1| iota(0, 2).each!((a){i++;});
1| assert(i == 0);
|}
|
|/++
|The call `eachUploPair!(fun)(matrix)`
|evaluates `fun` for each pair (`matrix[j, i]`, `matrix[i, j]`),
|for i <= j (default) or i < j (if includeDiagonal is false).
|
|Params:
| fun = A function.
| includeDiagonal = true if applying function to diagonal,
| false (default) otherwise.
|+/
|template eachUploPair(alias fun, bool includeDiagonal = false)
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!fun, fun))
| {
| /++
| Params:
| matrix = Square matrix.
| +/
| auto eachUploPair(Iterator, SliceKind kind)(Slice!(Iterator, 2, kind) matrix)
| in
| {
10| assert(matrix.length!0 == matrix.length!1, "matrix must be square.");
| }
| do
| {
| static if (kind == Contiguous)
| {
| import mir.ndslice.topology: canonical;
5| .eachUploPair!(fun, includeDiagonal)(matrix.canonical);
| }
| else
| {
| static if (includeDiagonal == true)
| {
2| if (matrix.length) do
| {
6| eachImpl!fun(matrix.lightScope.front!0, matrix.lightScope.front!1);
6| matrix.popFront!1;
6| matrix.popFront!0;
| // hint for optimizer
6| matrix._lengths[1] = matrix._lengths[0];
| }
6| while (matrix.length);
| }
| else
| {
3| if (matrix.length) for(;;)
| {
10| assert(!matrix.empty!0);
10| assert(!matrix.empty!1);
10| auto l = matrix.lightScope.front!1;
10| auto u = matrix.lightScope.front!0;
10| matrix.popFront!1;
10| matrix.popFront!0;
10| l.popFront;
10| u.popFront;
| // hint for optimizer
10| matrix._lengths[1] = matrix._lengths[0] = l._lengths[0] = u._lengths[0];
10| if (u.length == 0)
3| break;
7| eachImpl!fun(u, l);
| }
| }
| }
| }
| }
| else
| {
| alias eachUploPair = .eachUploPair!(naryFun!fun, includeDiagonal);
| }
|}
|
|/// Transpose matrix in place.
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota, universal;
| import mir.ndslice.dynamic: transposed;
| import mir.utility: swap;
|
1| auto m = iota(4, 4).slice;
|
1| m.eachUploPair!swap;
|
1| assert(m == iota(4, 4).universal.transposed);
|}
|
|/// Reflect Upper matrix part to lower part.
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota, universal;
| import mir.ndslice.dynamic: transposed;
| import mir.utility: swap;
|
| // 0 1 2
| // 3 4 5
| // 6 7 8
1| auto m = iota(3, 3).slice;
|
4| m.eachUploPair!((u, ref l) { l = u; });
|
1| assert(m == [
| [0, 1, 2],
| [1, 4, 5],
| [2, 5, 8]]);
|}
|
|/// Fill lower triangle and diagonal with zeroes.
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| // 1 2 3
| // 4 5 6
| // 7 8 9
1| auto m = iota([3, 3], 1).slice;
|
7| m.eachUploPair!((u, ref l) { l = 0; }, true);
|
1| assert(m == [
| [0, 2, 3],
| [0, 0, 6],
| [0, 0, 0]]);
|}
|
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| // 0 1 2
| // 3 4 5
| // 6 7 8
1| auto m = iota(3, 3).slice;
7| m.eachUploPair!((u, ref l) { l = l + 1; }, true);
1| assert(m == [
| [1, 1, 2],
| [4, 5, 5],
| [7, 8, 9]]);
|}
|
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| // 0 1 2
| // 3 4 5
| // 6 7 8
1| auto m = iota(3, 3).slice;
4| m.eachUploPair!((u, ref l) { l = l + 1; }, false);
|
1| assert(m == [
| [0, 1, 2],
| [4, 4, 5],
| [7, 8, 8]]);
|}
|
|/++
|Checks if the matrix is symmetric.
|+/
|template isSymmetric(alias fun = "a == b")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!fun, fun))
| /++
| Params:
| matrix = 2D ndslice.
| +/
| bool isSymmetric(Iterator, SliceKind kind)(Slice!(Iterator, 2, kind) matrix)
| {
| static if (kind == Contiguous)
| {
| import mir.ndslice.topology: canonical;
2| return .isSymmetric!fun(matrix.canonical);
| }
| else
| {
2| if (matrix.length!0 != matrix.length!1)
0000000| return false;
2| if (matrix.length) do
| {
3| if (!allImpl!fun(matrix.lightScope.front!0, matrix.lightScope.front!1))
| {
1| return false;
| }
2| matrix.popFront!1;
2| matrix.popFront!0;
2| matrix._lengths[1] = matrix._lengths[0];
| }
2| while (matrix.length);
1| return true;
| }
| }
| else
| alias isSymmetric = .isSymmetric!(naryFun!fun);
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: iota;
1| assert(iota(2, 2).isSymmetric == false);
|
1| assert(
| [1, 2,
| 2, 3].sliced(2, 2).isSymmetric == true);
|}
|
|bool minPosImpl(alias fun, Iterator, size_t N, SliceKind kind)(scope ref size_t[N] backwardIndex, scope ref Iterator iterator, Slice!(Iterator, N, kind) slice)
|{
32| bool found;
| do
| {
| static if (slice.shape.length == 1)
| {
132| if (fun(*slice._iterator, *iterator))
| {
42| backwardIndex[0] = slice.length;
42| iterator = slice._iterator;
42| found = true;
| }
| }
| else
| {
21| if (minPosImpl!(fun, LightScopeOf!Iterator, N - 1, kind)(backwardIndex[1 .. $], iterator, lightScope(slice).front))
| {
11| backwardIndex[0] = slice.length;
11| found = true;
| }
| }
153| slice.popFront;
| }
153| while(!slice.empty);
32| return found;
|}
|
|bool[2] minmaxPosImpl(alias fun, Iterator, size_t N, SliceKind kind)(scope ref size_t[2][N] backwardIndex, scope ref Iterator[2] iterator, Slice!(Iterator, N, kind) slice)
|{
10| bool[2] found;
| do
| {
| static if (slice.shape.length == 1)
| {
48| if (fun(*slice._iterator, *iterator[0]))
| {
4| backwardIndex[0][0] = slice.length;
4| iterator[0] = slice._iterator;
4| found[0] = true;
| }
| else
44| if (fun(*iterator[1], *slice._iterator))
| {
27| backwardIndex[0][1] = slice.length;
27| iterator[1] = slice._iterator;
27| found[1] = true;
| }
| }
| else
| {
6| auto r = minmaxPosImpl!(fun, LightScopeOf!Iterator, N - 1, kind)(backwardIndex[1 .. $], iterator, lightScope(slice).front);
6| if (r[0])
| {
4| backwardIndex[0][0] = slice.length;
| }
6| if (r[1])
| {
4| backwardIndex[0][1] = slice.length;
| }
| }
54| slice.popFront;
| }
54| while(!slice.empty);
10| return found;
|}
|
|/++
|Finds a positions (ndslices) such that
|`position[0].first` is minimal and `position[1].first` is maximal elements in the slice.
|
|Position is sub-ndslice of the same dimension in the right-$(RPAREN)down-$(RPAREN)etc$(LPAREN)$(LPAREN) corner.
|
|Params:
| pred = A predicate.
|
|See_also:
| $(LREF minmaxIndex),
| $(LREF minPos),
| $(LREF maxPos),
| $(NDSLICEREF slice, Slice.backward).
|+/
|template minmaxPos(alias pred = "a < b")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slice = ndslice.
| Returns:
| 2 subslices with minimal and maximal `first` elements.
| +/
| @optmath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)[2]
| minmaxPos(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
2| typeof(return) pret;
2| if (!slice.anyEmpty)
| {
2| size_t[2][N] ret;
2| auto scopeSlice = lightScope(slice);
2| auto it = scopeSlice._iterator;
2| LightScopeOf!Iterator[2] iterator = [it, it];
2| minmaxPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
| foreach (i; Iota!N)
| {
3| pret[0]._lengths[i] = ret[i][0];
3| pret[1]._lengths[i] = ret[i][1];
| }
2| pret[0]._iterator = slice._iterator + (iterator[0] - scopeSlice._iterator);
2| pret[1]._iterator = slice._iterator + (iterator[1] - scopeSlice._iterator);
| }
2| auto strides = slice.strides;
| foreach(i; Iota!(0, pret[0].S))
| {
1| pret[0]._strides[i] = strides[i];
1| pret[1]._strides[i] = strides[i];
| }
2| return pret;
| }
| else
| alias minmaxPos = .minmaxPos!(naryFun!pred);
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.slice: sliced;
1| auto s = [
| 2, 6, 4, -3,
| 0, -4, -3, 3,
| -3, -2, 7, 2,
| ].sliced(3, 4);
|
1| auto pos = s.minmaxPos;
|
1| assert(pos[0] == s[$ - 2 .. $, $ - 3 .. $]);
1| assert(pos[1] == s[$ - 1 .. $, $ - 2 .. $]);
|
1| assert(pos[0].first == -4);
1| assert(s.backward(pos[0].shape) == -4);
1| assert(pos[1].first == 7);
1| assert(s.backward(pos[1].shape) == 7);
|}
|
|/++
|Finds a backward indices such that
|`slice[indices[0]]` is minimal and `slice[indices[1]]` is maximal elements in the slice.
|
|Params:
| pred = A predicate.
|
|See_also:
| $(LREF minmaxIndex),
| $(LREF minPos),
| $(LREF maxPos),
| $(REF Slice.backward, mir,ndslice,slice).
|+/
|template minmaxIndex(alias pred = "a < b")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slice = ndslice.
| Returns:
| Subslice with minimal (maximal) `first` element.
| +/
| @optmath size_t[N][2] minmaxIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
2| typeof(return) pret = size_t.max;
2| if (!slice.anyEmpty)
| {
2| auto shape = slice.shape;
2| size_t[2][N] ret;
| foreach (i; Iota!N)
| {
3| ret[i][1] = ret[i][0] = shape[i];
| }
2| auto scopeSlice = lightScope(slice);
2| auto it = scopeSlice._iterator;
2| LightScopeOf!Iterator[2] iterator = [it, it];
2| minmaxPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
| foreach (i; Iota!N)
| {
3| pret[0][i] = slice._lengths[i] - ret[i][0];
3| pret[1][i] = slice._lengths[i] - ret[i][1];
| }
| }
2| return pret;
| }
| else
| alias minmaxIndex = .minmaxIndex!(naryFun!pred);
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.slice: sliced;
1| auto s = [
| 2, 6, 4, -3,
| 0, -4, -3, 3,
| -3, -2, 7, 8,
| ].sliced(3, 4);
|
1| auto indices = s.minmaxIndex;
|
1| assert(indices == [[1, 1], [2, 3]]);
1| assert(s[indices[0]] == -4);
1| assert(s[indices[1]] == 8);
|}
|
|/++
|Finds a backward index such that
|`slice.backward(index)` is minimal(maximal).
|
|Params:
| pred = A predicate.
|
|See_also:
| $(LREF minIndex),
| $(LREF maxPos),
| $(LREF maxIndex),
| $(REF Slice.backward, mir,ndslice,slice).
|+/
|template minPos(alias pred = "a < b")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slice = ndslice.
| Returns:
| Multidimensional backward index such that element is minimal(maximal).
| Backward index equals zeros, if slice is empty.
| +/
| @optmath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)
| minPos(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
4| typeof(return) ret;
4| auto iterator = slice.lightScope._iterator;
4| if (!slice.anyEmpty)
| {
4| minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret._lengths, iterator, lightScope(slice));
4| ret._iterator = slice._iterator + (iterator - slice.lightScope._iterator);
| }
4| auto strides = slice.strides;
| foreach(i; Iota!(0, ret.S))
| {
2| ret._strides[i] = strides[i];
| }
4| return ret;
| }
| else
| alias minPos = .minPos!(naryFun!pred);
|}
|
|/// ditto
|template maxPos(alias pred = "a < b")
|{
| import mir.functional: naryFun, reverseArgs;
| alias maxPos = minPos!(reverseArgs!(naryFun!pred));
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.slice: sliced;
1| auto s = [
| 2, 6, 4, -3,
| 0, -4, -3, 3,
| -3, -2, 7, 2,
| ].sliced(3, 4);
|
1| auto pos = s.minPos;
|
1| assert(pos == s[$ - 2 .. $, $ - 3 .. $]);
1| assert(pos.first == -4);
1| assert(s.backward(pos.shape) == -4);
|
1| pos = s.maxPos;
|
1| assert(pos == s[$ - 1 .. $, $ - 2 .. $]);
1| assert(pos.first == 7);
1| assert(s.backward(pos.shape) == 7);
|}
|
|/++
|Finds an index such that
|`slice[index]` is minimal(maximal).
|
|Params:
| pred = A predicate.
|
|See_also:
| $(LREF minIndex),
| $(LREF maxPos),
| $(LREF maxIndex).
|+/
|template minIndex(alias pred = "a < b")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slice = ndslice.
| Returns:
| Multidimensional index such that element is minimal(maximal).
| Index elements equal to `size_t.max`, if slice is empty.
| +/
| @optmath size_t[N] minIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
7| size_t[N] ret = size_t.max;
7| if (!slice.anyEmpty)
| {
7| ret = slice.shape;
7| auto scopeSlice = lightScope(slice);
7| auto iterator = scopeSlice._iterator;
7| minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
| foreach (i; Iota!N)
12| ret[i] = slice._lengths[i] - ret[i];
| }
7| return ret;
| }
| else
| alias minIndex = .minIndex!(naryFun!pred);
|}
|
|/// ditto
|template maxIndex(alias pred = "a < b")
|{
| import mir.functional: naryFun, reverseArgs;
| alias maxIndex = minIndex!(reverseArgs!(naryFun!pred));
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.slice: sliced;
1| auto s = [
| 2, 6, 4, -3,
| 0, -4, -3, 3,
| -3, -2, 7, 8,
| ].sliced(3, 4);
|
1| auto index = s.minIndex;
|
1| assert(index == [1, 1]);
1| assert(s[index] == -4);
|
1| index = s.maxIndex;
|
1| assert(index == [2, 3]);
1| assert(s[index] == 8);
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.ndslice.slice: sliced;
1| auto s = [
| -8, 6, 4, -3,
| 0, -4, -3, 3,
| -3, -2, 7, 8,
| ].sliced(3, 4);
|
1| auto index = s.minIndex;
|
1| assert(index == [0, 0]);
1| assert(s[index] == -8);
|}
|
|version(mir_test)
|unittest
|{
| import mir.ndslice.slice: sliced;
1| auto s = [
| 0, 1, 2, 3,
| 4, 5, 6, 7,
| 8, 9, 10, 11
| ].sliced(3, 4);
|
1| auto index = s.minIndex;
1| assert(index == [0, 0]);
1| assert(s[index] == 0);
|
1| index = s.maxIndex;
1| assert(index == [2, 3]);
1| assert(s[index] == 11);
|}
|
|bool findImpl(alias fun, size_t N, Slices...)(scope ref size_t[N] backwardIndex, Slices slices)
| if (Slices.length)
|{
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
| {
| auto cnt = BitSliceAccelerator!(Field, I)(slices[0]).cttz;
| if (cnt = -1)
| return false;
| backwardIndex[0] = slices[0].length - cnt;
| }
| else
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
| {
| import mir.ndslice.topology: retro;
| auto cnt = BitSliceAccelerator!(Field, I)(slices[0].retro).ctlz;
| if (cnt = -1)
| return false;
| backwardIndex[0] = slices[0].length - cnt;
| }
| else
| {
| do
| {
| static if (DimensionCount!(Slices[0]) == 1)
| {
72| if (mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
| {
9| backwardIndex[0] = slices[0].length;
9| return true;
| }
| }
| else
| {
14| if (mixin(`findImpl!fun(backwardIndex[1 .. $],` ~ frontOf!(Slices.length) ~ `)`))
| {
5| backwardIndex[0] = slices[0].length;
5| return true;
| }
| }
76| foreach_reverse(ref slice; slices)
76| slice.popFront;
| }
72| while(!slices[0].empty);
13| return false;
| }
|}
|
|/++
|Finds an index such that
|`pred(slices[0][index], ..., slices[$-1][index])` is `true`.
|
|Params:
| pred = A predicate.
|
|See_also:
| $(LREF find),
| $(LREF any).
|Optimization:
| `findIndex!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
|+/
|template findIndex(alias pred)
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slices = One or more slices.
| Returns:
| Multidimensional index such that the predicate is true.
| Index equals `size_t.max`, if the predicate evaluates `false` for all indices.
| Constraints:
| All slices must have the same shape.
| +/
| @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) findIndex(Slices...)(Slices slices)
| if (Slices.length)
| {
| static if (Slices.length > 1)
| slices.checkShapesMatch;
4| size_t[DimensionCount!(Slices[0])] ret = -1;
4| auto lengths = slices[0].shape;
8| if (!slices[0].anyEmpty && findImpl!pred(ret, allLightScope!slices))
| foreach (i; Iota!(DimensionCount!(Slices[0])))
3| ret[i] = lengths[i] - ret[i];
| static if (DimensionCount!(Slices[0]) > 1)
2| return ret;
| else
2| return ret[0];
| }
| else
| alias findIndex = .findIndex!(naryFun!pred);
|}
|
|/// Ranges and arrays
|version(mir_test)
|unittest
|{
| import std.range : iota;
| // 0 1 2 3 4 5
1| auto sl = iota(5);
1| size_t index = sl.findIndex!"a == 3";
|
1| assert(index == 3);
1| assert(sl[index] == 3);
|
6| assert(sl.findIndex!(a => a == 8) == size_t.max);
|}
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3);
5| size_t[2] index = sl.findIndex!(a => a == 3);
|
1| assert(sl[index] == 3);
|
1| index = sl.findIndex!"a == 6";
1| assert(index[0] == size_t.max);
1| assert(index[1] == size_t.max);
|}
|
|/++
|Finds a backward index such that
|`pred(slices[0].backward(index), ..., slices[$-1].backward(index))` is `true`.
|
|Params:
| pred = A predicate.
|
|Optimization:
| To check if any element was found
| use the last dimension (row index).
| This will slightly optimize the code.
|--------
|if (backwardIndex)
|{
| auto elem1 = slice1.backward(backwardIndex);
| //...
| auto elemK = sliceK.backward(backwardIndex);
|}
|else
|{
| // not found
|}
|--------
|
|See_also:
| $(LREF findIndex),
| $(LREF any),
| $(REF Slice.backward, mir,ndslice,slice).
|
|Optimization:
| `find!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
|+/
|template find(alias pred)
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slices = One or more slices.
| Returns:
| Multidimensional backward index such that the predicate is true.
| Backward index equals zeros, if the predicate evaluates `false` for all indices.
| Constraints:
| All slices must have the same shape.
| +/
| @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) find(Slices...)(Slices slices)
| if (Slices.length && allSatisfy!(hasShape, Slices))
| {
| static if (Slices.length > 1)
1| slices.checkShapesMatch;
10| size_t[DimensionCount!(Slices[0])] ret;
10| if (!slices[0].anyEmpty)
9| findImpl!pred(ret, allLightScope!slices);
| static if (DimensionCount!(Slices[0]) > 1)
6| return ret;
| else
4| return ret[0];
| }
| else
| alias find = .find!(naryFun!pred);
|}
|
|/// Ranges and arrays
|version(mir_test)
|unittest
|{
| import std.range : iota;
|
1| auto sl = iota(10);
1| size_t index = sl.find!"a == 3";
|
1| assert(sl[$ - index] == 3);
|}
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3);
1| size_t[2] bi = sl.find!"a == 3";
1| assert(sl.backward(bi) == 3);
1| assert(sl[$ - bi[0], $ - bi[1]] == 3);
|
1| bi = sl.find!"a == 6";
1| assert(bi[0] == 0);
1| assert(bi[1] == 0);
|}
|
|/// Multiple slices
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
|
| // 0 1 2
| // 3 4 5
1| auto a = iota(2, 3);
| // 10 11 12
| // 13 14 15
1| auto b = iota([2, 3], 10);
|
5| size_t[2] bi = find!((a, b) => a * b == 39)(a, b);
1| assert(a.backward(bi) == 3);
1| assert(b.backward(bi) == 13);
|}
|
|/// Zipped slices
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota, zip;
|
| // 0 1 2
| // 3 4 5
1| auto a = iota(2, 3);
| // 10 11 12
| // 13 14 15
1| auto b = iota([2, 3], 10);
|
1| size_t[2] bi = zip!true(a, b).find!"a.a * a.b == 39";
|
1| assert(a.backward(bi) == 3);
1| assert(b.backward(bi) == 13);
|}
|
|/// Mutation on-the-fly
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3).as!double.slice;
|
| static bool pred(T)(ref T a)
| {
6| if (a == 5)
1| return true;
5| a = 8;
5| return false;
| }
|
1| size_t[2] bi = sl.find!pred;
|
1| assert(bi == [1, 1]);
1| assert(sl.backward(bi) == 5);
|
| import mir.test;
| // sl was changed
1| sl.should == [[8, 8, 8],
| [8, 8, 5]];
|}
|
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
1| size_t i;
1| size_t[2] bi = iota(2, 0).find!((elem){i++; return true;});
1| assert(i == 0);
1| assert(bi == [0, 0]);
|}
|
|size_t anyImpl(alias fun, Slices...)(Slices slices)
| if (Slices.length)
|{
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
| {
| return BitSliceAccelerator!(Field, I)(slices[0]).any;
| }
| else
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
| {
| // pragma(msg, S);
| import mir.ndslice.topology: retro;
| return .anyImpl!fun(lightScope(slices[0]).retro);
| }
| else
| {
| do
| {
| static if (DimensionCount!(Slices[0]) == 1)
| {
226| if (mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
5| return true;
| }
| else
| {
| if (anyImpl!fun(frontOf2!slices))
| return true;
| }
224| foreach_reverse(ref slice; slices)
224| slice.popFront;
| }
221| while(!slices[0].empty);
3| return false;
| }
|}
|
|/++
|Like $(LREF find), but only returns whether or not the search was successful.
|
|Params:
| pred = The predicate.
|Optimization:
| `any!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
|+/
|template any(alias pred = "a")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slices = One or more slices, ranges, and arrays.
| Returns:
| `true` if the search was successful and `false` otherwise.
| Constraints:
| All slices must have the same shape.
| +/
| @optmath bool any(Slices...)(Slices slices)
| if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
| {
| static if (Slices.length > 1)
2| slices.checkShapesMatch;
| static if (areAllContiguousSlices!Slices)
| {
| import mir.ndslice.topology: flattened;
5| return .any!pred(allFlattened!(allLightScope!slices));
| }
| else
| {
16| return !slices[0].anyEmpty && anyImpl!pred(allLightScope!slices);
| }
| }
| else
| alias any = .any!(naryFun!pred);
|}
|
|/// Ranges and arrays
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import std.range : iota;
| // 0 1 2 3 4 5
1| auto r = iota(6);
|
1| assert(r.any!"a == 3");
1| assert(!r.any!"a == 6");
|}
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3);
|
1| assert(sl.any!"a == 3");
1| assert(!sl.any!"a == 6");
|}
|
|/// Multiple slices
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
|
| // 0 1 2
| // 3 4 5
1| auto a = iota(2, 3);
| // 10 11 12
| // 13 14 15
1| auto b = iota([2, 3], 10);
|
5| assert(any!((a, b) => a * b == 39)(a, b));
|}
|
|/// Zipped slices
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota, zip;
|
| // 0 1 2
| // 3 4 5
1| auto a = iota(2, 3);
| // 10 11 12
| // 13 14 15
1| auto b = iota([2, 3], 10);
|
| // slices must have the same strides
|
1| assert(zip!true(a, b).any!"a.a * a.b == 39");
|}
|
|/// Mutation on-the-fly
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3).as!double.slice;
|
| static bool pred(T)(ref T a)
| {
6| if (a == 5)
1| return true;
5| a = 8;
5| return false;
| }
|
1| assert(sl.any!pred);
|
| // sl was changed
1| assert(sl == [[8, 8, 8],
| [8, 8, 5]]);
|}
|
|size_t allImpl(alias fun, Slices...)(Slices slices)
| if (Slices.length)
|{
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
| {
| return BitSliceAccelerator!(LightScopeOf!Field, I)(lightScope(slices[0])).all;
| }
| else
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
| {
| // pragma(msg, S);
| import mir.ndslice.topology: retro;
| return .allImpl!fun(lightScope(slices[0]).retro);
| }
| else
| {
| do
| {
| static if (DimensionCount!(Slices[0]) == 1)
| {
5304| if (!mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
15| return false;
| }
| else
| {
132| if (!allImpl!fun(frontOf2!slices))
0000000| return false;
| }
10456| foreach_reverse(ref slice; slices)
10396| slice.popFront;
| }
5421| while(!slices[0].empty);
983| return true;
| }
|}
|
|/++
|Checks if all of the elements verify `pred`.
|
|Params:
| pred = The predicate.
|Optimization:
| `all!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
|+/
|template all(alias pred = "a")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| slices = One or more slices.
| Returns:
| `true` all of the elements verify `pred` and `false` otherwise.
| Constraints:
| All slices must have the same shape.
| +/
| @optmath bool all(Slices...)(Slices slices)
| if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
| {
| static if (Slices.length > 1)
862| slices.checkShapesMatch;
| static if (areAllContiguousSlices!Slices)
| {
| import mir.ndslice.topology: flattened;
53| return .all!pred(allFlattened!(allLightScope!slices));
| }
| else
| {
1733| return slices[0].anyEmpty || allImpl!pred(allLightScope!slices);
| }
| }
| else
| alias all = .all!(naryFun!pred);
|}
|
|/// Ranges and arrays
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import std.range : iota;
| // 0 1 2 3 4 5
1| auto r = iota(6);
|
1| assert(r.all!"a < 6");
1| assert(!r.all!"a < 5");
|}
|
|///
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
|
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3);
|
1| assert(sl.all!"a < 6");
1| assert(!sl.all!"a < 5");
|}
|
|/// Multiple slices
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
|
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3);
|
1| assert(all!"a - b == 0"(sl, sl));
|}
|
|/// Zipped slices
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota, zip;
|
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3);
|
|
1| assert(zip!true(sl, sl).all!"a.a - a.b == 0");
|}
|
|/// Mutation on-the-fly
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| // 0 1 2
| // 3 4 5
1| auto sl = iota(2, 3).as!double.slice;
|
| static bool pred(T)(ref T a)
| {
5| if (a < 4)
| {
4| a = 8;
4| return true;
| }
1| return false;
| }
|
1| assert(!sl.all!pred);
|
| // sl was changed
1| assert(sl == [[8, 8, 8],
| [8, 4, 5]]);
|}
|
|@safe pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
1| size_t i;
1| assert(iota(2, 0).all!((elem){i++; return true;}));
1| assert(i == 0);
|}
|
|/++
|Counts elements in slices according to the `fun`.
|Params:
| fun = A predicate.
|
|Optimization:
| `count!"a"` has accelerated specialization for slices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
|+/
|template count(alias fun)
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!fun, fun))
| /++
| Params:
| slices = One or more slices, ranges, and arrays.
|
| Returns: The number elements according to the `fun`.
|
| Constraints:
| All slices must have the same shape.
| +/
| @optmath size_t count(Slices...)(Slices slices)
| if (Slices.length)
| {
| static if (Slices.length > 1)
| slices.checkShapesMatch;
| static if (__traits(isSame, fun, naryFun!"true"))
| {
3| return slices[0].elementCount;
| }
| else
| static if (areAllContiguousSlices!Slices)
| {
| import mir.ndslice.topology: flattened;
5| return .count!fun(allFlattened!(allLightScope!slices));
| }
| else
| {
12| if (slices[0].anyEmpty)
0000000| return 0;
12| return countImpl!(fun)(allLightScope!slices);
| }
| }
| else
| alias count = .count!(naryFun!fun);
|
|}
|
|/// Ranges and arrays
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import std.range : iota;
| // 0 1 2 3 4 5
1| auto r = iota(6);
|
1| assert(r.count!"true" == 6);
1| assert(r.count!"a" == 5);
1| assert(r.count!"a % 2" == 3);
|}
|
|/// Single slice
|version(mir_test)
|unittest
|{
| import mir.ndslice.topology : iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
1| auto sl = iota(2, 3);
|
1| assert(sl.count!"true" == 6);
1| assert(sl.count!"a" == 5);
1| assert(sl.count!"a % 2" == 3);
|}
|
|/// Accelerated set bit count
|version(mir_test)
|unittest
|{
| import mir.ndslice.topology: retro, iota, bitwise;
| import mir.ndslice.allocation: slice;
|
| //| 0 1 2 |
| //| 3 4 5 |
1| auto sl = iota!size_t(2, 3).bitwise;
|
1| assert(sl.count!"true" == 6 * size_t.sizeof * 8);
|
1| assert(sl.slice.count!"a" == 7);
|
| // accelerated
1| assert(sl.count!"a" == 7);
1| assert(sl.retro.count!"a" == 7);
|
1| auto sl2 = iota!ubyte([6], 128).bitwise;
| // accelerated
1| assert(sl2.count!"a" == 13);
1| assert(sl2[4 .. $].count!"a" == 13);
1| assert(sl2[4 .. $ - 1].count!"a" == 12);
1| assert(sl2[4 .. $ - 1].count!"a" == 12);
1| assert(sl2[41 .. $ - 1].count!"a" == 1);
|}
|
|version(mir_test)
|unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: bitwise, assumeFieldsHaveZeroShift;
1| auto sl = slice!uint([6]).bitwise;
1| auto slb = slice!ubyte([6]).bitwise;
1| slb[4] = true;
1| auto d = slb[4];
1| auto c = assumeFieldsHaveZeroShift(slb & ~slb);
| // pragma(msg, typeof(c));
1| assert(!sl.any);
1| assert((~sl).all);
| // pragma(msg, typeof(~slb));
| // pragma(msg, typeof(~slb));
| // assert(sl.findIndex);
|}
|
|/++
|Compares two or more slices for equality, as defined by predicate `pred`.
|
|See_also: $(NDSLICEREF slice, Slice.opEquals)
|
|Params:
| pred = The predicate.
|+/
|template equal(alias pred = "a == b")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| {
| /++
| Params:
| slices = Two or more ndslices, ranges, and arrays.
|
| Returns:
| `true` any of the elements verify `pred` and `false` otherwise.
| +/
| bool equal(Slices...)(Slices slices) @safe
| if (Slices.length >= 2)
| {
| import mir.internal.utility;
| static if (allSatisfy!(hasShape, Slices))
| {
262| auto shape0 = slices[0].shape;
| enum N = DimensionCount!(Slices[0]);
263| foreach (ref slice; slices[1 .. $])
| {
263| if (slice.shape != shape0)
2| goto False;
| }
260| return all!pred(allLightScope!slices);
| }
| else
| {
| for(;;)
| {
137| auto empty = slices[0].empty;
137| foreach (ref slice; slices[1 .. $])
| {
137| if (slice.empty != empty)
0000000| goto False;
| }
137| if (empty)
34| return true;
103| if (!mixin(`pred(`~ frontOf!(Slices.length) ~ `)`))
0000000| goto False;
206| foreach (ref slice; slices)
206| slice.popFront;
| }
| }
2| False: return false;
| }
| }
| else
| alias equal = .equal!(naryFun!pred);
|}
|
|/// Ranges and arrays
|@safe pure nothrow
|version(mir_test) unittest
|{
| import std.range : iota;
1| auto r = iota(6);
1| assert(r.equal([0, 1, 2, 3, 4, 5]));
|}
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : iota;
|
| // 0 1 2
| // 3 4 5
1| auto sl1 = iota(2, 3);
| // 1 2 3
| // 4 5 6
1| auto sl2 = iota([2, 3], 1);
|
1| assert(equal(sl1, sl1));
1| assert(sl1 == sl1); //can also use opEquals for two Slices
1| assert(equal!"2 * a == b + c"(sl1, sl1, sl1));
|
1| assert(equal!"a < b"(sl1, sl2));
|
1| assert(!equal(sl1[0 .. $ - 1], sl1));
1| assert(!equal(sl1[0 .. $, 0 .. $ - 1], sl1));
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.math.common: approxEqual;
| import mir.ndslice.allocation: rcslice;
| import mir.ndslice.topology: as, iota;
|
2| auto x = 5.iota.as!double.rcslice;
2| auto y = x.rcslice;
|
1| assert(equal(x, y));
1| assert(equal!approxEqual(x, y));
|}
|
|ptrdiff_t cmpImpl(alias pred, A, B)
| (scope A sl1, scope B sl2)
| if (DimensionCount!A == DimensionCount!B)
|{
| for (;;)
| {
| static if (DimensionCount!A == 1)
| {
| import mir.functional : naryFun;
36| if (naryFun!pred(sl1.front, sl2.front))
14| return -1;
22| if (naryFun!pred(sl2.front, sl1.front))
3| return 1;
| }
| else
| {
6| if (auto res = .cmpImpl!pred(sl1.front, sl2.front))
3| return res;
| }
22| sl1.popFront;
22| if (sl1.empty)
8| return -cast(ptrdiff_t)(sl2.length > 1);
14| sl2.popFront;
14| if (sl2.empty)
1| return 1;
| }
|}
|
|/++
|Performs three-way recursive lexicographical comparison on two slices according to predicate `pred`.
|Iterating `sl1` and `sl2` in lockstep, `cmp` compares each `N-1` dimensional element `e1` of `sl1`
|with the corresponding element `e2` in `sl2` recursively.
|If one of the slices has been finished,`cmp` returns a negative value if `sl1` has fewer elements than `sl2`,
|a positive value if `sl1` has more elements than `sl2`,
|and `0` if the ranges have the same number of elements.
|
|Params:
| pred = The predicate.
|+/
|template cmp(alias pred = "a < b")
|{
| import mir.functional: naryFun;
| static if (__traits(isSame, naryFun!pred, pred))
| /++
| Params:
| sl1 = First slice, range, or array.
| sl2 = Second slice, range, or array.
|
| Returns:
| `0` if both ranges compare equal.
| Negative value if the first differing element of `sl1` is less than the corresponding
| element of `sl2` according to `pred`.
| Positive value if the first differing element of `sl2` is less than the corresponding
| element of `sl1` according to `pred`.
| +/
| auto cmp(A, B)
| (scope A sl1, scope B sl2)
| if (DimensionCount!A == DimensionCount!B)
| {
28| auto b = sl2.anyEmpty;
28| if (sl1.anyEmpty)
| {
4| if (!b)
1| return -1;
3| auto sh1 = sl1.shape;
3| auto sh2 = sl2.shape;
| foreach (i; Iota!(DimensionCount!A))
4| if (sh1[i] != sh2[i])
4| return sh1[i] > sh2[i] ? 1 : -1;
1| return 0;
| }
24| if (b)
1| return 1;
23| return cmpImpl!pred(lightScope(sl1), lightScope(sl2));
| }
| else
| alias cmp = .cmp!(naryFun!pred);
|}
|
|/// Ranges and arrays
|@safe pure nothrow
|version(mir_test) unittest
|{
| import std.range : iota;
|
| // 0 1 2 3 4 5
1| auto r1 = iota(0, 6);
| // 1 2 3 4 5 6
1| auto r2 = iota(1, 7);
|
1| assert(cmp(r1, r1) == 0);
1| assert(cmp(r1, r2) < 0);
1| assert(cmp!"a >= b"(r1, r2) > 0);
|}
|
|///
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
|
| // 0 1 2
| // 3 4 5
1| auto sl1 = iota(2, 3);
| // 1 2 3
| // 4 5 6
1| auto sl2 = iota([2, 3], 1);
|
1| assert(cmp(sl1, sl1) == 0);
1| assert(cmp(sl1, sl2) < 0);
1| assert(cmp!"a >= b"(sl1, sl2) > 0);
|}
|
|@safe pure nothrow @nogc
|version(mir_test) unittest
|{
| import mir.ndslice.topology : iota;
|
1| auto sl1 = iota(2, 3);
1| auto sl2 = iota([2, 3], 1);
|
1| assert(cmp(sl1[0 .. $ - 1], sl1) < 0);
1| assert(cmp(sl1, sl1[0 .. $, 0 .. $ - 1]) > 0);
|
1| assert(cmp(sl1[0 .. $ - 2], sl1) < 0);
1| assert(cmp(sl1, sl1[0 .. $, 0 .. $ - 3]) > 0);
1| assert(cmp(sl1[0 .. $, 0 .. $ - 3], sl1[0 .. $, 0 .. $ - 3]) == 0);
1| assert(cmp(sl1[0 .. $, 0 .. $ - 3], sl1[0 .. $ - 1, 0 .. $ - 3]) > 0);
1| assert(cmp(sl1[0 .. $ - 1, 0 .. $ - 3], sl1[0 .. $, 0 .. $ - 3]) < 0);
|}
|
|size_t countImpl(alias fun, Slices...)(Slices slices)
|{
13| size_t ret;
| alias S = Slices[0];
| import mir.functional: naryFun;
| import mir.ndslice.iterator: FieldIterator, RetroIterator;
| import mir.ndslice.field: BitField;
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
| {
7| ret = BitSliceAccelerator!(Field, I)(slices[0]).ctpop;
| }
| else
| static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))), Field, I))
| {
| // pragma(msg, S);
| import mir.ndslice.topology: retro;
1| ret = .countImpl!fun(lightScope(slices[0]).retro);
| }
| else
| do
| {
| static if (DimensionCount!(Slices[0]) == 1)
| {
216| if(mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
23| ret++;
| }
| else
| ret += .countImpl!fun(frontOf2!slices);
216| foreach_reverse(ref slice; slices)
216| slice.popFront;
| }
216| while(!slices[0].empty);
13| return ret;
|}
|
|/++
|Returns: max length across all dimensions.
|+/
|size_t maxLength(S)(auto ref scope S s)
| if (hasShape!S)
|{
81| auto shape = s.shape;
81| size_t length = 0;
| foreach(i; Iota!(shape.length))
87| if (shape[i] > length)
85| length = shape[i];
81| return length;
|}
|
|/++
|The call `eachLower!(fun)(slice1, ..., sliceN)` evaluates `fun` on the lower
|triangle in `slice1, ..., sliceN` respectively.
|
|`eachLower` allows iterating multiple slices in the lockstep.
|
|Params:
| fun = A function
|See_Also:
| This is functionally similar to $(LREF each).
|+/
|template eachLower(alias fun)
|{
| import mir.functional : naryFun;
|
| static if (__traits(isSame, naryFun!fun, fun))
| {
| /++
| Params:
| inputs = One or more two-dimensional slices and an optional
| integer, `k`.
|
| The value `k` determines which diagonals will have the function
| applied:
| For k = 0, the function is also applied to the main diagonal
| For k = 1 (default), only the non-main diagonals below the main
| diagonal will have the function applied.
| For k > 1, fewer diagonals below the main diagonal will have the
| function applied.
| For k < 0, more diagonals above the main diagonal will have the
| function applied.
| +/
| void eachLower(Inputs...)(scope Inputs inputs)
| if (((Inputs.length > 1) &&
| (isIntegral!(Inputs[$ - 1]))) ||
| (Inputs.length))
| {
| import mir.ndslice.traits : isMatrix;
|
19| size_t val;
|
| static if ((Inputs.length > 1) && (isIntegral!(Inputs[$ - 1])))
| {
14| immutable(sizediff_t) k = inputs[$ - 1];
| alias Slices = Inputs[0..($ - 1)];
| alias slices = inputs[0..($ - 1)];
| }
| else
| {
| enum sizediff_t k = 1;
| alias Slices = Inputs;
| alias slices = inputs;
| }
|
| static assert (allSatisfy!(isMatrix, Slices),
| "eachLower: Every slice input must be a two-dimensional slice");
| static if (Slices.length > 1)
1| slices.checkShapesMatch;
19| if (slices[0].anyEmpty)
0000000| return;
|
20| foreach(ref slice; slices)
20| assert(!slice.empty);
|
19| immutable(size_t) m = slices[0].length!0;
19| immutable(size_t) n = slices[0].length!1;
|
19| if ((n + k) < m)
| {
6| val = m - (n + k);
6| .eachImpl!fun(selectBackOf!(val, slices));
| }
|
19| size_t i;
|
19| if (k > 0)
| {
9| foreach(ref slice; slices)
9| slice.popFrontExactly!0(k);
8| i = k;
| }
|
| do
| {
41| val = i - k + 1;
41| .eachImpl!fun(frontSelectFrontOf!(val, slices));
|
43| foreach(ref slice; slices)
43| slice.popFront!0;
41| i++;
71| } while ((i < (n + k)) && (i < m));
| }
| }
| else
| {
| alias eachLower = .eachLower!(naryFun!fun);
| }
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota, canonical, universal;
| alias AliasSeq(T...) = T;
|
| pure nothrow
| void test(alias func)()
| {
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
3| auto m = func(iota([3, 3], 1).slice);
3| m.eachLower!"a = 0"(0);
3| assert(m == [
| [0, 2, 3],
| [0, 0, 6],
| [0, 0, 0]]);
| }
|
| @safe pure nothrow @nogc
| T identity(T)(T x)
| {
1| return x;
| }
|
| alias kinds = AliasSeq!(identity, canonical, universal);
1| test!(kinds[0]);
1| test!(kinds[1]);
1| test!(kinds[2]);
|}
|
|///
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachLower!"a = 0";
1| assert(m == [
| [1, 2, 3],
| [0, 5, 6],
| [0, 0, 9]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachLower!"a = 0"(-1);
1| assert(m == [
| [0, 0, 3],
| [0, 0, 0],
| [0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachLower!"a = 0"(2);
1| assert(m == [
| [1, 2, 3],
| [4, 5, 6],
| [0, 8, 9]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachLower!"a = 0"(-2);
1| assert(m == [
| [0, 0, 0],
| [0, 0, 0],
| [0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachLower!"a = 0"(0);
1| assert(m == [
| [0, 2, 3, 4],
| [0, 0, 7, 8],
| [0, 0, 0, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachLower!"a = 0";
1| assert(m == [
| [1, 2, 3, 4],
| [0, 6, 7, 8],
| [0, 0, 11, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachLower!"a = 0"(-1);
1| assert(m == [
| [0, 0, 3, 4],
| [0, 0, 0, 8],
| [0, 0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachLower!"a = 0"(2);
1| assert(m == [
| [1, 2, 3, 4],
| [5, 6, 7, 8],
| [0, 10, 11, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachLower!"a = 0"(-2);
1| assert(m == [
| [0, 0, 0, 4],
| [0, 0, 0, 0],
| [0, 0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachLower!"a = 0"(0);
1| assert(m == [
| [0, 2, 3],
| [0, 0, 6],
| [0, 0, 0],
| [0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachLower!"a = 0";
1| assert(m == [
| [1, 2, 3],
| [0, 5, 6],
| [0, 0, 9],
| [0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachLower!"a = 0"(-1);
1| assert(m == [
| [0, 0, 3],
| [0, 0, 0],
| [0, 0, 0],
| [0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachLower!"a = 0"(2);
1| assert(m == [
| [1, 2, 3],
| [4, 5, 6],
| [0, 8, 9],
| [0, 0, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachLower!"a = 0"(-2);
1| assert(m == [
| [0, 0, 0],
| [0, 0, 0],
| [0, 0, 0],
| [0, 0, 0]]);
|}
|
|/// Swap two slices
|pure nothrow
|version(mir_test) unittest
|{
| import mir.utility : swap;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
| //| 6 7 8 |
1| auto a = iota([3, 3]).as!double.slice;
| //| 10 11 12 |
| //| 13 14 15 |
| //| 16 17 18 |
1| auto b = iota([3, 3], 10).as!double.slice;
|
1| eachLower!swap(a, b);
|
1| assert(a == [
| [ 0, 1, 2],
| [13, 4, 5],
| [16, 17, 8]]);
1| assert(b == [
| [10, 11, 12],
| [ 3, 14, 15],
| [ 6, 7, 18]]);
|}
|
|/// Swap two zipped slices
|pure nothrow
|version(mir_test) unittest
|{
| import mir.utility : swap;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, zip, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
| //| 6 7 8 |
1| auto a = iota([3, 3]).as!double.slice;
| //| 10 11 12 |
| //| 13 14 15 |
| //| 16 17 18 |
1| auto b = iota([3, 3], 10).as!double.slice;
|
1| auto z = zip(a, b);
|
7| z.eachLower!(z => swap(z.a, z.b));
|
1| assert(a == [
| [ 0, 1, 2],
| [13, 4, 5],
| [16, 17, 8]]);
1| assert(b == [
| [10, 11, 12],
| [ 3, 14, 15],
| [ 6, 7, 18]]);
|}
|
|/++
|The call `eachUpper!(fun)(slice1, ..., sliceN)` evaluates `fun` on the upper
|triangle in `slice1, ..., sliceN`, respectively.
|
|`eachUpper` allows iterating multiple slices in the lockstep.
|
|Params:
| fun = A function
|See_Also:
| This is functionally similar to $(LREF each).
|+/
|template eachUpper(alias fun)
|{
| import mir.functional: naryFun;
|
| static if (__traits(isSame, naryFun!fun, fun))
| {
| /++
| Params:
| inputs = One or more two-dimensional slices and an optional
| integer, `k`.
|
| The value `k` determines which diagonals will have the function
| applied:
| For k = 0, the function is also applied to the main diagonal
| For k = 1 (default), only the non-main diagonals above the main
| diagonal will have the function applied.
| For k > 1, fewer diagonals below the main diagonal will have the
| function applied.
| For k < 0, more diagonals above the main diagonal will have the
| function applied.
| +/
| void eachUpper(Inputs...)(scope Inputs inputs)
| if (((Inputs.length > 1) &&
| (isIntegral!(Inputs[$ - 1]))) ||
| (Inputs.length))
| {
| import mir.ndslice.traits : isMatrix;
|
19| size_t val;
|
| static if ((Inputs.length > 1) && (isIntegral!(Inputs[$ - 1])))
| {
14| immutable(sizediff_t) k = inputs[$ - 1];
| alias Slices = Inputs[0..($ - 1)];
| alias slices = inputs[0..($ - 1)];
| }
| else
| {
| enum sizediff_t k = 1;
| alias Slices = Inputs;
| alias slices = inputs;
| }
|
| static assert (allSatisfy!(isMatrix, Slices),
| "eachUpper: Every slice input must be a two-dimensional slice");
| static if (Slices.length > 1)
1| slices.checkShapesMatch;
19| if (slices[0].anyEmpty)
0000000| return;
|
20| foreach(ref slice; slices)
20| assert(!slice.empty);
|
19| immutable(size_t) m = slices[0].length!0;
19| immutable(size_t) n = slices[0].length!1;
|
19| size_t i;
|
19| if (k < 0)
| {
6| val = -k;
6| .eachImpl!fun(selectFrontOf!(val, slices));
|
6| foreach(ref slice; slices)
6| slice.popFrontExactly!0(-k);
6| i = -k;
| }
|
| do
| {
41| val = (n - k) - i;
41| .eachImpl!fun(frontSelectBackOf!(val, slices));
|
43| foreach(ref slice; slices)
43| slice.popFront;
41| i++;
69| } while ((i < (n - k)) && (i < m));
| }
| }
| else
| {
| alias eachUpper = .eachUpper!(naryFun!fun);
| }
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota, canonical, universal;
|
| pure nothrow
| void test(alias func)()
| {
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
3| auto m = func(iota([3, 3], 1).slice);
3| m.eachUpper!"a = 0"(0);
3| assert(m == [
| [0, 0, 0],
| [4, 0, 0],
| [7, 8, 0]]);
| }
|
| @safe pure nothrow @nogc
| T identity(T)(T x)
| {
1| return x;
| }
|
| alias kinds = AliasSeq!(identity, canonical, universal);
1| test!(kinds[0]);
1| test!(kinds[1]);
1| test!(kinds[2]);
|}
|
|///
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachUpper!"a = 0";
1| assert(m == [
| [1, 0, 0],
| [4, 5, 0],
| [7, 8, 9]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachUpper!"a = 0"(-1);
1| assert(m == [
| [0, 0, 0],
| [0, 0, 0],
| [7, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachUpper!"a = 0"(2);
1| assert(m == [
| [1, 2, 0],
| [4, 5, 6],
| [7, 8, 9]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
1| auto m = iota([3, 3], 1).slice;
1| m.eachUpper!"a = 0"(-2);
1| assert(m == [
| [0, 0, 0],
| [0, 0, 0],
| [0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachUpper!"a = 0"(0);
1| assert(m == [
| [0, 0, 0, 0],
| [5, 0, 0, 0],
| [9, 10, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachUpper!"a = 0";
1| assert(m == [
| [1, 0, 0, 0],
| [5, 6, 0, 0],
| [9, 10, 11, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachUpper!"a = 0"(-1);
1| assert(m == [
| [0, 0, 0, 0],
| [0, 0, 0, 0],
| [9, 0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachUpper!"a = 0"(2);
1| assert(m == [
| [1, 2, 0, 0],
| [5, 6, 7, 0],
| [9, 10, 11, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 4 |
| //| 5 6 7 8 |
| //| 9 10 11 12 |
1| auto m = iota([3, 4], 1).slice;
1| m.eachUpper!"a = 0"(-2);
1| assert(m == [
| [0, 0, 0, 0],
| [0, 0, 0, 0],
| [0, 0, 0, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachUpper!"a = 0"(0);
1| assert(m == [
| [0, 0, 0],
| [4, 0, 0],
| [7, 8, 0],
| [10, 11, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachUpper!"a = 0";
1| assert(m == [
| [1, 0, 0],
| [4, 5, 0],
| [7, 8, 9],
| [10, 11, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachUpper!"a = 0"(-1);
1| assert(m == [
| [0, 0, 0],
| [0, 0, 0],
| [7, 0, 0],
| [10, 11, 0]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachUpper!"a = 0"(2);
1| assert(m == [
| [1, 2, 0],
| [4, 5, 6],
| [7, 8, 9],
| [10, 11, 12]]);
|}
|
|pure nothrow
|version(mir_test) unittest
|{
| import mir.ndslice.allocation: slice;
| import mir.ndslice.topology: iota;
|
| //| 1 2 3 |
| //| 4 5 6 |
| //| 7 8 9 |
| //| 10 11 12 |
1| auto m = iota([4, 3], 1).slice;
1| m.eachUpper!"a = 0"(-2);
1| assert(m == [
| [0, 0, 0],
| [0, 0, 0],
| [0, 0, 0],
| [10, 0, 0]]);
|}
|
|/// Swap two slices
|pure nothrow
|version(mir_test) unittest
|{
| import mir.utility : swap;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
| //| 6 7 8 |
1| auto a = iota([3, 3]).as!double.slice;
| //| 10 11 12 |
| //| 13 14 15 |
| //| 16 17 18 |
1| auto b = iota([3, 3], 10).as!double.slice;
|
1| eachUpper!swap(a, b);
|
1| assert(a == [
| [0, 11, 12],
| [3, 4, 15],
| [6, 7, 8]]);
1| assert(b == [
| [10, 1, 2],
| [13, 14, 5],
| [16, 17, 18]]);
|}
|
|/// Swap two zipped slices
|pure nothrow
|version(mir_test) unittest
|{
| import mir.utility : swap;
| import mir.ndslice.allocation : slice;
| import mir.ndslice.topology : as, zip, iota;
|
| //| 0 1 2 |
| //| 3 4 5 |
| //| 6 7 8 |
1| auto a = iota([3, 3]).as!double.slice;
| //| 10 11 12 |
| //| 13 14 15 |
| //| 16 17 18 |
1| auto b = iota([3, 3], 10).as!double.slice;
|
1| auto z = zip(a, b);
|
7| z.eachUpper!(z => swap(z.a, z.b));
|
1| assert(a == [
| [0, 11, 12],
| [3, 4, 15],
| [6, 7, 8]]);
1| assert(b == [
| [10, 1, 2],
| [13, 14, 5],
| [16, 17, 18]]);
|}
|
|// uniq
|/**
|Lazily iterates unique consecutive elements of the given range (functionality
|akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system
|utility). Equivalence of elements is assessed by using the predicate
|$(D pred), by default $(D "a == b"). The predicate is passed to
|$(REF nary, mir,functional), and can either accept a string, or any callable
|that can be executed via $(D pred(element, element)). If the given range is
|bidirectional, $(D uniq) also yields a
|`std,range,primitives`.
|Params:
| pred = Predicate for determining equivalence between range elements.
|*/
|template uniq(alias pred = "a == b")
|{
| static if (__traits(isSame, naryFun!pred, pred))
| {
| /++
| Params:
| r = An input range of elements to filter.
| Returns:
| An input range of
| consecutively unique elements in the original range. If `r` is also a
| forward range or bidirectional range, the returned range will be likewise.
| +/
| Uniq!(naryFun!pred, Range) uniq(Range)(Range r)
| if (isInputRange!Range && !isSlice!Range)
| {
| import core.lifetime: move;
4| return typeof(return)(r.move);
| }
|
| /// ditto
| auto uniq(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
| import mir.ndslice.topology: flattened;
| import core.lifetime: move;
4| auto r = slice.move.flattened;
4| return Uniq!(pred, typeof(r))(move(r));
| }
| }
| else
| alias uniq = .uniq!(naryFun!pred);
|}
|
|///
|@safe version(mir_test) unittest
|{
1| int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
1| assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ]));
|
| import std.algorithm.mutation : copy;
| // Filter duplicates in-place using copy
1| arr.length -= arr.uniq.copy(arr).length;
1| assert(arr == [ 1, 2, 3, 4, 5 ]);
|
| // Note that uniqueness is only determined consecutively; duplicated
| // elements separated by an intervening different element will not be
| // eliminated:
1| assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
|}
|
|/// N-dimensional case
|version(mir_test)
|@safe pure unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: byDim, map, iota;
|
1| auto matrix = [ [1, 2, 2], [2, 2, 3], [4, 4, 4] ].fuse;
|
1| assert(matrix.uniq.equal([ 1, 2, 3, 4 ]));
|
| // unique elements for each row
1| assert(matrix.byDim!0.map!uniq.equal!equal([ [1, 2], [2, 3], [4] ]));
|}
|
|/++
|Authros: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilia Ki (betterC rework)
|+/
|struct Uniq(alias pred, Range)
|{
| Range _input;
|
| void popFront() scope
| {
178| assert(!empty, "Attempting to popFront an empty uniq.");
178| auto last = _input.front;
| do
| {
266| _input.popFront();
| }
499| while (!_input.empty && pred(last, _input.front));
| }
|
| auto ref front() @property
| {
79| assert(!empty, "Attempting to fetch the front of an empty uniq.");
79| return _input.front;
| }
|
| import std.range.primitives: isBidirectionalRange;
|
| static if (isBidirectionalRange!Range)
| {
| void popBack() scope
| {
0000000| assert(!empty, "Attempting to popBack an empty uniq.");
0000000| auto last = _input.back;
| do
| {
0000000| _input.popBack();
| }
0000000| while (!_input.empty && pred(last, _input.back));
| }
|
| auto ref back() return scope @property
| {
0000000| assert(!empty, "Attempting to fetch the back of an empty uniq.");
0000000| return _input.back;
| }
| }
|
| static if (isInfinite!Range)
| {
| enum bool empty = false; // Propagate infiniteness.
| }
| else
| {
447| @property bool empty() const { return _input.empty; }
| }
|
| ref opIndex()() scope return
| {
1| return this;
| }
|
| auto opIndex()() const return scope
| {
| return Filter!(typeof(_input[]))(_input[]);
| }
|
| import std.range.primitives: isForwardRange;
|
| static if (isForwardRange!Range)
| {
| @property typeof(this) save() return scope
| {
0000000| return typeof(this)(_input.save);
| }
| }
|}
|
|version(none)
|@safe version(mir_test) unittest
|{
| import std.internal.test.dummyrange;
| import std.range;
|
| int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
| auto r = uniq(arr);
| static assert(isForwardRange!(typeof(r)));
|
| assert(equal(r, [ 1, 2, 3, 4, 5 ][]));
| assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][])));
|
| foreach (DummyType; AllDummyRanges)
| {
| DummyType d;
| auto u = uniq(d);
| assert(equal(u, [1,2,3,4,5,6,7,8,9,10]));
|
| static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u)));
|
| static if (d.rt >= RangeType.Bidirectional)
| {
| assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1]));
| }
| }
|}
|
|@safe version(mir_test) unittest // https://issues.dlang.org/show_bug.cgi?id=17264
|{
1| const(int)[] var = [0, 1, 1, 2];
1| assert(var.uniq.equal([0, 1, 2]));
|}
|
|@safe version(mir_test) unittest {
| import mir.ndslice.allocation;
| import mir.math.common: approxEqual;
2| auto x = rcslice!double(2);
2| auto y = rcslice!double(2);
1| x[] = [2, 3];
1| y[] = [2, 3];
1| assert(equal!approxEqual(x,y));
|}
|
|/++
|Implements the higher order filter function. The predicate is passed to
|`mir.functional.naryFun`, and can either accept a string, or any callable
|that can be executed via `pred(element)`.
|Params:
| pred = Function to apply to each element of range
|Returns:
| `filter!(pred)(range)` returns a new range containing only elements `x` in `range` for
| which `pred(x)` returns `true`.
|See_Also:
| $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
|Note:
| $(RED User and library code MUST call `empty` method ahead each call of pair or one of `front` and `popFront` methods.)
|+/
|template filter(alias pred = "a")
|{
| static if (__traits(isSame, naryFun!pred, pred))
| {
| /++
| Params:
| r = An input range of elements to filter.
| Returns:
| A new range containing only elements `x` in `range` for which `predicate(x)` returns `true`.
| +/
| Filter!(naryFun!pred, Range) filter(Range)(Range r)
| if (isInputRange!Range && !isSlice!Range)
| {
| import core.lifetime: move;
7| return typeof(return)(r.move);
| }
|
| /// ditto
| auto filter(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| {
| import mir.ndslice.topology: flattened;
| import core.lifetime: move;
20| auto r = slice.move.flattened;
20| return Filter!(pred, typeof(r))(move(r));
| }
| }
| else
| alias filter = .filter!(naryFun!pred);
|}
|
|/// ditto
|struct Filter(alias pred, Range)
|{
| Range _input;
| version(assert) bool _freshEmpty;
|
| void popFront() scope
| {
61| assert(!_input.empty, "Attempting to popFront an empty Filter.");
61| version(assert) assert(_freshEmpty, "Attempting to pop the front of a Filter without calling '.empty' method ahead.");
61| version(assert) _freshEmpty = false;
61| _input.popFront;
| }
|
| auto ref front() @safe @property return scope
| {
61| assert(!_input.empty, "Attempting to fetch the front of an empty Filter.");
61| version(assert) assert(_freshEmpty, "Attempting to fetch the front of a Filter without calling '.empty' method ahead.");
61| return _input.front;
| }
|
| bool empty() @safe scope @property
| {
112| version(assert) _freshEmpty = true;
| for (;;)
| {
159| if (auto r = _input.empty)
27| return true;
132| if (pred(_input.front))
85| return false;
47| _input.popFront;
| }
| }
|
| import std.range.primitives: isForwardRange;
| static if (isForwardRange!Range)
| {
| @property typeof(this) save() return scope
| {
0000000| return typeof(this)(_input.save);
| }
| }
|
| ref opIndex()() scope return
| {
| return this;
| }
|
| auto opIndex()() const return scope
| {
| return Filter!(pred, typeof(_input[]))(_input[]);
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow unittest
|{
1| int[] arr = [ 0, 1, 2, 3, 4, 5 ];
|
| // Filter below 3
7| auto small = filter!(a => a < 3)(arr);
1| assert(equal(small, [ 0, 1, 2 ]));
|
| // Filter again, but with Uniform Function Call Syntax (UFCS)
7| auto sum = arr.filter!(a => a < 3);
1| assert(equal(sum, [ 0, 1, 2 ]));
|
| // Filter with the default predicate
1| auto nonZeros = arr.filter;
1| assert(equal(nonZeros, [ 1, 2, 3, 4, 5 ]));
|
| // In combination with concatenation() to span multiple ranges
| import mir.ndslice.concatenation;
|
1| int[] a = [ 3, -2, 400 ];
1| int[] b = [ 100, -101, 102 ];
7| auto r = concatenation(a, b).filter!(a => a > 0);
1| assert(equal(r, [ 3, 400, 100, 102 ]));
|
| // Mixing convertible types is fair game, too
1| double[] c = [ 2.5, 3.0 ];
9| auto r1 = concatenation(c, a, b).filter!(a => cast(int) a != a);
1| assert(equal(r1, [ 2.5 ]));
|}
|
|/// N-dimensional filtering
|version(mir_test)
|@safe pure unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: byDim, map;
|
1| auto matrix =
| [[ 3, -2, 400 ],
| [ 100, -101, 102 ]].fuse;
|
| alias filterPositive = filter!"a > 0";
|
| // filter all elements in the matrix
1| auto r = filterPositive(matrix);
1| assert(equal(r, [ 3, 400, 100, 102 ]));
|
| // filter all elements for each row
1| auto rr = matrix.byDim!0.map!filterPositive;
1| assert(equal!equal(rr, [ [3, 400], [100, 102] ]));
|
| // filter all elements for each column
1| auto rc = matrix.byDim!1.map!filterPositive;
1| assert(equal!equal(rc, [ [3, 100], [], [400, 102] ]));
|}
|
|/// N-dimensional filtering based on value in specific row/column
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.ndslice.fuse;
| import mir.ndslice.topology: byDim;
|
1| auto matrix =
| [[ 3, 2, 400 ],
| [ 100, -101, 102 ]].fuse;
|
| // filter row based on value in index 1
1| auto r1 = matrix.byDim!0.filter!("a[1] > 0");
1| assert(equal!equal(r1, [ [3, 2, 400] ]));
|
| // filter column based on value in index 1
1| auto r2 = matrix.byDim!1.filter!("a[1] > 0");
1| assert(equal!equal(r2, [ [3, 100], [400, 102] ]));
|}
|
|/// Filter out NaNs
|version(mir_test)
|@safe pure nothrow
|unittest {
| import mir.algorithm.iteration: equal, filter;
| import mir.ndslice.slice: sliced;
| import std.math.traits: isNaN;
|
| static immutable result1 = [1.0, 2];
|
1| double x;
1| auto y = [1.0, 2, x].sliced;
4| auto z = y.filter!(a => !isNaN(a));
1| assert(z.equal(result1));
|}
|
|/// Filter out NaNs by row and by column
|version(mir_test)
|@safe pure
|unittest {
| import mir.algorithm.iteration: equal, filter;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: byDim, map;
| import std.math.traits: isNaN;
|
| static immutable result1 = [[1.0, 2], [3.0, 4, 5]];
| static immutable result2 = [[1.0, 3], [2.0, 4], [5.0]];
|
1| double x;
1| auto y = [[1.0, 2, x], [3.0, 4, 5]].fuse;
|
| // by row
7| auto z1 = y.byDim!0.map!(filter!(a => !isNaN(a)));
1| assert(z1.equal!equal(result1));
| // by column
7| auto z2 = y.byDim!1.map!(filter!(a => !isNaN(a)));
1| assert(z2.equal!equal(result2));
|}
|
|/// Filter entire rows/columns that have NaNs
|version(mir_test)
|@safe pure
|unittest {
| import mir.algorithm.iteration: equal, filter;
| import mir.ndslice.fuse: fuse;
| import mir.ndslice.topology: byDim, map;
| import std.math.traits: isNaN;
|
| static immutable result1 = [[3.0, 4, 5]];
| static immutable result2 = [[1.0, 3], [2.0, 4]];
|
1| double x;
1| auto y = [[1.0, 2, x], [3.0, 4, 5]].fuse;
|
| // by row
9| auto z1 = y.byDim!0.filter!(a => a.all!(b => !isNaN(b)));
1| assert(z1.equal!equal(result1));
| // by column
9| auto z2 = y.byDim!1.filter!(a => a.all!(b => !isNaN(b)));
1| assert(z2.equal!equal(result2));
|}
|
|/++
|Implements the higher order filter and map function. The predicate and map functions are passed to
|`mir.functional.naryFun`, and can either accept a string, or any callable
|that can be executed via `pred(element)` and `map(element)`.
|Params:
| pred = Filter function to apply to each element of range (optional)
| map = Map function to apply to each element of range
|Returns:
| `rcfilter!(pred)(range)` returns a new RCArray containing only elements `map(x)` in `range` for
| which `pred(x)` returns `true`.
|See_Also:
| $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
|+/
|template rcfilter(alias pred = "a", alias map = "a")
|{
| static if (__traits(isSame, naryFun!pred, pred) && __traits(isSame, naryFun!map, map))
| {
| /++
| Params:
| r = An input range of elements to filter.
| Returns:
| A new range containing only elements `x` in `range` for which `predicate(x)` returns `true`.
| +/
| auto rcfilter(Range)(Range r)
| if (isIterable!Range && (!isSlice!Range || DimensionCount!Range == 1))
| {
| import core.lifetime: forward;
| import mir.appender: scopedBuffer;
| import mir.primitives: isInputRange;
| import mir.rc.array: RCArray;
|
| alias T = typeof(map(r.front));
8| auto buffer = scopedBuffer!T;
80| foreach (ref e; r)
| {
24| if (pred(e))
| {
| static if (__traits(isSame, naryFun!"a", map))
6| buffer.put(forward!e);
| else
6| buffer.put(map(forward!e));
| }
| }
4| return () @trusted
| {
4| auto ret = RCArray!T(buffer.length);
4| buffer.moveDataAndEmplaceTo(ret[]);
4| return ret;
| } ();
| }
|
| /// ditto
| auto rcfilter(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
| if (N > 1)
| {
| import mir.ndslice.topology: flattened;
| import core.lifetime: move;
2| return rcfilter(slice.move.flattened);
| }
| }
| else
| alias rcfilter = .rcfilter!(naryFun!pred, naryFun!map);
|}
|
|///
|version(mir_test)
|@safe pure nothrow @nogc unittest
|{
| import mir.ndslice.topology: iota;
|
1| auto val = 3;
1| auto factor = 5;
| // Filter iota 2x3 matrix below 3
7| assert(iota(2, 3).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
| // Filter and map below 3
10| assert(6.iota.rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
|}
|
|version(mir_test)
|@safe pure nothrow @nogc unittest
|{
| import mir.ndslice.topology: iota, as;
|
1| auto val = 3;
1| auto factor = 5;
| // Filter iota 2x3 matrix below 3
7| assert(iota(2, 3).as!(const int).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
| // Filter and map below 3
10| assert(6.iota.as!(immutable int).rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
|}
|
|/++
|Implements the homonym function (also known as `accumulate`, $(D
|compress), `inject`, or `foldl`) present in various programming
|languages of functional flavor. The call `fold!(fun)(slice, seed)`
|first assigns `seed` to an internal variable `result`,
|also called the accumulator. Then, for each element `x` in $(D
|slice), `result = fun(result, x)` gets evaluated. Finally, $(D
|result) is returned.
|
|Params:
| fun = the predicate function to apply to the elements
|
|See_Also:
| $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
| $(LREF sum) is similar to `fold!((a, b) => a + b)` that offers
| precise summing of floating point numbers.
| This is functionally equivalent to $(LREF reduce) with the argument order
| reversed.
|+/
|template fold(alias fun)
|{
| /++
| Params:
| slice = A slice, range, and array.
| seed = An initial accumulation value.
| Returns:
| the accumulated result
| +/
| @optmath auto fold(Slice, S)(scope Slice slice, S seed)
| {
| import core.lifetime: move;
9| return reduce!fun(seed, slice.move);
| }
|}
|
|///
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.slice: sliced;
| import mir.ndslice.topology: map;
|
1| auto arr = [1, 2, 3, 4, 5].sliced;
|
| // Sum all elements
6| assert(arr.fold!((a, b) => a + b)(0) == 15);
6| assert(arr.fold!((a, b) => a + b)(6) == 21);
|
| // Can be used in a UFCS chain
11| assert(arr.map!(a => a + 1).fold!((a, b) => a + b)(0) == 20);
|
| // Return the last element of any range
6| assert(arr.fold!((a, b) => b)(0) == 5);
|}
|
|/// Works for matrices
|version(mir_test)
|@safe pure
|unittest
|{
| import mir.ndslice.fuse: fuse;
|
1| auto arr = [
| [1, 2, 3],
| [4, 5, 6]
| ].fuse;
|
7| assert(arr.fold!((a, b) => a + b)(0) == 21);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
| import mir.ndslice.topology: map;
|
1| int[] arr = [1, 2, 3, 4, 5];
|
| // Sum all elements
6| assert(arr.fold!((a, b) => a + b)(0) == 15);
6| assert(arr.fold!((a, b) => a + b)(6) == 21);
|
| // Can be used in a UFCS chain
11| assert(arr.map!(a => a + 1).fold!((a, b) => a + b)(0) == 20);
|
| // Return the last element of any range
6| assert(arr.fold!((a, b) => b)(0) == 5);
|}
|
|version(mir_test)
|@safe pure nothrow
|unittest
|{
1| int[] arr = [1];
| static assert(!is(typeof(arr.fold!()(0))));
| static assert(!is(typeof(arr.fold!(a => a)(0))));
| static assert(is(typeof(arr.fold!((a, b) => a)(0))));
1| assert(arr.length == 1);
|}
|
|version(mir_test)
|unittest
|{
| import mir.rc.array: RCArray;
| import mir.algorithm.iteration: minmaxPos, minPos, maxPos, minmaxIndex, minIndex, maxIndex;
|
| static immutable a = [0.0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
2| auto x = RCArray!double(12);
51| foreach(i, ref e; x)
12| e = a[i];
2| auto y = x.asSlice;
2| auto z0 = y.minmaxPos;
2| auto z1 = y.minPos;
2| auto z2 = y.maxPos;
1| auto z3 = y.minmaxIndex;
1| auto z4 = y.minIndex;
1| auto z5 = y.maxIndex;
|}
source/mir/algorithm/iteration.d is 87% covered
<<<<<< EOF
# path=source-mir-algorithm-setops.lst
|// Written in the D programming language.
|/**
|This is a submodule of $(MREF mir, algorithm). It contains `nothrow` `@nogc` BetterC alternative to `MultiwayMerge` from `std.algorithm.setops`.
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
|
|Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilia Ki (Mir & BetterC rework, optimization).
| */
|module mir.algorithm.setops;
|
|import core.lifetime: move;
|import mir.functional: naryFun;
|import mir.primitives;
|import mir.qualifier;
|import std.range.primitives: isRandomAccessRange;
|
|/**
|Merges multiple sets. The input sets are passed as a
|range of ranges and each is assumed to be sorted by $(D
|less). Computation is done lazily, one union element at a time. The
|complexity of one $(D popFront) operation is $(BIGOH
|log(ror.length)). However, the length of $(D ror) decreases as ranges
|in it are exhausted, so the complexity of a full pass through $(D
|MultiwayMerge) is dependent on the distribution of the lengths of ranges
|contained within $(D ror). If all ranges have the same length $(D n)
|(worst case scenario), the complexity of a full pass through $(D
|MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D
|log(ror.length)) times worse than just spanning all ranges in
|turn. The output comes sorted (unstably) by $(D less).
|The length of the resulting range is the sum of all lengths of
|the ranges passed as input. This means that all elements (duplicates
|included) are transferred to the resulting range.
|For backward compatibility, `multiwayMerge` is available under
|the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` .
|Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion`
|and `NWayUnion` will be deprecated.
|Params:
| less = Predicate the given ranges are sorted by.
| ror = A range of ranges sorted by `less` to compute the union for.
|Returns:
| A range of the union of the ranges in `ror`.
|Warning: Because $(D MultiwayMerge) does not allocate extra memory, it
|will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership
|of $(D ror) and discretionarily swaps and advances elements of it. If
|you want $(D ror) to preserve its contents after the call, you may
|want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the
|duplicate in between calls).
| */
|struct MultiwayMerge(alias less, RangeOfRanges)
| if (isRandomAccessRange!RangeOfRanges)
|{
| import mir.primitives;
| import mir.container.binaryheap;
|
| ///
| @disable this();
| ///
| @disable this(this);
|
| ///
| static bool compFront(ElementType!RangeOfRanges a, ElementType!RangeOfRanges b)
| {
| // revert comparison order so we get the smallest elements first
378| return less(b.front, a.front);
| }
|
| /// Heap
| BinaryHeap!(compFront, RangeOfRanges) _heap;
|
| ///
27| this(RangeOfRanges ror)
| {
| // Preemptively get rid of all empty ranges in the input
| // No need for stability either
27| auto temp = ror.lightScope;
99| for (;!temp.empty;)
| {
72| if (!temp.front.empty)
| {
72| temp.popFront;
72| continue;
| }
| import mir.utility: swap;
0000000| swap(temp.back, temp.front);
0000000| temp.popBack;
0000000| ror.popBack;
| }
| //Build the heap across the range
27| _heap = typeof(_heap)(ror.move);
| }
|
| ///
1016| @property bool empty() scope const { return _heap.empty; }
|
| ///
| @property auto ref front()
| {
417| assert(!empty);
417| return _heap.front.front;
| }
|
| ///
| void popFront() scope @safe
| {
239| _heap._store.front.popFront;
239| if (!_heap._store.front.empty)
167| _heap.siftDown(_heap._store[], 0, _heap._length);
| else
72| _heap.removeFront;
| }
|}
|
|/// Ditto
|MultiwayMerge!(naryFun!less, RangeOfRanges) multiwayMerge
|(alias less = "a < b", RangeOfRanges)
|(RangeOfRanges ror)
|{
27| return typeof(return)(move(ror));
|}
|
|///
|@safe nothrow @nogc version(mir_test) unittest
|{
| import mir.algorithm.iteration: equal;
|
| static a =
| [
| [ 1, 4, 7, 8 ],
| [ 1, 7 ],
| [ 1, 7, 8],
| [ 4 ],
| [ 7 ],
| ];
| static witness = [
| 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8
| ];
1| assert(a.multiwayMerge.equal(witness));
|
| static b =
| [
| // range with duplicates
| [ 1, 1, 4, 7, 8 ],
| [ 7 ],
| [ 1, 7, 8],
| [ 4 ],
| [ 7 ],
| ];
| // duplicates are propagated to the resulting range
1| assert(b.multiwayMerge.equal(witness));
|}
|
|/**
|Computes the union of multiple ranges. The input ranges are passed
|as a range of ranges and each is assumed to be sorted by $(D
|less). Computation is done lazily, one union element at a time.
|`multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`.
|"The output of multiwayUnion has no duplicates even when its inputs contain duplicates."
|Params:
| less = Predicate the given ranges are sorted by.
| ror = A range of ranges sorted by `less` to compute the intersection for.
|Returns:
| A range of the union of the ranges in `ror`.
|See also: $(LREF multiwayMerge)
| */
|auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
|{
| import mir.functional: not;
| import mir.algorithm.iteration : Uniq;
|
25| return Uniq!(not!less, typeof(multiwayMerge!less(ror)))(multiwayMerge!less(move(ror)));
|}
|
|///
|@safe version(mir_test) unittest
|{
| import mir.algorithm.iteration: equal;
|
| // sets
1| double[][] a =
| [
| [ 1, 4, 7, 8 ],
| [ 1, 7 ],
| [ 1, 7, 8],
| [ 4 ],
| [ 7 ],
| ];
|
1| auto witness = [1, 4, 7, 8];
1| assert(a.multiwayUnion.equal(witness));
|
| // multisets
1| double[][] b =
| [
| [ 1, 1, 1, 4, 7, 8 ],
| [ 1, 7 ],
| [ 1, 7, 7, 8],
| [ 4 ],
| [ 7 ],
| ];
1| assert(b.multiwayUnion.equal(witness));
|}
|
|/++
|Computes the length of union of multiple ranges. The input ranges are passed
|as a range of ranges and each is assumed to be sorted by `less`.
|
|Params:
| less = Predicate the given ranges are sorted by.
| ror = A range of ranges sorted by `less` to compute the intersection for.
|Returns:
| A length of the union of the ranges in `ror`.
|+/
|pragma(inline, false)
|size_t unionLength(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
|{
17| size_t length;
17| auto u = move(ror).multiwayUnion!less;
17| if (!u.empty) do {
99| length++;
99| u.popFront;
99| } while(!u.empty);
17| return length;
|}
source/mir/algorithm/setops.d is 91% covered
<<<<<< EOF
# path=source-mir-annotated.lst
|/++
|$(H1 Annotated value)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki
|Macros:
|+/
|module mir.annotated;
|
|import mir.internal.meta: basicElementType;
|import mir.serde: serdeRegister, serdeAnnotation, serdeIsDynamicAlgebraic;
|
|static immutable excMsg = "At least one annotation is required to create an annotated value.";
|version (D_Exceptions)
| static immutable exc = new Exception(excMsg);
|
|/++
|A convenience definition of an annotated value.
|
|A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member.
|+/
|@serdeRegister
|@serdeAnnotation
|struct Annotated(T) {
| ///
| @serdeAnnotation
| string[] annotations;
|
| static if (!(is(T == union) || is(T == struct)))
| private enum _alloc_ = false;
| else
| static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic"))
| private enum _alloc_ = true;
| else
| {
| import mir.algebraic: isVariant;
| static if (isVariant!T)
| private enum _alloc_ = true;
| else
| private enum _alloc_ = false;
| }
|
| static if (_alloc_)
| {
| ///
| private T* _value;
| ///
| ref inout(T) value() inout @property
6| in(_value)
| {
6| return *_value;
| }
|
| ///
| ref T value(T value) @property return scope
| {
0000000| if (_value is null)
| {
0000000| _value = new T;
| import core.lifetime: move;
0000000| *_value = move(value);
| }
0000000| return *_value;
| }
|
| ///
| bool opEquals(scope const Annotated rhs) scope const
| {
0000000| return annotations == rhs.annotations && value == rhs.value;
| }
|
| /// ditto
| bool opEquals(ref scope const Annotated rhs) scope const
| {
4| return annotations == rhs.annotations && value == rhs.value;
| }
|
| size_t toHash() scope @trusted const pure nothrow @nogc
| {
| static if (__traits(compiles, hashOf(value)))
0000000| return hashOf(value);
| else
| {
| debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof);
| return cast(size_t)_value;
| }
| }
| }
| else
| {
| ///
| T value;
| }
|
|
| /++
| Params:
| annotations = non-empty array of annotations
| args = arguments to construct value with
| +/
5| this(Args...)(string[] annotations, Args args) @safe pure {
5| if (annotations.length == 0)
| {
| version (D_Exceptions)
0000000| throw exc;
| else
| assert(0, excMsg);
| }
| import core.lifetime: forward;
5| this.annotations = annotations;
| static if (_alloc_)
3| this._value = new T(forward!args);
| else
| static if (__traits(compiles, value = args))
| this.value = args;
| else
| static if (is(T == class))
| this.value = new T(forward!args);
| else
2| this.value = T(forward!args);
| }
|
| private alias E = .basicElementType!T;
|
| import std.traits: isAssociativeArray, isAggregateType;
| ///
| int opCmp()(ref scope const typeof(this) rhs) scope const pure nothrow @nogc
| if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
| {
0000000| if (auto d = __cmp(annotations, rhs.annotations))
0000000| return d;
|
| static if (__traits(compiles, __cmp(value, rhs.value)))
| return __cmp(value, rhs.value);
| else
| static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U))
0000000| return value.opCmp(rhs.value);
| else
| return value < rhs.value ? -1 : value > rhs.value ? +1 : 0;
| }
|}
|
|///
|version(mir_test)
|unittest
|{
1| auto annotations = ["annotation"];
| static struct S {double x;}
1| auto as = Annotated!S(annotations, 5);
1| assert(as.annotations == annotations);
1| assert(as.value.x == 5);
|
| static struct C {double x;}
1| auto ac = Annotated!S(annotations, 5);
1| assert(ac.annotations == annotations);
1| assert(ac.value.x == 5);
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.algebraic;
1| auto annotations = ["annotation"];
| static struct S {double x;}
1| auto as = Annotated!(Variant!S)(annotations, 5);
1| assert(as.annotations == annotations);
1| assert(as.value.x == 5);
|
| static struct C {double x;}
1| auto ac = Annotated!(Variant!S)(annotations, 5);
1| assert(ac.annotations == annotations);
1| assert(ac.value.x == 5);
|}
|
|/++
|A convenience definition of an annotated value.
|
|A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member.
|+/
|@serdeRegister
|@serdeAnnotation
|struct AnnotatedOnce(T) {
| ///
| @serdeAnnotation
| string annotation;
|
| static if (!(is(T == union) || is(T == struct)))
| private enum _alloc_ = false;
| else
| static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic"))
| private enum _alloc_ = true;
| else
| {
| import mir.algebraic: isVariant;
| static if (isVariant!T)
| private enum _alloc_ = true;
| else
| private enum _alloc_ = false;
| }
|
| static if (_alloc_)
| {
| ///
| private T* _value;
| ///
| ref inout(T) value() inout @property
| {
2| return *_value;
| }
|
| ///
| ref T value(T value) @property
| {
0000000| if (_value is null)
| {
0000000| _value = new T;
| import core.lifetime: move;
0000000| *_value = move(value);
| }
0000000| return *_value;
| }
|
| ///
| bool opEquals(scope const AnnotatedOnce rhs) scope const
| {
0000000| return annotation == rhs.annotation && value == rhs.value;
| }
|
| ///
| bool opEquals(ref scope const AnnotatedOnce rhs) scope const
| {
0000000| return annotation == rhs.annotation && value == rhs.value;
| }
| }
| else
| {
| ///
| T value;
| }
|
|
| /++
| Params:
| annotation = non-empty array of annotation
| args = arguments to construct value with
| +/
4| this(Args...)(string annotation, Args args) @safe pure {
| import core.lifetime: forward;
4| this.annotation = annotation;
| static if (_alloc_)
2| this._value = new T(forward!args);
| else
| static if (__traits(compiles, value = args))
| this.value = args;
| else
| static if (is(T == class))
| this.value = new T(forward!args);
| else
2| this.value = T(forward!args);
| }
|
| private alias E = .basicElementType!T;
|
| import std.traits: isAssociativeArray, isAggregateType;
| static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
| ///
| int opCmp()(ref scope const typeof(this) rhs) scope const @safe pure nothrow @nogc
| {
| if (auto d = __cmp(annotation, rhs.annotation))
| return d;
|
| static if (__traits(compiles, __cmp(value, rhs.value)))
| return __cmp(value, rhs.value);
| else
| static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U))
| return value.opCmp(rhs.value);
| else
| return value < rhs.value ? -1 : value > rhs.value ? +1 : 0;
| }
|}
|
|///
|version(mir_test)
|unittest
|{
1| auto annotation = "annotation";
| static struct S {double x;}
1| auto as = AnnotatedOnce!S(annotation, 5);
1| assert(as.annotation == annotation);
1| assert(as.value.x == 5);
|
| static struct C {double x;}
1| auto ac = AnnotatedOnce!S(annotation, 5);
1| assert(ac.annotation == annotation);
1| assert(ac.value.x == 5);
|}
|
|///
|version(mir_test)
|unittest
|{
| import mir.algebraic;
1| auto annotation = "annotation";
| static struct S {double x;}
1| auto as = AnnotatedOnce!(Variant!S)(annotation, 5);
1| assert(as.annotation == annotation);
1| assert(as.value.x == 5);
|
| static struct C {double x;}
1| auto ac = AnnotatedOnce!(Variant!S)(annotation, 5);
1| assert(ac.annotation == annotation);
1| assert(ac.value.x == 5);
|}
source/mir/annotated.d is 71% covered
<<<<<< EOF
# path=source-mir-appender.lst
|/++
|$(H1 Scoped Buffer)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Ilia Ki
|+/
|module mir.appender;
|
|// import std.traits: isAssignable, hasElaborateDestructorhasElaborateCopyConstructor, hasElaborateAssign;
|import mir.conv: _mir_destroy = xdestroy;
|
|private extern(C) @system nothrow @nogc pure void* memcpy(scope void* s1, scope const void* s2, size_t n);
|
|
|/++
|The buffer uses stack memory and C Runtime to allocate temporal memory.
|
|Shouldn't store references to GC allocated data.
|+/
|struct ScopedBuffer(T, size_t bytes = 4096)
| if (bytes && T.sizeof <= bytes)
|{
| import std.traits: Unqual, isMutable, isIterable, hasElaborateAssign, isAssignable, isArray;
| import mir.primitives: hasLength;
| import mir.conv: emplaceRef;
|
| private enum size_t _bufferLength = bytes / T.sizeof + (bytes % T.sizeof != 0);
| private T[] _buffer;
| size_t _currentLength;
|
| version (mir_secure_memory)
| private align(T.alignof) ubyte[_bufferLength * T.sizeof] _scopeBufferPayload;
| else
| private align(T.alignof) ubyte[_bufferLength * T.sizeof] _scopeBufferPayload = void;
|
| private ref inout(T[_bufferLength]) _scopeBuffer() inout @trusted scope
| {
90368| return *cast(inout(T[_bufferLength])*)&_scopeBufferPayload;
| }
|
| /// Reserve `n` more elements.
| void reserve(size_t n) @safe scope
| {
1| prepare(n);
1| _currentLength -= n;
| }
|
| /// Return a slice to `n` more elements.
| T[] prepare(size_t n) @trusted scope
| {
| import mir.internal.memory: realloc, malloc;
46277| _currentLength += n;
46277| if (_buffer.length == 0)
| {
29825| if (_currentLength <= _bufferLength)
| {
29788| return _scopeBuffer[0 .. _currentLength];
| }
| else
| {
37| const newLen = _currentLength << 1;
37| if (auto p = malloc(T.sizeof * newLen))
| {
37| _buffer = (cast(T*)p)[0 .. newLen];
| }
| else assert(0);
| version (mir_secure_memory)
| {
| (cast(ubyte[])_buffer)[] = 0;
| }
37| memcpy(cast(void*)_buffer.ptr, _scopeBuffer.ptr, T.sizeof * (_currentLength - n));
| }
| }
| else
16452| if (_currentLength > _buffer.length)
| {
35| const newLen = _currentLength << 1;
35| if (auto p = realloc(cast(void*)_buffer.ptr, T.sizeof * newLen))
| {
35| _buffer = (cast(T*)p)[0 .. newLen];
| }
| else assert(0);
| version (mir_secure_memory)
| {
| (cast(ubyte[])_buffer[_currentLength .. $])[] = 0;
| }
| }
16489| return _buffer[0 .. _currentLength];
| }
|
| static if (isAssignable!(T, const T))
| private alias R = const T;
| else
| private alias R = T;
|
| /// Copy constructor is enabled only if `T` is mutable type without eleborate assign.
| static if (isMutable!T && !hasElaborateAssign!T)
| this(this)
| {
| import mir.internal.memory: malloc;
140| if (_buffer.ptr)
| {
0000000| typeof(_buffer) buffer;
0000000| if (auto p = malloc(T.sizeof * _buffer.length))
| {
0000000| buffer = (cast(T*)p)[0 .. T.sizeof * _buffer.length];
| }
| else assert(0);
| version (mir_secure_memory)
| {
| (cast(ubyte[])buffer)[] = 0;
| }
0000000| buffer[0 .. _currentLength] = _buffer[0 .. _currentLength];
0000000| _buffer = buffer;
| }
| }
| else
| @disable this(this);
|
| ///
| ~this()
| {
| import mir.internal.memory: free;
816| data._mir_destroy;
| version(mir_secure_memory)
| _currentLength = 0;
1669| (() @trusted { if (_buffer.ptr) free(cast(void*)_buffer.ptr); })();
| }
|
| ///
| void shrinkTo(size_t length)
| {
45798| assert(length <= _currentLength);
45798| data[length .. _currentLength]._mir_destroy;
45798| _currentLength = length;
| }
|
| ///
| size_t length() scope const @property
| {
48| return _currentLength;
| }
|
| ///
| void popBackN(size_t n)
| {
4| sizediff_t t = _currentLength - n;
4| if (t < 0)
| assert(0, "ScopedBffer.popBackN: n is too large.");
4| data[t .. _currentLength]._mir_destroy;
4| _currentLength = t;
| }
|
| ///
| void put(T e) @safe scope
| {
45849| auto cl = _currentLength;
45849| auto d = prepare(1);
| static if (isMutable!T)
| {
| import core.lifetime: moveEmplace;
91686| ()@trusted{moveEmplace(e, d[cl]);}();
| }
| else
| {
6| emplaceRef!(Unqual!T)(d[cl], e);
| }
| }
|
| static if (T.sizeof > 8 || hasElaborateAssign!T)
| ///
| void put(ref R e) scope
| {
0000000| auto cl = _currentLength;
0000000| auto d = prepare(1);
0000000| emplaceRef!(Unqual!T)(d[cl], e);
| }
|
| static if (!hasElaborateAssign!T)
| ///
| void put(scope R[] e) scope
| {
427| auto cl = _currentLength;
427| auto d = prepare(e.length);
427| if (!__ctfe)
854| (()@trusted=>memcpy(cast(void*)(d.ptr + cl), e.ptr, e.length * T.sizeof))();
| else
| static if (isMutable!T)
0000000| (()@trusted=> d[cl .. cl + e.length] = e)();
| else
| assert(0);
| }
|
| ///
| void put(Iterable)(Iterable range) scope
| if (isIterable!Iterable && !__traits(isStaticArray, Iterable) && (!isArray!Iterable || hasElaborateAssign!T))
| {
| static if (hasLength!Iterable)
| {
| auto cl = _currentLength;
| auto d = prepare(range.length);
| foreach(ref e; range)
| emplaceRef!(Unqual!T)(d[cl++], e);
| assert(_currentLength == cl);
| }
| else
| {
| foreach(ref e; range)
| put(e);
| }
| }
|
| ///
| alias opOpAssign(string op : "~") = put;
|
| ///
| void reset() scope nothrow
| {
209| this.__dtor;
209| _currentLength = 0;
209| _buffer = null;
| }
|
| ///
| void initialize() @system scope nothrow @nogc
| {
88| _currentLength = 0;
88| _buffer = null;
| }
|
| ///
| inout(T)[] data() inout @property @trusted scope return
| {
187042| return _buffer.length ? _buffer[0 .. _currentLength] : _scopeBuffer[0 .. _currentLength];
| }
|
| /++
| Copies data into an array of the same length using `memcpy` C routine.
| Shrinks the length to `0`.
| +/
| void moveDataAndEmplaceTo(T[] array) @system
| in {
7| assert(array.length == _currentLength);
| }
| do {
7| memcpy(cast(void*)array.ptr, data.ptr, _currentLength * T.sizeof);
7| _currentLength = 0;
| }
|}
|
|/// ditto
|auto scopedBuffer(T, size_t bytes = 4096)() @trusted
|{
67| ScopedBuffer!(T, bytes) buffer = void;
67| buffer.initialize;
67| return buffer;
|}
|
|///
|@safe pure nothrow @nogc
|version (mir_test) unittest
|{
2| auto buf = scopedBuffer!char;
1| buf.put('c');
1| buf.put("str");
1| assert(buf.data == "cstr");
|
1| buf.popBackN(2);
1| assert(buf.data == "cs");
|}
|
|/// immutable
|@safe pure nothrow @nogc
|version (mir_test) unittest
|{
2| auto buf = scopedBuffer!(immutable char);
1| buf.put('c');
1| buf.put("str");
1| assert(buf.data == "cstr");
|
1| buf.popBackN(2);
1| assert(buf.data == "cs");
|}
|
|@safe pure nothrow @nogc
|version (mir_test) unittest
|{
2| auto buf = scopedBuffer!(char, 3);
1| buf.put('c');
1| buf.put("str");
1| assert(buf.data == "cstr");
|
1| buf.popBackN(2);
1| assert(buf.data == "cs");
|}
|
|@safe pure nothrow @nogc
|version (mir_test) unittest
|{
| alias T = char;
1| const n = 3;
|
2| auto buf = scopedBuffer!(T, n * T.sizeof);
1| assert(buf._scopeBuffer.length == n); // stack
1| assert(buf._buffer.length == 0); // unset
|
1| buf.reserve(n + 1); // transition to heap
1| assert(buf._buffer.length >= n + 1); // heap
|
1| buf ~= 'c';
1| buf ~= "str";
1| assert(buf.data == "cstr");
|
1| buf.popBackN(2);
1| assert(buf.data == "cs");
|}
|
|///
|struct UnsafeArrayBuffer(T)
|{
| import std.traits: isImplicitlyConvertible;
|
| ///
| T[] buffer;
| ///
| size_t length;
|
| ///
| void put(T a)
| {
| import core.lifetime: move;
489| assert(length < buffer.length);
489| buffer[length++] = move(a);
| }
|
| static if (isImplicitlyConvertible!(const T, T))
| private alias E = const T;
| else
| private alias E = T;
|
| ///
| void put(E[] a)
| {
| import core.lifetime: move;
390| assert(buffer.length >= a.length + length);
390| buffer[length .. length + a.length] = a;
390| length += a.length;
| }
|
| ///
| inout(T)[] data() inout @property @safe scope
| {
136| return buffer[0 .. length];
| }
|
| ///
| void popBackN(size_t n)
| {
1| sizediff_t t = length - n;
1| if (t < 0)
| assert(0, "UnsafeBuffer.popBackN: n is too large.");
1| buffer[t .. length]._mir_destroy;
1| length = t;
| }
|}
|
|///
|@safe pure nothrow @nogc
|version (mir_test) unittest
|{
1| char[4] array;
1| auto buf = UnsafeArrayBuffer!char(array);
1| buf.put('c');
1| buf.put("str");
1| assert(buf.data == "cstr");
|
1| buf.popBackN(2);
1| assert(buf.data == "cs");
|}
|
|version(mir_bignum_test) // for DIP1000
|@safe pure nothrow
|unittest
|{
| import mir.conv: to;
| import mir.algebraic : Algebraic;
| static struct S
| {
| @safe pure nothrow @nogc:
| @property string toString() scope const
| {
1| return "_";
| }
| }
1| Algebraic!(int, string, double) x;
1| x = 42;
1| auto s = x.to!string;
1| assert(s == "42");
1| x = "abc";
1| assert(x.to!string == "abc");
1| x = 42.0;
1| assert(x.to!string == "42.0");
1| Algebraic!S y;
1| y = S();
1| assert(y.to!string == "_");
|}
source/mir/appender.d is 92% 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 Ilia Ki, 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;
|
44| 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)
| {
34| auto length = r.length;
34| if (length == 0)
0000000| return null;
|
| import mir.conv : emplaceRef;
| import std.array: uninitializedArray;
|
68| auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))();
|
| static if (isInputRange!Range)
| {
15681| foreach(ref e; result)
| {
5194| emplaceRef!E(e, r.front);
5102| 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 .. $];
| }
| }
|
68| return (() @trusted => cast(E[]) result)();
| }
| else
| {
| import std.array: std_appender = appender;
|
10| auto a = std_appender!(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 a.data;
| }
|}
|
|///
|@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() scope const @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=source-mir-base64.lst
|/++
|$(H1 @nogc Simple Base64 parsing)
|
|License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
|Authors: Harrison Ford
|Copyright: 2021 Harrison Ford, Symmetry Investments
|+/
|module mir.base64;
|
|package static immutable base64DecodeInvalidCharMsg = "base64: Invalid character encountered.";
|package static immutable base64DecodeInvalidLenMsg = "Cannot decode a buffer with given length (not a multiple of 4, missing padding?)";
|version(D_Exceptions) {
| package static immutable base64DecodeInvalidCharException = new Exception(base64DecodeInvalidCharMsg);
| package static immutable base64DecodeInvalidLenException = new Exception(base64DecodeInvalidLenMsg);
|}
|
|// NOTE: I do not know if this would work on big-endian systems.
|// Needs further testing to figure out if it *does* work on them.
|
|// Technique borrowed from http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html#branchless-code-for-lookup-table
|private char lookup_encoding(ubyte i, char plusChar = '+', char slashChar = '/') @safe @nogc pure {
270| assert(i < 64);
|
270| ubyte shift;
|
270| if (i < 26)
| {
| // range A-Z
158| shift = 'A';
| }
224| else if (i >= 26 && i < 52)
| {
| // range a-z
90| shift = 'a' - 26;
| }
44| else if (i >= 52 && i < 62)
| {
| // range 0-9
18| shift = cast(ubyte)('0' - 52);
| }
4| else if (i == 62)
| {
| // character plus
2| shift = cast(ubyte)(plusChar - 62);
| }
2| else if (i == 63)
| {
| // character slash
2| shift = cast(ubyte)(slashChar - 63);
| }
|
270| return cast(char)(i + shift);
|}
|
|// Do the inverse of above (convert an ASCII value into the Base64 character set)
|private ubyte lookup_decoding(char i, char plusChar = '+', char slashChar = '/') @safe @nogc pure
|{
| // Branching bad, but this isn't performance sensitive
514| if (i <= 'Z' && i >= 'A') {
187| return cast(ubyte)(i - 'A');
| }
234| else if (i <= 'z' && i >= 'a') {
94| return cast(ubyte)(i - 'a' + 26);
| }
42| else if (i <= '9' && i >= '0') {
15| return cast(ubyte)(i - '0' + 52);
| }
8| else if (i == plusChar) {
2| return 62;
| }
6| else if (i == slashChar) {
1| return 63;
| }
| // Just return 0 for padding,
| // as it typically means nothing.
5| else if (i == '=') {
4| return 0;
| }
| else {
| version(D_Exceptions) {
1| throw base64DecodeInvalidCharException;
| } else {
| assert(0, base64DecodeInvalidCharMsg);
| }
| }
|
|}
|
|/++
|Decode a Base64 encoded value, returning the buffer.
|+/
|ubyte[] decodeBase64(scope const(char)[] data, char plusChar = '+', char slashChar = '/') @safe pure
|{
| import mir.appender : scopedBuffer;
44| auto app = scopedBuffer!ubyte;
22| decodeBase64(data, app, plusChar, slashChar);
10| return app.data.dup;
|}
|
|/++
|Decode a Base64 encoded value, placing the result onto an Appender.
|+/
|void decodeBase64(Appender)(scope const(char)[] data,
| scope ref Appender appender,
| char plusChar = '+',
| char slashChar = '/') @safe pure
|{
| // We expect data should be well-formed (with padding),
| // so we should throw if it is not well-formed.
22| if (data.length % 4 != 0)
| {
| version(D_Exceptions) {
2| throw base64DecodeInvalidLenException;
| } else {
| assert(0, base64DecodeInvalidLenMsg);
| }
| }
|
20| ubyte[3] decodedByteGroup;
20| ubyte sz = 0;
|
| // We can't use mir.ndslice.chunk.chunks here, as it violates
| // the scope requirements.
186| for (size_t i = 0; i < data.length; i += 4)
| {
83| auto group = data[i .. (i + 4)];
|
83| ubyte[4] decodedBytes;
83| decodedBytes[0] = lookup_decoding(group[0], plusChar, slashChar);
82| decodedBytes[1] = lookup_decoding(group[1], plusChar, slashChar);
|
82| uint transformed_group = (decodedBytes[0] << 26) | (decodedBytes[1] << 20);
|
| // According to RFC4648 Section 3.3, we don't have to accept extra padding characters,
| // and we can safely throw (and stay within spec).
| // x=== is also invalid, so we can just throw on that here.
162| if (group[0] == '=' || group[1] == '=')
| {
| version(D_Exceptions)
3| throw base64DecodeInvalidCharException;
| else
| assert(0, base64DecodeInvalidCharMsg);
| }
|
| // xx=(=)?
79| if (group[2] == '=')
| {
| // If we are not at the end of a string, according to RFC4648,
| // we can safely treat a padding character as "non-alphabet data",
| // and as such, we should throw. See RFC4648 Section 3.3 for more information
7| if ((i / 4) != ((data.length / 4) - 1))
| {
| version(D_Exceptions)
4| throw base64DecodeInvalidCharException;
| else
| assert(0, base64DecodeInvalidCharMsg);
| }
|
3| if (group[3] == '=')
| {
| // xx==
2| sz = 1;
| }
| // xx=x (invalid)
| // Padding should not be in the middle of a chunk
| else
| {
| version(D_Exceptions)
1| throw base64DecodeInvalidCharException;
| else
| assert(0, base64DecodeInvalidCharMsg);
| }
| }
| // xxx=
72| else if (group[3] == '=')
| {
| // If we are not at the end of a string, according to RFC4648,
| // we can safely treat a padding character as "non-alphabet data",
| // and as such, we should throw. See RFC4648 Section 3.3 for more information
4| if ((i / 4) != ((data.length / 4) - 1))
| {
| version(D_Exceptions)
1| throw base64DecodeInvalidCharException;
| else
| assert(0, base64DecodeInvalidCharMsg);
| }
|
3| decodedBytes[2] = lookup_decoding(group[2], plusChar, slashChar);
3| transformed_group |= (decodedBytes[2] << 14);
3| sz = 2;
| }
| // xxxx
| else
| {
68| decodedBytes[2] = lookup_decoding(group[2], plusChar, slashChar);
68| decodedBytes[3] = lookup_decoding(group[3], plusChar, slashChar);
68| transformed_group |= ((decodedBytes[2] << 14) | (decodedBytes[3] << 8));
68| sz = 3;
| }
|
73| decodedByteGroup[0] = (transformed_group >> 24) & 0xff;
73| decodedByteGroup[1] = (transformed_group >> 16) & 0xff;
73| decodedByteGroup[2] = (transformed_group >> 8) & 0xff;
|
| // Only emit the transformed bytes that we got data for.
73| appender.put(decodedByteGroup[0 .. sz]);
| }
|}
|
|/// Test decoding of data which has a length which can be
|/// cleanly decoded.
|version(mir_test)
|@safe pure unittest
|{
| {
| enum data = "QUJD";
1| assert(data.decodeBase64 == "ABC");
| }
|
| {
| enum data = "QQ==";
1| assert(data.decodeBase64 == "A");
| }
|
| {
| enum data = "YSBiIGMgZCBlIGYgZyBoIGkgaiBrIGwgbSBuIG8gcCBxIHIgcyB0IHUgdiB3IHggeSB6";
1| assert(data.decodeBase64 == "a b c d e f g h i j k l m n o p q r s t u v w x y z");
| }
|
| {
| enum data = "LCAuIDsgLyBbICcgXSBcID0gLSAwIDkgOCA3IDYgNSA0IDMgMiAxIGAgfiAhIEAgIyAkICUgXiAmICogKCApIF8gKyB8IDogPCA+ID8=";
1| assert(data.decodeBase64 == ", . ; / [ ' ] \\ = - 0 9 8 7 6 5 4 3 2 1 ` ~ ! @ # $ % ^ & * ( ) _ + | : < > ?");
| }
|
| {
| enum data = "AAA=";
1| assert(data.decodeBase64 == "\x00\x00");
| }
|
| {
| enum data = "AAAABBCC";
1| assert(data.decodeBase64 == "\x00\x00\x00\x04\x10\x82");
| }
|
| {
| enum data = "AA==";
1| assert(data.decodeBase64 == "\x00");
| }
|
| {
| enum data = "AA/=";
1| assert(data.decodeBase64 == "\x00\x0f");
| }
|}
|
|/// Test decoding invalid data
|version(mir_test)
|@safe pure unittest
|{
| void testFail(const(char)[] input) @safe pure
| {
12| bool thrown = false;
| try {
12| ubyte[] decoded = input.decodeBase64;
| } catch (Exception t) {
12| thrown = true;
| }
|
12| assert(thrown);
| }
|
1| testFail("===A");
1| testFail("A=");
1| testFail("AA=");
1| testFail("A=AA");
1| testFail("AA=A");
1| testFail("AA=A====");
1| testFail("=AAA");
1| testFail("AAA=QUJD");
| // This fails because we don't allow extra padding (than what is necessary)
1| testFail("AA======");
| // This fails because we don't allow padding before the end of the string (otherwise we'd have a side-channel)
1| testFail("QU==QUJD");
1| testFail("QU======QUJD");
| // Invalid data that's out of the alphabet
1| testFail("!@##@@!@");
|}
|
|/++
|Encode a ubyte array as Base64, returning the encoded value.
|+/
|string encodeBase64(scope const(ubyte)[] buf, char plusChar = '+', char slashChar = '/') @safe pure
|{
| import mir.appender : scopedBuffer;
22| auto app = scopedBuffer!char;
11| encodeBase64(buf, app, plusChar, slashChar);
11| return app.data.idup;
|}
|
|/++
|Encode a ubyte array as Base64, placing the result onto an Appender.
|+/
|void encodeBase64(Appender)(scope const(ubyte)[] input,
| scope ref Appender appender,
| char plusChar = '+',
| char slashChar = '/') @safe pure
|{
| import core.bitop : bswap;
| import mir.ndslice.topology : bytegroup, map;
| // Slice our input array so that n % 3 == 0 (we have a multiple of 3)
| // If we have less then 3, then this is effectively a no-op (will result in a 0-length slice)
13| char[4] encodedByteGroup;
13| const(ubyte)[] window = input[0 .. input.length - (input.length % 3)];
218| foreach(group; window.bytegroup!(3, uint).map!bswap) {
64| const(ubyte) a = (group >> 26) & 0x3f;
64| const(ubyte) b = (group >> 20) & 0x3f;
64| const(ubyte) c = (group >> 14) & 0x3f;
64| const(ubyte) d = (group >> 8) & 0x3f;
|
64| encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
64| encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
64| encodedByteGroup[2] = c.lookup_encoding(plusChar, slashChar);
64| encodedByteGroup[3] = d.lookup_encoding(plusChar, slashChar);
64| appender.put(encodedByteGroup[]);
| }
|
| // If it's a clean multiple of 3, then it requires no padding.
| // If not, then we need to add padding.
13| if (input.length % 3 != 0)
| {
6| window = input[window.length .. input.length];
|
6| uint group = (window[0] << 24);
|
6| if (window.length == 1) {
4| const(ubyte) a = (group >> 26) & 0x3f;
4| const(ubyte) b = (group >> 20) & 0x3f;
4| encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
4| encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
4| encodedByteGroup[2] = '=';
4| encodedByteGroup[3] = '=';
| }
| else {
| // Just in case
2| assert(window.length == 2);
|
2| group |= (window[1] << 16);
2| const(ubyte) a = (group >> 26) & 0x3f;
2| const(ubyte) b = (group >> 20) & 0x3f;
2| const(ubyte) c = (group >> 14) & 0x3f;
2| encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
2| encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
2| encodedByteGroup[2] = c.lookup_encoding(plusChar, slashChar);
2| encodedByteGroup[3] = '=';
| }
|
6| appender.put(encodedByteGroup[]);
| }
|}
|
|/// Test encoding of data which has a length that can be cleanly
|/// encoded.
|version(mir_test)
|@safe pure unittest
|{
| // 3 bytes
| {
| enum data = cast(immutable(ubyte)[])"ABC";
1| assert(data.encodeBase64 == "QUJD");
| }
|
| // 6 bytes
| {
| enum data = cast(immutable(ubyte)[])"ABCDEF";
1| assert(data.encodeBase64 == "QUJDREVG");
| }
|
| // 9 bytes
| {
| enum data = cast(immutable(ubyte)[])"ABCDEFGHI";
1| assert(data.encodeBase64 == "QUJDREVGR0hJ");
| }
|
| // 12 bytes
| {
| enum data = cast(immutable(ubyte)[])"ABCDEFGHIJKL";
1| assert(data.encodeBase64 == "QUJDREVGR0hJSktM");
| }
|}
|
|/// Test encoding of data which has a length which CANNOT be cleanly encoded.
|/// This typically means that there's padding.
|version(mir_test)
|@safe pure unittest
|{
| // 1 byte
| {
| enum data = cast(immutable(ubyte)[])"A";
1| assert(data.encodeBase64 == "QQ==");
| }
| // 2 bytes
| {
| enum data = cast(immutable(ubyte)[])"AB";
1| assert(data.encodeBase64 == "QUI=");
| }
| // 2 bytes
| {
| enum data = [0xFF, 0xFF];
1| assert(data.encodeBase64 == "//8=");
| }
| // 4 bytes
| {
| enum data = [0xDE, 0xAD, 0xBA, 0xBE];
1| assert(data.encodeBase64 == "3q26vg==");
| }
| // 37 bytes
| {
| enum data = cast(immutable(ubyte)[])"A Very Very Very Very Large Test Blob";
1| assert(data.encodeBase64 == "QSBWZXJ5IFZlcnkgVmVyeSBWZXJ5IExhcmdlIFRlc3QgQmxvYg==");
| }
|}
|
|/// Test nogc encoding
|version(mir_test)
|@safe pure @nogc unittest
|{
| import mir.appender : scopedBuffer;
|
| {
| enum data = cast(immutable(ubyte)[])"A Very Very Very Very Large Test Blob";
2| auto appender = scopedBuffer!char();
1| data.encodeBase64(appender);
1| assert(appender.data == "QSBWZXJ5IFZlcnkgVmVyeSBWZXJ5IExhcmdlIFRlc3QgQmxvYg==");
| }
|
| {
| enum data = cast(immutable(ubyte)[])"abc123!?$*&()'-=@~";
2| auto appender = scopedBuffer!char();
1| data.encodeBase64(appender);
1| assert(appender.data == "YWJjMTIzIT8kKiYoKSctPUB+");
| }
|}
|
|/// Make sure we can decode what we encode.
|version(mir_test)
|@safe pure unittest
|{
| // Test an example string
| {
| enum data = cast(immutable(ubyte)[])"abc123!?$*&()'-=@~";
1| assert(data.encodeBase64.decodeBase64 == data);
| }
| // Test an example from Ion data
| {
| enum data = cast(immutable(ubyte)[])"a b c d e f g h i j k l m n o p q r s t u v w x y z";
1| assert(data.encodeBase64.decodeBase64 == data);
| }
|}
|
source/mir/base64.d is 100% covered
<<<<<< 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.parse: DecimalExponentKey;
|import mir.bignum.low_level_view: ceilLog10Exp2;
|
|private enum expBufferLength = 2 + ceilLog10Exp2(ulong.sizeof * 8);
|private static immutable C[9] zerosImpl(C) = "0.00000.0";
|
|/++
|Stack-allocated decimal type.
|Params:
| size64 = count of 64bit words in coefficient
|+/
|@serdeScoped @serdeProxy!(const(char)[])
|struct Decimal(uint size64)
| if (size64 && size64 <= ushort.max)
|{
| import mir.format: NumericSpec;
| import mir.bignum.integer;
| import mir.bignum.low_level_view;
| import std.traits: isMutable, isFloatingPoint;
|
| ///
| long exponent;
| ///
| BigInt!size64 coefficient;
|
| ///
| void toString(C = char, W)(ref scope W w, NumericSpec spec = NumericSpec.init) const scope
| if(isSomeChar!C && isMutable!C)
| {
150| assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human);
| import mir.utility: _expect;
| // handle special values
75| 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;
| }
|
67| C[coefficientBufferLength + 16] buffer0 = void;
67| auto buffer = buffer0[0 .. $ - 16];
|
67| 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);
| data[i * 2 + 0] = l;
| data[i * 2 + 1] = h;
| }
| auto work = BigUIntView!uint(data);
| work = work.topLeastSignificantPart(coefficient.length * 2).normalized;
| coefficientLength = work.toStringImpl(buffer);
| }
| else
| {
| BigInt!size64 work = coefficient;
| coefficientLength = work.view.unsigned.toStringImpl(buffer);
| }
| }
| else
| {
67| BigInt!size64 work = coefficient;
67| coefficientLength = work.view.unsigned.toStringImpl(buffer);
| }
|
134| C[1] sign = coefficient.sign ? "-" : "+";
120| bool addSign = coefficient.sign || spec.plus;
67| long s = this.exponent + coefficientLength;
|
| alias zeros = zerosImpl!C;
|
67| if (spec.format == NumericSpec.Format.human)
| {
67| 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
67| 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 .. cast(sizediff_t)(-s + 2)]);
30| w.put(buffer[$ - coefficientLength .. $]);
30| return;
| }
| }
| else
30| 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[($ - (cast(sizediff_t)this.exponent + 2)) .. $]);
8| return;
| }
| }
| else
| {
8| if (s <= 12)
| {
7| buffer0[$ - 16 .. $] = '0';
7| putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + cast(sizediff_t)this.exponent]);
7| w.put(zeros[$ - 2 .. $]);
7| return;
| }
| }
| }
| else
| {
| ///dddddd.0
12| if (!spec.separatorChar)
| {
| ///dddddd.d....
9| if (s <= 6 || coefficientLength <= 6)
| {
7| buffer[$ - coefficientLength - 1] = sign[0];
7| w.put(buffer[$ - coefficientLength - addSign .. $ - coefficientLength + cast(sizediff_t)s]);
| T2:
11| buffer[$ - coefficientLength + cast(sizediff_t)s - 1] = '.';
11| w.put(buffer[$ - coefficientLength + cast(sizediff_t)s - 1 .. $]);
11| return;
| }
| }
| else
| {
4| if (s <= 12 || coefficientLength <= 12)
| {
4| putL(buffer[$ - coefficientLength .. $ - coefficientLength + cast(sizediff_t)s]);
4| goto T2;
| }
| }
| }
| }
|
11| assert(coefficientLength);
|
11| long 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 (exponent.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 .. $]);
| }
|
|@safe:
|
| ///
| DecimalView!size_t view() return scope
| {
1| return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
| }
|
| /// ditto
| DecimalView!(const size_t) view() const return scope
| {
159| 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!" ~ size64.stringof ~ " from string `", str , "`");
| }
| else
| {
| static immutable exception = new Exception("Can't parse Decimal!" ~ size64.stringof ~ ".");
0000000| throw exception;
| }
| }
|
| /++
| 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.
| +/
74| this(T)(const T x)
| if (isFloatingPoint!T && size64 >= 1 + (T.mant_dig >= 64))
| {
| import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
74| this = genericBinaryToDecimal(x);
| }
|
| ///
| ref opAssign(uint rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return
| if (rhsMaxSize64 < size64)
| {
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,
| ) @trusted
| 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;
|
6| auto buffer = stringBuf;
3| assert(thousandsSeparator != fractionSeparator);
9| if (str.length && (str[0] == '+' || str[0] == '-'))
| {
0000000| buffer.put(cast(char)str[0]);
0000000| str = str[1 .. $];
| }
28| auto integer = str[0 .. $ - str.find!(a => a == fractionSeparator)];
3| if (integer.length % 4 == 0)
0000000| return false;
27| foreach_reverse (chunk; integer.sliced.retro.chunks(4))
| {
7| auto s = chunk.retro.field;
7| if (s.length == 4)
| {
4| if (s[0] != thousandsSeparator)
0000000| return false;
4| s = s[1 .. $];
| }
| do
| {
38| if (s[0] < '0' || s[0] > '9')
0000000| return false;
19| buffer.put(cast(char)s[0]);
19| s = s[1 .. $];
| }
19| while(s.length);
| }
3| if (str.length > integer.length)
| {
2| buffer.put('.');
2| str = str[integer.length + 1 .. $];
2| if (str.length == 0)
0000000| return false;
| do
| {
6| buffer.put(cast(char)str[0]);
6| str = str[1 .. $];
| }
6| while(str.length);
| }
3| return fromStringImpl!(char,
| allowSpecialValues,
| false, // allowDotOnBounds
| false, // allowDExponent
| allowStartingPlus,
| false, // allowUnderscores
| allowLeadingZeros, // allowLeadingZeros
| false, // allowExponent
| false, // checkEmpty
| )(buffer.data, key, exponentShift);
| }
|
| /++
| 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)
| {
| import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
195| auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
195| auto ret = work.fromStringImpl!(C,
| allowSpecialValues,
| allowDotOnBounds,
| allowDExponent,
| allowStartingPlus,
| allowUnderscores,
| allowLeadingZeros,
| allowExponent,
| checkEmpty,
| )(str, key, exponentShift);
195| coefficient.length = cast(uint) work.coefficient.coefficients.length;
195| coefficient.sign = work.sign;
195| exponent = work.exponent;
195| return ret;
| }
|
| 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 scope @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;
| }
|
| /++
| 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 = true)() scope const
| if (isFloatingPoint!T && isMutable!T)
| {
159| return view.opCast!(T, wordNormalized);
| }
|
| ///
| bool isNaN() scope const @property
| {
0000000| return exponent == exponent.max && coefficient.length;
| }
|
| ///
| bool isInfinity() scope const @property
| {
0000000| return exponent == exponent.max && coefficient.length == 0;
| }
|
| ///
| bool isSpecial() scope const @property
| {
0000000| return exponent == exponent.max;
| }
|
| ///
| ref opOpAssign(string op, size_t rhsMaxSize64)(ref const Decimal!rhsMaxSize64 rhs) @safe pure return
| if (op == "+" || op == "-")
| {
| import mir.utility: max;
22| BigInt!(max(rhsMaxSize64, size64, 256u)) rhsCopy = void;
22| BigIntView!(const size_t) rhsView;
22| auto expDiff = cast(sizediff_t) (exponent - rhs.exponent);
22| if (expDiff >= 0)
| {
12| exponent = rhs.exponent;
12| coefficient.mulPow5(expDiff);
12| coefficient.opOpAssign!"<<"(expDiff);
12| rhsView = rhs.coefficient.view;
| }
| else
| {
10| rhsCopy.copyFrom(rhs.coefficient.coefficients, rhs.coefficient.sign);
10| rhsCopy.mulPow5(-expDiff);
10| rhsCopy.opOpAssign!"<<"(-expDiff);
10| rhsView = rhsCopy.view;
| }
22| coefficient.opOpAssign!op(rhsView);
22| return this;
| }
|}
|
|///
|version(mir_bignum_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.test: should;
| import mir.conv: to;
1| Decimal!128 decimal = void;
1| DecimalExponentKey key;
|
1| assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
1| decimal.opCast!double.should == 0x1.596bf8ce7631ep-32;
1| key.should == 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);
|}
|
|///
|version(mir_bignum_test) @safe pure @nogc unittest
|{
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");
|}
|
|/// 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| auto buffer = stringBuf;
1| buffer << decimal;
1| assert(buffer.data == str);
|}
|
|///
|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);
1| assert(i.coefficient.sign);
|}
|
|///
|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");
|}
|
|///
|version(mir_bignum_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.test: should;
|
| import mir.conv: to;
1| Decimal!3 decimal;
1| DecimalExponentKey key;
|
| // Check precise percentate parsing
1| assert(decimal.fromStringImpl("71.7", key, -2));
1| key.should == DecimalExponentKey.dot;
| // The result is exact value instead of 0.7170000000000001 = 71.7 / 100
1| (cast(double) decimal).should == 0.717;
|
1| assert(decimal.fromStringImpl("+0.334e-5"w, key));
1| key.should == DecimalExponentKey.e;
1| (cast(double) decimal).should == 0.334e-5;
|
1| assert(decimal.fromStringImpl("100_000_000"w, key));
1| key.should == DecimalExponentKey.none;
1| (cast(double) decimal).should == 1e8;
|
1| assert(decimal.fromStringImpl("-334D-5"d, key));
1| key.should == DecimalExponentKey.D;
1| (cast(double) decimal).should == -334e-5;
|
1| assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
1| key.should == DecimalExponentKey.none;
1| (cast(double) decimal).should == 2482734692817364218734682973648217364981273648923423.0;
|
1| assert(decimal.fromStringImpl(".023", key));
1| key.should == DecimalExponentKey.dot;
1| (cast(double) decimal).should == .023;
|
1| assert(decimal.fromStringImpl("0E100", key));
1| key.should == DecimalExponentKey.E;
1| (cast(double) decimal).should == 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| key.should == DecimalExponentKey.nan;
3| auto nan = cast(double) decimal;
3| (cast(double) decimal).should == double.nan;
| }
|
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| (cast(double) decimal).should == 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| should(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));
|}
|
|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);
|
| /++ Test that Issue #365 is handled properly +/
1| assert(decimal.fromStringImpl("123456.e0", key));
1| assert(key == DecimalExponentKey.e);
1| assert(cast(double) decimal == 123_456.0);
|
1| assert(decimal.fromStringImpl("123_456.e0", key));
1| assert(key == DecimalExponentKey.e);
1| assert(cast(double) decimal == 123_456.0);
|
1| assert(decimal.fromStringImpl("123456.E0", key));
1| assert(key == DecimalExponentKey.E);
1| assert(cast(double) decimal == 123_456.0);
|
1| assert(decimal.fromStringImpl("123_456.E0", key));
1| assert(key == DecimalExponentKey.E);
1| assert(cast(double) decimal == 123_456.0);
|
1| assert(decimal.fromStringImpl("123456.d0", key));
1| assert(key == DecimalExponentKey.d);
1| assert(cast(double) decimal == 123_456.0);
|
1| assert(decimal.fromStringImpl("123_456.d0", key));
1| assert(key == DecimalExponentKey.d);
1| assert(cast(double) decimal == 123_456.0);
|
1| assert(decimal.fromStringImpl("123456.D0", key));
1| assert(key == DecimalExponentKey.D);
1| assert(cast(double) decimal == 123_456.0);
|
1| assert(decimal.fromStringImpl("123_456.D0", key));
1| assert(key == DecimalExponentKey.D);
1| assert(cast(double) decimal == 123_456.0);
|
| /++ Test invalid examples with the fix introduced for Issue #365 +/
1| assert(!decimal.fromStringImpl("123_456_.D0", key));
1| assert(!decimal.fromStringImpl("123_456.DD0", key));
1| assert(!decimal.fromStringImpl("123_456_.E0", key));
1| assert(!decimal.fromStringImpl("123_456.EE0", key));
1| assert(!decimal.fromStringImpl("123456.ED0", key));
1| assert(!decimal.fromStringImpl("123456E0D0", key));
1| assert(!decimal.fromStringImpl("123456._D0", key));
1| assert(!decimal.fromStringImpl("123456_.D0", key));
1| assert(!decimal.fromStringImpl("123456.E0D0", key));
1| assert(!decimal.fromStringImpl("123456.D0_", key));
1| assert(!decimal.fromStringImpl("123456_", key));
|
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));
|}
|
|///
|version(mir_bignum_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);
|}
|
|
|///
|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);
|}
|
|///
|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);
|}
source/mir/bignum/decimal.d is 96% covered
<<<<<< EOF
# path=source-mir-bignum-fixed.lst
|/++
|Note:
| The module doesn't provide full arithmetic API for now.
|+/
|module mir.bignum.fixed;
|
|import std.traits;
|import mir.bitop;
|import mir.utility;
|
|/++
|Fixed-length unsigned integer.
|
|Params:
| size = size in bits
|+/
|struct UInt(size_t size)
| if (size % (size_t.sizeof * 8) == 0 && size >= size_t.sizeof * 8)
|{
| import mir.bignum.fixed: UInt;
| /++
| Payload. The data is located in the target endianness.
| +/
| size_t[size / (size_t.sizeof * 8)] data;
|
| ///
12932| this(size_t N)(auto ref const size_t[N] data)
| if (N && N <= this.data.length)
| {
| version(LittleEndian)
12932| this.data[0 .. N] = data;
| else
| this.data[$ - N .. $] = data;
| }
|
| ///
6334| this(size_t argSize)(auto ref const UInt!argSize arg)
| if (argSize <= size)
| {
6334| this(arg.data);
| }
|
| static if (size_t.sizeof == uint.sizeof && data.length % 2 == 0)
| ///
335| this()(auto ref const ulong[data.length / 2] data)
| {
335| if (!__ctfe)
| {
335| this.data = cast(typeof(this.data)) data;
| }
| else
| {
| version(LittleEndian)
| {
| static foreach (i; 0 .. data.length)
| {
0000000| this.data[i * 2 + 0] = cast(uint) data[i];
0000000| this.data[i * 2 + 1] = cast(uint) (data[i] >> 32);
| }
| }
| else
| {
| static foreach (i; 0 .. data.length)
| {
| this.data[i * 2 + 1] = cast(uint) data[i];
| this.data[i * 2 + 0] = cast(uint) (data[i] >> 32);
| }
| }
| }
| }
|
| static if (size >= 64)
| ///
4267| this(ulong data)
| {
| static if (size_t.sizeof == ulong.sizeof)
| {
| this.data[0] = data;
| }
| else
| {
4267| this.data[0] = cast(uint) data;
4267| this.data[1] = cast(uint) (data >> 32);
| }
| }
|
| static if (size < 64)
| ///
0000000| this(uint data)
| {
0000000| this.data[0] = data;
| }
|
| ///
| this(C)(scope const(C)[] str) @safe pure @nogc
| if (isSomeChar!C)
| {
3| if (fromStringImpl(str))
3| return;
| static if (__traits(compiles, () @nogc { throw new Exception("Can't parse UInt."); }))
| {
| import mir.exception: MirException;
| throw new MirException("Can't parse UInt!" ~ size.stringof ~ " from string `", str , "`.");
| }
| else
| {
| static immutable exception = new Exception("Can't parse UInt!" ~ size.stringof ~ ".");
0000000| throw exception;
| }
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test) @safe pure @nogc unittest
| {
| import mir.math.constant: PI;
1| UInt!256 integer = "34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
1| assert(integer == UInt!256.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8"));
| }
|
| /++
| 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)
| {
| import mir.bignum.low_level_view: BigUIntView;
3| return BigUIntView!size_t(data[]).fromStringImpl(str);
| }
|
| ///
| immutable(C)[] toString(C = char)() scope const @safe pure nothrow
| if(isSomeChar!C && isMutable!C)
| {
2| UInt!size copy = this;
2| auto work = copy.view.normalized;
| import mir.bignum.low_level_view: ceilLog10Exp2;
2| C[ceilLog10Exp2(data.length * (size_t.sizeof * 8))] buffer = void;
2| return buffer[$ - work.toStringImpl(buffer) .. $].idup;
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test) @safe pure unittest
| {
1| auto str = "34010447314490204552169750449563978034784726557588085989975288830070948234680";
1| auto integer = UInt!256(str);
1| assert(integer.toString == str);
|
1| integer = UInt!256.init;
1| assert(integer.toString == "0");
| }
|
| ///
| void toString(C = char, W)(ref scope W w) scope const
| if(isSomeChar!C && isMutable!C)
| {
1| UInt!size copy = this;
1| auto work = copy.view.normalized;
| import mir.bignum.low_level_view: ceilLog10Exp2;
1| C[ceilLog10Exp2(data.length * (size_t.sizeof * 8))] buffer = void;
1| w.put(buffer[$ - work.toStringImpl(buffer) .. $]);
| }
|
| static if (size == 128)
| /// Check @nogc toString impl
| version(mir_bignum_test) @safe pure @nogc unittest
| {
| import mir.format: stringBuf;
1| auto str = "34010447314490204552169750449563978034784726557588085989975288830070948234680";
1| auto integer = UInt!256(str);
2| auto buffer = stringBuf;
1| buffer << integer;
1| assert(buffer.data == str);
| }
|
| ///
| enum UInt!size max = ((){UInt!size ret; ret.data = size_t.max; return ret;})();
|
| ///
| enum UInt!size min = UInt!size.init;
|
| import mir.bignum.low_level_view: BigUIntView;
|
| ///
| BigUIntView!size_t view() @property pure nothrow @nogc scope return @safe
| {
38260| return BigUIntView!size_t(data);
| }
|
| ///
| BigUIntView!(const size_t) view() const @property pure nothrow @nogc scope return @safe
| {
604| return BigUIntView!(const size_t)(data);
| }
|
| ///
| static UInt!size fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
| {
93| typeof(return) ret;
93| if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
93| return ret;
| version(D_Exceptions)
| {
| import mir.bignum.low_level_view: hexStringException;
0000000| throw hexStringException;
| }
| else
| {
| import mir.bignum.low_level_view: hexStringErrorMsg;
| assert(0, hexStringErrorMsg);
| }
| }
|
| /++
| +/
| bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
| @safe pure @nogc nothrow
| if (isSomeChar!C)
| {
93| return view.fromHexStringImpl!(C, allowUnderscores)(str);
| }
|
| ///
| static UInt!size fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str)
| {
| typeof(return) ret;
| if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
| return ret;
| version(D_Exceptions)
| {
| import mir.bignum.low_level_view: binaryStringException;
| throw binaryStringException;
| }
| else
| {
| import mir.bignum.low_level_view: binaryStringErrorMsg;
| assert(0, binaryStringErrorMsg);
| }
| }
|
| /++
| +/
| bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
| @safe pure @nogc nothrow
| if (isSomeChar!C)
| {
| return view.fromBinaryStringImpl!(C, allowUnderscores)(str);
| }
|
| /++
| +/
| auto opEquals(size_t rhsSize)(auto ref const UInt!rhsSize rhs) const
| {
| static if (rhsSize == size)
2358| return this.data == rhs.data;
| else
| static if (rhsSize > size)
1| return this.toSize!rhsSize.data == rhs.data;
| else
0000000| return this.data == rhs.toSize!size.data;
| }
|
| static if (size >= 64)
| /// ditto
| auto opEquals(ulong rhs) const
| {
1105| return opEquals(UInt!size(rhs));
| }
| else
| auto opEquals(uint rhs) const
| {
0000000| return opEquals(UInt!size(rhs));
| }
|
| /++
| +/
| auto opCmp(UInt!size rhs) const
| {
3201| foreach_reverse(i; 0 .. data.length)
| {
962| if (this.data[i] < rhs.data[i])
408| return -1;
554| if (this.data[i] > rhs.data[i])
211| return +1;
| }
13| return 0;
| }
|
| static if (size >= 64)
| /// ditto
| auto opCmp(ulong rhs) const scope
| {
4| return opCmp(UInt!size(rhs));
| }
| else
| auto opCmp(uint rhs) const scope
| {
0000000| return opCmp(UInt!size(rhs));
| }
|
| static if (size >= 64)
| /++
| +/
| ref UInt!size opAssign(ulong rhs) scope return
| @safe pure nothrow @nogc
| {
1| this.data = UInt!size(rhs).data;
1| return this;
| }
| else
| ///
| ref UInt!size opAssign(uint rhs) scope return
| @safe pure nothrow @nogc
| {
0000000| this.data = UInt!size(rhs).data;
0000000| return this;
| }
|
| /++
| +/
| ref UInt!size opAssign(uint rhsSize)(UInt!rhsSize rhs) scope return
| @safe pure nothrow @nogc
| {
6333| this.data = UInt!size(rhs).data;
6333| return this;
| }
|
| /++
| `bool overflow = a += b ` and `bool overflow = a -= b` operations.
| +/
| bool opOpAssign(string op)(UInt!size rhs, bool overflow = false)
| @safe pure nothrow @nogc scope
| if (op == "+" || op == "-")
| {
2628| return view.opOpAssign!op(rhs.view, overflow);
| }
|
| /// ditto
| bool opOpAssign(string op)(size_t rhs)
| @safe pure nothrow @nogc scope
| if (op == "+" || op == "-")
| {
104| return view.opOpAssign!op(rhs);
| }
|
| static if (size_t.sizeof < ulong.sizeof)
| /// ditto
| bool opOpAssign(string op)(ulong rhs)
| @safe pure nothrow @nogc scope
| if (op == "+" || op == "-")
| {
483| return opOpAssign!op(UInt!size(rhs));
| }
|
| /// ditto
| bool opOpAssign(string op, uint rsize)(UInt!rsize rhs, bool overflow = false)
| @safe pure nothrow @nogc scope
| if ((op == "+" || op == "-") && rsize < size)
| {
6| return opOpAssign!op(rhs.toSize!size, overflow);
| }
|
| /++
| Returns: overflow value of multiplication
| +/
| size_t opOpAssign(string op : "*")(size_t rhs, size_t carry = 0)
| @safe pure nothrow @nogc scope
| {
| return view.opOpAssign!op(rhs, carry);
| }
|
| static if (size_t.sizeof == 4)
| /// ditto
| auto opOpAssign(string op : "*")(ulong rhs)
| @safe pure nothrow @nogc scope
| {
3760| return opOpAssign!op(UInt!64(rhs));
| }
|
|
| /++
| Returns: overflow value of multiplication
| +/
| void opOpAssign(string op : "*", size_t rhsSize)(UInt!rhsSize rhs)
| @safe pure nothrow @nogc scope
| if (rhsSize <= size)
| {
1899| this = extendedMul(this, rhs).toSize!size;
| }
|
| /++
| 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 scope
| {
| assert(overflow < rhs);
| auto work = view.normalized;
| if (worl.coefficients.length)
| return work.opOpAssign!op(rhs, overflow);
| return overflow;
| }
|
| /++
| Performs division & extracts the remainder.
| Params:
| rhs = unsigned value to divide by
| Returns: quotient, sets `rhs` to remainder
| +/
| ref divMod(size_t rhsSize)(scope ref UInt!rhsSize rhs)
| @safe pure nothrow @nogc scope return
| {
| import mir.bignum.internal.kernel: divMod, divisionRequiredBuffSize;
|
7| UInt!size quotient;
|
7| auto dividendV = this.view;
7| auto divisorV = rhs.view;
7| divisorV = divisorV.normalized;
7| dividendV = dividendV.normalized;
|
| import mir.utility: min;
| enum vlen = min(rhs.data.length, data.length);
7| size_t[divisionRequiredBuffSize(data.length, vlen)] buffer = void;
|
7| divMod(
| quotient.data,
| divisorV.coefficients,
| dividendV.coefficients,
| divisorV.coefficients,
| buffer);
7| this = quotient;
7| return this;
| }
|
| /++
| Performs `big /= rhs` operation.
| Params:
| rhs = unsigned value to divide by
| Returns:
| quotient from division
| +/
| ref opOpAssign(string op : "/", size_t rhsSize)(UInt!rhsSize rhs)
| @safe pure nothrow @nogc scope return
| {
3| return this.divMod(rhs);
| }
|
| /// ditto
| ref opOpAssign(string op : "/")(ulong rhs)
| @safe pure nothrow @nogc scope return
| {
1| return opOpAssign!(op, ulong.sizeof * 8)(UInt!(ulong.sizeof * 8)(rhs));
| }
|
| /++
| Performs `big %= rhs` operation.
| Params:
| rhs = unsigned value to divide by
| Returns:
| remainder from division
| +/
| ref opOpAssign(string op : "%", size_t rhsSize)(UInt!rhsSize rhs)
| @safe pure nothrow @nogc scope return
| {
4| this.divMod(rhs);
4| this = cast(UInt!size)rhs;
| }
|
| /// ditto
| ref opOpAssign(string op : "%")(ulong rhs)
| @safe pure nothrow @nogc scope
| {
4| return opOpAssign!(op, ulong.sizeof * 8)(UInt!(ulong.sizeof * 8)(rhs));
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314");
1| auto b = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
1| assert(a / b == UInt!128.fromHexString("1"));
1| assert(a % b == UInt!128.fromHexString("36920c8e407c9645d0b611586c0a077"));
| }
|
| ///
| ref UInt!size opOpAssign(string op)(UInt!size rhs) nothrow return
| if (op == "^" || op == "|" || op == "&")
| {
| static foreach (i; 0 .. data.length)
| mixin(`data[i] ` ~ op ~ `= rhs.data[i];`);
527| return this;
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
1| auto b = UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314");
1| assert((a.opBinary!"|"(b)) == UInt!128.fromHexString("ffbffbeffd1affaf75adfff5abf0a39d"));
| }
|
| ///
| ref UInt!size opOpAssign(string op)(size_t rhs) nothrow return scope
| if (op == "^" || op == "|" || op == "&")
| {
| mixin(`view.coefficients[0] ` ~ op ~ `= rhs;`);
| return this;
| }
|
| static if (size_t.sizeof < ulong.sizeof)
| /// ditto
| ref opOpAssign(string op)(ulong rhs) return
| @safe pure nothrow @nogc scope
| if (op == "^" || op == "|" || op == "&")
| {
78| return opOpAssign!op(UInt!size(rhs));
| }
|
| ///
| ref UInt!size opOpAssign(string op)(size_t shift)
| @safe pure nothrow @nogc return
| if (op == "<<" || op == ">>")
| {
7336| auto d = view.coefficients;
7336| assert(shift < size);
7336| auto index = shift / (size_t.sizeof * 8);
7336| auto bs = shift % (size_t.sizeof * 8);
7336| auto ss = size_t.sizeof * 8 - bs;
| static if (op == ">>")
| {
6829| if (bs)
| {
27390| foreach (j; 0 .. data.length - (index + 1))
| {
5585| d[j] = (d[j + index] >>> bs) | (d[j + (index + 1)] << ss);
| }
| }
| else
| {
22944| foreach (j; 0 .. data.length - (index + 1))
| {
4364| d[j] = d[j + index];
| }
| }
6829| d[$ - (index + 1)] = d[$ - 1] >>> bs;
46803| foreach (j; data.length - index .. data.length)
| {
8772| d[j] = 0;
| }
| }
| else
| {
507| if (bs)
| {
1890| foreach_reverse (j; index + 1 .. data.length)
| {
207| d[j] = (d[j - index] << bs) | (d[j - (index + 1)] >> ss);
| }
| }
| else
| {
117| foreach_reverse (j; index + 1 .. data.length)
| {
36| d[j] = d[j - index];
| }
| }
507| d[index] = d[0] << bs;
2289| foreach_reverse (j; 0 .. index)
| {
384| d[j] = 0;
| }
| }
7336| return this;
| }
|
| /++
| `auto c = a << b` operation.
| +/
| UInt!size opBinary(string op)(size_t rhs)
| const @safe pure nothrow @nogc
| if (op == "<<" || op == ">>>" || op == ">>")
| {
6753| UInt!size ret = this;
6753| ret.opOpAssign!op(rhs);
6753| return ret;
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1| assert(a << 0 == a);
1| assert(a << 4 == UInt!128.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
1| assert(a << 68 == UInt!128.fromHexString("4a1de7022b0029d00000000000000000"));
1| assert(a << 127 == UInt!128.fromHexString("80000000000000000000000000000000"));
1| assert(a >> 0 == a);
1| assert(a >> 4 == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
1| assert(a >> 68 == UInt!128.fromHexString("afbbfae3cd0aff2"));
1| assert(a >> 127 == UInt!128(1));
| }
|
| /++
| Binary operations
| +/
| template opBinary(string op)
| if (op == "^" || op == "|" || op == "&" || op == "+" || op == "-" || op == "*" || op == "/" || op == "%")
| {
| ///
| UInt!size opBinary(size_t rsize)(UInt!rsize rhs)
| const @safe pure nothrow @nogc
| if (rsize <= size)
| {
2445| UInt!size ret = this;
2445| ret.opOpAssign!op(rhs);
2445| return ret;
| }
|
| /// ditto
| UInt!size opBinary(ulong rhs)
| const @safe pure nothrow @nogc
| {
2444| UInt!size ret = this;
2444| ret.opOpAssign!op(rhs);
2444| return ret;
| }
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1| assert(a / UInt!128.fromHexString("5") == UInt!128.fromHexString("23259893f5ceffd49db9f949a0899a1f"));
1| assert(a == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
1| assert(a % UInt!128.fromHexString("5") == UInt!128.fromHexString("2"));
1| assert(a == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
|
1| assert(a / 5 == UInt!128.fromHexString("23259893f5ceffd49db9f949a0899a1f"));
1| assert(a % 5 == UInt!64.fromHexString("2"));
1| assert(a % 5 == 2);
| }
|
| /// ditto
| template opBinaryRight(string op)
| if (op == "^" || op == "|" || op == "&" || op == "+" || op == "*")
| {
| ///
| UInt!size opBinaryRight(size_t lsize)(UInt!lsize lhs)
| const @safe pure nothrow @nogc
| if (lsize < size)
| {
| UInt!size ret = this;
| ret.opOpAssign!op(lhs);
| return ret;
| }
|
| /// ditto
| UInt!size opBinaryRight(ulong lhs)
| const @safe pure nothrow @nogc
| {
0000000| UInt!size ret = this;
0000000| ret.opOpAssign!op(lhs);
0000000| return ret;
| }
| }
|
| /++
| Shifts left using at most `size_t.sizeof * 8 - 1` bits
| +/
| UInt!size smallLeftShift(uint shift) const
| {
360| assert(shift < size_t.sizeof * 8);
360| UInt!size ret = this;
360| if (shift)
| {
345| auto csh = size_t.sizeof * 8 - shift;
| static foreach_reverse (i; 1 .. data.length)
| {
1079| ret.data[i] = (ret.data[i] << shift) | (ret.data[i - 1] >>> csh);
| }
345| ret.data[0] = ret.data[0] << shift;
| }
360| return ret;
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1| assert(a.smallLeftShift(4) == UInt!128.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
| }
|
| /++
| Shifts right using at most `size_t.sizeof * 8 - 1` bits
| +/
| UInt!size smallRightShift(uint shift) const
| {
1| assert(shift < size_t.sizeof * 8);
1| UInt!size ret = this;
1| if (shift)
| {
1| auto csh = size_t.sizeof * 8 - shift;
| static foreach (i; 0 .. data.length - 1)
| {
3| ret.data[i] = (ret.data[i] >>> shift) | (ret.data[i + 1] << csh);
| }
1| ret.data[$ - 1] = ret.data[$ - 1] >>> shift;
| }
1| return ret;
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1| assert(a.smallRightShift(4) == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
| }
|
| /++
| +/
| T opCast(T)() const
| if (is(Unqual!T == bool))
| {
| static foreach (i; 0 .. data.length)
| {
1001| if (data[i])
573| return true;
| }
4| return false;
| }
|
| /++
| +/
| T opCast(T)() const
| if (is(Unqual!T == ulong))
| {
| static if (size_t.sizeof == ulong.sizeof)
| {
| return data[0];
| }
| else
| {
587| return data[0] | (ulong(data[1]) << 32);
| }
| }
|
| /++
| +/
| T opCast(T)() const @safe pure nothrow @nogc
| if (is(T == UInt!newSize, uint newSize))
| {
| enum newLength = typeof(return).data.length;
| static if (newLength <= data.length)
| {
| return typeof(return)(data[0 .. newLength]);
| }
| else
| {
2| typeof(return) ret;
2| ret.data[0 .. data.length] = data;
2| return ret;
| }
| }
|
| /++
| +/
| T opCast(T)() const
| if (is(Unqual!T == uint))
| {
1010| return cast(uint) data[0];
| }
|
| /++
| Returns:
| the number with shrinked or extended size.
| +/
| UInt!newSize toSize(size_t newSize, bool lowerBits = true)()
| const @safe pure @nogc nothrow
| {
20774| typeof(return) ret;
| import mir.utility: min;
| enum N = min(ret.data.length, data.length);
| static if (lowerBits)
| {
20689| ret.data[0 .. N] = data[0 .. N];
| }
| else
| {
85| ret.data[0 .. N] = data[$ - N .. $];
| }
20774| return ret;
| }
|
| ///
| UInt!(size + additionalRightBits) rightExtend(size_t additionalRightBits)()
| const @safe pure @nogc nothrow
| {
| static if (additionalRightBits)
| {
4| typeof(return) ret;
| version (BigEndian)
| ret.data[0 .. data.length] = data;
| else
4| ret.data[$ - data.length .. $] = data;
4| return ret;
| }
| else
| {
1| return this;
| }
| }
|
| /++
| +/
| bool bt(size_t position) const
| @safe pure nothrow @nogc
| {
330| assert(position < data.sizeof * 8);
330| return view.bt(position);
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
1| assert(a.bt(127) == 1);
1| assert(a.bt(126) == 0);
1| assert(a.bt(125) == 1);
1| assert(a.bt(124) == 0);
1| assert(a.bt(0) == 1);
1| assert(a.bt(1) == 0);
1| assert(a.bt(2) == 1);
1| assert(a.bt(3) == 1);
| }
|
| /++
| +/
| size_t ctlz() const scope @property
| @safe pure nothrow @nogc
| {
271| return view.ctlz;
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
1| assert (a.ctlz == 0);
1| a = UInt!128.init;
1| assert (a.ctlz == 128);
1| a = UInt!128.fromHexString("3");
1| assert (a.ctlz == 126);
| }
|
| /++
| +/
| size_t cttz() const @property
| @safe pure nothrow @nogc
| {
3| return view.cttz;
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| @safe pure @nogc
| unittest
| {
1| auto a = UInt!128.fromHexString("d");
1| assert (a.cttz == 0);
1| a = UInt!128.init;
1| assert (a.cttz == 128);
1| a = UInt!128.fromHexString("300000000000000000");
1| assert (a.cttz == 68);
| }
|
| /++
| +/
| bool signBit() const @property
| {
386| return data[$ - 1] >> (size_t.sizeof * 8 - 1);
| }
|
| /// ditto
| void signBit(bool value) @property
| {
| enum signMask = ptrdiff_t.max;
2| data[$ - 1] = (data[$ - 1] & ptrdiff_t.max) | (size_t(value) << (size_t.sizeof * 8 - 1));
| }
|
| static if (size == 128)
| ///
| version(mir_bignum_test)
| unittest
| {
1| auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
1| assert(a.signBit);
1| a.signBit = false;
1| assert(a == UInt!128.fromHexString("5fbbfae3cd0aff2714a1de7022b0029d"));
1| assert(!a.signBit);
1| a.signBit = true;
1| assert(a == UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"));
| }
|}
|
|/++
|+/
|UInt!sizeB extendedMulHigh(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
| @safe pure nothrow @nogc
|{
2974| return (extendedMul(a, b) >> sizeA).toSize!sizeB;
|}
|
|/++
|+/
|UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
| @safe pure nothrow @nogc
|{
5542| UInt!(sizeA + sizeB) ret;
| enum al = a.data.length;
| enum alp1 = a.data.length + 1;
5542| ret.data[0 .. alp1] = extendedMul(a, b.data[0]).data;
| static foreach ( i; 1 .. b.data.length)
6596| ret.data[i .. i + alp1] = extendedMulAdd(a, b.data[i], UInt!sizeA(ret.data[i .. i + al])).data;
5542| return ret;
|}
|
|/// ditto
|UInt!(size + size_t.sizeof * 8)
| extendedMul(size_t size)(UInt!size a, size_t b)
| @safe pure nothrow @nogc
|{
12138| size_t overflow = a.view *= b;
12138| auto ret = a.toSize!(size + size_t.sizeof * 8);
12138| ret.data[$ - 1] = overflow;
12138| return ret;
|}
|
|/// ditto
|auto extendedMul()(ulong a, ulong b)
| @safe pure nothrow @nogc
|{
| static if (size_t.sizeof == ulong.sizeof)
| {
| import mir.utility: extMul;
| auto e = extMul(a, b);
| return UInt!128([e.low, e.high]);
| }
| else
| {
2| return extendedMul(UInt!64(a), UInt!64(b));
| }
|}
|
|/// ditto
|auto extendedMul()(uint a, uint b)
| @safe pure nothrow @nogc
|{
| static if (size_t.sizeof == uint.sizeof)
| {
| import mir.utility: extMul;
2| auto e = extMul(a, b);
| version(LittleEndian)
2| return UInt!64([e.low, e.high]);
| else
| return UInt!64([e.high, e.low]);
| }
| else
| {
| return UInt!64([ulong(a) * b]);
| }
|}
|
|///
|version(mir_bignum_test)
|@safe pure @nogc
|unittest
|{
1| auto a = UInt!128.max;
1| auto b = UInt!256.max;
1| auto c = UInt!384.max;
1| assert(extendedMul(a, a) == UInt!256.max - UInt!128.max - UInt!128.max);
1| assert(extendedMul(a, b) == UInt!384.max - UInt!128.max - UInt!256.max);
1| assert(extendedMul(b, a) == UInt!384.max - UInt!128.max - UInt!256.max);
|
1| a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
1| b = UInt!256.fromHexString("3fe48f2dc8aad570d037bc9b323fc0cfa312fcc2f63cb521bd8a4ca6157ef619");
1| c = UInt!384.fromHexString("37d7034b86e8d58a9fc564463fcedef9e2ad1126dd2c0f803e61c72852a9917ef74fa749e7936a9e4e224aeeaff91f55");
1| assert(extendedMul(a, b) == c);
1| assert(extendedMul(b, a) == c);
|
1| a = UInt!128.fromHexString("23edf5ff44ee3a4feafc652607aa1eb9");
1| b = UInt!256.fromHexString("d3d79144b8941fb50c9102e3251bacb112c88b71ad3f85a970a31458ce24297b");
1| c = UInt!384.fromHexString("1dbb62fe6ca5fed101068eda7222d6a9857633ecdfed37a2d156ff6309065ecc633f31465727677a93a7acbd1dac63e3");
1| assert(extendedMul(a, b) == c);
1| assert(extendedMul(b, a) == c);
|}
|
|/// ulong
|version(mir_bignum_test)
|@safe pure @nogc
|unittest
|{
1| ulong a = 0xdfbbfae3cd0aff27;
1| ulong b = 0x14a1de7022b0029d;
1| auto c = UInt!128.fromHexString("120827399968ea2a2db185d16e8cc8eb");
1| assert(extendedMul(a, b) == c);
1| assert(extendedMul(b, a) == c);
|}
|
|/// uint
|version(mir_bignum_test)
|@safe pure @nogc
|unittest
|{
1| uint a = 0xdfbbfae3;
1| uint b = 0xcd0aff27;
1| auto c = UInt!64.fromHexString("b333243de8695595");
1| assert(extendedMul(a, b) == c);
1| assert(extendedMul(b, a) == c);
|}
|
|/++
|+/
|UInt!(size + size_t.sizeof * 8)
| extendedMulAdd(size_t size)(UInt!size a, size_t b, UInt!size c)
|{
6596| auto ret = extendedMul(a, b);
6596| auto view = ret.view;
6596| view.coefficients[$ - 1] += view.topLeastSignificantPart(a.data.length) += c.view;
6596| return ret;
|}
source/mir/bignum/fixed.d is 94% covered
<<<<<< EOF
# path=source-mir-bignum-fp.lst
|/++
|Note:
| The module doesn't provide full arithmetic API for now.
|+/
|module mir.bignum.fp;
|
|import mir.bitop;
|import mir.utility;
|import std.traits;
|
|package enum half(uint hs) = (){
| import mir.bignum.fixed: UInt;
| UInt!hs ret; ret.signBit = true; return ret;
|}();
|
|/++
|Software floating point number.
|
|Params:
| size = coefficient size in bits
|+/
|struct Fp(uint size)
| if (size % (uint.sizeof * 8) == 0 && size >= (uint.sizeof * 8))
|{
| import mir.bignum.fixed: UInt;
|
| bool sign;
| long exponent;
| UInt!size coefficient;
|
| /++
| +/
| nothrow
315| this(bool sign, long exponent, UInt!size normalizedCoefficient)
| {
315| this.coefficient = normalizedCoefficient;
315| this.exponent = exponent;
315| 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.
| +/
81| this(T)(const T value, bool normalize = true)
| @safe pure nothrow @nogc
| if (isFloatingPoint!T && T.mant_dig <= size)
| {
| import mir.math.common : fabs;
| import mir.math.ieee : frexp, signbit, ldexp;
87| this.sign = value.signbit != 0;
87| if (value == 0)
1| return;
86| T x = value.fabs;
86| if (_expect(!(x < T.infinity), false))
| {
5| this.exponent = this.exponent.max;
5| this.coefficient = x != T.infinity;
5| return;
| }
81| int exp;
| {
| enum scale = T(2) ^^ T.mant_dig;
81| x = frexp(x, exp) * scale;
| }
|
| static if (T.mant_dig < 64)
| {
79| auto xx = cast(ulong)cast(long)x;
79| if (normalize)
| {
3| auto shift = ctlz(xx);
3| exp -= shift + T.mant_dig + size - 64;
3| xx <<= shift;
3| this.coefficient = UInt!64(xx).rightExtend!(size - 64);
| }
| else
| {
76| this.coefficient = xx;
| }
| }
| else
| static if (T.mant_dig == 64)
| {
2| auto xx = cast(ulong)x;
2| if (normalize)
| {
1| auto shift = ctlz(xx);
1| exp -= shift + T.mant_dig + size - 64;
1| xx <<= shift;
1| this.coefficient = UInt!64(xx).rightExtend!(size - 64);
| }
| else
| {
1| this.coefficient = xx;
| }
| }
| 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;
| auto most = ulong(high);
| auto least = cast(ulong)x;
| version(LittleEndian)
| ulong[2] pair = [least, most];
| else
| ulong[2] pair = [most, least];
|
| if (normalize)
| {
| this.coefficient = UInt!128(pair).rightExtend!(size - 128);
| auto shift = most ? ctlz(most) : ctlz(least) + 64;
| exp -= shift + T.mant_dig + size - 64 * (1 + (T.mant_dig > 64));
| this.coefficient <<= shift;
| }
| else
| {
| this.coefficient = pair;
| }
| }
81| if (!normalize)
| {
77| exp -= T.mant_dig;
77| int shift = T.min_exp - T.mant_dig - exp;
77| if (shift > 0)
| {
0000000| this.coefficient >>= shift;
0000000| exp = T.min_exp - T.mant_dig;
| }
| }
81| this.exponent = exp;
| }
|
| static if (size == 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 if (real.mant_dig <= 64)
| {
| static assert(cast(real) Fp!128(real.min_normal / 2) == real.min_normal / 2);
| static assert(cast(real) Fp!128(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 if (real.mant_dig <= 64)
| {
| 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);
| }
|
| import mir.bignum.fixed: UInt;
|
1| assert(cast(double)Fp!128(+double.infinity) == +double.infinity);
1| assert(cast(double)Fp!128(-double.infinity) == -double.infinity);
|
| import mir.math.ieee : signbit;
1| auto r = cast(double)Fp!128(-double.nan);
2| assert(r != r && r.signbit);
| }
|
| // static if (size == 128)
| // /// Without normalization
| // version(mir_bignum_test)
| // @safe pure @nogc nothrow
| // unittest
| // {
| // auto f = Fp!64(-33.0 * 2.0 ^^ -10, false);
| // assert(f.sign);
| // assert(f.exponent == -10 - (double.mant_dig - 6));
| // assert(f.coefficient == 33UL << (double.mant_dig - 6));
| // }
|
| /++
| +/
595| this(uint isize)(UInt!isize integer, bool normalizedInteger = false)
| nothrow
| {
| import mir.bignum.fixed: UInt;
| static if (isize < size)
| {
36| if (normalizedInteger)
| {
1| this(false, long(isize) - size, integer.rightExtend!(size - isize));
| }
| else
| {
35| this(integer.toSize!size, false);
| }
| }
| else
| {
559| if (!integer)
0000000| return;
559| this.exponent = isize - size;
559| if (!normalizedInteger)
| {
268| auto c = integer.ctlz;
268| integer <<= c;
268| this.exponent -= c;
| }
| static if (isize == size)
| {
265| coefficient = integer;
| }
| else
| {
| enum N = coefficient.data.length;
| version (LittleEndian)
294| coefficient.data = integer.data[$ - N .. $];
| else
| coefficient.data = integer.data[0 .. N];
| enum tailSize = isize - size;
294| auto cr = integer.toSize!tailSize.opCmp(half!tailSize);
509| if (cr > 0 || cr == 0 && coefficient.bt(0))
| {
87| if (auto overflow = coefficient += 1)
| {
1| coefficient = half!size;
1| exponent++;
| }
| }
| }
| }
| }
|
| static if (size == 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"));
| }
|
| /// ditto
258| this(ulong value)
| {
258| this(UInt!64(value));
| }
|
| ///
1| this(long value)
| {
2| this(ulong(value >= 0 ? value : -value));
1| this.sign = !(value >= 0);
| }
|
| ///
1| this(int value)
| {
1| this(long(value));
| }
|
| ///
0000000| this(uint value)
| {
0000000| this(ulong(value));
| }
|
| ///
| bool isNaN() scope const @property
| {
0000000| return this.exponent == this.exponent.max && this.coefficient != this.coefficient.init;
| }
|
| ///
| bool isInfinity() scope const @property
| {
0000000| return this.exponent == this.exponent.max && this.coefficient == coefficient.init;
| }
|
| ///
| bool isSpecial() scope const @property
| {
373| return this.exponent == this.exponent.max;
| }
|
| ///
| bool opEquals(const Fp rhs) scope const
| {
3| if (this.exponent != rhs.exponent)
0000000| return false;
3| if (this.coefficient != rhs.coefficient)
0000000| return false;
3| if (this.coefficient == 0)
2| return !this.isSpecial || this.sign == rhs.sign;
2| if (this.sign != rhs.sign)
0000000| return false;
2| return !this.isSpecial;
| }
|
| ///
| ref Fp opOpAssign(string op)(Fp rhs) nothrow scope return
| if (op == "*" || op == "/")
| {
26| this = this.opBinary!op(rhs);
26| return this;
| }
|
| ///
| Fp!(max(size, rhsSize)) opBinary(string op : "*", uint rhsSize)(Fp!rhsSize rhs) nothrow const
| {
28| return cast(Fp) .extendedMul(cast()this, rhs);
| }
|
| static if (size == 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.opBinary!"*"(b);
1| assert(fp.sign);
1| assert(fp.exponent == 100 - 13 + 128);
1| assert(fp.coefficient == UInt!128.fromHexString("c6841dd302415d785373ab6d93712988"));
| }
|
| /// Uses approximate division for now
| /// TODO: use full precision division for void when Fp division is ready
| Fp!(max(size, rhsSize)) opBinary(string op : "/", uint rhsSize)(Fp!rhsSize rhs) nothrow const
| {
1| Fp a = this;
| alias b = rhs;
1| auto exponent = a.exponent - b.exponent;
1| a.exponent = b.exponent = -long(size);
1| auto ret = typeof(return)(cast(real) a / cast(real) b);
1| ret.exponent += exponent;
1| return ret;
| }
|
| ///
| T opCast(T)() nothrow const
| if (is(Unqual!T == bool))
| {
291| return exponent || coefficient;
| }
|
| ///
| T opCast(T, bool noSpecial = false, bool noHalf = false)() nothrow const
| if (is(T == float) || is(T == double) || is(T == real))
| {
| import mir.math.ieee: ldexp;
| static if (!noSpecial)
| {
19| if (_expect(this.isSpecial, false))
| {
10| T ret = this.coefficient ? T.nan : T.infinity;
5| if (this.sign)
4| ret = -ret;
5| return ret;
| }
| }
408| auto exp = cast()this.exponent;
| static if (size == 32)
| {
30| T c = cast(uint) coefficient;
| }
| else
| static if (size == 64)
| {
63| T c = cast(ulong) coefficient;
| }
| else
| {
| enum shift = size - T.mant_dig;
| enum rMask = (UInt!size(1) << shift) - UInt!size(1);
| enum rHalf = UInt!size(1) << (shift - 1);
| enum rInc = UInt!size(1) << shift;
315| UInt!size adC = this.coefficient;
| static if (!noHalf)
| {
315| auto cr = (this.coefficient & rMask).opCmp(rHalf);
315| if ((cr > 0) | (cr == 0) & this.coefficient.bt(shift))
| {
113| if (auto overflow = adC += rInc)
| {
19| adC = half!size;
19| exp++;
| }
| }
| }
315| adC >>= shift;
315| exp += shift;
315| 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);
| }
| }
408| if (this.sign)
5| c = -c;
| static if (exp.sizeof > int.sizeof)
| {
| import mir.utility: min, max;
408| exp = exp.max(int.min).min(int.max);
| }
408| return ldexp(c, cast(int)exp);
| }
|
| static if (size == 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);
|
1| fp = Fp!128(1, long.max, UInt!128.init);
1| assert(cast(double)fp == -double.infinity);
|
| import mir.math.ieee : signbit;
1| fp = Fp!128(1, long.max, UInt!128(123));
1| auto r = cast(double)fp;
2| assert(r != r && r.signbit);
| }
|
| static if (size == 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 (size == 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 (size == 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!newSize, bool noSpecial = false, size_t newSize)() nothrow const
| if (newSize != size)
| {
291| Fp!newSize ret;
291| ret.sign = this.sign;
|
| static if (!noSpecial)
| {
291| if (_expect(this.isSpecial, false))
| {
1| ret.exponent = ret.exponent.max;
1| ret.coefficient = !!this.coefficient;
1| return ret;
| }
290| if (!this)
| {
0000000| return ret;
| }
| }
|
290| UInt!size coefficient = this.coefficient;
290| int shift;
| // subnormal
|
| static if (!noSpecial)
| {
290| if (this.exponent == this.exponent.min)
| {
0000000| shift = cast(int)coefficient.ctlz;
0000000| coefficient <<= shift;
| }
| }
|
290| ret = Fp!newSize(coefficient, true);
290| ret.exponent -= shift;
290| ret.sign = this.sign;
|
| import mir.checkedint: adds;
| /// overflow
|
| static if (!noSpecial)
| {
290| bool overflow;
290| ret.exponent = adds(ret.exponent, this.exponent, overflow);
290| if (_expect(overflow, false))
| {
| // overflow
0000000| if (this.exponent > 0)
| {
0000000| ret.exponent = ret.exponent.max;
0000000| ret.coefficient = 0u;
| }
| // underflow
| else
| {
0000000| ret.coefficient >>= cast(uint)(ret.exponent - exponent.min);
0000000| ret.exponent = ret.coefficient ? ret.exponent.min : 0;
| }
| }
| }
| else
| {
| ret.exponent += this.exponent;
| }
290| return ret;
| }
|
| static if (size == 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"));
|
1| assert(Fp!128(-double.infinity) * Fp!128(1) == Fp!128(-double.infinity));
| }
|}
|
|///
|Fp!(coefficientizeA + coefficientizeB) extendedMul(bool noSpecial = false, uint coefficientizeA, uint coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
| @safe pure nothrow @nogc
|{
| import mir.bignum.fixed: extendedMul;
| import mir.checkedint: adds;
|
332| typeof(return) ret = void;
332| ret.coefficient = extendedMul(a.coefficient, b.coefficient);
| static if (noSpecial)
| {
304| ret.exponent = a.exponent + b.exponent;
304| if (!ret.coefficient.signBit)
| {
214| ret.exponent -= 1; // check overflow
214| ret.coefficient = ret.coefficient.smallLeftShift(1);
| }
| }
| else
| {
| // nan * any -> nan
| // inf * fin -> inf
28| if (_expect(a.isSpecial | b.isSpecial, false))
| { // set nan
1| ret.exponent = ret.exponent.max;
| // nan inf case
1| if (a.isSpecial & b.isSpecial)
0000000| ret.coefficient = a.coefficient | b.coefficient;
| }
| else
| {
27| bool overflow;
27| ret.exponent = adds(a.exponent, b.exponent, overflow);
| // exponent underflow -> 0 or subnormal
| // overflow -> inf
27| if (_expect(overflow, false))
| {
| // overflow
0000000| if (a.exponent > 0) // && b.exponent > 0 is always true
| {
0000000| ret.exponent = ret.exponent.max;
0000000| ret.coefficient = 0;
| }
| // underflow
| else // a.exponent < 0 and b.exponent < 0
| {
| // TODO: subnormal
0000000| ret.exponent = 0;
0000000| ret.coefficient = 0;
| }
| }
| else
27| if (!ret.coefficient.signBit)
| {
16| auto normal = ret.exponent != ret.exponent.min;
16| ret.exponent -= normal; // check overflow
16| ret.coefficient = ret.coefficient.smallLeftShift(normal);
| }
| }
| }
332| ret.sign = a.sign ^ b.sign;
332| return ret;
|}
|
|///
|template fp_log2(T)
| if (is(T == float) || is(T == double) || is(T == real))
|{
| ///
| T fp_log2(uint size)(Fp!size x)
| {
| import mir.math.common: log2;
2| auto exponent = x.exponent + size;
2| if (!x.isSpecial)
2| x.exponent = -long(size);
2| return log2(cast(T)x) + exponent;
| }
|}
|
|///
|version(mir_bignum_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: log2, approxEqual;
| import mir.bignum.fp: fp_log2;
|
1| double x = 123456789.0e+123;
1| assert(approxEqual(x.Fp!128.fp_log2!double, x.log2));
|}
|
|///
|template fp_log(T)
| if (is(T == float) || is(T == double) || is(T == real))
|{
| ///
| T fp_log(uint size)(Fp!size x)
| {
| import mir.math.constant: LN2;
1| return T(LN2) * fp_log2!T(x);
| }
|}
|
|///
|version(mir_bignum_test)
|@safe pure nothrow @nogc
|unittest
|{
| import mir.math.common: log, approxEqual;
| import mir.bignum.fp: fp_log;
|
1| double x = 123456789.0e+123;
1| assert(approxEqual(x.Fp!128.fp_log!double, x.log));
|}
source/mir/bignum/fp.d is 89% 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:
| size64 = count of 64bit words in coefficient
|+/
|@serdeScoped @serdeProxy!(const(char)[])
|struct BigInt(uint size64)
| if (size64 && size64 <= ushort.max)
|{
| import mir.bignum.low_level_view;
| import mir.bignum.fixed;
|
| ///
| bool sign;
| ///
| uint length;
| ///
| size_t[ulong.sizeof / size_t.sizeof * size64] data;// = void;
|
|@safe:
|
| ///
85| this(uint size)(UInt!size fixedInt)
| {
85| this(fixedInt.data);
| }
|
| ///
85| this(uint N)(size_t[N] data)
| if (N <= this.data.length)
| {
| static if (data.length == 0)
| {
| sign = false;
| length = 0;
| }
| static if (data.length == 1)
| {
| this(data[0]);
| }
| else
| static if (data.length == 2)
| {
76| sign = false;
76| this.data[0] = data[0];
76| this.data[1] = data[1];
152| this.length = data[1] ? 2 : data[0] != 0;
| }
| else
| {
9| sign = false;
9| this.data[0 .. N] = data;
9| length = data.length;
9| normalize;
| }
| }
|
| ///
33| this(ulong data)
| {
33| sign = false;
| static if (size_t.sizeof == ulong.sizeof)
| {
| length = data != 0;
| view.coefficients[0] = data;
| }
| else
| {
33| this.length = !!data + !!(data >> 32);
33| this.data[0] = cast(uint) data;
33| this.data[1] = cast(uint) (data >> 32);
| }
| }
|
| ///
27| this(long data)
| {
54| this(ulong(data < 0 ? -data : data));
27| this.sign = data < 0;
| }
|
| ///
24| this(int data)
| {
24| this(long(data));
| }
|
| ///
6| this(uint data)
| {
6| this(ulong(data));
| }
|
| ///
| this()(scope const(char)[] str) @safe pure @nogc
| {
11| if (fromStringImpl(str))
11| return;
| static if (__traits(compiles, () @nogc { throw new Exception("Can't parse BigInt."); }))
| {
| import mir.exception: MirException;
| throw new MirException("Can't parse BigInt!" ~ size64.stringof ~ " from string `", str , "`.");
| }
| else
| {
| static immutable exception = new Exception("Can't parse BigInt!" ~ size64.stringof ~ ".");
0000000| throw exception;
| }
| }
|
| ///
| inout(size_t)[] coefficients()() inout @property scope return
| {
1252| return data[0 .. length];
| }
|
| ///
| ref opAssign(ulong data) return scope
| {
0000000| __ctor(data);
0000000| return this;
| }
|
| ///
| ref opAssign(long data) return scope
| {
0000000| __ctor(data);
0000000| return this;
| }
|
| ///
| ref opAssign(uint data) return scope
| {
6| __ctor(data);
6| return this;
| }
|
| ///
| ref opAssign(int data) return scope
| {
20| __ctor(data);
20| return this;
| }
|
| ///
| ref opAssign(uint rhsSize)(UInt!rhsSize data) return scope
| {
| __ctor(data);
| return this;
| }
|
| static if (size64 == 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(uint rhsSize64)(auto ref scope const BigInt!rhsSize64 rhs) return
| @trusted pure nothrow @nogc
332| in (rhs.length <= this.data.length)
| {
| static if (size64 == rhsSize64)
| {
110| if (&this is &rhs)
0000000| return this;
| }
332| this.sign = rhs.sign;
332| this.length = rhs.length;
332| this.data[0 .. length] = rhs.data[0 .. length];
332| return this;
| }
|
| ///
| static BigInt fromBigEndian()(scope const(ubyte)[] data, bool sign = false)
| @trusted pure @nogc
| {
| BigInt ret = void;
| if (!ret.copyFromBigEndian(data, sign))
| static immutable bigIntOverflowException = new Exception("BigInt!" ~ size64.stringof ~ ".fromBigEndian: data overflow");
| return ret;
| }
|
| ///
| bool copyFromBigEndian()(scope const(ubyte)[] data, bool sign = false)
| @trusted pure @nogc
| {
| while(data.length && data[0] == 0)
| data = data[1 .. $];
| if (data.length == 0)
| {
| this.length = 0;
| this.sign = false;
| }
| else
| {
| if (data.length > this.data.sizeof)
| return false;
| this.sign = sign;
| this.length = cast(uint) (data.length / size_t.sizeof);
| auto tail = data[0 .. data.length % size_t.sizeof];
| data = data[data.length % size_t.sizeof .. $];
| foreach_reverse (ref c; this.coefficients)
| {
| size_t value;
| foreach (j; 0 .. size_t.sizeof)
| {
| value <<= 8;
| value |= data[0];
| data = data[1 .. $];
| }
| c = value;
| }
| assert(data.length == 0);
| if (tail.length)
| {
| this.length++;
| size_t value;
| foreach (b; tail)
| {
| value <<= 8;
| value |= b;
| }
| this.data[length - 1] = value;
| }
| }
| return true;
| }
|
| /++
| 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)
| {
11| auto work = data[].BigIntView!size_t;
11| if (work.fromStringImpl(str))
| {
11| length = cast(uint) work.coefficients.length;
11| sign = work.sign;
11| 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
| {
20| return view == rhs.view;
| }
|
| ///
| bool opEquals(ulong rhs, bool rhsSign = false)
| const @safe pure nothrow @nogc
| {
45| if (rhs == 0 && this.length == 0 || this.length == 1 && this.sign == rhsSign && this.data[0] == rhs)
9| return true;
| static if (is(size_t == ulong) || size64 == 1)
0000000| return false;
| else
4| return this.length == 2 && this.data[0] == cast(uint) rhs && this.data[1] == cast(uint) (rhs >> 32);
| }
|
| ///
| bool opEquals(long rhs)
| const @safe pure nothrow @nogc
| {
12| auto sign = rhs < 0;
24| return this.opEquals(sign ? ulong(-rhs) : ulong(rhs), sign);
| }
|
| ///
| bool opEquals(uint rhs)
| const @safe pure nothrow @nogc
| {
1| return opEquals(ulong(rhs), false);
| }
|
| ///
| bool opEquals(int rhs)
| const @safe pure nothrow @nogc
| {
10| return opEquals(long(rhs));
| }
|
| /++
| +/
| auto opCmp()(auto ref const BigInt rhs)
| const @safe pure nothrow @nogc
| {
21| return view.opCmp(rhs.view);
| }
|
| ///
| BigIntView!size_t view()() scope return @property
| {
1411| return typeof(return)(this.data[0 .. this.length], this.sign);
| }
|
| ///
| BigIntView!(const size_t) view()() const scope return @property
| {
282| return typeof(return)(this.data[0 .. this.length], this.sign);
| }
|
| ///
| void normalize()()
| {
| pragma(inline, false);
285| auto norm = view.normalized;
285| this.length = cast(uint) norm.unsigned.coefficients.length;
285| this.sign = norm.sign;
| }
|
| /++
| +/
| void putCoefficient(size_t value)
| {
796| assert(length < data.length);
796| 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
| {
872| if (length == 0)
0000000| goto L;
872| overflow = view.unsigned.opOpAssign!op(rhs, overflow);
1669| if (overflow && length < data.length)
| {
| L:
796| putCoefficient(overflow);
796| overflow = 0;
| }
872| 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
| {
1| if (length == 0)
0000000| goto L;
1| overflow = view.unsigned.opOpAssign!op(rhs, overflow);
2| if (overflow && length < data.length)
| {
| L:
| static if (size <= 64)
| {
| auto o = cast(ulong)overflow;
| static if (size_t.sizeof == ulong.sizeof)
| {
| putCoefficient(o);
| overflow = UInt!size.init;
| }
| else
| {
| putCoefficient(cast(uint)o);
| o >>= 32;
| if (length < data.length)
| {
| putCoefficient(cast(uint)o);
| o = 0;
| }
| overflow = UInt!size(o);
| }
| }
| else
| {
| do
| {
0000000| putCoefficient(cast(size_t)overflow);
0000000| overflow >>= size_t.sizeof * 8;
| }
0000000| while(overflow && length < data.length);
| }
| }
1| return overflow;
| }
|
| ///
| ref opOpAssign(string op, size_t size)(UInt!size rhs)
| @safe pure nothrow @nogc scope return
| if (op == "/" || op == "%")
| {
8| BigInt!(size / 64) bigRhs = rhs;
8| return this.opOpAssign!op(bigRhs);
| }
|
| /// ditto
| ref opOpAssign(string op)(ulong rhs)
| @safe pure nothrow @nogc scope return
| if (op == "/" || op == "%")
| {
| BigInt!1 bigRhs = rhs;
| return this.opOpAssign!op(bigRhs);
| }
|
| /// ditto
| ref opOpAssign(string op)(long rhs)
| @safe pure nothrow @nogc scope return
| if (op == "/" || op == "%")
| {
| BigInt!1 bigRhs = rhs;
| return this.opOpAssign!op(bigRhs);
| }
|
| /++
| +/
| ref powMod(uint expSize)(scope ref const BigInt!expSize exponent, scope ref const BigInt modulus)
| @safe pure nothrow @nogc return scope
2| in(!exponent.sign)
| {
2| return this.powMod(exponent.view.unsigned, modulus);
| }
|
| ///ditto
| ref powMod()(scope BigUIntView!(const size_t) exponent, scope ref const BigInt modulus)
| @safe pure nothrow @nogc return scope
| {
| pragma(inline, false);
|
| import mir.ndslice.topology: bitwise;
|
4| if (modulus == 1 || modulus == -1)
| {
0000000| this.sign = 0;
0000000| this.length = 0;
0000000| return this;
| }
|
2| BigInt!(size64 * 2) bas = void;
2| bas = this;
2| BigInt!(size64 * 2) res = void;
2| res = 1u;
|
448| foreach (b; exponent.coefficients.bitwise[0 .. $ - exponent.ctlz])
| {
148| bas %= modulus;
148| if (b)
| {
65| res *= bas;
65| res %= modulus;
| }
148| bas *= bas;
| }
|
2| this = res;
2| return this;
| }
|
| ///
| static if (size64 == 3)
| version (mir_bignum_test)
| unittest
| {
1| BigInt!3 x = 2;
1| BigInt!3 e = 10;
1| BigInt!3 m = 100;
|
1| x.powMod(e, m);
1| assert(x == 24);
| }
|
| ///
| static if (size64 == 3)
| version (mir_bignum_test)
| unittest
| {
1| BigInt!3 x = 564321;
1| BigInt!3 e = "13763753091226345046315979581580902400000310";
1| BigInt!3 m = "13763753091226345046315979581580902400000311";
|
1| x.powMod(e, m);
1| assert(x == 1);
| }
|
| /++
| +/
| ref multiply(uint aSize64, uint bSize64)
| (
| scope ref const BigInt!aSize64 a,
| scope ref const BigInt!bSize64 b,
| )
| @safe pure nothrow @nogc scope return
| if (size64 >= aSize64 + bSize64)
| {
| import mir.utility: max;
| import mir.bignum.internal.kernel : multiply, karatsubaRequiredBuffSize;
| enum sizeM = ulong.sizeof / size_t.sizeof;
214| size_t[max(aSize64 * sizeM, bSize64 * sizeM).karatsubaRequiredBuffSize] buffer = void;
214| this.length = cast(uint) multiply(data, a.coefficients, b.coefficients, buffer);
214| this.sign = (this.length != 0) & (a.sign ^ b.sign);
214| return this;
| }
|
| ///
| ref divMod(uint divisorSize64, uint remainderSize = size64)
| (
| scope ref const BigInt!divisorSize64 divisor,
| scope ref BigInt!size64 quotient,
| scope ref BigInt!remainderSize remainder,
| )
| const @trusted pure nothrow @nogc scope return
| if (remainderSize >= divisorSize64)
| {
21| return this.divMod(divisor, quotient, &remainder);
| }
|
| private ref divMod(uint divisorSize64, uint remainderSize = size64)
| (
| scope ref const BigInt!divisorSize64 divisor,
| scope ref BigInt!size64 quotient,
| scope BigInt!remainderSize* remainder = null,
| )
| const @trusted pure nothrow @nogc scope return
| if (remainderSize >= divisorSize64)
| {
| import mir.bignum.internal.kernel : divMod, divisionRequiredBuffSize;
|
| pragma(inline, false);
|
244| if (divisor.length == 0)
| assert(0, "Zero BigInt divizor");
244| if (divisor.coefficients[$ - 1] == 0)
| assert(0, "Denormalized BigInt divizor");
|
244| if (this.length < divisor.length)
| {
7| if (remainder !is null)
| {
6| if (&this !is remainder)
0000000| *remainder = this;
6| remainder.sign = 0;
| }
|
| static if (size64 == remainderSize)
7| if ("ient is remainder)
6| return this;
|
1| quotient.sign = 0;
1| quotient.length = 0;
|
1| return this;
| }
|
| enum sizeM = ulong.sizeof / size_t.sizeof;
| enum vlen = min(divisorSize64, size64);
237| size_t[divisionRequiredBuffSize(size64 * sizeM, vlen * sizeM)] buffer = void;
|
237| quotient.length = cast(uint) divMod(
| quotient.data,
237| remainder !is null ? remainder.data[] : null,
| this.coefficients,
| divisor.coefficients,
| buffer,
| );
|
237| quotient.sign = (this.sign ^ divisor.sign) && quotient.length;
|
237| if (remainder !is null)
| {
233| remainder.sign = 0;
233| remainder.length = divisor.length;
233| remainder.normalize;
| }
|
237| return this;
| }
|
| /++
| Performs `this %= rhs` and `this /= rhs` operations.
| Params:
| rhs = value to divide by
| Returns:
| remainder or quotient from the truncated division
| +/
| ref opOpAssign(string op, size_t rhsSize64)(scope const ref BigInt!rhsSize64 rhs)
| @safe pure nothrow @nogc return
| if (op == "/" || op == "%")
| {
| enum isRem = op == "%";
223| return this.divMod(rhs, this, isRem ? &this : null);
| }
|
| /++
| Performs `this %= rhs` and `this /= rhs` operations.
| Params:
| rhs = value to divide by
| Returns:
| remainder or quotient from the truncated division
| +/
| ref opOpAssign(string op : "*", size_t rhsSize64)(scope const ref BigInt!rhsSize64 rhs)
| @safe pure nothrow @nogc return
| {
214| BigInt!(size64 + rhsSize64) c = void;
214| c.multiply(this, rhs);
214| this = c;
214| return this;
| }
|
| ///
| static if (size64 == 3)
| version (mir_bignum_test)
| unittest
| {
1| BigInt!32 x = "236089459999051800787306800176765276560685597708945239133346035689205694959466543423391020917332149321603397284295007899190053323478336179162578944";
1| BigInt!32 y = "19095614279677503764429420557677401943131308638097701560446870251856566051181587499424174939645900335127490246389509326965738171086235365599977209919032320327138167362675030414072140005376";
1| BigInt!32 z = "4508273263639244391466225874448166762388283627989411942887789415132291146444880491003321910228134369483394456858712486391978856464207606191606690798518090459546799016472580324664149788791167494389789813815605288815981925073283892089331019170542792502117265455020551819803771537458327634120582677504637693661973404860326560198184402944";
1| x *= y;
1| assert(x == z);
| }
|
| /++
| Performs `size_t overflow = big *= fixed` operatrion.
| Params:
| rhs = unsigned value to multiply by
| Returns:
| overflow
| +/
| bool opOpAssign(string op, size_t rhsSize64)(ref const BigInt!rhsSize64 rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
22| return opOpAssign!op(rhs.view);
| }
|
| /// ditto
| bool opOpAssign(string op)(BigIntView!(const size_t) rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
44| sizediff_t diff = length - rhs.coefficients.length;
44| if (diff < 0)
| {
8| auto oldLength = length;
8| length = cast(int)rhs.coefficients.length;
8| coefficients[oldLength .. $] = 0;
| }
| else
36| if (rhs.coefficients.length == 0)
1| return false;
43| auto thisView = view;
43| auto overflow = thisView.opOpAssign!op(rhs);
43| this.sign = thisView.sign;
43| if (overflow)
| {
0000000| if (length < data.length)
| {
0000000| putCoefficient(overflow);
0000000| overflow = false;
| }
| }
| else
| {
43| normalize;
| }
43| return overflow;
| }
|
| /// ditto
| bool opOpAssign(string op)(ulong rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
| import mir.ndslice.bignum.fixed: UInt;
| return this.opOpAssign!op(rhs.UInt!64.view.signed);
| }
|
| /// ditto
| bool opOpAssign(string op)(uint rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
| return this.opOpAssign!op(ulong(rhs));
| }
|
| /// ditto
| bool opOpAssign(string op)(long rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
| import mir.bignum.fixed: UInt;
| auto sign = rhs < 0;
| rhs = sign ? -rhs : rhs;
| return this.opOpAssign!op(rhs.UInt!64.view.normalized.signed(sign));
| }
|
| /// ditto
| bool opOpAssign(string op)(int rhs)
| @safe pure nothrow @nogc
| if (op == "+" || op == "-")
| {
| return this.opOpAssign!op(long(rhs));
| }
|
| /++
| +/
| static BigInt fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
| @trusted pure
| {
25| BigInt ret;
25| if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
25| 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)
| {
25| auto work = BigIntView!size_t(data);
25| auto ret = work.fromHexStringImpl!(C, allowUnderscores)(str);
25| if (ret)
| {
25| length = cast(uint)work.unsigned.coefficients.length;
25| sign = work.sign;
| }
25| return ret;
| }
|
| /++
| +/
| static BigInt fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str)
| @trusted pure
| {
1| BigInt ret;
1| if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
1| return ret;
| version(D_Exceptions)
0000000| throw binaryStringException;
| else
| assert(0, binaryStringErrorMsg);
| }
|
| static if (size64 == 3)
| ///
| version(mir_bignum_test) @safe pure @nogc unittest
| {
1| BigInt!4 integer = "-34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
1| assert(integer == BigInt!4.fromBinaryString("-100101100110001001110110010001110101010010101100000111000011011000010011000010111111000100111001011111001101101111101010100011000001000011000001110001110011010011001001011101010010010101101001010101111011101001111101110011101111110010011100000010110111000"));
| }
|
| /++
| +/
| bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
| @safe pure @nogc nothrow
| if (isSomeChar!C)
| {
1| auto work = BigIntView!size_t(data);
1| auto ret = work.fromBinaryStringImpl!(C, allowUnderscores)(str);
1| if (ret)
| {
1| length = cast(uint)work.unsigned.coefficients.length;
1| sign = work.sign;
| }
1| return ret;
| }
|
| ///
| ref pow()(ulong degree)
| {
| BigInt!size64 bas = void;
| bas = this;
| this = 1u;
|
| while (degree)
| {
| if (degree & 1)
| this *= bas;
| bas *= bas;
| degree >>= 1;
| }
| return this;
| }
|
| ///
| bool mulPow5()(ulong degree)
| {
| import mir.bignum.internal.dec2float: MaxWordPow5;
| // assert(approxCanMulPow5(degree));
43| if (length == 0)
5| return false;
| enum n = MaxWordPow5!size_t;
| enum wordInit = size_t(5) ^^ n;
38| size_t word = wordInit;
38| size_t overflow;
|
909| while(degree)
| {
871| if (degree >= n)
| {
842| degree -= n;
| }
| else
| {
29| word = 1;
193| do word *= 5;
193| while(--degree);
| }
871| overflow |= this *= word;
| }
38| return overflow != 0;
| }
|
| ///
| ref BigInt opOpAssign(string op)(size_t shift)
| @safe pure nothrow @nogc return
| if (op == "<<" || op == ">>")
| {
68| auto index = shift / (size_t.sizeof * 8);
68| auto bs = shift % (size_t.sizeof * 8);
68| auto ss = size_t.sizeof * 8 - bs;
| static if (op == ">>")
| {
3| if (index >= length)
| {
0000000| length = 0;
0000000| return this;
| }
3| auto d = view.coefficients;
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[$ - 1] >>> bs;
3| length -= index + (most == 0);
| }
| else
| {
130| if (index >= data.length || length == 0)
| {
5| length = 0;
5| return this;
| }
|
60| if (bs)
| {
52| auto most = coefficients[$ - 1] >> ss;
52| length += index;
52| if (length < data.length)
| {
49| if (most)
| {
12| length++;
12| coefficients[$ - 1] = most;
12| length--;
| }
| }
| else
| {
3| length = data.length;
| }
|
52| auto d = view.coefficients;
1700| foreach_reverse (j; index + 1 .. length)
| {
772| d[j] = (d[j - index] << bs) | (d[j - (index + 1)] >> ss);
| }
52| d[index] = d[0] << bs;
52| if (length < data.length)
49| length += most != 0;
| }
| else
| {
8| length = cast(uint) min(length + index, cast(uint)data.length);
8| auto d = view.coefficients;
132| foreach_reverse (j; index .. length)
| {
54| d[j] = d[j - index];
| }
| }
60| view.coefficients[0 .. index] = 0;
| }
63| return this;
| }
|
| ///
| T opCast(T)() const
| if (isFloatingPoint!T && isMutable!T)
| {
3| return view.opCast!T;
| }
|
| ///
| 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)(scope const(W)[] coefficients, bool sign = false)
| if (__traits(isUnsigned, W))
| {
| static if (W.sizeof > size_t.sizeof)
| {
| return this.copyFrom(cast(BigIntView!(const size_t))view, sign);
| }
| else
| {
32| this.sign = sign;
32| auto dest = cast(W[])data;
32| auto overflow = dest.length < coefficients.length;
64| auto n = overflow ? dest.length : coefficients.length;
32| this.length = cast(uint)(n / (size_t.sizeof / W.sizeof));
32| dest[0 .. n] = coefficients[0 .. n];
| static if (size_t.sizeof / W.sizeof > 1)
| {
1| if (auto tail = n % (size_t.sizeof / W.sizeof))
| {
1| this.length++;
1| auto shift = ((size_t.sizeof / W.sizeof) - tail) * (W.sizeof * 8);
1| auto value = this.coefficients[$ - 1];
1| value <<= shift;
1| value >>= shift;
1| this.coefficients[$ - 1] = value;
| }
| }
32| return overflow;
| }
| }
|
| ///
| immutable(C)[] toString(C = char)() scope 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 (size64 == 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)(ref scope W w) scope 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 .. $]);
| }
|
| ///
| size_t bitLength()() const @property
| {
82| return length == 0 ? 0 : length * size_t.sizeof * 8 - data[length - 1].ctlz;
| }
|
| ///
| size_t ctlz()() const @property
| {
20| return data.sizeof * 8 - bitLength;
| }
|}
|
|/// Check @nogc toString impl
|version(mir_bignum_test) @safe pure @nogc unittest
|{
| import mir.format;
1| auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
1| auto integer = BigInt!4(str);
2| auto buffer = stringBuf;
1| buffer << integer;
1| assert(buffer.data == str);
|}
|
|///
|version(mir_bignum_test)
|unittest
|{
| import mir.bignum.fixed;
| import mir.bignum.low_level_view;
|
| {
1| auto a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
1| auto b = UInt!128.fromHexString("f79a222050aaeaaa417fa25a2ac93291");
|
| // ca3d7e25aebe687b 168dcef32d0bb2f0
| import mir.format;
1| assert((a %= b) == BigInt!4.fromHexString("bf4c87424431d21563f23b1fc00d75ac"));
1| a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
1| a /= b;
1| assert(a == BigInt!4.fromHexString("ca3d7e25aebe687b7cc1b250b44690fb"), a.data.text);
| }
|
| {
1| auto a = BigInt!4.fromHexString("7fff000080000000000000000000");
1| auto b = UInt!128.fromHexString("80000000000000000001");
|
1| assert((a /= b) == BigInt!4.fromHexString("fffe0000"));
1| a = BigInt!4.fromHexString("7fff000080000000000000000000");
1| assert((a %= b) == BigInt!4.fromHexString("7fffffffffff00020000"));
| }
|
| {
1| auto a = BigInt!16.fromHexString("76d053cdcc87ec8c9455c375d6a08c799fad73cf07415e70af5dfacaff4bd306647a7cceb98839cce89ae65900938821564fd2af3c9d881c172264bb17e3530ce79b938d5eb7ffec558be43ab5b684978417c5053fb8df63fc65c9efd8b2e86469c53259509eb597f81647930f24ef05a79bfecf04e5ec52414c6a3f7481d533");
1| auto b = UInt!128.fromHexString("9c5c1aa6ad7ad18065a3a74598e27bee");
|
1| assert((a /= b) == BigInt!16.fromHexString("c2871f2b07522bda1e63de12850d2208bb242c716b5739d6744ee1d9c937b8d765d3742e18785d08c2405e5c83f3c875d5726d09dfaee29e813675a4f91bfee01e8cbbbca9588325d54cf2a625faffde4d8709e0517f786f609d8ce6997e0e71d2f976ae169b0c4be7a7dba3135af96c"));
1| a = BigInt!16.fromHexString("76d053cdcc87ec8c9455c375d6a08c799fad73cf07415e70af5dfacaff4bd306647a7cceb98839cce89ae65900938821564fd2af3c9d881c172264bb17e3530ce79b938d5eb7ffec558be43ab5b684978417c5053fb8df63fc65c9efd8b2e86469c53259509eb597f81647930f24ef05a79bfecf04e5ec52414c6a3f7481d533");
1| assert((a %= b) == BigInt!16.fromHexString("85d81587a8b62af1874315d26ebf0ecb"));
| }
|
| {
1| auto a = BigInt!4.fromHexString("DEADBEEF");
1| auto b = UInt!256.fromHexString("18aeff9fa4aace484a9f8f9002cdf38fa6e53fc0f6c035051dc86931c1c08316");
|
1| assert((a /= b) == 0);
1| a = BigInt!4.fromHexString("DEADBEEF");
1| assert((a %= b) == 0xDEADBEEF);
| }
|
| void test(const long av, const long bv)
| {
1| auto a = BigInt!4(av);
1| const b = BigInt!4(bv);
1| a /= b;
1| assert(a == av / bv);
1| a = BigInt!4(av);
1| a %= b;
1| assert(a == av % bv);
| }
|
| {
1| auto av = 0xDEADBEEF;
1| auto bv = 0xDEAD;
1| test(+av, +bv);
| // test(+av, -bv);
| // test(-av, +bv);
| // test(+av, +bv);
| }
|}
|
|///
|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.coefficients, b.sign));
1| assert(c == b);
1| b >>= 18;
1| auto bView = cast(BigIntView!ushort)b.view;
1| assert(!c.copyFrom(bView.coefficients[0 .. $ - 1], bView.sign));
1| assert(c == b);
|}
|
|version(mir_bignum_test)
|@safe pure @nogc unittest
|{
1| BigInt!4 i = "-0";
1| assert(i.coefficients.length == 0);
1| assert(!i.sign);
1| assert(cast(long) i == 0);
|}
source/mir/bignum/integer.d is 91% covered
<<<<<< EOF
# path=source-mir-bignum-internal-dec2float.lst
|module mir.bignum.internal.dec2float;
|
|version (LDC) import ldc.attributes: optStrategy;
|else struct optStrategy { string opt; }
|
|template MaxWordPow5(T)
|{
| static if (is(T == ubyte))
| enum MaxWordPow5 = 3;
| else
| static if (is(T == ushort))
| enum MaxWordPow5 = 6;
| else
| static if (is(T == uint))
| enum MaxWordPow5 = 13;
| else
| static if (is(T == ulong))
| enum MaxWordPow5 = 27;
| else
| static assert(0);
|}
|
|template MaxFpPow5(T)
|{
| static if (T.mant_dig == 24)
| enum MaxFpPow5 = 6;
| else
| static if (T.mant_dig == 53)
| enum MaxFpPow5 = 10;
| else
| static if (T.mant_dig == 64)
| enum MaxFpPow5 = 27;
| else
| static if (T.mant_dig == 113)
| enum MaxFpPow5 = 48;
| else
| static assert(0, "floating point format isn't supported");
|}
|
|@safe pure nothrow @nogc:
|
|alias decimalTo(T : float) = decimalToFloat32;
|alias decimalTo(T : double) = decimalToFloat64;
|alias decimalTo(T : real) = decimalToReal;
|
|alias binaryTo(T : float) = binaryToFloat32;
|alias binaryTo(T : double) = binaryToFloat64;
|alias binaryTo(T : real) = binaryToReal;
|
|private float binaryToFloat32(scope const size_t[] coefficients, long exponent = 0)
|{
| pragma(inline, false);
30| return binaryToFloatImpl!float(coefficients, exponent);
|}
|
|private double binaryToFloat64(scope const size_t[] coefficients, long exponent = 0)
|{
| pragma(inline, false);
54| return binaryToFloatImpl!double(coefficients, exponent);
|}
|
|private real binaryToReal(scope const size_t[] coefficients, long exponent = 0)
|{
| pragma(inline, real.mant_dig == double.mant_dig);
| static if (real.mant_dig == double.mant_dig)
| return binaryToFloat64(coefficients, exponent);
| else
14| return binaryToFloatImpl!real(coefficients, exponent);
|}
|
|private float decimalToFloat32(scope const ulong coefficient, long exponent)
|{
| pragma(inline, false);
67| return decimalToFloatImpl!float(coefficient, exponent);
|}
|
|private double decimalToFloat64(scope const ulong coefficient, long exponent)
|{
| pragma(inline, false);
104| return decimalToFloatImpl!double(coefficient, exponent);
|}
|
|private real decimalToReal(scope const ulong coefficient, long exponent)
|{
| pragma(inline, real.mant_dig == double.mant_dig);
| static if (real.mant_dig == double.mant_dig)
| return decimalToFloat64(coefficient, exponent);
| else
66| return decimalToFloatImpl!real(coefficient, exponent);
|}
|
|private float decimalToFloat32(scope const size_t[] coefficients, long exponent)
|{
| pragma(inline, false);
78| return decimalToFloatImpl!float(coefficients, exponent);
|}
|
|private double decimalToFloat64(scope const size_t[] coefficients, long exponent)
|{
| pragma(inline, false);
120| return decimalToFloatImpl!double(coefficients, exponent);
|}
|
|private real decimalToReal(scope const size_t[] coefficients, long exponent)
|{
| pragma(inline, real.mant_dig == double.mant_dig);
| static if (real.mant_dig == double.mant_dig)
| return decimalToFloat64(coefficients, exponent);
| else
77| return decimalToFloatImpl!real(coefficients, exponent);
|}
|
|T decimalToFloatImpl(T)(ulong coefficient, long exponent)
| if (is(T == float) || is(T == double) || is(T == real))
|{
| version (LDC)
| pragma(inline, true);
|
| import mir.bignum.fixed: UInt;
| import mir.bignum.fp: Fp, extendedMul;
| import mir.utility: _expect;
|
| enum wordBits = T.mant_dig < 64 ? 64 : 128;
| enum ulong half = (1UL << (wordBits - T.mant_dig - 1));
| enum bigHalf = UInt!128([0UL, half]);
| static if (T.mant_dig < 64)
| enum bigMask = (UInt!128(1UL) << (64 - T.mant_dig)) - 1;
|
| static if (T.mant_dig > 64)
| enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
| else
| static if (T.mant_dig == 64)
| enum ulong mask = ulong.max;
| else
| enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
|
237| if (coefficient == 0)
0000000| return 0;
|
| version (TeslAlgoM) {} else
462| if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
| {
225| auto c = coefficient.Fp!64;
|
225| T approx = void;
|
225| if (exponent < 0)
| {
| version (all)
| {{
141| auto z = c.extendedMul!true(_load!wordBits(exponent));
141| approx = z.opCast!(T, true);
141| long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
141| uint slop = 3;
325| if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
88| return approx;
| }}
|
53| if (-exponent <= MaxFpPow5!T)
| {
0000000| auto e = _load!wordBits(-exponent);
0000000| return coefficient / e.opCast!(T, true);
| }
|
| static if (T.mant_dig < 64)
| {{
53| auto z = c.extendedMul!true(_load!128(exponent));
53| approx = z.opCast!(T, true);
53| auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
53| if (bitsDiff.signBit)
53| bitsDiff = UInt!128.init - bitsDiff;
53| uint slop = 3;
57| if (_expect(approx > T.min_normal && bitsDiff > slop, true))
4| return approx;
| }}
|
| }
| else
| {
| version (all)
| {{
84| auto z = c.extendedMul!true(_load!wordBits(exponent));
84| approx = z.opCast!(T, true);
|
84| if (exponent <= 27) // exact exponent
| {
42| return approx;
| }
|
42| long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
42| uint slop;
126| if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
42| return approx;
| }}
|
| static if (T.mant_dig < 64)
| {{
0000000| auto z = c.extendedMul!true(_load!128(exponent));
0000000| approx = z.opCast!(T, true);
|
0000000| if (exponent <= 55) // exact exponent
| {
0000000| return approx;
| }
|
0000000| auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
0000000| if (bitsDiff.signBit)
0000000| bitsDiff = UInt!128.init - bitsDiff;
0000000| uint slop;
0000000| if (_expect(approx > T.min_normal && bitsDiff > slop, true))
0000000| return approx;
| }}
|
| }
| }
61| size_t[ulong.sizeof / size_t.sizeof] coefficients;
61| coefficients[0] = cast(size_t) coefficient;
| static if (coefficients.length == 2)
61| coefficients[1] = cast(size_t) (coefficient >> 32);
| static if (coefficients.length == 1)
| return algorithmM!T(coefficients, exponent);
| else
61| return algorithmM!T(coefficients[0 .. 1 + (coefficient > uint.max)], exponent);
|}
|
|private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent)
| @safe
| if ((is(T == float) || is(T == double) || is(T == real)))
538| in (coefficients.length == 0 || coefficients[$ - 1])
|{
| version (LDC)
| pragma(inline, true);
|
| import mir.bignum.fixed: UInt;
| import mir.bignum.fp: Fp, extendedMul;
| import mir.utility: _expect;
|
| enum wordBits = T.mant_dig < 64 ? 64 : 128;
| enum ulong half = (1UL << (wordBits - T.mant_dig - 1));
| static if (T.mant_dig > 64)
| enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
| else
| static if (T.mant_dig == 64)
| enum ulong mask = ulong.max;
| else
| enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
|
275| if (coefficients.length < 1)
12| return 0;
|
263| if (coefficients.length == 1)
181| return decimalTo!T(coefficients[0], exponent);
|
| static if (size_t.sizeof == uint.sizeof)
| {
82| if (coefficients.length == 2)
56| return decimalTo!T(coefficients[0] | (ulong(coefficients[1]) << 32), exponent);
| }
|
| version (TeslAlgoM) {} else
52| if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
| {
26| auto c = coefficients.binaryToFp!wordBits;
26| auto z = c.extendedMul!true(_load!wordBits(exponent));
26| auto approx = z.opCast!(T, true);
26| auto slop = 1 + 3 * (exponent < 0);
26| long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
|
78| if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
19| return approx;
| }
7| return algorithmM!T(coefficients, exponent);
|}
|
|private enum LOG2_10 = 0x3.5269e12f346e2bf924afdbfd36bf6p0;
|
|private template bigSize(T)
| if ((is(T == float) || is(T == double) || is(T == real)))
|{
| static if (T.mant_dig < 64)
| {
| enum size_t bigSize = 128;
| }
| else
| {
| enum size_t bits = T.max_exp - T.min_exp + T.mant_dig;
| enum size_t bigSize = bits / 64 + bits % 64 + 1;
| }
|}
|
|@optStrategy("minsize")
|private T algorithmM(T)(scope const size_t[] coefficients, long exponent)
| if ((is(T == float) || is(T == double) || is(T == real)))
68| in (coefficients.length)
|{
| pragma(inline, false);
|
| import mir.bitop: ctlz;
| import mir.bignum.fp: Fp;
| import mir.bignum.integer: BigInt;
| import mir.math.common: log2, ceil;
| import mir.math.ieee: ldexp, nextUp;
|
68| BigInt!(bigSize!T) u = void;
68| BigInt!(bigSize!T) v = void;
68| BigInt!(bigSize!T) q = void;
68| BigInt!(bigSize!T) r = void;
|
68| if (coefficients.length == 0)
0000000| return 0;
|
| // if no overflow
68| if (exponent >= 0)
| {
4| if (3 * exponent + coefficients.length * size_t.sizeof * 8 - ctlz(coefficients[$ - 1]) - 1 > T.max_exp)
0000000| return T.infinity;
4| if (exponent == 0)
4| return coefficients.binaryTo!T;
0000000| u.copyFrom(coefficients);
0000000| u.mulPow5(exponent);
0000000| return u.coefficients.binaryTo!T(exponent);
| }
|
64| auto log2_u = coefficients.binaryTo!T.log2;
64| auto log2_v = cast(T)(-LOG2_10) * exponent;
64| sizediff_t k = cast(sizediff_t) ceil(log2_u - log2_v);
|
64| k -= T.mant_dig;
|
64| if (k < T.min_exp - T.mant_dig)
| {
60| if (k + T.mant_dig + 1 < T.min_exp - T.mant_dig)
44| return 0;
16| k = T.min_exp - T.mant_dig;
| }
| else
4| if (k > T.max_exp)
| {
0000000| if (k - 2 > T.max_exp)
0000000| return T.infinity;
0000000| k = T.max_exp;
| }
|
20| if(u.copyFrom(coefficients))
0000000| return T.nan;
20| if (k < 0)
| {
19| if (u.ctlz < -k)
0000000| return T.nan;
19| u <<= -k;
| }
|
20| if (log2_v >= bigSize!T * 64)
0000000| return T.nan;
|
20| v = 1;
20| v.mulPow5(-exponent);
40| v <<= cast(int)-exponent + (k > 0 ? k : 0);
|
20| sizediff_t s;
| for(;;)
| {
21| u.divMod(v, q, r);
|
21| s = cast(int) q.bitLength - T.mant_dig;
21| assert(k >= T.min_exp - T.mant_dig);
21| if (s == 0)
6| break;
|
15| if (s < 0)
| {
14| if (k == T.min_exp - T.mant_dig)
14| break;
0000000| k--;
| }
| else
| {
1| if (k == T.max_exp)
0000000| return T.infinity;
1| k++;
| }
|
2| if ((s < 0 ? u : v).ctlz == 0)
0000000| return T.nan;
2| (s < 0 ? u : v) <<= 1;
| }
|
20| sizediff_t cmp;
20| if (s <= 0)
| {
20| u = v;
20| u -= r;
20| cmp = r.opCmp(u);
| }
| else
| {
0000000| cmp = s - 1 - q.view.unsigned.cttz;
0000000| if (cmp == 0) // half
0000000| cmp += r != 0;
0000000| q >>= s;
0000000| k += s;
| }
20| auto z = q.coefficients.binaryTo!T.ldexp(cast(int)k);
49| return cmp < 0 || cmp == 0 && !q.view.unsigned.bt(0) ? z : nextUp(z);
|}
|
|private T binaryToFloatImpl(T)(scope const size_t[] coefficients, long exponent)
| @safe
| if ((is(T == float) || is(T == double) || is(T == real)))
188| in (coefficients.length == 0 || coefficients[$ - 1])
|{
| version (LDC)
| pragma(inline, true);
|
| enum md = T.mant_dig;
| enum b = size_t.sizeof * 8;
| enum n = md / b + (md % b != 0);
| enum s = n * b;
|
98| if (coefficients.length == 0)
8| return 0;
|
90| if (exponent > T.max_exp)
0000000| return T.infinity;
|
90| auto fp = coefficients.binaryToFp!(s, s - md);
90| fp.exponent += exponent;
90| return fp.opCast!(T, true, true);
|}
|
|
|package(mir.bignum) auto binaryToFp(uint coefficientSize, uint internalRoundLastBits = 0)
| (scope const(size_t)[] coefficients)
| if (internalRoundLastBits < size_t.sizeof * 8)
129| in (coefficients.length)
129| in (coefficients[$ - 1])
|{
| import mir.bignum.fixed: UInt;
| import mir.bignum.fp: Fp;
| import mir.bitop: ctlz;
| import mir.utility: _expect;
|
| version (LDC)
| pragma(inline, true);
|
129| Fp!coefficientSize ret;
|
| enum N = ret.coefficient.data.length;
129| sizediff_t size = coefficients.length * (size_t.sizeof * 8);
129| sizediff_t expShift = size - coefficientSize;
129| ret.exponent = expShift;
129| if (_expect(expShift <= 0, true))
| {
| static if (N == 1)
| {
22| ret.coefficient.data[0] = coefficients[$ - 1];
| }
| else
| {
62| ret.coefficient.data[$ - coefficients.length .. $] = coefficients;
| }
84| auto c = cast(uint) ctlz(ret.coefficient.view.coefficients[$ - 1]);
84| ret.exponent -= c;
84| ret.coefficient = ret.coefficient.smallLeftShift(c);
| }
| else
| {
45| UInt!(coefficientSize + size_t.sizeof * 8) holder;
|
| static if (N == 1)
| {
8| holder.data[0] = coefficients[$ - 2];
8| holder.data[1] = coefficients[$ - 1];
| }
| else
| {
| import mir.utility: min;
37| auto minLength = min(coefficients.length, holder.data.length);
37| holder.data[$ - minLength .. $] = coefficients[$ - minLength .. $];
| }
|
45| auto c = cast(uint) ctlz(holder.data[$ - 1]);
45| ret.exponent -= c;
45| holder = holder.smallLeftShift(c);
45| ret.coefficient = holder.toSize!(coefficientSize, false);
45| auto tail = holder.data[0];
|
| bool nonZeroTail()
| {
10| while(_expect(coefficients[0] == 0, false))
| {
1| coefficients = coefficients[1 .. $];
1| assert(coefficients.length);
| }
9| return coefficients.length > N + 1;
| }
|
| static if (internalRoundLastBits)
| {
| enum half = size_t(1) << (internalRoundLastBits - 1);
| enum mask0 = (size_t(1) << internalRoundLastBits) - 1;
14| auto tail0 = ret.coefficient.data[0] & mask0;
14| ret.coefficient.data[0] &= ~mask0;
14| auto condInc = tail0 >= half
9| && ( tail0 > half
0000000| || tail
0000000| || (ret.coefficient.data[0] & 1)
0000000| || nonZeroTail);
| }
| else
| {
| enum half = cast(size_t)sizediff_t.min;
31| auto condInc = tail >= half
16| && ( tail > half
10| || (ret.coefficient.data[0] & 1)
9| || nonZeroTail);
| }
|
45| if (condInc)
| {
| enum inc = size_t(1) << internalRoundLastBits;
17| if (auto overflow = ret.coefficient += inc)
| {
| import mir.bignum.fp: half;
1| ret.coefficient = half!coefficientSize;
1| ret.exponent++;
| }
| }
| }
129| return ret;
|}
|
|private enum ExponentM = 512;
|
|private auto _load(uint size : 64)(long e) @trusted
362| in (-ExponentM < e && e < ExponentM)
|{
| version (LDC)
| pragma(inline, true);
|
| import mir.bignum.fixed: UInt;
| import mir.bignum.fp: Fp;
| import mir.bignum.internal.dec2float_table;
|
181| auto idx = cast(sizediff_t)e - min_p10_e;
181| auto p10coeff = p10_coefficients_h[idx];
181| auto p10exp = p10_exponents[idx];
181| return Fp!64(false, p10exp, UInt!64(p10coeff));
|}
|
|private auto _load(uint size : 128)(long e) @trusted
246| in (-ExponentM < e && e < ExponentM)
|{
| version (LDC)
| pragma(inline, true);
|
| import mir.bignum.fixed: UInt;
| import mir.bignum.fp: Fp;
| import mir.bignum.internal.dec2float_table;
|
| static assert(min_p10_e <= -ExponentM);
| static assert(max_p10_e >= ExponentM);
123| auto idx = cast(sizediff_t)e - min_p10_e;
123| ulong h = p10_coefficients_h[idx];
123| ulong l = p10_coefficients_l[idx];
123| if (l >= cast(ulong)long.min)
30| h--;
123| auto p10coeff = UInt!128(cast(ulong[2])[l, h]);
123| auto p10exp = p10_exponents[idx] - 64;
123| return Fp!128(false, p10exp, p10coeff);
|}
|
|version(mir_test)
|unittest
|{
| import mir.bignum.fp;
| import mir.bignum.fixed;
| import mir.test;
1| ulong[2] data = [ulong.max - 2, 1];
1| auto coefficients = cast(size_t[])data[];
1| if (coefficients[$ - 1] == 0)
1| coefficients = coefficients[0 .. $ - 1];
1| coefficients.binaryToFp!64.should == Fp!64(false, 1, UInt!64(0xFFFFFFFFFFFFFFFE));
1| coefficients.binaryToFp!128.should == Fp!128(false, -63, UInt!128([0x8000000000000000, 0xFFFFFFFFFFFFFFFE]));
|}
source/mir/bignum/internal/dec2float.d is 82% covered
<<<<<< EOF
# path=source-mir-bignum-internal-dec2float_table.lst
|/++
|Tables of approximations of powers of ten.
|DO NOT MODIFY: Generated by `etc/dec2float_table.py`
|+/
|module mir.bignum.internal.dec2float_table;
|
|enum min_p10_e = -512;
|enum max_p10_e = 512;
|
|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,
|];
|
|static immutable align(16) ulong[1025] p10_coefficients_h = [
| 0x9049EE32DB23D21C,
| 0xB45C69BF91ECC6A4,
| 0xE173842F7667F84C,
| 0x8CE8329DAA00FB30,
| 0xB0223F45148139FC,
| 0xDC2ACF1659A1887B,
| 0x899AC16DF804F54D,
| 0xAC0171C9760632A0,
| 0xD701CE3BD387BF48,
| 0x866120E56434D78D,
| 0xA7F9691EBD420D70,
| 0xD1F7C3666C9290CC,
| 0x833ADA2003DB9A80,
| 0xA40990A804D2811F,
| 0xCD0BF4D206072167,
| 0x8027790343C474E1,
| 0xA031574414B59219,
| 0xC83DAD1519E2F69F,
| 0xFA4D185A605BB447,
| 0x9C702F387C3950AC,
| 0xC38C3B069B47A4D7,
| 0xF46F49C842198E0D,
| 0x98C58E1D294FF8C8,
| 0xBEF6F1A473A3F6FA,
| 0xEEB4AE0D908CF4B9,
| 0x9530ECC87A5818F3,
| 0xBA7D27FA98EE1F30,
| 0xE91C71F93F29A6FC,
| 0x91B1C73BC77A085E,
| 0xB61E390AB9588A75,
| 0xE3A5C74D67AEAD12,
| 0x8E479C9060CD2C2C,
| 0xB1D983B479007736,
| 0xDE4FE4A197409504,
| 0x8AF1EEE4FE885D22,
| 0xADAE6A9E3E2A746B,
| 0xD91A0545CDB51186,
| 0x87B0434BA0912AF4,
| 0xA99C541E88B575B1,
| 0xD40369262AE2D31D,
| 0x848221B7DACDC3F2,
| 0xA5A2AA25D18134EE,
| 0xCF0B54AF45E1822A,
| 0x816714ED8BACF15A,
| 0xA1C0DA28EE982DB1,
| 0xCA3110B32A3E391D,
| 0xFCBD54DFF4CDC764,
| 0x9DF6550BF9009C9F,
| 0xC573EA4EF740C3C6,
| 0xF6D0E4E2B510F4B8,
| 0x9A428F0DB12A98F3,
| 0xC0D332D11D753F30,
| 0xF107FF8564D28EFC,
| 0x96A4FFB35F03995D,
| 0xBC4E3FA036C47FB5,
| 0xEB61CF8844759FA2,
| 0x931D21B52AC983C5,
| 0xB7E46A22757BE4B6,
| 0xE5DD84AB12DADDE4,
| 0x8FAA72EAEBC8CAAF,
| 0xB3950FA5A6BAFD5A,
| 0xE07A538F1069BCB1,
| 0x8C4C74396A4215EE,
| 0xAF5F9147C4D29B6A,
| 0xDB377599B6074245,
| 0x8902A98011C4896B,
| 0xAB4353E01635ABC6,
| 0xD61428D81BC316B7,
| 0x85CC99871159EE32,
| 0xA73FBFE8D5B069BF,
| 0xD10FAFE30B1C842F,
| 0x82A9CDEDE6F1D29D,
| 0xA354416960AE4744,
| 0xCC2951C3B8D9D916,
| 0xFF33A634A7104F5B,
| 0x9F8047E0E86A3199,
| 0xC76059D92284BDFF,
| 0xF938704F6B25ED7F,
| 0x9BC34631A2F7B46F,
| 0xC2B417BE0BB5A18B,
| 0xF3611DAD8EA309EE,
| 0x981CB28C7925E635,
| 0xBE23DF2F976F5FC2,
| 0xEDACD6FB7D4B37B2,
| 0x948C065D2E4F02CF,
| 0xB9AF07F479E2C383,
| 0xE81AC9F1985B7464,
| 0x9110BE36FF3928BF,
| 0xB554EDC4BF0772EE,
| 0xE2AA2935EEC94FAA,
| 0x8DAA59C1B53DD1CA,
| 0xB114F032228D463D,
| 0xDD5A2C3EAB3097CC,
| 0x8A585BA72AFE5EDF,
| 0xACEE7290F5BDF697,
| 0xD82A0F35332D743D,
| 0x871A49813FFC68A6,
| 0xA8E0DBE18FFB82D0,
| 0xD31912D9F3FA6384,
| 0x83EFABC8387C7E32,
| 0xA4EB96BA469B9DBF,
| 0xCE267C68D842852E,
| 0x80D80DC18729933D,
| 0xA10E1131E8F3F80C,
| 0xC951957E6330F60F,
| 0xFBA5FADDFBFD3393,
| 0x9D47BCCABD7E403C,
| 0xC499ABFD6CDDD04B,
| 0xF5C016FCC815445E,
| 0x99980E5DFD0D4ABB,
| 0xBFFE11F57C509D69,
| 0xEFFD9672DB64C4C4,
| 0x95FE7E07C91EFAFA,
| 0xBB7E1D89BB66B9B9,
| 0xEA5DA4EC2A406827,
| 0x927A87139A684118,
| 0xB71928D88102515E,
| 0xE4DF730EA142E5B6,
| 0x8F0BA7E924C9CF92,
| 0xB2CE91E36DFC4376,
| 0xDF82365C497B5454,
| 0x8BB161F9ADED14B4,
| 0xAE9DBA78196859E1,
| 0xDA4529161FC2705A,
| 0x886B39ADD3D98638,
| 0xAA86081948CFE7C6,
| 0xD5278A1F9B03E1B8,
| 0x8538B653C0E26D13,
| 0xA686E3E8B11B0858,
| 0xD0289CE2DD61CA6D,
| 0x8219620DCA5D1E84,
| 0xA29FBA913CF46625,
| 0xCB47A9358C317FAF,
| 0xFE199382EF3DDF9B,
| 0x9ECFFC31D586ABC1,
| 0xC683FB3E4AE856B1,
| 0xF824FA0DDDA26C5D,
| 0x9B171C48AA8583BA,
| 0xC1DCE35AD526E4A9,
| 0xF2541C318A709DD3,
| 0x9774919EF68662A4,
| 0xBD51B606B427FB4D,
| 0xECA623886131FA20,
| 0x93E7D6353CBF3C54,
| 0xB8E1CBC28BEF0B69,
| 0xE71A3EB32EEACE43,
| 0x9070672FFD52C0EA,
| 0xB48C80FBFCA77124,
| 0xE1AFA13AFBD14D6E,
| 0x8D0DC4C4DD62D064,
| 0xB05135F614BB847E,
| 0xDC65837399EA659D,
| 0x89BF722840327F82,
| 0xAC2F4EB2503F1F63,
| 0xD73B225EE44EE73B,
| 0x8684F57B4EB15085,
| 0xA82632DA225DA4A6,
| 0xD22FBF90AAF50DD0,
| 0x835DD7BA6AD928A2,
| 0xA4354DA9058F72CA,
| 0xCD42A11346F34F7D,
| 0x8049A4AC0C5811AE,
| 0xA05C0DD70F6E161A,
| 0xC873114CD3499BA0,
| 0xFA8FD5A0081C0288,
| 0x9C99E58405118195,
| 0xC3C05EE50655E1FA,
| 0xF4B0769E47EB5A79,
| 0x98EE4A22ECF3188C,
| 0xBF29DCABA82FDEAE,
| 0xEEF453D6923BD65A,
| 0x9558B4661B6565F8,
| 0xBAAEE17FA23EBF76,
| 0xE95A99DF8ACE6F54,
| 0x91D8A02BB6C10594,
| 0xB64EC836A47146FA,
| 0xE3E27A444D8D98B8,
| 0x8E6D8C6AB0787F73,
| 0xB208EF855C969F50,
| 0xDE8B2B66B3BC4724,
| 0x8B16FB203055AC76,
| 0xADDCB9E83C6B1794,
| 0xD953E8624B85DD79,
| 0x87D4713D6F33AA6C,
| 0xA9C98D8CCB009506,
| 0xD43BF0EFFDC0BA48,
| 0x84A57695FE98746D,
| 0xA5CED43B7E3E9188,
| 0xCF42894A5DCE35EA,
| 0x818995CE7AA0E1B2,
| 0xA1EBFB4219491A1F,
| 0xCA66FA129F9B60A7,
| 0xFD00B897478238D1,
| 0x9E20735E8CB16382,
| 0xC5A890362FDDBC63,
| 0xF712B443BBD52B7C,
| 0x9A6BB0AA55653B2D,
| 0xC1069CD4EABE89F9,
| 0xF148440A256E2C77,
| 0x96CD2A865764DBCA,
| 0xBC807527ED3E12BD,
| 0xEBA09271E88D976C,
| 0x93445B8731587EA3,
| 0xB8157268FDAE9E4C,
| 0xE61ACF033D1A45DF,
| 0x8FD0C16206306BAC,
| 0xB3C4F1BA87BC8697,
| 0xE0B62E2929ABA83C,
| 0x8C71DCD9BA0B4926,
| 0xAF8E5410288E1B6F,
| 0xDB71E91432B1A24B,
| 0x892731AC9FAF056F,
| 0xAB70FE17C79AC6CA,
| 0xD64D3D9DB981787D,
| 0x85F0468293F0EB4E,
| 0xA76C582338ED2622,
| 0xD1476E2C07286FAA,
| 0x82CCA4DB847945CA,
| 0xA37FCE126597973D,
| 0xCC5FC196FEFD7D0C,
| 0xFF77B1FCBEBCDC4F,
| 0x9FAACF3DF73609B1,
| 0xC795830D75038C1E,
| 0xF97AE3D0D2446F25,
| 0x9BECCE62836AC577,
| 0xC2E801FB244576D5,
| 0xF3A20279ED56D48A,
| 0x9845418C345644D7,
| 0xBE5691EF416BD60C,
| 0xEDEC366B11C6CB8F,
| 0x94B3A202EB1C3F39,
| 0xB9E08A83A5E34F08,
| 0xE858AD248F5C22CA,
| 0x91376C36D99995BE,
| 0xB58547448FFFFB2E,
| 0xE2E69915B3FFF9F9,
| 0x8DD01FAD907FFC3C,
| 0xB1442798F49FFB4B,
| 0xDD95317F31C7FA1D,
| 0x8A7D3EEF7F1CFC52,
| 0xAD1C8EAB5EE43B67,
| 0xD863B256369D4A41,
| 0x873E4F75E2224E68,
| 0xA90DE3535AAAE202,
| 0xD3515C2831559A83,
| 0x8412D9991ED58092,
| 0xA5178FFF668AE0B6,
| 0xCE5D73FF402D98E4,
| 0x80FA687F881C7F8E,
| 0xA139029F6A239F72,
| 0xC987434744AC874F,
| 0xFBE9141915D7A922,
| 0x9D71AC8FADA6C9B5,
| 0xC4CE17B399107C23,
| 0xF6019DA07F549B2B,
| 0x99C102844F94E0FB,
| 0xC0314325637A193A,
| 0xF03D93EEBC589F88,
| 0x96267C7535B763B5,
| 0xBBB01B9283253CA3,
| 0xEA9C227723EE8BCB,
| 0x92A1958A7675175F,
| 0xB749FAED14125D37,
| 0xE51C79A85916F485,
| 0x8F31CC0937AE58D3,
| 0xB2FE3F0B8599EF08,
| 0xDFBDCECE67006AC9,
| 0x8BD6A141006042BE,
| 0xAECC49914078536D,
| 0xDA7F5BF590966849,
| 0x888F99797A5E012D,
| 0xAAB37FD7D8F58179,
| 0xD5605FCDCF32E1D7,
| 0x855C3BE0A17FCD26,
| 0xA6B34AD8C9DFC070,
| 0xD0601D8EFC57B08C,
| 0x823C12795DB6CE57,
| 0xA2CB1717B52481ED,
| 0xCB7DDCDDA26DA269,
| 0xFE5D54150B090B03,
| 0x9EFA548D26E5A6E2,
| 0xC6B8E9B0709F109A,
| 0xF867241C8CC6D4C1,
| 0x9B407691D7FC44F8,
| 0xC21094364DFB5637,
| 0xF294B943E17A2BC4,
| 0x979CF3CA6CEC5B5B,
| 0xBD8430BD08277231,
| 0xECE53CEC4A314EBE,
| 0x940F4613AE5ED137,
| 0xB913179899F68584,
| 0xE757DD7EC07426E5,
| 0x9096EA6F3848984F,
| 0xB4BCA50B065ABE63,
| 0xE1EBCE4DC7F16DFC,
| 0x8D3360F09CF6E4BD,
| 0xB080392CC4349DED,
| 0xDCA04777F541C568,
| 0x89E42CAAF9491B61,
| 0xAC5D37D5B79B6239,
| 0xD77485CB25823AC7,
| 0x86A8D39EF77164BD,
| 0xA8530886B54DBDEC,
| 0xD267CAA862A12D67,
| 0x8380DEA93DA4BC60,
| 0xA46116538D0DEB78,
| 0xCD795BE870516656,
| 0x806BD9714632DFF6,
| 0xA086CFCD97BF97F4,
| 0xC8A883C0FDAF7DF0,
| 0xFAD2A4B13D1B5D6C,
| 0x9CC3A6EEC6311A64,
| 0xC3F490AA77BD60FD,
| 0xF4F1B4D515ACB93C,
| 0x991711052D8BF3C5,
| 0xBF5CD54678EEF0B7,
| 0xEF340A98172AACE5,
| 0x9580869F0E7AAC0F,
| 0xBAE0A846D2195713,
| 0xE998D258869FACD7,
| 0x91FF83775423CC06,
| 0xB67F6455292CBF08,
| 0xE41F3D6A7377EECA,
| 0x8E938662882AF53E,
| 0xB23867FB2A35B28E,
| 0xDEC681F9F4C31F31,
| 0x8B3C113C38F9F37F,
| 0xAE0B158B4738705F,
| 0xD98DDAEE19068C76,
| 0x87F8A8D4CFA417CA,
| 0xA9F6D30A038D1DBC,
| 0xD47487CC8470652B,
| 0x84C8D4DFD2C63F3B,
| 0xA5FB0A17C777CF0A,
| 0xCF79CC9DB955C2CC,
| 0x81AC1FE293D599C0,
| 0xA21727DB38CB0030,
| 0xCA9CF1D206FDC03C,
| 0xFD442E4688BD304B,
| 0x9E4A9CEC15763E2F,
| 0xC5DD44271AD3CDBA,
| 0xF7549530E188C129,
| 0x9A94DD3E8CF578BA,
| 0xC13A148E3032D6E8,
| 0xF18899B1BC3F8CA2,
| 0x96F5600F15A7B7E5,
| 0xBCB2B812DB11A5DE,
| 0xEBDF661791D60F56,
| 0x936B9FCEBB25C996,
| 0xB84687C269EF3BFB,
| 0xE65829B3046B0AFA,
| 0x8FF71A0FE2C2E6DC,
| 0xB3F4E093DB73A093,
| 0xE0F218B8D25088B8,
| 0x8C974F7383725573,
| 0xAFBD2350644EEAD0,
| 0xDBAC6C247D62A584,
| 0x894BC396CE5DA772,
| 0xAB9EB47C81F5114F,
| 0xD686619BA27255A3,
| 0x8613FD0145877586,
| 0xA798FC4196E952E7,
| 0xD17F3B51FCA3A7A1,
| 0x82EF85133DE648C5,
| 0xA3AB66580D5FDAF6,
| 0xCC963FEE10B7D1B3,
| 0xFFBBCFE994E5C620,
| 0x9FD561F1FD0F9BD4,
| 0xC7CABA6E7C5382C9,
| 0xF9BD690A1B68637B,
| 0x9C1661A651213E2D,
| 0xC31BFA0FE5698DB8,
| 0xF3E2F893DEC3F126,
| 0x986DDB5C6B3A76B8,
| 0xBE89523386091466,
| 0xEE2BA6C0678B597F,
| 0x94DB483840B717F0,
| 0xBA121A4650E4DDEC,
| 0xE896A0D7E51E1566,
| 0x915E2486EF32CD60,
| 0xB5B5ADA8AAFF80B8,
| 0xE3231912D5BF60E6,
| 0x8DF5EFABC5979C90,
| 0xB1736B96B6FD83B4,
| 0xDDD0467C64BCE4A1,
| 0x8AA22C0DBEF60EE4,
| 0xAD4AB7112EB3929E,
| 0xD89D64D57A607745,
| 0x87625F056C7C4A8B,
| 0xA93AF6C6C79B5D2E,
| 0xD389B47879823479,
| 0x843610CB4BF160CC,
| 0xA54394FE1EEDB8FF,
| 0xCE947A3DA6A9273E,
| 0x811CCC668829B887,
| 0xA163FF802A3426A9,
| 0xC9BCFF6034C13053,
| 0xFC2C3F3841F17C68,
| 0x9D9BA7832936EDC1,
| 0xC5029163F384A931,
| 0xF64335BCF065D37D,
| 0x99EA0196163FA42E,
| 0xC06481FB9BCF8D3A,
| 0xF07DA27A82C37088,
| 0x964E858C91BA2655,
| 0xBBE226EFB628AFEB,
| 0xEADAB0ABA3B2DBE5,
| 0x92C8AE6B464FC96F,
| 0xB77ADA0617E3BBCB,
| 0xE55990879DDCAABE,
| 0x8F57FA54C2A9EAB7,
| 0xB32DF8E9F3546564,
| 0xDFF9772470297EBD,
| 0x8BFBEA76C619EF36,
| 0xAEFAE51477A06B04,
| 0xDAB99E59958885C5,
| 0x88B402F7FD75539B,
| 0xAAE103B5FCD2A882,
| 0xD59944A37C0752A2,
| 0x857FCAE62D8493A5,
| 0xA6DFBD9FB8E5B88F,
| 0xD097AD07A71F26B2,
| 0x825ECC24C8737830,
| 0xA2F67F2DFA90563B,
| 0xCBB41EF979346BCA,
| 0xFEA126B7D78186BD,
| 0x9F24B832E6B0F436,
| 0xC6EDE63FA05D3144,
| 0xF8A95FCF88747D94,
| 0x9B69DBE1B548CE7D,
| 0xC24452DA229B021C,
| 0xF2D56790AB41C2A3,
| 0x97C560BA6B0919A6,
| 0xBDB6B8E905CB600F,
| 0xED246723473E3813,
| 0x9436C0760C86E30C,
| 0xB94470938FA89BCF,
| 0xE7958CB87392C2C3,
| 0x90BD77F3483BB9BA,
| 0xB4ECD5F01A4AA828,
| 0xE2280B6C20DD5232,
| 0x8D590723948A535F,
| 0xB0AF48EC79ACE837,
| 0xDCDB1B2798182245,
| 0x8A08F0F8BF0F156B,
| 0xAC8B2D36EED2DAC6,
| 0xD7ADF884AA879177,
| 0x86CCBB52EA94BAEB,
| 0xA87FEA27A539E9A5,
| 0xD29FE4B18E88640F,
| 0x83A3EEEEF9153E89,
| 0xA48CEAAAB75A8E2B,
| 0xCDB02555653131B6,
| 0x808E17555F3EBF12,
| 0xA0B19D2AB70E6ED6,
| 0xC8DE047564D20A8C,
| 0xFB158592BE068D2F,
| 0x9CED737BB6C4183D,
| 0xC428D05AA4751E4D,
| 0xF53304714D9265E0,
| 0x993FE2C6D07B7FAC,
| 0xBF8FDB78849A5F97,
| 0xEF73D256A5C0F77D,
| 0x95A8637627989AAE,
| 0xBB127C53B17EC159,
| 0xE9D71B689DDE71B0,
| 0x9226712162AB070E,
| 0xB6B00D69BB55C8D1,
| 0xE45C10C42A2B3B06,
| 0x8EB98A7A9A5B04E3,
| 0xB267ED1940F1C61C,
| 0xDF01E85F912E37A3,
| 0x8B61313BBABCE2C6,
| 0xAE397D8AA96C1B78,
| 0xD9C7DCED53C72256,
| 0x881CEA14545C7575,
| 0xAA242499697392D3,
| 0xD4AD2DBFC3D07788,
| 0x84EC3C97DA624AB5,
| 0xA6274BBDD0FADD62,
| 0xCFB11EAD453994BA,
| 0x81CEB32C4B43FCF5,
| 0xA2425FF75E14FC32,
| 0xCAD2F7F5359A3B3E,
| 0xFD87B5F28300CA0E,
| 0x9E74D1B791E07E48,
| 0xC612062576589DDB,
| 0xF79687AED3EEC551,
| 0x9ABE14CD44753B53,
| 0xC16D9A0095928A27,
| 0xF1C90080BAF72CB1,
| 0x971DA05074DA7BEF,
| 0xBCE5086492111AEB,
| 0xEC1E4A7DB69561A5,
| 0x9392EE8E921D5D07,
| 0xB877AA3236A4B449,
| 0xE69594BEC44DE15B,
| 0x901D7CF73AB0ACD9,
| 0xB424DC35095CD80F,
| 0xE12E13424BB40E13,
| 0x8CBCCC096F5088CC,
| 0xAFEBFF0BCB24AAFF,
| 0xDBE6FECEBDEDD5BF,
| 0x89705F4136B4A597,
| 0xABCC77118461CEFD,
| 0xD6BF94D5E57A42BC,
| 0x8637BD05AF6C69B6,
| 0xA7C5AC471B478423,
| 0xD1B71758E219652C,
| 0x83126E978D4FDF3B,
| 0xA3D70A3D70A3D70A,
| 0xCCCCCCCCCCCCCCCD,
| 0x8000000000000000,
| 0xA000000000000000,
| 0xC800000000000000,
| 0xFA00000000000000,
| 0x9C40000000000000,
| 0xC350000000000000,
| 0xF424000000000000,
| 0x9896800000000000,
| 0xBEBC200000000000,
| 0xEE6B280000000000,
| 0x9502F90000000000,
| 0xBA43B74000000000,
| 0xE8D4A51000000000,
| 0x9184E72A00000000,
| 0xB5E620F480000000,
| 0xE35FA931A0000000,
| 0x8E1BC9BF04000000,
| 0xB1A2BC2EC5000000,
| 0xDE0B6B3A76400000,
| 0x8AC7230489E80000,
| 0xAD78EBC5AC620000,
| 0xD8D726B7177A8000,
| 0x878678326EAC9000,
| 0xA968163F0A57B400,
| 0xD3C21BCECCEDA100,
| 0x84595161401484A0,
| 0xA56FA5B99019A5C8,
| 0xCECB8F27F4200F3A,
| 0x813F3978F8940984,
| 0xA18F07D736B90BE5,
| 0xC9F2C9CD04674EDF,
| 0xFC6F7C4045812296,
| 0x9DC5ADA82B70B59E,
| 0xC5371912364CE305,
| 0xF684DF56C3E01BC7,
| 0x9A130B963A6C115C,
| 0xC097CE7BC90715B3,
| 0xF0BDC21ABB48DB20,
| 0x96769950B50D88F4,
| 0xBC143FA4E250EB31,
| 0xEB194F8E1AE525FD,
| 0x92EFD1B8D0CF37BE,
| 0xB7ABC627050305AE,
| 0xE596B7B0C643C719,
| 0x8F7E32CE7BEA5C70,
| 0xB35DBF821AE4F38C,
| 0xE0352F62A19E306F,
| 0x8C213D9DA502DE45,
| 0xAF298D050E4395D7,
| 0xDAF3F04651D47B4C,
| 0x88D8762BF324CD10,
| 0xAB0E93B6EFEE0054,
| 0xD5D238A4ABE98068,
| 0x85A36366EB71F041,
| 0xA70C3C40A64E6C52,
| 0xD0CF4B50CFE20766,
| 0x82818F1281ED44A0,
| 0xA321F2D7226895C8,
| 0xCBEA6F8CEB02BB3A,
| 0xFEE50B7025C36A08,
| 0x9F4F2726179A2245,
| 0xC722F0EF9D80AAD6,
| 0xF8EBAD2B84E0D58C,
| 0x9B934C3B330C8577,
| 0xC2781F49FFCFA6D5,
| 0xF316271C7FC3908B,
| 0x97EDD871CFDA3A57,
| 0xBDE94E8E43D0C8EC,
| 0xED63A231D4C4FB27,
| 0x945E455F24FB1CF9,
| 0xB975D6B6EE39E437,
| 0xE7D34C64A9C85D44,
| 0x90E40FBEEA1D3A4B,
| 0xB51D13AEA4A488DD,
| 0xE264589A4DCDAB15,
| 0x8D7EB76070A08AED,
| 0xB0DE65388CC8ADA8,
| 0xDD15FE86AFFAD912,
| 0x8A2DBF142DFCC7AB,
| 0xACB92ED9397BF996,
| 0xD7E77A8F87DAF7FC,
| 0x86F0AC99B4E8DAFD,
| 0xA8ACD7C0222311BD,
| 0xD2D80DB02AABD62C,
| 0x83C7088E1AAB65DB,
| 0xA4B8CAB1A1563F52,
| 0xCDE6FD5E09ABCF27,
| 0x80B05E5AC60B6178,
| 0xA0DC75F1778E39D6,
| 0xC913936DD571C84C,
| 0xFB5878494ACE3A5F,
| 0x9D174B2DCEC0E47B,
| 0xC45D1DF942711D9A,
| 0xF5746577930D6501,
| 0x9968BF6ABBE85F20,
| 0xBFC2EF456AE276E9,
| 0xEFB3AB16C59B14A3,
| 0x95D04AEE3B80ECE6,
| 0xBB445DA9CA61281F,
| 0xEA1575143CF97227,
| 0x924D692CA61BE758,
| 0xB6E0C377CFA2E12E,
| 0xE498F455C38B997A,
| 0x8EDF98B59A373FEC,
| 0xB2977EE300C50FE7,
| 0xDF3D5E9BC0F653E1,
| 0x8B865B215899F46D,
| 0xAE67F1E9AEC07188,
| 0xDA01EE641A708DEA,
| 0x884134FE908658B2,
| 0xAA51823E34A7EEDF,
| 0xD4E5E2CDC1D1EA96,
| 0x850FADC09923329E,
| 0xA6539930BF6BFF46,
| 0xCFE87F7CEF46FF17,
| 0x81F14FAE158C5F6E,
| 0xA26DA3999AEF774A,
| 0xCB090C8001AB551C,
| 0xFDCB4FA002162A63,
| 0x9E9F11C4014DDA7E,
| 0xC646D63501A1511E,
| 0xF7D88BC24209A565,
| 0x9AE757596946075F,
| 0xC1A12D2FC3978937,
| 0xF209787BB47D6B85,
| 0x9745EB4D50CE6333,
| 0xBD176620A501FC00,
| 0xEC5D3FA8CE427B00,
| 0x93BA47C980E98CE0,
| 0xB8A8D9BBE123F018,
| 0xE6D3102AD96CEC1E,
| 0x9043EA1AC7E41393,
| 0xB454E4A179DD1877,
| 0xE16A1DC9D8545E95,
| 0x8CE2529E2734BB1D,
| 0xB01AE745B101E9E4,
| 0xDC21A1171D42645D,
| 0x899504AE72497EBA,
| 0xABFA45DA0EDBDE69,
| 0xD6F8D7509292D603,
| 0x865B86925B9BC5C2,
| 0xA7F26836F282B733,
| 0xD1EF0244AF2364FF,
| 0x8335616AED761F1F,
| 0xA402B9C5A8D3A6E7,
| 0xCD036837130890A1,
| 0x802221226BE55A65,
| 0xA02AA96B06DEB0FE,
| 0xC83553C5C8965D3D,
| 0xFA42A8B73ABBF48D,
| 0x9C69A97284B578D8,
| 0xC38413CF25E2D70E,
| 0xF46518C2EF5B8CD1,
| 0x98BF2F79D5993803,
| 0xBEEEFB584AFF8604,
| 0xEEAABA2E5DBF6785,
| 0x952AB45CFA97A0B3,
| 0xBA756174393D88E0,
| 0xE912B9D1478CEB17,
| 0x91ABB422CCB812EF,
| 0xB616A12B7FE617AA,
| 0xE39C49765FDF9D95,
| 0x8E41ADE9FBEBC27D,
| 0xB1D219647AE6B31C,
| 0xDE469FBD99A05FE3,
| 0x8AEC23D680043BEE,
| 0xADA72CCC20054AEA,
| 0xD910F7FF28069DA4,
| 0x87AA9AFF79042287,
| 0xA99541BF57452B28,
| 0xD3FA922F2D1675F2,
| 0x847C9B5D7C2E09B7,
| 0xA59BC234DB398C25,
| 0xCF02B2C21207EF2F,
| 0x8161AFB94B44F57D,
| 0xA1BA1BA79E1632DC,
| 0xCA28A291859BBF93,
| 0xFCB2CB35E702AF78,
| 0x9DEFBF01B061ADAB,
| 0xC56BAEC21C7A1916,
| 0xF6C69A72A3989F5C,
| 0x9A3C2087A63F6399,
| 0xC0CB28A98FCF3C80,
| 0xF0FDF2D3F3C30B9F,
| 0x969EB7C47859E744,
| 0xBC4665B596706115,
| 0xEB57FF22FC0C795A,
| 0x9316FF75DD87CBD8,
| 0xB7DCBF5354E9BECE,
| 0xE5D3EF282A242E82,
| 0x8FA475791A569D11,
| 0xB38D92D760EC4455,
| 0xE070F78D3927556B,
| 0x8C469AB843B89563,
| 0xAF58416654A6BABB,
| 0xDB2E51BFE9D0696A,
| 0x88FCF317F22241E2,
| 0xAB3C2FDDEEAAD25B,
| 0xD60B3BD56A5586F2,
| 0x85C7056562757457,
| 0xA738C6BEBB12D16D,
| 0xD106F86E69D785C8,
| 0x82A45B450226B39D,
| 0xA34D721642B06084,
| 0xCC20CE9BD35C78A5,
| 0xFF290242C83396CE,
| 0x9F79A169BD203E41,
| 0xC75809C42C684DD1,
| 0xF92E0C3537826146,
| 0x9BBCC7A142B17CCC,
| 0xC2ABF989935DDBFE,
| 0xF356F7EBF83552FE,
| 0x98165AF37B2153DF,
| 0xBE1BF1B059E9A8D6,
| 0xEDA2EE1C7064130C,
| 0x9485D4D1C63E8BE8,
| 0xB9A74A0637CE2EE1,
| 0xE8111C87C5C1BA9A,
| 0x910AB1D4DB9914A0,
| 0xB54D5E4A127F59C8,
| 0xE2A0B5DC971F303A,
| 0x8DA471A9DE737E24,
| 0xB10D8E1456105DAD,
| 0xDD50F1996B947519,
| 0x8A5296FFE33CC930,
| 0xACE73CBFDC0BFB7B,
| 0xD8210BEFD30EFA5A,
| 0x8714A775E3E95C78,
| 0xA8D9D1535CE3B396,
| 0xD31045A8341CA07C,
| 0x83EA2B892091E44E,
| 0xA4E4B66B68B65D61,
| 0xCE1DE40642E3F4B9,
| 0x80D2AE83E9CE78F4,
| 0xA1075A24E4421731,
| 0xC94930AE1D529CFD,
| 0xFB9B7CD9A4A7443C,
| 0x9D412E0806E88AA6,
| 0xC491798A08A2AD4F,
| 0xF5B5D7EC8ACB58A3,
| 0x9991A6F3D6BF1766,
| 0xBFF610B0CC6EDD3F,
| 0xEFF394DCFF8A948F,
| 0x95F83D0A1FB69CD9,
| 0xBB764C4CA7A44410,
| 0xEA53DF5FD18D5514,
| 0x92746B9BE2F8552C,
| 0xB7118682DBB66A77,
| 0xE4D5E82392A40515,
| 0x8F05B1163BA6832D,
| 0xB2C71D5BCA9023F8,
| 0xDF78E4B2BD342CF7,
| 0x8BAB8EEFB6409C1A,
| 0xAE9672ABA3D0C321,
| 0xDA3C0F568CC4F3E9,
| 0x8865899617FB1871,
| 0xAA7EEBFB9DF9DE8E,
| 0xD51EA6FA85785631,
| 0x8533285C936B35DF,
| 0xA67FF273B8460357,
| 0xD01FEF10A657842C,
| 0x8213F56A67F6B29C,
| 0xA298F2C501F45F43,
| 0xCB3F2F7642717713,
| 0xFE0EFB53D30DD4D8,
| 0x9EC95D1463E8A507,
| 0xC67BB4597CE2CE49,
| 0xF81AA16FDC1B81DB,
| 0x9B10A4E5E9913129,
| 0xC1D4CE1F63F57D73,
| 0xF24A01A73CF2DCD0,
| 0x976E41088617CA02,
| 0xBD49D14AA79DBC82,
| 0xEC9C459D51852BA3,
| 0x93E1AB8252F33B46,
| 0xB8DA1662E7B00A17,
| 0xE7109BFBA19C0C9D,
| 0x906A617D450187E2,
| 0xB484F9DC9641E9DB,
| 0xE1A63853BBD26451,
| 0x8D07E33455637EB3,
| 0xB049DC016ABC5E60,
| 0xDC5C5301C56B75F7,
| 0x89B9B3E11B6329BB,
| 0xAC2820D9623BF429,
| 0xD732290FBACAF134,
| 0x867F59A9D4BED6C0,
| 0xA81F301449EE8C70,
| 0xD226FC195C6A2F8C,
| 0x83585D8FD9C25DB8,
| 0xA42E74F3D032F526,
| 0xCD3A1230C43FB26F,
| 0x80444B5E7AA7CF85,
| 0xA0555E361951C367,
| 0xC86AB5C39FA63441,
| 0xFA856334878FC151,
| 0x9C935E00D4B9D8D2,
| 0xC3B8358109E84F07,
| 0xF4A642E14C6262C9,
| 0x98E7E9CCCFBD7DBE,
| 0xBF21E44003ACDD2D,
| 0xEEEA5D5004981478,
| 0x95527A5202DF0CCB,
| 0xBAA718E68396CFFE,
| 0xE950DF20247C83FD,
| 0x91D28B7416CDD27E,
| 0xB6472E511C81471E,
| 0xE3D8F9E563A198E5,
| 0x8E679C2F5E44FF8F,
| 0xB201833B35D63F73,
| 0xDE81E40A034BCF50,
| 0x8B112E86420F6192,
| 0xADD57A27D29339F6,
| 0xD94AD8B1C7380874,
| 0x87CEC76F1C830549,
| 0xA9C2794AE3A3C69B,
| 0xD433179D9C8CB841,
| 0x849FEEC281D7F329,
| 0xA5C7EA73224DEFF3,
| 0xCF39E50FEAE16BF0,
| 0x81842F29F2CCE376,
| 0xA1E53AF46F801C53,
| 0xCA5E89B18B602368,
| 0xFCF62C1DEE382C42,
| 0x9E19DB92B4E31BA9,
| 0xC5A05277621BE294,
| 0xF70867153AA2DB39,
| 0x9A65406D44A5C903,
| 0xC0FE908895CF3B44,
| 0xF13E34AABB430A15,
| 0x96C6E0EAB509E64D,
| 0xBC789925624C5FE1,
| 0xEB96BF6EBADF77D9,
| 0x933E37A534CBAAE8,
| 0xB80DC58E81FE95A1,
| 0xE61136F2227E3B0A,
| 0x8FCAC257558EE4E6,
| 0xB3BD72ED2AF29E20,
| 0xE0ACCFA875AF45A8,
| 0x8C6C01C9498D8B89,
| 0xAF87023B9BF0EE6B,
| 0xDB68C2CA82ED2A06,
| 0x892179BE91D43A44,
| 0xAB69D82E364948D4,
| 0xD6444E39C3DB9B0A,
| 0x85EAB0E41A6940E6,
| 0xA7655D1D2103911F,
| 0xD13EB46469447567,
| 0x82C730BEC1CAC961,
| 0xA378FCEE723D7BB9,
| 0xCC573C2A0ECCDAA7,
| 0xFF6D0B3492801151,
| 0x9FA42700DB900AD2,
| 0xC78D30C112740D87,
| 0xF9707CF1571110E9,
| 0x9BE64E16D66AAA91,
| 0xC2DFE19C8C055536,
| 0xF397DA03AF06AA83,
| 0x983EE8424D642A92,
| 0xBE4EA252E0BD3537,
| 0xEDE24AE798EC8284,
| 0x94AD6ED0BF93D193,
| 0xB9D8CA84EF78C5F7,
| 0xE84EFD262B56F775,
| 0x91315E37DB165AA9,
| 0xB57DB5C5D1DBF153,
| 0xE2DD23374652EDA8,
| 0x8DCA36028BF3D489,
| 0xB13CC3832EF0C9AC,
| 0xDD8BF463FAACFC16,
| 0x8A7778BE7CAC1D8E,
| 0xAD1556EE1BD724F1,
| 0xD85AACA9A2CCEE2E,
| 0x8738ABEA05C014DD,
| 0xA906D6E487301A14,
| 0xD3488C9DA8FC2099,
| 0x840D57E2899D945F,
| 0xA510ADDB2C04F977,
| 0xCE54D951F70637D5,
| 0x80F507D33A63E2E5,
| 0xA13249C808FCDB9F,
| 0xC97EDC3A0B3C1286,
| 0xFBDE93488E0B1728,
| 0x9D6B1C0D58C6EE79,
| 0xC4C5E310AEF8AA17,
| 0xF5F75BD4DAB6D49D,
| 0x99BA996508B244E2,
| 0xC0293FBE4ADED61B,
| 0xF0338FADDD968BA1,
| 0x962039CCAA7E1745,
| 0xBBA8483FD51D9D16,
| 0xEA925A4FCA65045B,
| 0x929B7871DE7F22B9,
| 0xB742568E561EEB67,
| 0xE512EC31EBA6A641,
| 0x8F2BD39F334827E9,
| 0xB2F6C887001A31E3,
| 0xDFB47AA8C020BE5C,
| 0x8BD0CCA9781476F9,
| 0xAEC4FFD3D61994B8,
| 0xDA763FC8CB9FF9E6,
| 0x8889E7DD7F43FC2F,
| 0xAAAC61D4DF14FB3B,
| 0xD5577A4A16DA3A0A,
| 0x8556AC6E4E486446,
| 0xA6AC5789E1DA7D58,
| 0xD0576D6C5A511CAE,
| 0x8236A463B872B1ED,
| 0xA2C44D7CA68F5E68,
| 0xCB7560DBD0333602,
| 0xFE52B912C4400382,
| 0x9EF3B3ABBAA80231,
| 0xC6B0A096A95202BE,
| 0xF85CC8BC53A6836D,
| 0x9B39FD75B4481224,
| 0xC2087CD3215A16AD,
| 0xF28A9C07E9B09C59,
| 0x9796A184F20E61B7,
| 0xBD7C49E62E91FA25,
| 0xECDB5C5FBA3678AF,
| 0x940919BBD4620B6D,
| 0xB90B602AC97A8E48,
| 0xE74E38357BD931DB,
| 0x9090E3216D67BF29,
| 0xB4B51BE9C8C1AEF3,
| 0xE1E262E43AF21AAF,
| 0x8D2D7DCEA4D750AE,
| 0xB078DD424E0D24D9,
| 0xDC971492E1906E0F,
| 0x89DE6CDBCCFA44CA,
| 0xAC560812C038D5FC,
| 0xD76B8A1770470B7B,
| 0x86A3364EA62C672D,
| 0xA84C03E24FB780F8,
| 0xD25F04DAE3A56136,
| 0x837B6308CE475CC2,
| 0xA45A3BCB01D933F2,
| 0xCD70CABDC24F80EF,
| 0x80667EB69971B095,
| 0xA0801E643FCE1CBB,
| 0xC8A025FD4FC1A3E9,
| 0xFAC82F7CA3B20CE4,
| 0x9CBD1DADE64F480E,
| 0xC3EC65195FE31A12,
| 0xF4E77E5FB7DBE096,
| 0x9910AEFBD2E96C5E,
| 0xBF54DABAC7A3C775,
| 0xEF2A1169798CB953,
| 0x957A4AE1EBF7F3D4,
| 0xBAD8DD9A66F5F0C9,
| 0xE98F150100B36CFB,
| 0x91F96D20A070241D,
| 0xB677C868C88C2D24,
| 0xE415BA82FAAF386D,
| 0x8E8D9491DCAD8344,
| 0xB230F9B653D8E415,
| 0xDEBD3823E8CF1D1A,
| 0x8B36431671817230,
| 0xAE03D3DC0DE1CEBD,
| 0xD984C8D3115A426C,
| 0x87F2FD83EAD86983,
| 0xA9EFBCE4E58E83E4,
| 0xD46BAC1E1EF224DD,
| 0x84C34B92D357570A,
| 0xA5F41E77882D2CCD,
| 0xCF7126156A387800,
| 0x81A6B7CD62634B00,
| 0xA21065C0BAFC1DC0,
| 0xCA947F30E9BB2530,
| 0xFD399EFD2429EE7C,
| 0x9E44035E369A350D,
| 0xC5D50435C440C251,
| 0xF74A45433550F2E5,
| 0x9A8E6B4A015297CF,
| 0xC132061C81A73DC3,
| 0xF17E87A3A2110D34,
| 0x96EF14C6454AA840,
| 0xBCAAD9F7D69D5250,
| 0xEBD59075CC44A6E4,
| 0x93657A499FAAE84F,
| 0xB83ED8DC0795A262,
| 0xE64E8F13097B0AFB,
| 0x8FF1196BE5ECE6DD,
| 0xB3ED5FC6DF682094,
| 0xE0E8B7B8974228B9,
| 0x8C9172D35E895974,
| 0xAFB5CF88362BAFD1,
| 0xDBA3436A43B69BC5,
| 0x89460A226A52215B,
| 0xAB978CAB04E6A9B2,
| 0xD67D6FD5C620541E,
| 0x860E65E59BD43493,
| 0xA791FF5F02C941B8,
| 0xD1767F36C37B9226,
| 0x82EA0F823A2D3B57,
| 0xA3A49362C8B88A2D,
| 0xCC8DB83B7AE6ACB9,
| 0xFFB1264A59A057E7,
| 0x9FCEB7EE780436F0,
| 0xC7C265EA160544AC,
| 0xF9B2FF649B8695D7,
| 0x9C0FDF9EE1341DA7,
| 0xC313D78699812510,
| 0xF3D8CD683FE16E54,
| 0x9867806127ECE4F5,
| 0xBE81607971E81E32,
| 0xEE21B897CE6225BE,
| 0x94D5135EE0FD5797,
| 0xBA0A5836993CAD7D,
| 0xE88CEE443F8BD8DC,
| 0x915814EAA7B76789,
| 0xB5AE1A2551A5416C,
| 0xE319A0AEA60E91C7,
|];
|
|static immutable align(16) ulong[1025] p10_coefficients_l = [
| 0x7132D332E3F204D5,
| 0x8D7F87FF9CEE860A,
| 0x70DF69FF842A278D,
| 0xC68BA23FB29A58B8,
| 0xB82E8ACF9F40EEE6,
| 0xA63A2D8387112A9F,
| 0xA7E45C72346ABAA4,
| 0xD1DD738EC185694D,
| 0xC654D07271E6C3A0,
| 0xDBF5024787303A44,
| 0x12F242D968FC48D5,
| 0x17AED38FC33B5B0A,
| 0x8ECD4439DA0518E6,
| 0x7280954850865F20,
| 0x4F20BA9A64A7F6E8,
| 0x917474A07EE8FA51,
| 0xB5D191C89EA338E5,
| 0xE345F63AC64C071E,
| 0x9C1773C977DF08E6,
| 0x218EA85DEAEB6590,
| 0x29F2527565A63EF4,
| 0xF46EE712BF0FCEB0,
| 0x18C5506BB769E12E,
| 0x1EF6A486A544597A,
| 0xA6B44DA84E956FD8,
| 0x6830B089311D65E7,
| 0x423CDCAB7D64BF61,
| 0x52CC13D65CBDEF39,
| 0xB3BF8C65F9F6B584,
| 0x20AF6F7F787462E5,
| 0x68DB4B5F56917B9E,
| 0x81890F1B961AED43,
| 0x61EB52E27BA1A893,
| 0xFA66279B1A8A12B8,
| 0x7C7FD8C0F0964BB3,
| 0x1B9FCEF12CBBDEA0,
| 0xE287C2AD77EAD648,
| 0xAD94D9AC6AF2C5ED,
| 0x98FA101785AF7768,
| 0xBF38941D671B5542,
| 0xF7835C9260711549,
| 0x756433B6F88D5A9C,
| 0x12BD40A4B6B0B143,
| 0x4BB64866F22E6ECA,
| 0xDEA3DA80AEBA0A7C,
| 0x164CD120DA688D1B,
| 0x5BE005691102B062,
| 0xB96C0361AAA1AE3D,
| 0x67C7043A154A19CC,
| 0x01B8C5489A9CA040,
| 0x01137B4D60A1E428,
| 0xC1585A20B8CA5D32,
| 0xB1AE70A8E6FCF47E,
| 0x4F0D0669905E18CF,
| 0xA2D04803F4759F02,
| 0xCB845A04F19306C3,
| 0x1F32B84316FBE43A,
| 0x66FF6653DCBADD48,
| 0x00BF3FE8D3E9949A,
| 0x807787F18471FCE1,
| 0x209569EDE58E7C19,
| 0xA8BAC4695EF21B1F,
| 0x6974BAC1DB5750F3,
| 0x03D1E972522D2530,
| 0x84C663CEE6B86E7C,
| 0xD2FBFE615033450E,
| 0x87BAFDF9A4401651,
| 0xE9A9BD780D501BE5,
| 0x520A166B0852116F,
| 0xE68C9C05CA6695CB,
| 0xA02FC3073D003B3E,
| 0x241DD9E486202507,
| 0x6D25505DA7A82E48,
| 0x886EA475119239DA,
| 0xEA8A4D9255F6C851,
| 0xD296707B75BA3D33,
| 0x073C0C9A5328CC7F,
| 0xC90B0FC0E7F2FF9F,
| 0x3DA6E9D890F7DFC3,
| 0x0D10A44EB535D7B4,
| 0xD054CD6262834DA1,
| 0xA235005D7D921085,
| 0xCAC24074DCF694A6,
| 0x3D72D092143439D0,
| 0x6667C25B4CA0A422,
| 0x4001B2F21FC8CD2A,
| 0x10021FAEA7BB0075,
| 0x8A0153CD28D4E049,
| 0x2C81A8C0730A185B,
| 0xB7A212F08FCC9E72,
| 0x12C54BD659DFE307,
| 0x97769ECBF057DBC9,
| 0xBD54467EEC6DD2BB,
| 0x5654AC0F53C4A3B5,
| 0x2BE9D71328B5CCA2,
| 0xF6E44CD7F2E33FCB,
| 0x1A4EB006F7CE07DF,
| 0xA0E25C08B5C189D7,
| 0x891AF30AE331EC4C,
| 0x35B0D7E6CDFF33B0,
| 0xC31D0DE0817F009C,
| 0x73E45158A1DEC0C2,
| 0x086EB2D7652B387A,
| 0x4A8A5F8D3E760698,
| 0x5D2CF7708E13883E,
| 0x3478354CB1986A4D,
| 0x00CB214FEEFF4270,
| 0x00FDE9A3EABF130C,
| 0xC13D640CE56ED7D0,
| 0x98C65E880F6546E2,
| 0x3EF7F62A133E989A,
| 0x8EB5F3B4980E3EC1,
| 0x3931B850DF08E738,
| 0xC77E266516CB2106,
| 0xF95DAFFE5C7DE948,
| 0x5BDA8DFEF9CEB1CD,
| 0x72D1317EB8425E40,
| 0x0F857DDE6652F5D0,
| 0xC9B36EAAFFF3D9A2,
| 0x3C204A55BFF0D00B,
| 0xCB285CEB2FED040E,
| 0x5EF93A12FDF42288,
| 0x76B78897BD712B2B,
| 0xD4656ABDACCD75F5,
| 0x24BF62B68C0069B9,
| 0x2DEF3B642F008428,
| 0xB96B0A3D3AC0A531,
| 0xD3E2E66644B8673F,
| 0x88DB9FFFD5E6810F,
| 0x6B1287FFCB602152,
| 0x62EB94FFDF1C14D3,
| 0x7BA67A3FD6E31A08,
| 0xDA9018CFCC9BE08A,
| 0x91341F03BFC2D8AD,
| 0x9AC0936257D9C76C,
| 0xC170B83AEDD03947,
| 0xF1CCE649A9444799,
| 0x17200FEE09CAACC0,
| 0x9CE813E98C3D57EF,
| 0xC42218E3EF4CADEB,
| 0xBA954F8E758FECB3,
| 0xA93AA37212F3E7E0,
| 0xD3894C4E97B0E1D8,
| 0xE435CFB11ECE8D27,
| 0xDD43439D66823071,
| 0x14941484C022BC8D,
| 0xECDC8CD2F815B5D8,
| 0x6813B007B61B234E,
| 0x82189C09A3A1EC21,
| 0x714F618606453395,
| 0x8DA339E787D6807A,
| 0xF10C086169CC2099,
| 0x16A7853CE21F945F,
| 0x9C51668C1AA77977,
| 0x4365C02F215157D5,
| 0x0A1F981D74D2D6E5,
| 0x4CA77E24D2078C9E,
| 0xDFD15DAE06896FC6,
| 0xEBE2DA8CC415E5DC,
| 0x66DB912FF51B5F53,
| 0x0092757BF2623727,
| 0x205B896D777D6279,
| 0xA8726BC8D55CBB17,
| 0x128F06BB0AB3E9DD,
| 0x1732C869CD60E454,
| 0x0E7FBD42205C8EB4,
| 0x521FAC92A873B261,
| 0xE6A797B752909EFA,
| 0x9028BED2939A635C,
| 0x7432EE873880FC33,
| 0x113FAA2906A13B40,
| 0x4AC7CA59A424C508,
| 0x5D79BCF00D2DF64A,
| 0xF4D82C2C107973DC,
| 0x79071B9B8A4BE86A,
| 0x9748E2826CDEE284,
| 0xFD1B1B2308169B25,
| 0xFE30F0F5E50E20F7,
| 0xBDBD2D335E51A935,
| 0xAD2C788035E61382,
| 0x4C3BCB5021AFCC31,
| 0xDF4ABE242A1BBF3E,
| 0xD71D6DAD34A2AF0D,
| 0x8672648C40E5AD68,
| 0x680EFDAF511F18C2,
| 0x0212BD1B2566DEF3,
| 0x014BB630F7604B58,
| 0x419EA3BD35385E2E,
| 0x52064CAC828675B9,
| 0x7343EFEBD1940994,
| 0x1014EBE6C5F90BF9,
| 0xD41A26E077774EF7,
| 0x8920B098955522B5,
| 0x55B46E5F5D5535B1,
| 0xEB2189F734AA831D,
| 0xA5E9EC7501D523E4,
| 0x47B233C92125366F,
| 0x999EC0BB696E840A,
| 0xC00670EA43CA250D,
| 0x380406926A5E5728,
| 0xC605083704F5ECF2,
| 0xF7864A44C633682F,
| 0x7AB3EE6AFBE0211D,
| 0x5960EA05BAD82965,
| 0x6FB92487298E33BE,
| 0xA5D3B6D479F8E057,
| 0x8F48A4899877186C,
| 0x331ACDABFE94DE87,
| 0x9FF0C08B7F1D0B15,
| 0x07ECF0AE5EE44DDA,
| 0xC9E82CD9F69D6150,
| 0xBE311C083A225CD2,
| 0x6DBD630A48AAF407,
| 0x092CBBCCDAD5B108,
| 0x25BBF56008C58EA5,
| 0xAF2AF2B80AF6F24E,
| 0x1AF5AF660DB4AEE2,
| 0x50D98D9FC890ED4D,
| 0xE50FF107BAB528A1,
| 0x1E53ED49A96272C9,
| 0x25E8E89C13BB0F7B,
| 0x77B191618C54E9AD,
| 0xD59DF5B9EF6A2418,
| 0x4B0573286B44AD1E,
| 0x4EE367F9430AEC33,
| 0x229C41F793CDA73F,
| 0x6B43527578C1110F,
| 0x830A13896B78AAAA,
| 0x23CC986BC656D554,
| 0x2CBFBE86B7EC8AA9,
| 0x7BF7D71432F3D6AA,
| 0xDAF5CCD93FB0CC54,
| 0xD1B3400F8F9CFF69,
| 0x23100809B9C21FA2,
| 0xABD40A0C2832A78A,
| 0x16C90C8F323F516D,
| 0xAE3DA7D97F6792E4,
| 0x99CD11CFDF41779D,
| 0x40405643D711D584,
| 0x482835EA666B2572,
| 0xDA3243650005EECF,
| 0x90BED43E40076A83,
| 0x5A7744A6E804A292,
| 0x711515D0A205CB36,
| 0x0D5A5B44CA873E04,
| 0xE858790AFE9486C2,
| 0x626E974DBE39A873,
| 0xFB0A3D212DC81290,
| 0x7CE66634BC9D0B9A,
| 0x1C1FFFC1EBC44E80,
| 0xA327FFB266B56220,
| 0x4BF1FF9F0062BAA8,
| 0x6F773FC3603DB4A9,
| 0xCB550FB4384D21D4,
| 0x7E2A53A146606A48,
| 0x2EDA7444CBFC426D,
| 0xFA911155FEFB5309,
| 0x793555AB7EBA27CB,
| 0x4BC1558B2F3458DF,
| 0x9EB1AAEDFB016F16,
| 0x465E15A979C1CADC,
| 0x0BFACD89EC191ECA,
| 0xCEF980EC671F667C,
| 0x82B7E12780E7401B,
| 0xD1B2ECB8B0908811,
| 0x861FA7E6DCB4AA15,
| 0x67A791E093E1D49A,
| 0xE0C8BB2C5C6D24E0,
| 0x58FAE9F773886E19,
| 0xAF39A475506A899F,
| 0x6D8406C952429603,
| 0xC8E5087BA6D33B84,
| 0xFB1E4A9A90880A65,
| 0x5CF2EEA09A55067F,
| 0xF42FAA48C0EA481F,
| 0xF13B94DAF124DA27,
| 0x76C53D08D6B70858,
| 0x54768C4B0C64CA6E,
| 0xA9942F5DCF7DFD0A,
| 0xD3F93B35435D7C4C,
| 0xC47BC5014A1A6DB0,
| 0x359AB6419CA1091B,
| 0xC30163D203C94B62,
| 0x79E0DE63425DCF1D,
| 0x985915FC12F542E5,
| 0x3E6F5B7B17B2939E,
| 0xA705992CEECF9C43,
| 0x50C6FF782A838353,
| 0xA4F8BF5635246428,
| 0x871B7795E136BE99,
| 0x28E2557B59846E3F,
| 0x331AEADA2FE589CF,
| 0x3FF0D2C85DEF7622,
| 0x0FED077A756B53AA,
| 0xD3E8495912C62894,
| 0x64712DD7ABBBD95D,
| 0xBD8D794D96AACFB4,
| 0xECF0D7A0FC5583A1,
| 0xF41686C49DB57245,
| 0x311C2875C522CED6,
| 0x7D633293366B828B,
| 0xAE5DFF9C02033197,
| 0xD9F57F830283FDFD,
| 0xD072DF63C324FD7C,
| 0x4247CB9E59F71E6D,
| 0x52D9BE85F074E609,
| 0x67902E276C921F8B,
| 0x00BA1CD8A3DB53B7,
| 0x80E8A40ECCD228A5,
| 0x6122CD128006B2CE,
| 0x796B805720085F81,
| 0xCBE3303674053BB1,
| 0xBEDBFC4411068A9D,
| 0xEE92FB5515482D44,
| 0x751BDD152D4D1C4B,
| 0xD262D45A78A0635D,
| 0x86FB897116C87C35,
| 0xD45D35E6AE3D4DA1,
| 0x8974836059CCA109,
| 0x2BD1A438703FC94B,
| 0x7B6306A34627DDCF,
| 0x1A3BC84C17B1D543,
| 0x20CABA5F1D9E4A94,
| 0x547EB47B7282EE9C,
| 0xE99E619A4F23AA43,
| 0x6405FA00E2EC94D4,
| 0xDE83BC408DD3DD05,
| 0x9624AB50B148D446,
| 0x3BADD624DD9B0957,
| 0xE54CA5D70A80E5D6,
| 0x5E9FCF4CCD211F4C,
| 0x7647C3200069671F,
| 0x29ECD9F40041E073,
| 0xF468107100525890,
| 0x7182148D4066EEB4,
| 0xC6F14CD848405531,
| 0xB8ADA00E5A506A7D,
| 0xA6D90811F0E4851C,
| 0x908F4A166D1DA663,
| 0x9A598E4E043287FE,
| 0x40EFF1E1853F29FE,
| 0xD12BEE59E68EF47D,
| 0x82BB74F8301958CE,
| 0xE36A52363C1FAF02,
| 0xDC44E6C3CB279AC2,
| 0x29AB103A5EF8C0B9,
| 0x7415D448F6B6F0E8,
| 0x111B495B3464AD21,
| 0xCAB10DD900BEEC35,
| 0x3D5D514F40EEA742,
| 0x0CB4A5A3112A5113,
| 0x47F0E785EABA72AC,
| 0x59ED216765690F57,
| 0x306869C13EC3532C,
| 0x1E414218C73A13FC,
| 0xE5D1929EF90898FB,
| 0xDF45F746B74ABF39,
| 0x6B8BBA8C328EB784,
| 0x066EA92F3F326565,
| 0xC80A537B0EFEFEBE,
| 0xBD06742CE95F5F37,
| 0x2C48113823B73704,
| 0xF75A15862CA504C5,
| 0x9A984D73DBE722FB,
| 0xC13E60D0D2E0EBBA,
| 0x318DF905079926A9,
| 0xFDF17746497F7053,
| 0xFEB6EA8BEDEFA634,
| 0xFE64A52EE96B8FC1,
| 0x3DFDCE7AA3C673B1,
| 0x06BEA10CA65C084F,
| 0x486E494FCFF30A62,
| 0x5A89DBA3C3EFCCFB,
| 0xF89629465A75E01D,
| 0xF6BBB397F1135824,
| 0x746AA07DED582E2D,
| 0xA8C2A44EB4571CDC,
| 0x92F34D62616CE413,
| 0x77B020BAF9C81D18,
| 0x0ACE1474DC1D122F,
| 0x0D819992132456BB,
| 0x10E1FFF697ED6C69,
| 0xCA8D3FFA1EF463C2,
| 0xBD308FF8A6B17CB2,
| 0xAC7CB3F6D05DDBDF,
| 0x6BCDF07A423AA96B,
| 0x86C16C98D2C953C6,
| 0xE871C7BF077BA8B8,
| 0x11471CD764AD4973,
| 0xD598E40D3DD89BCF,
| 0x4AFF1D108D4EC2C3,
| 0xCEDF722A585139BA,
| 0xC2974EB4EE658829,
| 0x733D226229FEEA33,
| 0x0806357D5A3F5260,
| 0xCA07C2DCB0CF26F8,
| 0xFC89B393DD02F0B6,
| 0xBBAC2078D443ACE3,
| 0xD54B944B84AA4C0E,
| 0x0A9E795E65D4DF11,
| 0x4D4617B5FF4A16D6,
| 0x504BCED1BF8E4E46,
| 0xE45EC2862F71E1D7,
| 0x5D767327BB4E5A4D,
| 0x3A6A07F8D510F870,
| 0x890489F70A55368C,
| 0x2B45AC74CCEA842F,
| 0x3B0B8BC90012929D,
| 0x09CE6EBB40173745,
| 0xCC420A6A101D0516,
| 0x9FA946824A12232E,
| 0x47939822DC96ABF9,
| 0x59787E2B93BC56F7,
| 0x57EB4EDB3C55B65B,
| 0xEDE622920B6B23F1,
| 0xE95FAB368E45ECED,
| 0x11DBCB0218EBB414,
| 0xD652BDC29F26A11A,
| 0x4BE76D3346F04960,
| 0x6F70A4400C562DDC,
| 0xCB4CCD500F6BB953,
| 0x7E2000A41346A7A8,
| 0x8ED400668C0C28C9,
| 0x728900802F0F32FB,
| 0x4F2B40A03AD2FFBA,
| 0xE2F610C84987BFA8,
| 0x0DD9CA7D2DF4D7C9,
| 0x91503D1C79720DBB,
| 0x75A44C6397CE912A,
| 0xC986AFBE3EE11ABA,
| 0xFBE85BADCE996169,
| 0xFAE27299423FB9C3,
| 0xDCCD879FC967D41A,
| 0x5400E987BBC1C921,
| 0x290123E9AAB23B69,
| 0xF9A0B6720AAF6521,
| 0xF808E40E8D5B3E6A,
| 0xB60B1D1230B20E04,
| 0xB1C6F22B5E6F48C3,
| 0x1E38AEB6360B1AF3,
| 0x25C6DA63C38DE1B0,
| 0x579C487E5A38AD0E,
| 0x2D835A9DF0C6D852,
| 0xF8E431456CF88E66,
| 0x1B8E9ECB641B5900,
| 0xE272467E3D222F40,
| 0x5B0ED81DCC6ABB10,
| 0x98E947129FC2B4EA,
| 0x3F2398D747B36224,
| 0x8EEC7F0D19A03AAD,
| 0x1953CF68300424AC,
| 0x5FA8C3423C052DD7,
| 0x3792F412CB06794D,
| 0xE2BBD88BBEE40BD0,
| 0x5B6ACEAEAE9D0EC4,
| 0xF245825A5A445275,
| 0xEED6E2F0F0D56713,
| 0x55464DD69685606C,
| 0xAA97E14C3C26B887,
| 0xD53DD99F4B3066A8,
| 0xE546A8038EFE4029,
| 0xDE98520472BDD033,
| 0x963E66858F6D4440,
| 0xDDE7001379A44AA8,
| 0x5560C018580D5D52,
| 0xAAB8F01E6E10B4A7,
| 0xCAB3961304CA70E8,
| 0x3D607B97C5FD0D22,
| 0x8CB89A7DB77C506B,
| 0x77F3608E92ADB243,
| 0x55F038B237591ED3,
| 0x6B6C46DEC52F6688,
| 0x2323AC4B3B3DA015,
| 0xABEC975E0A0D081B,
| 0x96E7BD358C904A21,
| 0x7E50D64177DA2E55,
| 0xDDE50BD1D5D0B9EA,
| 0x955E4EC64B44E864,
| 0xBD5AF13BEF0B113F,
| 0xECB1AD8AEACDD58E,
| 0x67DE18EDA5814AF2,
| 0x80EACF948770CED7,
| 0xA1258379A94D028D,
| 0x096EE45813A04330,
| 0x8BCA9D6E188853FC,
| 0x775EA264CF55347E,
| 0x95364AFE032A819D,
| 0x3A83DDBD83F52205,
| 0xC4926A9672793543,
| 0x75B7053C0F178294,
| 0x5324C68B12DD6338,
| 0xD3F6FC16EBCA5E03,
| 0x88F4BB1CA6BCF584,
| 0x2B31E9E3D06C32E5,
| 0x3AFF322E62439FCF,
| 0x09BEFEB9FAD487C3,
| 0x4C2EBE687989A9B4,
| 0x0F9D37014BF60A10,
| 0x538484C19EF38C94,
| 0x2865A5F206B06FBA,
| 0xF93F87B7442E45D4,
| 0xF78F69A51539D749,
| 0xB573440E5A884D1B,
| 0x31680A88F8953031,
| 0xFDC20D2B36BA7C3D,
| 0x3D32907604691B4D,
| 0xA63F9A49C2C1B110,
| 0x0FCF80DC33721D54,
| 0xD3C36113404EA4A9,
| 0x645A1CAC083126E9,
| 0x3D70A3D70A3D70A4,
| 0xCCCCCCCCCCCCCCCD,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x0000000000000000,
| 0x4000000000000000,
| 0x5000000000000000,
| 0xA400000000000000,
| 0x4D00000000000000,
| 0xF020000000000000,
| 0x6C28000000000000,
| 0xC732000000000000,
| 0x3C7F400000000000,
| 0x4B9F100000000000,
| 0x1E86D40000000000,
| 0x1314448000000000,
| 0x17D955A000000000,
| 0x5DCFAB0800000000,
| 0x5AA1CAE500000000,
| 0xF14A3D9E40000000,
| 0x6D9CCD05D0000000,
| 0xE4820023A2000000,
| 0xDDA2802C8A800000,
| 0xD50B2037AD200000,
| 0x4526F422CC340000,
| 0x9670B12B7F410000,
| 0x3C0CDD765F114000,
| 0xA5880A69FB6AC800,
| 0x8EEA0D047A457A00,
| 0x72A4904598D6D880,
| 0x47A6DA2B7F864750,
| 0x999090B65F67D924,
| 0xFFF4B4E3F741CF6D,
| 0xBFF8F10E7A8921A4,
| 0xAFF72D52192B6A0D,
| 0x9BF4F8A69F764490,
| 0x02F236D04753D5B5,
| 0x01D762422C946591,
| 0x424D3AD2B7B97EF5,
| 0xD2E0898765A7DEB2,
| 0x63CC55F49F88EB2F,
| 0x3CBF6B71C76B25FB,
| 0x8BEF464E3945EF7A,
| 0x97758BF0E3CBB5AC,
| 0x3D52EEED1CBEA317,
| 0x4CA7AAA863EE4BDD,
| 0x8FE8CAA93E74EF6A,
| 0xB3E2FD538E122B45,
| 0x60DBBCA87196B616,
| 0xBC8955E946FE31CE,
| 0x6BABAB6398BDBE41,
| 0xC696963C7EED2DD2,
| 0xFC1E1DE5CF543CA3,
| 0x3B25A55F43294BCC,
| 0x49EF0EB713F39EBF,
| 0x6E3569326C784337,
| 0x49C2C37F07965405,
| 0xDC33745EC97BE906,
| 0x69A028BB3DED71A4,
| 0xC40832EA0D68CE0D,
| 0xF50A3FA490C30190,
| 0x792667C6DA79E0FA,
| 0x577001B891185939,
| 0xED4C0226B55E6F87,
| 0x544F8158315B05B4,
| 0x696361AE3DB1C721,
| 0x03BC3A19CD1E38EA,
| 0x04AB48A04065C724,
| 0x62EB0D64283F9C76,
| 0x3BA5D0BD324F8394,
| 0xCA8F44EC7EE36479,
| 0x7E998B13CF4E1ECC,
| 0x9E3FEDD8C321A67F,
| 0xC5CFE94EF3EA101E,
| 0xBBA1F1D158724A13,
| 0x2A8A6E45AE8EDC98,
| 0xF52D09D71A3293BE,
| 0x593C2626705F9C56,
| 0x6F8B2FB00C77836C,
| 0x0B6DFB9C0F956447,
| 0x4724BD4189BD5EAC,
| 0x58EDEC91EC2CB658,
| 0x2F2967B66737E3ED,
| 0xBD79E0D20082EE74,
| 0xECD8590680A3AA11,
| 0xE80E6F4820CC9496,
| 0x3109058D147FDCDE,
| 0xBD4B46F0599FD415,
| 0x6C9E18AC7007C91A,
| 0x03E2CF6BC604DDB0,
| 0x84DB8346B786151D,
| 0xE612641865679A64,
| 0x4FCB7E8F3F60C07E,
| 0xE3BE5E330F38F09E,
| 0x5CADF5BFD3072CC5,
| 0x73D9732FC7C8F7F7,
| 0x2867E7FDDCDD9AFA,
| 0xB281E1FD541501B9,
| 0x1F225A7CA91A4227,
| 0x3375788DE9B06958,
| 0x0052D6B1641C83AE,
| 0xC0678C5DBD23A49A,
| 0xF840B7BA963646E0,
| 0xB650E5A93BC3D898,
| 0xA3E51F138AB4CEBE,
| 0xC66F336C36B10137,
| 0xB80B0047445D4185,
| 0xA60DC059157491E6,
| 0x87C89837AD68DB30,
| 0x29BABE4598C311FC,
| 0xF4296DD6FEF3D67B,
| 0x1899E4A65F58660D,
| 0x5EC05DCFF72E7F90,
| 0x76707543F4FA1F74,
| 0x6A06494A791C53A8,
| 0x0487DB9D17636892,
| 0x45A9D2845D3C42B7,
| 0x0B8A2392BA45A9B2,
| 0x8E6CAC7768D7141F,
| 0x3207D795430CD927,
| 0x7F44E6BD49E807B8,
| 0x5F16206C9C6209A6,
| 0x36DBA887C37A8C10,
| 0xC2494954DA2C978A,
| 0xF2DB9BAA10B7BD6C,
| 0x6F92829494E5ACC7,
| 0xCB772339BA1F17F9,
| 0xFF2A760414536EFC,
| 0xFEF5138519684ABB,
| 0x7EB258665FC25D69,
| 0xEF2F773FFBD97A62,
| 0xAAFB550FFACFD8FA,
| 0x95BA2A53F983CF39,
| 0xDD945A747BF26184,
| 0x94F971119AEEF9E4,
| 0x7A37CD5601AAB85E,
| 0xAC62E055C10AB33B,
| 0x577B986B314D6009,
| 0xED5A7E85FDA0B80B,
| 0x14588F13BE847307,
| 0x596EB2D8AE258FC9,
| 0x6FCA5F8ED9AEF3BB,
| 0x25DE7BB9480D5855,
| 0xAF561AA79A10AE6A,
| 0x1B2BA1518094DA05,
| 0x90FB44D2F05D0843,
| 0x353A1607AC744A54,
| 0x42889B8997915CE9,
| 0x69956135FEBADA11,
| 0x43FAB9837E699096,
| 0x94F967E45E03F4BB,
| 0x1D1BE0EEBAC278F5,
| 0x6462D92A69731732,
| 0x7D7B8F7503CFDCFF,
| 0x5CDA735244C3D43F,
| 0x3A0888136AFA64A7,
| 0x088AAA1845B8FDD1,
| 0x8AAD549E57273D45,
| 0x36AC54E2F678864B,
| 0x84576A1BB416A7DE,
| 0x656D44A2A11C51D5,
| 0x9F644AE5A4B1B325,
| 0x873D5D9F0DDE1FEF,
| 0xA90CB506D155A7EA,
| 0x09A7F12442D588F3,
| 0x0C11ED6D538AEB2F,
| 0x8F1668C8A86DA5FB,
| 0xF96E017D694487BD,
| 0x37C981DCC395A9AC,
| 0x85BBE253F47B1417,
| 0x93956D7478CCEC8E,
| 0x387AC8D1970027B2,
| 0x06997B05FCC0319F,
| 0x441FECE3BDF81F03,
| 0xD527E81CAD7626C4,
| 0x8A71E223D8D3B075,
| 0xF6872D5667844E49,
| 0xB428F8AC016561DB,
| 0xE13336D701BEBA52,
| 0xECC0024661173473,
| 0x27F002D7F95D0190,
| 0x31EC038DF7B441F4,
| 0x7E67047175A15271,
| 0x0F0062C6E984D387,
| 0x52C07B78A3E60868,
| 0xA7709A56CCDF8A83,
| 0x88A66076400BB692,
| 0x6ACFF893D00EA436,
| 0x0583F6B8C4124D43,
| 0xC3727A337A8B704A,
| 0x744F18C0592E4C5D,
| 0x1162DEF06F79DF74,
| 0x8ADDCB5645AC2BA8,
| 0x6D953E2BD7173693,
| 0xC8FA8DB6CCDD0437,
| 0x1D9C9892400A22A2,
| 0x2503BEB6D00CAB4B,
| 0x2E44AE64840FD61E,
| 0x5CEAECFED289E5D3,
| 0x7425A83E872C5F47,
| 0xD12F124E28F77719,
| 0x82BD6B70D99AAA70,
| 0x636CC64D1001550C,
| 0x3C47F7E05401AA4F,
| 0x65ACFAEC34810A71,
| 0x7F1839A741A14D0D,
| 0x1EDE48111209A051,
| 0x934AED0AAB460432,
| 0xF81DA84D5617853F,
| 0x36251260AB9D668F,
| 0xC1D72B7C6B426019,
| 0xB24CF65B8612F820,
| 0xDEE033F26797B628,
| 0x169840EF017DA3B1,
| 0x8E1F289560EE864F,
| 0xF1A6F2BAB92A27E3,
| 0xAE10AF696774B1DB,
| 0xACCA6DA1E0A8EF29,
| 0x17FD090A58D32AF3,
| 0xDDFC4B4CEF07F5B0,
| 0x4ABDAF101564F98E,
| 0x9D6D1AD41ABE37F2,
| 0x84C86189216DC5EE,
| 0x32FD3CF5B4E49BB5,
| 0x3FBC8C33221DC2A2,
| 0x0FABAF3FEAA5334A,
| 0x29CB4D87F2A7400E,
| 0x743E20E9EF511012,
| 0x914DA9246B255417,
| 0x1AD089B6C2F7548E,
| 0xA184AC2473B529B2,
| 0xC9E5D72D90A2741E,
| 0x7E2FA67C7A658893,
| 0xDDBB901B98FEEAB8,
| 0x552A74227F3EA565,
| 0xD53A88958F87275F,
| 0x8A892ABAF368F137,
| 0x2D2B7569B0432D85,
| 0x9C3B29620E29FC73,
| 0x8349F3BA91B47B90,
| 0x241C70A936219A74,
| 0xED238CD383AA0111,
| 0xF4363804324A40AB,
| 0xB143C6053EDCD0D5,
| 0xDD94B7868E94050A,
| 0xCA7CF2B4191C8327,
| 0xFD1C2F611F63A3F0,
| 0xBC633B39673C8CEC,
| 0xD5BE0503E085D814,
| 0x4B2D8644D8A74E19,
| 0xDDF8E7D60ED1219F,
| 0xCABB90E5C942B503,
| 0x3D6A751F3B936244,
| 0x0CC512670A783AD5,
| 0x27FB2B80668B24C5,
| 0xB1F9F660802DEDF6,
| 0x5E7873F8A0396974,
| 0xDB0B487B6423E1E8,
| 0x91CE1A9A3D2CDA63,
| 0x7641A140CC7810FB,
| 0xA9E904C87FCB0A9D,
| 0x546345FA9FBDCD44,
| 0xA97C177947AD4095,
| 0x49ED8EABCCCC485D,
| 0x5C68F256BFFF5A75,
| 0x73832EEC6FFF3112,
| 0xC831FD53C5FF7EAB,
| 0xBA3E7CA8B77F5E56,
| 0x28CE1BD2E55F35EB,
| 0x7980D163CF5B81B3,
| 0xD7E105BCC3326220,
| 0x8DD9472BF3FEFAA8,
| 0xB14F98F6F0FEB952,
| 0x6ED1BF9A569F33D3,
| 0x0A862F80EC4700C8,
| 0xCD27BB612758C0FA,
| 0x8038D51CB897789C,
| 0xE0470A63E6BD56C3,
| 0x1858CCFCE06CAC74,
| 0x0F37801E0C43EBC9,
| 0xD30560258F54E6BB,
| 0x47C6B82EF32A2069,
| 0x4CDC331D57FA5442,
| 0xE0133FE4ADF8E952,
| 0x58180FDDD97723A7,
| 0x570F09EAA7EA7648,
| 0x2CD2CC6551E513DA,
| 0xF8077F7EA65E58D1,
| 0xFB04AFAF27FAF783,
| 0x79C5DB9AF1F9B563,
| 0x18375281AE7822BC,
| 0x8F2293910D0B15B6,
| 0xB2EB3875504DDB23,
| 0x5FA60692A46151EC,
| 0xDBC7C41BA6BCD333,
| 0x12B9B522906C0800,
| 0xD768226B34870A00,
| 0xE6A1158300D46640,
| 0x60495AE3C1097FD0,
| 0x385BB19CB14BDFC4,
| 0x46729E03DD9ED7B5,
| 0x6C07A2C26A8346D1,
| 0xC7098B7305241886,
| 0xB8CBEE4FC66D1EA7,
| 0x737F74F1DC043328,
| 0x505F522E53053FF2,
| 0x647726B9E7C68FEF,
| 0x5ECA783430DC19F5,
| 0xB67D16413D132073,
| 0xE41C5BD18C57E88F,
| 0x8E91B962F7B6F15A,
| 0x723627BBB5A4ADB0,
| 0xCEC3B1AAA30DD91C,
| 0x213A4F0AA5E8A7B2,
| 0xA988E2CD4F62D19E,
| 0x93EB1B80A33B8605,
| 0xBC72F130660533C3,
| 0xEB8FAD7C7F8680B4,
| 0xA67398DB9F6820E1,
| 0x88083F8943A1148D,
| 0x6A0A4F6B948959B0,
| 0x848CE34679ABB01C,
| 0xF2D80E0C0C0B4E12,
| 0x6F8E118F0F0E2196,
| 0x4B7195F2D2D1A9FB,
| 0x8F26FDB7C3C30A3D,
| 0xB2F0BD25B4B3CCCC,
| 0xDFACEC6F21E0C000,
| 0x9798278AEA58EFFF,
| 0x5EBF18B6D2779600,
| 0xF66EDEE487157B80,
| 0xB40A969DA8DADA5F,
| 0x70869E228988C87C,
| 0xCCA845AB2BEAFA9B,
| 0x3FD25715F6E5B941,
| 0x07E3766DBA4F93C9,
| 0x89DC540928E378BB,
| 0x2C53690B731C56EA,
| 0x9BB421A727F1B652,
| 0x42A12A10F1EE23E7,
| 0x134974952E69ACE0,
| 0x2C0DE8DD3D020C0C,
| 0x771163148C428F0F,
| 0x54D5BBD9AF5332D3,
| 0x350595680D93FFC4,
| 0x8246FAC210F8FFB5,
| 0x62D8B97295373FA2,
| 0xFDC773E79D4287C5,
| 0x7D3950E1849329B7,
| 0xDC87A519E5B7F424,
| 0xA9D4C7302F92F897,
| 0xD449F8FC3B77B6BC,
| 0xC95C773B4A55A46B,
| 0x7DD9CA850E7586C3,
| 0x5D503D265212E874,
| 0x34A44C6FE697A291,
| 0x40E6AFC5F01EC59B,
| 0x91205BB76C267701,
| 0x356872A5473014C1,
| 0xC2C28F4E98FC19F2,
| 0xD9B999911F9D9037,
| 0x1027FFF56784F445,
| 0xD431FFF2C1663156,
| 0x049F3FF7B8DFDED6,
| 0x85C70FF5A717D68B,
| 0x2738D3F310DDCC2E,
| 0xB8838477EA8A9F9D,
| 0xE6A46595E52D4784,
| 0x604D7EFB5E789965,
| 0x1C306F5D1B0B5FDF,
| 0x633C8B3461CE37D7,
| 0x3C0BAE017A41C5CD,
| 0xC5874CC0EC691BA0,
| 0xF6E91FF127836288,
| 0xB4A367ED71643B2A,
| 0x50E620F466DEA4FA,
| 0xA51FA93180964E39,
| 0x8E67937DE0BBE1C7,
| 0x7900BC2EAC756D1C,
| 0x5740EB3A5792C863,
| 0x2D112608ED777A7C,
| 0x5C2AB7C5946AAC8E,
| 0xF33565B6F98557B1,
| 0xF002BF24B7E6AD9D,
| 0xB601B776F2F02C82,
| 0xE3822554AFAC37A3,
| 0xDC62AEA9DB97458C,
| 0x537B5A54527D16EE,
| 0x742D1874B38E2E55,
| 0xD1385E91E071B9EA,
| 0x45867636588E2865,
| 0x4B7409E1F758D93F,
| 0x5E510C5A752F0F8F,
| 0xB5E54F71127AD373,
| 0x71AF51A6AB8CC428,
| 0x4E1B2610566FF531,
| 0xA1A1EF946C0BF27E,
| 0x250535BCC387778F,
| 0x6E46832BF4695572,
| 0x89D823F6F183AACF,
| 0x9627167A56F24AC1,
| 0xBBB0DC18ECAEDD72,
| 0x6A9D131F27DA94CE,
| 0xA2A22BF378E89D01,
| 0x0B4AB6F05722C441,
| 0x4E1D64AC6CEB7551,
| 0x90D25EEBC4132953,
| 0xF506F6A6B517F3A7,
| 0xF248B450625DF091,
| 0xD76D70B23D7AB65B,
| 0x0D48CCDECCD963F2,
| 0x109B0016800FBCEE,
| 0xCA60E00E1009D615,
| 0x3CF91811940C4B9A,
| 0xCC375E15F90F5E80,
| 0x3FA29ACDBBA99B10,
| 0x8F8B41812A9401D4,
| 0x336E11E175390249,
| 0x80499659D28742DC,
| 0x302DFDF8239489C9,
| 0xBC397D762C79AC3C,
| 0x2B47DCD3B798174B,
| 0xDB0CEA0452BF0E8F,
| 0x51D02485676ED232,
| 0xA6442DA6C14A86BF,
| 0xA7EA9C8838CE9437,
| 0x91E543AA47023945,
| 0xB65E9494D8C2C796,
| 0xB1FB1CDD0779BCBE,
| 0xDE79E41449582BED,
| 0xD6185D195BAE36E9,
| 0x05CF3A2FD94CE251,
| 0x074308BBCFA01AE6,
| 0x4913CAEAC388219F,
| 0x6DAC5ED2BA351504,
| 0x8917768768C25A44,
| 0xAB5D542942F2F0D6,
| 0x4B1A5499C9D7D685,
| 0x1DE0E9C03C4DCC27,
| 0x255924304B613F31,
| 0x3757B69E2F1CC77E,
| 0xC52DA445BAE3F95E,
| 0xF6790D57299CF7B5,
| 0xFA0BA8567A021AD1,
| 0xF88E926C1882A186,
| 0xF6B237071EA349E7,
| 0xF45EC4C8E64C1C61,
| 0x78BB3AFD8FEF91BD,
| 0xD6EA09BCF3EB762C,
| 0x0CA48C2C30E653B7,
| 0x27E6D79B9E8FF452,
| 0xF1E08D828633F167,
| 0xAE58B0E327C0EDC0,
| 0x4CF76E8DF8D89498,
| 0x60354A31770EB9BE,
| 0x78429CBDD4D2682E,
| 0xCB29A1F6A503811D,
| 0x7DF40A744E446164,
| 0x1D710D1161D579BD,
| 0xF266A82ADD256C16,
| 0x2F005235946EC71C,
| 0x3AC066C2F98A78E2,
| 0xC4B84039DBF68B8E,
| 0xB5E6504852F42E71,
| 0xE35FE45A67B13A0D,
| 0x0E1BEEB880CEC448,
| 0xD1A2EA66A102755A,
| 0x460BA500494312B1,
| 0xEBC747202DC9EBAF,
| 0xA6B918E8393C669A,
| 0x90675F22478B8041,
| 0x7A409B756CB73028,
| 0x58D0C252C7E4FC33,
| 0xAF04F2E779DE3B3F,
| 0xDAC62FA15855CA0F,
| 0x48BBDDC4D7359E49,
| 0x5AEAD5360D0305DC,
| 0x71A58A839043C753,
| 0xA70776923A2A5C94,
| 0x50C95436C8B4F3B9,
| 0x64FBA9447AE230A7,
| 0xBF1D49CACCCD5E68,
| 0xEEE49C3D8000B602,
| 0x6A9DC34CE000E383,
| 0x02A29A100C008E32,
| 0xC34B40940F00B1BE,
| 0xF41E10B912C0DE2E,
| 0x7892CA73ABB88ADD,
| 0xD6B77D1096A6AD94,
| 0xCC655C54BC5058F9,
|];
source/mir/bignum/internal/dec2float_table.d has no code
<<<<<< EOF
# path=source-mir-bignum-internal-kernel.lst
|module mir.bignum.internal.kernel;
|
|import mir.bignum.internal.phobos_kernel;
|public import mir.bignum.internal.phobos_kernel: karatsubaRequiredBuffSize, divisionRequiredBuffSize;
|
|private inout(uint)[] toUints(return scope inout ulong[] data)
| @trusted pure nothrow @nogc
|{
0000000| auto ret = cast(typeof(return)) data;
0000000| if (ret.length && ret[$ - 1] == 0)
0000000| ret = ret[0 .. $ - 1];
0000000| return ret;
|}
|
|static if (is(size_t == ulong))
|size_t multiply(
| scope ulong[] c,
| scope const(ulong)[] a,
| scope const(ulong)[] b,
| scope ulong[] buffer,
|)
| @safe pure nothrow @nogc
| in (c.length >= a.length + b.length)
|{
| pragma (inline, false);
|
| c[a.length + b.length - 1] = 0;
|
| auto length = multiply(
| cast(uint[]) c[],
| a.toUints,
| b.toUints,
| cast(uint[]) buffer[],
| );
|
| return length / 2 + length % 2;
|}
|
|size_t multiply(
| scope uint[] c,
| scope const(uint)[] a,
| scope const(uint)[] b,
| scope uint[] buffer,
|)
| @trusted pure nothrow @nogc
214| in (c.length >= a.length + b.length)
|{
| pragma (inline, false);
|
214| if (a.length < b.length)
| {
4| auto t = a;
4| a = b;
4| b = t;
| }
|
214| if (b.length == 0)
0000000| return 0;
|
214| assert(a.length);
214| assert(b.length);
214| assert(c.length);
|
214| c = c[0 .. a.length + b.length];
|
214| if (a is b)
148| squareInternal(c, a, buffer);
| else
66| mulInternal(c, a, b, buffer);
|
214| auto ret = a.length + b.length;
428| ret -= ret && !c[ret - 1];
214| return ret;
|}
|
|
|static if (is(size_t == ulong))
|size_t divMod(
| scope ulong[] q,
| scope ulong[] r,
| scope const(ulong)[] u,
| scope const(ulong)[] v,
| scope ulong[] buffer,
|)
| @trusted pure nothrow @nogc
| in (u.length == 0 || u[$ - 1])
| in (v.length)
| in (v[$ - 1])
| in (q.length + v.length >= u.length + 1)
|{
| pragma (inline, false);
|
| sizediff_t idx = u.length - v.length;
| if (idx < 0)
| return 0;
|
| auto ui = u.toUints;
| auto vi = v.toUints;
|
| auto length = divMod(
| cast(uint[])q,
| cast(uint[])r,
| ui,
| vi,
| cast(uint[]) buffer,
| );
|
| if (r.length && vi.length % 2)
| r[vi.length / 2] &= uint.max;
|
| if (q.ptr is r.ptr)
| return 0;
|
| auto ret = length / 2;
| if (length % 2)
| q[ret++] &= uint.max;
| return ret;
|}
|
|size_t divMod(
| scope uint[] q,
| scope uint[] r,
| scope const(uint)[] u,
| scope const(uint)[] v,
| scope uint[] buffer,
|)
| @trusted pure nothrow @nogc
496| in (u.length == 0 || u[$ - 1])
248| in (v.length)
248| in (v[$ - 1])
248| in (q.length + v.length >= u.length + 1)
|{
| pragma (inline, false);
|
| import mir.bitop: ctlz;
| import mir.utility: _expect;
|
248| if (u.length < v.length)
0000000| return 0;
|
248| q = q[0 .. u.length - v.length + 1];
248| if (v.length == 1)
| {
19| if (q !is u)
11| q[] = u;
19| auto rem = multibyteDivAssign(q, v[0], 0);
19| if (r)
| {
18| r[0] = rem;
18| r[1 .. $] = 0;
| }
| }
| else
| {
458| divModInternal(q[], r ? r[0 .. v.length] : null, u, v, buffer);
| }
|
248| auto length = u.length - v.length;
248| return length + (q[length] != 0);
|}
|
|///
|version(mir_bignum_test)
|unittest
|{
| import mir.bignum.fixed: UInt;
| import mir.bignum.low_level_view: BigUIntView;
|
1| uint[100] buffer = void;
| auto divMod(BigUIntView!uint a, BigUIntView!uint b, BigUIntView!uint c)
| {
4| .divMod(
| c.coefficients,
| a.coefficients,
| a.coefficients,
| b.coefficients,
| buffer,
| );
4| a.coefficients[b.coefficients.length .. $] = 0;
| }
|
| {
| // Test division by a single-digit divisor here.
1| auto dividend = BigUIntView!uint.fromHexString("5");
1| auto divisor = BigUIntView!uint.fromHexString("5");
1| auto quotient = BigUIntView!uint.fromHexString("0");
| // auto remainder = BigUIntView!uint.fromHexString("0");
|
1| divMod(dividend, divisor, quotient);
1| assert(quotient == BigUIntView!uint.fromHexString("1"));
| }
|
| {
| // Test division by a single-digit divisor here.
1| auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
1| auto divisor = BigUIntView!uint.fromHexString("5");
1| auto quotient = BigUIntView!uint.fromHexString("0000000000000000000000000000000000000000000000000000000000000000");
1| divMod(dividend, divisor, quotient);
1| assert(dividend.normalized == BigUIntView!uint.fromHexString("3"));
1| assert(quotient == BigUIntView!uint.fromHexString("1120a1229e8a217d0691b02b819107174a4b677088ef0df874259b283c1d588c"));
| }
|
| // Test big number division
| {
1| auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
1| auto divisor = BigUIntView!uint.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
1| auto quotient = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000");
| // auto remainder = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
1| divMod(dividend, divisor, quotient);
1| assert(quotient.normalized == BigUIntView!uint.fromHexString("ff3a8aa4da35237811a0ffbf007fe938630dee8a810f2f82ae01f80c033291f6"));
1| assert(dividend.normalized == BigUIntView!uint.fromHexString("27926263cf248bef1c2cd63ea004d9f7041bffc8568560ec30fc9a9548057857"));
| }
|
| // Trigger the adding back sequence
| {
1| auto dividend = BigUIntView!uint.fromHexString("800000000000000000000003");
1| auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001");
1| auto quotient = BigUIntView!uint.fromHexString("0");
| // auto remainder = BigUIntView!uint.fromHexString("000000000000000000000000");
1| divMod(dividend, divisor, quotient);
1| assert(quotient == BigUIntView!uint.fromHexString("3"));
1| assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000"));
| }
|}
source/mir/bignum/internal/kernel.d is 90% covered
<<<<<< EOF
# path=source-mir-bignum-internal-parse.lst
|module mir.bignum.internal.parse;
|
|import std.traits: isSomeChar;
|import mir.parse: DecimalExponentKey;
|
|/+
|https://arxiv.org/abs/2101.11408
|Number Parsing at a Gigabyte per Second
|Daniel Lemire
|+/
7|bool isMadeOfEightDigits()(ref const char[8] chars)
|{
| pragma(inline, true);
0000000| ulong val = (cast(ulong[1]) cast(ubyte[8]) chars)[0];
0000000| return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080));
|}
|
|// ditto
|uint parseEightDigits()(ref const char[8] chars)
|{
| pragma(inline, true);
0000000| ulong val = (cast(ulong[1]) cast(ubyte[8]) chars)[0];
0000000| val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
0000000| val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
0000000| return uint((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
|}
|
|/++
|+/
|struct SmallDecimalParsingResult
|{
| ///
| bool success;
| ///
| bool sign;
| ///
| DecimalExponentKey key;
| ///
| long exponent;
| ///
| ulong coefficient;
|}
|
|/++
|+/
|SmallDecimalParsingResult parseJsonNumberImpl()(scope const(char)[] str)
| @trusted pure nothrow @nogc
3| in (str.length)
|{
| pragma(inline, true);
|
| alias W = ulong;
| enum bool allowSpecialValues = false;
| enum bool allowDotOnBounds = false;
| enum bool allowDExponent = false;
| enum bool allowStartingPlus = false;
| enum bool allowUnderscores = false;
| enum bool allowLeadingZeros = false;
| enum bool allowExponent = true;
| enum bool checkEmpty = false;
|
3| typeof(return) result;
|
| bool mullAdd(ulong multiplier, ulong carry)
| {
| import mir.checkedint: mulu, addu;
15| bool overflow;
15| result.coefficient = mulu(result.coefficient, multiplier, overflow);
15| if (overflow)
0000000| return overflow;
15| result.coefficient = addu(result.coefficient, carry, overflow);
15| return overflow;
| }
|
| alias impl = decimalFromStringImpl!(mullAdd, ulong);
| alias specialization = impl!(char,
| allowSpecialValues,
| allowDotOnBounds,
| allowDExponent,
| allowStartingPlus,
| allowUnderscores,
| allowLeadingZeros,
| allowExponent,
| checkEmpty,
| );
|
3| with(result)
3| success = specialization(str, key, exponent, sign);
3| return result;
|}
|
|version(mir_test)
|unittest
|{
| import mir.test;
1| auto res = "-0.1234567890e-30".parseJsonNumberImpl;
1| res.key.should == DecimalExponentKey.e;
1| res.sign.should == true;
1| res.exponent.should == -40;
1| res.coefficient.should == 1234567890;
|}
|
|version(mir_test)
|unittest
|{
| import mir.test;
1| auto res = "-12345.67890e-10".parseJsonNumberImpl;
1| res.key.should == DecimalExponentKey.e;
1| res.sign.should == true;
1| res.exponent.should == -15;
1| res.coefficient.should == 1234567890;
|}
|
|
|version(mir_test)
|unittest
|{
| import mir.test;
1| auto res = "2.9802322387695312E-8".parseJsonNumberImpl;
1| res.key.should == DecimalExponentKey.E;
1| res.sign.should == false;
1| res.exponent.should == -24;
1| res.coefficient.should == 29802322387695312;
|}
|
|/++
|Returns: false in case of overflow or incorrect string.
|+/
|template decimalFromStringImpl(alias mullAdd, W = size_t)
|{
| bool decimalFromStringImpl(C,
| bool allowSpecialValues,
| bool allowDotOnBounds,
| bool allowDExponent,
| bool allowStartingPlus,
| bool allowUnderscores,
| bool allowLeadingZeros,
| bool allowExponent,
| bool checkEmpty,
| )
| (scope const(C)[] str, out DecimalExponentKey key, scope ref long exponent, scope ref bool sign)
| scope @trusted pure @nogc nothrow
| if (isSomeChar!C)
| {
| version(LDC)
| pragma(inline, true);
|
| import mir.utility: _expect;
| static if (checkEmpty)
| {
192| if (_expect(str.length == 0, false))
| return false;
| }
|
198| sign = str[0] == '-';
198| if (sign)
| {
26| str = str[1 .. $];
26| if (_expect(str.length == 0, false))
0000000| return false;
| }
| else
| static if (allowStartingPlus)
| {
171| if (_expect(str[0] == '+', false))
| {
3| str = str[1 .. $];
3| if (_expect(str.length == 0, false))
| return false;
| }
| }
|
198| W multiplier = 10;
198| W d = str[0] - C('0');
198| str = str[1 .. $];
|
198| if (_expect(d >= 10, false))
| {
| static if (allowDotOnBounds)
| {
24| if (d == '.' - '0')
| {
5| if (str.length == 0)
2| return false;
3| key = DecimalExponentKey.dot;
3| d = str[0] - C('0');
3| str = str[1 .. $];
3| if (_expect(d < 10, true))
3| goto FI;
| return false;
| }
| }
| static if (allowSpecialValues)
19| goto NI;
| else
0000000| return false;
| }
|
| static if (!allowLeadingZeros)
| {
3| if (_expect(d == 0, false))
| {
1| if (str.length == 0)
0000000| return true;
1| d = str[0] - C('0');
1| str = str[1 .. $];
1| if (d < 10)
0000000| return false;
1| goto DOT;
| }
| }
|
173| goto F0;
|
| S:
1061| if (str.length == 0)
16| return true;
1045| d = str[0] - C('0');
1045| str = str[1 .. $];
|
1045| if (d < 10)
| {
873| multiplier = 10;
| static if (is(C == char) && is(W == ulong))
4| if (!__ctfe)
4| if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
| {
0000000| multiplier = 1000000000UL;
0000000| d *= 100000000;
0000000| d += parseEightDigits(str[0 .. 8]);
0000000| str = str[8 .. $];
0000000| if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
| {
0000000| multiplier = 1000000000UL * 100000000;
0000000| d *= 100000000;
0000000| d += parseEightDigits(str[0 .. 8]);
0000000| str = str[8 .. $];
| }
| }
|
| F0:
1062| if (_expect(mullAdd(multiplier, d), false))
1| return false;
1061| goto S;
| }
|
| static if (allowUnderscores)
| {
168| if (d == '_' - '0')
| {
25| if (str.length == 0)
2| return false;
23| d = str[0] - C('0');
23| str = str[1 .. $];
23| if (_expect(d < 10, true))
16| goto F0;
7| return false;
| }
| }
| DOT:
148| if (d == DecimalExponentKey.dot)
| {
| // The dot modifier CANNOT be preceded by any modifiers.
93| if (key != DecimalExponentKey.none)
0000000| return false;
|
93| key = DecimalExponentKey.dot;
|
93| if (str.length == 0)
| {
19| return allowDotOnBounds;
| }
|
| IF:
74| multiplier = 10;
| static if (is(C == char) && is(W == ulong))
3| if (!__ctfe)
| {
3| if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
| {
2| multiplier = 100000000;
2| d = parseEightDigits(str[0 .. 8]);
2| str = str[8 .. $];
2| exponent -= 8;
2| if (str.length >= 7)
| {
1| if (isMadeOfEightDigits((str.ptr - 1)[0 .. 8]))
| {
1| multiplier = 100000000UL * 10000000;
1| d -= str.ptr[-1] - '0';
1| d *= 10000000;
1| d += parseEightDigits((str.ptr - 1)[0 .. 8]);
1| str = str[7 .. $];
1| exponent -= 7;
1| if (str.length)
| {
1| auto md = str[0] - C('0');
1| if (md < 10)
| {
1| d *= 10;
1| multiplier = 100000000UL * 100000000;
1| d += md;
1| str = str[1 .. $];
1| exponent -= 1;
| }
| }
| }
| else
| {
| TrySix:
1| if (isMadeOfEightDigits((str.ptr - 2)[0 .. 8]))
| {
0000000| multiplier = 100000000UL * 1000000;
0000000| d -= str.ptr[-1] - '0';
0000000| d -= (str.ptr[-2] - '0') * 10;
0000000| d *= 1000000;
0000000| d += parseEightDigits((str.ptr - 2)[0 .. 8]);
0000000| str = str[6 .. $];
0000000| exponent -= 6;
| }
| }
|
| }
| else
1| if (str.length == 6)
1| goto TrySix;
2| goto FIL;
| }
| }
|
72| d = str[0] - C('0');
72| str = str[1 .. $];
72| if (_expect(d >= 10, false))
15| goto DOB;
| FI:
390| exponent--;
390| multiplier = 10;
| FIL:
392| if (_expect(mullAdd(multiplier, d), false))
0000000| return false;
392| if (str.length == 0)
25| return true;
367| d = str[0] - C('0');
367| str = str[1 .. $];
367| if (d < 10)
329| goto FI;
|
| static if (allowUnderscores)
| {
35| if (d == '_' - '0')
| {
2| if (str.length == 0)
1| return false;
1| d = str[0] - C('0');
1| str = str[1 .. $];
1| if (_expect(d < 10, true))
1| goto FI;
| return false;
| }
| }
| DOB:
| static if (!allowDotOnBounds)
| {
3| if (exponent == 0)
0000000| return false;
| }
| }
|
| static if (allowExponent)
| {
106| if (d == DecimalExponentKey.e
26| || d == DecimalExponentKey.E
20| || d == DecimalExponentKey.d && allowDExponent
18| || d == DecimalExponentKey.D && allowDExponent
| )
| {
| import mir.parse: parse;
100| key = cast(DecimalExponentKey)d;
100| long exponentPlus;
195| if (parse(str, exponentPlus) && str.length == 0)
| {
| import mir.checkedint: adds;
92| bool overflow;
92| exponent = exponent.adds(exponentPlus, overflow);
92| return !overflow;
| }
| }
| }
14| return false;
|
| static if (allowSpecialValues)
| {
| NI:
19| if (str.length == 2)
| {
16| auto stail = cast(C[2])str[0 .. 2];
39| if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
| {
9| key = DecimalExponentKey.infinity;
9| return true;
| }
20| if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
| {
7| key = DecimalExponentKey.nan;
7| return true;
| }
| }
3| return false;
| }
| }
|}
source/mir/bignum/internal/parse.d is 82% covered
<<<<<< EOF
# path=source-mir-bignum-internal-phobos_kernel.lst
|/** Fundamental operations for arbitrary-precision arithmetic
| *
| * These functions are for internal use only.
| */
|
| // Original Phobos code:
|/* Copyright Don Clugston 2008 - 2010.
| * Distributed under the Boost Software License, Version 1.0.
| * (See accompanying file LICENSE_1_0.txt or copy at
| * http://www.boost.org/LICENSE_1_0.txt)
| */
|
| // All other changes:
|/* Copyright Ilia Ki 2022
| * Distributed under Apache-2.0 License
| */
|
|/* References:
| "Modern Computer Arithmetic" (MCA) is the primary reference for all
| algorithms used in this library.
| - R.P. Brent and P. Zimmermann, "Modern Computer Arithmetic",
| Version 0.5.9, (Oct 2010).
| - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022,
| Max-Planck Institute fuer Informatik, (Oct 1998).
| - G. Hanrot, M. Quercia, and P. Zimmermann, "The Middle Product Algorithm, I.",
| INRIA 4664, (Dec 2002).
| - M. Bodrato and A. Zanoni, "What about Toom-Cook Matrices Optimality?",
| http://bodrato.it/papers (2006).
| - A. Fog, "Optimizing subroutines in assembly language",
| www.agner.org/optimize (2008).
| - A. Fog, "The microarchitecture of Intel and AMD CPU's",
| www.agner.org/optimize (2008).
| - A. Fog, "Instruction tables: Lists of instruction latencies, throughputs
| and micro-operation breakdowns for Intel and AMD CPU's.", www.agner.org/optimize (2008).
|Idioms:
| Many functions in this module use
| 'func(Tulong)(Tulong x) if (is(Tulong == ulong))' rather than 'func(ulong x)'
| in order to disable implicit conversion.
|*/
|module mir.bignum.internal.phobos_kernel;
|
|static if (__VERSION__ > 2100)
| version (D_InlineAsm_X86)
| static import std.internal.math.biguintx86;
|
|version (DMD)
|{
| static if (__VERSION__ > 2100)
| version (D_InlineAsm_X86)
| version = HaveAsmVersion;
|}
|
|version (LDC)
|{
| static if (__VERSION__ > 2100)
| version (D_InlineAsm_X86)
| version = HaveAsmVersion;
|
| version (ARM)
| {
| version (ARM_Thumb) {}
| else
| {
| static import std.internal.math.biguintarm;
| version = LDC_ARM_asm;
| version = HaveAsmVersion;
| }
| }
|}
|static import std.internal.math.biguintnoasm;
|alias multibyteAdd = multibyteAddSub!('+');
|alias multibyteSub = multibyteAddSub!('-');
|
|// import std.internal.math.biguintnoasm : BigDigit, KARATSUBALIMIT,
|// KARATSUBASQUARELIMIT;
|
|import std.internal.math.biguintnoasm : BigDigit;
|enum FASTDIVLIMIT = 16; // crossover to recursive division
|enum CACHELIMIT = 256;
|enum KARATSUBALIMIT = 32; // Minimum value for which Karatsuba is worthwhile.
|enum KARATSUBASQUARELIMIT = 32; // Minimum value for which square Karatsuba is worthwhile
|
|import std.ascii: LetterCase;
|
|pure nothrow @nogc:
|package(mir.bignum):
|
|// dipatchers to the right low-level primitives. Added to allow BigInt CTFE for
|// 32 bit systems (https://issues.dlang.org/show_bug.cgi?id=14767) although it's
|// used by the other architectures too.
|// See comments below in case it has to be refactored.
|version (HaveAsmVersion)
|uint multibyteAddSub(char op)(uint[] dest, const(uint)[] src1, const (uint)[] src2, uint carry)
|{
| // must be checked before, otherwise D_InlineAsm_X86 is true.
| if (__ctfe)
| return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry);
| // Runtime.
| else version (D_InlineAsm_X86)
| return std.internal.math.biguintx86.multibyteAddSub!op(dest, src1, src2, carry);
| else version (LDC_ARM_asm)
| return std.internal.math.biguintarm.multibyteAddSub!op(dest, src1, src2, carry);
| // Runtime if no asm available.
| else
| return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry);
|}
|// Any other architecture
|else alias multibyteAddSub = std.internal.math.biguintnoasm.multibyteAddSub;
|
|version (HaveAsmVersion)
|uint multibyteIncrementAssign(char op)(uint[] dest, uint carry)
|{
| if (__ctfe)
| return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry);
| else version (D_InlineAsm_X86)
| return std.internal.math.biguintx86.multibyteIncrementAssign!op(dest, carry);
| else version (LDC_ARM_asm)
| return std.internal.math.biguintarm.multibyteIncrementAssign!op(dest, carry);
| else
| return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry);
|}
|else alias multibyteIncrementAssign = std.internal.math.biguintnoasm.multibyteIncrementAssign;
|
|version (HaveAsmVersion)
|uint multibyteShl()(uint[] dest, const(uint)[] src, uint numbits)
|{
| if (__ctfe)
| return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits);
| else version (D_InlineAsm_X86)
| return std.internal.math.biguintx86.multibyteShl(dest, src, numbits);
| else version (LDC_ARM_asm)
| return std.internal.math.biguintarm.multibyteShl(dest, src, numbits);
| else
| return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits);
|}
|else alias multibyteShl = std.internal.math.biguintnoasm.multibyteShl;
|
|version (HaveAsmVersion)
|void multibyteShr()(uint[] dest, const(uint)[] src, uint numbits)
|{
| if (__ctfe)
| std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits);
| else version (D_InlineAsm_X86)
| std.internal.math.biguintx86.multibyteShr(dest, src, numbits);
| else version (LDC_ARM_asm)
| std.internal.math.biguintarm.multibyteShr(dest, src, numbits);
| else
| std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits);
|}
|else alias multibyteShr = std.internal.math.biguintnoasm.multibyteShr;
|
|version (HaveAsmVersion)
|uint multibyteMul()(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
|{
| if (__ctfe)
| return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry);
| else version (D_InlineAsm_X86)
| return std.internal.math.biguintx86.multibyteMul(dest, src, multiplier, carry);
| else version (LDC_ARM_asm)
| return std.internal.math.biguintarm.multibyteMul(dest, src, multiplier, carry);
| else
| return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry);
|}
|else alias multibyteMul = std.internal.math.biguintnoasm.multibyteMul;
|
|version (HaveAsmVersion)
|uint multibyteMulAdd(char op)(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
|{
| if (__ctfe)
| return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry);
| else version (D_InlineAsm_X86)
| return std.internal.math.biguintx86.multibyteMulAdd!op(dest, src, multiplier, carry);
| else version (LDC_ARM_asm)
| return std.internal.math.biguintarm.multibyteMulAdd!op(dest, src, multiplier, carry);
| else
| return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry);
|}
|else alias multibyteMulAdd = std.internal.math.biguintnoasm.multibyteMulAdd;
|
|version (HaveAsmVersion)
|void multibyteMultiplyAccumulate()(uint[] dest, const(uint)[] left, const(uint)[] right)
|{
| if (__ctfe)
| std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right);
| else version (D_InlineAsm_X86)
| std.internal.math.biguintx86.multibyteMultiplyAccumulate(dest, left, right);
| else version (LDC_ARM_asm)
| std.internal.math.biguintarm.multibyteMultiplyAccumulate(dest, left, right);
| else
| std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right);
|}
|else alias multibyteMultiplyAccumulate = std.internal.math.biguintnoasm.multibyteMultiplyAccumulate;
|
|version (HaveAsmVersion)
|uint multibyteDivAssign()(uint[] dest, uint divisor, uint overflow)
|{
| if (__ctfe)
| return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow);
| else version (D_InlineAsm_X86)
| return std.internal.math.biguintx86.multibyteDivAssign(dest, divisor, overflow);
| else version (LDC_ARM_asm)
| return std.internal.math.biguintarm.multibyteDivAssign(dest, divisor, overflow);
| else
| return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow);
|}
|else alias multibyteDivAssign = std.internal.math.biguintnoasm.multibyteDivAssign;
|
|version (HaveAsmVersion)
|void multibyteAddDiagonalSquares()(uint[] dest, const(uint)[] src)
|{
| if (__ctfe)
| std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src);
| else version (D_InlineAsm_X86)
| std.internal.math.biguintx86.multibyteAddDiagonalSquares(dest, src);
| else version (LDC_ARM_asm)
| std.internal.math.biguintarm.multibyteAddDiagonalSquares(dest, src);
| else
| std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src);
|}
|else alias multibyteAddDiagonalSquares = std.internal.math.biguintnoasm.multibyteAddDiagonalSquares;
|
|version (HaveAsmVersion)
|void multibyteTriangleAccumulate()(uint[] dest, const(uint)[] x)
|{
| if (__ctfe)
| std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x);
| else version (D_InlineAsm_X86)
| std.internal.math.biguintx86.multibyteTriangleAccumulate(dest, x);
| else version (LDC_ARM_asm)
| std.internal.math.biguintarm.multibyteTriangleAccumulate(dest, x);
| else
| std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x);
|}
|else alias multibyteTriangleAccumulate = std.internal.math.biguintnoasm.multibyteTriangleAccumulate;
|
|version (HaveAsmVersion)
|void multibyteSquare()(BigDigit[] result, const(BigDigit)[] x)
|{
| if (__ctfe)
| std.internal.math.biguintnoasm.multibyteSquare(result, x);
| else version (D_InlineAsm_X86)
| std.internal.math.biguintx86.multibyteSquare(result, x);
| else version (LDC_ARM_asm)
| std.internal.math.biguintarm.multibyteSquare(result, x);
| else
| std.internal.math.biguintnoasm.multibyteSquare(result, x);
|}
|else alias multibyteSquare = std.internal.math.biguintnoasm.multibyteSquare;
|
|
|// These constants are used by shift operations
|static if (BigDigit.sizeof == int.sizeof)
|{
| enum { LG2BIGDIGITBITS = 5, BIGDIGITSHIFTMASK = 31 }
| alias BIGHALFDIGIT = ushort;
|}
|else static if (BigDigit.sizeof == long.sizeof)
|{
| alias BIGHALFDIGIT = uint;
| enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 }
|}
|else static assert(0, "Unsupported BigDigit size");
|
|import std.traits : isIntegral;
|enum BigDigitBits = BigDigit.sizeof*8;
|template maxBigDigits(T)
|if (isIntegral!T)
|{
| enum maxBigDigits = (T.sizeof+BigDigit.sizeof-1)/BigDigit.sizeof;
|}
|
|static immutable BigDigit[] ZERO = [0];
|static immutable BigDigit[] ONE = [1];
|static immutable BigDigit[] TWO = [2];
|static immutable BigDigit[] TEN = [10];
|
|void twosComplement(const(BigDigit) [] x, BigDigit[] result)
|pure nothrow @safe
|{
0000000| foreach (i; 0 .. x.length)
| {
0000000| result[i] = ~x[i];
| }
0000000| result[x.length..$] = BigDigit.max;
|
0000000| foreach (i; 0 .. result.length)
| {
0000000| if (result[i] == BigDigit.max)
| {
0000000| result[i] = 0;
| }
| else
| {
0000000| result[i] += 1;
0000000| break;
| }
| }
|}
|
|// works for any type
|T intpow(T)(T x, ulong n) pure nothrow @safe
|{
| T p;
|
| switch (n)
| {
| case 0:
| p = 1;
| break;
|
| case 1:
| p = x;
| break;
|
| case 2:
| p = x * x;
| break;
|
| default:
| p = 1;
| while (1)
| {
| if (n & 1)
| p *= x;
| n >>= 1;
| if (!n)
| break;
| x *= x;
| }
| break;
| }
| return p;
|}
|
|
|// returns the maximum power of x that will fit in a uint.
|int highestPowerBelowUintMax(uint x) pure nothrow @safe
|{
0000000| assert(x > 1, "x must be greater than 1");
| static immutable ubyte [22] maxpwr = [ 31, 20, 15, 13, 12, 11, 10, 10, 9, 9,
| 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7];
0000000| if (x<24) return maxpwr[x-2];
0000000| if (x<41) return 6;
0000000| if (x<85) return 5;
0000000| if (x<256) return 4;
0000000| if (x<1626) return 3;
0000000| if (x<65_536) return 2;
0000000| return 1;
|}
|
|// returns the maximum power of x that will fit in a ulong.
|int highestPowerBelowUlongMax(uint x) pure nothrow @safe
|{
0000000| assert(x > 1, "x must be greater than 1");
| static immutable ubyte [39] maxpwr = [ 63, 40, 31, 27, 24, 22, 21, 20, 19, 18,
| 17, 17, 16, 16, 15, 15, 15, 15, 14, 14,
| 14, 14, 13, 13, 13, 13, 13, 13, 13, 12,
| 12, 12, 12, 12, 12, 12, 12, 12, 12];
0000000| if (x<41) return maxpwr[x-2];
0000000| if (x<57) return 11;
0000000| if (x<85) return 10;
0000000| if (x<139) return 9;
0000000| if (x<256) return 8;
0000000| if (x<566) return 7;
0000000| if (x<1626) return 6;
0000000| if (x<7132) return 5;
0000000| if (x<65_536) return 4;
0000000| if (x<2_642_246) return 3;
0000000| return 2;
|}
|
|version (StdUnittest)
|{
|
|private int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe
|{
| int pwr = 1;
| for (ulong q = x;x*q < cast(ulong) uint.max; )
| {
| q*=x; ++pwr;
| }
| return pwr;
|}
|
|version(mir_bignum_test)
|@safe pure unittest
|{
| assert(highestPowerBelowUintMax(10)==9);
| for (int k=82; k<88; ++k)
| {
| assert(highestPowerBelowUintMax(k)== slowHighestPowerBelowUintMax(k));
| }
|}
|
|}
|
|/** General unsigned multiply routine for bigints.
| * Sets result = x * y.
| *
| * The length of y must not be larger than the length of x.
| * Different algorithms are used, depending on the lengths of x and y.
| * TODO: "Modern Computer Arithmetic" suggests the OddEvenKaratsuba algorithm for the
| * unbalanced case. (But I doubt it would be faster in practice).
| *
| */
|void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y, BigDigit[] scratchbuff)
| pure nothrow @safe
|{
103| assert( result.length == x.length + y.length,
| "result array must have enough space to store computed result");
103| assert( y.length > 0, "y must not be empty");
103| assert( x.length >= y.length, "x must be greater or equal than y");
103| if (y.length < KARATSUBALIMIT)
| {
| // Small multiplier, we'll just use the asm classic multiply.
102| if (y.length == 1)
| { // Trivial case, no cache effects to worry about
3| result[x.length] = multibyteMul(result[0 .. x.length], x, y[0], 0);
3| return;
| }
|
| static assert (CACHELIMIT >= KARATSUBALIMIT * 2);
|
99| auto chunksize = CACHELIMIT - y.length * 2;
|
99| if (chunksize > x.length)
99| chunksize = x.length;
|
99| mulSimple(result[0 .. chunksize + y.length], x[0 .. chunksize], y);
|
99| auto done = chunksize;
|
99| while (done < x.length)
| {
0000000| if (done + chunksize > x.length)
0000000| chunksize = x.length - done;
0000000| BigDigit[KARATSUBALIMIT] partial = void;
0000000| partial[0 .. y.length] = result[done .. done+y.length];
0000000| mulSimple(result[done .. done+chunksize+y.length], x[done .. done+chunksize], y);
0000000| addAssignSimple(result[done .. done+chunksize + y.length], partial[0 .. y.length]);
0000000| done += chunksize;
| }
99| return;
| }
|
1| immutable half = (x.length >> 1) + (x.length & 1);
1| if (2*y.length*y.length <= x.length*x.length)
| {
| // UNBALANCED MULTIPLY
| // Use school multiply to cut into quasi-squares of Karatsuba-size
| // or larger. The ratio of the two sides of the 'square' must be
| // between 1.414:1 and 1:1. Use Karatsuba on each chunk.
| //
| // For maximum performance, we want the ratio to be as close to
| // 1:1 as possible. To achieve this, we can either pad x or y.
| // The best choice depends on the modulus x%y.
1| auto numchunks = x.length / y.length;
1| auto chunksize = y.length;
1| auto extra = x.length % y.length;
1| auto maxchunk = chunksize + extra;
1| bool paddingY; // true = we're padding Y, false = we're padding X.
1| bool isExtraSmall = extra * extra * 2 < y.length * y.length;
2| if (numchunks == 1 && isExtraSmall)
| {
| // We divide (x_first_half * y) and (x_last_half * y)
| // between 1.414:1 and 1.707:1 (1.707 = 1+1/sqrt(2)).
| // (1.414 ~ 1.707)/2:1 is balanced.
1| BigDigit [] partial = scratchbuff[$ - y.length .. $];
1| scratchbuff = scratchbuff[0 .. $ - y.length];
1| mulKaratsuba(result[0 .. half + y.length], y, x[0 .. half], scratchbuff);
1| partial[] = result[half .. half + y.length];
1| mulKaratsuba(result[half .. $], y, x[half .. $], scratchbuff);
1| BigDigit c = addAssignSimple(result[half .. half + y.length], partial);
1| if (c) multibyteIncrementAssign!('+')(result[half + y.length..$], c);
| }
| else
| {
0000000| if (isExtraSmall)
| {
| // The leftover bit is small enough that it should be incorporated
| // in the existing chunks.
| // Make all the chunks a tiny bit bigger
| // (We're padding y with zeros)
0000000| chunksize += extra / numchunks;
0000000| extra = x.length - chunksize*numchunks;
| // there will probably be a few left over.
| // Every chunk will either have size chunksize, or chunksize+1.
0000000| maxchunk = chunksize + 1;
0000000| paddingY = true;
0000000| assert(chunksize + extra + chunksize *(numchunks-1) == x.length,
| "Unexpected size");
| }
| else
| {
| // the extra bit is large enough that it's worth making a new chunk.
| // (This means we're padding x with zeros, when doing the first one).
0000000| maxchunk = chunksize;
0000000| ++numchunks;
0000000| paddingY = false;
0000000| assert(extra + chunksize *(numchunks-1) == x.length,
| "Unexpected size");
| }
| // We make the buffer a bit bigger so we have space for the partial sums.
0000000| BigDigit [] partial = scratchbuff[$ - y.length .. $];
0000000| scratchbuff = scratchbuff[0 .. $ - y.length];
0000000| size_t done; // how much of X have we done so far?
0000000| if (paddingY)
| {
| // If the first chunk is bigger, do it first. We're padding y.
0000000| mulKaratsuba(result[0 .. y.length + chunksize + (extra > 0 ? 1 : 0 )],
0000000| x[0 .. chunksize + (extra>0?1:0)], y, scratchbuff);
0000000| done = chunksize + (extra > 0 ? 1 : 0);
0000000| if (extra) --extra;
| }
| else
| { // We're padding X. Begin with the extra bit.
0000000| mulKaratsuba(result[0 .. y.length + extra], y, x[0 .. extra], scratchbuff);
0000000| done = extra;
0000000| extra = 0;
| }
0000000| immutable basechunksize = chunksize;
0000000| while (done < x.length)
| {
0000000| chunksize = basechunksize + (extra > 0 ? 1 : 0);
0000000| if (extra) --extra;
0000000| partial[] = result[done .. done+y.length];
0000000| mulKaratsuba(result[done .. done + y.length + chunksize],
| x[done .. done+chunksize], y, scratchbuff);
0000000| addAssignSimple(result[done .. done + y.length + chunksize], partial);
0000000| done += chunksize;
| }
| }
| }
| else
| {
| // Balanced. Use Karatsuba directly.
0000000| mulKaratsuba(result, x, y, scratchbuff);
| }
|}
|
|// https://issues.dlang.org/show_bug.cgi?id=20493
|version(mir_bignum_test)
|@safe unittest
|{
| // the bug report has a testcase with very large numbers (~10^3800 and ~10^2300)
| // the number itself isn't important, only the amount of digits, so we do a simpler
| // multiplication of the same size, analogous to:
| // 11111111 * 11111111 = 0123456787654321
| // but instead of base 10, it's in base `BigDigit`
|
1| BigDigit[398] x = 1;
1| BigDigit[236] y = 1;
1| BigDigit[x.length + y.length] result;
1| BigDigit[x.length.karatsubaRequiredBuffSize] buff;
1| mulInternal(result[], x[], y[], buff);
|
| // create an array of the form [1, 2, ..., y.length, ..., y.length, y.length-1, ..., 1, 0]
1| BigDigit[x.length + y.length] expected = y.length;
711| foreach (BigDigit i; 0 .. y.length)
| {
236| expected[i] = i+1;
236| expected[$-1-i] = i;
| }
|
1| assert(result == expected);
|}
|
|/** General unsigned squaring routine for BigInts.
| * Sets result = x*x.
| * NOTE: If the highest half-digit of x is zero, the highest digit of result will
| * also be zero.
| */
|void squareInternal(BigDigit[] result, const BigDigit[] x, BigDigit [] scratchbuff) pure nothrow @safe
148| in (scratchbuff.length >= x.length.karatsubaRequiredBuffSize)
|{
| // Squaring is potentially half a multiply, plus add the squares of
| // the diagonal elements.
148| assert(result.length == 2*x.length,
| "result needs to have twice the capacity of x");
148| if (x.length <= KARATSUBASQUARELIMIT)
| {
148| if (x.length == 1)
| {
5| result[1] = multibyteMul(result[0 .. 1], x, x[0], 0);
5| return;
| }
286| return squareSimple(result, x);
| }
| // The nice thing about squaring is that it always stays balanced
0000000| squareKaratsuba(result, x, scratchbuff);
|}
|
|
|/// if remainder is null, only calculate quotient.
|void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [] u,
| const BigDigit [] v, BigDigit[] buffer) pure nothrow @safe
|{
| import core.bitop : bsr;
|
232| assert(quotient.length == u.length - v.length + 1,
| "Invalid quotient length");
461| assert(remainder == null || remainder.length == v.length,
| "Invalid remainder");
232| assert(v.length > 1, "v must have more than 1 element");
232| assert(u.length >= v.length, "u must be as longer or longer than v");
|
| // Normalize by shifting v left just enough so that
| // its high-order bit is on, and shift u left the
| // same amount. The highest bit of u will never be set.
|
232| auto vn = buffer[0 .. v.length];
232| buffer = buffer[v.length .. $];
|
232| auto un = buffer[0 .. u.length + 1];
232| buffer = buffer[u.length + 1 .. $];
|
| // How much to left shift v, so that its MSB is set.
232| uint s = BIGDIGITSHIFTMASK - bsr(v[$-1]);
232| if (s != 0)
| {
223| multibyteShl(vn, v, s);
223| un[$-1] = multibyteShl(un[0..$-1], u, s);
| }
| else
| {
9| vn[] = v[];
9| un[0..$-1] = u[];
9| un[$-1] = 0;
| }
232| if (quotient.length < FASTDIVLIMIT)
| {
229| schoolbookDivMod(quotient, un, vn);
| }
| else
| {
3| blockDivMod(quotient, un, vn, buffer);
| }
|
| // Unnormalize remainder, if required.
232| if (remainder != null)
| {
236| if (s == 0) remainder[] = un[0 .. vn.length];
222| else multibyteShr(remainder, un[0 .. vn.length+1], s);
| }
|}
|
|version(mir_bignum_test)
|pure @safe unittest
|{ //7fff_00007fff_ffffffff_00020000
1| uint[3] u = [0, 0xFFFF_FFFE, 0x8000_0000];
1| uint[2] v = [0xFFFF_FFFF, 0x8000_0000];
1| uint[10] buffer = void;
1| uint[u.length - v.length + 1] q;
1| uint[2] r;
1| divModInternal(q, r, u, v, buffer);
1| assert(q[]==[0xFFFF_FFFFu, 0]);
1| assert(r[]==[0xFFFF_FFFFu, 0x7FFF_FFFF]);
1| u = [0, 0xFFFF_FFFE, 0x8000_0001];
1| v = [0xFFFF_FFFF, 0x8000_0000];
1| divModInternal(q, r, u, v, buffer);
|}
|
|
|// Converts a big uint to a hexadecimal string.
|//
|// Optionally, a separator character (eg, an underscore) may be added between
|// every 8 digits.
|// buff.length must be data.length*8 if separator is zero,
|// or data.length*9 if separator is non-zero. It will be completely filled.
|char [] biguintToHex(return scope char [] buff, const scope BigDigit [] data, char separator=0,
| LetterCase letterCase = LetterCase.upper) pure nothrow @safe
|{
0000000| int x=0;
0000000| for (ptrdiff_t i=data.length - 1; i >= 0; --i)
| {
0000000| toHexZeroPadded(buff[x .. x+8], data[i], letterCase);
0000000| x+=8;
0000000| if (separator)
| {
0000000| if (i>0) buff[x] = separator;
0000000| ++x;
| }
| }
0000000| return buff;
|}
|
|/**
| * Convert a big uint into an octal string.
| *
| * Params:
| * buff = The destination buffer for the octal string. Must be large enough to
| * store the result, including leading zeroes, which is
| * ceil(data.length * BigDigitBits / 3) characters.
| * The buffer is filled from back to front, starting from `buff[$-1]`.
| * data = The biguint to be converted.
| *
| * Returns: The index of the leading non-zero digit in `buff`. Will be
| * `buff.length - 1` if the entire big uint is zero.
| */
|size_t biguintToOctal(char[] buff, const(BigDigit)[] data)
| pure nothrow @safe @nogc
|{
5| ubyte carry = 0;
5| int shift = 0;
5| size_t penPos = buff.length - 1;
5| size_t lastNonZero = buff.length - 1;
|
| pragma(inline) void output(uint digit) @nogc nothrow
| {
108| if (digit != 0)
34| lastNonZero = penPos;
108| buff[penPos--] = cast(char)('0' + digit);
| }
|
45| foreach (bigdigit; data)
| {
10| if (shift < 0)
| {
| // Some bits were carried over from previous word.
4| assert(shift > -3, "shift must be greater than -3");
4| output(((bigdigit << -shift) | carry) & 0b111);
4| shift += 3;
4| assert(shift > 0, "shift must be 1 or greater");
| }
|
110| while (shift <= BigDigitBits - 3)
| {
100| output((bigdigit >>> shift) & 0b111);
100| shift += 3;
| }
|
10| if (shift < BigDigitBits)
| {
| // Some bits need to be carried forward.
8| carry = (bigdigit >>> shift) & 0b11;
| }
10| shift -= BigDigitBits;
20| assert(shift >= -2 && shift <= 0, "shift must in [-2,0]");
| }
|
5| if (shift < 0)
| {
| // Last word had bits that haven't been output yet.
4| assert(shift > -3, "Shift must be greater than -3");
4| output(carry);
| }
|
5| return lastNonZero;
|}
|
|/** Convert a big uint into a decimal string.
| *
| * Params:
| * buff = The destination buffer for the decimal string. Must be
| * large enough to store the result, including leading zeros.
| * Will be filled backwards, starting from buff[$-1].
| * data = The biguint to be converted. Will be destroyed.
| *
| * buff.length must be >= (data.length*32)/log2(10) = 9.63296 * data.length.
| * Returns:
| * the lowest index of buff which was used.
| */
|size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow @safe
|{
0000000| ptrdiff_t sofar = buff.length;
| // Might be better to divide by (10^38/2^32) since that gives 38 digits for
| // the price of 3 divisions and a shr; this version only gives 27 digits
| // for 3 divisions.
0000000| while (data.length>1)
| {
0000000| uint rem = multibyteDivAssign(data, 10_0000_0000, 0);
0000000| itoaZeroPadded(buff[sofar-9 .. sofar], rem);
0000000| sofar -= 9;
0000000| if (data[$-1] == 0 && data.length > 1)
| {
0000000| data = data[0 .. $ - 1];
| }
| }
0000000| itoaZeroPadded(buff[sofar-10 .. sofar], data[0]);
0000000| sofar -= 10;
| // and strip off the leading zeros
0000000| while (sofar != buff.length-1 && buff[sofar] == '0')
0000000| sofar++;
0000000| return sofar;
|}
|
|/** Convert a decimal string into a big uint.
| *
| * Params:
| * data = The biguint to be receive the result. Must be large enough to
| * store the result.
| * s = The decimal string. May contain _ or 0 .. 9
| *
| * The required length for the destination buffer is slightly less than
| * 1 + s.length/log2(10) = 1 + s.length/3.3219.
| *
| * Returns:
| * the highest index of data which was used. 0 if case of failure.
| */
|int biguintFromDecimal(Range)(BigDigit[] data, Range s)
|if (
| isInputRange!Range &&
| isSomeChar!(ElementType!Range) &&
| !isInfinite!Range)
|in
|{
| static if (hasLength!Range)
| assert((data.length >= 2) || (data.length == 1 && s.length == 1),
| "data has a invalid length");
|}
|do
|{
| // Convert to base 1e19 = 10_000_000_000_000_000_000.
| // (this is the largest power of 10 that will fit into a long).
| // The length will be less than 1 + s.length/log2(10) = 1 + s.length/3.3219.
| // 485 bits will only just fit into 146 decimal digits.
| // As we convert the string, we record the number of digits we've seen in base 19:
| // hi is the number of digits/19, lo is the extra digits (0 to 18).
| // TODO: This is inefficient for very large strings (it is O(n^^2)).
| // We should take advantage of fast multiplication once the numbers exceed
| // Karatsuba size.
| uint lo = 0; // number of powers of digits, 0 .. 18
| uint x = 0;
| ulong y = 0;
| uint hi = 0; // number of base 1e19 digits
| data[0] = 0; // initially number is 0.
| if (data.length > 1)
| data[1] = 0;
|
| foreach (character; s)
| {
| if (character == '_')
| continue;
|
| if (character < '0' || character > '9')
| return 0;
| x *= 10;
| x += character - '0';
| ++lo;
| if (lo == 9)
| {
| y = x;
| x = 0;
| }
| if (lo == 18)
| {
| y *= 10_0000_0000;
| y += x;
| x = 0;
| }
| if (lo == 19)
| {
| y *= 10;
| y += x;
| x = 0;
| // Multiply existing number by 10^19, then add y1.
| if (hi>0)
| {
| data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 1_220_703_125*2u, 0); // 5^13*2 = 0x9184_E72A
| ++hi;
| data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 15_625*262_144u, 0); // 5^6*2^18 = 0xF424_0000
| ++hi;
| }
| else
| hi = 2;
| uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF));
| c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32));
| if (c != 0)
| {
| data[hi]=c;
| ++hi;
| }
| y = 0;
| lo = 0;
| }
| }
| // Now set y = all remaining digits.
| if (lo >= 18)
| {
| }
| else if (lo >= 9)
| {
| for (int k=9; k>> 32);
| hi=2;
| }
| }
| else
| {
| while (lo>0)
| {
| immutable c = multibyteMul(data[0 .. hi], data[0 .. hi], 10, 0);
| if (c != 0)
| {
| data[hi]=c;
| ++hi;
| }
| --lo;
| }
| uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF));
| if (y > 0xFFFF_FFFFL)
| {
| c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32));
| }
| if (c != 0)
| {
| data[hi]=c;
| ++hi;
| }
| }
| }
| while (hi>1 && data[hi-1]==0)
| --hi;
| return hi;
|}
|
|
|// ------------------------
|// These in-place functions are only for internal use; they are incompatible
|// with COW.
|
|// Classic 'schoolbook' multiplication.
|void mulSimple(BigDigit[] result, const(BigDigit) [] left,
| const(BigDigit)[] right) pure nothrow @safe
|in
|{
163| assert(result.length == left.length + right.length,
| "Result must be able to store left + right");
163| assert(right.length>1, "right must not be empty");
|}
|do
|{
163| result[left.length] = multibyteMul(result[0 .. left.length], left, right[0], 0);
163| multibyteMultiplyAccumulate(result[1..$], left, right[1..$]);
|}
|
|// Classic 'schoolbook' squaring
|void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure nothrow @safe
|in
|{
143| assert(result.length == 2*x.length, "result must be twice as long as x");
143| assert(x.length>1, "x must not be empty");
|}
|do
|{
143| multibyteSquare(result, x);
|}
|
|
|// add two uints of possibly different lengths. Result must be as long
|// as the larger length.
|// Returns carry (0 or 1).
|uint addSimple(BigDigit[] result, const BigDigit [] left, const BigDigit [] right)
|pure nothrow @safe
|in
|{
0000000| assert(result.length == left.length,
| "result and left must be of the same length");
0000000| assert(left.length >= right.length,
| "left must be longer or of equal length to right");
0000000| assert(right.length > 0, "right must not be empty");
|}
|do
|{
0000000| uint carry = multibyteAdd(result[0 .. right.length],
| left[0 .. right.length], right, 0);
0000000| if (right.length < left.length)
| {
0000000| result[right.length .. left.length] = left[right.length .. $];
0000000| carry = multibyteIncrementAssign!('+')(result[right.length..$], carry);
| }
0000000| return carry;
|}
|
|// result = left - right
|// returns carry (0 or 1)
|BigDigit subSimple(BigDigit [] result,const(BigDigit) [] left,
| const(BigDigit) [] right) pure nothrow
|in
|{
0000000| assert(result.length == left.length,
| "result and left must be of the same length");
0000000| assert(left.length >= right.length,
| "left must be longer or of equal length to right");
0000000| assert(right.length > 0, "right must not be empty");
|}
|do
|{
0000000| BigDigit carry = multibyteSub(result[0 .. right.length],
| left[0 .. right.length], right, 0);
0000000| if (right.length < left.length)
| {
0000000| result[right.length .. left.length] = left[right.length .. $];
0000000| carry = multibyteIncrementAssign!('-')(result[right.length..$], carry);
| } //else if (result.length == left.length+1) { result[$-1] = carry; carry=0; }
0000000| return carry;
|}
|
|
|/* result = result - right
| * Returns carry = 1 if result was less than right.
|*/
|BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right)
|pure nothrow @safe
|{
60| assert(result.length >= right.length,
| "result must be longer or of equal length to right");
60| uint c = multibyteSub(result[0 .. right.length], result[0 .. right.length], right, 0);
61| if (c && result.length > right.length)
1| c = multibyteIncrementAssign!('-')(result[right.length .. $], c);
60| return c;
|}
|
|/* result = result + right
|*/
|BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right)
|pure nothrow @safe
|{
39| assert(result.length >= right.length,
| "result must be longer or of equal length to right");
39| uint c = multibyteAdd(result[0 .. right.length], result[0 .. right.length], right, 0);
39| if (c && result.length > right.length)
0000000| c = multibyteIncrementAssign!('+')(result[right.length .. $], c);
39| return c;
|}
|
|/* performs result += wantSub? - right : right;
|*/
|BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right,
| bool wantSub) pure nothrow @safe
|{
30| if (wantSub)
24| return subAssignSimple(result, right);
| else
6| return addAssignSimple(result, right);
|}
|
|
|// return true if x= y.length,
| "x must be longer or of equal length to y");
60| auto k = x.length-1;
112| while (x[k]==0 && k >= y.length)
16| --k;
60| if (k >= y.length)
34| return false;
1868| while (k>0 && x[k]==y[k])
918| --k;
26| return x[k] < y[k];
|}
|
|// Set result = abs(x-y), return true if result is negative(x= y.length) ? x.length : y.length),
| "result must capable to store the maximum of x and y");
|
60| size_t minlen;
60| bool negative;
60| if (x.length >= y.length)
| {
60| minlen = y.length;
60| negative = less(x, y);
| }
| else
| {
0000000| minlen = x.length;
0000000| negative = !less(y, x);
| }
120| const (BigDigit)[] large, small;
60| if (negative)
| {
12| large = y; small = x;
| }
| else
| {
108| large = x; small = y;
| }
|
60| BigDigit carry = multibyteSub(result[0 .. minlen], large[0 .. minlen], small[0 .. minlen], 0);
60| if (x.length != y.length)
| {
50| result[minlen .. large.length]= large[minlen..$];
50| result[large.length..$] = 0;
50| if (carry)
8| multibyteIncrementAssign!('-')(result[minlen..$], carry);
| }
60| return negative;
|}
|
|/* Determine how much space is required for the temporaries
| * when performing a Karatsuba multiplication.
| * TODO: determining a tight bound is non-trivial and depends on KARATSUBALIMIT, see:
| * https://issues.dlang.org/show_bug.cgi?id=20493
| */
|size_t karatsubaRequiredBuffSize()(size_t xlen) pure nothrow @safe
|{
148| return xlen < KARATSUBALIMIT ? 0 : (xlen * 9) / 4;
|}
|
|size_t divisionRequiredBuffSize()(size_t ulen, size_t vlen) pure nothrow @safe
|{
0000000| return 2 * vlen + ulen + 2 + vlen.karatsubaRequiredBuffSize;
|}
|
|/* Sets result = x*y, using Karatsuba multiplication.
|* x must be longer or equal to y.
|* Valid only for balanced multiplies, where x is not shorter than y.
|* It is superior to schoolbook multiplication if and only if
|* sqrt(2)*y.length > x.length > y.length.
|* Karatsuba multiplication is O(n^1.59), whereas schoolbook is O(n^2)
|* The maximum allowable length of x and y is uint.max; but better algorithms
|* should be used far before that length is reached.
|* Params:
|* scratchbuff An array long enough to store all the temporaries. Will be destroyed.
|*/
|void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x,
| const(BigDigit)[] y, BigDigit [] scratchbuff) pure nothrow @safe
|{
90| assert(x.length >= y.length, "x must be greater or equal to y");
90| assert(result.length < uint.max, "Operands too large");
90| assert(result.length == x.length + y.length,
| "result must be as large as x + y");
90| if (x.length < KARATSUBALIMIT)
| {
120| return mulSimple(result, x, y);
| }
| // Must be almost square (otherwise, a schoolbook iteration is better)
30| assert(2L * y.length * y.length > (x.length-1) * (x.length-1),
| "Bigint Internal Error: Asymmetric Karatsuba");
|
| // The subtractive version of Karatsuba multiply uses the following result:
| // (Nx1 + x0)*(Ny1 + y0) = (N*N)*x1y1 + x0y0 + N * (x0y0 + x1y1 - mid)
| // where mid = (x0-x1)*(y0-y1)
| // requiring 3 multiplies of length N, instead of 4.
| // The advantage of the subtractive over the additive version is that
| // the mid multiply cannot exceed length N. But there are subtleties:
| // (x0-x1),(y0-y1) may be negative or zero. To keep it simple, we
| // retain all of the leading zeros in the subtractions
|
| // half length, round up.
30| auto half = (x.length >> 1) + (x.length & 1);
|
30| const(BigDigit) [] x0 = x[0 .. half];
30| const(BigDigit) [] x1 = x[half .. $];
30| const(BigDigit) [] y0 = y[0 .. half];
30| const(BigDigit) [] y1 = y[half .. $];
30| BigDigit [] mid = scratchbuff[0 .. half*2];
30| BigDigit [] newscratchbuff = scratchbuff[half*2 .. $];
30| BigDigit [] resultLow = result[0 .. 2*half];
30| BigDigit [] resultHigh = result[2*half .. $];
| // initially use result to store temporaries
30| BigDigit [] xdiff= result[0 .. half];
30| BigDigit [] ydiff = result[half .. half*2];
|
| // First, we calculate mid, and sign of mid
30| immutable bool midNegative = inplaceSub(xdiff, x0, x1)
| ^ inplaceSub(ydiff, y0, y1);
30| mulKaratsuba(mid, xdiff, ydiff, newscratchbuff);
|
| // Low half of result gets x0 * y0. High half gets x1 * y1
|
30| mulKaratsuba(resultLow, x0, y0, newscratchbuff);
|
30| if (2L * y1.length * y1.length < x1.length * x1.length)
| {
| // an asymmetric situation has been created.
| // Worst case is if x:y = 1.414 : 1, then x1:y1 = 2.41 : 1.
| // Applying one schoolbook multiply gives us two pieces each 1.2:1
6| if (y1.length < KARATSUBALIMIT)
4| mulSimple(resultHigh, x1, y1);
| else
| {
| // divide x1 in two, then use schoolbook multiply on the two pieces.
2| auto quarter = (x1.length >> 1) + (x1.length & 1);
2| immutable ysmaller = (quarter >= y1.length);
4| mulKaratsuba(resultHigh[0 .. quarter+y1.length], ysmaller ? x1[0 .. quarter] : y1,
2| ysmaller ? y1 : x1[0 .. quarter], newscratchbuff);
| // Save the part which will be overwritten.
2| immutable ysmaller2 = ((x1.length - quarter) >= y1.length);
2| newscratchbuff[0 .. y1.length] = resultHigh[quarter .. quarter + y1.length];
4| mulKaratsuba(resultHigh[quarter..$], ysmaller2 ? x1[quarter..$] : y1,
2| ysmaller2 ? y1 : x1[quarter..$], newscratchbuff[y1.length..$]);
|
2| resultHigh[quarter..$].addAssignSimple(newscratchbuff[0 .. y1.length]);
| }
| }
| else
24| mulKaratsuba(resultHigh, x1, y1, newscratchbuff);
|
| /* We now have result = x0y0 + (N*N)*x1y1
| Before adding or subtracting mid, we must calculate
| result += N * (x0y0 + x1y1)
| We can do this with three half-length additions. With a = x0y0, b = x1y1:
| aHI aLO
| + aHI aLO
| + bHI bLO
| + bHI bLO
| = R3 R2 R1 R0
| R1 = aHI + bLO + aLO
| R2 = aHI + bLO + aHI + carry_from_R1
| R3 = bHi + carry_from_R2
| It might actually be quicker to do it in two full-length additions:
| newscratchbuff[2*half] = addSimple(newscratchbuff[0 .. 2*half], result[0 .. 2*half], result[2*half..$]);
| addAssignSimple(result[half..$], newscratchbuff[0 .. 2*half+1]);
| */
30| BigDigit[] R1 = result[half .. half*2];
30| BigDigit[] R2 = result[half*2 .. half*3];
30| BigDigit[] R3 = result[half*3..$];
30| BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1
30| BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0
30| BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3
30| if (c1+c2)
0000000| multibyteIncrementAssign!('+')(result[half*2..$], c1+c2);
30| if (c1+c3)
0000000| multibyteIncrementAssign!('+')(R3, c1+c3);
|
| // And finally we subtract mid
30| addOrSubAssignSimple(result[half..$], mid, !midNegative);
|}
|
|void squareKaratsuba(BigDigit [] result, const BigDigit [] x,
| BigDigit [] scratchbuff) pure nothrow @safe
|{
| // See mulKaratsuba for implementation comments.
| // Squaring is simpler, since it never gets asymmetric.
0000000| assert(result.length < uint.max, "Operands too large");
0000000| assert(result.length == 2*x.length,
| "result must be twice the length of x");
0000000| if (x.length <= KARATSUBASQUARELIMIT)
| {
0000000| return squareSimple(result, x);
| }
| // half length, round up.
0000000| auto half = (x.length >> 1) + (x.length & 1);
|
0000000| const(BigDigit)[] x0 = x[0 .. half];
0000000| const(BigDigit)[] x1 = x[half .. $];
0000000| BigDigit [] mid = scratchbuff[0 .. half*2];
0000000| BigDigit [] newscratchbuff = scratchbuff[half*2 .. $];
| // initially use result to store temporaries
0000000| BigDigit [] xdiff= result[0 .. half];
0000000| const BigDigit [] ydiff = result[half .. half*2];
|
| // First, we calculate mid. We don't need its sign
0000000| inplaceSub(xdiff, x0, x1);
0000000| squareKaratsuba(mid, xdiff, newscratchbuff);
|
| // Set result = x0x0 + (N*N)*x1x1
0000000| squareKaratsuba(result[0 .. 2*half], x0, newscratchbuff);
0000000| squareKaratsuba(result[2*half .. $], x1, newscratchbuff);
|
| /* result += N * (x0x0 + x1x1)
| Do this with three half-length additions. With a = x0x0, b = x1x1:
| R1 = aHI + bLO + aLO
| R2 = aHI + bLO + aHI + carry_from_R1
| R3 = bHi + carry_from_R2
| */
0000000| BigDigit[] R1 = result[half .. half*2];
0000000| BigDigit[] R2 = result[half*2 .. half*3];
0000000| BigDigit[] R3 = result[half*3..$];
0000000| BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1
0000000| BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0
0000000| BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3
0000000| if (c1+c2) multibyteIncrementAssign!('+')(result[half*2..$], c1+c2);
0000000| if (c1+c3) multibyteIncrementAssign!('+')(R3, c1+c3);
|
| // And finally we subtract mid, which is always positive
0000000| subAssignSimple(result[half..$], mid);
|}
|
|/* Knuth's Algorithm D, as presented in
| * H.S. Warren, "Hacker's Delight", Addison-Wesley Professional (2002).
| * Also described in "Modern Computer Arithmetic" 0.2, Exercise 1.8.18.
| * Given u and v, calculates quotient = u / v, u = u % v.
| * v must be normalized (ie, the MSB of v must be 1).
| * The most significant words of quotient and u may be zero.
| * u[0 .. v.length] holds the remainder.
| */
|void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v)
| pure nothrow @safe
|{
271| assert(quotient.length == u.length - v.length,
| "quotient has wrong length");
271| assert(v.length > 1, "v must not be empty");
271| assert(u.length >= v.length, "u must be larger or equal to v");
271| assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]");
271| assert(u[$ - 1] < v[$ - 1], "u[$ - 1] must be less than v[$ - 1]");
| // BUG: This code only works if BigDigit is uint.
271| uint vhi = v[$-1];
271| uint vlo = v[$-2];
|
3342| for (ptrdiff_t j = u.length - v.length - 1; j >= 0; j--)
| {
| // Compute estimate of quotient[j],
| // qhat = (three most significant words of u)/(two most sig words of v).
1400| uint qhat;
1400| if (u[j + v.length] == vhi)
| {
| // uu/vhi could exceed uint.max (it will be 0x8000_0000 or 0x8000_0001)
1| qhat = uint.max;
| }
| else
| {
1399| uint ulo = u[j + v.length - 2];
| version (D_InlineAsm_X86)
| {
| // Note: On DMD, this is only ~10% faster than the non-asm code.
1399| uint *p = &u[j + v.length - 1];
| asm pure nothrow @trusted @nogc
| {
| mov EAX, p;
| mov EDX, [EAX+4];
| mov EAX, [EAX];
| div dword ptr [vhi];
| mov qhat, EAX;
| mov ECX, EDX;
|div3by2correction:
| mul dword ptr [vlo]; // EDX:EAX = qhat * vlo
| sub EAX, ulo;
| sbb EDX, ECX;
| jbe div3by2done;
| mov EAX, qhat;
| dec EAX;
| mov qhat, EAX;
| add ECX, dword ptr [vhi];
| jnc div3by2correction;
|div3by2done: ;
| }
| }
| else
| { // version (InlineAsm)
| ulong uu = (cast(ulong)(u[j + v.length]) << 32) | u[j + v.length - 1];
| immutable bigqhat = uu / vhi;
| ulong rhat = uu - bigqhat * vhi;
| qhat = cast(uint) bigqhat;
|again:
| if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo))
| {
| --qhat;
| rhat += vhi;
| if (!(rhat & 0xFFFF_FFFF_0000_0000L))
| goto again;
| }
| } // version (InlineAsm)
| }
| // Multiply and subtract.
1400| uint carry = multibyteMulAdd!('-')(u[j .. j + v.length], v, qhat, 0);
|
1400| if (u[j+v.length] < carry)
| {
| // If we subtracted too much, add back
6| --qhat;
6| carry -= multibyteAdd(u[j .. j + v.length],u[j .. j + v.length], v, 0);
| }
1400| quotient[j] = qhat;
1400| u[j + v.length] = u[j + v.length] - carry;
| }
|}
|
|// TODO: Replace with a library call
|void itoaZeroPadded(char[] output, uint value)
| pure nothrow @safe @nogc
|{
0000000| for (auto i = output.length; i--;)
| {
0000000| if (value < 10)
| {
0000000| output[i] = cast(char)(value + '0');
0000000| value = 0;
| }
| else
| {
0000000| output[i] = cast(char)(value % 10 + '0');
0000000| value /= 10;
| }
| }
|}
|
|void toHexZeroPadded(char[] output, uint value,
| LetterCase letterCase = LetterCase.upper) pure nothrow @safe
|{
0000000| ptrdiff_t x = output.length - 1;
| static immutable string upperHexDigits = "0123456789ABCDEF";
| static immutable string lowerHexDigits = "0123456789abcdef";
0000000| for ( ; x >= 0; --x)
| {
0000000| if (letterCase == LetterCase.upper)
| {
0000000| output[x] = upperHexDigits[value & 0xF];
| }
| else
| {
0000000| output[x] = lowerHexDigits[value & 0xF];
| }
0000000| value >>= 4;
| }
|}
|
|// Returns the highest value of i for which left[i]!=right[i],
|// or 0 if left[] == right[]
|size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right)
|pure nothrow @nogc @safe
|{
0000000| assert(left.length == right.length,
| "left have a length equal to that of right");
0000000| for (ptrdiff_t i = left.length - 1; i>0; --i)
| {
0000000| if (left[i] != right[i])
0000000| return i;
| }
0000000| return 0;
|}
|
|// Returns the lowest value of i for which x[i]!=0.
|int firstNonZeroDigit(const BigDigit [] x) pure nothrow @nogc @safe
|{
0000000| int k = 0;
0000000| while (x[k]==0)
| {
0000000| ++k;
0000000| assert(k < x.length, "k must be less than x.length");
| }
0000000| return k;
|}
|
|/*
| Calculate quotient and remainder of u / v using fast recursive division.
| v must be normalised, and must be at least half as long as u.
| Given u and v, v normalised, calculates quotient = u/v, u = u%v.
| scratch is temporary storage space, length must be >= quotient + 1.
|Returns:
| u[0 .. v.length] is the remainder. u[v.length..$] is corrupted.
| Implements algorithm 1.8 from MCA.
| This algorithm has an annoying special case. After the first recursion, the
| highest bit of the quotient may be set. This means that in the second
| recursive call, the 'in' contract would be violated. (This happens only
| when the top quarter of u is equal to the top half of v. A base 10
| equivalent example of this situation is 5517/56; the first step gives
| 55/5 = 11). To maintain the in contract, we pad a zero to the top of both
| u and the quotient. 'mayOverflow' indicates that that the special case
| has occurred.
| (In MCA, a different strategy is used: the in contract is weakened, and
| schoolbookDivMod is more general: it allows the high bit of u to be set).
| See also:
| - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022,
| Max-Planck Institute fuer Informatik, (Oct 1998).
|*/
|void recursiveDivMod(BigDigit[] quotient, BigDigit[] u, const(BigDigit)[] v,
| BigDigit[] scratch, bool mayOverflow = false)
| pure nothrow @safe
|in
|{
| // v must be normalized
60| assert(v.length > 1, "v must not be empty");
60| assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]");
60| assert(!(u[$ - 1] & 0x8000_0000), "Invalid value at u[$ - 1]");
60| assert(quotient.length == u.length - v.length,
| "quotient must be of equal length of u - v");
60| if (mayOverflow)
| {
8| assert(u[$-1] == 0, "Invalid value at u[$ - 1]");
8| assert(u[$-2] & 0x8000_0000, "Invalid value at u[$ - 2]");
| }
|
| // Must be symmetric. Use block schoolbook division if not.
120| assert((mayOverflow ? u.length-1 : u.length) <= 2 * v.length,
| "Invalid length of u");
120| assert((mayOverflow ? u.length-1 : u.length) >= v.length,
| "Invalid length of u");
120| assert(scratch.length >= quotient.length + (mayOverflow ? 0 : 1),
| "Invalid quotient length");
|}
|do
|{
60| if (quotient.length < FASTDIVLIMIT)
| {
84| return schoolbookDivMod(quotient, u, v);
| }
|
| // Split quotient into two halves, but keep padding in the top half
36| auto k = (mayOverflow ? quotient.length - 1 : quotient.length) >> 1;
|
| // RECURSION 1: Calculate the high half of the quotient
|
| // Note that if u and quotient were padded, they remain padded during
| // this call, so in contract is satisfied.
18| recursiveDivMod(quotient[k .. $], u[2 * k .. $], v[k .. $],
| scratch, mayOverflow);
|
| // quotient[k..$] is our guess at the high quotient.
| // u[2*k .. 2.*k + v.length - k = k + v.length] is the high part of the
| // first remainder. u[0 .. 2*k] is the low part.
|
| // Calculate the full first remainder to be
| // remainder - highQuotient * lowDivisor
| // reducing highQuotient until the remainder is positive.
| // The low part of the remainder, u[0 .. k], cannot be altered by this.
|
18| adjustRemainder(quotient[k .. $], u[k .. k + v.length], v, k,
| scratch, mayOverflow);
|
| // RECURSION 2: Calculate the low half of the quotient
| // The full first remainder is now in u[0 .. k + v.length].
|
18| if (u[k + v.length - 1] & 0x8000_0000)
| {
| // Special case. The high quotient is 0x1_00...000 or 0x1_00...001.
| // This means we need an extra quotient word for the next recursion.
| // We need to restore the invariant for the recursive calls.
| // We do this by padding both u and quotient. Extending u is trivial,
| // because the higher words will not be used again. But for the
| // quotient, we're clobbering the low word of the high quotient,
| // so we need save it, and add it back in after the recursive call.
|
3| auto clobberedQuotient = quotient[k];
3| u[k + v.length] = 0;
|
3| recursiveDivMod(quotient[0 .. k + 1], u[k .. k + v.length + 1],
| v[k .. $], scratch, true);
3| adjustRemainder(quotient[0 .. k + 1], u[0 .. v.length], v, k,
| scratch, true);
|
| // Now add the quotient word that got clobbered earlier.
3| multibyteIncrementAssign!('+')(quotient[k..$], clobberedQuotient);
| }
| else
| {
| // The special case has NOT happened.
15| recursiveDivMod(quotient[0 .. k], u[k .. k + v.length], v[k .. $],
| scratch, false);
|
| // high remainder is in u[k .. k+(v.length-k)] == u[k .. v.length]
|
15| adjustRemainder(quotient[0 .. k], u[0 .. v.length], v, k,
| scratch);
| }
|}
|
|// rem -= quot * v[0 .. k].
|// If would make rem negative, decrease quot until rem is >= 0.
|// Needs (quot.length * k) scratch space to store the result of the multiply.
|void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v,
| ptrdiff_t k,
| BigDigit[] scratch, bool mayOverflow = false) pure nothrow @safe
|{
36| assert(rem.length == v.length, "rem must be as long as v");
36| auto res = scratch[0 .. quot.length + k];
36| mulInternal(res, quot, v[0 .. k], scratch[quot.length + k .. $]);
36| uint carry = subAssignSimple(rem, res[0..$-mayOverflow]);
36| if (mayOverflow)
5| carry += res[$-1];
36| while (carry)
| {
0000000| multibyteIncrementAssign!('-')(quot, 1); // quot--
0000000| carry -= multibyteAdd(rem, rem, v, 0);
| }
|}
|//u + 1, v, v kur,
|// Cope with unbalanced division by performing block schoolbook division.
|void blockDivMod(BigDigit [] q, BigDigit [] u, in BigDigit [] v, BigDigit [] scratch)
|pure nothrow @safe
|{
4| assert(q.length == u.length - v.length,
| "quotient must be of equal length of u - v");
4| assert(v.length > 1, "v must not be empty");
4| assert(u.length >= v.length, "u must be longer or of equal length as v");
4| assert((v[$-1] & 0x8000_0000)!=0, "Invalid value at v[$ - 1]");
4| assert((u[$-1] & 0x8000_0000)==0, "Invalid value at u[$ - 1]");
|
| // Perform block schoolbook division, with 'v.length' blocks.
4| auto m = u.length - v.length;
4| auto n = u.length - v.length;
| do
| {
24| if (n <= v.length)
4| n = v.length;
24| auto mayOverflow = (u[m + v.length -1 ] & 0x8000_0000)!=0;
24| BigDigit saveq;
24| if (mayOverflow)
| {
3| u[m + v.length] = 0;
3| saveq = q[m];
| }
|
24| recursiveDivMod(
| q[n - v.length .. m + mayOverflow],
| u[n - v.length .. m + mayOverflow + v.length],
| v, scratch, mayOverflow);
24| if (mayOverflow)
| {
3| assert(q[m] == 0, "q must not be 0");
3| q[m] = saveq;
| }
24| m -= v.length;
24| n -= v.length;
| }
24| while(n);
|}
|
|version(mir_bignum_test)
|@system unittest
|{
| version (none)
| {
| import core.stdc.stdio;
|
| void printBiguint(const uint [] data)
| {
| char [] buff = biguintToHex(new char[data.length*9], data, '_');
| printf("%.*s\n", cast(int) buff.length, buff.ptr);
| }
|
| void printDecimalBigUint(BigUint data)
| {
| auto str = data.toDecimalString(0);
| printf("%.*s\n", cast(int) str.length, str.ptr);
| }
| }
|
1| uint[43] a;
1| uint[179] b;
131| for (int i=0; i