.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 = | * | * $0
Special Values
| * 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-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-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 | { 90359| 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; 46270| _currentLength += n; 46270| if (_buffer.length == 0) | { 29818| if (_currentLength <= _bufferLength) | { 29781| 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; 815| data._mir_destroy; | version(mir_secure_memory) | _currentLength = 0; 1667| (() @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 | { 45847| auto cl = _currentLength; 45847| auto d = prepare(1); | static if (isMutable!T) | { | import core.lifetime: moveEmplace; 91682| ()@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 | { 422| auto cl = _currentLength; 422| auto d = prepare(e.length); 422| if (!__ctfe) 844| (()@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 | { 87| _currentLength = 0; 87| _buffer = null; | } | | /// | inout(T)[] data() inout @property @trusted scope return | { 187038| 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