pax_global_header 0000666 0000000 0000000 00000000064 14501060713 0014507 g ustar 00root root 0000000 0000000 52 comment=69b98a43dd5c5660d2b43ac0f95befe70491467e mir-core-1.7.0/ 0000775 0000000 0000000 00000000000 14501060713 0013231 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/.circleci/ 0000775 0000000 0000000 00000000000 14501060713 0015064 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/.circleci/config.yml 0000664 0000000 0000000 00000000722 14501060713 0017055 0 ustar 00root root 0000000 0000000 version: 2.1 orbs: mirci: libmir/upload_docs@0.3.0 workflows: version: 2 build-deploy: jobs: - mirci/test_and_build_docs: filters: tags: only: /^v(\d)+(\.(\d)+)+$/ - mirci/upload_docs: to: mir-core.libmir.org requires: - mirci/test_and_build_docs filters: branches: ignore: /.*/ tags: only: /^v(\d)+(\.(\d)+)+$/ mir-core-1.7.0/.github/ 0000775 0000000 0000000 00000000000 14501060713 0014571 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14501060713 0016626 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/.github/workflows/ci.yml 0000664 0000000 0000000 00000002543 14501060713 0017750 0 ustar 00root root 0000000 0000000 name: CI on: push: branches: - master pull_request: branches: - master workflow_dispatch: # allow this workflow to be triggered manually jobs: builder: name: 'Build and test on ${{ matrix.arch }}-${{ matrix.os }}/${{ matrix.dc }}' runs-on: ${{ matrix.os }} continue-on-error: ${{ contains(matrix.dc, 'beta') }} env: ARCH: ${{ matrix.arch }} strategy: fail-fast: false matrix: dc: [ldc-latest, ldc-beta, dmd-latest, dmd-beta] os: [ubuntu-latest, windows-latest] arch: [x86, x86_64] include: - dc: ldc-latest os: macos-latest arch: x86_64 - dc: dmd-latest os: macos-latest arch: x86_64 steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 - uses: dlang-community/setup-dlang@763d869b4d67e50c3ccd142108c8bca2da9df166 with: compiler: ${{ matrix.dc }} - name: Install multi-lib for 32-bit systems if: matrix.arch == 'x86' && matrix.os == 'ubuntu-latest' run: sudo apt-get install gcc-multilib - id: build name: Test building run: | dub test --arch=$ARCH --build=unittest-cov shell: bash - id: coverage uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b mir-core-1.7.0/.gitignore 0000664 0000000 0000000 00000000343 14501060713 0015221 0 ustar 00root root 0000000 0000000 *.a *.log *.lst *.o *.pdf *.s *.exe *.sublime-project *.sublime-workspace .dub .generated .vscode __* dub.selections.json web .DS_* docs *.html docs.json out/ build/ files *.lib mir-core-test-library bench_ldexp_frexp builddir mir-core-1.7.0/LICENSE 0000664 0000000 0000000 00000001176 14501060713 0014243 0 ustar 00root root 0000000 0000000 Copyright 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. mir-core-1.7.0/README.md 0000664 0000000 0000000 00000000720 14501060713 0014507 0 ustar 00root root 0000000 0000000 [](https://travis-ci.org/libmir/mir-core) Mir Core ============== Base software building blocks: Algebraic types (aka sumtype/tagged union/variant), universal reflection API, basic math, and more. #### Code Constraints 1. generic code only 2. no runtime dependency : betterC compatible when compiled with LDC in release mode. Exceptions: `@nogc` `mir.exception`. 3. no complex algorithms mir-core-1.7.0/dub.sdl 0000664 0000000 0000000 00000001611 14501060713 0014506 0 ustar 00root root 0000000 0000000 name "mir-core" description "Base software building blocks: Algebraic types (aka sumtype/tagged union/variant), universal reflection API, basic math, and more" authors "Ilia Ki" "Phobos authors (see information per file)" copyright "Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments" license "Apache-2.0" buildType "unittest" { buildOptions "unittests" "debugMode" "debugInfo" versions "mir_core_test" } buildType "unittest-verbose" { buildOptions "unittests" "debugMode" "debugInfo" versions "mir_core_test" dflags "-checkaction=context" "-allinst" } buildType "unittest-dip1008" { buildOptions "unittests" "debugMode" "debugInfo" versions "mir_core_test" dflags "-lowmem" "-preview=dip1008" } buildType "unittest-dip1000" { buildOptions "unittests" "debugMode" "debugInfo" versions "mir_core_test" dflags "-lowmem" "-preview=dip1000" } mir-core-1.7.0/index.d 0000664 0000000 0000000 00000004662 14501060713 0014515 0 ustar 00root root 0000000 0000000 Ddoc $(P JSON Parsing and Serialization library.) $(P The following table is a quick reference guide for which Mir Core modules to use for a given category of functionality.) $(BOOKTABLE , $(TR $(TH Modules) $(TH Description) ) $(TR $(TDNW $(MREF mir,algebraic)) $(TD Generic variant and nullable types )) $(TR $(TDNW $(MREF mir,exception)) $(TD @nogc MirException with formatting)) $(TR $(TDNW $(MREF mir,reflection)) $(TD Compile time reflection utilities )) $(TR $(TDNW $(MREF mir,bitmanip)) $(TD Bit-level manipulation facilities) ) $(TR $(TDNW $(MREF mir,conv)) $(TD Conversion utilities) ) $(TR $(TDNW $(MREF mir,functional)) $(TD Functions that manipulate other functions) ) $(TR $(TDNW $(MREF mir,primitives)) $(TD Templates used to check primitives and range primitives for arrays with multi-dimensional like API support) ) $(TR $(TDNW $(MREF mir,qualifier)) $(TD Const and Immutable qualifiers helpers for Mir Type System.) ) $(TR $(TDNW $(MREF mir,utility)) $(TD Utilities) ) $(TR $(TDNW $(MREF mir,enums)) $(TD Utilities to work with enums) ) $(TR $(TDNW $(MREF mir,string_table)) $(TD Mir String Table designed for fast deserialization routines) ) $(LEADINGROW Integer Routines) $(TR $(TDNW $(MREF mir,bitop)) $(TD A collection of bit-level operations) ) $(TR $(TDNW $(MREF mir,checkedint)) $(TD Integral arithmetic primitives that check for out-of-range results) ) $(LEADINGROW Basic Math) $(TR $(TDNW $(MREF mir,math)) $(TD Publicly imports $(MREF mir,math,common), $(MREF mir,math,constant), $(MREF mir,math,ieee). ) ) $(TR $(TDNW $(MREF mir,math,common)) $(TD Common floating point math functions) ) $(TR $(TDNW $(MREF mir,complex)) $(TD Generic complex type) ) $(TR $(TDNW $(MREF mir,complex,math)) $(TD Basic complex math) ) $(TR $(TDNW $(MREF mir,math,constant)) $(TD Math constants) ) $(TR $(TDNW $(MREF mir,math,ieee)) $(TD Base floating point routines) ) ) Copyright: Copyright © 2020-, Ilia Ki. Macros: TITLE=Mir Core WIKI=Mir Core DDOC_BLANKLINE= _= mir-core-1.7.0/meson.build 0000664 0000000 0000000 00000004010 14501060713 0015366 0 ustar 00root root 0000000 0000000 project('mir-core', 'd', version : '1.1.1', license: 'Apache-2.0') description = 'Mir Core - Base software building blocks and conventions' subprojects = [] has_cpp_headers = false dc = meson.get_compiler('d') sources_list = [ 'mir/algebraic', 'mir/bitmanip', 'mir/bitop', 'mir/checkedint', 'mir/complex/math', 'mir/complex/package', 'mir/conv', 'mir/enums', 'mir/exception', 'mir/functional', 'mir/internal/memory', 'mir/internal/meta', 'mir/internal/utility', 'mir/math/common', 'mir/math/constant', 'mir/math/ieee', 'mir/math/package', 'mir/primitives', 'mir/qualifier', 'mir/reflection', 'mir/string_table', 'mir/utility', ] sources = [] foreach s : sources_list sources += 'source/' + s + '.d' endforeach if dc.get_id() == 'gcc' add_project_arguments([ '-fpreview=dip1008', ], language: 'd') else add_project_arguments([ '-preview=dip1008', '-lowmem', ], language: 'd') endif required_deps = [] foreach p : subprojects required_deps += dependency(p, fallback : [p, p.underscorify() + '_dep']) endforeach directories = ['source'] if has_cpp_headers directories += 'include' endif directories = include_directories(directories) this_lib = library(meson.project_name(), sources, include_directories: directories, install: true, version: meson.project_version(), dependencies: required_deps, ) this_dep = declare_dependency( link_with: [this_lib], include_directories: directories, dependencies: required_deps, ) test_versions = ['mir_core_test'] if has_cpp_headers install_subdir('include/', strip_directory :true, install_dir: 'include/', ) endif install_subdir('source/', strip_directory : true, install_dir: 'include/d/' + meson.project_name(), ) import('pkgconfig').generate(this_lib, description: description, subdirs: 'd/' + meson.project_name(), ) mir_core_dep = this_dep mir_core_lib = this_lib test_subdirs = [] mir-core-1.7.0/proposed-mir-architecture.gv 0000664 0000000 0000000 00000001661 14501060713 0020673 0 ustar 00root root 0000000 0000000 digraph G { rankdir=BT; node [style=filled]; "mir-core" [color=lightblue] "mir" [color=lightblue] "mir-algorithm" [color=lightblue] "mir-image" [color=lightblue] "mir-color" [color=lightblue] "mir-core" [color=lightblue] "mir-core" -> "mir-color" "mir-core" -> "mir" "mir-image" -> "mir-cv" "mir-cpuid" -> "mir-cv" "mir-compute" -> "mir-cv" "mir" -> "mir-algorithm" "mir-algorithm" -> "mir-image" "mir-algorithm" -> "mir-model" "mir-core" -> "mir-algorithm" "mir-core" -> "mir-optim" "mir-core" -> "mir-cpuid" "mir-core" -> "mir-random" "cblas" -> "mir-blas" "lapack" -> "mir-lapack" "mir" -> "mir-blas" "mir-blas" -> "mir-lapack" "dcompute" -> "mir-compute" "mir-color" -> "mir-image" "mir" -> "mir-compute" "mir-lapack" -> "mir-optim" "mir-algorithm" -> "mir-json" "mir-optim" -> "mir-model" "mir" -> "mir-sparse" "mir-algorithm" -> "mir-sparse" "mir-sparse" -> "mir-model" } mir-core-1.7.0/source/ 0000775 0000000 0000000 00000000000 14501060713 0014531 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/source/mir/ 0000775 0000000 0000000 00000000000 14501060713 0015320 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/source/mir/algebraic.d 0000664 0000000 0000000 00000377256 14501060713 0017422 0 ustar 00root root 0000000 0000000 /++ $(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; import mir.exception: toMutable; /++ 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: int opCmp(typeof(this)) { return 0; } 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 implementation 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); // test equality with self-referencing allowed type auto arr = [JsonAlgebraic("str"), JsonAlgebraic(120)]; v = arr; assert(v == arr); assert(v == [JsonAlgebraic("str"), JsonAlgebraic(120)]); } /// 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 implementation 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 { foreach (name; names) if (name == member) return true; 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 && is(T__[0] == union)) { 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__); 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; } (); } else { alias Types__ = T__; private alias MetaInfo__ = T__[0 .. 0]; enum immutable(char[][]) metaFieldNames__ = null; enum immutable(char[][]) typeFieldNames__ = null; } 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 (!(hasElaborateCopyConstructor!(Algebraic!T__) && is(Algebraic!RhsTypes == typeof(this))) && 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 @trusted { 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; } } } } } /++ +/ static if (typeFieldNames__.length) 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: couldn't compute hash. Expected a `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; } else size_t toHash() const scope @trusted nothrow { 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))) { static if (typeFieldNames__.length) 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); } } } else 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 scope const { static if (AllowedTypes.length == 0) { return "Algebraic"; } else { import mir.conv: to; immutable(char)[] ret; static foreach (i, member; metaFieldNames__) static if (!MetaInfo__[i].transparent) { 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 (!MetaInfo__[i].transparent) { 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.toMutable; } 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.toMutable; } } } /// 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.toMutable; } } 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 algebraics 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].toMutable; } 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; } mir-core-1.7.0/source/mir/bitmanip.d 0000664 0000000 0000000 00000042202 14501060713 0017270 0 ustar 00root root 0000000 0000000 /++ Bit-level manipulation facilities. $(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE, $(TR $(TH Category) $(TH Functions)) $(TR $(TD Bit constructs) $(TD $(LREF bitfields) )) $(TR $(TD Tagging) $(TD $(LREF taggedClassRef) $(LREF taggedPointer) )) ) License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP digitalmars.com, Walter Bright), $(HTTP erdani.org, Andrei Alexandrescu), Amaury SECHET +/ module mir.bitmanip; import std.traits; private string normString()(string str) { // if (str.length && (str[$-1] == 'U' || str[$-1] == 'u')) str = str[0 .. $-1]; // if (str.length && (str[$-1] == 'L' || str[$-1] == 'l')) str = str[0 .. $-1]; return str; } private template createAccessors( string store, T, string name, size_t len, size_t offset) { static if (!name.length) { // No need to create any accessor enum result = ""; } else static if (len == 0) { // Fields of length 0 are always zero enum result = "enum "~T.stringof~" "~name~" = 0;\n"; } else { enum ulong maskAllElse = ((~0uL) >> (64 - len)) << offset, signBitCheck = 1uL << (len - 1); static if (T.min < 0) { enum long minVal = -(1uL << (len - 1)); enum long maxVal = (1uL << (len - 1)) - 1; alias UT = Unsigned!(T); enum UT extendSign = cast(UT)~((~0uL) >> (64 - len)); } else { enum ulong minVal = 0; enum ulong maxVal = (~0uL) >> (64 - len); enum extendSign = 0; } static if (is(T == bool)) { static assert(len == 1); enum result = // getter "@property bool " ~ name ~ "()() @safe pure nothrow @nogc const { return " ~"("~store~" & "~ maskAllElse.stringof ~") != 0;}\n" // setter ~"@property void " ~ name ~ "()(bool v) @safe pure nothrow @nogc { " ~"if (v) "~store~" |= "~ maskAllElse.stringof ~";" ~"else "~store~" &= cast(typeof("~store~"))(-1-cast(typeof("~store~"))"~ maskAllElse.stringof ~");}\n"; } else { // getter enum result = "@property "~T.stringof~" "~name~"()() @safe pure nothrow @nogc const { ulong result = " ~"(ulong("~store~") & " ~ maskAllElse.stringof ~ ") >>" ~ offset.stringof ~ ";" ~ (T.min < 0 ? "if (result >= " ~ signBitCheck.stringof ~ ") result |= " ~ extendSign.stringof ~ ";" : "") ~ " return cast("~T.stringof~") result;}\n" // setter ~"@property void "~name~"()("~T.stringof~" v) @safe pure nothrow @nogc { " ~"assert(v >= "~name~`_min, "Value is smaller than the minimum value of bitfield '`~name~`'"); ` ~"assert(v <= "~name~`_max, "Value is greater than the maximum value of bitfield '`~name~`'"); ` ~store~" = cast(typeof("~store~"))" ~" (("~store~" & (-1-cast(typeof("~store~"))"~ maskAllElse.stringof ~"))" ~" | ((cast(typeof("~store~")) v << "~ offset.stringof ~")" ~" & "~ maskAllElse.stringof ~"));}\n" // constants ~"enum "~T.stringof~" "~name~"_min = cast("~T.stringof~")" ~ (minVal == minVal.min && minVal.min < 0 ? "long.min" : minVal.stringof) ~"; " ~" enum "~T.stringof~" "~name~"_max = cast("~T.stringof~")" ~ maxVal.stringof ~"; "; } } } private template createStoreName(Ts...) { static if (Ts.length < 2) enum createStoreName = ""; else enum createStoreName = "_" ~ Ts[1] ~ createStoreName!(Ts[3 .. $]); } private template createStorageAndFields(Ts...) { enum Name = createStoreName!Ts; enum Size = sizeOfBitField!Ts; static if (Size == ubyte.sizeof * 8) alias StoreType = ubyte; else static if (Size == ushort.sizeof * 8) alias StoreType = ushort; else static if (Size == uint.sizeof * 8) alias StoreType = uint; else static if (Size == ulong.sizeof * 8) alias StoreType = ulong; else { static assert(false, "Field widths must sum to 8, 16, 32, or 64"); alias StoreType = ulong; // just to avoid another error msg } enum result = "private " ~ StoreType.stringof ~ " " ~ Name ~ ";" ~ createFields!(Name, 0, Ts).result; } private template createFields(string store, size_t offset, Ts...) { static if (Ts.length > 0) enum result = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset).result ~ createFields!(store, offset + Ts[2], Ts[3 .. $]).result; else enum result = ""; } private ulong getBitsForAlign()(ulong a) { ulong bits = 0; while ((a & 0x01) == 0) { bits++; a >>= 1; } assert(a == 1, "alignment is not a power of 2"); return bits; } private template createReferenceAccessor(string store, T, ulong bits, string name) { import std.traits : CopyTypeQualifiers, PointerTarget; static if (is(T == class)) alias Q = T; else alias Q = PointerTarget!T; enum storageType = (CopyTypeQualifiers!(Q, void)*).stringof; enum storage = "private " ~ storageType ~ ' ' ~ store ~ "_ptr;\n"; enum storage_accessor = "@property ref size_t " ~ store ~ "()() return @trusted pure nothrow @nogc const { " ~ "return *cast(size_t*) &" ~ store ~ "_ptr;}\n" ~ "@property void " ~ store ~ "()(size_t v) @trusted pure nothrow @nogc { " ~ "" ~ store ~ "_ptr = cast(" ~ storageType ~ ") v;}\n"; enum mask = (1UL << bits) - 1; enum maskInv = ~mask; // getter enum ref_accessor = "@property "~T.stringof~" "~name~"()() @trusted pure nothrow @nogc const { auto result = " ~ "("~store~" & "~ maskInv.stringof ~"); " ~ "return cast("~T.stringof~") cast(" ~ storageType ~ ") result;}\n" // setter ~"@property void "~name~"()("~T.stringof~" v) @trusted pure nothrow @nogc { " ~"assert(((cast(typeof("~store~")) cast(" ~ storageType ~ ") v) & "~ mask.stringof ~`) == 0, "Value not properly aligned for '`~name~`'"); ` ~store~" = cast(typeof("~store~"))" ~" (("~store~" & (cast(typeof("~store~")) "~ mask.stringof ~"))" ~" | ((cast(typeof("~store~")) cast(" ~ storageType ~ ") v) & (cast(typeof("~store~")) "~ maskInv.stringof ~")));}\n"; enum result = storage ~ storage_accessor ~ ref_accessor; } private template sizeOfBitField(T...) { static if (T.length < 2) enum sizeOfBitField = 0; else enum sizeOfBitField = T[2] + sizeOfBitField!(T[3 .. $]); } private template createTaggedReference(T, ulong a, string name, Ts...) { static assert( sizeOfBitField!Ts <= getBitsForAlign(a), "Fields must fit in the bits know to be zero because of alignment." ); enum StoreName = createStoreName!(T, name, 0, Ts); enum result = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name).result ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts).result; } /** Allows creating bit fields inside $(D_PARAM struct)s and $(D_PARAM class)es. Example: ---- struct A { int a; mixin(bitfields!( uint, "x", 2, int, "y", 3, uint, "z", 2, bool, "flag", 1)); } A obj; obj.x = 2; obj.z = obj.x; ---- The example above creates a bitfield pack of eight bits, which fit in one $(D_PARAM ubyte). The bitfields are allocated starting from the least significant bit, i.e. x occupies the two least significant bits of the bitfields storage. The sum of all bit lengths in one $(D_PARAM bitfield) instantiation must be exactly 8, 16, 32, or 64. If padding is needed, just allocate one bitfield with an empty name. Example: ---- struct A { mixin(bitfields!( bool, "flag1", 1, bool, "flag2", 1, uint, "", 6)); } ---- The type of a bit field can be any integral type or enumerated type. The most efficient type to store in bitfields is $(D_PARAM bool), followed by unsigned types, followed by signed types. */ template bitfields(T...) { enum { bitfields = createStorageAndFields!T.result } } /** This string mixin generator allows one to create tagged pointers inside $(D_PARAM struct)s and $(D_PARAM class)es. A tagged pointer uses the bits known to be zero in a normal pointer or class reference to store extra information. For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero. One can store a 2-bit integer there. The example above creates a tagged pointer in the struct A. The pointer is of type $(D uint*) as specified by the first argument, and is named x, as specified by the second argument. Following arguments works the same way as $(D bitfield)'s. The bitfield must fit into the bits known to be zero because of the pointer alignment. */ template taggedPointer(T : T*, string name, Ts...) { enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts).result; } /// @safe version(mir_core_test) unittest { struct A { int a; mixin(taggedPointer!( uint*, "x", bool, "b1", 1, bool, "b2", 1)); } A obj; obj.x = new uint; obj.b1 = true; obj.b2 = false; } /** This string mixin generator allows one to create tagged class reference inside $(D_PARAM struct)s and $(D_PARAM class)es. A tagged class reference uses the bits known to be zero in a normal class reference to store extra information. For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero. One can store a 2-bit integer there. The example above creates a tagged reference to an Object in the struct A. This expects the same parameters as $(D taggedPointer), except the first argument which must be a class type instead of a pointer type. */ template taggedClassRef(T, string name, Ts...) if (is(T == class)) { enum taggedClassRef = createTaggedReference!(T, 8, name, Ts).result; } /// @safe version(mir_core_test) unittest { struct A { int a; mixin(taggedClassRef!( Object, "o", uint, "i", 2)); } A obj; obj.o = new Object(); obj.i = 3; } @safe pure nothrow @nogc version(mir_core_test) unittest { // Degenerate bitfields (#8474 / #11160) tests mixed with range tests struct Test1 { mixin(bitfields!(uint, "a", 32, uint, "b", 4, uint, "c", 4, uint, "d", 8, uint, "e", 16,)); static assert(Test1.b_min == 0); static assert(Test1.b_max == 15); } struct Test2 { mixin(bitfields!(bool, "a", 0, ulong, "b", 64)); static assert(Test2.b_min == ulong.min); static assert(Test2.b_max == ulong.max); } struct Test1b { mixin(bitfields!(bool, "a", 0, int, "b", 8)); } struct Test2b { mixin(bitfields!(int, "a", 32, int, "b", 4, int, "c", 4, int, "d", 8, int, "e", 16,)); static assert(Test2b.b_min == -8); static assert(Test2b.b_max == 7); } struct Test3b { mixin(bitfields!(bool, "a", 0, long, "b", 64)); static assert(Test3b.b_min == long.min); static assert(Test3b.b_max == long.max); } struct Test4b { mixin(bitfields!(long, "a", 32, int, "b", 32)); } // Sign extension tests Test2b t2b; Test4b t4b; t2b.b = -5; assert(t2b.b == -5); t2b.d = -5; assert(t2b.d == -5); t2b.e = -5; assert(t2b.e == -5); t4b.a = -5; assert(t4b.a == -5L); } @system version(mir_core_test) unittest { struct Test5 { mixin(taggedPointer!( int*, "a", uint, "b", 2)); } Test5 t5; t5.a = null; t5.b = 3; assert(t5.a is null); assert(t5.b == 3); int myint = 42; t5.a = &myint; assert(t5.a is &myint); assert(t5.b == 3); struct Test6 { mixin(taggedClassRef!( Object, "o", bool, "b", 1)); } Test6 t6; t6.o = null; t6.b = false; assert(t6.o is null); assert(t6.b == false); auto o = new Object(); t6.o = o; t6.b = true; assert(t6.o is o); assert(t6.b == true); } @safe version(mir_core_test) unittest { static assert(!__traits(compiles, taggedPointer!( int*, "a", uint, "b", 3))); static assert(!__traits(compiles, taggedClassRef!( Object, "a", uint, "b", 4))); struct S { mixin(taggedClassRef!( Object, "a", bool, "b", 1)); } const S s; void bar(S s) {} static assert(!__traits(compiles, bar(s))); } @safe version(mir_core_test) unittest { // Bug #6686 union S { ulong bits = ulong.max; mixin (bitfields!( ulong, "back", 31, ulong, "front", 33) ); } S num; num.bits = ulong.max; num.back = 1; assert(num.bits == 0xFFFF_FFFF_8000_0001uL); } @safe version(mir_core_test) unittest { // Bug #5942 struct S { mixin(bitfields!( int, "a" , 32, int, "b" , 32 )); } S data; data.b = 42; data.a = 1; assert(data.b == 42); } @safe version(mir_core_test) unittest { struct Test { mixin(bitfields!(bool, "a", 1, uint, "b", 3, short, "c", 4)); } @safe void test() pure nothrow { Test t; t.a = true; t.b = 5; t.c = 2; assert(t.a); assert(t.b == 5); assert(t.c == 2); } test(); } @safe version(mir_core_test) unittest { { static struct Integrals { bool checkExpectations(bool eb, int ei, short es) { return b == eb && i == ei && s == es; } mixin(bitfields!( bool, "b", 1, uint, "i", 3, short, "s", 4)); } Integrals i; assert(i.checkExpectations(false, 0, 0)); i.b = true; assert(i.checkExpectations(true, 0, 0)); i.i = 7; assert(i.checkExpectations(true, 7, 0)); i.s = -8; assert(i.checkExpectations(true, 7, -8)); i.s = 7; assert(i.checkExpectations(true, 7, 7)); } //Bug# 8876 { struct MoreIntegrals { bool checkExpectations(uint eu, ushort es, uint ei) { return u == eu && s == es && i == ei; } mixin(bitfields!( uint, "u", 24, short, "s", 16, int, "i", 24)); } MoreIntegrals i; assert(i.checkExpectations(0, 0, 0)); i.s = 20; assert(i.checkExpectations(0, 20, 0)); i.i = 72; assert(i.checkExpectations(0, 20, 72)); i.u = 8; assert(i.checkExpectations(8, 20, 72)); i.s = 7; assert(i.checkExpectations(8, 7, 72)); } enum A { True, False } enum B { One, Two, Three, Four } static struct Enums { bool checkExpectations(A ea, B eb) { return a == ea && b == eb; } mixin(bitfields!( A, "a", 1, B, "b", 2, uint, "", 5)); } Enums e; assert(e.checkExpectations(A.True, B.One)); e.a = A.False; assert(e.checkExpectations(A.False, B.One)); e.b = B.Three; assert(e.checkExpectations(A.False, B.Three)); static struct SingleMember { bool checkExpectations(bool eb) { return b == eb; } mixin(bitfields!( bool, "b", 1, uint, "", 7)); } SingleMember f; assert(f.checkExpectations(false)); f.b = true; assert(f.checkExpectations(true)); } // Issue 12477 @system version(mir_core_test) unittest { import std.algorithm.searching : canFind; import mir.bitmanip : bitfields; import core.exception : AssertError; static struct S { mixin(bitfields!( uint, "a", 6, int, "b", 2)); } S s; try { s.a = uint.max; assert(0); } catch (AssertError ae) { assert(ae.msg.canFind("Value is greater than the maximum value of bitfield 'a'"), ae.msg); } try { s.b = int.min; assert(0); } catch (AssertError ae) { assert(ae.msg.canFind("Value is smaller than the minimum value of bitfield 'b'"), ae.msg); } } @system version(mir_core_test) unittest { import core.atomic : atomicStore, atomicLoad, MO = MemoryOrder; static struct S { mixin(taggedPointer!( shared(int)*, "si", bool, "f", 1)); this(shared(int)* ptr, bool flag) { si = ptr; f = flag; } } shared static S s; shared static int i; s.atomicStore!(MO.raw)(S(&i, true)); assert(s.atomicLoad!(MO.raw) == S(&i, true)); } mir-core-1.7.0/source/mir/bitop.d 0000664 0000000 0000000 00000025454 14501060713 0016614 0 ustar 00root root 0000000 0000000 /++ 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; 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); } mir-core-1.7.0/source/mir/checkedint.d 0000664 0000000 0000000 00000061167 14501060713 0017601 0 ustar 00root root 0000000 0000000 /********************************************** * 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; } } long r = cast(long)x + cast(long)y; if (r < int.min || r > int.max) overflow = true; 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; } } long r = cast(ulong)x + cast(ulong)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(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; } } immutable uint r = x + y; if (r < x || r < y) overflow = true; 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; } } immutable ulong r = x + y; if (r < x || r < y) overflow = true; 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; } } immutable long r = cast(long)x - cast(long)y; if (r < int.min || r > int.max) overflow = true; 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; } } immutable long r = cast(ulong)x - cast(ulong)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(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; } } if (x < y) overflow = true; 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; } } if (x < y) overflow = true; 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) { if (x == int.min) overflow = true; 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) { if (x == long.min) overflow = true; 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; } } long r = cast(long)x * cast(long)y; if (r < int.min || r > int.max) overflow = true; 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; } } immutable long r = cast(ulong)x * cast(ulong)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(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; } } immutable ulong r = ulong(x) * ulong(y); if (r >> 32) overflow = true; 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) { ulong r = x * y; if (x >> 32 && r / x != y) overflow = true; 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; } } immutable ulong r = x * y; if ((x | y) >> 32 && x && r / x != y) overflow = true; 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 } } mir-core-1.7.0/source/mir/complex/ 0000775 0000000 0000000 00000000000 14501060713 0016767 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/source/mir/complex/math.d 0000664 0000000 0000000 00000030336 14501060713 0020072 0 ustar 00root root 0000000 0000000 /++ 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))); } mir-core-1.7.0/source/mir/complex/package.d 0000664 0000000 0000000 00000020222 14501060713 0020525 0 ustar 00root root 0000000 0000000 /++ 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) { return re == rhs.re && im == rhs.im; } /// size_t toHash() { T[2] val = [re, im]; 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)); } mir-core-1.7.0/source/mir/conv.d 0000664 0000000 0000000 00000023427 14501060713 0016442 0 ustar 00root root 0000000 0000000 /++ Conversion utilities. License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) Authors: Ilia Ki +/ module mir.conv; import mir.exception: toMutable; 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.toMutable; } 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 = void; 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); } mir-core-1.7.0/source/mir/enums.d 0000664 0000000 0000000 00000024165 14501060713 0016624 0 ustar 00root root 0000000 0000000 /++ 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)); } mir-core-1.7.0/source/mir/exception.d 0000664 0000000 0000000 00000024124 14501060713 0017466 0 ustar 00root root 0000000 0000000 /++ `@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); } @trusted pure nothrow @nogc Exception toMutable()(immutable Exception e) { return cast() e; } @trusted pure nothrow @nogc Error toMutable()(immutable Error e) { return cast() e; } @trusted pure nothrow @nogc Exception toMutable()(const Exception e) { return cast() e; } @trusted pure nothrow @nogc Error toMutable()(const Error e) { return cast() e; } /// 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).toMutable; } /// @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(MirException e) assert(e.scopeMessage == "Hi D2!"); } } /// Generic style, GC allocated MSG 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.message == "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.scopeMessage == "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; const(char)[] _msg; override string message() const @safe pure nothrow { return _msg ? _msg.idup : msg; } const(char)[] scopeMessage() scope const @safe pure nothrow @nogc { return _msg ? _msg : msg; } /++ 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) +/ @nogc @trusted pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) { this._msg = mirExceptionInitilizePayloadImpl(_payload, msg); super(cast(immutable)this._msg, file, line, nextInChain); } /// ditto @nogc @trusted pure nothrow this(scope const(char)[] msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) { this._msg = mirExceptionInitilizePayloadImpl(_payload, msg); super(cast(immutable)this._msg, file, line, nextInChain); } /// ditto @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) { this._global = true; super(msg, file, line, nextInChain); } /// ditto @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) { this._global = true; super(msg, file, line, nextInChain); } /// ~this() @trusted { import mir.internal.memory: free; if (!_global && msg.ptr != _payload.ptr) 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 && !is(Args[$ - 1] == Throwable)) { 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); } this(Args...)(auto ref scope const Args args) pure @trusted if (Args.length > 4 && is(Args[$ - 1] == Throwable)) { 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[0 .. $ - 3]) buf.print(arg); this(buf.data, args[$ - 3 .. $ - 1], cast() args[$ - 1]); } } /// 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; if (msg.length > payload.length) { if (auto ret = (() @trusted { if (__ctfe) return null; if (auto ptr = malloc(msg.length)) { memcpy(ptr, msg.ptr, msg.length); return cast(const(char)[]) ptr[0 .. msg.length]; } return null; })()) return ret; msg = msg[0 .. payload.length]; // remove tail UTF-8 symbol chunk if any uint c = msg[$-1]; if (c > 0b_0111_1111) { do { c = msg[$-1]; msg = msg[0 .. $ - 1]; } while (msg.length && c < 0b_1100_0000); } } if (__ctfe) payload[][0 .. msg.length] = msg; else (() @trusted => memcpy(payload.ptr, msg.ptr, msg.length))(); return payload[0 .. msg.length]; } mir-core-1.7.0/source/mir/functional.d 0000664 0000000 0000000 00000054225 14501060713 0017637 0 ustar 00root root 0000000 0000000 /++ 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)(return scope T[N] a...) { import std.traits: isDynamicArray; static if (isDynamicArray!T) { T[N] ret; static foreach(i; 0..a.length) ret[i] = a[i]; return ret; } else return a; } @safe version(mir_core_test) unittest { string[2] v = ["AA", "BB"]; auto res = staticArray(v); assert(res == v); } /++ 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")))); } mir-core-1.7.0/source/mir/internal/ 0000775 0000000 0000000 00000000000 14501060713 0017134 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/source/mir/internal/memory.d 0000664 0000000 0000000 00000014204 14501060713 0020612 0 ustar 00root root 0000000 0000000 /++ License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) Authors: $(HTTP erdani.com, Andrei Alexandrescu), Ilia Ki (Mir rework of original Phobos code) +/ module mir.internal.memory; pure nothrow @nogc extern(C) { /// void* malloc(size_t size); /// void* calloc(size_t nmemb, size_t size); /// void* realloc(void* ptr, size_t size); /// void free(void* ptr); } pure: enum uint platformAlignment = double.alignof > real.alignof ? double.alignof : real.alignof; @nogc nothrow: @safe pure package bool isGoodDynamicAlignment()(uint x) { return (x & -x) > (x - 1) && x >= (void*).sizeof; } version (Posix) private extern(C) int posix_memalign(void**, size_t, size_t); version (Windows) { // DMD Win 32 bit, DigitalMars C standard library misses the _aligned_xxx // functions family (snn.lib) version(CRuntime_DigitalMars) { // Helper to cast the infos written before the aligned pointer // this header keeps track of the size (required to realloc) and of // the base ptr (required to free). private struct AlignInfo() { void* basePtr; size_t size; @nogc nothrow pragma(inline, true) static AlignInfo* opCall()(void* ptr) { return cast(AlignInfo*) (ptr - AlignInfo.sizeof); } } private void* _aligned_malloc()(size_t size, size_t alignment) { size_t offset = alignment + size_t.sizeof * 2 - 1; // unaligned chunk void* basePtr = malloc(size + offset); if (!basePtr) return null; // get aligned location within the chunk void* alignedPtr = cast(void**)((cast(size_t)(basePtr) + offset) & ~(alignment - 1)); // write the header before the aligned pointer AlignInfo!()* head = AlignInfo!()(alignedPtr); head.basePtr = basePtr; head.size = size; return alignedPtr; } private void* _aligned_realloc()(void* ptr, size_t size, size_t alignment) { import core.stdc.string : memcpy; if (!ptr) return _aligned_malloc(size, alignment); // gets the header from the existing pointer AlignInfo!()* head = AlignInfo!()(ptr); // gets a new aligned pointer void* alignedPtr = _aligned_malloc(size, alignment); if (!alignedPtr) { //to https://msdn.microsoft.com/en-us/library/ms235462.aspx //see Return value: in this case the original block is unchanged return null; } // copy existing data memcpy(alignedPtr, ptr, head.size); free(head.basePtr); return alignedPtr; } private void _aligned_free()(void *ptr) { if (!ptr) return; AlignInfo!()* head = AlignInfo!()(ptr); free(head.basePtr); } } // DMD Win 64 bit, uses microsoft standard C library which implements them else { private extern(C) void _aligned_free(void *); private extern(C) void* _aligned_malloc(size_t, size_t); private extern(C) void* _aligned_realloc(void *, size_t, size_t); } } /** Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, $(D posix_memalign)) on Posix and $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx, $(D __aligned_malloc)) on Windows. */ version(Posix) @trusted void* alignedAllocate()(size_t bytes, uint a) { import core.stdc.errno : ENOMEM, EINVAL; assert(a.isGoodDynamicAlignment); void* result; auto code = posix_memalign(&result, a, bytes); if (code == ENOMEM) return null; else if (code == EINVAL) { assert(0, "AlignedMallocator.alignment is not a power of two " ~"multiple of (void*).sizeof, according to posix_memalign!"); } else if (code != 0) assert (0, "posix_memalign returned an unknown code!"); else return result; } else version(Windows) @trusted void* alignedAllocate()(size_t bytes, uint a) { return _aligned_malloc(bytes, a); } else static assert(0); /** Calls $(D free(b.ptr)) on Posix and $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx, $(D __aligned_free(b.ptr))) on Windows. */ version (Posix) { alias alignedFree = free; } else version (Windows) @system void alignedFree()(void* b) { _aligned_free(b); } else static assert(0); /** On Posix, uses $(D alignedAllocate) and copies data around because there is no realloc for aligned memory. On Windows, calls $(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx, $(D __aligned_realloc(b.ptr, newSize, a))). */ version (Windows) @system void alignedReallocate()(ref void* b, size_t s, uint a) { if (!s) { alignedFree(b); b = null; return; } auto p = cast(ubyte*) _aligned_realloc(b, s, a); if (!p) return; b = p; } /// version(mir_core_test) unittest { auto buffer = alignedAllocate(1024 * 1024 * 4, 128); alignedFree(buffer); //... } version (CRuntime_DigitalMars) version(unittest) private size_t addr(ref void* ptr) { return cast(size_t) ptr; } version(CRuntime_DigitalMars) version(mir_core_test) unittest { void* m; m = _aligned_malloc(16, 0x10); if (m) { assert((m.addr & 0xF) == 0); _aligned_free(m); } m = _aligned_malloc(16, 0x100); if (m) { assert((m.addr & 0xFF) == 0); _aligned_free(m); } m = _aligned_malloc(16, 0x1000); if (m) { assert((m.addr & 0xFFF) == 0); _aligned_free(m); } m = _aligned_malloc(16, 0x10); if (m) { assert((cast(size_t)m & 0xF) == 0); m = _aligned_realloc(m, 32, 0x10000); if (m) assert((m.addr & 0xFFFF) == 0); _aligned_free(m); } m = _aligned_malloc(8, 0x10); if (m) { *cast(ulong*) m = 0X01234567_89ABCDEF; m = _aligned_realloc(m, 0x800, 0x1000); if (m) assert(*cast(ulong*) m == 0X01234567_89ABCDEF); _aligned_free(m); } } mir-core-1.7.0/source/mir/internal/meta.d 0000664 0000000 0000000 00000073023 14501060713 0020234 0 ustar 00root root 0000000 0000000 module mir.internal.meta; /++ Determine if a symbol has a given $(DDSUBLINK spec/attribute, uda, user-defined attribute). See_Also: $(LREF getUDAs) +/ enum hasUDA(alias symbol, alias attribute) = getUDAs!(symbol, attribute).length != 0; /// @safe unittest { enum E; struct S {} @("alpha") int a; static assert(hasUDA!(a, "alpha")); static assert(!hasUDA!(a, S)); static assert(!hasUDA!(a, E)); @(E) int b; static assert(!hasUDA!(b, "alpha")); static assert(!hasUDA!(b, S)); static assert(hasUDA!(b, E)); @E int c; static assert(!hasUDA!(c, "alpha")); static assert(!hasUDA!(c, S)); static assert(hasUDA!(c, E)); @(S, E) int d; static assert(!hasUDA!(d, "alpha")); static assert(hasUDA!(d, S)); static assert(hasUDA!(d, E)); @S int e; static assert(!hasUDA!(e, "alpha")); static assert(hasUDA!(e, S)); static assert(!hasUDA!(e, S())); static assert(!hasUDA!(e, E)); @S() int f; static assert(!hasUDA!(f, "alpha")); static assert(hasUDA!(f, S)); static assert(hasUDA!(f, S())); static assert(!hasUDA!(f, E)); @(S, E, "alpha") int g; static assert(hasUDA!(g, "alpha")); static assert(hasUDA!(g, S)); static assert(hasUDA!(g, E)); @(100) int h; static assert(hasUDA!(h, 100)); struct Named { string name; } @Named("abc") int i; static assert(hasUDA!(i, Named)); static assert(hasUDA!(i, Named("abc"))); static assert(!hasUDA!(i, Named("def"))); struct AttrT(T) { string name; T value; } @AttrT!int("answer", 42) int j; static assert(hasUDA!(j, AttrT)); static assert(hasUDA!(j, AttrT!int)); static assert(!hasUDA!(j, AttrT!string)); @AttrT!string("hello", "world") int k; static assert(hasUDA!(k, AttrT)); static assert(!hasUDA!(k, AttrT!int)); static assert(hasUDA!(k, AttrT!string)); struct FuncAttr(alias f) { alias func = f; } static int fourtyTwo() { return 42; } static size_t getLen(string s) { return s.length; } @FuncAttr!getLen int l; static assert(hasUDA!(l, FuncAttr)); static assert(!hasUDA!(l, FuncAttr!fourtyTwo)); static assert(hasUDA!(l, FuncAttr!getLen)); static assert(!hasUDA!(l, FuncAttr!fourtyTwo())); static assert(!hasUDA!(l, FuncAttr!getLen())); @FuncAttr!getLen() int m; static assert(hasUDA!(m, FuncAttr)); static assert(!hasUDA!(m, FuncAttr!fourtyTwo)); static assert(hasUDA!(m, FuncAttr!getLen)); static assert(!hasUDA!(m, FuncAttr!fourtyTwo())); static assert(hasUDA!(m, FuncAttr!getLen())); } /++ 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(alias symbol, alias attribute) { import std.meta : Filter, AliasSeq, staticMap; static if (AliasSeq!symbol.length != 1) alias getUDAs = AliasSeq!(); else static if (__traits(compiles, __traits(getAttributes, symbol))) alias getUDAs = Filter!(isDesiredUDA!attribute, __traits(getAttributes, symbol)); else alias getUDAs = AliasSeq!(); } /// @safe unittest { struct Attr { string name; int value; } @Attr("Answer", 42) int a; static assert(getUDAs!(a, Attr).length == 1); static assert(getUDAs!(a, Attr)[0].name == "Answer"); static assert(getUDAs!(a, Attr)[0].value == 42); @(Attr("Answer", 42), "string", 9999) int b; static assert(getUDAs!(b, Attr).length == 1); static assert(getUDAs!(b, Attr)[0].name == "Answer"); static assert(getUDAs!(b, Attr)[0].value == 42); @Attr("Answer", 42) @Attr("Pi", 3) int c; static assert(getUDAs!(c, Attr).length == 2); static assert(getUDAs!(c, Attr)[0].name == "Answer"); static assert(getUDAs!(c, Attr)[0].value == 42); static assert(getUDAs!(c, Attr)[1].name == "Pi"); static assert(getUDAs!(c, Attr)[1].value == 3); static assert(getUDAs!(c, Attr("Answer", 42)).length == 1); static assert(getUDAs!(c, Attr("Answer", 42))[0].name == "Answer"); static assert(getUDAs!(c, Attr("Answer", 42))[0].value == 42); static assert(getUDAs!(c, Attr("Answer", 99)).length == 0); struct AttrT(T) { string name; T value; } @AttrT!uint("Answer", 42) @AttrT!int("Pi", 3) @AttrT int d; static assert(getUDAs!(d, AttrT).length == 2); static assert(getUDAs!(d, AttrT)[0].name == "Answer"); static assert(getUDAs!(d, AttrT)[0].value == 42); static assert(getUDAs!(d, AttrT)[1].name == "Pi"); static assert(getUDAs!(d, AttrT)[1].value == 3); static assert(getUDAs!(d, AttrT!uint).length == 1); static assert(getUDAs!(d, AttrT!uint)[0].name == "Answer"); static assert(getUDAs!(d, AttrT!uint)[0].value == 42); static assert(getUDAs!(d, AttrT!int).length == 1); static assert(getUDAs!(d, AttrT!int)[0].name == "Pi"); static assert(getUDAs!(d, AttrT!int)[0].value == 3); struct SimpleAttr {} @SimpleAttr int e; static assert(getUDAs!(e, SimpleAttr).length == 1); static assert(is(getUDAs!(e, SimpleAttr)[0] == SimpleAttr)); @SimpleAttr() int f; static assert(getUDAs!(f, SimpleAttr).length == 1); static assert(is(typeof(getUDAs!(f, SimpleAttr)[0]) == SimpleAttr)); struct FuncAttr(alias f) { alias func = f; } static int add42(int v) { return v + 42; } static string concat(string l, string r) { return l ~ r; } @FuncAttr!add42 int g; static assert(getUDAs!(g, FuncAttr).length == 1); static assert(getUDAs!(g, FuncAttr)[0].func(5) == 47); static assert(getUDAs!(g, FuncAttr!add42).length == 1); static assert(getUDAs!(g, FuncAttr!add42)[0].func(5) == 47); static assert(getUDAs!(g, FuncAttr!add42()).length == 0); static assert(getUDAs!(g, FuncAttr!concat).length == 0); static assert(getUDAs!(g, FuncAttr!concat()).length == 0); @FuncAttr!add42() int h; static assert(getUDAs!(h, FuncAttr).length == 1); static assert(getUDAs!(h, FuncAttr)[0].func(5) == 47); static assert(getUDAs!(h, FuncAttr!add42).length == 1); static assert(getUDAs!(h, FuncAttr!add42)[0].func(5) == 47); static assert(getUDAs!(h, FuncAttr!add42()).length == 1); static assert(getUDAs!(h, FuncAttr!add42())[0].func(5) == 47); static assert(getUDAs!(h, FuncAttr!concat).length == 0); static assert(getUDAs!(h, FuncAttr!concat()).length == 0); @("alpha") @(42) int i; static assert(getUDAs!(i, "alpha").length == 1); static assert(getUDAs!(i, "alpha")[0] == "alpha"); static assert(getUDAs!(i, 42).length == 1); static assert(getUDAs!(i, 42)[0] == 42); static assert(getUDAs!(i, 'c').length == 0); } private template isFunction(T, string member) { static if (is(typeof(&__traits(getMember, T, member)) U : U*) && is(U == function) || is(typeof(&__traits(getMember, T, member)) U == delegate)) { // x is a (nested) function symbol. enum isFunction = true; } else static if (is(__traits(getMember, T, member) T)) { // x is a type. Take the type of it and examine. enum isFunction = is(T == function); } else enum isFunction = false; } private template autoGetUDAs(alias symbol) { import std.meta : AliasSeq; static if (__traits(compiles, __traits(getAttributes, symbol))) alias autoGetUDAs = __traits(getAttributes, symbol); else alias autoGetUDAs = AliasSeq!(); } /++ 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, staticMap; private __gshared T* aggregate; static if (!__traits(hasMember, T, member)) { alias getUDAs = AliasSeq!(); } else static if (is(T == union) && !__traits(compiles, __traits(getMember, aggregate, member))) { alias getUDAs = AliasSeq!(); } else static if (AliasSeq!(__traits(getMember, T, member)).length != 1) { alias getUDAs = AliasSeq!(); } else static if (__traits(getOverloads, T, member, true).length >= 1) { alias getUDAsImpl(alias overload) = Filter!(isDesiredUDA!attribute, autoGetUDAs!overload); alias getUDAs = staticMap!(getUDAsImpl, __traits(getOverloads, T, member, true)); } else { alias getUDAs = Filter!(isDesiredUDA!attribute, __traits(getAttributes, __traits(getMember, T, member))); } } /++ 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) { import std.traits: isInstanceOf; 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) { private __gshared 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) { private __gshared 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; } mir-core-1.7.0/source/mir/internal/utility.d 0000664 0000000 0000000 00000004341 14501060713 0021006 0 ustar 00root root 0000000 0000000 /// 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; }); mir-core-1.7.0/source/mir/math/ 0000775 0000000 0000000 00000000000 14501060713 0016251 5 ustar 00root root 0000000 0000000 mir-core-1.7.0/source/mir/math/common.d 0000664 0000000 0000000 00000056731 14501060713 0017722 0 ustar 00root root 0000000 0000000 /++ 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 x, 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))); } mir-core-1.7.0/source/mir/math/constant.d 0000664 0000000 0000000 00000003560 14501060713 0020253 0 ustar 00root root 0000000 0000000 /// Math constants /// Authors: Phobos Team, Ilia Ki module mir.math.constant; enum real E = 0x1.5bf0a8b1457695355fb8ac404e7a8p+1L; /++ e = 2.718281... +/ enum real LOG2T = 0x1.a934f0979a3715fc9257edfe9b5fbp+1L; /++ $(SUB log, 2)10 = 3.321928... +/ enum real LOG2E = 0x1.71547652b82fe1777d0ffda0d23a8p+0L; /++ $(SUB log, 2)e = 1.442695... +/ enum real LOG2 = 0x1.34413509f79fef311f12b35816f92p-2L; /++ $(SUB log, 10)2 = 0.301029... +/ enum real LOG10E = 0x1.bcb7b1526e50e32a6ab7555f5a67cp-2L; /++ $(SUB log, 10)e = 0.434294... +/ enum real LN2 = 0x1.62e42fefa39ef35793c7673007e5fp-1L; /++ ln 2 = 0.693147... +/ enum real LN10 = 0x1.26bb1bbb5551582dd4adac5705a61p+1L; /++ ln 10 = 2.302585... +/ enum real PI = 0x1.921fb54442d18469898cc51701b84p+1L; /++ π = 3.141592... +/ enum real PI_2 = PI/2; /++ $(PI) / 2 = 1.570796... +/ enum real PI_4 = PI/4; /++ $(PI) / 4 = 0.785398... +/ enum real M_1_PI = 0x1.45f306dc9c882a53f84eafa3ea69cp-2L; /++ 1 / $(PI) = 0.318309... +/ enum real M_2_PI = 2*M_1_PI; /++ 2 / $(PI) = 0.636619... +/ enum real M_2_SQRTPI = 0x1.20dd750429b6d11ae3a914fed7fd8p+0L; /++ 2 / $(SQRT)$(PI) = 1.128379... +/ enum real SQRT2 = 0x1.6a09e667f3bcc908b2fb1366ea958p+0L; /++ $(SQRT)2 = 1.414213... +/ enum real SQRT1_2 = SQRT2 / 2; /++ $(SQRT)$(HALF) = 0.707106... +/ /++ Exponent of minus Euler–Mascheroni constant. +/ enum real ExpMEuler = 0x0.8fbbcf07f2e5f2c56894d7014c3086p0; enum real GoldenRatio = 0x1.9e3779b97f4a7c15f39cc0605cedc8341082276bf3a27251f86c6a11d0c18p+0L; /++ Golden Ratio = (1 + sqrt(5)) / 2 = 1.6180339... ++/ enum real GoldenRatioInverseSquared = 0x0.61c8864680b583ea0c633f9fa31237cbef7dd8940c5d8dae079395ee2f3e71p+0L; /++ Golden Ratio ^ -2 = 0.381966... ++/ mir-core-1.7.0/source/mir/math/ieee.d 0000664 0000000 0000000 00000104160 14501060713 0017327 0 ustar 00root root 0000000 0000000 /** * Base floating point routines. * * Macros: * TABLE_SV =