From f57deb5fa3debc365b5c74cef85fc792b73ec77d Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 2 Jun 2020 19:54:47 -0400
Subject: [PATCH 001/573] Add move to fold (#266)

---
 source/mir/algorithm/iteration.d | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index e3a36a12..a9bfb6d4 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -17,7 +17,7 @@ $(T2 equal, Compares two slices for equality.)
 $(T2 filter, Filters elements in a range or an ndslice.)
 $(T2 find, Finds backward index.)
 $(T2 findIndex, Finds index.)
-$(T2 fold, Accumulates all elements (different parameter order than reduce).)
+$(T2 fold, Accumulates all elements (different parameter order than `reduce`).)
 $(T2 isSymmetric, Checks if the matrix is symmetric.)
 $(T2 maxIndex, Finds index of the maximum.)
 $(T2 maxPos, Finds backward index of the maximum.)
@@ -3923,7 +3923,8 @@ template fold(alias fun)
     +/
     @optmath auto fold(Slice, S)(scope Slice slice, S seed)
     {
-        return reduce!fun(seed, slice);
+        import core.lifetime: move;
+        return reduce!fun(seed, slice.move);
     }
 }
 

From f111c311eadb0057f5a233c156b772a010ceab71 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 2 Jun 2020 19:55:25 -0400
Subject: [PATCH 002/573] Remove variadic template gmean (#267)

---
 source/mir/math/stat.d | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index d3ed7ca5..a80753a1 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -619,26 +619,17 @@ See_also:
 
 /++
 Params:
-    val = values
+    ar = values
 +/
-@fmamath gmeanType!F gmean(F)(scope const F[] val...)
+@fmamath gmeanType!F gmean(F)(scope const F[] ar...)
     if (isFloatingPoint!F)
 {
     alias G = typeof(return);
     GMeanAccumulator!G gmean;
-    gmean.put(val);
+    gmean.put(ar);
     return gmean.gmean;
 }
 
-/// ditto
-@fmamath gmeanType!(CommonType!T) gmean(T...)(T val)
-    if (T.length > 0 &&
-        !is(CommonType!T == void))
-{
-    alias G = typeof(return);
-    return .gmean!G(val);
-}
-
 ///
 version(mir_test)
 @safe pure nothrow

From 11dac273e63833207e4089cb11b156ebeae52aba Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 3 Jun 2020 08:46:00 -0400
Subject: [PATCH 003/573] Fix stat/numeric FP issue / Add variadic hmean
 overload (#268)

* Fix stat/prod functions to force return of double

* Add variadic hmean

* Add @nogc stat/numeric UTs

* Fix @nogc CI error
---
 source/mir/math/numeric.d     | 108 +++++--
 source/mir/math/stat.d        | 521 ++++++++++++++++++++++++++++------
 source/mir/math/sum.d         |  21 +-
 source/mir/ndslice/topology.d |   2 +-
 4 files changed, 544 insertions(+), 108 deletions(-)

diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index 767eed0f..ffd82daa 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -155,32 +155,56 @@ unittest
 
     ProdAccumulator!float x;
     x.put([1, 2, 3].sliced);
-    assert(x.prod == 6);
+    assert(x.prod == 6f);
     x.put(4);
-    assert(x.prod == 24);
+    assert(x.prod == 24f);
 }
 
-package template prodType(T)
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
 {
-    import mir.ndslice.slice: isSlice, DeepElementType;
+    import mir.ndslice.slice: sliced;
 
-    static if (isIterable!T) {    
-        static if (isSlice!T)
-            alias U = Unqual!(DeepElementType!(T.This));
-        else
-            alias U = Unqual!(ForeachType!T);
-    } else {
-        alias U = Unqual!T;
-    }
+    static immutable a = [1, 2, 3];
+    ProdAccumulator!float x;
+    x.put(a);
+    assert(x.prod == 6f);
+    x.put(4);
+    assert(x.prod == 24f);
+    static assert(is(typeof(x.prod) == float));
+}
 
-    static if (isFloatingPoint!U) {
-        alias prodType = U;
-    } else static if (is(U : double)) {
-        alias prodType = double;
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+
+    ProdAccumulator!double x;
+    x.put([1.0, 2.0, 3.0]);
+    assert(x.prod == 6.0);
+    x.put(4.0);
+    assert(x.prod == 24.0);
+    static assert(is(typeof(x.prod) == double));
+}
+
+package template prodType(T)
+{
+    import mir.math.sum: elementType;
+
+    alias U = elementType!T;
+    
+    static if (__traits(compiles, {
+        auto temp = U.init * U.init;
+        temp *= U.init;
+    })) {
+        import mir.math.stat: statType;
+
+        alias V = typeof(U.init * U.init);
+        alias prodType = statType!(V, false);
     } else {
-        static assert(0, "prodType: U must be a floating point type or " ~ 
-                         "implicitly convertible to a floating point type, " ~ 
-                         "not a " ~ U.stringof);
+        static assert(0, "prodType: Can't prod elements of type " ~ U.stringof);
     }
 }
 
@@ -411,6 +435,52 @@ unittest
     static assert(is(typeof(y) == double));
 }
 
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.algorithm.iteration: reduce;
+    import mir.math.common: approxEqual;
+
+    enum l = 2.0 ^^ (double.max_exp - 1);
+    enum s = 2.0 ^^ -(double.max_exp - 1);
+    enum c = 0.8;
+    enum u = c * 2.0 ^^ 10;
+    static immutable r = [l, l, l, s, s, s, u, u, u];
+    static immutable result1 = [u, u, u];
+    static immutable result2 = [c, c, c];
+              
+    assert(r.sliced.prod.approxEqual(reduce!"a * b"(1.0, result1)));
+
+    long e;
+    assert(r.sliced.prod(e).approxEqual(reduce!"a * b"(1.0, result2)));
+    assert(e == 30);
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.algorithm.iteration: reduce;
+    import mir.math.common: approxEqual;
+
+    enum l = 2.0 ^^ (float.max_exp - 1);
+    enum s = 2.0 ^^ -(float.max_exp - 1);
+    enum c = 0.8;
+    enum u = c * 2.0 ^^ 10;
+    static immutable r = [l, l, l, s, s, s, u, u, u];
+    static immutable result1 = [u, u, u];
+    static immutable result2 = [c, c, c];
+              
+    assert(r.sliced.prod!double.approxEqual(reduce!"a * b"(1.0, result1)));
+
+    long e;
+    assert(r.sliced.prod!double(e).approxEqual(reduce!"a * b"(1.0, result2)));
+    assert(e == 30);
+}
+
 /++
 Compute the sum of binary logarithms of the input range $(D r).
 The error of this method is much smaller than with a naive sum of log2.
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index a80753a1..4f63fa83 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -28,6 +28,158 @@ import mir.internal.utility: isFloatingPoint;
 
 // version = mir_test_topN;
 
+package template statType(T, bool checkComplex = true)
+{
+    import mir.internal.utility: isFloatingPoint, isComplex;
+
+    static if (isFloatingPoint!T || (checkComplex && isComplex!T)) {
+        import std.traits: Unqual;
+        alias statType = Unqual!T;
+    } else static if (is(T : double)) {
+        alias statType = double;
+    } else static if (checkComplex) {
+        static if (is(T : cdouble)) {
+            alias statType = cdouble;
+        } else {
+            static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a floating point (or complex floating point) type");
+        }
+    } else {
+        static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a floating point type");
+    }
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static assert(is(statType!int == double));
+    static assert(is(statType!uint == double));
+    static assert(is(statType!double == double));
+    static assert(is(statType!float == float));
+    static assert(is(statType!real == real));
+    static assert(is(statType!cfloat == cfloat));
+    static assert(is(statType!cdouble == cdouble));
+    static assert(is(statType!creal == creal));
+    
+    static assert(is(statType!(const(int)) == double));
+    static assert(is(statType!(immutable(int)) == double));
+    static assert(is(statType!(const(double)) == double));
+    static assert(is(statType!(immutable(double)) == double));
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
+        float x;
+        alias x this;
+    }
+    
+    static struct Bar {
+        cfloat x;
+        alias x this;
+    }
+
+    static assert(is(statType!Foo == double)); // note: this is not float
+    static assert(is(statType!Bar == cdouble)); // note: this is not cfloat
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
+        double x;
+        alias x this;
+    }
+    
+    static struct Bar {
+        cdouble x;
+        alias x this;
+    }
+
+    static assert(is(statType!Foo == double));
+    static assert(is(statType!Bar == cdouble));
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
+        real x;
+        alias x this;
+    }
+    
+    static struct Bar {
+        creal x;
+        alias x this;
+    }
+
+    static assert(is(statType!Foo == double)); // note: this is not real
+    static assert(is(statType!Bar == cdouble)); // note: this is not creal
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
+        int x;
+        alias x this;
+    }
+
+    static assert(is(statType!Foo == double)); // note: this is not ints
+}
+
+package template meanType(T)
+{
+    import mir.math.sum: sumType;
+    import mir.internal.utility: isFloatingPoint, isComplex;
+    
+    alias U = sumType!T;
+
+    static if (__traits(compiles, {
+        auto temp = U.init + U.init;
+        auto a = temp / 2;
+        temp += U.init;
+    })) {
+        alias V = typeof((U.init + U.init) / 2);
+        alias meanType = statType!V;
+    } else {
+        static assert(0, "meanType: Can't calculate mean of elements of type " ~ U.stringof);
+    }
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static assert(is(meanType!(int[]) == double));
+    static assert(is(meanType!(double[]) == double));
+    static assert(is(meanType!(float[]) == float));
+    static assert(is(meanType!(cfloat[]) == cfloat));
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
+        float x;
+        alias x this;
+    }
+    
+    static struct Bar {
+        cfloat x;
+        alias x this;
+    }
+
+    static assert(is(meanType!(Foo[]) == float));
+    static assert(is(meanType!(Bar[]) == cfloat));
+}
+
 /++
 Output range for mean.
 +/
@@ -100,10 +252,10 @@ unittest
 }
 
 /++
-Computes the average of the input.
+Computes the mean of the input.
 
 Returns:
-    The average of all the elements in the input.
+    The mean of all the elements in the input, must be floating point or complex
 
 See_also: 
     $(SUBREF sum, Summation)
@@ -114,22 +266,24 @@ template mean(F, Summation summation = Summation.appropriate)
     Params:
         r = range, must be finite iterable
     +/
-    @fmamath F mean(Range)(Range r)
+    @fmamath meanType!F mean(Range)(Range r)
         if (isIterable!Range)
     {
-        MeanAccumulator!(F, ResolveSummationType!(summation, Range, F)) mean;
+        alias G = typeof(return);
+        MeanAccumulator!(G, ResolveSummationType!(summation, Range, G)) mean;
         mean.put(r.move);
         return mean.mean;
     }
     
     /++
     Params:
-        val = values
+        ar = values
     +/
-    @fmamath F mean(scope const F[] val...)
+    @fmamath meanType!F mean(scope const F[] ar...)
     {
-        MeanAccumulator!(F, ResolveSummationType!(summation, const(F)[], F)) mean;
-        mean.put(val);
+        alias G = typeof(return);
+        MeanAccumulator!(G, ResolveSummationType!(summation, const(G)[], G)) mean;
+        mean.put(ar);
         return mean.mean;
     }
 }
@@ -141,17 +295,18 @@ template mean(Summation summation = Summation.appropriate)
     Params:
         r = range, must be finite iterable
     +/
-    @fmamath sumType!Range mean(Range)(Range r)
+    @fmamath meanType!Range mean(Range)(Range r)
         if (isIterable!Range)
     {
-        return .mean!(sumType!Range, summation)(r.move);
+        alias F = typeof(return);
+        return .mean!(F, summation)(r.move);
     }
     
     /++
     Params:
         ar = values
     +/
-    @fmamath sumType!T mean(T)(scope const T[] ar...)
+    @fmamath meanType!T mean(T)(scope const T[] ar...)
     {
         alias F = typeof(return);
         return .mean!(F, summation)(ar);
@@ -187,43 +342,49 @@ unittest
 
 /// Mean of vector
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
 
-    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
-                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
-    assert(x.sliced.mean == 29.25 / 12);
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+    assert(x.mean == 29.25 / 12);
 }
 
 /// Mean of matrix
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure
 unittest
 {
-    import mir.ndslice.slice: sliced;
+    import mir.ndslice.fuse: fuse;
 
-    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
-                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
-    assert(x.sliced(2, 6).mean == 29.25 / 12);
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    assert(x.mean == 29.25 / 12);
 }
 
 /// Column mean of matrix
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure
 unittest
 {
+    import mir.ndslice.fuse: fuse;
     import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: alongDim, byDim, map;
 
-    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
-                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
-    static immutable result = [1, 4.25, 3.25, 1.5, 2.5, 2.125];
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+    auto result = [1, 4.25, 3.25, 1.5, 2.5, 2.125].sliced;
 
     // Use byDim or alongDim with map to compute mean of row/column.
-    assert(x.sliced(2, 6).byDim!1.map!mean == result);
-    assert(x.sliced(2, 6).alongDim!0.map!mean == result);
+    assert(x.byDim!1.map!mean == result);
+    assert(x.alongDim!0.map!mean == result);
 
     // FIXME
     // Without using map, computes the mean of the whole slice
@@ -233,7 +394,7 @@ unittest
 
 /// Can also set algorithm or output type
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -241,9 +402,9 @@ unittest
 
     //Set sum algorithm or output type
 
-    static immutable a = [1, 1e100, 1, -1e100];
+    auto a = [1, 1e100, 1, -1e100].sliced;
 
-    auto x = a.sliced * 10_000;
+    auto x = a * 10_000;
     assert(x.mean!"kbn" == 20_000 / 4);
     assert(x.mean!"kb2" == 20_000 / 4);
     assert(x.mean!"precise" == 20_000 / 4);
@@ -255,50 +416,59 @@ unittest
 
 /++
 For integral slices, pass output type as template parameter to ensure output
-type is correct
+type is correct. By default, if an input type is not floating point, then the
+result will be a dobule if it is implicitly convertible to a floating point type.
 +/
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
 
-    static immutable x = [0, 1, 1, 2, 4, 4,
-                          2, 7, 5, 1, 2, 0];
-    assert(approxEqual(x.sliced.mean!double, 29.0 / 12, 1.0e-10));
+    auto x = [0, 1, 1, 2, 4, 4,
+              2, 7, 5, 1, 2, 0].sliced;
+
+    auto y = x.mean;
+    assert(y.approxEqual(29.0 / 12, 1.0e-10));
+    static assert(is(typeof(y) == double));
+
+    assert(x.mean!float.approxEqual(29f / 12, 1.0e-10));
 }
 
-/// Mean works for complex numbers (and other user-defined types)
+/++
+Mean works for complex numbers and other user-defined types (provided they
+can be converted to a floating point or complex type)
++/
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
 
-    static immutable cdouble[] x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i];
-    static immutable cdouble result = 2.5 + 3.5i;
-    assert(x.sliced.mean == result);
+    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
+    assert(x.mean.approxEqual(2.5 + 3.5i));
 }
 
 /// Compute mean tensors along specified dimention of tensors
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure nothrow
 unittest
 {
     import mir.ndslice: alongDim, iota, as, map;
-    /*
+    /++
       [[0,1,2],
        [3,4,5]]
-     */
+     +/
     auto x = iota(2, 3).as!double;
     assert(x.mean == (5.0 / 2.0));
 
-    static immutable m0 = [(0.0+3.0)/2.0, (1.0+4.0)/2.0, (2.0+5.0)/2.0];
+    auto m0 = [(0.0+3.0)/2.0, (1.0+4.0)/2.0, (2.0+5.0)/2.0];
     assert(x.alongDim!0.map!mean == m0);
     assert(x.alongDim!(-2).map!mean == m0);
 
-    static immutable m1 = [(0.0+1.0+2.0)/3.0, (3.0+4.0+5.0)/3.0];
+    auto m1 = [(0.0+1.0+2.0)/3.0, (3.0+4.0+5.0)/3.0];
     assert(x.alongDim!1.map!mean == m1);
     assert(x.alongDim!(-1).map!mean == m1);
 
@@ -321,8 +491,83 @@ unittest
     assert([1.0, 2, 3, 4].mean == 2.5);
 }
 
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, alongDim, map;
+    import mir.math.common: approxEqual;
+    import mir.algorithm.iteration: all;
+
+    auto x = iota([2, 2], 1);
+    auto y = x.alongDim!1.map!mean;
+    assert(y.all!approxEqual([1.5, 3.5]));
+    static assert(is(meanType!(typeof(y)) == double));
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+
+    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
+
+    assert(x.sliced.mean == 29.25 / 12);
+    assert(x.sliced.mean!float == 29.25 / 12);
+}
+
+package template hmeanType(T)
+{
+    import mir.math.sum: sumType;
+    
+    alias U = sumType!T;
+
+    static if (__traits(compiles, {
+        U t = U.init + cast(U) 1; //added for when U.init = 0
+        auto temp = cast(U) 1 / t + cast(U) 1 / t;
+    })) {
+        alias V = typeof(cast(U) 1 / ((cast(U) 1 / U.init + cast(U) 1 / U.init) / cast(U) 2));
+        alias hmeanType = statType!V;
+    } else {
+        static assert(0, "meanType: Can't calculate hmean of elements of type " ~ U.stringof);
+    }
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static assert(is(hmeanType!(int[]) == double));
+    static assert(is(hmeanType!(double[]) == double));
+    static assert(is(hmeanType!(float[]) == float)); 
+    static assert(is(hmeanType!(cfloat[]) == cfloat));    
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
+        float x;
+        alias x this;
+    }
+    
+    static struct Bar {
+        cfloat x;
+        alias x this;
+    }
+
+    static assert(is(hmeanType!(Foo[]) == float));
+    static assert(is(hmeanType!(Bar[]) == cfloat));
+}
+
 /++
-Computes the harmonic mean of a range.
+Computes the harmonic mean of the input.
+
+Returns:
+    harmonic mean of all the elements of the input, must be floating point
 
 See_also: 
     $(SUBREF sum, Summation)
@@ -332,23 +577,48 @@ template hmean(F, Summation summation = Summation.appropriate)
     /++
     Params:
         r = range
-    Returns:
-        harmonic mean of the range
     +/
-    @fmamath F hmean(Range)(Range r)
+    @fmamath hmeanType!F hmean(Range)(Range r)
         if (isIterable!Range)
     {
         import mir.ndslice.topology: map;
-        static if (summation == Summation.fast && __traits(compiles, r.move.map!"1.0 / a"))
+
+        alias G = typeof(return);
+        auto numerator = cast(G) 1;
+
+        static if (summation == Summation.fast && __traits(compiles, r.move.map!"numerator / a"))
         {
-            return 1.0 / r.move.map!"1.0 / a".mean!(F, summation);
+            return numerator / r.move.map!"numerator / a".mean!(G, summation);
         }
         else
         {
-            MeanAccumulator!(F, ResolveSummationType!(summation, Range, F)) imean;
+            MeanAccumulator!(G, ResolveSummationType!(summation, Range, G)) imean;
             foreach (e; r)
-                imean.put(1.0 / e);
-            return 1.0 / imean.mean;
+                imean.put(numerator / e);
+            return numerator / imean.mean;
+        }
+    }
+   
+    /++
+    Params:
+        ar = values
+    +/
+    @fmamath hmeanType!F hmean(scope const F[] ar...)
+    {
+        alias G = typeof(return);
+
+        auto numerator = cast(G) 1;
+
+        static if (summation == Summation.fast && __traits(compiles, ar.map!"numerator / a"))
+        {
+            return numerator / ar.map!"numerator / a".mean!(G, summation);
+        }
+        else
+        {
+            MeanAccumulator!(G, ResolveSummationType!(summation, const(G)[], G)) imean;
+            foreach (e; ar)
+                imean.put(numerator / e);
+            return numerator / imean.mean;
         }
     }
 }
@@ -359,13 +629,22 @@ template hmean(Summation summation = Summation.appropriate)
     /++
     Params:
         r = range
-    Returns:
-        harmonic mean of the range
     +/
-    @fmamath sumType!Range hmean(Range)(Range r)
+    @fmamath hmeanType!Range hmean(Range)(Range r)
         if (isIterable!Range)
     {
-        return .hmean!(typeof(1.0 / sumType!Range.init), summation)(r.move);
+        alias F = typeof(return);
+        return .hmean!(F, summation)(r.move);
+    }
+    
+    /++
+    Params:
+        ar = values
+    +/
+    @fmamath hmeanType!T hmean(T)(scope const T[] ar...)
+    {
+        alias F = typeof(return);
+        return .hmean!(F, summation)(ar);
     }
 }
 
@@ -383,12 +662,13 @@ template hmean(string summation)
 
 /// Harmonic mean of vector
 version(mir_test)
-pure @safe nothrow @nogc
+@safe pure nothrow
 unittest
 {
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
-    static immutable x = [20.0, 100.0, 2000.0, 10.0, 5.0, 2.0];
+    auto x = [20.0, 100.0, 2000.0, 10.0, 5.0, 2.0].sliced;
 
     assert(x.hmean.approxEqual(6.97269));
 }
@@ -401,7 +681,10 @@ unittest
     import mir.math.common: approxEqual;
     import mir.ndslice.fuse: fuse;
 
-    auto x = [[20.0, 100.0, 2000.0], [10.0, 5.0, 2.0]].fuse;
+    auto x = [
+        [20.0, 100.0, 2000.0], 
+        [10.0, 5.0, 2.0]
+    ].fuse;
 
     assert(x.hmean.approxEqual(6.97269));
 }
@@ -430,14 +713,15 @@ unittest
 
 /// Can also pass arguments to hmean
 version(mir_test)
-pure @safe nothrow @nogc
+pure @safe nothrow
 unittest
 {
     import mir.ndslice.topology: repeat;
+    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
 
     //Set sum algorithm or output type
-    static immutable x = [1, 1e-100, 1, -1e-100];
+    auto x = [1, 1e-100, 1, -1e-100].sliced;
 
     assert(x.hmean!"kb2".approxEqual(2));
     assert(x.hmean!"precise".approxEqual(2));
@@ -446,6 +730,71 @@ unittest
     assert(float.max.repeat(3).hmean!(double, "fast").approxEqual(float.max));
 }
 
+/++
+For integral slices, pass output type as template parameter to ensure output
+type is correct. By default, if an input type is not floating point, then the
+result will be a dobule if it is implicitly convertible to a floating point type.
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [20, 100, 2000, 10, 5, 2].sliced;
+
+    auto y = x.hmean;
+
+    assert(y.approxEqual(6.97269));
+    static assert(is(typeof(y) == double));
+
+    assert(x.hmean!float.approxEqual(6.97269));
+}
+
+/++
+hmean works for complex numbers and other user-defined types (provided they
+can be converted to a floating point or complex type)
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
+    assert(x.hmean.approxEqual(1.97110904 + 3.14849332i));
+}
+
+/// Arbitrary harmonic mean
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
+
+    auto x = hmean(20.0, 100, 2000, 10, 5, 2);
+    assert(x.approxEqual(6.97269));
+    
+    auto y = hmean!float(20, 100, 2000, 10, 5, 2);
+    assert(y.approxEqual(6.97269));
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
+
+    static immutable x = [20.0, 100.0, 2000.0, 10.0, 5.0, 2.0];
+
+    assert(x.sliced.hmean.approxEqual(6.97269));
+    assert(x.sliced.hmean!float.approxEqual(6.97269));
+}
+
 private
 F nthroot(F)(in F x, in size_t n)
     if (isFloatingPoint!F)
@@ -560,20 +909,15 @@ package template gmeanType(T)
     
     alias U = prodType!T;
 
-    static if (isFloatingPoint!U) {
-        static if (__traits(compiles, {
-            auto temp = U.init * U.init;
-            auto a = nthroot(temp, 2);
-            temp *= U.init;
-            a = nthroot(temp, 3);
-        }))
-            alias gmeanType = typeof(nthroot(U.init * U.init, 2));
-        else
-            static assert(0, "gmeanType: Can't gmean elements of type " ~ U.stringof);
-    } else static if (is(U : float) || is(U : double) || is(U : real)) {
-        alias gmeanType = gmeanType!double;
+    static if (__traits(compiles, {
+        auto temp = U.init * U.init;
+        auto a = nthroot(temp, 2);
+        temp *= U.init;
+    })) {
+        alias V = typeof(nthroot(U.init * U.init, 2));
+        alias gmeanType = statType!(V, false);
     } else {
-        static assert(0, "gmeanType: Can't gmean elements of type " ~ U.stringof);
+        static assert(0, "gmeanType: Can't calculate gmean of elements of type " ~ U.stringof);
     }
 }
 
@@ -595,7 +939,7 @@ Computes the geometric average of the input.
 Params:
     r = range, must be finite iterable
 Returns:
-    The geometric average of all the elements in the input.
+    The geometric average of all the elements in the input, must be floating point
 
 See_also: 
     $(SUBREF numeric, prod)
@@ -720,8 +1064,8 @@ unittest
 
 /++
 For integral slices, pass output type as template parameter to ensure output
-type is correct. By default, if an input type is not floating point, then if
-it is convertible to floating point, the result will be a double.
+type is correct. By default, if an input type is not floating point, then the
+result will be a dobule if it is implicitly convertible to a floating point type.
 +/
 version(mir_test)
 @safe pure nothrow
@@ -732,13 +1076,14 @@ unittest
 
     auto x = [5, 1, 1, 2, 4, 4,
               2, 7, 5, 1, 2, 10].sliced;
-    assert(x.gmean!float.approxEqual(2.79160522));
-    
+
     auto y = x.gmean;
     static assert(is(typeof(y) == double));
+    
+    assert(x.gmean!float.approxEqual(2.79160522));
 }
 
-/// Mean works for user-defined types, provided the nth root can be taken for them
+/// gean works for user-defined types, provided the nth root can be taken for them
 version(mir_test)
 @safe pure nothrow
 unittest
@@ -824,6 +1169,20 @@ unittest
     assert(gmean([1, 2, 3]).approxEqual(1.81712059));
 }
 
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    static immutable x = [3.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+                          2.0, 7.5, 5.0, 1.0, 1.5, 2.0];
+
+    assert(x.sliced.gmean.approxEqual(2.36178395));
+    assert(x.sliced.gmean!float.approxEqual(2.36178395));
+}
+
 /++
 Computes the median of `slice`.
 
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 7d3ade31..e7ab248d 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1983,25 +1983,32 @@ private T summationInitValue(T)()
     }
 }
 
-package template sumType(Range)
+package template elementType(T)
 {
     import mir.ndslice.slice: isSlice, DeepElementType;
+    import std.traits: Unqual, ForeachType;
 
-    static if (isIterable!Range) {
-        static if (isSlice!Range)
-            alias T = Unqual!(DeepElementType!(Range.This));
+    static if (isIterable!T) {
+        static if (isSlice!T)
+            alias elementType = Unqual!(DeepElementType!(T.This));
         else
-            alias T = Unqual!(ForeachType!Range);
+            alias elementType = Unqual!(ForeachType!T);
     } else {
-        alias T = Unqual!Range;
+        alias elementType = Unqual!T;
     }
+}
+
+package template sumType(Range)
+{
+    alias T = elementType!Range;
+
     static if (__traits(compiles, {
         auto a = T.init + T.init;
         a += T.init;
     }))
         alias sumType = typeof(T.init + T.init);
     else
-        static assert(0, "Can't sum elements of type " ~ T.stringof);
+        static assert(0, "sumType: Can't sum elements of type " ~ T.stringof);
 }
 
 /++
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 0705b791..eafe6c46 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2744,7 +2744,7 @@ pure version(mir_test) unittest
     // 1 2
     // 3 4
     auto matrix = iota([2, 2], 1);
-    assert(maxAvg(matrix) == 3);
+    assert(maxAvg(matrix) == 3.5);
 }
 
 

From 2642999bd04e53fb49a4f4935d1eb84680ebf995 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 6 Jun 2020 19:04:17 -0400
Subject: [PATCH 004/573] Adjust center UT (#270)

---
 source/mir/math/stat.d | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 4f63fa83..d20aa2fd 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -1619,11 +1619,13 @@ unittest
     auto a = [1, 1e100, 1, -1e100];
 
     auto x = a.sliced * 10_000;
-    auto result = [5000, 1e104 - 5000, 5000, -1e104 - 5000].sliced;
+    //Due to Floating Point precision, subtracting the mean from the second
+    //and fourth numbers in `x` does not change the value of the result
+    auto result = [5000, 1e104, 5000, -1e104].sliced;
 
-    assert(x.center!(mean!"kbn").all!approxEqual(result));
-    assert(x.center!(mean!"kb2").all!approxEqual(result));
-    assert(x.center!(mean!"precise").all!approxEqual(result));
+    assert(x.center!(mean!"kbn") == result);
+    assert(x.center!(mean!"kb2") == result);
+    assert(x.center!(mean!"precise") == result);
 }
 
 /++

From cb82c78d2b80e628188c04dd801fa69b76878a34 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 18 Jun 2020 03:45:13 -0400
Subject: [PATCH 005/573] Add variance (#271)

* Add variance

* Remove VarianceAccumulator constructors

* Add VarianceAccumulator moves

* Variance fixups

* Remove naryFun from MapSummator

* Address variance review

* Add twoPass variance algorithm

* Add put/VarianceAccumulator overload

* Add mean member function to VarianceAccumulator

* Add fast MeanAlgo

* Fixup docs for MeanAlgo

* Update fast MeanAlgo put

* Resolve merge issues

* Address review on summation

* Remove fast MeanAlgo
---
 source/mir/math/stat.d | 928 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 905 insertions(+), 23 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index d20aa2fd..ff4467e6 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -183,18 +183,23 @@ unittest
 /++
 Output range for mean.
 +/
-struct MeanAccumulator(T, Summation summation) 
-    if (isMutable!T)
+struct MeanAccumulator(T, Summation summation)
 {
     ///
     size_t count;
     ///
-    Summator!(T, summation) sumAccumulator;
+    Summator!(T, summation) summator;
 
     ///
     F mean(F = T)() @property
     {
-        return cast(F) sumAccumulator.sum / cast(F) count;
+        return cast(F) summator.sum / cast(F) count;
+    }
+    
+    ///
+    F sum(F = T)() @property
+    {
+        return cast(F) summator.sum ;
     }
 
     ///
@@ -204,14 +209,14 @@ struct MeanAccumulator(T, Summation summation)
         static if (hasShape!Range)
         {
             count += r.elementCount;
-            sumAccumulator.put(r);
+            summator.put(r);
         }
         else
         {
             foreach(x; r)
             {
                 count++;
-                sumAccumulator.put(x);
+                summator.put(x);
             }
         }
     }
@@ -220,7 +225,14 @@ struct MeanAccumulator(T, Summation summation)
     void put()(T x)
     {
         count++;
-        sumAccumulator.put(x);
+        summator.put(x);
+    }
+    
+    ///
+    void put(F = T)(MeanAccumulator!(F, summation) m)
+    {
+        count += m.count;
+        summator.put(cast(T) m.summator);
     }
 }
 
@@ -247,13 +259,32 @@ unittest
     MeanAccumulator!(float, Summation.pairwise) x;
     x.put([0, 1, 2, 3, 4].sliced);
     assert(x.mean == 2);
+    assert(x.sum == 10);
     x.put(5);
     assert(x.mean == 2.5);
 }
 
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    double[] x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25];
+    double[] y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
+    
+    MeanAccumulator!(float, Summation.pairwise) m0;
+    m0.put(x);
+    MeanAccumulator!(float, Summation.pairwise) m1;
+    m1.put(y);
+    m0.put(m1);
+    assert(m0.mean == 29.25 / 12);
+}
+
 /++
 Computes the mean of the input.
 
+Params:
+    F: controls type of output
+    summation: algorithm for calculating sums (default: Summation.appropriate)
 Returns:
     The mean of all the elements in the input, must be floating point or complex
 
@@ -337,7 +368,7 @@ unittest
     
     assert(mean!float([0, 1, 2, 3, 4, 5].sliced(3, 2)) == 2.5);
     
-    assert(is(typeof(mean!float([1, 2, 3])) == float));
+    static assert(is(typeof(mean!float([1, 2, 3])) == float));
 }
 
 /// Mean of vector
@@ -373,23 +404,24 @@ version(mir_test)
 unittest
 {
     import mir.ndslice.fuse: fuse;
-    import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: alongDim, byDim, map;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
 
     auto x = [
         [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
         [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
     ].fuse;
-    auto result = [1, 4.25, 3.25, 1.5, 2.5, 2.125].sliced;
+    auto result = [1, 4.25, 3.25, 1.5, 2.5, 2.125];
 
     // Use byDim or alongDim with map to compute mean of row/column.
-    assert(x.byDim!1.map!mean == result);
-    assert(x.alongDim!0.map!mean == result);
+    assert(x.byDim!1.map!mean.all!approxEqual(result));
+    assert(x.alongDim!0.map!mean.all!approxEqual(result));
 
     // FIXME
     // Without using map, computes the mean of the whole slice
-    // assert(x.sliced(2, 6).byDim!1.mean == x.sliced.mean);
-    // assert(x.sliced(2, 6).alongDim!0.mean == x.sliced.mean);
+    // assert(x.byDim!1.mean == x.sliced.mean);
+    // assert(x.alongDim!0.mean == x.sliced.mean);
 }
 
 /// Can also set algorithm or output type
@@ -398,13 +430,14 @@ version(mir_test)
 unittest
 {
     import mir.ndslice.slice: sliced;
-    import mir.ndslice.topology: map, repeat;
+    import mir.ndslice.topology: repeat;
 
     //Set sum algorithm or output type
 
     auto a = [1, 1e100, 1, -1e100].sliced;
 
     auto x = a * 10_000;
+
     assert(x.mean!"kbn" == 20_000 / 4);
     assert(x.mean!"kb2" == 20_000 / 4);
     assert(x.mean!"precise" == 20_000 / 4);
@@ -417,7 +450,7 @@ unittest
 /++
 For integral slices, pass output type as template parameter to ensure output
 type is correct. By default, if an input type is not floating point, then the
-result will be a dobule if it is implicitly convertible to a floating point type.
+result will be a double if it is implicitly convertible to a floating point type.
 +/
 version(mir_test)
 @safe pure nothrow
@@ -566,6 +599,9 @@ unittest
 /++
 Computes the harmonic mean of the input.
 
+Params:
+    F: controls type of output
+    summation: algorithm for calculating sums (default: Summation.appropriate)
 Returns:
     harmonic mean of all the elements of the input, must be floating point
 
@@ -725,15 +761,16 @@ unittest
 
     assert(x.hmean!"kb2".approxEqual(2));
     assert(x.hmean!"precise".approxEqual(2));
+    assert(x.hmean!(double, "precise").approxEqual(2));
 
     //Provide the summation type
-    assert(float.max.repeat(3).hmean!(double, "fast").approxEqual(float.max));
+    assert(float.max.repeat(3).hmean!double.approxEqual(float.max));
 }
 
 /++
 For integral slices, pass output type as template parameter to ensure output
 type is correct. By default, if an input type is not floating point, then the
-result will be a dobule if it is implicitly convertible to a floating point type.
+result will be a double if it is implicitly convertible to a floating point type.
 +/
 version(mir_test)
 @safe pure nothrow
@@ -986,7 +1023,7 @@ unittest
     
     assert(gmean!float([1, 2, 3, 4, 5, 6].sliced(3, 2)).approxEqual(2.99379516));
     
-    assert(is(typeof(gmean!float([1, 2, 3])) == float));
+    static assert(is(typeof(gmean!float([1, 2, 3])) == float));
 }
 
 /// Geometric mean of vector
@@ -1065,7 +1102,7 @@ unittest
 /++
 For integral slices, pass output type as template parameter to ensure output
 type is correct. By default, if an input type is not floating point, then the
-result will be a dobule if it is implicitly convertible to a floating point type.
+result will be a double if it is implicitly convertible to a floating point type.
 +/
 version(mir_test)
 @safe pure nothrow
@@ -1611,14 +1648,12 @@ pure @safe nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
-    import mir.algorithm.iteration: all;
-    import mir.ndslice.topology: repeat;
-    import mir.math.common: approxEqual;
 
     //Set sum algorithm or output type
     auto a = [1, 1e100, 1, -1e100];
 
     auto x = a.sliced * 10_000;
+
     //Due to Floating Point precision, subtracting the mean from the second
     //and fourth numbers in `x` does not change the value of the result
     auto result = [5000, 1e104, 5000, -1e104].sliced;
@@ -1628,6 +1663,853 @@ unittest
     assert(x.center!(mean!"precise") == result);
 }
 
+/++
+Output range that applies function `fun` to each input before summing
+
++/
+struct MapSummator(alias fun, T, Summation summation) 
+    if(isMutable!T)
+{
+    ///
+    Summator!(T, summation) summator;
+
+    ///
+    F sum(F = T)() @property
+    {
+        return cast(F) summator.sum;
+    }
+    
+    ///
+    void put(Range)(Range r)
+        if (isIterable!Range)
+    {
+        import mir.ndslice.topology: map;
+        summator.put(r.map!fun);
+    }
+
+    ///
+    void put()(T x)
+    {
+        summator.put(fun(x));
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: powi;
+
+    alias f = (double x) => (powi(x, 2));
+    MapSummator!(f, double, Summation.pairwise) x;
+    x.put([0.0, 1, 2, 3, 4].sliced);
+    assert(x.sum == 30.0);
+    x.put(5);
+    assert(x.sum == 55.0);
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+
+    alias f = (double x) => (x + 1);
+    MapSummator!(f, double, Summation.pairwise) x;
+    x.put([0.0, 1, 2, 3, 4].sliced);
+    assert(x.sum == 15.0);
+    x.put(5);
+    assert(x.sum == 21.0);
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+
+    alias f = (double x) => (x + 1);
+    MapSummator!(f, double, Summation.pairwise) x;
+    static immutable a = [0.0, 1, 2, 3, 4];
+    x.put(a.sliced);
+    assert(x.sum == 15.0);
+    x.put(5);
+    assert(x.sum == 21.0);
+}
+
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.ndslice.fuse: fuse;
+
+    alias f = (double x) => (x + 1);
+    MapSummator!(f, double, Summation.pairwise) x;
+    auto a = [
+        [0.0, 1, 2],
+        [3.0, 4, 5]
+    ].fuse;
+    auto b = [6.0, 7, 8].sliced;
+    x.put(a);
+    assert(x.sum == 21.0);
+    x.put(b);
+    assert(x.sum == 45.0);
+}
+
+/++
+Variance algorithms.
+
+See Also:
+    $(WEB en.wikipedia.org/wiki/Algorithms_for_calculating_variance, Algorithms for calculating variance).
++/
+enum VarianceAlgo
+{
+    /++
+    Performs Welford's online algorithm for updating variance. Can also `put`
+    another VarianceAccumulator of the same type, which uses the parallel
+    algorithm from Chan et al., described above.
+    +/
+    online,
+    
+    /++
+    Calculates variance using E(x^^2) - E(x)^2 (alowing for adjustments for 
+    population/sample variance). This algorithm can be numerically unstable.
+    +/
+    naive,
+
+    /++
+    Calculates variance using a two-pass algorithm whereby the input is first 
+    centered and then the sum of squares is calculated from that.
+    +/
+    twoPass
+}
+
+private
+mixin template moment_ops(T,
+                          Summation summation)
+{
+    ///
+    MeanAccumulator!(T, summation) meanAccumulator;
+
+    ///
+    size_t count() @property
+    {
+        return meanAccumulator.count;
+    }
+
+    ///
+    F mean(F = T)() @property
+    {
+        return meanAccumulator.mean;
+    }
+}
+
+private
+mixin template outputRange_ops(T)
+{
+    ///
+    this(Range)(Range r)
+        if (isIterable!Range)
+    {
+        import core.lifetime: move;
+        this.put(r.move);
+    }
+
+    ///
+    this()(T x)
+    {
+        this.put(x);
+    }
+}
+
+///
+struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
+    if (isMutable!T && varianceAlgo == VarianceAlgo.naive)
+{
+    import mir.functional: naryFun;
+
+    private alias square = naryFun!"a * a";
+
+    mixin moment_ops!(T, summation);
+    mixin outputRange_ops!T;
+
+    ///
+    Summator!(T, summation) sumOfSquares;
+
+    ///
+    void put(Range)(Range r)
+        if (isIterable!Range)
+    {
+        foreach(x; r)
+        {
+            this.put(x);
+        }
+    }
+
+    ///
+    void put()(T x)
+    {
+        meanAccumulator.put(x);
+        sumOfSquares.put(square(x));
+    }
+
+    ///
+    F variance(F = T)(bool isPopulation) @property
+    {
+        if (isPopulation == false)
+            return cast(F) sumOfSquares.sum / cast(F) (count - 1) - 
+                square(cast(F) meanAccumulator.mean) * (cast(F) count / cast(F) (count - 1));
+        else
+            return cast(F) sumOfSquares.sum / cast(F) count - 
+                square(cast(F) meanAccumulator.mean);
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    VarianceAccumulator!(double, VarianceAlgo.naive, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+
+    v.put(4.0);
+    assert(v.variance(PopulationTrueRT).approxEqual(57.01923 / 13));
+    assert(v.variance(PopulationTrueCT).approxEqual(57.01923 / 13));
+    assert(v.variance(PopulationFalseRT).approxEqual(57.01923 / 12));
+    assert(v.variance(PopulationFalseCT).approxEqual(57.01923 / 12));
+}
+
+///
+struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
+    if (isMutable!T && 
+        varianceAlgo == VarianceAlgo.online)
+{
+    mixin moment_ops!(T, summation);
+    mixin outputRange_ops!T;
+
+    ///
+    Summator!(T, summation) centeredSumOfSquares;
+
+    ///
+    void put(Range)(Range r)
+        if (isIterable!Range)
+    {
+        foreach(x; r)
+        {
+            this.put(x);
+        }
+    }
+
+    ///
+    void put()(T x)
+    {
+        size_t oldCount = count;
+        T delta = x;
+        if (count > 0) {
+            delta -= meanAccumulator.mean;
+        }
+        meanAccumulator.put(x);
+        centeredSumOfSquares.put(delta * (x - meanAccumulator.mean));
+    }
+
+    ///
+    void put()(VarianceAccumulator!(T, varianceAlgo, summation) v)
+    {
+        size_t oldCount = count;
+        T delta = v.mean;
+        if (oldCount > 0) {
+            delta -= meanAccumulator.mean;
+        }
+        meanAccumulator.put!T(v.meanAccumulator);
+        centeredSumOfSquares.put(v.centeredSumOfSquares.sum + delta * delta * v.count * oldCount / count);
+    }
+
+    ///
+    F variance(F = T)(bool isPopulation) @property
+    {
+        if (isPopulation == false)
+            return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
+        else
+            return cast(F) centeredSumOfSquares.sum / cast(F) count;
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
+    v.put(x);
+
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+
+    v.put(4.0);
+    assert(v.variance(PopulationTrueRT).approxEqual(57.01923 / 13));
+    assert(v.variance(PopulationTrueCT).approxEqual(57.01923 / 13));
+    assert(v.variance(PopulationFalseRT).approxEqual(57.01923 / 12));
+    assert(v.variance(PopulationFalseCT).approxEqual(57.01923 / 12));
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
+    auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(PopulationTrueRT).approxEqual(12.55208 / 6));
+    assert(v.variance(PopulationTrueCT).approxEqual(12.55208 / 6));
+    assert(v.variance(PopulationFalseRT).approxEqual(12.55208 / 5));
+    assert(v.variance(PopulationFalseCT).approxEqual(12.55208 / 5));
+
+    v.put(y);
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
+    auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(PopulationTrueRT).approxEqual(12.55208 / 6));
+    assert(v.variance(PopulationTrueCT).approxEqual(12.55208 / 6));
+    assert(v.variance(PopulationFalseRT).approxEqual(12.55208 / 5));
+    assert(v.variance(PopulationFalseCT).approxEqual(12.55208 / 5));
+
+    VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) w;
+    w.put(y);
+    v.put(w);
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [1.0 + 3i, 2, 3].sliced;
+
+    VarianceAccumulator!(cdouble, VarianceAlgo.online, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(true).approxEqual((-4.0 - 6i) / 3));
+    assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(false).approxEqual(54.76562 / 11));
+
+    v.put(4.0);
+    assert(v.variance(false).approxEqual(57.01923 / 12));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
+    auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(false).approxEqual(12.55208 / 5));
+
+    VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) w;
+    w.put(y);
+    v.put(w);
+    assert(v.variance(false).approxEqual(54.76562 / 11));
+}
+
+///
+struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
+    if (isMutable!T && varianceAlgo == VarianceAlgo.twoPass)
+{
+    import mir.functional: naryFun;
+    import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
+
+    mixin moment_ops!(T, summation);
+
+    ///
+    Summator!(T, summation) centeredSumOfSquares;
+
+    ///
+    this(Iterator, size_t N, SliceKind kind)(
+         Slice!(Iterator, N, kind) slice)
+    {
+        import core.lifetime: move;
+        import mir.ndslice.topology: vmap, map;
+        import mir.ndslice.internal: LeftOp;
+
+        meanAccumulator.put(slice.lightScope);
+        centeredSumOfSquares.put(slice.move.vmap(LeftOp!("-", T)(meanAccumulator.mean)).map!(naryFun!"a * a"));
+    }
+
+    ///
+    this(U)(U[] array)
+    {
+        import mir.ndslice.slice: sliced;
+        this(array.sliced);
+    }
+
+    ///
+    this(T)(T withAsSlice)
+        if (hasAsSlice!T)
+    {
+        this(withAsSlice.asSlice);
+    }
+
+    ///
+    this()(T x)
+    {
+        meanAccumulator.put(x);
+        centeredSumOfSquares.put(cast(T) 0);
+    }
+
+    ///
+    F variance(F = T)(bool isPopulation) @property
+    {
+        if (isPopulation == false)
+            return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
+        else
+            return cast(F) centeredSumOfSquares.sum / cast(F) count;
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    auto v = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+}
+
+/++
+Calculates the variance of the input
+
+Params:
+    F: controls type of output
+    varianceAlgo: algorithm for calculating variance (default: VarianceAlgo.online)
+    summation: algorithm for calculating sums (default: Summation.appropriate)
+
+Returns:
+    The variance of the input
++/
+template variance(
+    F, 
+    VarianceAlgo varianceAlgo = VarianceAlgo.online, 
+    Summation summation = Summation.appropriate)
+{
+    /++
+    Params:
+        r = range, must be finite iterable
+        isPopulation: true if population variace, false if sample variance (default)
+    +/
+    @fmamath meanType!F variance(Range)(Range r, bool isPopulation = false)
+        if (isIterable!Range)
+    {
+        import core.lifetime: move;
+        alias G = typeof(return);
+        auto varianceAccumulator = VarianceAccumulator!(G, varianceAlgo, ResolveSummationType!(summation, Range, G))(r.move);
+        return varianceAccumulator.variance(isPopulation);
+    }
+
+    /++
+    Params:
+        ar = values
+    +/
+    @fmamath meanType!F variance(scope const F[] ar...)
+    {
+        alias G = typeof(return);
+        auto varianceAccumulator = VarianceAccumulator!(G, varianceAlgo, ResolveSummationType!(summation, const(G)[], G))(ar);
+        return varianceAccumulator.variance(false);
+    }
+}
+
+/// ditto
+template variance(
+    VarianceAlgo varianceAlgo = VarianceAlgo.online, 
+    Summation summation = Summation.appropriate)
+{
+    /++
+    Params:
+        r = range, must be finite iterable
+        isPopulation: true if population variace, false if sample variance (default)
+    +/
+    @fmamath meanType!Range variance(Range)(Range r, bool isPopulation = false)
+        if(isIterable!Range)
+    {
+        import core.lifetime: move;
+        alias F = typeof(return);
+        return .variance!(F, varianceAlgo, summation)(r.move, isPopulation);
+    }
+
+    /++
+    Params:
+        ar = values
+    +/
+    @fmamath meanType!T variance(T)(scope const T[] ar...)
+    {
+        alias F = typeof(return);
+        return .variance!(F, varianceAlgo, summation)(ar);
+    }
+}
+
+/// ditto
+template variance(F, string varianceAlgo, string summation = "appropriate")
+{
+    mixin("alias variance = .variance!(F, VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
+}
+
+/// ditto
+template variance(string varianceAlgo, string summation = "appropriate")
+{
+    mixin("alias variance = .variance!(VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    assert(variance([1.0, 2, 3]).approxEqual(2.0 / 2));
+    assert(variance([1.0, 2, 3], true).approxEqual(2.0 / 3));
+
+    assert(variance([1.0 + 3i, 2, 3]).approxEqual((-4.0 - 6i) / 2));
+    
+    assert(variance!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 5));
+    
+    static assert(is(typeof(variance!float([1, 2, 3])) == float));
+}
+
+/// Variance of vector
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    assert(x.variance.approxEqual(54.76562 / 11));
+}
+
+/// Variance of matrix
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.math.common: approxEqual;
+
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    assert(x.variance.approxEqual(54.76562 / 11));
+}
+
+/// Column variance of matrix
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.topology: alongDim, byDim, map;
+    import mir.math.common: approxEqual;
+    import mir.algorithm.iteration: all;
+
+    auto x = [
+        [0.0,  1.0, 1.5, 2.0], 
+        [3.5, 4.25, 2.0, 7.5],
+        [5.0,  1.0, 1.5, 0.0]
+    ].fuse;
+    auto result = [13.16667 / 2, 7.041667 / 2, 0.1666667 / 2, 30.16667 / 2];
+
+    // Use byDim or alongDim with map to compute variance of row/column.
+    assert(x.byDim!1.map!variance.all!approxEqual(result));
+    assert(x.alongDim!0.map!variance.all!approxEqual(result));
+
+    // FIXME
+    // Without using map, computes the variance of the whole slice
+    // assert(x.byDim!1.variance == x.sliced.variance);
+    // assert(x.alongDim!0.variance == x.sliced.variance);
+}
+
+/// Can also set algorithm type
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    auto x = a + 1_000_000_000;
+
+    auto y = x.variance;
+    assert(y.approxEqual(54.76562 / 11));
+
+    // The naive algorithm is numerically unstable in this case
+    auto z0 = x.variance!"naive";
+    assert(!z0.approxEqual(y));
+
+    // But the two-pass algorithm provides a consistent answer
+    auto z1 = x.variance!"twoPass";
+    assert(z1.approxEqual(y));
+}
+
+/// Can also set algorithm or output type
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.ndslice.topology: repeat;
+    import mir.math.common: approxEqual;
+
+    //Set population variance, variance algorithm, sum algorithm or output type
+
+    auto a = [1.0, 1e100, 1, -1e100].sliced;
+    auto x = a * 10_000;
+
+    bool populationTrueRT = true;
+    bool populationFalseRT = false;
+    enum PopulationTrueCT = true;
+
+    /++
+    Due to Floating Point precision, when centering `x`, subtracting the mean 
+    from the second and fourth numbers has no effect. Further, after centering 
+    and squaring `x`, the first and third numbers in the slice have precision 
+    too low to be included in the centered sum of squares. 
+    +/
+    assert(x.variance(populationFalseRT).approxEqual(2.0e208 / 3));
+    assert(x.variance(populationTrueRT).approxEqual(2.0e208 / 4));
+    assert(x.variance(PopulationTrueCT).approxEqual(2.0e208 / 4));
+
+    assert(x.variance!("online").approxEqual(2.0e208 / 3));
+    assert(x.variance!("online", "kbn").approxEqual(2.0e208 / 3));
+    assert(x.variance!("online", "kb2").approxEqual(2.0e208 / 3));
+    assert(x.variance!("online", "precise").approxEqual(2.0e208 / 3));
+    assert(x.variance!(double, "online", "precise").approxEqual(2.0e208 / 3));
+    assert(x.variance!(double, "online", "precise")(populationTrueRT).approxEqual(2.0e208 / 4));
+
+    auto y = uint.max.repeat(3);
+    auto z = y.variance!ulong;
+    assert(z == 0.0);
+    static assert(is(typeof(z) == double));
+}
+
+/++
+For integral slices, pass output type as template parameter to ensure output
+type is correct. By default, if an input type is not floating point, then the
+result will be a double if it is implicitly convertible to a floating point type.
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0, 1, 1, 2, 4, 4,
+              2, 7, 5, 1, 2, 0].sliced;
+
+    auto y = x.variance;
+    assert(y.approxEqual(50.91667 / 11));
+    static assert(is(typeof(y) == double));
+
+    assert(x.variance!float.approxEqual(50.91667 / 11));
+}
+
+/++
+Variance works for complex numbers and other user-defined types (provided they
+can be converted to a floating point or complex type)
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
+    assert(x.variance.approxEqual((0.0+10.0i)/ 3));
+}
+
+/// Compute variance tensors along specified dimention of tensors
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+    import mir.ndslice.topology: as, iota, alongDim, map, repeat;
+
+    auto x = [
+        [0.0, 1, 2],
+        [3.0, 4, 5]
+    ].fuse;
+
+    assert(x.variance.approxEqual(17.5 / 5));
+
+    auto m0 = [4.5, 4.5, 4.5];
+    assert(x.alongDim!0.map!variance.all!approxEqual(m0));
+    assert(x.alongDim!(-2).map!variance.all!approxEqual(m0));
+
+    auto m1 = [1.0, 1.0];
+    assert(x.alongDim!1.map!variance.all!approxEqual(m1));
+    assert(x.alongDim!(-1).map!variance.all!approxEqual(m1));
+
+    assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!variance.all!approxEqual(repeat(3600.0 / 2, 3, 4, 5)));
+}
+
+/// Arbitrary variance
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    assert(variance(1.0, 2, 3) == 1.0);
+    assert(variance!float(1, 2, 3) == 1f);
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.math.common: approxEqual;
+    assert([1.0, 2, 3, 4].variance.approxEqual(5.0 / 3));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, alongDim, map;
+    import mir.math.common: approxEqual;
+    import mir.algorithm.iteration: all;
+
+    auto x = iota([2, 2], 1);
+    auto y = x.alongDim!1.map!variance;
+    assert(y.all!approxEqual([0.5, 0.5]));
+    static assert(is(meanType!(typeof(y)) == double));
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
+
+    assert(x.sliced.variance.approxEqual(54.76562 / 11));
+    assert(x.sliced.variance!float.approxEqual(54.76562 / 11));
+}
+
 /++
 A linear regression model with a single explanatory variable.
 +/

From cf723a99722cf2545b78b4d8bd64f6250a946b00 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 18 Jun 2020 07:43:54 -0400
Subject: [PATCH 006/573] Fix CircleCI error (#274)

---
 source/mir/math/stat.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index ff4467e6..02d9d0f0 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -2195,7 +2195,7 @@ template variance(
     /++
     Params:
         r = range, must be finite iterable
-        isPopulation: true if population variace, false if sample variance (default)
+        isPopulation = true if population variace, false if sample variance (default)
     +/
     @fmamath meanType!F variance(Range)(Range r, bool isPopulation = false)
         if (isIterable!Range)
@@ -2226,7 +2226,7 @@ template variance(
     /++
     Params:
         r = range, must be finite iterable
-        isPopulation: true if population variace, false if sample variance (default)
+        isPopulation = true if population variace, false if sample variance (default)
     +/
     @fmamath meanType!Range variance(Range)(Range r, bool isPopulation = false)
         if(isIterable!Range)

From c2934a1d8f67550fa9e964ad834323fe8fdc325a Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Fri, 19 Jun 2020 01:54:53 -0400
Subject: [PATCH 007/573] Add at/numStride/indexStride (#273)

* Add at/numStride/indexStride

* Re-use flattened in valStride

* Fixup at

* Address review

* Update docs
---
 source/mir/ndslice/slice.d | 184 +++++++++++++++++++++++++++++++++++++
 1 file changed, 184 insertions(+)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 6f830b70..f3759f09 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -2544,6 +2544,70 @@ public:
         assert(imm == x);
         assert(mut == x);
     }
+    
+    /++
+    Provides the index location of a slice, taking into account
+    `Slice._strides`. Similar to `Slice.indexStride`, except the slice is
+    indexed by a value. Called by `Slice.accessFlat`. 
+
+    Params:
+        n = location in slice
+    Returns:
+        location in slice, adjusted for `Slice._strides`
+    See_also:
+        $(SUBREF topology, flattened),
+        $(LREF Slice.indexStride),
+        $(LREF Slice.accessFlat)
+    +/
+    private
+    ptrdiff_t indexStrideValue(ptrdiff_t n) @safe scope const
+    {
+        assert(n < elementCount, "indexStrideValue: n must be less than elementCount");
+        assert(n >= 0, "indexStrideValue: n must be greater than or equal to zero");
+
+        static if (kind == Contiguous) {
+            return n;
+        } else {
+            ptrdiff_t _shift;
+            foreach_reverse (i; Iota!(1, N))
+            {
+                immutable v = n / ptrdiff_t(_lengths[i]);
+                n %= ptrdiff_t(_lengths[i]);
+                static if (i == S)
+                    _shift += n;
+                else
+                    _shift += n * _strides[i];
+                n = v;
+            }
+            _shift += n * _strides[0];
+            return _shift;
+        }
+    }
+
+    /++
+    Provides access to a slice as if it were `flattened`. 
+
+    Params:
+        index = location in slice
+    Returns:
+        value of flattened slice at `index`
+    See_also: $(SUBREF topology, flattened)
+    +/
+    auto ref accessFlat(size_t index) scope return @trusted
+    {
+        return _iterator[indexStrideValue(index)];
+    }
+
+    ///
+    version(mir_test)
+    @safe pure @nogc nothrow
+    unittest
+    {
+        import mir.ndslice.topology: iota, flattened;
+
+        auto x = iota(2, 3, 4);
+        assert(x.accessFlat(9) == x.flattened[9]);
+    }
 
     static if (isMutable!DeepElement)
     {
@@ -3840,3 +3904,123 @@ version(mir_test) @safe unittest
     b[] = a;
     assert(a == b);
 }
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened;
+
+    auto m = iota(2, 3, 4); // Contiguous Matrix
+    auto mFlat = m.flattened;
+
+    for (size_t i = 0; i < m.elementCount; i++) {
+        assert(m.accessFlat(i) == mFlat[i]);
+    }
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened;
+
+    auto m = iota(3, 4); // Contiguous Matrix
+    auto x = m.front; // Contiguous Vector
+
+    for (size_t i = 0; i < x.elementCount; i++) {
+        assert(x.accessFlat(i) == m[0, i]);
+    }
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened;
+
+    auto m = iota(3, 4); // Contiguous Matrix
+    auto x = m[0 .. $, 0 .. $ - 1]; // Canonical Matrix
+    auto xFlat = x.flattened;
+
+    for (size_t i = 0; i < x.elementCount; i++) {
+        assert(x.accessFlat(i) == xFlat[i]);
+    }
+}
+
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened;
+
+    auto m = iota(2, 3, 4); // Contiguous Matrix
+    auto x = m[0 .. $, 0 .. $, 0 .. $ - 1]; // Canonical Matrix
+    auto xFlat = x.flattened;
+
+    for (size_t i = 0; i < x.elementCount; i++) {
+        assert(x.accessFlat(i) == xFlat[i]);
+    }
+}
+
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened;
+    import mir.ndslice.dynamic: transposed;
+
+    auto m = iota(2, 3, 4); // Contiguous Matrix
+    auto x = m.transposed!(2, 1, 0); // Universal Matrix
+    auto xFlat = x.flattened;
+
+    for (size_t i = 0; i < x.elementCount; i++) {
+        assert(x.accessFlat(i) == xFlat[i]);
+    }
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened;
+    import mir.ndslice.dynamic: transposed;
+
+    auto m = iota(3, 4); // Contiguous Matrix
+    auto x = m.transposed; // Universal Matrix
+    auto xFlat = x.flattened;
+
+    for (size_t i = 0; i < x.elementCount; i++) {
+        assert(x.accessFlat(i) == xFlat[i]);
+    }
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened, diagonal;
+
+    auto m = iota(3, 4); // Contiguous Matrix
+    auto x = m.diagonal; // Universal Vector
+
+    for (size_t i = 0; i < x.elementCount; i++) {
+        assert(x.accessFlat(i) == m[i, i]);
+    }
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, flattened;
+
+    auto m = iota(3, 4); // Contiguous Matrix
+    auto x = m.front!1; // Universal Vector
+
+    for (size_t i = 0; i < x.elementCount; i++) {
+        assert(x.accessFlat(i) == m[i, 0]);
+    }
+}

From 929fb816e4436cff9aad33d86a8e2f14c5d85c83 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Fri, 19 Jun 2020 11:22:31 -0400
Subject: [PATCH 008/573] Add dispersion function (#275)

* Add dispersion function

* Make square public

* Adjust square function

* Remove square function
---
 source/mir/math/stat.d | 284 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 284 insertions(+)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 02d9d0f0..f557388d 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -23,6 +23,7 @@ import mir.ndslice.slice: Slice, SliceKind;
 import mir.math.common: fmamath;
 import mir.math.sum;
 import mir.primitives;
+import mir.functional: naryFun;
 import std.traits: isArray, isMutable, isIterable, isIntegral, CommonType;
 import mir.internal.utility: isFloatingPoint;
 
@@ -2510,6 +2511,289 @@ unittest
     assert(x.sliced.variance!float.approxEqual(54.76562 / 11));
 }
 
+/++
+Calculates the dispersion of the input.
+
+For an input `x`, this function first centers `x` by subtracting each `e` in `x`
+by the result of `centralTendency`, then it transforms the centered values using
+the function `transform`, and then finally summarizes that information using
+the `summarize` funcion. 
+
+The default functions provided are equivalent to calculating the population
+variance. The `centralTendency` default is the `mean` function, which results
+in the input being centered about the mean. The default `transform` function
+will square the centered values. The default `summarize` function is `mean`,
+which will return the mean of the squared centered values.
+
+Params:
+    centralTendency: function that will produce the value that the input is centered about, default is `mean`
+    transform: function to transform centered values, default squares the centered values
+    summarize: function to summarize the transformed centered values, default is `mean`
+
+Returns:
+    The dispersion of the input
++/
+template dispersion(
+    alias centralTendency = mean,
+    alias transform = naryFun!"a * a",
+    alias summarize = mean)
+{
+    import mir.ndslice.slice: Slice, SliceKind, sliced, hasAsSlice;
+
+    /++
+    Params:
+        slice
+    +/
+    @fmamath auto dispersion(Iterator, size_t N, SliceKind kind)(
+        Slice!(Iterator, N, kind) slice)
+    {
+        import core.lifetime: move;
+        import mir.ndslice.topology: map;
+
+        return summarize(slice.move.center!centralTendency.map!transform);
+    }
+    
+    /// ditto
+    @fmamath auto dispersion(T)(scope const T[] ar...)
+    {
+        return dispersion(ar.sliced);
+    }
+
+    /// ditto
+    @fmamath auto dispersion(T)(T withAsSlice)
+        if (hasAsSlice!T)
+    {
+        return dispersion(withAsSlice.asSlice);
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+    import mir.functional: naryFun;
+
+    assert(dispersion([1.0, 2, 3]).approxEqual(2.0 / 3));
+
+    assert(dispersion([1.0 + 3i, 2, 3]).approxEqual((-4.0 - 6i) / 3));
+
+    alias square = naryFun!"a * a";
+
+    assert(dispersion!(mean!float, square, mean!float)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 6));
+    
+    static assert(is(typeof(dispersion!(mean!float, square, mean!float)([1, 2, 3])) == float));
+}
+
+/// Dispersion of vector
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    assert(x.dispersion.approxEqual(54.76562 / 12));
+}
+
+/// Dispersion of matrix
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.math.common: approxEqual;
+
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    assert(x.dispersion.approxEqual(54.76562 / 12));
+}
+
+/// Column dispersion of matrix
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.topology: alongDim, byDim, map;
+    import mir.math.common: approxEqual;
+    import mir.algorithm.iteration: all;
+
+    auto x = [
+        [0.0,  1.0, 1.5, 2.0], 
+        [3.5, 4.25, 2.0, 7.5],
+        [5.0,  1.0, 1.5, 0.0]
+    ].fuse;
+    auto result = [13.16667 / 3, 7.041667 / 3, 0.1666667 / 3, 30.16667 / 3];
+
+    // Use byDim or alongDim with map to compute dispersion of row/column.
+    assert(x.byDim!1.map!dispersion.all!approxEqual(result));
+    assert(x.alongDim!0.map!dispersion.all!approxEqual(result));
+
+    // FIXME
+    // Without using map, computes the dispersion of the whole slice
+    // assert(x.byDim!1.dispersion == x.sliced.dispersion);
+    // assert(x.alongDim!0.dispersion == x.sliced.dispersion);
+}
+
+/// Can also set functions to change type of dispersion that is used
+version(mir_test)
+@safe
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual, fabs, sqrt;
+    import mir.functional: naryFun;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+              
+    alias square = naryFun!"a * a";
+
+    // Other population variance examples
+    assert(x.dispersion.approxEqual(54.76562 / 12));
+    assert(x.dispersion!mean.approxEqual(54.76562 / 12));
+    assert(x.dispersion!(mean, square).approxEqual(54.76562 / 12));
+    assert(x.dispersion!(mean, square, mean).approxEqual(54.76562 / 12));
+    
+    // Population standard deviation
+    assert(x.dispersion!(mean, square, mean).sqrt.approxEqual(sqrt(54.76562 / 12)));
+    
+    // Mean absolute deviation about the mean
+    assert(x.dispersion!(mean, fabs, mean).approxEqual(21.0 / 12));
+    //Mean absolute deviation about the median
+    assert(x.dispersion!(median, fabs, mean).approxEqual(19.25000 / 12));
+    //Median absolute deviation about the mean
+    assert(x.dispersion!(mean, fabs, median).approxEqual(1.43750));
+    //Median absolute deviation about the median
+    assert(x.dispersion!(median, fabs, median).approxEqual(1.25000));
+}
+
+/++
+For integral slices, pass output type to `centralTendency`, `transform`, and 
+`summary` functions as template parameter to ensure output type is correct.
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+    import mir.functional: naryFun;
+
+    auto x = [0, 1, 1, 2, 4, 4,
+              2, 7, 5, 1, 2, 0].sliced;
+
+    alias square = naryFun!"a * a";
+
+    auto y = x.dispersion;
+    assert(y.approxEqual(50.91667 / 12));
+    static assert(is(typeof(y) == double));
+
+    assert(x.dispersion!(mean!float, square, mean!float).approxEqual(50.91667 / 12));
+}
+
+/++
+Dispersion works for complex numbers and other user-defined types (provided that
+the `centralTendency`, `transform`, and `summary` functions are defined for those
+types)
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
+    assert(x.dispersion.approxEqual((0.0+10.0i)/ 4));
+}
+
+/// Compute mean tensors along specified dimention of tensors
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+    import mir.ndslice.topology: as, iota, alongDim, map, repeat;
+
+    auto x = [
+        [0.0, 1, 2],
+        [3.0, 4, 5]
+    ].fuse;
+
+    assert(x.dispersion.approxEqual(17.5 / 6));
+
+    auto m0 = [2.25, 2.25, 2.25];
+    assert(x.alongDim!0.map!dispersion.all!approxEqual(m0));
+    assert(x.alongDim!(-2).map!dispersion.all!approxEqual(m0));
+
+    auto m1 = [2.0 / 3, 2.0 / 3];
+    assert(x.alongDim!1.map!dispersion.all!approxEqual(m1));
+    assert(x.alongDim!(-1).map!dispersion.all!approxEqual(m1));
+
+    assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!dispersion.all!approxEqual(repeat(1800.0 / 2, 3, 4, 5)));
+}
+
+/// Arbitrary dispersion
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.functional: naryFun;
+
+    alias square = naryFun!"a * a";
+
+    assert(dispersion(1.0, 2, 3).approxEqual(2.0 / 3));
+    assert(dispersion!(mean!float, square, mean!float)(1, 2, 3).approxEqual(2f / 3));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.math.common: approxEqual;
+    assert([1.0, 2, 3, 4].dispersion.approxEqual(5.0 / 4));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, alongDim, map;
+    import mir.math.common: approxEqual;
+    import mir.algorithm.iteration: all;
+
+    auto x = iota([2, 2], 1);
+    auto y = x.alongDim!1.map!dispersion;
+    assert(y.all!approxEqual([0.25, 0.25]));
+    static assert(is(meanType!(typeof(y)) == double));
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
+
+    assert(x.sliced.dispersion.approxEqual(54.76562 / 12));
+}
+
 /++
 A linear regression model with a single explanatory variable.
 +/

From 369aaa84b71eaca79463ff49cb76cf60a15c1062 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sat, 20 Jun 2020 22:18:02 +0700
Subject: [PATCH 009/573] stat fixes (#277)

---
 source/mir/math/stat.d       | 73 ++++++++++++++++++------------------
 source/mir/ndslice/sorting.d |  2 +-
 2 files changed, 38 insertions(+), 37 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index f557388d..d6da76f0 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -23,7 +23,6 @@ import mir.ndslice.slice: Slice, SliceKind;
 import mir.math.common: fmamath;
 import mir.math.sum;
 import mir.primitives;
-import mir.functional: naryFun;
 import std.traits: isArray, isMutable, isIterable, isIntegral, CommonType;
 import mir.internal.utility: isFloatingPoint;
 
@@ -192,15 +191,15 @@ struct MeanAccumulator(T, Summation summation)
     Summator!(T, summation) summator;
 
     ///
-    F mean(F = T)() @property
+    F mean(F = T)() const @safe @property pure nothrow @nogc
     {
         return cast(F) summator.sum / cast(F) count;
     }
     
     ///
-    F sum(F = T)() @property
+    F sum(F = T)() const @safe @property pure nothrow @nogc
     {
-        return cast(F) summator.sum ;
+        return cast(F) summator.sum;
     }
 
     ///
@@ -1415,7 +1414,7 @@ unittest
 }
 
 version(mir_test)
-@safe
+@safe pure
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1832,8 +1831,6 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
 {
     import mir.functional: naryFun;
 
-    private alias square = naryFun!"a * a";
-
     mixin moment_ops!(T, summation);
     mixin outputRange_ops!T;
 
@@ -1854,7 +1851,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     void put()(T x)
     {
         meanAccumulator.put(x);
-        sumOfSquares.put(square(x));
+        sumOfSquares.put(x * x);
     }
 
     ///
@@ -1862,10 +1859,10 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     {
         if (isPopulation == false)
             return cast(F) sumOfSquares.sum / cast(F) (count - 1) - 
-                square(cast(F) meanAccumulator.mean) * (cast(F) count / cast(F) (count - 1));
+                (cast(F) meanAccumulator.mean) ^^ 2 * (cast(F) count / cast(F) (count - 1));
         else
             return cast(F) sumOfSquares.sum / cast(F) count - 
-                square(cast(F) meanAccumulator.mean);
+                (cast(F) meanAccumulator.mean) ^^ 2;
     }
 }
 
@@ -2535,36 +2532,42 @@ Returns:
 +/
 template dispersion(
     alias centralTendency = mean,
-    alias transform = naryFun!"a * a",
+    alias transform = "a * a",
     alias summarize = mean)
 {
+    import mir.functional: naryFun;
     import mir.ndslice.slice: Slice, SliceKind, sliced, hasAsSlice;
 
-    /++
-    Params:
-        slice
-    +/
-    @fmamath auto dispersion(Iterator, size_t N, SliceKind kind)(
-        Slice!(Iterator, N, kind) slice)
+    static if (__traits(isSame, naryFun!transform, transform))
     {
-        import core.lifetime: move;
-        import mir.ndslice.topology: map;
+        /++
+        Params:
+            slice
+        +/
+        @fmamath auto dispersion(Iterator, size_t N, SliceKind kind)(
+            Slice!(Iterator, N, kind) slice)
+        {
+            import core.lifetime: move;
+            import mir.ndslice.topology: map;
 
-        return summarize(slice.move.center!centralTendency.map!transform);
-    }
-    
-    /// ditto
-    @fmamath auto dispersion(T)(scope const T[] ar...)
-    {
-        return dispersion(ar.sliced);
-    }
+            return summarize(slice.move.center!centralTendency.map!transform);
+        }
+        
+        /// ditto
+        @fmamath auto dispersion(T)(scope const T[] ar...)
+        {
+            return dispersion(ar.sliced);
+        }
 
-    /// ditto
-    @fmamath auto dispersion(T)(T withAsSlice)
-        if (hasAsSlice!T)
-    {
-        return dispersion(withAsSlice.asSlice);
+        /// ditto
+        @fmamath auto dispersion(T)(T withAsSlice)
+            if (hasAsSlice!T)
+        {
+            return dispersion(withAsSlice.asSlice);
+        }
     }
+    else
+        alias dispersion = .dispersion!(centralTendency, naryFun!transform, summarize);
 }
 
 ///
@@ -2580,11 +2583,9 @@ unittest
 
     assert(dispersion([1.0 + 3i, 2, 3]).approxEqual((-4.0 - 6i) / 3));
 
-    alias square = naryFun!"a * a";
-
-    assert(dispersion!(mean!float, square, mean!float)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 6));
+    assert(dispersion!(mean!float, "a * a", mean!float)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 6));
     
-    static assert(is(typeof(dispersion!(mean!float, square, mean!float)([1, 2, 3])) == float));
+    static assert(is(typeof(dispersion!(mean!float, "a ^^ 2", mean!float)([1, 2, 3])) == float));
 }
 
 /// Dispersion of vector
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 1330635c..a2bb71e3 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -1154,7 +1154,7 @@ void partitionAtImpl(alias less, Iterator)(
     ref const(Iterator) frontI, 
     ref const(Iterator) lastI, 
     size_t n, 
-    bool useSampling)
+    bool useSampling) pure
 {
     assert(frontI <= lastI, "partitionAtImpl: frontI must be less than or equal to lastI");
 

From 27ef53af185eb214c8d2add76321d20e9587a89d Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 20 Jun 2020 21:38:29 -0400
Subject: [PATCH 010/573] Add standard deviation (#279)

* Add standardDeviation

* Update stats documentation
---
 source/mir/math/stat.d | 422 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 403 insertions(+), 19 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index d6da76f0..f993bffd 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -282,11 +282,15 @@ unittest
 /++
 Computes the mean of the input.
 
+By default, if `F` is not floating point type or complex type, then the result
+will have a `double` type if `F` is implicitly convertible to a floating point 
+type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+
 Params:
     F: controls type of output
     summation: algorithm for calculating sums (default: Summation.appropriate)
 Returns:
-    The mean of all the elements in the input, must be floating point or complex
+    The mean of all the elements in the input, must be floating point or complex type
 
 See_also: 
     $(SUBREF sum, Summation)
@@ -449,8 +453,7 @@ unittest
 
 /++
 For integral slices, pass output type as template parameter to ensure output
-type is correct. By default, if an input type is not floating point, then the
-result will be a double if it is implicitly convertible to a floating point type.
+type is correct.
 +/
 version(mir_test)
 @safe pure nothrow
@@ -599,11 +602,15 @@ unittest
 /++
 Computes the harmonic mean of the input.
 
+By default, if `F` is not floating point type or complex type, then the result
+will have a `double` type if `F` is implicitly convertible to a floating point 
+type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+
 Params:
     F: controls type of output
     summation: algorithm for calculating sums (default: Summation.appropriate)
 Returns:
-    harmonic mean of all the elements of the input, must be floating point
+    harmonic mean of all the elements of the input, must be floating point or complex type
 
 See_also: 
     $(SUBREF sum, Summation)
@@ -769,8 +776,7 @@ unittest
 
 /++
 For integral slices, pass output type as template parameter to ensure output
-type is correct. By default, if an input type is not floating point, then the
-result will be a double if it is implicitly convertible to a floating point type.
+type is correct. 
 +/
 version(mir_test)
 @safe pure nothrow
@@ -973,10 +979,13 @@ unittest
 /++
 Computes the geometric average of the input.
 
+By default, if `F` is not floating point type, then the result will have a 
+`double` type if `F` is implicitly convertible to a floating point type.
+
 Params:
     r = range, must be finite iterable
 Returns:
-    The geometric average of all the elements in the input, must be floating point
+    The geometric average of all the elements in the input, must be floating point type
 
 See_also: 
     $(SUBREF numeric, prod)
@@ -1101,8 +1110,7 @@ unittest
 
 /++
 For integral slices, pass output type as template parameter to ensure output
-type is correct. By default, if an input type is not floating point, then the
-result will be a double if it is implicitly convertible to a floating point type.
+type is correct.
 +/
 version(mir_test)
 @safe pure nothrow
@@ -1223,8 +1231,8 @@ unittest
 /++
 Computes the median of `slice`.
 
-Can also pass a a flag `allowModify` that allows the input slice to be modified.
-By default, a copy is made. 
+Can also pass a boolean variable, `allowModify`, that allows the input slice to
+be modified. By default, a reference-counted copy is made. 
 
 Returns:
     the median of the slice
@@ -1521,8 +1529,8 @@ unittest
 /++
 Centers `slice`, which must be a finite iterable.
 
-By default, `slice` is centered by the mean. A custom function may also be provided
-using `centralTendency`.
+By default, `slice` is centered by the mean. A custom function may also be
+provided using `centralTendency`.
 
 Returns:
     The elements in the slice with the average subtracted from them.
@@ -2177,13 +2185,17 @@ unittest
 /++
 Calculates the variance of the input
 
+By default, if `F` is not floating point type or complex type, then the result
+will have a `double` type if `F` is implicitly convertible to a floating point 
+type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+
 Params:
     F: controls type of output
     varianceAlgo: algorithm for calculating variance (default: VarianceAlgo.online)
     summation: algorithm for calculating sums (default: Summation.appropriate)
 
 Returns:
-    The variance of the input
+    The variance of the input, must be floating point or complex type
 +/
 template variance(
     F, 
@@ -2193,7 +2205,7 @@ template variance(
     /++
     Params:
         r = range, must be finite iterable
-        isPopulation = true if population variace, false if sample variance (default)
+        isPopulation = true if population variance, false if sample variance (default)
     +/
     @fmamath meanType!F variance(Range)(Range r, bool isPopulation = false)
         if (isIterable!Range)
@@ -2224,7 +2236,7 @@ template variance(
     /++
     Params:
         r = range, must be finite iterable
-        isPopulation = true if population variace, false if sample variance (default)
+        isPopulation = true if population variance, false if sample variance (default)
     +/
     @fmamath meanType!Range variance(Range)(Range r, bool isPopulation = false)
         if(isIterable!Range)
@@ -2400,8 +2412,7 @@ unittest
 
 /++
 For integral slices, pass output type as template parameter to ensure output
-type is correct. By default, if an input type is not floating point, then the
-result will be a double if it is implicitly convertible to a floating point type.
+type is correct.
 +/
 version(mir_test)
 @safe pure nothrow
@@ -2435,7 +2446,7 @@ unittest
     assert(x.variance.approxEqual((0.0+10.0i)/ 3));
 }
 
-/// Compute variance tensors along specified dimention of tensors
+/// Compute variance along specified dimention of tensors
 version(mir_test)
 @safe pure
 unittest
@@ -2508,6 +2519,379 @@ unittest
     assert(x.sliced.variance!float.approxEqual(54.76562 / 11));
 }
 
+package template stdevType(T)
+{
+    import mir.internal.utility: isFloatingPoint;
+    
+    alias U = meanType!T;
+
+    static if (isFloatingPoint!U) {
+        alias stdevType = U;
+    } else {
+        static assert(0, "stdevType: Can't calculate standard deviation of elements of type " ~ U.stringof);
+    }
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static assert(is(stdevType!(int[]) == double));
+    static assert(is(stdevType!(double[]) == double));
+    static assert(is(stdevType!(float[]) == float));
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
+        float x;
+        alias x this;
+    }
+
+    static assert(is(stdevType!(Foo[]) == float));
+}
+
+/++
+Calculates the standard deviation of the input
+
+By default, if `F` is not floating point type, then the result will have a
+`double` type if `F` is implicitly convertible to a floating point type.
+
+Params:
+    F: controls type of output
+    varianceAlgo: algorithm for calculating variance (default: VarianceAlgo.online)
+    summation: algorithm for calculating sums (default: Summation.appropriate)
+
+Returns:
+    The standard deviation of the input, must be floating point type type
++/
+template standardDeviation(
+    F, 
+    VarianceAlgo varianceAlgo = VarianceAlgo.online, 
+    Summation summation = Summation.appropriate)
+{
+    import mir.math.common: sqrt;
+
+    /++
+    Params:
+        r = range, must be finite iterable
+        isPopulation = true if population standard deviation, false if sample standard deviation (default)
+    +/
+    @fmamath stdevType!F standardDeviation(Range)(Range r, bool isPopulation = false)
+        if (isIterable!Range)
+    {
+        import core.lifetime: move;
+        alias G = typeof(return);
+        return r.move.variance!(G, varianceAlgo, ResolveSummationType!(summation, Range, G))(isPopulation).sqrt;
+    }
+
+    /++
+    Params:
+        ar = values
+    +/
+    @fmamath stdevType!F standardDeviation(scope const F[] ar...)
+    {
+        alias G = typeof(return);
+        return ar.variance!(G, varianceAlgo, ResolveSummationType!(summation, const(G)[], G)).sqrt;
+    }
+}
+
+/// ditto
+template standardDeviation(
+    VarianceAlgo varianceAlgo = VarianceAlgo.online, 
+    Summation summation = Summation.appropriate)
+{
+    /++
+    Params:
+        r = range, must be finite iterable
+        isPopulation = true if population standard deviation, false if sample standard deviation (default)
+    +/
+    @fmamath stdevType!Range standardDeviation(Range)(Range r, bool isPopulation = false)
+        if(isIterable!Range)
+    {
+        import core.lifetime: move;
+        alias F = typeof(return);
+        return .standardDeviation!(F, varianceAlgo, summation)(r.move, isPopulation);
+    }
+
+    /++
+    Params:
+        ar = values
+    +/
+    @fmamath stdevType!T standardDeviation(T)(scope const T[] ar...)
+    {
+        alias F = typeof(return);
+        return .standardDeviation!(F, varianceAlgo, summation)(ar);
+    }
+}
+
+/// ditto
+template standardDeviation(F, string varianceAlgo, string summation = "appropriate")
+{
+    mixin("alias standardDeviation = .standardDeviation!(F, VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
+}
+
+/// ditto
+template standardDeviation(string varianceAlgo, string summation = "appropriate")
+{
+    mixin("alias standardDeviation = .standardDeviation!(VarianceAlgo." ~ varianceAlgo ~ ", Summation." ~ summation ~ ");");
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual, sqrt;
+
+    assert(standardDeviation([1.0, 2, 3]).approxEqual(sqrt(2.0 / 2)));
+    assert(standardDeviation([1.0, 2, 3], true).approxEqual(sqrt(2.0 / 3)));
+    
+    assert(standardDeviation!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(sqrt(17.5 / 5)));
+    
+    static assert(is(typeof(standardDeviation!float([1, 2, 3])) == float));
+}
+
+/// Standard deviation of vector
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual, sqrt;
+
+    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    assert(x.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
+}
+
+/// Standard deviation of matrix
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.math.common: approxEqual, sqrt;
+
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    assert(x.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
+}
+
+/// Column standard deviation of matrix
+version(mir_test_variance)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.topology: alongDim, byDim, map;
+    import mir.math.common: approxEqual, sqrt;
+    import mir.algorithm.iteration: all;
+
+    auto x = [
+        [0.0,  1.0, 1.5, 2.0], 
+        [3.5, 4.25, 2.0, 7.5],
+        [5.0,  1.0, 1.5, 0.0]
+    ].fuse;
+    auto result = [13.16667 / 2, 7.041667 / 2, 0.1666667 / 2, 30.16667 / 2].map!sqrt;
+
+    // Use byDim or alongDim with map to compute standardDeviation of row/column.
+    assert(x.byDim!1.map!standardDeviation.all!approxEqual(result));
+    assert(x.alongDim!0.map!standardDeviation.all!approxEqual(result));
+
+    // FIXME
+    // Without using map, computes the standardDeviation of the whole slice
+    // assert(x.byDim!1.standardDeviation == x.sliced.standardDeviation);
+    // assert(x.alongDim!0.standardDeviation == x.sliced.standardDeviation);
+}
+
+/// Can also set algorithm type
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual, sqrt;
+
+    auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+
+    auto x = a + 1_000_000_000;
+
+    auto y = x.standardDeviation;
+    assert(y.approxEqual(sqrt(54.76562 / 11)));
+
+    // The naive algorithm is numerically unstable in this case
+    auto z0 = x.standardDeviation!"naive";
+    assert(!z0.approxEqual(y));
+
+    // But the two-pass algorithm provides a consistent answer
+    auto z1 = x.standardDeviation!"twoPass";
+    assert(z1.approxEqual(y));
+}
+
+/// Can also set algorithm or output type
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.ndslice.topology: repeat;
+    import mir.math.common: approxEqual, sqrt;
+
+    //Set population standard deviation, standardDeviation algorithm, sum algorithm or output type
+
+    auto a = [1.0, 1e100, 1, -1e100].sliced;
+    auto x = a * 10_000;
+
+    bool populationTrueRT = true;
+    bool populationFalseRT = false;
+    enum PopulationTrueCT = true;
+
+    /++
+    Due to Floating Point precision, when centering `x`, subtracting the mean 
+    from the second and fourth numbers has no effect. Further, after centering 
+    and squaring `x`, the first and third numbers in the slice have precision 
+    too low to be included in the centered sum of squares. 
+    +/
+    assert(x.standardDeviation(populationFalseRT).approxEqual(sqrt(2.0e208 / 3)));
+    assert(x.standardDeviation(populationTrueRT).approxEqual(sqrt(2.0e208 / 4)));
+    assert(x.standardDeviation(PopulationTrueCT).approxEqual(sqrt(2.0e208 / 4)));
+
+    assert(x.standardDeviation!("online").approxEqual(sqrt(2.0e208 / 3)));
+    assert(x.standardDeviation!("online", "kbn").approxEqual(sqrt(2.0e208 / 3)));
+    assert(x.standardDeviation!("online", "kb2").approxEqual(sqrt(2.0e208 / 3)));
+    assert(x.standardDeviation!("online", "precise").approxEqual(sqrt(2.0e208 / 3)));
+    assert(x.standardDeviation!(double, "online", "precise").approxEqual(sqrt(2.0e208 / 3)));
+    assert(x.standardDeviation!(double, "online", "precise")(populationTrueRT).approxEqual(sqrt(2.0e208 / 4)));
+
+    auto y = uint.max.repeat(3);
+    auto z = y.standardDeviation!ulong;
+    assert(z == 0.0);
+    static assert(is(typeof(z) == double));
+}
+
+/++
+For integral slices, pass output type as template parameter to ensure output
+type is correct.
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual, sqrt;
+
+    auto x = [0, 1, 1, 2, 4, 4,
+              2, 7, 5, 1, 2, 0].sliced;
+
+    auto y = x.standardDeviation;
+    assert(y.approxEqual(sqrt(50.91667 / 11)));
+    static assert(is(typeof(y) == double));
+
+    assert(x.standardDeviation!float.approxEqual(sqrt(50.91667 / 11)));
+}
+
+/++
+Variance works for other user-defined types (provided they
+can be converted to a floating point)
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    
+    static struct Foo {
+        float x;
+        alias x this;
+    }
+    
+    Foo[] foo = [Foo(1f), Foo(2f), Foo(3f)];
+    assert(foo.standardDeviation == 1f);
+}
+
+/// Compute standard deviation along specified dimention of tensors
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.topology: as, iota, alongDim, map, repeat;
+
+    auto x = [
+        [0.0, 1, 2],
+        [3.0, 4, 5]
+    ].fuse;
+
+    assert(x.standardDeviation.approxEqual(sqrt(17.5 / 5)));
+
+    auto m0 = repeat(sqrt(4.5), 3);
+    assert(x.alongDim!0.map!standardDeviation.all!approxEqual(m0));
+    assert(x.alongDim!(-2).map!standardDeviation.all!approxEqual(m0));
+
+    auto m1 = [1.0, 1.0];
+    assert(x.alongDim!1.map!standardDeviation.all!approxEqual(m1));
+    assert(x.alongDim!(-1).map!standardDeviation.all!approxEqual(m1));
+
+    assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!standardDeviation.all!approxEqual(repeat(sqrt(3600.0 / 2), 3, 4, 5)));
+}
+
+/// Arbitrary standard deviation
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.math.common: sqrt;
+    assert(standardDeviation(1.0, 2, 3) == 1.0);
+    assert(standardDeviation!float(1, 2, 3) == 1f);
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.math.common: approxEqual, sqrt;
+    assert([1.0, 2, 3, 4].standardDeviation.approxEqual(sqrt(5.0 / 3)));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.topology: iota, alongDim, map;
+    import mir.math.common: approxEqual, sqrt;
+    import mir.algorithm.iteration: all;
+
+    auto x = iota([2, 2], 1);
+    auto y = x.alongDim!1.map!standardDeviation;
+    assert(y.all!approxEqual([sqrt(0.5), sqrt(0.5)]));
+    static assert(is(meanType!(typeof(y)) == double));
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual, sqrt;
+
+    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
+
+    assert(x.sliced.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
+    assert(x.sliced.standardDeviation!float.approxEqual(sqrt(54.76562 / 11)));
+}
+
 /++
 Calculates the dispersion of the input.
 

From a158d706d417c3bb04f4261b094e6f4c5277ba09 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sun, 21 Jun 2020 18:07:04 +0700
Subject: [PATCH 011/573] add bignum package (#278)

* add bignum package

* add bignum package

* fix docs

* fixup

* fix sdl

* fixup

* fixup docs

* use lowmem

* fixup docs

* fixup
---
 dub.sdl                                    |    9 +-
 meson.build                                |    6 +
 source/mir/bignum/decimal.d                |  221 ++
 source/mir/bignum/fixed.d                  |  640 ++++++
 source/mir/bignum/fp.d                     |  319 +++
 source/mir/bignum/integer.d                |  475 +++++
 source/mir/bignum/internal/dec2flt_table.d | 2066 ++++++++++++++++++
 source/mir/bignum/low_level_view.d         | 2198 ++++++++++++++++++++
 source/mir/ndslice/iterator.d              |   16 +-
 source/mir/ndslice/slice.d                 |    2 +-
 source/mir/ndslice/sorting.d               |    1 -
 11 files changed, 5941 insertions(+), 12 deletions(-)
 create mode 100644 source/mir/bignum/decimal.d
 create mode 100644 source/mir/bignum/fixed.d
 create mode 100644 source/mir/bignum/fp.d
 create mode 100644 source/mir/bignum/integer.d
 create mode 100644 source/mir/bignum/internal/dec2flt_table.d
 create mode 100644 source/mir/bignum/low_level_view.d

diff --git a/dub.sdl b/dub.sdl
index 43a8208b..6a41685f 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,15 +9,18 @@ dependency "mir-core" version=">=0.3.5 <1.2.0"
 
 buildType "unittest" {
 	buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_test"
+    versions "mir_test" "mir_bignum_test"
+    dflags "-lowmem"
 }
 buildType "unittest-cov" {
 	buildOptions "unittests" "coverage" "debugMode" "debugInfo"
-	versions "mir_test"
+	versions "mir_test"  "mir_bignum_test"
+    dflags "-lowmem"
 }
 buildType "unittest-release" {
 	buildOptions "unittests" "releaseMode" "optimize" "inline" "noBoundsCheck"
-	versions "mir_test"
+	versions "mir_test"  "mir_bignum_test"
+    dflags "-lowmem"
 }
 
 configuration "default" {
diff --git a/meson.build b/meson.build
index fbdb060a..ffb05869 100644
--- a/meson.build
+++ b/meson.build
@@ -12,6 +12,12 @@ sources_list = [
     'mir/algorithm/setops',
     'mir/appender',
     'mir/array/allocation',
+    'mir/bignum/decimal',
+    'mir/bignum/fixed',
+    'mir/bignum/fp',
+    'mir/bignum/integer',
+    'mir/bignum/internal/dec2flt_table',
+    'mir/bignum/low_level_view',
     'mir/combinatorics/package',
     'mir/container/binaryheap',
     'mir/cpp_export/numeric',
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
new file mode 100644
index 00000000..2e7594c5
--- /dev/null
+++ b/source/mir/bignum/decimal.d
@@ -0,0 +1,221 @@
+/++
+Note:
+    The module doesn't provide full arithmetic API for now.
++/
+module mir.bignum.decimal;
+
+import std.traits: isSomeChar;
+
+/++
+Stack-allocated decimal.
+Params:
+    maxSize64 = count of 64bit words in coefficient
++/
+struct Decimal(size_t maxSize64)
+    if (maxSize64 && maxSize64 <= ushort.max)
+{
+    import mir.bignum.integer;
+    import mir.bignum.low_level_view;
+
+    ///
+    int exponent;
+    ///
+    BigInt!maxSize64 coefficient;
+
+    ///
+    DecimalView!size_t view()
+    {
+        return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
+    }
+}
+
+/++
+Returns: false in case of integer overflow or incorrect input.
++/
+@trusted @nogc pure nothrow
+bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
+    if (isSomeChar!C)
+{
+    import mir.bignum.low_level_view: MaxWordPow10;
+    import mir.utility: _expect;
+    decimal.exponent = 0;
+    decimal.coefficient.sign = false;
+    decimal.coefficient.length = 0;
+
+    if (_expect(str.length == 0, false))
+        return false;
+    
+
+
+    if (str[0] == '-')
+    {
+        decimal.coefficient.sign = true;
+        str = str[1 .. $];
+        if (_expect(str.length == 0, false))
+            return false;
+    }
+    else
+    if (_expect(str[0] == '+', false))
+    {
+        str = str[1 .. $];
+        if (_expect(str.length == 0, false))
+            return false;
+    }
+
+    uint d = str[0] - '0';
+    str = str[1 .. $];
+
+    size_t v;
+    size_t t = 1;
+    uint afterDot;
+    bool dot;
+    bool hasExponent;
+
+    if (d == 0)
+    {
+        if (str.length == 0)
+            return true;
+        if (str[0] == '0')
+            return false;
+        goto S;
+    }
+    else
+    if (d < 10)
+    {
+        goto S;
+    }
+    else
+    if (d == '.' - '0')
+    {
+        goto D;
+    }
+    else
+    {
+        return false;
+    }
+
+    for(;;)
+    {
+        import std.stdio;
+        enum mp10 = size_t(10) ^^ MaxWordPow10!size_t;
+        d = str[0] - '0';
+        str = str[1 .. $];
+
+        if (_expect(d <= 10, true))
+        {
+            v *= 10;
+    S:
+            t *= 10;
+            v += d;
+            afterDot += dot;
+
+            if (_expect(t == mp10 || str.length == 0, false))
+            {
+            L:
+                auto overflow = decimal.coefficient.opOpAssign!"*"(t, v);
+                if (_expect(overflow, 0))
+                    return false;
+                v = 0;
+                t = 1;
+                if (str.length == 0)
+                {
+                    decimal.exponent -= afterDot;
+                    return true;
+                }
+            }
+
+            continue;
+        }
+        else
+        if (key != d) switch (d)
+        {
+            D:
+            case DecimalExponentKey.dot:
+                key = cast(DecimalExponentKey)d;
+                if (_expect(!dot, true))
+                {
+                    dot = true;
+                    if (str.length)
+                        continue;
+                }
+                break;
+            case DecimalExponentKey.e:
+            case DecimalExponentKey.d:
+            case DecimalExponentKey.E:
+            case DecimalExponentKey.D:
+                key = cast(DecimalExponentKey)d;
+                hasExponent = true;
+                import mir.parse: parse;
+                if (parse(str, decimal.exponent) && str.length == 0)
+                {
+                    if (t != 1)
+                       goto L;
+                    return true;
+                }
+                break;
+            default:
+        }
+        break;
+    }
+    return false;
+}
+
+///
+enum DecimalExponentKey
+{
+    ///
+    none = 0,
+    ///
+    dot = '.' - '0',
+    ///
+    d = 'd' - '0',
+    ///
+    e = 'e' - '0',
+    ///
+    D = 'D' - '0',
+    ///
+    E = 'E' - '0',
+}
+
+///
+version(mir_bignum_test) 
+@safe pure nothrow @nogc
+unittest
+{
+    Decimal!3 decimal;
+    int exponent;
+    DecimalExponentKey key;
+    import std.conv;
+
+    assert("1.334".parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == 1.334);
+    assert(key == DecimalExponentKey.dot);
+
+    assert("+0.334e-5"w.parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == 0.334e-5);
+    assert(key == DecimalExponentKey.e);
+
+    assert("-334D-5"d.parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == -334e-5);
+    assert(key == DecimalExponentKey.D);
+
+    assert("2482734692817364218734682973648217364981273648923423".parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == 2482734692817364218734682973648217364981273648923423.0);
+    assert(key == DecimalExponentKey.none);
+
+    assert(".023".parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == .023);
+    assert(key == DecimalExponentKey.dot);
+
+    assert("0E100".parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == 0);
+    assert(key == DecimalExponentKey.E);
+
+    assert(!"3.3.4".parseDecimal(decimal, key));
+    assert(!"3.4.".parseDecimal(decimal, key));
+    assert(!"4.".parseDecimal(decimal, key));
+    assert(!".".parseDecimal(decimal, key));
+    assert(!"0.".parseDecimal(decimal, key));
+    assert(!"00".parseDecimal(decimal, key));
+    assert(!"0d".parseDecimal(decimal, key));
+}
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
new file mode 100644
index 00000000..b0b051d7
--- /dev/null
+++ b/source/mir/bignum/fixed.d
@@ -0,0 +1,640 @@
+/++
+Note:
+    The module doesn't provide full arithmetic API for now.
++/
+module mir.bignum.fixed;
+
+import std.traits;
+import mir.bitop;
+import mir.utility;
+
+/++
+Fixed-length unsigned integer.
+
+Params:
+    size = size in bits
++/
+struct UInt(size_t size)
+    if (size % (size_t.sizeof * 8) == 0 && size >= (size_t.sizeof * 8))
+{
+    import mir.bignum.fixed: UInt;
+    /++
+    Payload. The data is located in the target endianness.
+    +/
+    size_t[size / (size_t.sizeof * 8)] data;
+
+    ///
+    this(size_t N)(auto ref size_t[N] data)
+        if (N <= this.data.length)
+    {
+        version(LittleEndian)
+            this.data[0 .. N] = data;
+        else
+            this.data[$ - N .. $] = data;
+    }
+
+    static if (size >= 64)
+    ///
+    this(ulong data)
+    {
+        auto d = view.leastSignificantFirst;
+        static if (size_t.sizeof == ulong.sizeof)
+        {
+            d.front = data;
+        }
+        else
+        {
+            d.front = cast(uint) data;
+            d[1] = cast(uint) (data >> 32);
+        }
+    }
+
+    static if (size < 64)
+    ///
+    this(uint data)
+    {
+        view.leastSignificant = data;
+    }
+
+    ///
+    enum UInt!size max = ((){UInt!size ret; ret.data = size_t.max; return ret;})();
+
+    ///
+    enum UInt!size min = UInt!size.init;
+
+    import mir.bignum.low_level_view: BigUIntView;
+
+    ///
+    BigUIntView!size_t view()() @property pure nothrow @nogc scope @safe
+    {
+        return BigUIntView!size_t(data);
+    }
+
+    ///
+    BigUIntView!(const size_t) view()() const @property pure nothrow @nogc scope @safe
+    {
+        return BigUIntView!(const size_t)(data);
+    }
+
+    ///
+    static UInt!size fromHexString(scope const(char)[] str)
+    {
+        typeof(return) ret;
+        ret.view.fromHexStringImpl(str);
+        return ret;
+    }
+
+    /++
+    +/
+    auto opCmp(UInt!size rhs)
+    {
+        import mir.algorithm.iteration: cmp;
+        return cmp(this.view.mostSignificantFirst, rhs.view.mostSignificantFirst);
+    }
+
+    /++
+    `bool overflow = a += b ` and `bool overflow = a -= b` operations.
+    +/
+    bool opOpAssign(string op)(UInt!size rhs, bool overflow = false)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return view.opOpAssign!op(rhs.view, overflow);
+    }
+
+    /// ditto
+    bool opOpAssign(string op)(size_t rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return view.opOpAssign!op(rhs);
+    }
+
+    /// ditto
+    bool opOpAssign(string op, size_t rsize)(UInt!rsize rhs, bool overflow = false)
+        @safe pure nothrow @nogc
+        if ((op == "+" || op == "-") && rsize < size)
+    {
+        return opOpAssign!op(rhs.toSize!size);
+    }
+
+    ///
+    ref UInt!size opOpAssign(string op)(UInt!size rhs) nothrow return
+        if (op == "^" || op == "|" || op == "&")
+    {
+        static foreach (i; 0 .. data.length)
+            mixin(`data[i] ` ~ op ~ `= rhs.data[i];`);
+        return this;
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
+        auto b = UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314");
+        assert((a.opBinary!"|"(b)) == UInt!128.fromHexString("ffbffbeffd1affaf75adfff5abf0a39d"));
+    }
+
+    ///
+    ref UInt!size opOpAssign(string op)(size_t shift)
+        @safe pure nothrow @nogc return
+        if (op == "<<" || op == ">>")
+    {
+        auto d = view.leastSignificantFirst;
+        assert(shift < size);
+        auto index = shift / (size_t.sizeof * 8);
+        auto bs = shift % (size_t.sizeof * 8);
+        auto ss = size_t.sizeof * 8 - bs;
+        static if (op == ">>")
+        {
+            if (bs)
+            {
+                foreach (j; 0 .. data.length - (index + 1))
+                {
+                    d[j] = (d[j + index] >>> bs) | (d[j + (index + 1)] << ss);
+                }
+            }
+            else
+            {
+                foreach (j; 0 .. data.length - (index + 1))
+                {
+                    d[j] = d[j + index];
+                }
+            }
+            d[$ - (index + 1)] = d.back >>> bs;
+            foreach (j; data.length - index .. data.length)
+            {
+                d[j] = 0;
+            }
+        }
+        else
+        {
+            if (bs)
+            {
+                foreach_reverse (j; index + 1 .. data.length)
+                {
+                    d[j] = (d[j - index] << bs) | (d[j - (index + 1)] >> ss);
+                }
+            }
+            else
+            {
+                foreach_reverse (j; index + 1 .. data.length)
+                {
+                    d[j] = d[j - index];
+                }
+            }
+            d[index] = d.front << bs;
+            foreach_reverse (j; 0 .. index)
+            {
+                d[j] = 0;
+            }
+        }
+        return this;
+    }
+
+    /++
+    `auto c = a << b` operation.
+    +/
+    UInt!size opBinary(string op)(size_t rhs)
+        const @safe pure nothrow @nogc
+        if (op == "<<" || op == ">>>" || op == ">>")
+    {
+        UInt!size ret = this;
+        ret.opOpAssign!op(rhs);
+        return ret;
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(a << 0 == a);
+        assert(a << 4 == UInt!128.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
+        assert(a << 68 == UInt!128.fromHexString("4a1de7022b0029d00000000000000000"));
+        assert(a << 127 == UInt!128.fromHexString("80000000000000000000000000000000"));
+        assert(a >> 0 == a);
+        assert(a >> 4 == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
+        assert(a >> 68 == UInt!128.fromHexString("afbbfae3cd0aff2"));
+        assert(a >> 127 == UInt!128(1));
+    }
+
+    /++
+    `auto c = a << b`, and `^`, `|`, `&` operations.
+    +/
+    UInt!size opBinary(string op)(UInt!size rhs)
+        const @safe pure nothrow @nogc
+        if (op == "^" || op == "|" || op == "&")
+    {
+        UInt!size ret = this;
+        ret.opOpAssign!op(rhs);
+        return ret;
+    }
+
+    /++
+    `auto c = a + b` and `auto c = a - b` operations.
+    +/
+    UInt!size opBinary(string op, size_t rsize)(UInt!rsize rhs)
+        const @safe pure nothrow @nogc
+        if ((op == "+" || op == "-") && rsize <= size)
+    {
+        UInt!size ret = this;
+        ret.opOpAssign!op(rhs);
+        return ret;
+    }
+
+    /++
+    Shifts left using at most `size_t.sizeof * 8 - 1` bits
+    +/
+    UInt!size smallLeftShift()(uint shift) const
+    {
+        assert(shift < size_t.sizeof * 8);
+        UInt!size ret = this;
+        if (shift)
+        {
+            auto csh = size_t.sizeof * 8 - shift;
+            version (LittleEndian)
+            {
+                static foreach_reverse (i; 1 .. data.length)
+                {
+                    ret.data[i] = (ret.data[i] << shift) | (ret.data[i - 1] >>> csh);
+                }
+                ret.data[0] = ret.data[0] << shift;
+            }
+            else
+            {
+                static foreach (i; 0 .. data.length - 1)
+                {
+                    ret.data[i] = (ret.data[i] << shift) | (ret.data[i + 1] >>> csh);
+                }
+                ret.data[$ - 1] = ret.data[$ - 1] << shift;
+            }
+        }
+        return ret;
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(a.smallLeftShift(4) == UInt!128.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
+    }
+
+    /++
+    Shifts right using at most `size_t.sizeof * 8 - 1` bits
+    +/
+    UInt!size smallRightShift()(uint shift) const
+    {
+        assert(shift < size_t.sizeof * 8);
+        UInt!size ret = this;
+        if (shift)
+        {
+            auto csh = size_t.sizeof * 8 - shift;
+            version (LittleEndian)
+            {
+                static foreach (i; 0 .. data.length - 1)
+                {
+                    ret.data[i] = (ret.data[i] >>> shift) | (ret.data[i + 1] << csh);
+                }
+                ret.data[$ - 1] = ret.data[$ - 1] >>> shift;
+            }
+            else
+            {
+                static foreach_reverse (i; 1 .. data.length)
+                {
+                    ret.data[i] = (ret.data[i] >>> shift) | (ret.data[i - 1] << csh);
+                }
+                ret.data[0] = ret.data[0] >>> shift;
+            }
+        }
+        return ret;
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(a.smallRightShift(4) == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
+    }
+
+    /++
+    +/
+    T opCast(T)() const
+        if (is(Unqual!T == bool))
+    {
+        static foreach (i; 0 .. data.length)
+        {
+            if (data[i])
+                return true;
+        }
+        return false;
+    }
+
+    /++
+    +/
+    T opCast(T)() const
+        if (is(Unqual!T == ulong))
+    {
+        auto d = view.leastSignificantFirst;
+        static if (size_t.sizeof == ulong.sizeof)
+        {
+            return d.front;
+        }
+        else
+        {
+            return d.front | (ulong(d[1]) << 32);
+        }
+    }
+
+    /++
+    +/
+    T opCast(T)() const
+        if (is(Unqual!T == uint))
+    {
+        auto d = view.leastSignificantFirst;
+        return cast(uint) d.front;
+    }
+
+    /++
+    Returns:
+        the number with shrinked or extended size.
+    +/
+    UInt!newSize toSize(size_t newSize, bool lowerBits = true)()
+        const @safe pure @nogc nothrow
+    {
+        typeof(return) ret;
+        import mir.utility: min;
+        enum N = min(ret.data.length, data.length);
+        static if (lowerBits)
+        {
+            version (LittleEndian)
+                ret.data[0 .. N] = data[0 .. N];
+            else
+                ret.data[$ - N .. $] = data[$ - N .. $];
+        }
+        else
+        {
+            version (LittleEndian)
+                ret.data[0 .. N] = data[$ - N .. $];
+            else
+                ret.data[$ - N .. $] = data[0 .. N];
+        }
+        return ret;
+    }
+
+    ///
+    UInt!(size + additionalRightBits) rightExtend(size_t additionalRightBits)()
+        const @safe pure @nogc nothrow
+        if (additionalRightBits)
+    {
+        typeof(return) ret;
+        version (BigEndian)
+            ret.data[0 .. data.length] = data;
+        else
+            ret.data[$ - data.length .. $] = data;
+        return ret;
+    }
+
+    /++
+    +/
+    bool bt()(size_t position) const
+        @safe pure nothrow @nogc
+    {
+        assert(position < data.sizeof * 8);
+        return view.bt(position);
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(a.bt(127) == 1);
+        assert(a.bt(126) == 0);
+        assert(a.bt(125) == 1);
+        assert(a.bt(124) == 0);
+        assert(a.bt(0) == 1);
+        assert(a.bt(1) == 0);
+        assert(a.bt(2) == 1);
+        assert(a.bt(3) == 1);
+    }
+
+    /++
+    +/
+    size_t ctlz()() const @property
+        @safe pure nothrow @nogc
+    {
+        return view.ctlz;
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
+        assert (a.ctlz == 0);
+        a = UInt!128.init;
+        assert (a.ctlz == 128);
+        a = UInt!128.fromHexString("3");
+        assert (a.ctlz == 126);
+    }
+
+    /++
+    +/
+    size_t cttz()() const @property
+        @safe pure nothrow @nogc
+    {
+        return view.cttz;
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("d");
+        assert (a.cttz == 0);
+        a = UInt!128.init;
+        assert (a.cttz == 128);
+        a = UInt!128.fromHexString("300000000000000000");
+        assert (a.cttz == 68);
+    }
+
+    /++
+    +/
+    bool signBit()() const @property
+    {
+        version (LittleEndian)
+            return data[$ - 1] >> (size_t.sizeof * 8 - 1);
+        else
+            return data[0] >> (size_t.sizeof * 8 - 1);
+    }
+
+    /// ditto
+    void signBit()(bool value) @property
+    {
+        enum signMask = ptrdiff_t.max;
+        version (LittleEndian)
+            data[$ - 1] = (data[$ - 1] & ptrdiff_t.max) | (size_t(value) << (size_t.sizeof * 8 - 1));
+        else
+            data[    0] = (data[    0] & ptrdiff_t.max) | (size_t(value) << (size_t.sizeof * 8 - 1));
+    }
+
+    ///
+    version(mir_bignum_test)
+    unittest
+    {
+        auto a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
+        assert(a.signBit);
+        a.signBit = false;
+        assert(a == UInt!128.fromHexString("5fbbfae3cd0aff2714a1de7022b0029d"));
+        assert(!a.signBit);
+        a.signBit = true;
+        assert(a == UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"));
+    }
+}
+
+/++
++/
+UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
+{
+    UInt!(sizeA + sizeB) ret;
+    enum al = a.data.length;
+    enum alp1 = a.data.length + 1;
+    version (LittleEndian)
+    {
+        ret.data[0 .. alp1] = extendedMul(a, b.data[0]).data;
+        static foreach ( i; 1 .. b.data.length)
+            ret.data[i .. i + alp1] = extendedMulAdd(a, b.data[i], UInt!sizeA(ret.data[i .. i + al])).data;
+    }
+    else
+    {
+        ret.data[$ - alp1 .. $] = extendedMul(a, b.data[$ - 1]).data;
+        static foreach_reverse ( i; 0 .. b.data.length - 1)
+            ret.data[i .. i + alp1] = extendedMulAdd(a, b.data[i], UInt!sizeA(ret.data[i .. i + al])).data;
+    }
+    return ret;
+}
+
+/// ditto
+UInt!(size + size_t.sizeof * 8)
+    extendedMul(size_t size)(UInt!size a, size_t b)
+{
+    size_t overflow = a.view *= b;
+    auto ret = a.toSize!(size + size_t.sizeof * 8);
+    ret.view.mostSignificant = overflow;
+    return ret;
+}
+
+/// ditto
+UInt!128 extendedMul()(ulong a, ulong b)
+{
+    static if (size_t.sizeof == ulong.sizeof)
+    {
+        import mir.utility: extMul;
+        auto e = extMul(a, b);
+        version(LittleEndian)
+            return typeof(return)([e.low, e.high]);
+        else
+            return typeof(return)([e.high, e.low]);
+    }
+    else
+    {
+        return extendedMul(UInt!64(a), UInt!64(b));
+    }
+}
+
+/// ditto
+UInt!64 extendedMul()(uint a, uint b)
+{
+    static if (size_t.sizeof == uint.sizeof)
+    {
+        import mir.utility: extMul;
+        auto e = extMul(a, b);
+        version(LittleEndian)
+            return typeof(return)([e.low, e.high]);
+        else
+            return typeof(return)([e.high, e.low]);
+    }
+    else
+    {
+        return typeof(return)([ulong(a) * b]);
+    }
+}
+
+///
+version(mir_bignum_test)
+version(mir_bignum_test)
+@safe pure @nogc
+unittest
+{
+    auto a = UInt!128.max;
+    auto b = UInt!256.max;
+    auto c = UInt!384.max;
+    assert(extendedMul(a, a) == UInt!256.max - UInt!128.max - UInt!128.max);
+    assert(extendedMul(a, b) == UInt!384.max - UInt!128.max - UInt!256.max);
+    assert(extendedMul(b, a) == UInt!384.max - UInt!128.max - UInt!256.max);
+
+    a = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
+    b = UInt!256.fromHexString("3fe48f2dc8aad570d037bc9b323fc0cfa312fcc2f63cb521bd8a4ca6157ef619");
+    c = UInt!384.fromHexString("37d7034b86e8d58a9fc564463fcedef9e2ad1126dd2c0f803e61c72852a9917ef74fa749e7936a9e4e224aeeaff91f55");
+    assert(extendedMul(a, b) == c);
+    assert(extendedMul(b, a) == c);
+
+    a = UInt!128.fromHexString("23edf5ff44ee3a4feafc652607aa1eb9");
+    b = UInt!256.fromHexString("d3d79144b8941fb50c9102e3251bacb112c88b71ad3f85a970a31458ce24297b");
+    c = UInt!384.fromHexString("1dbb62fe6ca5fed101068eda7222d6a9857633ecdfed37a2d156ff6309065ecc633f31465727677a93a7acbd1dac63e3");
+    assert(extendedMul(a, b) == c);
+    assert(extendedMul(b, a) == c);
+}
+
+/// ulong
+version(mir_bignum_test)
+@safe pure @nogc
+unittest
+{
+    ulong a = 0xdfbbfae3cd0aff27;
+    ulong b = 0x14a1de7022b0029d;
+    auto c = UInt!128.fromHexString("120827399968ea2a2db185d16e8cc8eb");
+    assert(extendedMul(a, b) == c);
+    assert(extendedMul(b, a) == c);
+}
+
+/// uint
+version(mir_bignum_test)
+@safe pure @nogc
+unittest
+{
+    uint a = 0xdfbbfae3;
+    uint b = 0xcd0aff27;
+    auto c = UInt!64.fromHexString("b333243de8695595");
+    assert(extendedMul(a, b) == c);
+    assert(extendedMul(b, a) == c);
+}
+
+/++
++/
+UInt!(size + size_t.sizeof * 8)
+    extendedMulAdd(size_t size)(UInt!size a, size_t b, UInt!size c)
+{
+    auto ret = extendedMul(a, b);
+    auto view = ret.view;
+    view.mostSignificant += view.topLeastSignificantPart(a.data.length) += c.view;
+    return ret;
+}
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
new file mode 100644
index 00000000..bd3e4821
--- /dev/null
+++ b/source/mir/bignum/fp.d
@@ -0,0 +1,319 @@
+/++
+Note:
+    The module doesn't provide full arithmetic API for now.
++/
+module mir.bignum.fp;
+
+import std.traits;
+import mir.bitop;
+import mir.utility;
+
+package enum half(size_t hs) = (){
+    import mir.bignum.fixed: UInt;
+    UInt!hs ret; ret.signBit = true; return ret;
+}();
+
+/++
+Software floating point bumber.
+Params:
+    coefficientSize = coefficient size in bits
++/
+struct Fp(size_t coefficientSize)
+    if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
+{
+    import mir.bignum.fixed: UInt;
+
+    bool sign;
+    sizediff_t exponent;
+    UInt!coefficientSize coefficient;
+
+    /++
+    +/
+    nothrow
+    this(bool sign, sizediff_t exponent, UInt!coefficientSize normalizedCoefficient)
+    {
+        this.coefficient = normalizedCoefficient;
+        this.exponent = exponent;
+        this.sign = sign;
+    }
+
+    /++
+    +/
+    this(size_t size)(UInt!size integer, bool normalizedInteger = false)
+        nothrow
+    {
+        import mir.bignum.fixed: UInt;
+        static if (size < coefficientSize)
+        {
+            if (normalizedInteger)
+            {
+                this(false, sizediff_t(size) - coefficientSize, integer.rightExtend!(coefficientSize - size));
+            }
+            else
+            {
+                this(integer.toSize!coefficientSize, false);
+            }
+        }
+        else
+        {
+            this.exponent = size - coefficientSize;
+            if (!normalizedInteger)
+            {
+                auto c = integer.ctlz;
+                integer <<= c;
+                this.exponent -= c;
+            }
+            static if (size == coefficientSize)
+            {
+                coefficient = integer;
+            }
+            else
+            {
+                enum N = coefficient.data.length;
+                version (LittleEndian)
+                    coefficient.data = integer.data[$ - N .. $];
+                else
+                    coefficient.data = integer.data[0 .. N];
+                enum tailSize = size - coefficientSize;
+                auto cr = integer.toSize!tailSize.opCmp(half!tailSize);
+                if (cr > 0 || cr == 0 && coefficient.bt(0))
+                {
+                    if (auto overflow = coefficient += 1)
+                    {
+                        coefficient = half!coefficientSize;
+                        exponent++;
+                    }
+                }
+            }
+        }
+    }
+
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto fp = Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+        assert(fp.exponent == 0);
+        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+
+        fp = Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"), true);
+        assert(fp.exponent == 0);
+        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+
+        fp = Fp!128(UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d"));
+        assert(fp.exponent == -20);
+        assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000"));
+
+        fp = Fp!128(UInt!128.fromHexString("e7022b0029d"));
+        assert(fp.exponent == -84);
+        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+
+        fp = Fp!128(UInt!64.fromHexString("e7022b0029d"));
+        assert(fp.exponent == -84);
+        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+
+        fp = Fp!128(UInt!64.fromHexString("e7022b0029dd0aff"), true);
+        assert(fp.exponent == -64);
+        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029dd0aff0000000000000000"));
+
+        fp = Fp!128(UInt!64.fromHexString("e7022b0029d"));
+        assert(fp.exponent == -84);
+        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+    
+        fp = Fp!128(UInt!192.fromHexString("ffffffffffffffffffffffffffffffff1000000000000000"));
+        assert(fp.exponent == 64);
+        assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
+
+        fp = Fp!128(UInt!192.fromHexString("ffffffffffffffffffffffffffffffff8000000000000000"));
+        assert(fp.exponent == 65);
+        assert(fp.coefficient == UInt!128.fromHexString("80000000000000000000000000000000"));
+
+        fp = Fp!128(UInt!192.fromHexString("fffffffffffffffffffffffffffffffe8000000000000000"));
+        assert(fp.exponent == 64);
+        assert(fp.coefficient == UInt!128.fromHexString("fffffffffffffffffffffffffffffffe"));
+
+        fp = Fp!128(UInt!192.fromHexString("fffffffffffffffffffffffffffffffe8000000000000001"));
+        assert(fp.exponent == 64);
+        assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
+    }
+
+    ///
+    ref Fp opOpAssign(string op : "*")(Fp rhs) nothrow return
+    {
+        this = this.opBinary!op(rhs);
+        return this;
+    }
+
+    ///
+    Fp opBinary(string op : "*")(Fp rhs) nothrow const
+    {
+        return cast(Fp) .extendedMul(this, rhs);
+    }
+
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = Fp!128(0, -13, UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"));
+        auto b = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314"));
+        auto fp = a * b;
+        assert(fp.sign);
+        assert(fp.exponent == 100 - 13 + 128);
+        assert(fp.coefficient == UInt!128.fromHexString("c6841dd302415d785373ab6d93712988"));
+    }
+
+    ///
+    T opCast(T)() nothrow const
+        if (is(Unqual!T == bool))
+    {
+        return coefficient != 0;
+    }
+    
+    ///
+    T opCast(T, bool noHalf = false)() nothrow const
+        if (isFloatingPoint!T)
+    {
+        // import mir.math.ieee: ldexp;
+        import std.math: ldexp;
+        auto exp = cast()exponent;
+        static if (coefficientSize == 32)
+        {
+            Unqual!T c = cast(uint) coefficient;
+        }
+        else
+        static if (coefficientSize == 64)
+        {
+            Unqual!T c = cast(ulong) coefficient;
+        }
+        else
+        {
+            enum shift = coefficientSize - T.mant_dig;
+            enum rMask = (UInt!coefficientSize(1) << shift) - UInt!coefficientSize(1);
+            enum rHalf = UInt!coefficientSize(1) << (shift - 1);
+            enum rInc = UInt!coefficientSize(1) << shift;
+            UInt!coefficientSize adC = coefficient;
+            static if (!noHalf)
+            {
+                auto cr = (coefficient & rMask).opCmp(rHalf);
+                if ((cr > 0) | (cr == 0) & coefficient.bt(shift))
+                {
+                    if (auto overflow = adC += rInc)
+                    {
+                        adC = half!coefficientSize;
+                        exp++;
+                    }
+                }
+            }
+            adC >>= shift;
+            exp += shift;
+            Unqual!T c = cast(ulong) adC;
+            static if (T.mant_dig > 64) //
+            {
+                static assert (T.mant_dig <= 128);
+                c += ldexp(cast(T) cast(ulong) (adC >> 64), 64);
+            }
+        }
+        if (sign)
+            c = -c;
+        static if (exp.sizeof > int.sizeof)
+        {
+            import mir.utility: min, max;
+            exp = exp.max(int.min).min(int.max);
+        }
+        return ldexp(c, cast(int)exp);
+    }
+
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto fp = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112cb8b71ad3f85a970a314"));
+        assert(cast(double)fp == -0xE3251BACB112C8p+172);
+    }
+
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto fp = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112cb8b71ad3f85a970a314"));
+        static if (real.mant_dig == 64)
+            assert(cast(real)fp == -0xe3251bacb112cb8bp+164L);
+    }
+
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto fp = Fp!64(1, 100, UInt!64(0xe3251bacb112cb8b));
+        version (DigitalMars)
+        {
+            // https://issues.dlang.org/show_bug.cgi?id=20963
+            assert(cast(double)fp == -0xE3251BACB112C8p+108
+                || cast(double)fp == -0xE3251BACB112D0p+108);
+        }
+        else
+        {
+            assert(cast(double)fp == -0xE3251BACB112C8p+108);
+        }
+    }
+// -0x1.c64a375962259p+163 = 
+// -0xe.3251bacb112cb8bp+160 = 
+// -0x1.c64a37596225ap+163 = 
+// -0xe.3251bacb112cb8bp+160 = 
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto fp = Fp!64(1, 100, UInt!64(0xe3251bacb112cb8b));
+        static if (real.mant_dig == 64)
+            assert(cast(real)fp == -0xe3251bacb112cb8bp+100L);
+    }
+
+    ///
+    T opCast(T : Fp!newCoefficientSize, size_t newCoefficientSize)() nothrow const
+    {
+        auto ret = Fp!newCoefficientSize(coefficient, true);
+        ret.exponent += exponent;
+        ret.sign = sign;
+        return ret;
+    }
+
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto fp = cast(Fp!64) Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2784a1de7022b0029d"));
+        assert(fp.exponent == 64);
+        assert(fp.coefficient == UInt!64.fromHexString("afbbfae3cd0aff28"));
+    }
+}
+
+///
+Fp!(coefficientizeA + coefficientizeB) extendedMul(size_t coefficientizeA, size_t coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
+    @safe pure nothrow @nogc
+{
+    import mir.bignum.fixed: extendedMul;
+    auto coefficient = extendedMul(a.coefficient, b.coefficient);
+    auto exponent = a.exponent + b.exponent;
+    auto sign = a.sign ^ b.sign;
+    if (!coefficient.signBit)
+    {
+        --exponent;
+        coefficient = coefficient.smallLeftShift(1);
+    }
+    return typeof(return)(sign, exponent, coefficient);
+}
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
new file mode 100644
index 00000000..df10bcbc
--- /dev/null
+++ b/source/mir/bignum/integer.d
@@ -0,0 +1,475 @@
+/++
+Note:
+    The module doesn't provide full arithmetic API for now.
++/
+module mir.bignum.integer;
+
+import std.traits;
+import mir.bitop;
+import mir.utility;
+
+/++
+Stack-allocated big signed integer.
+Params:
+    maxSize64 = count of 64bit words in coefficient
++/
+struct BigInt(size_t maxSize64)
+    if (maxSize64 && maxSize64 <= ushort.max)
+{
+    import mir.bignum.low_level_view;
+    import mir.bignum.fixed;
+
+    ///
+    bool sign;
+    ///
+    uint length;
+    ///
+    size_t[ulong.sizeof / size_t.sizeof * maxSize64] data = void;
+
+    @disable this(this);
+
+    ///
+    this(size_t size)(UInt!size fixedInt)
+    {
+        this(fixedInt.data);
+    }
+
+    ///
+    this(size_t N)(size_t[N] data)
+        if (N <= this.data.length)
+    {
+        version(LittleEndian)
+            this.data[0 .. N] = data;
+        else
+            this.data[$ - N .. $] = data;
+        length = data.length;
+        normalize;
+    }
+
+    ///
+    this(ulong data)
+    {
+        auto d = view.leastSignificantFirst;
+        static if (size_t.sizeof == ulong.sizeof)
+        {
+            d.front = data;
+            length = 1;
+        }
+        else
+        {
+            d.front = cast(uint) data;
+            d[1] = cast(uint) (data >> 32);
+            length = 2;
+        }
+        normalize;
+    }
+
+    ///
+    BigInt copy() @property
+    {
+        BigInt ret;
+        ret.sign = sign;
+        ret.length = length;
+        ret.data = data;
+        return ret;
+    }
+
+    ///
+    bool opEquals(ref const BigInt rhs)
+        const @safe pure nothrow @nogc
+    {
+        return view == rhs.view;
+    }
+
+    /++
+    +/
+    auto opCmp(ref const BigInt rhs) 
+        const @safe pure nothrow @nogc
+    {
+        return view.opCmp(rhs.view);
+    }
+
+    ///
+    BigIntView!size_t view()() @property
+    {
+        version (LittleEndian)
+            return typeof(return)(data[0 .. length], sign);
+        else
+            return typeof(return)(data[$ - length .. $], sign);
+    }
+
+    ///
+    BigIntView!(const size_t) view()() const @property
+    {
+        version (LittleEndian)
+            return typeof(return)(data[0 .. length], sign);
+        else
+            return typeof(return)(data[$ - length .. $], sign);
+    }
+
+    ///
+    void normalize()()
+    {
+        auto norm = view.normalized;
+        this.length = cast(uint) norm.unsigned.coefficients.length;
+        this.sign = norm.sign;
+    }
+
+    /++
+    +/
+    void putCoefficient(size_t value)
+    {
+        assert(length < data.length);
+        version (LittleEndian)
+            data[length++] = value;
+        else
+            data[$ - ++length] = value;
+    }
+
+    /++
+    Performs `size_t overflow = (big += overflow) *= scalar` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = unsigned value to multiply by
+        overflow = initial overflow value
+    Returns:
+        unsigned overflow value
+    +/
+    size_t opOpAssign(string op : "*")(size_t rhs, size_t overflow = 0u)
+        @safe pure nothrow @nogc
+    {
+        if (length == 0)
+            goto L;
+        overflow = view.unsigned.opOpAssign!op(rhs, overflow);
+        if (overflow && length < data.length)
+        {
+        L:
+            putCoefficient(overflow);
+            overflow = 0;
+        }
+        return overflow;
+    }
+
+    /++
+    Performs `size_t overflow = (big += overflow) *= fixed` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = unsigned value to multiply by
+        overflow = initial overflow value
+    Returns:
+        unsigned overflow value
+    +/
+    UInt!size opOpAssign(string op : "*", size_t size)(UInt!size rhs, UInt!size overflow = 0)
+        @safe pure nothrow @nogc
+    {
+        if (length == 0)
+            goto L;
+        overflow = view.unsigned.opOpAssign!op(rhs, overflow);
+        if (overflow && length < data.length)
+        {
+        L:
+            static if (size <= 64)
+            {
+                auto o = cast(ulong)overflow;
+                static if (size_t.sizeof == ulong.sizeof)
+                {
+                    putCoefficient(o);
+                    overflow = UInt!size.init;
+                }
+                else
+                {
+                    putCoefficient(cast(uint)o);
+                    o >>= 32;
+                    if (length < data.length)
+                    {
+                        putCoefficient(cast(uint)o);
+                        o = 0;
+                    }
+                    overflow = UInt!size(o);
+                }
+            }
+            else
+            {
+                do
+                {
+                    putCoefficient(cast(size_t)overflow);
+                    overflow >>= size_t.sizeof * 8;
+                }
+                while(overflow && length < data.length);
+            }
+        }
+        return overflow;
+    }
+
+    /++
+    Performs `size_t overflow = big *= fixed` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = unsigned value to multiply by
+    Returns:
+        overflow
+    +/
+    bool opOpAssign(string op)(ref const BigInt rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        int diff = length - rhs.length;
+        if (diff < 0)
+        {
+            auto oldLength = length;
+            length = rhs.length;
+            view.unsigned.leastSignificantFirst[oldLength .. $] = 0;
+        }
+        else
+        if (rhs.length == 0)
+            return false;
+        auto thisView = view;
+        auto overflow = thisView.opOpAssign!op(rhs.view);
+        this.sign = thisView.sign;
+        if (overflow)
+        {
+            if (length < data.length)
+            {
+                putCoefficient(overflow);
+                overflow = false;
+            }
+        }
+        else
+        {
+            normalize;
+        }
+        return overflow;
+    }
+
+    /++
+    +/
+    static BigInt fromHexString()(scope const(char)[] str)
+        @trusted pure
+    {
+        BigInt ret;
+        auto len = str.length / (size_t.sizeof * 2) + (str.length % (size_t.sizeof * 2) != 0);
+        if (len > ret.data.length)
+        {
+            version(D_Exceptions)
+                throw hexStringException;
+            else
+                assert(0, hexStringErrorMsg);
+        }
+        ret.length = cast(uint)len;
+        ret.view.unsigned.fromHexStringImpl(str);
+        return ret;
+    }
+
+    ///
+    bool mulPow5(size_t degree)
+    {
+        // assert(approxCanMulPow5(degree));
+        if (length == 0)
+            return false;
+        enum n = MaxWordPow5!size_t;
+        enum wordInit = size_t(5) ^^ n;
+        size_t word = wordInit;
+        bool of;
+        while(degree)
+        {
+            if (degree >= n)
+            {
+                degree -= n;
+            }
+            else
+            {
+                word = 1;
+                do word *= 5;
+                while(--degree);
+            }
+            if (auto overflow = view *= word)
+            {
+                of = length >= data.length;
+                if (!of)
+                    putCoefficient(overflow);
+            }
+        }
+        return of;
+    }
+
+    ///
+    ref BigInt opOpAssign(string op)(size_t shift)
+        @safe pure nothrow @nogc return
+        if (op == "<<" || op == ">>")
+    {
+        auto index = shift / (size_t.sizeof * 8);
+        auto bs = shift % (size_t.sizeof * 8);
+        auto ss = size_t.sizeof * 8 - bs;
+        static if (op == ">>")
+        {
+            if (index >= length)
+            {
+                length = 0;
+                return this;
+            }
+            auto d = view.leastSignificantFirst;
+            if (bs)
+            {
+                foreach (j; 0 .. d.length - (index + 1))
+                {
+                    d[j] = (d[j + index] >>> bs) | (d[j + (index + 1)] << ss);
+                }
+            }
+            else
+            {
+                foreach (j; 0 .. d.length - (index + 1))
+                {
+                    d[j] = d[j + index];
+                }
+            }
+            auto most = d[$ - (index + 1)] = d.back >>> bs;
+            length -= index + (most == 0);
+        }
+        else
+        {
+            if (index >= data.length || length == 0)
+            {
+                length = 0;
+                return this;
+            }
+
+            if (bs)
+            {
+                auto most = view.unsigned.mostSignificant >> ss;
+                length += index;
+                if (length < data.length)
+                {
+                    if (most)
+                    {
+                        length++;
+                        view.unsigned.mostSignificant = most;
+                        length--;
+                    }
+                }
+                else
+                {
+                    length = data.length;
+                }
+
+                auto d = view.leastSignificantFirst;
+                foreach_reverse (j; index + 1 .. length)
+                {
+                    d[j] = (d[j - index] << bs) | (d[j - (index + 1)] >> ss);
+                }
+                d[index] = d.front << bs;
+                if (length < data.length)
+                    length += most != 0;
+            }
+            else
+            {
+                length = cast(uint) min(length + index, cast(uint)data.length);
+                auto d = view.leastSignificantFirst;
+                foreach_reverse (j; index .. length)
+                {
+                    d[j] = d[j - index];
+                }
+            }
+            view.leastSignificantFirst[0 .. index] = 0;
+        }
+        return this;
+    }
+
+    ///
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+        if (isFloatingPoint!T && isMutable!T)
+    {
+        return view.opCast!(T, wordNormalized, nonZero);
+    }
+
+    /++
+    +/
+    bool copyFrom(W, WordEndian endian)(BigIntView!(const W, endian) view)
+    {
+        static if (W.sizeof > size_t.sizeof)
+        {
+            return this.copyFrom(cast(BigIntView!(const size_t))view);
+        }
+        else
+        {
+            this.sign = view.sign;
+            auto lhs = BigUIntView!W(cast(W[])data);
+            auto rhs = view;
+            auto overflow = lhs.coefficients.length < rhs.coefficients.length;
+            auto n = overflow ? lhs.coefficients.length : rhs.coefficients.length;
+            lhs.leastSignificantFirst[0 .. n] = rhs.leastSignificantFirst[0 .. n];
+            this.length = cast(uint)(n / (size_t.sizeof / W.sizeof));
+            if (auto tail = n % (size_t.sizeof / W.sizeof))
+            {
+                this.length++;
+                auto shift = ((size_t.sizeof / W.sizeof) - tail) * (W.sizeof * 8);
+                auto value = this.view.unsigned.mostSignificant;
+                value <<= shift;
+                value >>= shift;
+                this.view.unsigned.mostSignificant = value;
+            }
+            return overflow;
+        }
+    }
+
+    /// ditto
+    bool copyFrom(W, WordEndian endian)(BigUIntView!(const W, endian) view)
+    {
+        return this.copyFrom(BigIntView!(const W, endian)(view));
+    }
+}
+
+///
+version(mir_bignum_test)
+unittest
+{
+    import mir.bignum.fixed;
+    import mir.bignum.low_level_view;
+
+    auto a = BigInt!4.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8");
+    auto b = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
+    auto c = BigInt!4.fromHexString("7869dd864619cace5953a09910327b3971413e6aa5f417fa25a2ac93291b941f");
+    c.sign = true;
+    assert(a != b);
+    assert(a < b);
+    a -= b;
+    assert(a.sign);
+    assert(a == c);
+    a -= a;
+    assert(!a.sign);
+    assert(!a.length);
+
+    auto d = BigInt!4.fromHexString("0de1a911c6dc8f90a7169a148e65d22cf34f6a8254ae26362b064f26ac44218a");
+    assert((b *= 0x7869dd86) == 0x5c019770);
+    assert(b == d);
+
+    d = BigInt!4.fromHexString("856eeb23e68cc73f2a517448862cdc97e83f9dfa23768296724bf00fda7df32a");
+    auto o = b *= UInt!128.fromHexString("f79a222050aaeaaa417fa25a2ac93291");
+    assert(o == UInt!128.fromHexString("d6d15b99499b73e68c3331eb0f7bf16"));
+    assert(b == d);
+
+    d = BigInt!4.fromHexString("d"); // initial value
+    d.mulPow5(60);
+    c = BigInt!4.fromHexString("81704fcef32d3bd8117effd5c4389285b05d");
+    assert(d == c);
+
+    d >>= 80;
+    c = BigInt!4.fromHexString("81704fcef32d3bd8");
+    assert(d == c);
+
+    c = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
+    d = BigInt!4.fromHexString("9935cea0707f79a222050aaeaaaed17feb7aa76999d700000000000000000000");
+    c <<= 80;
+    assert(d == c);
+    c >>= 80;
+    c <<= 84;
+    d <<= 4;
+    assert(d == c);
+    assert(c != b);
+    b.sign = true;
+    assert(!c.copyFrom(b.view));
+    assert(c == b);
+    b >>= 18;
+    auto bView = cast(BigIntView!ushort)b.view;
+    assert(!c.copyFrom(bView.topLeastSignificantPart(bView.unsigned.coefficients.length - 1)));
+    assert(c == b);
+}
diff --git a/source/mir/bignum/internal/dec2flt_table.d b/source/mir/bignum/internal/dec2flt_table.d
new file mode 100644
index 00000000..89d043db
--- /dev/null
+++ b/source/mir/bignum/internal/dec2flt_table.d
@@ -0,0 +1,2066 @@
+/++
+Tables of approximations of powers of ten.
+DO NOT MODIFY: Generated by `etc/dec2flt_table.py`
++/
+module mir.bignum.internal.dec2flt_table;
+
+enum min_p10_e = -512;
+enum max_p10_e = 512;
+
+// 
+static immutable align(16) ulong[2][1025] p10_coefficients = [
+    [0x9049EE32DB23D21C, 0x7132D332E3F204D5],
+    [0xB45C69BF91ECC6A4, 0x8D7F87FF9CEE860A],
+    [0xE173842F7667F84C, 0x70DF69FF842A278D],
+    [0x8CE8329DAA00FB30, 0xC68BA23FB29A58B8],
+    [0xB0223F45148139FC, 0xB82E8ACF9F40EEE6],
+    [0xDC2ACF1659A1887B, 0xA63A2D8387112A9F],
+    [0x899AC16DF804F54D, 0xA7E45C72346ABAA4],
+    [0xAC0171C9760632A0, 0xD1DD738EC185694D],
+    [0xD701CE3BD387BF48, 0xC654D07271E6C3A0],
+    [0x866120E56434D78D, 0xDBF5024787303A44],
+    [0xA7F9691EBD420D70, 0x12F242D968FC48D5],
+    [0xD1F7C3666C9290CC, 0x17AED38FC33B5B0A],
+    [0x833ADA2003DB9A80, 0x8ECD4439DA0518E6],
+    [0xA40990A804D2811F, 0x7280954850865F20],
+    [0xCD0BF4D206072167, 0x4F20BA9A64A7F6E8],
+    [0x8027790343C474E1, 0x917474A07EE8FA51],
+    [0xA031574414B59219, 0xB5D191C89EA338E5],
+    [0xC83DAD1519E2F69F, 0xE345F63AC64C071E],
+    [0xFA4D185A605BB447, 0x9C1773C977DF08E6],
+    [0x9C702F387C3950AC, 0x218EA85DEAEB6590],
+    [0xC38C3B069B47A4D7, 0x29F2527565A63EF4],
+    [0xF46F49C842198E0D, 0xF46EE712BF0FCEB0],
+    [0x98C58E1D294FF8C8, 0x18C5506BB769E12E],
+    [0xBEF6F1A473A3F6FA, 0x1EF6A486A544597A],
+    [0xEEB4AE0D908CF4B9, 0xA6B44DA84E956FD8],
+    [0x9530ECC87A5818F3, 0x6830B089311D65E7],
+    [0xBA7D27FA98EE1F30, 0x423CDCAB7D64BF61],
+    [0xE91C71F93F29A6FC, 0x52CC13D65CBDEF39],
+    [0x91B1C73BC77A085E, 0xB3BF8C65F9F6B584],
+    [0xB61E390AB9588A75, 0x20AF6F7F787462E5],
+    [0xE3A5C74D67AEAD12, 0x68DB4B5F56917B9E],
+    [0x8E479C9060CD2C2C, 0x81890F1B961AED43],
+    [0xB1D983B479007736, 0x61EB52E27BA1A893],
+    [0xDE4FE4A197409504, 0xFA66279B1A8A12B8],
+    [0x8AF1EEE4FE885D22, 0x7C7FD8C0F0964BB3],
+    [0xADAE6A9E3E2A746B, 0x1B9FCEF12CBBDEA0],
+    [0xD91A0545CDB51186, 0xE287C2AD77EAD648],
+    [0x87B0434BA0912AF4, 0xAD94D9AC6AF2C5ED],
+    [0xA99C541E88B575B1, 0x98FA101785AF7768],
+    [0xD40369262AE2D31D, 0xBF38941D671B5542],
+    [0x848221B7DACDC3F2, 0xF7835C9260711549],
+    [0xA5A2AA25D18134EE, 0x756433B6F88D5A9C],
+    [0xCF0B54AF45E1822A, 0x12BD40A4B6B0B143],
+    [0x816714ED8BACF15A, 0x4BB64866F22E6ECA],
+    [0xA1C0DA28EE982DB1, 0xDEA3DA80AEBA0A7C],
+    [0xCA3110B32A3E391D, 0x164CD120DA688D1B],
+    [0xFCBD54DFF4CDC764, 0x5BE005691102B062],
+    [0x9DF6550BF9009C9F, 0xB96C0361AAA1AE3D],
+    [0xC573EA4EF740C3C6, 0x67C7043A154A19CC],
+    [0xF6D0E4E2B510F4B8, 0x01B8C5489A9CA040],
+    [0x9A428F0DB12A98F3, 0x01137B4D60A1E428],
+    [0xC0D332D11D753F30, 0xC1585A20B8CA5D32],
+    [0xF107FF8564D28EFC, 0xB1AE70A8E6FCF47E],
+    [0x96A4FFB35F03995D, 0x4F0D0669905E18CF],
+    [0xBC4E3FA036C47FB5, 0xA2D04803F4759F02],
+    [0xEB61CF8844759FA2, 0xCB845A04F19306C3],
+    [0x931D21B52AC983C5, 0x1F32B84316FBE43A],
+    [0xB7E46A22757BE4B6, 0x66FF6653DCBADD48],
+    [0xE5DD84AB12DADDE4, 0x00BF3FE8D3E9949A],
+    [0x8FAA72EAEBC8CAAF, 0x807787F18471FCE1],
+    [0xB3950FA5A6BAFD5A, 0x209569EDE58E7C19],
+    [0xE07A538F1069BCB1, 0xA8BAC4695EF21B1F],
+    [0x8C4C74396A4215EE, 0x6974BAC1DB5750F3],
+    [0xAF5F9147C4D29B6A, 0x03D1E972522D2530],
+    [0xDB377599B6074245, 0x84C663CEE6B86E7C],
+    [0x8902A98011C4896B, 0xD2FBFE615033450E],
+    [0xAB4353E01635ABC6, 0x87BAFDF9A4401651],
+    [0xD61428D81BC316B7, 0xE9A9BD780D501BE5],
+    [0x85CC99871159EE32, 0x520A166B0852116F],
+    [0xA73FBFE8D5B069BF, 0xE68C9C05CA6695CB],
+    [0xD10FAFE30B1C842F, 0xA02FC3073D003B3E],
+    [0x82A9CDEDE6F1D29D, 0x241DD9E486202507],
+    [0xA354416960AE4744, 0x6D25505DA7A82E48],
+    [0xCC2951C3B8D9D916, 0x886EA475119239DA],
+    [0xFF33A634A7104F5B, 0xEA8A4D9255F6C851],
+    [0x9F8047E0E86A3199, 0xD296707B75BA3D33],
+    [0xC76059D92284BDFF, 0x073C0C9A5328CC7F],
+    [0xF938704F6B25ED7F, 0xC90B0FC0E7F2FF9F],
+    [0x9BC34631A2F7B46F, 0x3DA6E9D890F7DFC3],
+    [0xC2B417BE0BB5A18B, 0x0D10A44EB535D7B4],
+    [0xF3611DAD8EA309EE, 0xD054CD6262834DA1],
+    [0x981CB28C7925E635, 0xA235005D7D921085],
+    [0xBE23DF2F976F5FC2, 0xCAC24074DCF694A6],
+    [0xEDACD6FB7D4B37B2, 0x3D72D092143439D0],
+    [0x948C065D2E4F02CF, 0x6667C25B4CA0A422],
+    [0xB9AF07F479E2C383, 0x4001B2F21FC8CD2A],
+    [0xE81AC9F1985B7464, 0x10021FAEA7BB0075],
+    [0x9110BE36FF3928BF, 0x8A0153CD28D4E049],
+    [0xB554EDC4BF0772EE, 0x2C81A8C0730A185B],
+    [0xE2AA2935EEC94FAA, 0xB7A212F08FCC9E72],
+    [0x8DAA59C1B53DD1CA, 0x12C54BD659DFE307],
+    [0xB114F032228D463D, 0x97769ECBF057DBC9],
+    [0xDD5A2C3EAB3097CC, 0xBD54467EEC6DD2BB],
+    [0x8A585BA72AFE5EDF, 0x5654AC0F53C4A3B5],
+    [0xACEE7290F5BDF697, 0x2BE9D71328B5CCA2],
+    [0xD82A0F35332D743D, 0xF6E44CD7F2E33FCB],
+    [0x871A49813FFC68A6, 0x1A4EB006F7CE07DF],
+    [0xA8E0DBE18FFB82D0, 0xA0E25C08B5C189D7],
+    [0xD31912D9F3FA6384, 0x891AF30AE331EC4C],
+    [0x83EFABC8387C7E32, 0x35B0D7E6CDFF33B0],
+    [0xA4EB96BA469B9DBF, 0xC31D0DE0817F009C],
+    [0xCE267C68D842852E, 0x73E45158A1DEC0C2],
+    [0x80D80DC18729933D, 0x086EB2D7652B387A],
+    [0xA10E1131E8F3F80C, 0x4A8A5F8D3E760698],
+    [0xC951957E6330F60F, 0x5D2CF7708E13883E],
+    [0xFBA5FADDFBFD3393, 0x3478354CB1986A4D],
+    [0x9D47BCCABD7E403C, 0x00CB214FEEFF4270],
+    [0xC499ABFD6CDDD04B, 0x00FDE9A3EABF130C],
+    [0xF5C016FCC815445E, 0xC13D640CE56ED7D0],
+    [0x99980E5DFD0D4ABB, 0x98C65E880F6546E2],
+    [0xBFFE11F57C509D69, 0x3EF7F62A133E989A],
+    [0xEFFD9672DB64C4C4, 0x8EB5F3B4980E3EC1],
+    [0x95FE7E07C91EFAFA, 0x3931B850DF08E738],
+    [0xBB7E1D89BB66B9B9, 0xC77E266516CB2106],
+    [0xEA5DA4EC2A406827, 0xF95DAFFE5C7DE948],
+    [0x927A87139A684118, 0x5BDA8DFEF9CEB1CD],
+    [0xB71928D88102515E, 0x72D1317EB8425E40],
+    [0xE4DF730EA142E5B6, 0x0F857DDE6652F5D0],
+    [0x8F0BA7E924C9CF92, 0xC9B36EAAFFF3D9A2],
+    [0xB2CE91E36DFC4376, 0x3C204A55BFF0D00B],
+    [0xDF82365C497B5454, 0xCB285CEB2FED040E],
+    [0x8BB161F9ADED14B4, 0x5EF93A12FDF42288],
+    [0xAE9DBA78196859E1, 0x76B78897BD712B2B],
+    [0xDA4529161FC2705A, 0xD4656ABDACCD75F5],
+    [0x886B39ADD3D98638, 0x24BF62B68C0069B9],
+    [0xAA86081948CFE7C6, 0x2DEF3B642F008428],
+    [0xD5278A1F9B03E1B8, 0xB96B0A3D3AC0A531],
+    [0x8538B653C0E26D13, 0xD3E2E66644B8673F],
+    [0xA686E3E8B11B0858, 0x88DB9FFFD5E6810F],
+    [0xD0289CE2DD61CA6D, 0x6B1287FFCB602152],
+    [0x8219620DCA5D1E84, 0x62EB94FFDF1C14D3],
+    [0xA29FBA913CF46625, 0x7BA67A3FD6E31A08],
+    [0xCB47A9358C317FAF, 0xDA9018CFCC9BE08A],
+    [0xFE199382EF3DDF9B, 0x91341F03BFC2D8AD],
+    [0x9ECFFC31D586ABC1, 0x9AC0936257D9C76C],
+    [0xC683FB3E4AE856B1, 0xC170B83AEDD03947],
+    [0xF824FA0DDDA26C5D, 0xF1CCE649A9444799],
+    [0x9B171C48AA8583BA, 0x17200FEE09CAACC0],
+    [0xC1DCE35AD526E4A9, 0x9CE813E98C3D57EF],
+    [0xF2541C318A709DD3, 0xC42218E3EF4CADEB],
+    [0x9774919EF68662A4, 0xBA954F8E758FECB3],
+    [0xBD51B606B427FB4D, 0xA93AA37212F3E7E0],
+    [0xECA623886131FA20, 0xD3894C4E97B0E1D8],
+    [0x93E7D6353CBF3C54, 0xE435CFB11ECE8D27],
+    [0xB8E1CBC28BEF0B69, 0xDD43439D66823071],
+    [0xE71A3EB32EEACE43, 0x14941484C022BC8D],
+    [0x9070672FFD52C0EA, 0xECDC8CD2F815B5D8],
+    [0xB48C80FBFCA77124, 0x6813B007B61B234E],
+    [0xE1AFA13AFBD14D6E, 0x82189C09A3A1EC21],
+    [0x8D0DC4C4DD62D064, 0x714F618606453395],
+    [0xB05135F614BB847E, 0x8DA339E787D6807A],
+    [0xDC65837399EA659D, 0xF10C086169CC2099],
+    [0x89BF722840327F82, 0x16A7853CE21F945F],
+    [0xAC2F4EB2503F1F63, 0x9C51668C1AA77977],
+    [0xD73B225EE44EE73B, 0x4365C02F215157D5],
+    [0x8684F57B4EB15085, 0x0A1F981D74D2D6E5],
+    [0xA82632DA225DA4A6, 0x4CA77E24D2078C9E],
+    [0xD22FBF90AAF50DD0, 0xDFD15DAE06896FC6],
+    [0x835DD7BA6AD928A2, 0xEBE2DA8CC415E5DC],
+    [0xA4354DA9058F72CA, 0x66DB912FF51B5F53],
+    [0xCD42A11346F34F7D, 0x0092757BF2623727],
+    [0x8049A4AC0C5811AE, 0x205B896D777D6279],
+    [0xA05C0DD70F6E161A, 0xA8726BC8D55CBB17],
+    [0xC873114CD3499BA0, 0x128F06BB0AB3E9DD],
+    [0xFA8FD5A0081C0288, 0x1732C869CD60E454],
+    [0x9C99E58405118195, 0x0E7FBD42205C8EB4],
+    [0xC3C05EE50655E1FA, 0x521FAC92A873B261],
+    [0xF4B0769E47EB5A79, 0xE6A797B752909EFA],
+    [0x98EE4A22ECF3188C, 0x9028BED2939A635C],
+    [0xBF29DCABA82FDEAE, 0x7432EE873880FC33],
+    [0xEEF453D6923BD65A, 0x113FAA2906A13B40],
+    [0x9558B4661B6565F8, 0x4AC7CA59A424C508],
+    [0xBAAEE17FA23EBF76, 0x5D79BCF00D2DF64A],
+    [0xE95A99DF8ACE6F54, 0xF4D82C2C107973DC],
+    [0x91D8A02BB6C10594, 0x79071B9B8A4BE86A],
+    [0xB64EC836A47146FA, 0x9748E2826CDEE284],
+    [0xE3E27A444D8D98B8, 0xFD1B1B2308169B25],
+    [0x8E6D8C6AB0787F73, 0xFE30F0F5E50E20F7],
+    [0xB208EF855C969F50, 0xBDBD2D335E51A935],
+    [0xDE8B2B66B3BC4724, 0xAD2C788035E61382],
+    [0x8B16FB203055AC76, 0x4C3BCB5021AFCC31],
+    [0xADDCB9E83C6B1794, 0xDF4ABE242A1BBF3E],
+    [0xD953E8624B85DD79, 0xD71D6DAD34A2AF0D],
+    [0x87D4713D6F33AA6C, 0x8672648C40E5AD68],
+    [0xA9C98D8CCB009506, 0x680EFDAF511F18C2],
+    [0xD43BF0EFFDC0BA48, 0x0212BD1B2566DEF3],
+    [0x84A57695FE98746D, 0x014BB630F7604B58],
+    [0xA5CED43B7E3E9188, 0x419EA3BD35385E2E],
+    [0xCF42894A5DCE35EA, 0x52064CAC828675B9],
+    [0x818995CE7AA0E1B2, 0x7343EFEBD1940994],
+    [0xA1EBFB4219491A1F, 0x1014EBE6C5F90BF9],
+    [0xCA66FA129F9B60A7, 0xD41A26E077774EF7],
+    [0xFD00B897478238D1, 0x8920B098955522B5],
+    [0x9E20735E8CB16382, 0x55B46E5F5D5535B1],
+    [0xC5A890362FDDBC63, 0xEB2189F734AA831D],
+    [0xF712B443BBD52B7C, 0xA5E9EC7501D523E4],
+    [0x9A6BB0AA55653B2D, 0x47B233C92125366F],
+    [0xC1069CD4EABE89F9, 0x999EC0BB696E840A],
+    [0xF148440A256E2C77, 0xC00670EA43CA250D],
+    [0x96CD2A865764DBCA, 0x380406926A5E5728],
+    [0xBC807527ED3E12BD, 0xC605083704F5ECF2],
+    [0xEBA09271E88D976C, 0xF7864A44C633682F],
+    [0x93445B8731587EA3, 0x7AB3EE6AFBE0211D],
+    [0xB8157268FDAE9E4C, 0x5960EA05BAD82965],
+    [0xE61ACF033D1A45DF, 0x6FB92487298E33BE],
+    [0x8FD0C16206306BAC, 0xA5D3B6D479F8E057],
+    [0xB3C4F1BA87BC8697, 0x8F48A4899877186C],
+    [0xE0B62E2929ABA83C, 0x331ACDABFE94DE87],
+    [0x8C71DCD9BA0B4926, 0x9FF0C08B7F1D0B15],
+    [0xAF8E5410288E1B6F, 0x07ECF0AE5EE44DDA],
+    [0xDB71E91432B1A24B, 0xC9E82CD9F69D6150],
+    [0x892731AC9FAF056F, 0xBE311C083A225CD2],
+    [0xAB70FE17C79AC6CA, 0x6DBD630A48AAF407],
+    [0xD64D3D9DB981787D, 0x092CBBCCDAD5B108],
+    [0x85F0468293F0EB4E, 0x25BBF56008C58EA5],
+    [0xA76C582338ED2622, 0xAF2AF2B80AF6F24E],
+    [0xD1476E2C07286FAA, 0x1AF5AF660DB4AEE2],
+    [0x82CCA4DB847945CA, 0x50D98D9FC890ED4D],
+    [0xA37FCE126597973D, 0xE50FF107BAB528A1],
+    [0xCC5FC196FEFD7D0C, 0x1E53ED49A96272C9],
+    [0xFF77B1FCBEBCDC4F, 0x25E8E89C13BB0F7B],
+    [0x9FAACF3DF73609B1, 0x77B191618C54E9AD],
+    [0xC795830D75038C1E, 0xD59DF5B9EF6A2418],
+    [0xF97AE3D0D2446F25, 0x4B0573286B44AD1E],
+    [0x9BECCE62836AC577, 0x4EE367F9430AEC33],
+    [0xC2E801FB244576D5, 0x229C41F793CDA73F],
+    [0xF3A20279ED56D48A, 0x6B43527578C1110F],
+    [0x9845418C345644D7, 0x830A13896B78AAAA],
+    [0xBE5691EF416BD60C, 0x23CC986BC656D554],
+    [0xEDEC366B11C6CB8F, 0x2CBFBE86B7EC8AA9],
+    [0x94B3A202EB1C3F39, 0x7BF7D71432F3D6AA],
+    [0xB9E08A83A5E34F08, 0xDAF5CCD93FB0CC54],
+    [0xE858AD248F5C22CA, 0xD1B3400F8F9CFF69],
+    [0x91376C36D99995BE, 0x23100809B9C21FA2],
+    [0xB58547448FFFFB2E, 0xABD40A0C2832A78A],
+    [0xE2E69915B3FFF9F9, 0x16C90C8F323F516D],
+    [0x8DD01FAD907FFC3C, 0xAE3DA7D97F6792E4],
+    [0xB1442798F49FFB4B, 0x99CD11CFDF41779D],
+    [0xDD95317F31C7FA1D, 0x40405643D711D584],
+    [0x8A7D3EEF7F1CFC52, 0x482835EA666B2572],
+    [0xAD1C8EAB5EE43B67, 0xDA3243650005EECF],
+    [0xD863B256369D4A41, 0x90BED43E40076A83],
+    [0x873E4F75E2224E68, 0x5A7744A6E804A292],
+    [0xA90DE3535AAAE202, 0x711515D0A205CB36],
+    [0xD3515C2831559A83, 0x0D5A5B44CA873E04],
+    [0x8412D9991ED58092, 0xE858790AFE9486C2],
+    [0xA5178FFF668AE0B6, 0x626E974DBE39A873],
+    [0xCE5D73FF402D98E4, 0xFB0A3D212DC81290],
+    [0x80FA687F881C7F8E, 0x7CE66634BC9D0B9A],
+    [0xA139029F6A239F72, 0x1C1FFFC1EBC44E80],
+    [0xC987434744AC874F, 0xA327FFB266B56220],
+    [0xFBE9141915D7A922, 0x4BF1FF9F0062BAA8],
+    [0x9D71AC8FADA6C9B5, 0x6F773FC3603DB4A9],
+    [0xC4CE17B399107C23, 0xCB550FB4384D21D4],
+    [0xF6019DA07F549B2B, 0x7E2A53A146606A48],
+    [0x99C102844F94E0FB, 0x2EDA7444CBFC426D],
+    [0xC0314325637A193A, 0xFA911155FEFB5309],
+    [0xF03D93EEBC589F88, 0x793555AB7EBA27CB],
+    [0x96267C7535B763B5, 0x4BC1558B2F3458DF],
+    [0xBBB01B9283253CA3, 0x9EB1AAEDFB016F16],
+    [0xEA9C227723EE8BCB, 0x465E15A979C1CADC],
+    [0x92A1958A7675175F, 0x0BFACD89EC191ECA],
+    [0xB749FAED14125D37, 0xCEF980EC671F667C],
+    [0xE51C79A85916F485, 0x82B7E12780E7401B],
+    [0x8F31CC0937AE58D3, 0xD1B2ECB8B0908811],
+    [0xB2FE3F0B8599EF08, 0x861FA7E6DCB4AA15],
+    [0xDFBDCECE67006AC9, 0x67A791E093E1D49A],
+    [0x8BD6A141006042BE, 0xE0C8BB2C5C6D24E0],
+    [0xAECC49914078536D, 0x58FAE9F773886E19],
+    [0xDA7F5BF590966849, 0xAF39A475506A899F],
+    [0x888F99797A5E012D, 0x6D8406C952429603],
+    [0xAAB37FD7D8F58179, 0xC8E5087BA6D33B84],
+    [0xD5605FCDCF32E1D7, 0xFB1E4A9A90880A65],
+    [0x855C3BE0A17FCD26, 0x5CF2EEA09A55067F],
+    [0xA6B34AD8C9DFC070, 0xF42FAA48C0EA481F],
+    [0xD0601D8EFC57B08C, 0xF13B94DAF124DA27],
+    [0x823C12795DB6CE57, 0x76C53D08D6B70858],
+    [0xA2CB1717B52481ED, 0x54768C4B0C64CA6E],
+    [0xCB7DDCDDA26DA269, 0xA9942F5DCF7DFD0A],
+    [0xFE5D54150B090B03, 0xD3F93B35435D7C4C],
+    [0x9EFA548D26E5A6E2, 0xC47BC5014A1A6DB0],
+    [0xC6B8E9B0709F109A, 0x359AB6419CA1091B],
+    [0xF867241C8CC6D4C1, 0xC30163D203C94B62],
+    [0x9B407691D7FC44F8, 0x79E0DE63425DCF1D],
+    [0xC21094364DFB5637, 0x985915FC12F542E5],
+    [0xF294B943E17A2BC4, 0x3E6F5B7B17B2939E],
+    [0x979CF3CA6CEC5B5B, 0xA705992CEECF9C43],
+    [0xBD8430BD08277231, 0x50C6FF782A838353],
+    [0xECE53CEC4A314EBE, 0xA4F8BF5635246428],
+    [0x940F4613AE5ED137, 0x871B7795E136BE99],
+    [0xB913179899F68584, 0x28E2557B59846E3F],
+    [0xE757DD7EC07426E5, 0x331AEADA2FE589CF],
+    [0x9096EA6F3848984F, 0x3FF0D2C85DEF7622],
+    [0xB4BCA50B065ABE63, 0x0FED077A756B53AA],
+    [0xE1EBCE4DC7F16DFC, 0xD3E8495912C62894],
+    [0x8D3360F09CF6E4BD, 0x64712DD7ABBBD95D],
+    [0xB080392CC4349DED, 0xBD8D794D96AACFB4],
+    [0xDCA04777F541C568, 0xECF0D7A0FC5583A1],
+    [0x89E42CAAF9491B61, 0xF41686C49DB57245],
+    [0xAC5D37D5B79B6239, 0x311C2875C522CED6],
+    [0xD77485CB25823AC7, 0x7D633293366B828B],
+    [0x86A8D39EF77164BD, 0xAE5DFF9C02033197],
+    [0xA8530886B54DBDEC, 0xD9F57F830283FDFD],
+    [0xD267CAA862A12D67, 0xD072DF63C324FD7C],
+    [0x8380DEA93DA4BC60, 0x4247CB9E59F71E6D],
+    [0xA46116538D0DEB78, 0x52D9BE85F074E609],
+    [0xCD795BE870516656, 0x67902E276C921F8B],
+    [0x806BD9714632DFF6, 0x00BA1CD8A3DB53B7],
+    [0xA086CFCD97BF97F4, 0x80E8A40ECCD228A5],
+    [0xC8A883C0FDAF7DF0, 0x6122CD128006B2CE],
+    [0xFAD2A4B13D1B5D6C, 0x796B805720085F81],
+    [0x9CC3A6EEC6311A64, 0xCBE3303674053BB1],
+    [0xC3F490AA77BD60FD, 0xBEDBFC4411068A9D],
+    [0xF4F1B4D515ACB93C, 0xEE92FB5515482D44],
+    [0x991711052D8BF3C5, 0x751BDD152D4D1C4B],
+    [0xBF5CD54678EEF0B7, 0xD262D45A78A0635D],
+    [0xEF340A98172AACE5, 0x86FB897116C87C35],
+    [0x9580869F0E7AAC0F, 0xD45D35E6AE3D4DA1],
+    [0xBAE0A846D2195713, 0x8974836059CCA109],
+    [0xE998D258869FACD7, 0x2BD1A438703FC94B],
+    [0x91FF83775423CC06, 0x7B6306A34627DDCF],
+    [0xB67F6455292CBF08, 0x1A3BC84C17B1D543],
+    [0xE41F3D6A7377EECA, 0x20CABA5F1D9E4A94],
+    [0x8E938662882AF53E, 0x547EB47B7282EE9C],
+    [0xB23867FB2A35B28E, 0xE99E619A4F23AA43],
+    [0xDEC681F9F4C31F31, 0x6405FA00E2EC94D4],
+    [0x8B3C113C38F9F37F, 0xDE83BC408DD3DD05],
+    [0xAE0B158B4738705F, 0x9624AB50B148D446],
+    [0xD98DDAEE19068C76, 0x3BADD624DD9B0957],
+    [0x87F8A8D4CFA417CA, 0xE54CA5D70A80E5D6],
+    [0xA9F6D30A038D1DBC, 0x5E9FCF4CCD211F4C],
+    [0xD47487CC8470652B, 0x7647C3200069671F],
+    [0x84C8D4DFD2C63F3B, 0x29ECD9F40041E073],
+    [0xA5FB0A17C777CF0A, 0xF468107100525890],
+    [0xCF79CC9DB955C2CC, 0x7182148D4066EEB4],
+    [0x81AC1FE293D599C0, 0xC6F14CD848405531],
+    [0xA21727DB38CB0030, 0xB8ADA00E5A506A7D],
+    [0xCA9CF1D206FDC03C, 0xA6D90811F0E4851C],
+    [0xFD442E4688BD304B, 0x908F4A166D1DA663],
+    [0x9E4A9CEC15763E2F, 0x9A598E4E043287FE],
+    [0xC5DD44271AD3CDBA, 0x40EFF1E1853F29FE],
+    [0xF7549530E188C129, 0xD12BEE59E68EF47D],
+    [0x9A94DD3E8CF578BA, 0x82BB74F8301958CE],
+    [0xC13A148E3032D6E8, 0xE36A52363C1FAF02],
+    [0xF18899B1BC3F8CA2, 0xDC44E6C3CB279AC2],
+    [0x96F5600F15A7B7E5, 0x29AB103A5EF8C0B9],
+    [0xBCB2B812DB11A5DE, 0x7415D448F6B6F0E8],
+    [0xEBDF661791D60F56, 0x111B495B3464AD21],
+    [0x936B9FCEBB25C996, 0xCAB10DD900BEEC35],
+    [0xB84687C269EF3BFB, 0x3D5D514F40EEA742],
+    [0xE65829B3046B0AFA, 0x0CB4A5A3112A5113],
+    [0x8FF71A0FE2C2E6DC, 0x47F0E785EABA72AC],
+    [0xB3F4E093DB73A093, 0x59ED216765690F57],
+    [0xE0F218B8D25088B8, 0x306869C13EC3532C],
+    [0x8C974F7383725573, 0x1E414218C73A13FC],
+    [0xAFBD2350644EEAD0, 0xE5D1929EF90898FB],
+    [0xDBAC6C247D62A584, 0xDF45F746B74ABF39],
+    [0x894BC396CE5DA772, 0x6B8BBA8C328EB784],
+    [0xAB9EB47C81F5114F, 0x066EA92F3F326565],
+    [0xD686619BA27255A3, 0xC80A537B0EFEFEBE],
+    [0x8613FD0145877586, 0xBD06742CE95F5F37],
+    [0xA798FC4196E952E7, 0x2C48113823B73704],
+    [0xD17F3B51FCA3A7A1, 0xF75A15862CA504C5],
+    [0x82EF85133DE648C5, 0x9A984D73DBE722FB],
+    [0xA3AB66580D5FDAF6, 0xC13E60D0D2E0EBBA],
+    [0xCC963FEE10B7D1B3, 0x318DF905079926A9],
+    [0xFFBBCFE994E5C620, 0xFDF17746497F7053],
+    [0x9FD561F1FD0F9BD4, 0xFEB6EA8BEDEFA634],
+    [0xC7CABA6E7C5382C9, 0xFE64A52EE96B8FC1],
+    [0xF9BD690A1B68637B, 0x3DFDCE7AA3C673B1],
+    [0x9C1661A651213E2D, 0x06BEA10CA65C084F],
+    [0xC31BFA0FE5698DB8, 0x486E494FCFF30A62],
+    [0xF3E2F893DEC3F126, 0x5A89DBA3C3EFCCFB],
+    [0x986DDB5C6B3A76B8, 0xF89629465A75E01D],
+    [0xBE89523386091466, 0xF6BBB397F1135824],
+    [0xEE2BA6C0678B597F, 0x746AA07DED582E2D],
+    [0x94DB483840B717F0, 0xA8C2A44EB4571CDC],
+    [0xBA121A4650E4DDEC, 0x92F34D62616CE413],
+    [0xE896A0D7E51E1566, 0x77B020BAF9C81D18],
+    [0x915E2486EF32CD60, 0x0ACE1474DC1D122F],
+    [0xB5B5ADA8AAFF80B8, 0x0D819992132456BB],
+    [0xE3231912D5BF60E6, 0x10E1FFF697ED6C69],
+    [0x8DF5EFABC5979C90, 0xCA8D3FFA1EF463C2],
+    [0xB1736B96B6FD83B4, 0xBD308FF8A6B17CB2],
+    [0xDDD0467C64BCE4A1, 0xAC7CB3F6D05DDBDF],
+    [0x8AA22C0DBEF60EE4, 0x6BCDF07A423AA96B],
+    [0xAD4AB7112EB3929E, 0x86C16C98D2C953C6],
+    [0xD89D64D57A607745, 0xE871C7BF077BA8B8],
+    [0x87625F056C7C4A8B, 0x11471CD764AD4973],
+    [0xA93AF6C6C79B5D2E, 0xD598E40D3DD89BCF],
+    [0xD389B47879823479, 0x4AFF1D108D4EC2C3],
+    [0x843610CB4BF160CC, 0xCEDF722A585139BA],
+    [0xA54394FE1EEDB8FF, 0xC2974EB4EE658829],
+    [0xCE947A3DA6A9273E, 0x733D226229FEEA33],
+    [0x811CCC668829B887, 0x0806357D5A3F5260],
+    [0xA163FF802A3426A9, 0xCA07C2DCB0CF26F8],
+    [0xC9BCFF6034C13053, 0xFC89B393DD02F0B6],
+    [0xFC2C3F3841F17C68, 0xBBAC2078D443ACE3],
+    [0x9D9BA7832936EDC1, 0xD54B944B84AA4C0E],
+    [0xC5029163F384A931, 0x0A9E795E65D4DF11],
+    [0xF64335BCF065D37D, 0x4D4617B5FF4A16D6],
+    [0x99EA0196163FA42E, 0x504BCED1BF8E4E46],
+    [0xC06481FB9BCF8D3A, 0xE45EC2862F71E1D7],
+    [0xF07DA27A82C37088, 0x5D767327BB4E5A4D],
+    [0x964E858C91BA2655, 0x3A6A07F8D510F870],
+    [0xBBE226EFB628AFEB, 0x890489F70A55368C],
+    [0xEADAB0ABA3B2DBE5, 0x2B45AC74CCEA842F],
+    [0x92C8AE6B464FC96F, 0x3B0B8BC90012929D],
+    [0xB77ADA0617E3BBCB, 0x09CE6EBB40173745],
+    [0xE55990879DDCAABE, 0xCC420A6A101D0516],
+    [0x8F57FA54C2A9EAB7, 0x9FA946824A12232E],
+    [0xB32DF8E9F3546564, 0x47939822DC96ABF9],
+    [0xDFF9772470297EBD, 0x59787E2B93BC56F7],
+    [0x8BFBEA76C619EF36, 0x57EB4EDB3C55B65B],
+    [0xAEFAE51477A06B04, 0xEDE622920B6B23F1],
+    [0xDAB99E59958885C5, 0xE95FAB368E45ECED],
+    [0x88B402F7FD75539B, 0x11DBCB0218EBB414],
+    [0xAAE103B5FCD2A882, 0xD652BDC29F26A11A],
+    [0xD59944A37C0752A2, 0x4BE76D3346F04960],
+    [0x857FCAE62D8493A5, 0x6F70A4400C562DDC],
+    [0xA6DFBD9FB8E5B88F, 0xCB4CCD500F6BB953],
+    [0xD097AD07A71F26B2, 0x7E2000A41346A7A8],
+    [0x825ECC24C8737830, 0x8ED400668C0C28C9],
+    [0xA2F67F2DFA90563B, 0x728900802F0F32FB],
+    [0xCBB41EF979346BCA, 0x4F2B40A03AD2FFBA],
+    [0xFEA126B7D78186BD, 0xE2F610C84987BFA8],
+    [0x9F24B832E6B0F436, 0x0DD9CA7D2DF4D7C9],
+    [0xC6EDE63FA05D3144, 0x91503D1C79720DBB],
+    [0xF8A95FCF88747D94, 0x75A44C6397CE912A],
+    [0x9B69DBE1B548CE7D, 0xC986AFBE3EE11ABA],
+    [0xC24452DA229B021C, 0xFBE85BADCE996169],
+    [0xF2D56790AB41C2A3, 0xFAE27299423FB9C3],
+    [0x97C560BA6B0919A6, 0xDCCD879FC967D41A],
+    [0xBDB6B8E905CB600F, 0x5400E987BBC1C921],
+    [0xED246723473E3813, 0x290123E9AAB23B69],
+    [0x9436C0760C86E30C, 0xF9A0B6720AAF6521],
+    [0xB94470938FA89BCF, 0xF808E40E8D5B3E6A],
+    [0xE7958CB87392C2C3, 0xB60B1D1230B20E04],
+    [0x90BD77F3483BB9BA, 0xB1C6F22B5E6F48C3],
+    [0xB4ECD5F01A4AA828, 0x1E38AEB6360B1AF3],
+    [0xE2280B6C20DD5232, 0x25C6DA63C38DE1B0],
+    [0x8D590723948A535F, 0x579C487E5A38AD0E],
+    [0xB0AF48EC79ACE837, 0x2D835A9DF0C6D852],
+    [0xDCDB1B2798182245, 0xF8E431456CF88E66],
+    [0x8A08F0F8BF0F156B, 0x1B8E9ECB641B5900],
+    [0xAC8B2D36EED2DAC6, 0xE272467E3D222F40],
+    [0xD7ADF884AA879177, 0x5B0ED81DCC6ABB10],
+    [0x86CCBB52EA94BAEB, 0x98E947129FC2B4EA],
+    [0xA87FEA27A539E9A5, 0x3F2398D747B36224],
+    [0xD29FE4B18E88640F, 0x8EEC7F0D19A03AAD],
+    [0x83A3EEEEF9153E89, 0x1953CF68300424AC],
+    [0xA48CEAAAB75A8E2B, 0x5FA8C3423C052DD7],
+    [0xCDB02555653131B6, 0x3792F412CB06794D],
+    [0x808E17555F3EBF12, 0xE2BBD88BBEE40BD0],
+    [0xA0B19D2AB70E6ED6, 0x5B6ACEAEAE9D0EC4],
+    [0xC8DE047564D20A8C, 0xF245825A5A445275],
+    [0xFB158592BE068D2F, 0xEED6E2F0F0D56713],
+    [0x9CED737BB6C4183D, 0x55464DD69685606C],
+    [0xC428D05AA4751E4D, 0xAA97E14C3C26B887],
+    [0xF53304714D9265E0, 0xD53DD99F4B3066A8],
+    [0x993FE2C6D07B7FAC, 0xE546A8038EFE4029],
+    [0xBF8FDB78849A5F97, 0xDE98520472BDD033],
+    [0xEF73D256A5C0F77D, 0x963E66858F6D4440],
+    [0x95A8637627989AAE, 0xDDE7001379A44AA8],
+    [0xBB127C53B17EC159, 0x5560C018580D5D52],
+    [0xE9D71B689DDE71B0, 0xAAB8F01E6E10B4A7],
+    [0x9226712162AB070E, 0xCAB3961304CA70E8],
+    [0xB6B00D69BB55C8D1, 0x3D607B97C5FD0D22],
+    [0xE45C10C42A2B3B06, 0x8CB89A7DB77C506B],
+    [0x8EB98A7A9A5B04E3, 0x77F3608E92ADB243],
+    [0xB267ED1940F1C61C, 0x55F038B237591ED3],
+    [0xDF01E85F912E37A3, 0x6B6C46DEC52F6688],
+    [0x8B61313BBABCE2C6, 0x2323AC4B3B3DA015],
+    [0xAE397D8AA96C1B78, 0xABEC975E0A0D081B],
+    [0xD9C7DCED53C72256, 0x96E7BD358C904A21],
+    [0x881CEA14545C7575, 0x7E50D64177DA2E55],
+    [0xAA242499697392D3, 0xDDE50BD1D5D0B9EA],
+    [0xD4AD2DBFC3D07788, 0x955E4EC64B44E864],
+    [0x84EC3C97DA624AB5, 0xBD5AF13BEF0B113F],
+    [0xA6274BBDD0FADD62, 0xECB1AD8AEACDD58E],
+    [0xCFB11EAD453994BA, 0x67DE18EDA5814AF2],
+    [0x81CEB32C4B43FCF5, 0x80EACF948770CED7],
+    [0xA2425FF75E14FC32, 0xA1258379A94D028D],
+    [0xCAD2F7F5359A3B3E, 0x096EE45813A04330],
+    [0xFD87B5F28300CA0E, 0x8BCA9D6E188853FC],
+    [0x9E74D1B791E07E48, 0x775EA264CF55347E],
+    [0xC612062576589DDB, 0x95364AFE032A819D],
+    [0xF79687AED3EEC551, 0x3A83DDBD83F52205],
+    [0x9ABE14CD44753B53, 0xC4926A9672793543],
+    [0xC16D9A0095928A27, 0x75B7053C0F178294],
+    [0xF1C90080BAF72CB1, 0x5324C68B12DD6338],
+    [0x971DA05074DA7BEF, 0xD3F6FC16EBCA5E03],
+    [0xBCE5086492111AEB, 0x88F4BB1CA6BCF584],
+    [0xEC1E4A7DB69561A5, 0x2B31E9E3D06C32E5],
+    [0x9392EE8E921D5D07, 0x3AFF322E62439FCF],
+    [0xB877AA3236A4B449, 0x09BEFEB9FAD487C3],
+    [0xE69594BEC44DE15B, 0x4C2EBE687989A9B4],
+    [0x901D7CF73AB0ACD9, 0x0F9D37014BF60A10],
+    [0xB424DC35095CD80F, 0x538484C19EF38C94],
+    [0xE12E13424BB40E13, 0x2865A5F206B06FBA],
+    [0x8CBCCC096F5088CC, 0xF93F87B7442E45D4],
+    [0xAFEBFF0BCB24AAFF, 0xF78F69A51539D749],
+    [0xDBE6FECEBDEDD5BF, 0xB573440E5A884D1B],
+    [0x89705F4136B4A597, 0x31680A88F8953031],
+    [0xABCC77118461CEFD, 0xFDC20D2B36BA7C3D],
+    [0xD6BF94D5E57A42BC, 0x3D32907604691B4D],
+    [0x8637BD05AF6C69B6, 0xA63F9A49C2C1B110],
+    [0xA7C5AC471B478423, 0x0FCF80DC33721D54],
+    [0xD1B71758E219652C, 0xD3C36113404EA4A9],
+    [0x83126E978D4FDF3B, 0x645A1CAC083126E9],
+    [0xA3D70A3D70A3D70A, 0x3D70A3D70A3D70A4],
+    [0xCCCCCCCCCCCCCCCD, 0xCCCCCCCCCCCCCCCD],
+    [0x8000000000000000, 0x0000000000000000],
+    [0xA000000000000000, 0x0000000000000000],
+    [0xC800000000000000, 0x0000000000000000],
+    [0xFA00000000000000, 0x0000000000000000],
+    [0x9C40000000000000, 0x0000000000000000],
+    [0xC350000000000000, 0x0000000000000000],
+    [0xF424000000000000, 0x0000000000000000],
+    [0x9896800000000000, 0x0000000000000000],
+    [0xBEBC200000000000, 0x0000000000000000],
+    [0xEE6B280000000000, 0x0000000000000000],
+    [0x9502F90000000000, 0x0000000000000000],
+    [0xBA43B74000000000, 0x0000000000000000],
+    [0xE8D4A51000000000, 0x0000000000000000],
+    [0x9184E72A00000000, 0x0000000000000000],
+    [0xB5E620F480000000, 0x0000000000000000],
+    [0xE35FA931A0000000, 0x0000000000000000],
+    [0x8E1BC9BF04000000, 0x0000000000000000],
+    [0xB1A2BC2EC5000000, 0x0000000000000000],
+    [0xDE0B6B3A76400000, 0x0000000000000000],
+    [0x8AC7230489E80000, 0x0000000000000000],
+    [0xAD78EBC5AC620000, 0x0000000000000000],
+    [0xD8D726B7177A8000, 0x0000000000000000],
+    [0x878678326EAC9000, 0x0000000000000000],
+    [0xA968163F0A57B400, 0x0000000000000000],
+    [0xD3C21BCECCEDA100, 0x0000000000000000],
+    [0x84595161401484A0, 0x0000000000000000],
+    [0xA56FA5B99019A5C8, 0x0000000000000000],
+    [0xCECB8F27F4200F3A, 0x0000000000000000],
+    [0x813F3978F8940984, 0x4000000000000000],
+    [0xA18F07D736B90BE5, 0x5000000000000000],
+    [0xC9F2C9CD04674EDF, 0xA400000000000000],
+    [0xFC6F7C4045812296, 0x4D00000000000000],
+    [0x9DC5ADA82B70B59E, 0xF020000000000000],
+    [0xC5371912364CE305, 0x6C28000000000000],
+    [0xF684DF56C3E01BC7, 0xC732000000000000],
+    [0x9A130B963A6C115C, 0x3C7F400000000000],
+    [0xC097CE7BC90715B3, 0x4B9F100000000000],
+    [0xF0BDC21ABB48DB20, 0x1E86D40000000000],
+    [0x96769950B50D88F4, 0x1314448000000000],
+    [0xBC143FA4E250EB31, 0x17D955A000000000],
+    [0xEB194F8E1AE525FD, 0x5DCFAB0800000000],
+    [0x92EFD1B8D0CF37BE, 0x5AA1CAE500000000],
+    [0xB7ABC627050305AE, 0xF14A3D9E40000000],
+    [0xE596B7B0C643C719, 0x6D9CCD05D0000000],
+    [0x8F7E32CE7BEA5C70, 0xE4820023A2000000],
+    [0xB35DBF821AE4F38C, 0xDDA2802C8A800000],
+    [0xE0352F62A19E306F, 0xD50B2037AD200000],
+    [0x8C213D9DA502DE45, 0x4526F422CC340000],
+    [0xAF298D050E4395D7, 0x9670B12B7F410000],
+    [0xDAF3F04651D47B4C, 0x3C0CDD765F114000],
+    [0x88D8762BF324CD10, 0xA5880A69FB6AC800],
+    [0xAB0E93B6EFEE0054, 0x8EEA0D047A457A00],
+    [0xD5D238A4ABE98068, 0x72A4904598D6D880],
+    [0x85A36366EB71F041, 0x47A6DA2B7F864750],
+    [0xA70C3C40A64E6C52, 0x999090B65F67D924],
+    [0xD0CF4B50CFE20766, 0xFFF4B4E3F741CF6D],
+    [0x82818F1281ED44A0, 0xBFF8F10E7A8921A4],
+    [0xA321F2D7226895C8, 0xAFF72D52192B6A0D],
+    [0xCBEA6F8CEB02BB3A, 0x9BF4F8A69F764490],
+    [0xFEE50B7025C36A08, 0x02F236D04753D5B5],
+    [0x9F4F2726179A2245, 0x01D762422C946591],
+    [0xC722F0EF9D80AAD6, 0x424D3AD2B7B97EF5],
+    [0xF8EBAD2B84E0D58C, 0xD2E0898765A7DEB2],
+    [0x9B934C3B330C8577, 0x63CC55F49F88EB2F],
+    [0xC2781F49FFCFA6D5, 0x3CBF6B71C76B25FB],
+    [0xF316271C7FC3908B, 0x8BEF464E3945EF7A],
+    [0x97EDD871CFDA3A57, 0x97758BF0E3CBB5AC],
+    [0xBDE94E8E43D0C8EC, 0x3D52EEED1CBEA317],
+    [0xED63A231D4C4FB27, 0x4CA7AAA863EE4BDD],
+    [0x945E455F24FB1CF9, 0x8FE8CAA93E74EF6A],
+    [0xB975D6B6EE39E437, 0xB3E2FD538E122B45],
+    [0xE7D34C64A9C85D44, 0x60DBBCA87196B616],
+    [0x90E40FBEEA1D3A4B, 0xBC8955E946FE31CE],
+    [0xB51D13AEA4A488DD, 0x6BABAB6398BDBE41],
+    [0xE264589A4DCDAB15, 0xC696963C7EED2DD2],
+    [0x8D7EB76070A08AED, 0xFC1E1DE5CF543CA3],
+    [0xB0DE65388CC8ADA8, 0x3B25A55F43294BCC],
+    [0xDD15FE86AFFAD912, 0x49EF0EB713F39EBF],
+    [0x8A2DBF142DFCC7AB, 0x6E3569326C784337],
+    [0xACB92ED9397BF996, 0x49C2C37F07965405],
+    [0xD7E77A8F87DAF7FC, 0xDC33745EC97BE906],
+    [0x86F0AC99B4E8DAFD, 0x69A028BB3DED71A4],
+    [0xA8ACD7C0222311BD, 0xC40832EA0D68CE0D],
+    [0xD2D80DB02AABD62C, 0xF50A3FA490C30190],
+    [0x83C7088E1AAB65DB, 0x792667C6DA79E0FA],
+    [0xA4B8CAB1A1563F52, 0x577001B891185939],
+    [0xCDE6FD5E09ABCF27, 0xED4C0226B55E6F87],
+    [0x80B05E5AC60B6178, 0x544F8158315B05B4],
+    [0xA0DC75F1778E39D6, 0x696361AE3DB1C721],
+    [0xC913936DD571C84C, 0x03BC3A19CD1E38EA],
+    [0xFB5878494ACE3A5F, 0x04AB48A04065C724],
+    [0x9D174B2DCEC0E47B, 0x62EB0D64283F9C76],
+    [0xC45D1DF942711D9A, 0x3BA5D0BD324F8394],
+    [0xF5746577930D6501, 0xCA8F44EC7EE36479],
+    [0x9968BF6ABBE85F20, 0x7E998B13CF4E1ECC],
+    [0xBFC2EF456AE276E9, 0x9E3FEDD8C321A67F],
+    [0xEFB3AB16C59B14A3, 0xC5CFE94EF3EA101E],
+    [0x95D04AEE3B80ECE6, 0xBBA1F1D158724A13],
+    [0xBB445DA9CA61281F, 0x2A8A6E45AE8EDC98],
+    [0xEA1575143CF97227, 0xF52D09D71A3293BE],
+    [0x924D692CA61BE758, 0x593C2626705F9C56],
+    [0xB6E0C377CFA2E12E, 0x6F8B2FB00C77836C],
+    [0xE498F455C38B997A, 0x0B6DFB9C0F956447],
+    [0x8EDF98B59A373FEC, 0x4724BD4189BD5EAC],
+    [0xB2977EE300C50FE7, 0x58EDEC91EC2CB658],
+    [0xDF3D5E9BC0F653E1, 0x2F2967B66737E3ED],
+    [0x8B865B215899F46D, 0xBD79E0D20082EE74],
+    [0xAE67F1E9AEC07188, 0xECD8590680A3AA11],
+    [0xDA01EE641A708DEA, 0xE80E6F4820CC9496],
+    [0x884134FE908658B2, 0x3109058D147FDCDE],
+    [0xAA51823E34A7EEDF, 0xBD4B46F0599FD415],
+    [0xD4E5E2CDC1D1EA96, 0x6C9E18AC7007C91A],
+    [0x850FADC09923329E, 0x03E2CF6BC604DDB0],
+    [0xA6539930BF6BFF46, 0x84DB8346B786151D],
+    [0xCFE87F7CEF46FF17, 0xE612641865679A64],
+    [0x81F14FAE158C5F6E, 0x4FCB7E8F3F60C07E],
+    [0xA26DA3999AEF774A, 0xE3BE5E330F38F09E],
+    [0xCB090C8001AB551C, 0x5CADF5BFD3072CC5],
+    [0xFDCB4FA002162A63, 0x73D9732FC7C8F7F7],
+    [0x9E9F11C4014DDA7E, 0x2867E7FDDCDD9AFA],
+    [0xC646D63501A1511E, 0xB281E1FD541501B9],
+    [0xF7D88BC24209A565, 0x1F225A7CA91A4227],
+    [0x9AE757596946075F, 0x3375788DE9B06958],
+    [0xC1A12D2FC3978937, 0x0052D6B1641C83AE],
+    [0xF209787BB47D6B85, 0xC0678C5DBD23A49A],
+    [0x9745EB4D50CE6333, 0xF840B7BA963646E0],
+    [0xBD176620A501FC00, 0xB650E5A93BC3D898],
+    [0xEC5D3FA8CE427B00, 0xA3E51F138AB4CEBE],
+    [0x93BA47C980E98CE0, 0xC66F336C36B10137],
+    [0xB8A8D9BBE123F018, 0xB80B0047445D4185],
+    [0xE6D3102AD96CEC1E, 0xA60DC059157491E6],
+    [0x9043EA1AC7E41393, 0x87C89837AD68DB30],
+    [0xB454E4A179DD1877, 0x29BABE4598C311FC],
+    [0xE16A1DC9D8545E95, 0xF4296DD6FEF3D67B],
+    [0x8CE2529E2734BB1D, 0x1899E4A65F58660D],
+    [0xB01AE745B101E9E4, 0x5EC05DCFF72E7F90],
+    [0xDC21A1171D42645D, 0x76707543F4FA1F74],
+    [0x899504AE72497EBA, 0x6A06494A791C53A8],
+    [0xABFA45DA0EDBDE69, 0x0487DB9D17636892],
+    [0xD6F8D7509292D603, 0x45A9D2845D3C42B7],
+    [0x865B86925B9BC5C2, 0x0B8A2392BA45A9B2],
+    [0xA7F26836F282B733, 0x8E6CAC7768D7141F],
+    [0xD1EF0244AF2364FF, 0x3207D795430CD927],
+    [0x8335616AED761F1F, 0x7F44E6BD49E807B8],
+    [0xA402B9C5A8D3A6E7, 0x5F16206C9C6209A6],
+    [0xCD036837130890A1, 0x36DBA887C37A8C10],
+    [0x802221226BE55A65, 0xC2494954DA2C978A],
+    [0xA02AA96B06DEB0FE, 0xF2DB9BAA10B7BD6C],
+    [0xC83553C5C8965D3D, 0x6F92829494E5ACC7],
+    [0xFA42A8B73ABBF48D, 0xCB772339BA1F17F9],
+    [0x9C69A97284B578D8, 0xFF2A760414536EFC],
+    [0xC38413CF25E2D70E, 0xFEF5138519684ABB],
+    [0xF46518C2EF5B8CD1, 0x7EB258665FC25D69],
+    [0x98BF2F79D5993803, 0xEF2F773FFBD97A62],
+    [0xBEEEFB584AFF8604, 0xAAFB550FFACFD8FA],
+    [0xEEAABA2E5DBF6785, 0x95BA2A53F983CF39],
+    [0x952AB45CFA97A0B3, 0xDD945A747BF26184],
+    [0xBA756174393D88E0, 0x94F971119AEEF9E4],
+    [0xE912B9D1478CEB17, 0x7A37CD5601AAB85E],
+    [0x91ABB422CCB812EF, 0xAC62E055C10AB33B],
+    [0xB616A12B7FE617AA, 0x577B986B314D6009],
+    [0xE39C49765FDF9D95, 0xED5A7E85FDA0B80B],
+    [0x8E41ADE9FBEBC27D, 0x14588F13BE847307],
+    [0xB1D219647AE6B31C, 0x596EB2D8AE258FC9],
+    [0xDE469FBD99A05FE3, 0x6FCA5F8ED9AEF3BB],
+    [0x8AEC23D680043BEE, 0x25DE7BB9480D5855],
+    [0xADA72CCC20054AEA, 0xAF561AA79A10AE6A],
+    [0xD910F7FF28069DA4, 0x1B2BA1518094DA05],
+    [0x87AA9AFF79042287, 0x90FB44D2F05D0843],
+    [0xA99541BF57452B28, 0x353A1607AC744A54],
+    [0xD3FA922F2D1675F2, 0x42889B8997915CE9],
+    [0x847C9B5D7C2E09B7, 0x69956135FEBADA11],
+    [0xA59BC234DB398C25, 0x43FAB9837E699096],
+    [0xCF02B2C21207EF2F, 0x94F967E45E03F4BB],
+    [0x8161AFB94B44F57D, 0x1D1BE0EEBAC278F5],
+    [0xA1BA1BA79E1632DC, 0x6462D92A69731732],
+    [0xCA28A291859BBF93, 0x7D7B8F7503CFDCFF],
+    [0xFCB2CB35E702AF78, 0x5CDA735244C3D43F],
+    [0x9DEFBF01B061ADAB, 0x3A0888136AFA64A7],
+    [0xC56BAEC21C7A1916, 0x088AAA1845B8FDD1],
+    [0xF6C69A72A3989F5C, 0x8AAD549E57273D45],
+    [0x9A3C2087A63F6399, 0x36AC54E2F678864B],
+    [0xC0CB28A98FCF3C80, 0x84576A1BB416A7DE],
+    [0xF0FDF2D3F3C30B9F, 0x656D44A2A11C51D5],
+    [0x969EB7C47859E744, 0x9F644AE5A4B1B325],
+    [0xBC4665B596706115, 0x873D5D9F0DDE1FEF],
+    [0xEB57FF22FC0C795A, 0xA90CB506D155A7EA],
+    [0x9316FF75DD87CBD8, 0x09A7F12442D588F3],
+    [0xB7DCBF5354E9BECE, 0x0C11ED6D538AEB2F],
+    [0xE5D3EF282A242E82, 0x8F1668C8A86DA5FB],
+    [0x8FA475791A569D11, 0xF96E017D694487BD],
+    [0xB38D92D760EC4455, 0x37C981DCC395A9AC],
+    [0xE070F78D3927556B, 0x85BBE253F47B1417],
+    [0x8C469AB843B89563, 0x93956D7478CCEC8E],
+    [0xAF58416654A6BABB, 0x387AC8D1970027B2],
+    [0xDB2E51BFE9D0696A, 0x06997B05FCC0319F],
+    [0x88FCF317F22241E2, 0x441FECE3BDF81F03],
+    [0xAB3C2FDDEEAAD25B, 0xD527E81CAD7626C4],
+    [0xD60B3BD56A5586F2, 0x8A71E223D8D3B075],
+    [0x85C7056562757457, 0xF6872D5667844E49],
+    [0xA738C6BEBB12D16D, 0xB428F8AC016561DB],
+    [0xD106F86E69D785C8, 0xE13336D701BEBA52],
+    [0x82A45B450226B39D, 0xECC0024661173473],
+    [0xA34D721642B06084, 0x27F002D7F95D0190],
+    [0xCC20CE9BD35C78A5, 0x31EC038DF7B441F4],
+    [0xFF290242C83396CE, 0x7E67047175A15271],
+    [0x9F79A169BD203E41, 0x0F0062C6E984D387],
+    [0xC75809C42C684DD1, 0x52C07B78A3E60868],
+    [0xF92E0C3537826146, 0xA7709A56CCDF8A83],
+    [0x9BBCC7A142B17CCC, 0x88A66076400BB692],
+    [0xC2ABF989935DDBFE, 0x6ACFF893D00EA436],
+    [0xF356F7EBF83552FE, 0x0583F6B8C4124D43],
+    [0x98165AF37B2153DF, 0xC3727A337A8B704A],
+    [0xBE1BF1B059E9A8D6, 0x744F18C0592E4C5D],
+    [0xEDA2EE1C7064130C, 0x1162DEF06F79DF74],
+    [0x9485D4D1C63E8BE8, 0x8ADDCB5645AC2BA8],
+    [0xB9A74A0637CE2EE1, 0x6D953E2BD7173693],
+    [0xE8111C87C5C1BA9A, 0xC8FA8DB6CCDD0437],
+    [0x910AB1D4DB9914A0, 0x1D9C9892400A22A2],
+    [0xB54D5E4A127F59C8, 0x2503BEB6D00CAB4B],
+    [0xE2A0B5DC971F303A, 0x2E44AE64840FD61E],
+    [0x8DA471A9DE737E24, 0x5CEAECFED289E5D3],
+    [0xB10D8E1456105DAD, 0x7425A83E872C5F47],
+    [0xDD50F1996B947519, 0xD12F124E28F77719],
+    [0x8A5296FFE33CC930, 0x82BD6B70D99AAA70],
+    [0xACE73CBFDC0BFB7B, 0x636CC64D1001550C],
+    [0xD8210BEFD30EFA5A, 0x3C47F7E05401AA4F],
+    [0x8714A775E3E95C78, 0x65ACFAEC34810A71],
+    [0xA8D9D1535CE3B396, 0x7F1839A741A14D0D],
+    [0xD31045A8341CA07C, 0x1EDE48111209A051],
+    [0x83EA2B892091E44E, 0x934AED0AAB460432],
+    [0xA4E4B66B68B65D61, 0xF81DA84D5617853F],
+    [0xCE1DE40642E3F4B9, 0x36251260AB9D668F],
+    [0x80D2AE83E9CE78F4, 0xC1D72B7C6B426019],
+    [0xA1075A24E4421731, 0xB24CF65B8612F820],
+    [0xC94930AE1D529CFD, 0xDEE033F26797B628],
+    [0xFB9B7CD9A4A7443C, 0x169840EF017DA3B1],
+    [0x9D412E0806E88AA6, 0x8E1F289560EE864F],
+    [0xC491798A08A2AD4F, 0xF1A6F2BAB92A27E3],
+    [0xF5B5D7EC8ACB58A3, 0xAE10AF696774B1DB],
+    [0x9991A6F3D6BF1766, 0xACCA6DA1E0A8EF29],
+    [0xBFF610B0CC6EDD3F, 0x17FD090A58D32AF3],
+    [0xEFF394DCFF8A948F, 0xDDFC4B4CEF07F5B0],
+    [0x95F83D0A1FB69CD9, 0x4ABDAF101564F98E],
+    [0xBB764C4CA7A44410, 0x9D6D1AD41ABE37F2],
+    [0xEA53DF5FD18D5514, 0x84C86189216DC5EE],
+    [0x92746B9BE2F8552C, 0x32FD3CF5B4E49BB5],
+    [0xB7118682DBB66A77, 0x3FBC8C33221DC2A2],
+    [0xE4D5E82392A40515, 0x0FABAF3FEAA5334A],
+    [0x8F05B1163BA6832D, 0x29CB4D87F2A7400E],
+    [0xB2C71D5BCA9023F8, 0x743E20E9EF511012],
+    [0xDF78E4B2BD342CF7, 0x914DA9246B255417],
+    [0x8BAB8EEFB6409C1A, 0x1AD089B6C2F7548E],
+    [0xAE9672ABA3D0C321, 0xA184AC2473B529B2],
+    [0xDA3C0F568CC4F3E9, 0xC9E5D72D90A2741E],
+    [0x8865899617FB1871, 0x7E2FA67C7A658893],
+    [0xAA7EEBFB9DF9DE8E, 0xDDBB901B98FEEAB8],
+    [0xD51EA6FA85785631, 0x552A74227F3EA565],
+    [0x8533285C936B35DF, 0xD53A88958F87275F],
+    [0xA67FF273B8460357, 0x8A892ABAF368F137],
+    [0xD01FEF10A657842C, 0x2D2B7569B0432D85],
+    [0x8213F56A67F6B29C, 0x9C3B29620E29FC73],
+    [0xA298F2C501F45F43, 0x8349F3BA91B47B90],
+    [0xCB3F2F7642717713, 0x241C70A936219A74],
+    [0xFE0EFB53D30DD4D8, 0xED238CD383AA0111],
+    [0x9EC95D1463E8A507, 0xF4363804324A40AB],
+    [0xC67BB4597CE2CE49, 0xB143C6053EDCD0D5],
+    [0xF81AA16FDC1B81DB, 0xDD94B7868E94050A],
+    [0x9B10A4E5E9913129, 0xCA7CF2B4191C8327],
+    [0xC1D4CE1F63F57D73, 0xFD1C2F611F63A3F0],
+    [0xF24A01A73CF2DCD0, 0xBC633B39673C8CEC],
+    [0x976E41088617CA02, 0xD5BE0503E085D814],
+    [0xBD49D14AA79DBC82, 0x4B2D8644D8A74E19],
+    [0xEC9C459D51852BA3, 0xDDF8E7D60ED1219F],
+    [0x93E1AB8252F33B46, 0xCABB90E5C942B503],
+    [0xB8DA1662E7B00A17, 0x3D6A751F3B936244],
+    [0xE7109BFBA19C0C9D, 0x0CC512670A783AD5],
+    [0x906A617D450187E2, 0x27FB2B80668B24C5],
+    [0xB484F9DC9641E9DB, 0xB1F9F660802DEDF6],
+    [0xE1A63853BBD26451, 0x5E7873F8A0396974],
+    [0x8D07E33455637EB3, 0xDB0B487B6423E1E8],
+    [0xB049DC016ABC5E60, 0x91CE1A9A3D2CDA63],
+    [0xDC5C5301C56B75F7, 0x7641A140CC7810FB],
+    [0x89B9B3E11B6329BB, 0xA9E904C87FCB0A9D],
+    [0xAC2820D9623BF429, 0x546345FA9FBDCD44],
+    [0xD732290FBACAF134, 0xA97C177947AD4095],
+    [0x867F59A9D4BED6C0, 0x49ED8EABCCCC485D],
+    [0xA81F301449EE8C70, 0x5C68F256BFFF5A75],
+    [0xD226FC195C6A2F8C, 0x73832EEC6FFF3112],
+    [0x83585D8FD9C25DB8, 0xC831FD53C5FF7EAB],
+    [0xA42E74F3D032F526, 0xBA3E7CA8B77F5E56],
+    [0xCD3A1230C43FB26F, 0x28CE1BD2E55F35EB],
+    [0x80444B5E7AA7CF85, 0x7980D163CF5B81B3],
+    [0xA0555E361951C367, 0xD7E105BCC3326220],
+    [0xC86AB5C39FA63441, 0x8DD9472BF3FEFAA8],
+    [0xFA856334878FC151, 0xB14F98F6F0FEB952],
+    [0x9C935E00D4B9D8D2, 0x6ED1BF9A569F33D3],
+    [0xC3B8358109E84F07, 0x0A862F80EC4700C8],
+    [0xF4A642E14C6262C9, 0xCD27BB612758C0FA],
+    [0x98E7E9CCCFBD7DBE, 0x8038D51CB897789C],
+    [0xBF21E44003ACDD2D, 0xE0470A63E6BD56C3],
+    [0xEEEA5D5004981478, 0x1858CCFCE06CAC74],
+    [0x95527A5202DF0CCB, 0x0F37801E0C43EBC9],
+    [0xBAA718E68396CFFE, 0xD30560258F54E6BB],
+    [0xE950DF20247C83FD, 0x47C6B82EF32A2069],
+    [0x91D28B7416CDD27E, 0x4CDC331D57FA5442],
+    [0xB6472E511C81471E, 0xE0133FE4ADF8E952],
+    [0xE3D8F9E563A198E5, 0x58180FDDD97723A7],
+    [0x8E679C2F5E44FF8F, 0x570F09EAA7EA7648],
+    [0xB201833B35D63F73, 0x2CD2CC6551E513DA],
+    [0xDE81E40A034BCF50, 0xF8077F7EA65E58D1],
+    [0x8B112E86420F6192, 0xFB04AFAF27FAF783],
+    [0xADD57A27D29339F6, 0x79C5DB9AF1F9B563],
+    [0xD94AD8B1C7380874, 0x18375281AE7822BC],
+    [0x87CEC76F1C830549, 0x8F2293910D0B15B6],
+    [0xA9C2794AE3A3C69B, 0xB2EB3875504DDB23],
+    [0xD433179D9C8CB841, 0x5FA60692A46151EC],
+    [0x849FEEC281D7F329, 0xDBC7C41BA6BCD333],
+    [0xA5C7EA73224DEFF3, 0x12B9B522906C0800],
+    [0xCF39E50FEAE16BF0, 0xD768226B34870A00],
+    [0x81842F29F2CCE376, 0xE6A1158300D46640],
+    [0xA1E53AF46F801C53, 0x60495AE3C1097FD0],
+    [0xCA5E89B18B602368, 0x385BB19CB14BDFC4],
+    [0xFCF62C1DEE382C42, 0x46729E03DD9ED7B5],
+    [0x9E19DB92B4E31BA9, 0x6C07A2C26A8346D1],
+    [0xC5A05277621BE294, 0xC7098B7305241886],
+    [0xF70867153AA2DB39, 0xB8CBEE4FC66D1EA7],
+    [0x9A65406D44A5C903, 0x737F74F1DC043328],
+    [0xC0FE908895CF3B44, 0x505F522E53053FF2],
+    [0xF13E34AABB430A15, 0x647726B9E7C68FEF],
+    [0x96C6E0EAB509E64D, 0x5ECA783430DC19F5],
+    [0xBC789925624C5FE1, 0xB67D16413D132073],
+    [0xEB96BF6EBADF77D9, 0xE41C5BD18C57E88F],
+    [0x933E37A534CBAAE8, 0x8E91B962F7B6F15A],
+    [0xB80DC58E81FE95A1, 0x723627BBB5A4ADB0],
+    [0xE61136F2227E3B0A, 0xCEC3B1AAA30DD91C],
+    [0x8FCAC257558EE4E6, 0x213A4F0AA5E8A7B2],
+    [0xB3BD72ED2AF29E20, 0xA988E2CD4F62D19E],
+    [0xE0ACCFA875AF45A8, 0x93EB1B80A33B8605],
+    [0x8C6C01C9498D8B89, 0xBC72F130660533C3],
+    [0xAF87023B9BF0EE6B, 0xEB8FAD7C7F8680B4],
+    [0xDB68C2CA82ED2A06, 0xA67398DB9F6820E1],
+    [0x892179BE91D43A44, 0x88083F8943A1148D],
+    [0xAB69D82E364948D4, 0x6A0A4F6B948959B0],
+    [0xD6444E39C3DB9B0A, 0x848CE34679ABB01C],
+    [0x85EAB0E41A6940E6, 0xF2D80E0C0C0B4E12],
+    [0xA7655D1D2103911F, 0x6F8E118F0F0E2196],
+    [0xD13EB46469447567, 0x4B7195F2D2D1A9FB],
+    [0x82C730BEC1CAC961, 0x8F26FDB7C3C30A3D],
+    [0xA378FCEE723D7BB9, 0xB2F0BD25B4B3CCCC],
+    [0xCC573C2A0ECCDAA7, 0xDFACEC6F21E0C000],
+    [0xFF6D0B3492801151, 0x9798278AEA58EFFF],
+    [0x9FA42700DB900AD2, 0x5EBF18B6D2779600],
+    [0xC78D30C112740D87, 0xF66EDEE487157B80],
+    [0xF9707CF1571110E9, 0xB40A969DA8DADA5F],
+    [0x9BE64E16D66AAA91, 0x70869E228988C87C],
+    [0xC2DFE19C8C055536, 0xCCA845AB2BEAFA9B],
+    [0xF397DA03AF06AA83, 0x3FD25715F6E5B941],
+    [0x983EE8424D642A92, 0x07E3766DBA4F93C9],
+    [0xBE4EA252E0BD3537, 0x89DC540928E378BB],
+    [0xEDE24AE798EC8284, 0x2C53690B731C56EA],
+    [0x94AD6ED0BF93D193, 0x9BB421A727F1B652],
+    [0xB9D8CA84EF78C5F7, 0x42A12A10F1EE23E7],
+    [0xE84EFD262B56F775, 0x134974952E69ACE0],
+    [0x91315E37DB165AA9, 0x2C0DE8DD3D020C0C],
+    [0xB57DB5C5D1DBF153, 0x771163148C428F0F],
+    [0xE2DD23374652EDA8, 0x54D5BBD9AF5332D3],
+    [0x8DCA36028BF3D489, 0x350595680D93FFC4],
+    [0xB13CC3832EF0C9AC, 0x8246FAC210F8FFB5],
+    [0xDD8BF463FAACFC16, 0x62D8B97295373FA2],
+    [0x8A7778BE7CAC1D8E, 0xFDC773E79D4287C5],
+    [0xAD1556EE1BD724F1, 0x7D3950E1849329B7],
+    [0xD85AACA9A2CCEE2E, 0xDC87A519E5B7F424],
+    [0x8738ABEA05C014DD, 0xA9D4C7302F92F897],
+    [0xA906D6E487301A14, 0xD449F8FC3B77B6BC],
+    [0xD3488C9DA8FC2099, 0xC95C773B4A55A46B],
+    [0x840D57E2899D945F, 0x7DD9CA850E7586C3],
+    [0xA510ADDB2C04F977, 0x5D503D265212E874],
+    [0xCE54D951F70637D5, 0x34A44C6FE697A291],
+    [0x80F507D33A63E2E5, 0x40E6AFC5F01EC59B],
+    [0xA13249C808FCDB9F, 0x91205BB76C267701],
+    [0xC97EDC3A0B3C1286, 0x356872A5473014C1],
+    [0xFBDE93488E0B1728, 0xC2C28F4E98FC19F2],
+    [0x9D6B1C0D58C6EE79, 0xD9B999911F9D9037],
+    [0xC4C5E310AEF8AA17, 0x1027FFF56784F445],
+    [0xF5F75BD4DAB6D49D, 0xD431FFF2C1663156],
+    [0x99BA996508B244E2, 0x049F3FF7B8DFDED6],
+    [0xC0293FBE4ADED61B, 0x85C70FF5A717D68B],
+    [0xF0338FADDD968BA1, 0x2738D3F310DDCC2E],
+    [0x962039CCAA7E1745, 0xB8838477EA8A9F9D],
+    [0xBBA8483FD51D9D16, 0xE6A46595E52D4784],
+    [0xEA925A4FCA65045B, 0x604D7EFB5E789965],
+    [0x929B7871DE7F22B9, 0x1C306F5D1B0B5FDF],
+    [0xB742568E561EEB67, 0x633C8B3461CE37D7],
+    [0xE512EC31EBA6A641, 0x3C0BAE017A41C5CD],
+    [0x8F2BD39F334827E9, 0xC5874CC0EC691BA0],
+    [0xB2F6C887001A31E3, 0xF6E91FF127836288],
+    [0xDFB47AA8C020BE5C, 0xB4A367ED71643B2A],
+    [0x8BD0CCA9781476F9, 0x50E620F466DEA4FA],
+    [0xAEC4FFD3D61994B8, 0xA51FA93180964E39],
+    [0xDA763FC8CB9FF9E6, 0x8E67937DE0BBE1C7],
+    [0x8889E7DD7F43FC2F, 0x7900BC2EAC756D1C],
+    [0xAAAC61D4DF14FB3B, 0x5740EB3A5792C863],
+    [0xD5577A4A16DA3A0A, 0x2D112608ED777A7C],
+    [0x8556AC6E4E486446, 0x5C2AB7C5946AAC8E],
+    [0xA6AC5789E1DA7D58, 0xF33565B6F98557B1],
+    [0xD0576D6C5A511CAE, 0xF002BF24B7E6AD9D],
+    [0x8236A463B872B1ED, 0xB601B776F2F02C82],
+    [0xA2C44D7CA68F5E68, 0xE3822554AFAC37A3],
+    [0xCB7560DBD0333602, 0xDC62AEA9DB97458C],
+    [0xFE52B912C4400382, 0x537B5A54527D16EE],
+    [0x9EF3B3ABBAA80231, 0x742D1874B38E2E55],
+    [0xC6B0A096A95202BE, 0xD1385E91E071B9EA],
+    [0xF85CC8BC53A6836D, 0x45867636588E2865],
+    [0x9B39FD75B4481224, 0x4B7409E1F758D93F],
+    [0xC2087CD3215A16AD, 0x5E510C5A752F0F8F],
+    [0xF28A9C07E9B09C59, 0xB5E54F71127AD373],
+    [0x9796A184F20E61B7, 0x71AF51A6AB8CC428],
+    [0xBD7C49E62E91FA25, 0x4E1B2610566FF531],
+    [0xECDB5C5FBA3678AF, 0xA1A1EF946C0BF27E],
+    [0x940919BBD4620B6D, 0x250535BCC387778F],
+    [0xB90B602AC97A8E48, 0x6E46832BF4695572],
+    [0xE74E38357BD931DB, 0x89D823F6F183AACF],
+    [0x9090E3216D67BF29, 0x9627167A56F24AC1],
+    [0xB4B51BE9C8C1AEF3, 0xBBB0DC18ECAEDD72],
+    [0xE1E262E43AF21AAF, 0x6A9D131F27DA94CE],
+    [0x8D2D7DCEA4D750AE, 0xA2A22BF378E89D01],
+    [0xB078DD424E0D24D9, 0x0B4AB6F05722C441],
+    [0xDC971492E1906E0F, 0x4E1D64AC6CEB7551],
+    [0x89DE6CDBCCFA44CA, 0x90D25EEBC4132953],
+    [0xAC560812C038D5FC, 0xF506F6A6B517F3A7],
+    [0xD76B8A1770470B7B, 0xF248B450625DF091],
+    [0x86A3364EA62C672D, 0xD76D70B23D7AB65B],
+    [0xA84C03E24FB780F8, 0x0D48CCDECCD963F2],
+    [0xD25F04DAE3A56136, 0x109B0016800FBCEE],
+    [0x837B6308CE475CC2, 0xCA60E00E1009D615],
+    [0xA45A3BCB01D933F2, 0x3CF91811940C4B9A],
+    [0xCD70CABDC24F80EF, 0xCC375E15F90F5E80],
+    [0x80667EB69971B095, 0x3FA29ACDBBA99B10],
+    [0xA0801E643FCE1CBB, 0x8F8B41812A9401D4],
+    [0xC8A025FD4FC1A3E9, 0x336E11E175390249],
+    [0xFAC82F7CA3B20CE4, 0x80499659D28742DC],
+    [0x9CBD1DADE64F480E, 0x302DFDF8239489C9],
+    [0xC3EC65195FE31A12, 0xBC397D762C79AC3C],
+    [0xF4E77E5FB7DBE096, 0x2B47DCD3B798174B],
+    [0x9910AEFBD2E96C5E, 0xDB0CEA0452BF0E8F],
+    [0xBF54DABAC7A3C775, 0x51D02485676ED232],
+    [0xEF2A1169798CB953, 0xA6442DA6C14A86BF],
+    [0x957A4AE1EBF7F3D4, 0xA7EA9C8838CE9437],
+    [0xBAD8DD9A66F5F0C9, 0x91E543AA47023945],
+    [0xE98F150100B36CFB, 0xB65E9494D8C2C796],
+    [0x91F96D20A070241D, 0xB1FB1CDD0779BCBE],
+    [0xB677C868C88C2D24, 0xDE79E41449582BED],
+    [0xE415BA82FAAF386D, 0xD6185D195BAE36E9],
+    [0x8E8D9491DCAD8344, 0x05CF3A2FD94CE251],
+    [0xB230F9B653D8E415, 0x074308BBCFA01AE6],
+    [0xDEBD3823E8CF1D1A, 0x4913CAEAC388219F],
+    [0x8B36431671817230, 0x6DAC5ED2BA351504],
+    [0xAE03D3DC0DE1CEBD, 0x8917768768C25A44],
+    [0xD984C8D3115A426C, 0xAB5D542942F2F0D6],
+    [0x87F2FD83EAD86983, 0x4B1A5499C9D7D685],
+    [0xA9EFBCE4E58E83E4, 0x1DE0E9C03C4DCC27],
+    [0xD46BAC1E1EF224DD, 0x255924304B613F31],
+    [0x84C34B92D357570A, 0x3757B69E2F1CC77E],
+    [0xA5F41E77882D2CCD, 0xC52DA445BAE3F95E],
+    [0xCF7126156A387800, 0xF6790D57299CF7B5],
+    [0x81A6B7CD62634B00, 0xFA0BA8567A021AD1],
+    [0xA21065C0BAFC1DC0, 0xF88E926C1882A186],
+    [0xCA947F30E9BB2530, 0xF6B237071EA349E7],
+    [0xFD399EFD2429EE7C, 0xF45EC4C8E64C1C61],
+    [0x9E44035E369A350D, 0x78BB3AFD8FEF91BD],
+    [0xC5D50435C440C251, 0xD6EA09BCF3EB762C],
+    [0xF74A45433550F2E5, 0x0CA48C2C30E653B7],
+    [0x9A8E6B4A015297CF, 0x27E6D79B9E8FF452],
+    [0xC132061C81A73DC3, 0xF1E08D828633F167],
+    [0xF17E87A3A2110D34, 0xAE58B0E327C0EDC0],
+    [0x96EF14C6454AA840, 0x4CF76E8DF8D89498],
+    [0xBCAAD9F7D69D5250, 0x60354A31770EB9BE],
+    [0xEBD59075CC44A6E4, 0x78429CBDD4D2682E],
+    [0x93657A499FAAE84F, 0xCB29A1F6A503811D],
+    [0xB83ED8DC0795A262, 0x7DF40A744E446164],
+    [0xE64E8F13097B0AFB, 0x1D710D1161D579BD],
+    [0x8FF1196BE5ECE6DD, 0xF266A82ADD256C16],
+    [0xB3ED5FC6DF682094, 0x2F005235946EC71C],
+    [0xE0E8B7B8974228B9, 0x3AC066C2F98A78E2],
+    [0x8C9172D35E895974, 0xC4B84039DBF68B8E],
+    [0xAFB5CF88362BAFD1, 0xB5E6504852F42E71],
+    [0xDBA3436A43B69BC5, 0xE35FE45A67B13A0D],
+    [0x89460A226A52215B, 0x0E1BEEB880CEC448],
+    [0xAB978CAB04E6A9B2, 0xD1A2EA66A102755A],
+    [0xD67D6FD5C620541E, 0x460BA500494312B1],
+    [0x860E65E59BD43493, 0xEBC747202DC9EBAF],
+    [0xA791FF5F02C941B8, 0xA6B918E8393C669A],
+    [0xD1767F36C37B9226, 0x90675F22478B8041],
+    [0x82EA0F823A2D3B57, 0x7A409B756CB73028],
+    [0xA3A49362C8B88A2D, 0x58D0C252C7E4FC33],
+    [0xCC8DB83B7AE6ACB9, 0xAF04F2E779DE3B3F],
+    [0xFFB1264A59A057E7, 0xDAC62FA15855CA0F],
+    [0x9FCEB7EE780436F0, 0x48BBDDC4D7359E49],
+    [0xC7C265EA160544AC, 0x5AEAD5360D0305DC],
+    [0xF9B2FF649B8695D7, 0x71A58A839043C753],
+    [0x9C0FDF9EE1341DA7, 0xA70776923A2A5C94],
+    [0xC313D78699812510, 0x50C95436C8B4F3B9],
+    [0xF3D8CD683FE16E54, 0x64FBA9447AE230A7],
+    [0x9867806127ECE4F5, 0xBF1D49CACCCD5E68],
+    [0xBE81607971E81E32, 0xEEE49C3D8000B602],
+    [0xEE21B897CE6225BE, 0x6A9DC34CE000E383],
+    [0x94D5135EE0FD5797, 0x02A29A100C008E32],
+    [0xBA0A5836993CAD7D, 0xC34B40940F00B1BE],
+    [0xE88CEE443F8BD8DC, 0xF41E10B912C0DE2E],
+    [0x915814EAA7B76789, 0x7892CA73ABB88ADD],
+    [0xB5AE1A2551A5416C, 0xD6B77D1096A6AD94],
+    [0xE319A0AEA60E91C7, 0xCC655C54BC5058F9],
+];
+
+// 
+static immutable short[1025] p10_exponents = [
+    -1764,
+    -1761,
+    -1758,
+    -1754,
+    -1751,
+    -1748,
+    -1744,
+    -1741,
+    -1738,
+    -1734,
+    -1731,
+    -1728,
+    -1724,
+    -1721,
+    -1718,
+    -1714,
+    -1711,
+    -1708,
+    -1705,
+    -1701,
+    -1698,
+    -1695,
+    -1691,
+    -1688,
+    -1685,
+    -1681,
+    -1678,
+    -1675,
+    -1671,
+    -1668,
+    -1665,
+    -1661,
+    -1658,
+    -1655,
+    -1651,
+    -1648,
+    -1645,
+    -1641,
+    -1638,
+    -1635,
+    -1631,
+    -1628,
+    -1625,
+    -1621,
+    -1618,
+    -1615,
+    -1612,
+    -1608,
+    -1605,
+    -1602,
+    -1598,
+    -1595,
+    -1592,
+    -1588,
+    -1585,
+    -1582,
+    -1578,
+    -1575,
+    -1572,
+    -1568,
+    -1565,
+    -1562,
+    -1558,
+    -1555,
+    -1552,
+    -1548,
+    -1545,
+    -1542,
+    -1538,
+    -1535,
+    -1532,
+    -1528,
+    -1525,
+    -1522,
+    -1519,
+    -1515,
+    -1512,
+    -1509,
+    -1505,
+    -1502,
+    -1499,
+    -1495,
+    -1492,
+    -1489,
+    -1485,
+    -1482,
+    -1479,
+    -1475,
+    -1472,
+    -1469,
+    -1465,
+    -1462,
+    -1459,
+    -1455,
+    -1452,
+    -1449,
+    -1445,
+    -1442,
+    -1439,
+    -1435,
+    -1432,
+    -1429,
+    -1425,
+    -1422,
+    -1419,
+    -1416,
+    -1412,
+    -1409,
+    -1406,
+    -1402,
+    -1399,
+    -1396,
+    -1392,
+    -1389,
+    -1386,
+    -1382,
+    -1379,
+    -1376,
+    -1372,
+    -1369,
+    -1366,
+    -1362,
+    -1359,
+    -1356,
+    -1352,
+    -1349,
+    -1346,
+    -1342,
+    -1339,
+    -1336,
+    -1332,
+    -1329,
+    -1326,
+    -1323,
+    -1319,
+    -1316,
+    -1313,
+    -1309,
+    -1306,
+    -1303,
+    -1299,
+    -1296,
+    -1293,
+    -1289,
+    -1286,
+    -1283,
+    -1279,
+    -1276,
+    -1273,
+    -1269,
+    -1266,
+    -1263,
+    -1259,
+    -1256,
+    -1253,
+    -1249,
+    -1246,
+    -1243,
+    -1239,
+    -1236,
+    -1233,
+    -1229,
+    -1226,
+    -1223,
+    -1220,
+    -1216,
+    -1213,
+    -1210,
+    -1206,
+    -1203,
+    -1200,
+    -1196,
+    -1193,
+    -1190,
+    -1186,
+    -1183,
+    -1180,
+    -1176,
+    -1173,
+    -1170,
+    -1166,
+    -1163,
+    -1160,
+    -1156,
+    -1153,
+    -1150,
+    -1146,
+    -1143,
+    -1140,
+    -1136,
+    -1133,
+    -1130,
+    -1127,
+    -1123,
+    -1120,
+    -1117,
+    -1113,
+    -1110,
+    -1107,
+    -1103,
+    -1100,
+    -1097,
+    -1093,
+    -1090,
+    -1087,
+    -1083,
+    -1080,
+    -1077,
+    -1073,
+    -1070,
+    -1067,
+    -1063,
+    -1060,
+    -1057,
+    -1053,
+    -1050,
+    -1047,
+    -1043,
+    -1040,
+    -1037,
+    -1034,
+    -1030,
+    -1027,
+    -1024,
+    -1020,
+    -1017,
+    -1014,
+    -1010,
+    -1007,
+    -1004,
+    -1000,
+    -997,
+    -994,
+    -990,
+    -987,
+    -984,
+    -980,
+    -977,
+    -974,
+    -970,
+    -967,
+    -964,
+    -960,
+    -957,
+    -954,
+    -950,
+    -947,
+    -944,
+    -940,
+    -937,
+    -934,
+    -931,
+    -927,
+    -924,
+    -921,
+    -917,
+    -914,
+    -911,
+    -907,
+    -904,
+    -901,
+    -897,
+    -894,
+    -891,
+    -887,
+    -884,
+    -881,
+    -877,
+    -874,
+    -871,
+    -867,
+    -864,
+    -861,
+    -857,
+    -854,
+    -851,
+    -847,
+    -844,
+    -841,
+    -838,
+    -834,
+    -831,
+    -828,
+    -824,
+    -821,
+    -818,
+    -814,
+    -811,
+    -808,
+    -804,
+    -801,
+    -798,
+    -794,
+    -791,
+    -788,
+    -784,
+    -781,
+    -778,
+    -774,
+    -771,
+    -768,
+    -764,
+    -761,
+    -758,
+    -754,
+    -751,
+    -748,
+    -744,
+    -741,
+    -738,
+    -735,
+    -731,
+    -728,
+    -725,
+    -721,
+    -718,
+    -715,
+    -711,
+    -708,
+    -705,
+    -701,
+    -698,
+    -695,
+    -691,
+    -688,
+    -685,
+    -681,
+    -678,
+    -675,
+    -671,
+    -668,
+    -665,
+    -661,
+    -658,
+    -655,
+    -651,
+    -648,
+    -645,
+    -642,
+    -638,
+    -635,
+    -632,
+    -628,
+    -625,
+    -622,
+    -618,
+    -615,
+    -612,
+    -608,
+    -605,
+    -602,
+    -598,
+    -595,
+    -592,
+    -588,
+    -585,
+    -582,
+    -578,
+    -575,
+    -572,
+    -568,
+    -565,
+    -562,
+    -558,
+    -555,
+    -552,
+    -549,
+    -545,
+    -542,
+    -539,
+    -535,
+    -532,
+    -529,
+    -525,
+    -522,
+    -519,
+    -515,
+    -512,
+    -509,
+    -505,
+    -502,
+    -499,
+    -495,
+    -492,
+    -489,
+    -485,
+    -482,
+    -479,
+    -475,
+    -472,
+    -469,
+    -465,
+    -462,
+    -459,
+    -455,
+    -452,
+    -449,
+    -446,
+    -442,
+    -439,
+    -436,
+    -432,
+    -429,
+    -426,
+    -422,
+    -419,
+    -416,
+    -412,
+    -409,
+    -406,
+    -402,
+    -399,
+    -396,
+    -392,
+    -389,
+    -386,
+    -382,
+    -379,
+    -376,
+    -372,
+    -369,
+    -366,
+    -362,
+    -359,
+    -356,
+    -353,
+    -349,
+    -346,
+    -343,
+    -339,
+    -336,
+    -333,
+    -329,
+    -326,
+    -323,
+    -319,
+    -316,
+    -313,
+    -309,
+    -306,
+    -303,
+    -299,
+    -296,
+    -293,
+    -289,
+    -286,
+    -283,
+    -279,
+    -276,
+    -273,
+    -269,
+    -266,
+    -263,
+    -259,
+    -256,
+    -253,
+    -250,
+    -246,
+    -243,
+    -240,
+    -236,
+    -233,
+    -230,
+    -226,
+    -223,
+    -220,
+    -216,
+    -213,
+    -210,
+    -206,
+    -203,
+    -200,
+    -196,
+    -193,
+    -190,
+    -186,
+    -183,
+    -180,
+    -176,
+    -173,
+    -170,
+    -166,
+    -163,
+    -160,
+    -157,
+    -153,
+    -150,
+    -147,
+    -143,
+    -140,
+    -137,
+    -133,
+    -130,
+    -127,
+    -123,
+    -120,
+    -117,
+    -113,
+    -110,
+    -107,
+    -103,
+    -100,
+    -97,
+    -93,
+    -90,
+    -87,
+    -83,
+    -80,
+    -77,
+    -73,
+    -70,
+    -67,
+    -63,
+    -60,
+    -57,
+    -54,
+    -50,
+    -47,
+    -44,
+    -40,
+    -37,
+    -34,
+    -30,
+    -27,
+    -24,
+    -20,
+    -17,
+    -14,
+    -10,
+    -7,
+    -4,
+    0,
+    3,
+    6,
+    10,
+    13,
+    16,
+    20,
+    23,
+    26,
+    30,
+    33,
+    36,
+    39,
+    43,
+    46,
+    49,
+    53,
+    56,
+    59,
+    63,
+    66,
+    69,
+    73,
+    76,
+    79,
+    83,
+    86,
+    89,
+    93,
+    96,
+    99,
+    103,
+    106,
+    109,
+    113,
+    116,
+    119,
+    123,
+    126,
+    129,
+    132,
+    136,
+    139,
+    142,
+    146,
+    149,
+    152,
+    156,
+    159,
+    162,
+    166,
+    169,
+    172,
+    176,
+    179,
+    182,
+    186,
+    189,
+    192,
+    196,
+    199,
+    202,
+    206,
+    209,
+    212,
+    216,
+    219,
+    222,
+    226,
+    229,
+    232,
+    235,
+    239,
+    242,
+    245,
+    249,
+    252,
+    255,
+    259,
+    262,
+    265,
+    269,
+    272,
+    275,
+    279,
+    282,
+    285,
+    289,
+    292,
+    295,
+    299,
+    302,
+    305,
+    309,
+    312,
+    315,
+    319,
+    322,
+    325,
+    328,
+    332,
+    335,
+    338,
+    342,
+    345,
+    348,
+    352,
+    355,
+    358,
+    362,
+    365,
+    368,
+    372,
+    375,
+    378,
+    382,
+    385,
+    388,
+    392,
+    395,
+    398,
+    402,
+    405,
+    408,
+    412,
+    415,
+    418,
+    422,
+    425,
+    428,
+    431,
+    435,
+    438,
+    441,
+    445,
+    448,
+    451,
+    455,
+    458,
+    461,
+    465,
+    468,
+    471,
+    475,
+    478,
+    481,
+    485,
+    488,
+    491,
+    495,
+    498,
+    501,
+    505,
+    508,
+    511,
+    515,
+    518,
+    521,
+    524,
+    528,
+    531,
+    534,
+    538,
+    541,
+    544,
+    548,
+    551,
+    554,
+    558,
+    561,
+    564,
+    568,
+    571,
+    574,
+    578,
+    581,
+    584,
+    588,
+    591,
+    594,
+    598,
+    601,
+    604,
+    608,
+    611,
+    614,
+    617,
+    621,
+    624,
+    627,
+    631,
+    634,
+    637,
+    641,
+    644,
+    647,
+    651,
+    654,
+    657,
+    661,
+    664,
+    667,
+    671,
+    674,
+    677,
+    681,
+    684,
+    687,
+    691,
+    694,
+    697,
+    701,
+    704,
+    707,
+    711,
+    714,
+    717,
+    720,
+    724,
+    727,
+    730,
+    734,
+    737,
+    740,
+    744,
+    747,
+    750,
+    754,
+    757,
+    760,
+    764,
+    767,
+    770,
+    774,
+    777,
+    780,
+    784,
+    787,
+    790,
+    794,
+    797,
+    800,
+    804,
+    807,
+    810,
+    813,
+    817,
+    820,
+    823,
+    827,
+    830,
+    833,
+    837,
+    840,
+    843,
+    847,
+    850,
+    853,
+    857,
+    860,
+    863,
+    867,
+    870,
+    873,
+    877,
+    880,
+    883,
+    887,
+    890,
+    893,
+    897,
+    900,
+    903,
+    907,
+    910,
+    913,
+    916,
+    920,
+    923,
+    926,
+    930,
+    933,
+    936,
+    940,
+    943,
+    946,
+    950,
+    953,
+    956,
+    960,
+    963,
+    966,
+    970,
+    973,
+    976,
+    980,
+    983,
+    986,
+    990,
+    993,
+    996,
+    1000,
+    1003,
+    1006,
+    1009,
+    1013,
+    1016,
+    1019,
+    1023,
+    1026,
+    1029,
+    1033,
+    1036,
+    1039,
+    1043,
+    1046,
+    1049,
+    1053,
+    1056,
+    1059,
+    1063,
+    1066,
+    1069,
+    1073,
+    1076,
+    1079,
+    1083,
+    1086,
+    1089,
+    1093,
+    1096,
+    1099,
+    1102,
+    1106,
+    1109,
+    1112,
+    1116,
+    1119,
+    1122,
+    1126,
+    1129,
+    1132,
+    1136,
+    1139,
+    1142,
+    1146,
+    1149,
+    1152,
+    1156,
+    1159,
+    1162,
+    1166,
+    1169,
+    1172,
+    1176,
+    1179,
+    1182,
+    1186,
+    1189,
+    1192,
+    1196,
+    1199,
+    1202,
+    1205,
+    1209,
+    1212,
+    1215,
+    1219,
+    1222,
+    1225,
+    1229,
+    1232,
+    1235,
+    1239,
+    1242,
+    1245,
+    1249,
+    1252,
+    1255,
+    1259,
+    1262,
+    1265,
+    1269,
+    1272,
+    1275,
+    1279,
+    1282,
+    1285,
+    1289,
+    1292,
+    1295,
+    1298,
+    1302,
+    1305,
+    1308,
+    1312,
+    1315,
+    1318,
+    1322,
+    1325,
+    1328,
+    1332,
+    1335,
+    1338,
+    1342,
+    1345,
+    1348,
+    1352,
+    1355,
+    1358,
+    1362,
+    1365,
+    1368,
+    1372,
+    1375,
+    1378,
+    1382,
+    1385,
+    1388,
+    1392,
+    1395,
+    1398,
+    1401,
+    1405,
+    1408,
+    1411,
+    1415,
+    1418,
+    1421,
+    1425,
+    1428,
+    1431,
+    1435,
+    1438,
+    1441,
+    1445,
+    1448,
+    1451,
+    1455,
+    1458,
+    1461,
+    1465,
+    1468,
+    1471,
+    1475,
+    1478,
+    1481,
+    1485,
+    1488,
+    1491,
+    1494,
+    1498,
+    1501,
+    1504,
+    1508,
+    1511,
+    1514,
+    1518,
+    1521,
+    1524,
+    1528,
+    1531,
+    1534,
+    1538,
+    1541,
+    1544,
+    1548,
+    1551,
+    1554,
+    1558,
+    1561,
+    1564,
+    1568,
+    1571,
+    1574,
+    1578,
+    1581,
+    1584,
+    1587,
+    1591,
+    1594,
+    1597,
+    1601,
+    1604,
+    1607,
+    1611,
+    1614,
+    1617,
+    1621,
+    1624,
+    1627,
+    1631,
+    1634,
+    1637,
+];
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
new file mode 100644
index 00000000..a8b210be
--- /dev/null
+++ b/source/mir/bignum/low_level_view.d
@@ -0,0 +1,2198 @@
+/++
+Low-level betterC utilities for big integer arithmetic libraries.
+
+The module provides $(REF BigUIntAccumulator), $(REF BigUIntView), and $(LREF BigIntView).
+
+Note:
+    The module doesn't provide full arithmetic API for now.
++/
+module mir.bignum.low_level_view;
+
+import mir.checkedint;
+import std.traits;
+
+private alias cop(string op : "-") = subu;
+private alias cop(string op : "+") = addu;
+private enum inverseSign(string op) = op == "+" ? "-" : "+";
+
+package immutable hexStringErrorMsg = "Incorrect hex string for BigUIntView.fromHexString";
+version (D_Exceptions)
+{
+    package immutable hexStringException = new Exception(hexStringErrorMsg);
+}
+
+/++
++/
+enum WordEndian
+{
+    ///
+    little,
+    ///
+    big,
+}
+
+version(LittleEndian)
+{
+    /++
+    +/
+    enum TargetEndian = WordEndian.little;
+}
+else
+{
+    /++
+    +/
+    enum TargetEndian = WordEndian.big;
+}
+
+package template MaxWordPow10(T)
+{
+    static if (is(T == ubyte))
+        enum MaxWordPow10 = 2;
+    else
+    static if (is(T == ushort))
+        enum MaxWordPow10 = 4;
+    else
+    static if (is(T == uint))
+        enum MaxWordPow10 = 9;
+    else
+    static if (is(T == ulong))
+        enum MaxWordPow10 = 19;
+    else
+        static assert(0);
+}
+
+package template MaxWordPow5(T)
+{
+    static if (is(T == ubyte))
+        enum MaxWordPow5 = 3;
+    else
+    static if (is(T == ushort))
+        enum MaxWordPow5 = 6;
+    else
+    static if (is(T == uint))
+        enum MaxWordPow5 = 13;
+    else
+    static if (is(T == ulong))
+        enum MaxWordPow5 = 27;
+    else
+        static assert(0);
+}
+
+private template MaxFpPow5(T)
+{
+    static if (T.mant_dig == 24)
+        enum MaxFpPow5 = 6;
+    else
+    static if (T.mant_dig == 53)
+        enum MaxFpPow5 = 10;
+    else
+    static if (T.mant_dig == 64)
+        enum MaxFpPow5 = 27;
+    else
+    static if (T.mant_dig == 113)
+        enum MaxFpPow5 = 48;
+    else
+        static assert(0, "floating point format isn't supported");
+}
+
+/++
+Arbitrary length unsigned integer view.
++/
+struct BigUIntView(W, WordEndian endian = TargetEndian)
+    if (__traits(isUnsigned, W))
+{
+    import mir.bignum.fp: Fp, half;
+    import mir.bignum.fixed: UInt;
+
+    /++
+    A group of coefficients for a radix `W.max + 1`.
+
+    The order corresponds to endianness.
+    +/
+    W[] coefficients;
+
+    /++
+    Retrurns: signed integer view using the same data payload
+    +/
+    BigIntView!(W, endian) signed()() @safe pure nothrow @nogc @property
+    {
+        return typeof(return)(this);
+    }
+
+    ///
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+        if (isFloatingPoint!T && isMutable!T)
+    {
+        import mir.bignum.fp;
+        enum md = T.mant_dig;
+        enum b = size_t.sizeof * 8;
+        enum n = md / b + (md % b != 0);
+        enum s = n * b;
+        return this.opCast!(Fp!s, s - md, wordNormalized, nonZero).opCast!(T, true);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    unittest
+    {
+        auto a = cast(double) BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(a == 0xa.fbbfae3cd0bp+124);
+        assert(cast(double) BigUIntView!size_t.init == 0);
+        assert(cast(double) BigUIntView!size_t([0]) == 0);
+    }
+
+    ///
+    @safe
+    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
+        if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
+    {
+        static if (isMutable!W)
+        {
+            return lightConst.opCast!(T, internalRoundLastBits, wordNormalized, nonZero);
+        }
+        else
+        static if (W.sizeof > size_t.sizeof)
+        {
+            return lightConst.opCast!(BigUIntView!size_t).opCast!(T, internalRoundLastBits, false, nonZero);
+        }
+        else
+        {
+            import mir.utility: _expect;
+            import mir.bitop: ctlz;
+            Fp!coefficientSize ret;
+            auto integer = lightConst;
+            static if (!wordNormalized)
+                integer = integer.normalized;
+            static if (!nonZero)
+                if (integer.coefficients.length == 0)
+                    goto R;
+            {
+                assert(integer.coefficients.length);
+                enum N = ret.coefficient.data.length;
+                sizediff_t size = integer.coefficients.length * (W.sizeof * 8);
+                sizediff_t expShift = size - coefficientSize;
+                ret.exponent = expShift;
+                if (_expect(expShift <= 0, true))
+                {
+                    static if (N == 1 && W.sizeof == size_t.sizeof)
+                    {
+                        ret.coefficient.data[0] = integer.mostSignificant;
+                    }
+                    else
+                    {
+                        BigUIntView!size_t(ret.coefficient.data)
+                            .opCast!(BigUIntView!(Unqual!W))
+                            .leastSignificantFirst
+                                [$ - integer.coefficients.length .. $] = integer.leastSignificantFirst;
+                    }
+                    auto c = cast(uint) ctlz(ret.coefficient.view.mostSignificant);
+                    ret.exponent -= c;
+                    ret.coefficient = ret.coefficient.smallLeftShift(c);
+                }
+                else
+                {
+                    UInt!(coefficientSize + size_t.sizeof * 8) holder;
+
+
+                    static if (N == 1 && W.sizeof == size_t.sizeof)
+                    {
+                        version (BigEndian)
+                        {
+                            holder.data[0] = integer.mostSignificantFirst[0];
+                            holder.data[1] = integer.mostSignificantFirst[1];
+                        }
+                        else
+                        {
+                            holder.data[0] = integer.mostSignificantFirst[1];
+                            holder.data[1] = integer.mostSignificantFirst[0];
+                        }
+                    }
+                    else
+                    {
+                        auto holderView = holder
+                            .view
+                            .opCast!(BigUIntView!(Unqual!W))
+                            .leastSignificantFirst;
+                        import mir.utility: min;
+                        auto minLength = min(integer.coefficients.length, holderView.length);
+                        holderView[$ - minLength .. $] = integer.leastSignificantFirst[$ - minLength .. $];
+                    }
+
+                    auto c = cast(uint) ctlz(holder.view.mostSignificant);
+                    ret.exponent -= c;
+                    holder = holder.smallLeftShift(c);
+                    ret.coefficient = holder.toSize!(coefficientSize, false);
+                    auto tail = BigUIntView!size_t(holder.data).leastSignificant;
+
+                    bool nonZeroTail()
+                    {
+                        while(_expect(integer.leastSignificant == 0, false))
+                        {
+                            integer.popLeastSignificant;
+                            assert(integer.coefficients.length);
+                        }
+                        return integer.coefficients.length > (N + 1) * (size_t.sizeof / W.sizeof);
+                    }
+
+                    static if (internalRoundLastBits)
+                    {
+                        enum half = size_t(1) << (internalRoundLastBits - 1);
+                        enum mask0 = (size_t(1) << internalRoundLastBits) - 1;
+                        auto tail0 = BigUIntView!size_t(ret.coefficient.data).leastSignificant & mask0;
+                        BigUIntView!size_t(ret.coefficient.data).leastSignificant &= ~mask0;
+                        auto condInc = tail0 >= half
+                            && (   tail0 > half
+                                || tail
+                                || (BigUIntView!size_t(ret.coefficient.data).leastSignificant & 1)
+                                || nonZeroTail);
+                    }
+                    else
+                    {
+                        enum half = cast(size_t)Signed!size_t.min;
+                        auto condInc = tail >= half
+                            && (    tail > half
+                                || (BigUIntView!size_t(ret.coefficient.data).leastSignificant & 1)
+                                || nonZeroTail);
+                    }
+
+                    if (condInc)
+                    {
+                        enum inc = size_t(1) << internalRoundLastBits;
+                        if (auto overflow = ret.coefficient += inc)
+                        {
+                            import mir.bignum.fp: half;
+                            ret.coefficient = half!coefficientSize;
+                            ret.exponent++;
+                        }
+                    }
+                }
+            }
+        R:
+            return ret;
+        }
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        import mir.bignum.fp: Fp;
+        import mir.bignum.fixed: UInt;
+
+        auto fp = cast(Fp!128) BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(fp.exponent == 0);
+        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+
+        fp = cast(Fp!128) BigUIntView!uint.fromHexString("ae3cd0aff2714a1de7022b0029d");
+        assert(fp.exponent == -20);
+        assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000"));
+
+        fp = cast(Fp!128) BigUIntView!ushort.fromHexString("e7022b0029d");
+        assert(fp.exponent == -84);
+        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+
+        fp = cast(Fp!128) BigUIntView!ubyte.fromHexString("e7022b0029d");
+        assert(fp.exponent == -84);
+        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+
+        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
+        assert(fp.exponent == -84);
+        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+    
+        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff1000000000000000");
+        assert(fp.exponent == 64);
+        assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
+
+        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff8000000000000000");
+        assert(fp.exponent == 65);
+        assert(fp.coefficient == UInt!128.fromHexString("80000000000000000000000000000000"));
+
+        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000000");
+        assert(fp.exponent == 64);
+        assert(fp.coefficient == UInt!128.fromHexString("fffffffffffffffffffffffffffffffe"));
+
+        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000001");
+        assert(fp.exponent == 64);
+        assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
+    }
+
+    static if (endian == TargetEndian)
+    ///
+    @trusted pure nothrow @nogc
+    BigUIntView!V opCast(T : BigUIntView!V, V)()
+        if (V.sizeof <= W.sizeof)
+    {
+        return typeof(return)(cast(V[])this.coefficients);
+    }
+
+    ///
+    BigUIntView!(const W, endian) lightConst()()
+        const @safe pure nothrow @nogc @property
+    {
+        return typeof(return)(coefficients);
+    }
+    ///ditto
+    alias lightConst this;
+
+    /++
+    +/
+    sizediff_t opCmp(BigUIntView!(const W, endian) rhs)
+        const @safe pure nothrow @nogc
+    {
+        import mir.algorithm.iteration: cmp;
+        auto l = this.lightConst.normalized;
+        auto r = rhs.lightConst.normalized;
+        if (auto d = l.coefficients.length - r.coefficients.length)
+            return d;
+        return cmp(l.mostSignificantFirst, r.mostSignificantFirst);
+    }
+
+    ///
+    bool opEquals(BigUIntView!(const W, endian) rhs)
+        const @safe pure nothrow @nogc
+    {
+        return this.coefficients == rhs.coefficients;
+    }
+
+    /++
+    +/
+    ref inout(W) mostSignificant() inout @property
+    {
+        static if (endian == WordEndian.big)
+            return coefficients[0];
+        else
+            return coefficients[$ - 1];
+    }
+
+    /++
+    +/
+    ref inout(W) leastSignificant() inout @property
+    {
+        static if (endian == WordEndian.little)
+            return coefficients[0];
+        else
+            return coefficients[$ - 1];
+    }
+
+    /++
+    +/
+    void popMostSignificant()
+    {
+        static if (endian == WordEndian.big)
+            coefficients = coefficients[1 .. $];
+        else
+            coefficients = coefficients[0 .. $ - 1];
+    }
+
+    /++
+    +/
+    void popLeastSignificant()
+    {
+        static if (endian == WordEndian.little)
+            coefficients = coefficients[1 .. $];
+        else
+            coefficients = coefficients[0 .. $ - 1];
+    }
+
+    /++
+    +/
+    BigUIntView topMostSignificantPart(size_t length)
+    {
+        static if (endian == WordEndian.big)
+            return BigUIntView(coefficients[0 .. length]);
+        else
+            return BigUIntView(coefficients[$ - length .. $]);
+    }
+
+    /++
+    +/
+    BigUIntView topLeastSignificantPart(size_t length)
+    {
+        static if (endian == WordEndian.little)
+            return BigUIntView(coefficients[0 .. length]);
+        else
+            return BigUIntView(coefficients[$ - length .. $]);
+    }
+
+    /++
+    Shifts left using at most `size_t.sizeof * 8 - 1` bits
+    +/
+    void smallLeftShiftInPlace()(uint shift)
+    {
+        assert(shift < W.sizeof * 8);
+        if (shift == 0)
+            return;
+        auto csh = W.sizeof * 8 - shift;
+        auto d = leastSignificantFirst;
+        assert(d.length);
+        foreach_reverse (i; 1 .. d.length)
+            d[i] = (d[i] << shift) | (d[i - 1] >>> csh);
+        d.front <<= shift;
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        a.smallLeftShiftInPlace(4);
+        assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
+        a.smallLeftShiftInPlace(0);
+        assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
+    }
+
+    /++
+    Shifts right using at most `size_t.sizeof * 8 - 1` bits
+    +/
+    void smallRightShiftInPlace()(uint shift)
+    {
+        assert(shift < W.sizeof * 8);
+        if (shift == 0)
+            return;
+        auto csh = W.sizeof * 8 - shift;
+        auto d = leastSignificantFirst;
+        assert(d.length);
+        foreach (i; 0 .. d.length - 1)
+            d[i] = (d[i] >>> shift) | (d[i + 1] << csh);
+        d.back >>>= shift;
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        a.smallRightShiftInPlace(4);
+        assert(a == BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
+    }
+
+    /++
+    +/
+    static BigUIntView fromHexString(scope const(char)[] str)
+        @trusted pure
+    {
+        auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
+        auto data = new Unqual!W[length];
+        BigUIntView!(Unqual!W, endian)(data).fromHexStringImpl(str);
+        return BigUIntView(cast(W[])data);
+    }
+
+    static if (isMutable!W)
+    /++
+    +/
+    void fromHexStringImpl(scope const(char)[] str)
+        @safe pure @nogc
+    {
+        pragma(inline, false);
+        import mir.utility: _expect;
+        if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false))
+        {
+            version(D_Exceptions)
+                throw hexStringException;
+            else
+                assert(0, hexStringErrorMsg);
+        }
+        auto rdata = leastSignificantFirst;
+        W current;
+        size_t i;
+        do
+        {
+            ubyte c;
+            switch(str[$ - ++i])
+            {
+                case '0': c = 0x0; break;
+                case '1': c = 0x1; break;
+                case '2': c = 0x2; break;
+                case '3': c = 0x3; break;
+                case '4': c = 0x4; break;
+                case '5': c = 0x5; break;
+                case '6': c = 0x6; break;
+                case '7': c = 0x7; break;
+                case '8': c = 0x8; break;
+                case '9': c = 0x9; break;
+                case 'A':
+                case 'a': c = 0xA; break;
+                case 'B':
+                case 'b': c = 0xB; break;
+                case 'C':
+                case 'c': c = 0xC; break;
+                case 'D':
+                case 'd': c = 0xD; break;
+                case 'E':
+                case 'e': c = 0xE; break;
+                case 'F':
+                case 'f': c = 0xF; break;
+                default:
+                    version(D_Exceptions)
+                        throw hexStringException;
+                    else
+                        assert(0, hexStringErrorMsg);
+            }
+            enum s = W.sizeof * 8 - 4;
+            W cc = cast(W)(W(c) << s);
+            current >>>= 4;
+            current |= cc;
+            if (i % (W.sizeof * 2) == 0)
+            {
+                rdata.front = current;
+                rdata.popFront;
+                current = 0;
+            }
+        }
+        while(i < str.length);
+        if (current)
+        {
+            current >>>= 4 * (W.sizeof * 2 - i % (W.sizeof * 2));
+            rdata.front = current;
+        }
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Performs `bool overflow = big +(-)= big` operatrion.
+    Params:
+        rhs = value to add with non-empty coefficients
+        overflow = (overflow) initial iteration overflow
+    Precondition: non-empty coefficients length of greater or equal to the `rhs` coefficients length.
+    Returns:
+        true in case of unsigned overflow
+    +/
+    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
+    @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        assert(this.coefficients.length > 0);
+        assert(rhs.coefficients.length <= this.coefficients.length);
+        auto ls = this.leastSignificantFirst;
+        auto rs = rhs.leastSignificantFirst;
+        do
+        {
+            bool overflowM, overflowG;
+            ls.front = ls.front.cop!op(rs.front, overflowM).cop!op(overflow, overflowG);
+            overflow = overflowG | overflowM;
+            ls.popFront;
+            rs.popFront;
+        }
+        while(rs.length);
+        if (overflow && ls.length)
+            return topMostSignificantPart(ls.length).opOpAssign!op(W(overflow));
+        return overflow;
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /// ditto
+    bool opOpAssign(string op)(BigIntView!(const W, endian) rhs, bool overflow = false)
+    @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return rhs.sign == false ?
+            opOpAssign!op(rhs.unsigned, overflow):
+            opOpAssign!(inverseSign!op)(rhs.unsigned, overflow);
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Performs `bool Overflow = big +(-)= scalar` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = value to add
+    Returns:
+        true in case of unsigned overflow
+    +/
+    bool opOpAssign(string op, T)(const T rhs)
+        @safe pure nothrow @nogc
+        if ((op == "+" || op == "-") && is(T == W))
+    {
+        assert(this.coefficients.length > 0);
+        auto ns = this.leastSignificantFirst;
+        W additive = rhs;
+        do
+        {
+            bool overflow;
+            ns.front = ns.front.cop!op(additive, overflow);
+            if (!overflow)
+                return overflow;
+            additive = overflow;
+            ns.popFront;
+        }
+        while (ns.length);
+        return true;
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /// ditto
+    bool opOpAssign(string op, T)(const T rhs)
+        @safe pure nothrow @nogc
+        if ((op == "+" || op == "-") && is(T == Signed!W))
+    {
+        return rhs >= 0 ?
+            opOpAssign!op(cast(W)rhs):
+            opOpAssign!(inverseSign!op)(cast(W)(-rhs));
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Performs `W overflow = (big += overflow) *= scalar` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = unsigned value to multiply by
+        overflow = initial overflow
+    Returns:
+        unsigned overflow value
+    +/
+    W opOpAssign(string op : "*")(W rhs, W overflow = 0u)
+        @safe pure nothrow @nogc
+    {
+        assert(coefficients.length);
+        auto ns = this.leastSignificantFirst;
+        do
+        {
+            import mir.utility: extMul;
+            auto ext = ns.front.extMul(rhs);
+            bool overflowM;
+            ns.front = ext.low.cop!"+"(overflow, overflowM);
+            overflow = ext.high + overflowM;
+            ns.popFront;
+        }
+        while (ns.length);
+        return overflow;
+    }
+
+    static if (isMutable!W && W.sizeof == size_t.sizeof)
+    /++
+    Performs `W overflow = (big += overflow) *= scalar` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = unsigned fixed-length integer to multiply by
+        overflow = initial overflow
+    Returns:
+        unsigned fixed-length integer overflow value
+    +/
+    UInt!size
+    opOpAssign(string op : "*", size_t size)(UInt!size rhs, UInt!size overflow = 0)
+        @safe pure nothrow @nogc
+    {
+        assert(coefficients.length);
+        auto ns = this.leastSignificantFirst;
+        do
+        {
+            auto t = rhs;
+            auto overflowW = t.view *= ns.front;
+            auto overflowM = t += overflow;
+            overflowW += overflowM;
+            ns.front = cast(size_t) t;
+            static if (size > size_t.sizeof * 8)
+                overflow = t.toSize!(size - size_t.sizeof * 8, false).toSize!size;
+            BigUIntView!size_t(overflow.data).mostSignificant = overflowW;
+            ns.popFront;
+        }
+        while (ns.length);
+        return overflow;
+    }
+
+    /++
+    Returns: the same intger view with inversed sign
+    +/
+    BigIntView!(W, endian) opUnary(string op : "-")()
+    {
+        return typeof(return)(this, true);
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    +/
+    void bitwiseNotInPlace()
+    {
+        foreach (ref coefficient; this.coefficients)
+            coefficient = cast(W)~(0 + coefficient);
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Performs `number=-number` operatrion.
+    Precondition: non-empty coefficients
+    Returns:
+        true if 'number=-number=0' and false otherwise
+    +/
+    bool twoComplementInPlace()
+    {
+        assert(coefficients.length);
+        bitwiseNotInPlace();
+        return this.opOpAssign!"+"(W(1));
+    }
+
+    /++
+    Returns: a slice of coefficients starting from the least significant.
+    +/
+    auto leastSignificantFirst()
+        @safe pure nothrow @nogc @property
+    {
+        import mir.ndslice.slice: sliced;
+        static if (endian == WordEndian.little)
+        {
+            return coefficients.sliced;
+        }
+        else
+        {
+            import mir.ndslice.topology: retro;
+            return coefficients.sliced.retro;
+        }
+    }
+
+    ///
+    auto leastSignificantFirst()
+        const @safe pure nothrow @nogc @property
+    {
+        import mir.ndslice.slice: sliced;
+        static if (endian == WordEndian.little)
+        {
+            return coefficients.sliced;
+        }
+        else
+        {
+            import mir.ndslice.topology: retro;
+            return coefficients.sliced.retro;
+        }
+    }
+
+    /++
+    Returns: a slice of coefficients starting from the most significant.
+    +/
+    auto mostSignificantFirst()
+        @safe pure nothrow @nogc @property
+    {
+        import mir.ndslice.slice: sliced;
+        static if (endian == WordEndian.big)
+        {
+            return coefficients.sliced;
+        }
+        else
+        {
+            import mir.ndslice.topology: retro;
+            return coefficients.sliced.retro;
+        }
+    }
+
+    ///
+    auto mostSignificantFirst()
+        const @safe pure nothrow @nogc @property
+    {
+        import mir.ndslice.slice: sliced;
+        static if (endian == WordEndian.big)
+        {
+            return coefficients.sliced;
+        }
+        else
+        {
+            import mir.ndslice.topology: retro;
+            return coefficients.sliced.retro;
+        }
+    }
+
+    /++
+    Strips most significant zero coefficients.
+    +/
+    BigUIntView normalized()
+    {
+        auto number = this;
+        if (number.coefficients.length) do
+        {
+            static if (endian == WordEndian.big)
+            {
+                if (number.coefficients[0])
+                    break;
+                number.coefficients = number.coefficients[1 .. $];
+            }
+            else
+            {
+                if (number.coefficients[$ - 1])
+                    break;
+                number.coefficients = number.coefficients[0 .. $ - 1];
+            }
+        }
+        while (number.coefficients.length);
+        return number;
+    }
+
+    ///ditto
+    BigUIntView!(const W, endian) normalized() const
+    {
+        return lightConst.normalized;
+    }
+
+    /++
+    +/
+    bool bt()(size_t position)
+    {
+        import mir.ndslice.topology: bitwise;
+        assert(position < coefficients.length * W.sizeof * 8);
+        return leastSignificantFirst.bitwise[position];
+    }
+
+    /++
+    +/
+    size_t ctlz()() const @property
+        @safe pure nothrow @nogc
+    {
+        import mir.bitop: ctlz;
+        assert(coefficients.length);
+        auto d = mostSignificantFirst;
+        size_t ret;
+        do
+        {
+            if (auto c = d.front)
+            {
+                ret += ctlz(c);
+                break;
+            }
+            ret += W.sizeof * 8;
+            d.popFront;
+        }
+        while(d.length);
+        return ret;
+    }
+
+    /++
+    +/
+    size_t cttz()() const @property
+        @safe pure nothrow @nogc
+    {
+        import mir.bitop: cttz;
+        assert(coefficients.length);
+        auto d = leastSignificantFirst;
+        size_t ret;
+        do
+        {
+            if (auto c = d.front)
+            {
+                ret += cttz(c);
+                break;
+            }
+            ret += W.sizeof * 8;
+            d.popFront;
+        }
+        while(d.length);
+        return ret;
+    }
+
+    ///
+    BigIntView!(W, endian) withSign()(bool sign)
+    {
+        return typeof(return)(this, sign);
+    }
+
+    /++
+    Params:
+        value = (out) unsigned integer
+    Returns: $(SUBREF exception, IonErrorCode)
+    +/
+    bool get(U)(scope out U value)
+        @safe pure nothrow @nogc const
+        if (isUnsigned!U)
+    {
+        auto d = lightConst.mostSignificantFirst;
+        if (d.length == 0)
+            return false;
+        static if (U.sizeof > W.sizeof)
+        {
+            size_t i;
+            for(;;)
+            {
+                value |= d[0];
+                d = d[1 .. $];
+                if (d.length == 0)
+                    return false;
+                i += cast(bool)value;
+                value <<= W.sizeof * 8;
+                import mir.utility: _expect;
+                if (_expect(i >= U.sizeof / W.sizeof, false))
+                    return true;
+            }
+        }
+        else
+        {
+            for(;;)
+            {
+                W f = d[0];
+                d = d[1 .. $];
+                if (d.length == 0)
+                {
+                    value = cast(U)f;
+                    static if (U.sizeof < W.sizeof)
+                    {
+                        if (value != f)
+                            return true;
+                    }
+                    return false;
+                }
+                if (f)
+                    return true;
+            }
+        }
+    }
+
+    /++
+    Returns: true if the integer and equals to `rhs`.
+    +/
+    bool opEquals(ulong rhs)
+        @safe pure nothrow @nogc const
+    {
+        foreach_reverse(d; lightConst.leastSignificantFirst)
+        {
+            static if (W.sizeof >= ulong.sizeof)
+            {
+                if (d != rhs)
+                    return false;
+                rhs = 0;
+            }
+            else
+            {
+                if (d != (rhs & W.max))
+                    return false;
+                rhs >>>= W.sizeof * 8;
+            }
+        }
+        return rhs == 0;
+    }
+}
+
+///
+version(mir_bignum_test)
+@safe pure nothrow
+unittest
+{
+    import std.traits;
+    alias AliasSeq(T...) = T;
+
+    foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
+    foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big))
+    {
+        static if (endian == WordEndian.little)
+        {
+            T[3] lhsData = [1, T.max-1, 0];
+            T[3] rhsData = [T.max, T.max, 0];
+        }
+        else
+        {
+            T[3] lhsData = [0, T.max-1, 1];
+            T[3] rhsData = [0, T.max, T.max];
+        }
+
+        auto lhs = BigUIntView!(T, endian)(lhsData).normalized;
+
+        /// bool overflow = bigUInt op= scalar
+        assert(lhs.leastSignificantFirst == [1, T.max-1]);
+        assert(lhs.mostSignificantFirst == [T.max-1, 1]);
+        static if (T.sizeof >= 4)
+        {
+            assert((lhs += T.max) == false);
+            assert(lhs.leastSignificantFirst == [0, T.max]);
+            assert((lhs += T.max) == false);
+            assert((lhs += T.max) == true); // overflow bit
+            assert(lhs.leastSignificantFirst == [T.max-1, 0]);
+            assert((lhs -= T(1)) == false);
+            assert(lhs.leastSignificantFirst == [T.max-2, 0]);
+            assert((lhs -= T.max) == true); // underflow bit
+            assert(lhs.leastSignificantFirst == [T.max-1, T.max]);
+            assert((lhs -= Signed!T(-4)) == true); // overflow bit
+            assert(lhs.leastSignificantFirst == [2, 0]);
+            assert((lhs += Signed!T.max) == false); // overflow bit
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0]);
+
+            ///  bool overflow = bigUInt op= bigUInt/bigInt
+            lhs = BigUIntView!(T, endian)(lhsData);
+            auto rhs = BigUIntView!(T, endian)(rhsData).normalized;
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert(rhs.leastSignificantFirst == [T.max, T.max]);
+            assert((lhs += rhs) == false);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 1, 0, 1]);
+            assert((lhs -= rhs) == false);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert((lhs += -rhs) == true);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 3, 0, T.max]);
+            assert((lhs += -(-rhs)) == true);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+
+            /// W overflow = bigUInt *= scalar
+            assert((lhs *= T.max) == 0);
+            assert((lhs += T(Signed!T.max + 2)) == false);
+            assert(lhs.leastSignificantFirst == [0, Signed!T.max + 2, 0]);
+            lhs = lhs.normalized;
+            lhs.leastSignificantFirst[1] = T.max / 2 + 3;
+            assert(lhs.leastSignificantFirst == [0, T.max / 2 + 3]);
+            assert((lhs *= 8u) == 4);
+            assert(lhs.leastSignificantFirst == [0, 16]);
+        }
+    }
+}
+
+/++
+Arbitrary length signed integer view.
++/
+struct BigIntView(W, WordEndian endian = TargetEndian)
+    if (is(Unqual!W == ubyte) || is(Unqual!W == ushort) || is(Unqual!W == uint) || is(Unqual!W == ulong))
+{
+    import mir.bignum.fp: Fp;
+
+    /++
+    Self-assigned to unsigned integer view $(MREF BigUIntView).
+
+    Sign is stored in the most significant bit.
+
+    The number is encoded as pair of `unsigned` and `sign`.
+    +/
+    BigUIntView!(W, endian) unsigned;
+
+    /++
+    Sign bit
+    +/
+    bool sign;
+
+    ///
+    inout(W)[] coefficients() inout @property
+    {
+        return unsigned.coefficients;
+    }
+
+    ///
+    this(W[] coefficients, bool sign = false)
+    {
+        this(BigUIntView!(W, endian)(coefficients), sign);
+    }
+
+    ///
+    this(BigUIntView!(W, endian) unsigned, bool sign = false)
+    {
+        this.unsigned = unsigned;
+        this.sign = sign;
+    }
+
+    ///
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+        if (isFloatingPoint!T)
+    {
+        auto ret = this.unsigned.opCast!(T, wordNormalized, nonZero);
+        if (sign)
+            ret = -ret;
+        return ret;
+    }
+
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    unittest
+    {
+        auto a = cast(double) -BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(a == -0xa.fbbfae3cd0bp+124);
+    }
+
+    /++
+    +/
+    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
+        if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
+    {
+        auto ret = unsigned.opCast!(Fp!coefficientSize, internalRoundLastBits, wordNormalized, nonZero);
+        ret.sign = sign;
+        return ret;
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        import mir.bignum.fixed: UInt;
+        import mir.bignum.fp: Fp;
+
+        auto fp = cast(Fp!128) -BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(fp.sign);
+        assert(fp.exponent == 0);
+        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+    }
+
+    static if (endian == TargetEndian)
+    ///
+    BigIntView!V opCast(T : BigIntView!V, V)()
+        if (V.sizeof <= W.sizeof)
+    {
+        return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
+    }
+
+    ///
+    BigIntView!(const W, endian) lightConst()()
+        const @safe pure nothrow @nogc @property
+    {
+        return typeof(return)(unsigned.lightConst, sign);
+    }
+
+    ///ditto
+    alias lightConst this;
+
+    /++
+    +/
+    sizediff_t opCmp(BigIntView!(const W, endian) rhs) 
+        const @safe pure nothrow @nogc
+    {
+        import mir.algorithm.iteration: cmp;
+        if (auto s = rhs.sign - this.sign)
+        {
+            return s;
+        }
+        sizediff_t d = this.unsigned.opCmp(rhs.unsigned);
+        return sign ? -d : d;
+    }
+
+    ///
+    bool opEquals(BigIntView!(const W, endian) rhs)
+        const @safe pure nothrow @nogc
+    {
+        return this.sign == rhs.sign && this.unsigned == rhs.unsigned;
+    }
+
+    /++
+    +/
+    BigIntView topMostSignificantPart(size_t length)
+    {
+        return BigIntView(unsigned.topMostSignificantPart(length), sign);
+    }
+
+    /++
+    +/
+    BigIntView topLeastSignificantPart(size_t length)
+    {
+        return BigIntView(unsigned.topLeastSignificantPart(length), sign);
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Performs `bool overflow = big +(-)= big` operatrion.
+    Params:
+        rhs = value to add with non-empty coefficients
+        overflow = (overflow) initial iteration overflow
+    Precondition: non-empty coefficients length of greater or equal to the `rhs` coefficients length.
+    Returns:
+        true in case of unsigned overflow
+    +/
+    bool opOpAssign(string op)(BigIntView!(const W, endian) rhs, bool overflow = false)
+    @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        assert(rhs.coefficients.length > 0);
+        import std.conv;
+        debug assert(this.coefficients.length >= rhs.coefficients.length, this.coefficients.length.to!string ~ " " ~ rhs.coefficients.length.to!string);
+        enum sum = op == "+";
+        // pos += pos
+        // neg += neg
+        // neg -= pos
+        // pos -= neg
+        if ((sign == rhs.sign) == sum)
+            return unsigned.opOpAssign!"+"(rhs.unsigned, overflow);
+        // pos -= pos
+        // pos += neg
+        // neg += pos
+        // neg -= neg
+        if (unsigned.opOpAssign!"-"(rhs.unsigned, overflow))
+        {
+            sign = !sign;
+            unsigned.twoComplementInPlace;
+        }
+        return false;
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /// ditto
+    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
+    @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return opOpAssign!op(rhs.signed, overflow);
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Performs `bool overflow = big +(-)= scalar` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = value to add
+    Returns:
+        true in case of unsigned overflow
+    +/
+    bool opOpAssign(string op, T)(const T rhs)
+        @safe pure nothrow @nogc
+        if ((op == "+" || op == "-") && is(T == Signed!W))
+    {
+        assert(this.coefficients.length > 0);
+        enum sum = op == "+";
+        // pos += pos
+        // neg += neg
+        // neg -= pos
+        // pos -= neg
+        auto urhs = cast(W) (rhs < 0 ? -rhs : rhs);
+        if ((sign == (rhs < 0)) == sum)
+            return unsigned.opOpAssign!"+"(urhs);
+        // pos -= pos
+        // pos += neg
+        // neg += pos
+        // neg -= neg
+        if (unsigned.opOpAssign!"-"(urhs))
+        {
+            sign = !sign;
+            unsigned.twoComplementInPlace;
+        }
+        return false;
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /// ditto
+    bool opOpAssign(string op, T)(const T rhs)
+        @safe pure nothrow @nogc
+        if ((op == "+" || op == "-") && is(T == W))
+    {
+        assert(this.coefficients.length > 0);
+        enum sum = op == "+";
+        // pos += pos
+        // neg -= pos
+        if ((sign == false) == sum)
+            return unsigned.opOpAssign!"+"(rhs);
+        // pos -= pos
+        // neg += pos
+        if (unsigned.opOpAssign!"-"(rhs))
+        {
+            sign = !sign;
+            unsigned.twoComplementInPlace;
+        }
+        return false;
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Performs `W overflow = (big += overflow) *= scalar` operatrion.
+    Precondition: non-empty coefficients
+    Params:
+        rhs = unsigned value to multiply by
+        overflow = initial overflow
+    Returns:
+        unsigned overflow value
+    +/
+    W opOpAssign(string op : "*")(W rhs, W overflow = 0u)
+        @safe pure nothrow @nogc
+    {
+        return unsigned.opOpAssign!op(rhs, overflow);
+    }
+
+    /++
+    Returns: the same intger view with inversed sign
+    +/
+    BigIntView opUnary(string op : "-")()
+    {
+        return BigIntView(unsigned, !sign);
+    }
+
+    /++
+    Returns: a slice of coefficients starting from the least significant.
+    +/
+    auto leastSignificantFirst()
+        @safe pure nothrow @nogc @property
+    {
+        return unsigned.leastSignificantFirst;
+    }
+
+    /++
+    Returns: a slice of coefficients starting from the most significant.
+    +/
+    auto mostSignificantFirst()
+        @safe pure nothrow @nogc @property
+    {
+        return unsigned.mostSignificantFirst;
+    }
+
+    /++
+    Strips zero most significant coefficients.
+    Strips most significant zero coefficients.
+    Sets sign to zero if no coefficients were left.
+    +/
+    BigIntView normalized()
+    {
+        auto number = this;
+        number.unsigned = number.unsigned.normalized;
+        number.sign = number.coefficients.length == 0 ? false : number.sign;
+        return number;
+    }
+
+    ///ditto
+    BigIntView!(const W, endian) normalized() const
+    {
+        return lightConst.normalized;
+    }
+}
+
+///
+version(mir_bignum_test)
+@safe pure nothrow
+unittest
+{
+    import std.traits;
+    alias AliasSeq(T...) = T;
+
+    foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
+    foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big))
+    {
+        static if (endian == WordEndian.little)
+        {
+            T[3] lhsData = [1, T.max-1, 0];
+            T[3] rhsData = [T.max, T.max, 0];
+        }
+        else
+        {
+            T[3] lhsData = [0, T.max-1, 1];
+            T[3] rhsData = [0, T.max, T.max];
+        }
+
+        auto lhs = BigIntView!(T, endian)(lhsData).normalized;
+
+        ///  bool overflow = bigUInt op= scalar
+        assert(lhs.leastSignificantFirst == [1, T.max-1]);
+        assert(lhs.mostSignificantFirst == [T.max-1, 1]);
+
+        static if (T.sizeof >= 4)
+        {
+
+            assert((lhs += T.max) == false);
+            assert(lhs.leastSignificantFirst == [0, T.max]);
+            assert((lhs += T.max) == false);
+            assert((lhs += T.max) == true); // overflow bit
+            assert(lhs.leastSignificantFirst == [T.max-1, 0]);
+            assert((lhs -= T(1)) == false);
+            assert(lhs.leastSignificantFirst == [T.max-2, 0]);
+            assert((lhs -= T.max) == false);
+            assert(lhs.leastSignificantFirst == [2, 0]);
+            assert(lhs.sign);
+            assert((lhs -= Signed!T(-4)) == false);
+            assert(lhs.leastSignificantFirst == [2, 0]);
+            assert(lhs.sign == false);
+            assert((lhs += Signed!T.max) == false);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0]);
+
+            ///  bool overflow = bigUInt op= bigUInt/bigInt
+            lhs = BigIntView!(T, endian)(lhsData);
+            auto rhs = BigUIntView!(T, endian)(rhsData).normalized;
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert(rhs.leastSignificantFirst == [T.max, T.max]);
+            assert((lhs += rhs) == false);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 1, 0, 1]);
+            assert((lhs -= rhs) == false);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert((lhs += -rhs) == false);
+            assert(lhs.sign);
+            assert(lhs.leastSignificantFirst == [T.max - (Signed!T.max + 2), T.max, 0]);
+            assert(lhs.sign);
+            assert((lhs -= -rhs) == false);
+            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert(lhs.sign == false);
+        }
+    }
+}
+
+/++
+An utility type to wrap a local buffer to accumulate unsigned numbers.
++/
+struct BigUIntAccumulator(W, WordEndian endian = TargetEndian)
+    if (is(Unqual!W == uint) || is(Unqual!W == ulong))
+{
+    /++
+    A group of coefficients for a $(MREF DecimalRadix)`!W`.
+
+    The order corresponds to endianness.
+
+    The unused part can be uninitialized.
+    +/
+    W[] coefficients;
+
+    /++
+    Current length of initialized coefficients.
+
+    The initialization order corresponds to endianness.
+
+    The `view` method may return a view with empty coefficients, which isn't usable.
+    Put `0` or another number first to make the accumulator maintain a non-empty view.
+    +/
+    size_t length;
+
+    /++
+    Returns:
+        Current unsigned integer view.
+    Note:
+        The method may return a view with empty coefficients, which isn't usable.
+        Put `0` or another number first to make the accumulator maintain a non-empty view.
+    +/
+    BigUIntView!(W, endian) view() @safe pure nothrow @nogc @property
+    {
+        static if (endian == WordEndian.little)
+            return typeof(return)(coefficients[0 .. length]);
+        else
+            return typeof(return)(coefficients[$ - length .. $]);
+    }
+
+    /++
+    Returns:
+        True if the accumulator can accept next most significant coefficient 
+    +/
+    bool canPut() @property
+    {
+        return length < coefficients.length;
+    }
+
+    /++
+    Places coefficient to the next most significant position.
+    +/
+    void put(W coeffecient)
+    in {
+        assert(length < coefficients.length);
+    }
+    do {
+        static if (endian == WordEndian.little)
+            coefficients[length++] = coeffecient;
+        else
+            coefficients[$ - ++length] = coeffecient;
+    }
+
+    /++
+    Strips most significant zero coefficients from the current `view`.
+    Note:
+        The `view` method may return a view with empty coefficients, which isn't usable.
+        Put `0` or another number first to make the accumulator maintain a non-empty view.
+    +/
+    void normalize()
+    {
+        length = view.normalized.coefficients.length;
+    }
+
+    ///
+    bool canPutN(size_t n)
+    {
+        return length + n <= coefficients.length;
+    }
+
+    ///
+    bool approxCanMulPow5(size_t degree)
+    {
+        // TODO: more precise result
+        enum n = MaxWordPow5!W;
+        return canPutN(degree / n + (degree % n != 0));
+    }
+
+    ///
+    bool canMulPow2(size_t degree)
+    {
+        import mir.bitop: ctlz;
+        enum n = W.sizeof * 8;
+        return canPutN(degree / n + (degree % n > ctlz(view.mostSignificant)));
+    }
+
+    ///
+    void mulPow5(size_t degree)
+    {
+        // assert(approxCanMulPow5(degree));
+        enum n = MaxWordPow5!W;
+        enum wordInit = W(5) ^^ n;
+        W word = wordInit;
+        while(degree)
+        {
+            if (degree >= n)
+            {
+                degree -= n;
+            }
+            else
+            {
+                word = 1;
+                do word *= 5;
+                while(--degree);
+            }
+            if (auto overflow = view *= word)
+            {
+                put(overflow);
+            }
+        }
+    }
+
+    ///
+    void mulPow2(size_t degree)
+    {
+        import mir.bitop: ctlz;
+        assert(canMulPow2(degree));
+        enum n = W.sizeof * 8;
+        auto ws = degree / n;
+        auto oldLength = length;
+        length += ws;
+        if (ws)
+        {
+            auto v = view.leastSignificantFirst;
+            foreach_reverse (i; 0 .. oldLength)
+            {
+                v[i + ws] = v[i];
+            }
+            do v[--ws] = 0;
+            while(ws);
+        }
+
+        if (auto tail = cast(uint)(degree % n))
+        {
+            if (tail > ctlz(view.mostSignificant))
+            {
+                put(0);
+                oldLength++;
+            }
+            view.topMostSignificantPart(oldLength).smallLeftShiftInPlace(tail);
+        }
+    }
+}
+
+///
+version(mir_bignum_test)
+@safe pure
+unittest
+{
+    import std.traits;
+    alias AliasSeq(T...) = T;
+
+    foreach (T; AliasSeq!(uint, ulong))
+    foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big))
+    {
+        T[16 / T.sizeof] buffer;
+        auto accumulator = BigUIntAccumulator!(T, endian)(buffer);
+        assert(accumulator.length == 0);
+        assert(accumulator.coefficients.length == buffer.length);
+        assert(accumulator.view.coefficients.length == 0);
+        // needs to put a number before any operations on `.view`
+        accumulator.put(1);
+        // compute N factorial
+        auto N = 30;
+        foreach(i; 1 .. N + 1)
+        {
+            if (auto overflow = accumulator.view *= i)
+            {
+                if (!accumulator.canPut)
+                    throw new Exception("Factorial buffer overflow");
+                accumulator.put(overflow);
+            }
+        }
+        assert(accumulator.view == BigUIntView!(T, endian).fromHexString("D13F6370F96865DF5DD54000000"));
+    }
+}
+
+///
+version(mir_bignum_test)
+unittest
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.low_level_view: BigUIntView;
+    auto bigView = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
+    auto fixed = UInt!256.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
+    auto overflow = bigView *= fixed;
+    assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398"));
+    assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
+
+}
+
+/// Computes `13 * 10^^60`
+version(mir_bignum_test)
+@safe pure
+unittest
+{
+    uint[7] buffer;
+    auto accumulator = BigUIntAccumulator!uint(buffer);
+    accumulator.put(13); // initial value
+    assert(accumulator.approxCanMulPow5(60));
+    accumulator.mulPow5(60);
+    assert(accumulator.canMulPow2(60));
+    accumulator.mulPow2(60);
+    assert(accumulator.view == BigUIntView!uint.fromHexString("81704fcef32d3bd8117effd5c4389285b05d000000000000000"));
+}
+
+// /++
+// +/
+// BigUIntView!(W, endian) multiplyAddImpl(W, WordEndian endian)(
+//     BigUIntView!(W, endian) result,
+//     BigUIntView!(const W, endian) a,
+//     BigUIntView!(const W, endian) b, out bool overflow)
+// @safe pure nothrow @nogc
+// {
+//     import mir.utility: swap;
+//     assert(a.length);
+//     assert(b.length);
+//     assert(result.length);
+//     if (a.length > b.length)
+//         swap(a, b);
+//             assert(coefficients.length);
+//     assert(b.length <= result.length);
+
+//     auto rs = a.leastSignificantFirst;
+//     do
+//     {
+//         auto ns = b.leastSignificantFirst;
+//         do
+//         {
+//             import mir.utility: extMul;
+//             bool overflowM;
+//             bool overflowN;
+//             auto ext = ns.front.extMul(rhs);
+//             ns.front = ext.low.cop!"+"(overflow, overflowM)
+//                 .cop!"+"(overflow, overflowN);
+//             overflow = ext.high + overflowM;
+//             ns.popFront;
+//         }
+//         while (ns.length);
+//     }
+//     while (rs.length);
+//     return overflow;
+// }
+
+alias d1 = DecimalView!(uint, WordEndian.little, );
+alias d2 = DecimalView!(uint, WordEndian.big);
+alias d3 = DecimalView!(ulong, WordEndian.little, );
+alias d4 = DecimalView!(ulong, WordEndian.big);
+alias d5 = DecimalView!(uint, WordEndian.little, long);
+alias d6 = DecimalView!(uint, WordEndian.big, long);
+alias d7 = DecimalView!(ulong, WordEndian.little, long);
+alias d8 = DecimalView!(ulong, WordEndian.big, long);
+
+/++
++/
+struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
+{
+    ///
+    bool sign;
+    ///
+    Exp exponent;
+    ///
+    BigUIntView!(W, endian) coefficient;
+
+    ///
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+        if (isFloatingPoint!T && isMutable!T)
+    {
+        import mir.bignum.integer: BigInt;
+        import mir.bignum.fixed: UInt;
+        import mir.bignum.fp: Fp, extendedMul;
+        import mir.bignum.internal.dec2flt_table;
+        import mir.math.common: floor;
+        import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
+        import mir.utility: _expect;
+
+        auto coeff = coefficient.lightConst;
+        T ret = 0;
+        static if (!wordNormalized)
+            coeff = coeff.normalized;
+        static if (!nonZero)
+            if (coeff.coefficients.length == 0)
+                goto R;
+        enum S = 9;
+        static if (T.mant_dig < 64)
+        {
+            Fp!64 load(Exp e)
+            {
+                auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
+                auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
+                return Fp!64(false, p10exp, UInt!64(p10coeff));
+            }
+            {
+                auto expSign = exponent < 0;
+                if (_expect((expSign ? -exponent : exponent) >>> S == 0, true))
+                {
+                    enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
+                    enum ulong half = (1UL << (64 - T.mant_dig - 1));
+                    enum ulong bound = ulong(1) << T.mant_dig;
+
+                    auto c = coeff.opCast!(Fp!64, 0, true, true);
+                    auto z = c.extendedMul(load(exponent));
+                    ret = cast(T) z;
+                    auto slop = (coeff.coefficients.length > (ulong.sizeof / W.sizeof)) + 3 * expSign;
+                    long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
+                    if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
+                        goto R;
+                    if (slop == 0 && exponent <= MaxWordPow5!ulong || exponent == 0)
+                        goto R;
+                    if (slop == 3 && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
+                    {
+                        auto e = load(-exponent);
+                        ret =  c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
+                        goto R;
+                    }
+                    goto AlgoR;
+                }
+                ret = expSign ? 0 : T.infinity;
+                goto R;
+            }
+        }
+        else
+        {
+            enum P = 1 << S;
+            static assert(min_p10_e <= -P);
+            static assert(max_p10_e >= P);
+            Fp!128 load(Exp e)
+            {
+                auto idx = cast(sizediff_t)e - min_p10_e;
+                ulong h = p10_coefficients[idx][0];
+                ulong l = p10_coefficients[idx][1];
+                if (l >= cast(ulong)long.min)
+                    h--;
+                version(BigEndian)
+                    auto p10coeff = UInt!128(cast(size_t[ulong.sizeof / size_t.sizeof * 2])cast(ulong[2])[h, l]);
+                else
+                    auto p10coeff = UInt!128(cast(size_t[ulong.sizeof / size_t.sizeof * 2])cast(ulong[2])[l, h]);
+                auto p10exp = p10_exponents[idx] - 64;
+                return Fp!128(false, p10exp, p10coeff);
+            }
+
+            {
+                auto expSign = exponent < 0;
+                Unsigned!Exp exp = exponent;
+                exp = expSign ? -exp : exp;
+                if (exp >= 5000)
+                {
+                    ret = expSign ? 0 : T.infinity;
+                    goto R;
+                }
+                Exp index = exp & 0x1FF;
+                bool gotoAlgoR;
+                auto c = load(expSign ? -index : index);
+                {
+                    exp >>= S;
+                    gotoAlgoR = exp != 0;
+                    if (_expect(gotoAlgoR, false))
+                    {
+                        auto v = load(expSign ? -P : P);
+                        do
+                        {
+                            if (exp & 1)
+                                c *= v;
+                            exp >>>= 1;
+                            if (exp == 0)
+                                break;
+                            v *= v;
+                        }
+                        while(true);
+                    }
+                }
+                auto z = coeff.opCast!(Fp!128, 0, true, true).extendedMul(c);
+                ret = cast(T) z;
+                if (!gotoAlgoR)
+                {
+                    static if (T.mant_dig == 64)
+                        enum ulong mask = ulong.max;
+                    else
+                        enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
+                    enum ulong half = (1UL << (128 - T.mant_dig - 1));
+                    enum UInt!128 bound = UInt!128(1) << T.mant_dig;
+
+                    auto slop = (coeff.coefficients.length > (ulong.sizeof * 2 / W.sizeof)) + 3 * expSign;
+                    long bitsDiff = (cast(ulong) z.opCast!(Fp!128).coefficient & mask) - half;
+                    if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
+                        goto R;
+                    if (slop == 0 && exponent <= 55 || exponent == 0)
+                        goto R;
+                    if (slop == 3 && MaxFpPow5!T >= -exponent && c.coefficient < bound)
+                    {
+                        auto e = load(-exponent);
+                        ret =  c.opCast!(T, true) / cast(T) e;
+                        goto R;
+                    }
+                }
+            }
+        }
+
+    AlgoR:
+        if (exponent >= 0)
+        {
+            BigInt!256 x; // max value is 2^(2^14)-1
+            if (x.copyFrom(coeff) || x.mulPow5(exponent)) // if overflow
+                goto R;
+            ret = ldexp(cast(T) x, cast(int) exponent);
+        }
+        else do
+        {
+            if (ret < ret.min_normal)
+                break;
+            int k;
+            auto m0 = frexp(ret, k);
+            k -= T.mant_dig;
+            static if (T.mant_dig <= 64)
+            {
+                enum p2 = T(2) ^^ T.mant_dig;
+                auto m = UInt!64(cast(ulong) (m0 * p2));
+            }
+            else
+            {
+                enum p2h = T(2) ^^ (T.mant_dig - 64);
+                enum p2l = T(2) ^^ 64;
+                m0 *= p2h;
+                auto mhf = floor(m0);
+                auto mh = cast(ulong) mhf;
+                m0 -= mhf;
+                m0 *= p2l;
+                auto ml = cast(ulong) m0;
+                auto m = UInt!128(mh);
+                m <<= 64;
+                m |= UInt!128(ml);
+            }
+            auto mtz = m.cttz;
+            if (mtz != m.sizeof * 8)
+            {
+                m >>= mtz;
+                k += mtz;
+            }
+
+            BigInt!256 x; // max value is 2^(2^14)-1
+            if (x.copyFrom(coeff)) // if overflow
+                break;
+            auto y = BigInt!256(m); // max value is 2^(2^14)-1
+            y.mulPow5(-exponent);
+            auto shift = k - cast(int)exponent;
+            (shift >= 0  ? y : x) <<= shift >= 0 ? shift : -shift;
+            x -= y;
+            if (x.length == 0)
+                break;
+            x <<= 1;
+            x *= m;
+            auto cond = mtz == T.mant_dig - 1 && x.sign;
+            auto cmp = x.view.unsigned.opCmp(y.view.unsigned);
+            if (cmp < 0)
+            {
+                if (!cond)
+                    break;
+                x <<= 1;
+                if (x.view.unsigned <= y.view.unsigned)
+                    break;
+            }
+            else
+            if (!cmp && !cond && !mtz)
+                break;
+            ret = x.sign ? nextDown(ret) : nextUp(ret);
+            if (ret == 0)
+                break;
+        }
+        while (T.mant_dig >= 64 && exponent < -512);
+    R:
+        if (sign)
+            ret = -ret;
+        return ret;
+    }
+}
+
+///
+version(mir_bignum_test)
+unittest
+{
+    alias AliasSeq(T...) = T;
+
+    foreach (T; AliasSeq!(float, double, real))
+    {{
+        T value = 3.518437208883201171875E+013;
+        
+    }}
+
+    foreach(E; AliasSeq!(WordEndian.little, WordEndian.big))
+    foreach(W; AliasSeq!(ulong, uint, ushort, ubyte))
+    static if (!(E != TargetEndian && (W.sizeof > size_t.sizeof || W.sizeof == 1)))
+    {{
+        alias Args = AliasSeq!(W, E);
+
+        auto view = DecimalView!Args(false, -8, BigUIntView!Args.fromHexString("BEBC2000000011E1A3"));
+
+        assert (cast(float)view == 3.518437208883201171875E+013f);
+        assert (cast(double)view == 3.518437208883201171875E+013);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 3.518437208883201171875E+013L);
+
+        view = DecimalView!Args(true, -169, BigUIntView!Args.fromHexString("5A174AEDA65CC"));
+        assert (cast(float)view == -0);
+        assert (cast(double)view == -1.584897405380044e-154);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == -1.584897405380044e-154L);
+
+        view = DecimalView!Args(true, 293, BigUIntView!Args.fromHexString("36496F6C4ED38"));
+        assert (cast(float)view == -float.infinity);
+        assert (cast(double)view == -9.55024478104888e+307);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == -9.55024478104888e+307L);
+
+        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 1);
+        assert (cast(double)view == 1);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1);
+
+        view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3"));
+        assert (cast(float)view == 3e-5f);
+        assert (cast(double)view == 3e-5);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 3e-5L);
+
+        view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0.1f);
+        assert (cast(double)view == 0.1);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0.1L);
+
+        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("3039"));
+        assert (cast(float)view == 12345.0f);
+        assert (cast(double)view == 12345.0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 12345.0L);
+
+        view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F"));
+        assert (cast(float)view == 0.9999999f);
+        assert (cast(double)view == 0.9999999);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0.9999999L);
+
+        view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("4F0CEDC95A718E"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 2.2250738585072014e-308);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 2.2250738585072014e-308L);
+
+        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1FFFFFFFFFFFFFFFD"));
+        assert (cast(float)view == 36893488147419103229f);
+        assert (cast(double)view == 36893488147419103229.0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 36893488147419103229.0L);
+
+        view = DecimalView!Args(false, -33, BigUIntView!Args.fromHexString("65"));
+        assert (cast(float)view == 101e-33f);
+        assert (cast(double)view == 101e-33);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 101e-33L);
+
+        view = DecimalView!Args(false, 23, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 1e23f);
+        assert (cast(double)view == 1e23);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e23L);
+
+        view = DecimalView!Args(false, 23, BigUIntView!Args.fromHexString("81B"));
+        assert (cast(float)view == 2075e23f);
+        assert (cast(double)view == 0xaba3d58a1f1a98p+32);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0xaba3d58a1f1a9cp+32L);
+    
+        view = DecimalView!Args(false, -23, BigUIntView!Args.fromHexString("2209"));
+        assert (cast(float)view == 8713e-23f);
+        assert (cast(double)view == 8713e-23);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 8713e-23L);
+
+        view = DecimalView!Args(false, 300, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == 1e300);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e300L);
+
+        view = DecimalView!Args(false, 245, BigUIntView!Args.fromHexString("B3A73CEB227"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == 123456789.34567e250);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 123456789.34567e250L);
+
+        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == 943794359898089732078308743689303290943794359843568973207830874368930329.0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 943794359898089732078308743689303290943794359843568973207830874368930329.0L);
+
+        view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == double.min_normal * double.epsilon);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 5e-324L);
+
+        view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5B"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == double.min_normal * double.epsilon * 18);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 91e-324L);
+
+        view = DecimalView!Args(false, -322, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == double.min_normal * double.epsilon * 20);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e-322L);
+
+        view = DecimalView!Args(false, -320, BigUIntView!Args.fromHexString("CA1CCB"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == double.min_normal * double.epsilon * 26809479897);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 13245643e-320L);
+
+        view = DecimalView!Args(false, -319, BigUIntView!Args.fromHexString("33CE7943FB"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 2.22507385851e-308);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 2.22507385851e-308L);
+
+        view = DecimalView!Args(false, -309, BigUIntView!Args.fromHexString("15"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0x0.f19c2629ccf53p-1022);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 2.1e-308L);
+
+        view = DecimalView!Args(false, -340, BigUIntView!Args.fromHexString("AF87023B9BF0EE"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == double.min_normal * double.epsilon);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 4.9406564584124654e-324L);
+
+        view = DecimalView!Args(false, 400, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == double.infinity);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e400L);
+
+        view = DecimalView!Args(false, 309, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == double.infinity);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e309L);
+
+        view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == double.infinity);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 2e308L);
+
+        view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == double.infinity);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 2e308L);
+
+        view = DecimalView!Args(false, 295, BigUIntView!Args.fromHexString("1059949B7090"));
+        assert (cast(float)view == float.infinity);
+        assert (cast(double)view == double.infinity);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1.7976931348624e308L);
+
+        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("0"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0);
+
+        view = DecimalView!Args(false, 0, BigUIntView!Args.init);
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0);
+
+        view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e-325L);
+
+        view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e-326L);
+
+        view = DecimalView!Args(false, -500, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e-500L);
+
+        view = DecimalView!Args(false, -1000, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e-1000L);
+
+        view = DecimalView!Args(false, -4999, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0);
+
+        view = DecimalView!Args(false, -10000, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0);
+
+        view = DecimalView!Args(false, -4969, BigUIntView!Args.fromHexString("329659A941466C6B"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == real.min_normal * real.epsilon);
+
+        view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB"));
+        assert (cast(float)view == 1.448997445238699f);
+        assert (cast(double)view == 0x1.72f17f1f49aadp0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1.448997445238699L);
+
+        view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB"));
+        assert (cast(float)view == 1.448997445238699f);
+        assert (cast(double)view == 0x1.72f17f1f49aadp0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1.448997445238699L);
+
+        view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e-325L);
+
+        view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0);
+        assert (cast(double)view == 0);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1e-326L);
+
+        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 1);
+        assert (cast(double)view == 1);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 1);
+
+        view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3"));
+        assert (cast(float)view == 3e-5f);
+        assert (cast(double)view == 3e-5);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 3e-5L);
+
+        view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1"));
+        assert (cast(float)view == 0.1f);
+        assert (cast(double)view == 0.1);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0.1L);
+
+        view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F"));
+        assert (cast(float)view == 0.9999999f);
+        assert (cast(double)view == 0.9999999);
+        static if (real.mant_dig >= 64)
+            assert (cast(real)view == 0.9999999L);
+    }}
+}
+
+/++
++/
+struct BinaryView(W, WordEndian endian = TargetEndian, Exp = int)
+{
+    ///
+    bool sign;
+    ///
+    Exp exponent;
+    ///
+    BigUIntView!(W, endian) coefficient;
+}
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 28bb0c00..ada9fc55 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -115,17 +115,19 @@ struct IotaIterator(I)
             return IotaIterator!(LightImmutableOf!I)(_index);
     }
 
+pure:
+
     I opUnary(string op : "*")()
     { return _index; }
 
-    void opUnary(string op)() scope
+    void opUnary(string op)()
         if (op == "--" || op == "++")
     { mixin(op ~ `_index;`); }
 
-    I opIndex()(ptrdiff_t index) scope const
+    I opIndex()(ptrdiff_t index) const
     { return cast(I)(_index + index); }
 
-    void opOpAssign(string op)(ptrdiff_t index) scope
+    void opOpAssign(string op)(ptrdiff_t index)
         if (op == `+` || op == `-`)
     { mixin(`_index ` ~ op ~ `= index;`); }
 
@@ -137,13 +139,13 @@ struct IotaIterator(I)
         return ret;
     }
 
-    ptrdiff_t opBinary(string op : "-")(const typeof(this) right) scope const
+    ptrdiff_t opBinary(string op : "-")(const typeof(this) right) const
     { return cast(ptrdiff_t)(this._index - right._index); }
 
-    bool opEquals()(const typeof(this) right) scope const
+    bool opEquals()(const typeof(this) right) const
     { return this._index == right._index; }
 
-    auto opCmp()(const typeof(this) right) scope const
+    auto opCmp()(const typeof(this) right) const
     { return this._index - right._index; }
 }
 
@@ -247,7 +249,7 @@ struct RetroIterator(Iterator)
     void opUnary(string op : "--")()
     { ++_iterator; }
 
-    void opUnary(string op : "++")()
+    void opUnary(string op : "++")() pure
     { --_iterator; }
 
     auto ref opIndex()(ptrdiff_t index)
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index f3759f09..b7d3b2a1 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1605,7 +1605,7 @@ public:
     }
 
     ///ditto
-    void popFront(size_t dimension = 0)() @trusted scope
+    void popFront(size_t dimension = 0)() @trusted
         if (dimension < N && (dimension == 0 || kind != Contiguous))
     {
         assert(_lengths[dimension], __FUNCTION__ ~ ": length!" ~ dimension.stringof ~ " should be greater than 0.");
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index a2bb71e3..d0448d1f 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -1018,7 +1018,6 @@ See_Also:
 
 +/
 @trusted
-deprecated("This function is experimental")
 template partitionAt(alias less = "a < b")
 {
     import mir.functional: naryFun;

From 6fdabff9471fe488d75ce644a8d7dc43f6b2e799 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 21 Jun 2020 18:09:18 +0700
Subject: [PATCH 012/573] update gitignore

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 3134bf73..6a00f79a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,4 @@ test
 test.d
 docgen
 _build_dir_
+.VSCodeCounter

From 33e6bd3c23ca882aae37bb46cb0fb3f73550fd11 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sun, 21 Jun 2020 21:49:37 +0700
Subject: [PATCH 013/573] add FP parsing (#280)

* add FP parsing

* windows fixup

* fixup

* fix tests

* fix docs
---
 dub.sdl                            |   2 +-
 index.d                            |   6 ++
 meson.build                        |   2 +-
 source/mir/bignum/decimal.d        |  23 +++++-
 source/mir/bignum/fixed.d          |  28 +++++++
 source/mir/bignum/low_level_view.d |  55 ++------------
 source/mir/parse.d                 | 115 +++++++++++++++++++++++------
 7 files changed, 156 insertions(+), 75 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 6a41685f..963828d0 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "Copyright © 2016 - 2019, Ilya Yaroshenko; see also information per file."
 license "BSL-1.0"
 
-dependency "mir-core" version=">=0.3.5 <1.2.0"
+dependency "mir-core" version=">=1.1.7"
 
 buildType "unittest" {
 	buildOptions "unittests" "debugMode" "debugInfo"
diff --git a/index.d b/index.d
index 01d5eaa5..2d544fa4 100644
--- a/index.d
+++ b/index.d
@@ -45,6 +45,12 @@ $(BOOKTABLE ,
     $(LEADINGROW Graphs)
     $(TR $(TDNW $(MREF mir,graph)) $(TD Basic routines to work with graphs))
     $(TR $(TDNW $(MREF mir,graph,tarjan)★) $(TD Tarjan's strongly connected components algorithm))
+    $(LEADINGROW Big Numbers (partial implementation))
+    $(TR $(TDNW $(MREF mir,bignum, decimal)) $(TD Stack-allocated decimal type))
+    $(TR $(TDNW $(MREF mir,bignum, fixed)) $(TD Stack-allocated fixed length unsigned integer type (like 256bit integers).))
+    $(TR $(TDNW $(MREF mir,bignum, fp)) $(TD Stack-allocated fixed length software floating point type.))
+    $(TR $(TDNW $(MREF mir,bignum, integer)) $(TD Stack-allocated integer type.))
+    $(TR $(TDNW $(MREF mir,bignum, low_level_view)) $(TD Low-level universal number representation formats.))
     $(LEADINGROW Combinatrorics)
     $(TR $(TDNW $(MREF mir,combinatorics)★) $(TD Combinations, combinations with repeats, cartesian power, permutations.))
     $(LEADINGROW Interconnection with other languages)
diff --git a/meson.build b/meson.build
index ffb05869..4cbd77cd 100644
--- a/meson.build
+++ b/meson.build
@@ -108,7 +108,7 @@ this_dep = declare_dependency(
     dependencies: required_deps,
 )
 
-test_versions = ['mir_test', 'NOGCEXP', 'mir_secure_memory']
+test_versions = ['mir_test', 'mir_bignum_test', 'mir_secure_memory']
 
 if has_cpp_headers
     install_subdir('include/',
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 2e7594c5..cb61fe1f 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -1,4 +1,6 @@
 /++
+Stack-allocated decimal type.
+
 Note:
     The module doesn't provide full arithmetic API for now.
 +/
@@ -7,7 +9,7 @@ module mir.bignum.decimal;
 import std.traits: isSomeChar;
 
 /++
-Stack-allocated decimal.
+Stack-allocated decimal type.
 Params:
     maxSize64 = count of 64bit words in coefficient
 +/
@@ -16,6 +18,7 @@ struct Decimal(size_t maxSize64)
 {
     import mir.bignum.integer;
     import mir.bignum.low_level_view;
+    import std.traits: isMutable, isFloatingPoint;
 
     ///
     int exponent;
@@ -27,6 +30,22 @@ struct Decimal(size_t maxSize64)
     {
         return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
     }
+
+    /// ditto
+    DecimalView!(const size_t) view() const
+    {
+        return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
+    }
+
+    /++
+    Mir parsing supports up-to quadruple precision.
+The conversion error is 0 ULP for normal numbers. 
+    Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+        if (isFloatingPoint!T && isMutable!T)
+    {
+        return view.opCast!(T, wordNormalized, nonZero);
+    }
 }
 
 /++
@@ -183,9 +202,7 @@ version(mir_bignum_test)
 unittest
 {
     Decimal!3 decimal;
-    int exponent;
     DecimalExponentKey key;
-    import std.conv;
 
     assert("1.334".parseDecimal(decimal, key));
     assert(cast(double) decimal.view == 1.334);
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index b0b051d7..34b3e6b8 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -33,6 +33,34 @@ struct UInt(size_t size)
             this.data[$ - N .. $] = data;
     }
 
+    static if (size_t.sizeof == uint.sizeof && data.length % 2 == 0)
+    this()(auto ref ulong[data.length / 2] data)
+    {
+        if (!__ctfe)
+        {
+            this.data =  cast(typeof(this.data)) data;
+        }
+        else
+        {
+            version(LittleEndian)
+            {
+                static foreach (i; 0 .. data.length)
+                {
+                    this.data[i * 2 + 0] = cast(uint) data[i];
+                    this.data[i * 2 + 1] = cast(uint) (data[i] >> 32);
+                }
+            }
+            else
+            {
+                static foreach (i; 0 .. data.length)
+                {
+                    this.data[i * 2 + 1] = cast(uint) data[i];
+                    this.data[i * 2 + 0] = cast(uint) (data[i] >> 32);
+                }
+            }
+        }
+    }
+
     static if (size >= 64)
     ///
     this(ulong data)
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index a8b210be..045caf2f 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1618,52 +1618,6 @@ unittest
     assert(accumulator.view == BigUIntView!uint.fromHexString("81704fcef32d3bd8117effd5c4389285b05d000000000000000"));
 }
 
-// /++
-// +/
-// BigUIntView!(W, endian) multiplyAddImpl(W, WordEndian endian)(
-//     BigUIntView!(W, endian) result,
-//     BigUIntView!(const W, endian) a,
-//     BigUIntView!(const W, endian) b, out bool overflow)
-// @safe pure nothrow @nogc
-// {
-//     import mir.utility: swap;
-//     assert(a.length);
-//     assert(b.length);
-//     assert(result.length);
-//     if (a.length > b.length)
-//         swap(a, b);
-//             assert(coefficients.length);
-//     assert(b.length <= result.length);
-
-//     auto rs = a.leastSignificantFirst;
-//     do
-//     {
-//         auto ns = b.leastSignificantFirst;
-//         do
-//         {
-//             import mir.utility: extMul;
-//             bool overflowM;
-//             bool overflowN;
-//             auto ext = ns.front.extMul(rhs);
-//             ns.front = ext.low.cop!"+"(overflow, overflowM)
-//                 .cop!"+"(overflow, overflowN);
-//             overflow = ext.high + overflowM;
-//             ns.popFront;
-//         }
-//         while (ns.length);
-//     }
-//     while (rs.length);
-//     return overflow;
-// }
-
-alias d1 = DecimalView!(uint, WordEndian.little, );
-alias d2 = DecimalView!(uint, WordEndian.big);
-alias d3 = DecimalView!(ulong, WordEndian.little, );
-alias d4 = DecimalView!(ulong, WordEndian.big);
-alias d5 = DecimalView!(uint, WordEndian.little, long);
-alias d6 = DecimalView!(uint, WordEndian.big, long);
-alias d7 = DecimalView!(ulong, WordEndian.little, long);
-alias d8 = DecimalView!(ulong, WordEndian.big, long);
 
 /++
 +/
@@ -1676,7 +1630,10 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
     ///
     BigUIntView!(W, endian) coefficient;
 
-    ///
+    /++
+    Mir parsing supports up-to quadruple precision.
+The conversion error is 0 ULP for normal numbers. 
+    Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
     T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T && isMutable!T)
     {
@@ -1746,9 +1703,9 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
                 if (l >= cast(ulong)long.min)
                     h--;
                 version(BigEndian)
-                    auto p10coeff = UInt!128(cast(size_t[ulong.sizeof / size_t.sizeof * 2])cast(ulong[2])[h, l]);
+                    auto p10coeff = UInt!128(cast(ulong[2])[h, l]);
                 else
-                    auto p10coeff = UInt!128(cast(size_t[ulong.sizeof / size_t.sizeof * 2])cast(ulong[2])[l, h]);
+                    auto p10coeff = UInt!128(cast(ulong[2])[l, h]);
                 auto p10exp = p10_exponents[idx] - 64;
                 return Fp!128(false, p10exp, p10coeff);
             }
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 7e820c29..5e203ceb 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -1,34 +1,65 @@
 /++
-$(H1 @nogc Parsing Utilities)
+$(H1 @nogc and nothrow Parsing Utilities)
 
 License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
 Authors:   Ilya Yaroshenko
+Copyright: Symmetry Investments and Kaleidic Associates
 +/
 module mir.parse;
 
 import mir.primitives;
 import std.range.primitives: isInputRange;
+import std.traits: isMutable, isFloatingPoint, isSomeChar;
 
 /++
-Throws: nogc Exception in case of parse error or non-empty remaining input.
-+/
-T fromString(T, Range)(scope auto ref Range r)
+Performs `nothorw` and `@nogc` string to native type conversion.
+
+Returns:
+    parsed value
+Throws:
+    `nogc` Exception in case of parse error or non-empty remaining input.
+
+Floating_point:
+    Mir parsing supports up-to quadruple precision.
+The conversion error is 0 ULP for normal numbers. 
+    Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.+/
+T fromString(T, C)(scope const(C)[] str)
+    if (isMutable!T)
 {
-    static immutable excne = new Exception("fromString: remaining input is not empty after parsing " ~ T.stringof);
+    import mir.utility: _expect;
     static immutable excfp = new Exception("fromString failed to parse " ~ T.stringof);
 
-    T value;
-    if (parse!T(r, value))
+    static if (isFloatingPoint!T)
     {
-        if (r.empty)
-        {
+        T value;
+        if (_expect(fromString(str, value), true))
             return value;
-        }
-        throw excne;
+        version (D_Exceptions)
+            throw excfp;
+        else
+            assert(0);
     }
     else
     {
-        throw excfp;
+        static immutable excne = new Exception("fromString: remaining input is not empty after parsing " ~ T.stringof);
+
+        T value;
+        if (_expect(parse!T(str, value), true))
+        {
+            if (_expect(str.empty, true))
+                return value;
+            version (D_Exceptions)
+                throw excne;
+            else
+                assert(0);
+        }
+        else
+        {
+            version (D_Exceptions)
+                throw excfp;
+            else
+                assert(0);
+        }
     }
 }
 
@@ -37,20 +68,59 @@ version(mir_test)
 @safe pure @nogc unittest
 {
     assert("123".fromString!int == 123);
+    static assert("-123".fromString!int == -123);
+
+    assert("12.3".fromString!double == 12.3);
+    assert("12.3".fromString!float == 12.3f);
+    assert("12.3".fromString!real == 12.3L);
+    assert("-12.3e-30".fromString!double == -12.3e-30);
 
-    auto s = "123";
-    assert(s.fromString!int == 123);
-    assert(s == "");
+    /// Test CTFE support  
+    static assert("-12.3e-30".fromString!double == -0x1.f2f280b2414d5p-97);
+    static assert("+12.3e+30".fromString!double == 0x1.367ee3119d2bap+103);
 
-    s = "123";
-    assert(s[].fromString!int == 123);
-    assert(s == "123");
+    static assert("1.448997445238699".fromString!double == 0x1.72f17f1f49aadp0);
+    static if (real.mant_dig >= 64)
+        static assert("1.448997445238699".fromString!real == 1.448997445238699L);
+
+    static assert("3.518437208883201171875".fromString!float == 0x1.c25c26p+1);
+    static assert("3.518437208883201171875".fromString!double == 0x1.c25c268497684p+1);
+    static if (real.mant_dig >= 64)
+        static assert("3.518437208883201171875".fromString!real == 3.518437208883201171875L);
+
+//  Related DMD Issues:
+// https://issues.dlang.org/show_bug.cgi?id=20951
+// https://issues.dlang.org/show_bug.cgi?id=20952
+// https://issues.dlang.org/show_bug.cgi?id=20953
+// https://issues.dlang.org/show_bug.cgi?id=20957
+// https://issues.dlang.org/show_bug.cgi?id=20963
+// https://issues.dlang.org/show_bug.cgi?id=20967
 }
 
-///
-bool fromString(T, Range)(scope auto ref Range r, ref T value)
+/++
+Performs `nothorw` and `@nogc` string to native type conversion.
+
+Returns: true if success and false otherwise.
++/
+bool fromString(T, C)(scope const(C)[] str, ref T value)
+    if (isSomeChar!C)
 {
-    return parse!T(r, value) && r.empty;
+    static if (isFloatingPoint!T)
+    {
+        import mir.bignum.decimal: Decimal, DecimalExponentKey, parseDecimal;
+        import mir.utility: _expect;
+
+        Decimal!256 decimal;
+        DecimalExponentKey key;
+        auto ret = parseDecimal(str, decimal, key);
+        if (_expect(ret, true))
+            value =  cast(T) decimal;
+        return ret;
+    }
+    else
+    {
+        return parse!T(str, value) && str.empty;
+    }
 }
 
 ///
@@ -62,6 +132,9 @@ version(mir_test)
 }
 
 /++
+Integer parsing utilities.
+
+Returns: true if success and false otherwise.
 +/
 bool parse(T : byte, Range)(scope ref Range r, scope ref T value)
     if (isInputRange!Range && !__traits(isUnsigned, T))

From d367c3733ffb97ad4ed891bd270113c0db2fed79 Mon Sep 17 00:00:00 2001
From: drug007 <drug2004@bk.ru>
Date: Mon, 22 Jun 2020 10:16:45 +0300
Subject: [PATCH 014/573] typo (#281)

---
 source/mir/parse.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/parse.d b/source/mir/parse.d
index 5e203ceb..e606d4bf 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -12,7 +12,7 @@ import std.range.primitives: isInputRange;
 import std.traits: isMutable, isFloatingPoint, isSomeChar;
 
 /++
-Performs `nothorw` and `@nogc` string to native type conversion.
+Performs `nothrow` and `@nogc` string to native type conversion.
 
 Returns:
     parsed value
@@ -98,7 +98,7 @@ version(mir_test)
 }
 
 /++
-Performs `nothorw` and `@nogc` string to native type conversion.
+Performs `nothrow` and `@nogc` string to native type conversion.
 
 Returns: true if success and false otherwise.
 +/

From 10a9683ef44315d3419b16d3f983e8d27b356cfb Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 22 Jun 2020 18:58:52 +0700
Subject: [PATCH 015/573] fix docs

---
 source/mir/parse.d | 1 -
 1 file changed, 1 deletion(-)

diff --git a/source/mir/parse.d b/source/mir/parse.d
index 5e203ceb..a0d9d80d 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -92,7 +92,6 @@ version(mir_test)
 // https://issues.dlang.org/show_bug.cgi?id=20951
 // https://issues.dlang.org/show_bug.cgi?id=20952
 // https://issues.dlang.org/show_bug.cgi?id=20953
-// https://issues.dlang.org/show_bug.cgi?id=20957
 // https://issues.dlang.org/show_bug.cgi?id=20963
 // https://issues.dlang.org/show_bug.cgi?id=20967
 }

From b78e39d56e77a5b1a4570e9cf5bb8da11a86b818 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 22 Jun 2020 22:21:51 +0700
Subject: [PATCH 016/573] fix issue #283

---
 source/mir/ndslice/sorting.d | 105 +++++++++++++++++++----------------
 1 file changed, 57 insertions(+), 48 deletions(-)

diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index d0448d1f..b1c7ca47 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -939,7 +939,7 @@ template pivotPartitionImpl(alias less)
             // Fixup
             assert(loI - hiI <= 2, "pivotPartition: Following compare not possible");
             assert(!less(*frontI, *hiI), "pivotPartition: *hiI must not be less than *frontI");
-            if (loI == hiI + 2)
+            if (loI - hiI == 2)
             {
                 assert(!less(hiI[1], *frontI), "pivotPartition: *(hiI + 1) must not be less than *frontI");
                 *(loI) = hiI[1];
@@ -979,6 +979,20 @@ template pivotPartitionImpl(alias less)
     }
 }
 
+version(mir_test)
+@safe pure nothrow
+unittest {
+    import mir.ndslice.sorting: partitionAt;
+    import mir.ndslice.allocation: rcslice;
+    auto x = rcslice!double(4);
+    x[0] = 3;
+    x[1] = 2;
+    x[2] = 1;
+    x[3] = 0;
+    partitionAt!("a > b")(x, 2);
+}
+
+
 version(mir_test_topN)
 @trusted pure nothrow
 unittest
@@ -1017,7 +1031,6 @@ See_Also:
     $(LREF pivotPartition), https://erdani.com/research/sea2017.pdf
 
 +/
-@trusted
 template partitionAt(alias less = "a < b")
 {
     import mir.functional: naryFun;
@@ -1031,24 +1044,25 @@ template partitionAt(alias less = "a < b")
                 function is finished.
         +/
         void partitionAt(Iterator, size_t N, SliceKind kind)
-            (Slice!(Iterator, N, kind) slice, size_t nth)
+            (Slice!(Iterator, N, kind) slice, size_t nth) @trusted nothrow
         {
+            import mir.qualifier: lightScope;
+            import core.lifetime: move;
+            import mir.ndslice.topology: flattened;
+
             assert(slice.elementCount > 0, "partitionAt: slice must have elementCount greater than 0");
             assert(nth >= 0, "partitionAt: nth must be greater than or equal to zero");
             assert(nth < slice.elementCount, "partitionAt: nth must be less than the elementCount of the slice");
         
-            import mir.ndslice.topology: flattened;
-
             bool useSampling = true;
-            auto flattenedSlice = slice.flattened;
-            auto frontI = flattenedSlice._iterator;
-            auto lastI = frontI + flattenedSlice.length - 1;
-            partitionAtImpl!(less, Iterator)(frontI, lastI, nth, useSampling);
+            auto flattenedSlice = slice.move.flattened;
+            auto frontI = flattenedSlice._iterator.lightScope;
+            auto lastI = frontI + (flattenedSlice.length - 1);
+            partitionAtImpl!less(frontI, lastI, nth, useSampling);
         }
-    } else {
-        alias partitionAt = .partitionAt!(naryFun!less);
-
     }
+    else
+        alias partitionAt = .partitionAt!(naryFun!less);
 }
 
 /// Partition 1-dimensional slice at nth
@@ -1150,21 +1164,18 @@ unittest {
 
 private @trusted
 void partitionAtImpl(alias less, Iterator)(
-    ref const(Iterator) frontI, 
-    ref const(Iterator) lastI, 
+    Iterator loI, 
+    Iterator hiI, 
     size_t n, 
-    bool useSampling) pure
+    bool useSampling) pure nothrow
 {
-    assert(frontI <= lastI, "partitionAtImpl: frontI must be less than or equal to lastI");
+    assert(loI <= hiI, "partitionAtImpl: frontI must be less than or equal to lastI");
 
     import mir.utility: swapStars;
     import mir.functional: reverseArgs;
 
-    auto loI = cast(Iterator) frontI;
-    auto hiI = cast(Iterator) lastI;
-    
-    Iterator pivotI = void;
-    size_t len = void;
+    Iterator pivotI;
+    size_t len;
 
     for (;;) {
         len = hiI - loI + 1;
@@ -1502,25 +1513,24 @@ unittest {
 
 private @trusted
 void p3(alias less, Iterator)(
-    ref const(Iterator) frontI,
-    ref const(Iterator) lastI,
-    ref const(Iterator) loI,
-    ref const(Iterator) hiI)
+    Iterator frontI,
+    Iterator lastI,
+    Iterator loI,
+    Iterator hiI)
 {
     assert(loI <= hiI && hiI <= lastI, "p3: loI must be less than or equal to hiI and hiI must be less than or equal to lastI");
     immutable diffI = hiI - loI;
-    Iterator lo_loI = void;
-    Iterator hi_loI = void;
-    Iterator loI_ = cast(Iterator) loI;
-    for (; loI_ < hiI; ++loI_)
+    Iterator lo_loI;
+    Iterator hi_loI;
+    for (; loI < hiI; ++loI)
     {
-        lo_loI = loI_;
+        lo_loI = loI;
         lo_loI -= diffI;
-        hi_loI = loI_;
+        hi_loI = loI;
         hi_loI += diffI;
         assert(lo_loI >= frontI, "p3: lo_loI must be greater than or equal to frontI");
         assert(hi_loI <= lastI, "p3: hi_loI must be less than or equal to lastI");
-        medianOf!less(lo_loI, loI_, hi_loI);
+        medianOf!less(lo_loI, loI, hi_loI);
     }
 }
 
@@ -1543,41 +1553,40 @@ private @trusted
 template p4(alias less, bool leanRight)
 {
     void p4(Iterator)(
-        ref const(Iterator) frontI,
-        ref const(Iterator) lastI,
-        ref const(Iterator) loI, 
-        ref const(Iterator) hiI)
+        Iterator frontI,
+        Iterator lastI,
+        Iterator loI, 
+        Iterator hiI)
     {
         assert(loI <= hiI && hiI <= lastI, "p4: loI must be less than or equal to hiI and hiI must be less than or equal to lastI");
 
         immutable diffI = hiI - loI; 
         immutable diffI2 = diffI * 2;
 
-        Iterator lo_loI = void;
-        Iterator hi_loI = void;
+        Iterator lo_loI;
+        Iterator hi_loI;
 
         static if (leanRight)
-            Iterator lo2_loI = void;
+            Iterator lo2_loI;
         else
-            Iterator hi2_loI = void;
+            Iterator hi2_loI;
 
-        Iterator loI_ = cast(Iterator) loI;
-        for (; loI_ < hiI; ++loI_)
+        for (; loI < hiI; ++loI)
         {
-            lo_loI = loI_ - diffI;
-            hi_loI = loI_ + diffI;
+            lo_loI = loI - diffI;
+            hi_loI = loI + diffI;
             
             assert(lo_loI >= frontI, "p4: lo_loI must be greater than or equal to frontI");
             assert(hi_loI <= lastI, "p4: hi_loI must be less than or equal to lastI");
 
             static if (leanRight) {
-                lo2_loI = loI_ - diffI2;
+                lo2_loI = loI - diffI2;
                 assert(lo2_loI >= frontI, "lo2_loI must be greater than or equal to frontI");
-                medianOf!(less, leanRight)(lo2_loI, lo_loI, loI_, hi_loI);
+                medianOf!(less, leanRight)(lo2_loI, lo_loI, loI, hi_loI);
             } else {
-                hi2_loI = loI_ + diffI2;
+                hi2_loI = loI + diffI2;
                 assert(hi2_loI <= lastI, "hi2_loI must be less than or equal to lastI");
-                medianOf!(less, leanRight)(lo_loI, loI_, hi_loI, hi2_loI);
+                medianOf!(less, leanRight)(lo_loI, loI, hi_loI, hi2_loI);
             }
         }
     }

From 790a79b991b4c2b023d9277fdfead4f9c48bbbe5 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Mon, 22 Jun 2020 21:08:17 -0400
Subject: [PATCH 017/573] Update UT version/purity (#284)

---
 source/mir/math/stat.d       |  32 +++++-----
 source/mir/ndslice/sorting.d | 118 +++++++++++++++++------------------
 2 files changed, 73 insertions(+), 77 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index f993bffd..f0f4d21b 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -26,8 +26,6 @@ import mir.primitives;
 import std.traits: isArray, isMutable, isIterable, isIntegral, CommonType;
 import mir.internal.utility: isFloatingPoint;
 
-// version = mir_test_topN;
-
 package template statType(T, bool checkComplex = true)
 {
     import mir.internal.utility: isFloatingPoint, isComplex;
@@ -1292,8 +1290,8 @@ template median(bool allowModify = false)
 }
 
 /// Median of vector
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1306,8 +1304,8 @@ unittest
 }
 
 /// Median of matrix
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure
 unittest
 {
     import mir.ndslice.fuse: fuse;
@@ -1321,8 +1319,8 @@ unittest
 }
 
 /// Row median of matrix
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure
 unittest
 {
     import mir.ndslice.fuse: fuse;
@@ -1344,8 +1342,8 @@ unittest
 }
 
 /// Can allow original slice to be modified or set output type
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1361,8 +1359,8 @@ unittest
 For integral slices, pass output type as template parameter to ensure output
 type is correct
 +/
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1372,7 +1370,7 @@ unittest
 }
 
 version(mir_test)
-@safe
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1422,7 +1420,7 @@ unittest
 }
 
 version(mir_test)
-@safe pure
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1570,7 +1568,7 @@ template center(alias centralTendency = mean!(Summation.appropriate))
 
 /// Center vector
 version(mir_test)
-@safe
+@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1622,7 +1620,7 @@ unittest
 
 /// Column center matrix
 version(mir_test)
-pure @safe
+@safe pure
 unittest
 {
     import mir.algorithm.iteration: all, equal;
@@ -2686,7 +2684,7 @@ unittest
 }
 
 /// Column standard deviation of matrix
-version(mir_test_variance)
+version(mir_test)
 @safe pure
 unittest
 {
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index b1c7ca47..138fa1e6 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -23,10 +23,8 @@ Macros:
 +/
 module mir.ndslice.sorting;
 
-// version = mir_test_topN;
-
 /// Check if ndslice is sorted, or strictly monotonic.
-@safe pure version(mir_test_topN) unittest
+@safe pure version(mir_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.slice: sliced;
@@ -50,7 +48,7 @@ module mir.ndslice.sorting;
 }
 
 /// Create index
-version(mir_test_topN) unittest
+version(mir_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.allocation: slice;
@@ -67,7 +65,7 @@ version(mir_test_topN) unittest
 }
 
 /// Schwartzian transform
-version(mir_test_topN) unittest
+version(mir_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.allocation: slice;
@@ -89,7 +87,7 @@ import mir.math.common: optmath;
 
 @optmath:
 
-@safe pure version(mir_test_topN) unittest
+@safe pure version(mir_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.topology: pairwise;
@@ -106,7 +104,7 @@ import mir.math.common: optmath;
     assert(c.pairwise!"a <= b".all);
 }
 
-@safe pure version(mir_test_topN) unittest
+@safe pure version(mir_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.topology: pairwise;
@@ -214,7 +212,7 @@ template sort(alias less = "a < b")
 }
 
 ///
-@safe pure version(mir_test_topN) unittest
+@safe pure version(mir_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.slice;
@@ -229,7 +227,7 @@ template sort(alias less = "a < b")
 }
 
 /// one-dimensional series
-pure version(mir_test_topN) unittest
+pure version(mir_test) unittest
 {
     import mir.series;
 
@@ -250,7 +248,7 @@ pure version(mir_test_topN) unittest
 }
 
 /// two-dimensional series
-pure version(mir_test_topN) unittest
+pure version(mir_test) unittest
 {
     import mir.series;
     import mir.ndslice.allocation: uninitSlice;
@@ -583,7 +581,7 @@ template assumeSortedEqualIndex(alias test = "a < b")
 }
 
 ///
-version(mir_test_topN)
+version(mir_test)
 @safe pure unittest
 {
     // sorted: a < b
@@ -648,7 +646,7 @@ template transitionIndex(alias test = "a < b")
 }
 
 ///
-version(mir_test_topN)
+version(mir_test)
 @safe pure unittest
 {
     // sorted: a < b
@@ -702,7 +700,7 @@ I[] makeIndex(I = size_t, alias less = "a < b", T)(scope T[] r)
 }
 
 ///
-version(mir_test_topN)
+version(mir_test)
 @system unittest
 {
     import mir.algorithm.iteration: all;
@@ -786,7 +784,7 @@ template pivotPartition(alias less = "a < b")
 }
 
 /// pivotPartition with 1-dimensional Slice
-version(mir_test_topN)
+version(mir_test)
 @safe pure nothrow
 unittest
 {
@@ -801,7 +799,7 @@ unittest
 }
 
 /// pivotPartition with 2-dimensional Slice
-version(mir_test_topN)
+version(mir_test)
 @safe pure
 unittest
 {
@@ -821,7 +819,7 @@ unittest
     assert(xFlattened[pivot .. $].all!(a => a >= xFlattened[pivot]));
 }
 
-version(mir_test_topN)
+version(mir_test)
 @safe
 unittest
 {
@@ -918,7 +916,7 @@ template pivotPartitionImpl(alias less)
             for (;;)
             {
                 // Loop invariant
-                version(mir_test_topN)
+                version(mir_test)
                 {
                     // this used to import std.algorithm.all, but we want to
                     // save imports when unittests are enabled if possible.
@@ -1066,8 +1064,8 @@ template partitionAt(alias less = "a < b")
 }
 
 /// Partition 1-dimensional slice at nth
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1078,8 +1076,8 @@ unittest {
 }
 
 /// Partition 2-dimensional slice
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1090,8 +1088,8 @@ unittest {
 }
 
 /// Can supply alternate ordering function
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1109,7 +1107,7 @@ version(unittest) {
 
         static if (__traits(isSame, naryFun!less, less))
         {
-            @safe
+            @safe pure nothrow
             static bool checkTopNAll
                 (Iterator, SliceKind kind)(
                     Slice!(Iterator, 1, kind) x)
@@ -1136,8 +1134,8 @@ version(unittest) {
     }
 }
 
-version(mir_test_topN)
-@safe
+version(mir_test)
+@safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1162,12 +1160,12 @@ unittest {
     assert(checkTopNAll([ 0,  2,  7, 16,  2, 20,  1, 11, 17,  5, 22, 17, 25, 13, 14,  5, 22, 21, 24, 14].sliced));
 }
 
-private @trusted
+private @trusted pure nothrow
 void partitionAtImpl(alias less, Iterator)(
     Iterator loI, 
     Iterator hiI, 
     size_t n, 
-    bool useSampling) pure nothrow
+    bool useSampling)
 {
     assert(loI <= hiI, "partitionAtImpl: frontI must be less than or equal to lastI");
 
@@ -1270,8 +1268,8 @@ void partitionAtImpl(alias less, Iterator)(
     }
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1283,8 +1281,8 @@ unittest {
     assert(x[nth] == 2);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1296,8 +1294,8 @@ unittest {
     assert(x[2, 0] == 5);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1309,8 +1307,8 @@ unittest {
     assert(x[nth] == 0);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1322,8 +1320,8 @@ unittest {
     assert(x[nth] == 3);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1335,8 +1333,8 @@ unittest {
     assert(x[nth] == 3);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1348,8 +1346,8 @@ unittest {
     assert(x[nth] == 7);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1361,8 +1359,8 @@ unittest {
     assert(x[nth] == 8);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1374,7 +1372,7 @@ unittest {
     assert(x[nth] == 10);
 }
 
-private @trusted
+private @trusted pure nothrow
 Iterator partitionAtPartition(alias less, Iterator)(
     ref Iterator frontI, 
     ref Iterator lastI, 
@@ -1426,8 +1424,8 @@ Iterator partitionAtPartition(alias less, Iterator)(
     return expandPartition!less(frontI, lastI, loI, pivotI, hiI);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
 
@@ -1441,7 +1439,7 @@ unittest {
     assert(x[n - 1] == x_sort[n - 1]);
 }
 
-private @trusted
+private @trusted pure nothrow
 Iterator partitionAtPartitionOffMedian(alias less, bool leanRight, Iterator)(
     ref Iterator frontI, 
     ref Iterator lastI, 
@@ -1485,8 +1483,8 @@ Iterator partitionAtPartitionOffMedian(alias less, bool leanRight, Iterator)(
     return expandPartition!less(frontI, lastI, loI, pivotI, hiI);
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
     import mir.algorithm.iteration: equal;
@@ -1498,8 +1496,8 @@ unittest {
     assert(x.equal([6, 7, 8, 9, 5, 0, 2, 7, 9, 15, 10, 25, 11, 10, 13, 18, 17, 13, 25, 22]));
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
     import mir.algorithm.iteration: equal;
@@ -1534,8 +1532,8 @@ void p3(alias less, Iterator)(
     }
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
     import mir.algorithm.iteration: equal;
@@ -1592,8 +1590,8 @@ template p4(alias less, bool leanRight)
     }
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
     import mir.algorithm.iteration: equal;
@@ -1607,8 +1605,8 @@ unittest {
     assert(x.equal([3, 1, 0, 4, 2, 6, 4, 7, 5]));
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
     import mir.algorithm.iteration: equal;
@@ -1742,8 +1740,8 @@ template expandPartition(alias less)
     }
 }
 
-version(mir_test_topN)
-@trusted
+version(mir_test)
+@trusted pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;

From dfccbb9e04d5d32155d62002ec02274ddf7c5319 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Tue, 23 Jun 2020 09:49:12 +0700
Subject: [PATCH 018/573] update appveyor (#286)

---
 appveyor.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/appveyor.yml b/appveyor.yml
index e6809565..558a3386 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,7 +8,7 @@ environment:
   #   DVersion: 2.080.0
   #   arch: x86
   - DC: ldc
-    DVersion: '1.18.0'
+    DVersion: '1.21.0'
     arch: x64
 
 matrix:

From 45ecbbcecb06bd7e46ae5fee942cc686bc3daa8b Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 23 Jun 2020 23:10:24 +0700
Subject: [PATCH 019/573] fix  issues #282  and  #285

---
 source/mir/bignum/low_level_view.d | 4 ++--
 source/mir/parse.d                 | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 045caf2f..f31b418f 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1872,9 +1872,9 @@ unittest
 
         view = DecimalView!Args(true, -169, BigUIntView!Args.fromHexString("5A174AEDA65CC"));
         assert (cast(float)view == -0);
-        assert (cast(double)view == -1.584897405380044e-154);
+        assert (cast(double)view == -0x1.1p-511);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == -1.584897405380044e-154L);
+            assert (cast(real)view == -0x8.80000000000019fp-514);
 
         view = DecimalView!Args(true, 293, BigUIntView!Args.fromHexString("36496F6C4ED38"));
         assert (cast(float)view == -float.infinity);
diff --git a/source/mir/parse.d b/source/mir/parse.d
index b8ade1f9..3ebe49b7 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -86,7 +86,7 @@ version(mir_test)
     static assert("3.518437208883201171875".fromString!float == 0x1.c25c26p+1);
     static assert("3.518437208883201171875".fromString!double == 0x1.c25c268497684p+1);
     static if (real.mant_dig >= 64)
-        static assert("3.518437208883201171875".fromString!real == 3.518437208883201171875L);
+        static assert("3.518437208883201171875".fromString!real == 0xe.12e13424bb4232fp-2L);
 
 //  Related DMD Issues:
 // https://issues.dlang.org/show_bug.cgi?id=20951

From 00c63159e8f87e7f8a3050264e4baec34df14614 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 24 Jun 2020 22:12:19 +0700
Subject: [PATCH 020/573] update bn llv

---
 source/mir/bignum/low_level_view.d | 33 ++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index f31b418f..2dca613d 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1622,6 +1622,7 @@ unittest
 /++
 +/
 struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
+    if (isUnsigned!W)
 {
     ///
     bool sign;
@@ -1630,6 +1631,22 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
     ///
     BigUIntView!(W, endian) coefficient;
 
+    ///
+    DecimalView!(const W, endian, Exp) lightConst()()
+        const @safe pure nothrow @nogc @property
+    {
+        return typeof(return)(sign, exponent, coefficient.lightConst);
+    }
+    ///ditto
+    alias lightConst this;
+
+    /++
+    +/
+    BigIntView!(W, endian) signedCoefficient()
+    {
+        return typeof(return)(coefficient, sign);
+    }
+
     /++
     Mir parsing supports up-to quadruple precision.
 The conversion error is 0 ULP for normal numbers. 
@@ -2152,4 +2169,20 @@ struct BinaryView(W, WordEndian endian = TargetEndian, Exp = int)
     Exp exponent;
     ///
     BigUIntView!(W, endian) coefficient;
+
+    ///
+    DecimalView!(const W, endian, Exp) lightConst()()
+        const @safe pure nothrow @nogc @property
+    {
+        return typeof(return)(sign, exponent, coefficient.lightConst);
+    }
+    ///ditto
+    alias lightConst this;
+
+    /++
+    +/
+    BigIntView!(W, endian) signedCoefficient()
+    {
+        return typeof(return)(sign, coefficients);
+    }
 }

From a7cc32282f5ba9b8452171dab3eedfe99a39c7e7 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 24 Jun 2020 22:28:26 +0700
Subject: [PATCH 021/573] partial fix for #285

---
 source/mir/bignum/low_level_view.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 2dca613d..4fee0f67 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1939,7 +1939,7 @@ unittest
         assert (cast(float)view == 36893488147419103229f);
         assert (cast(double)view == 36893488147419103229.0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 36893488147419103229.0L);
+            assert (cast(real)view == 0x1FFFFFFFFFFFFFFFDp0L);
 
         view = DecimalView!Args(false, -33, BigUIntView!Args.fromHexString("65"));
         assert (cast(float)view == 101e-33f);

From 677c93a8effab72e28843c72fdaed67bc484dae6 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 25 Jun 2020 07:19:55 +0700
Subject: [PATCH 022/573] fix issue #285

---
 source/mir/bignum/low_level_view.d | 56 +++++++++++++++---------------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 4fee0f67..eab5ee1f 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1963,97 +1963,97 @@ unittest
         assert (cast(float)view == 8713e-23f);
         assert (cast(double)view == 8713e-23);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 8713e-23L);
+            assert (cast(real)view == 0xc.dbada73ef15c401p-67);
 
         view = DecimalView!Args(false, 300, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 1e300);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e300L);
+            assert (cast(real)view == 0xb.f21e44003acdd2dp+993);
 
         view = DecimalView!Args(false, 245, BigUIntView!Args.fromHexString("B3A73CEB227"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 123456789.34567e250);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 123456789.34567e250L);
+            assert (cast(real)view == 0xa.471b9a999e5b01ep+854);
 
         view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 943794359898089732078308743689303290943794359843568973207830874368930329.0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 943794359898089732078308743689303290943794359843568973207830874368930329.0L);
+            assert (cast(real)view == 0x8.8bf4748507fb99p+236L);
 
         view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5"));
         assert (cast(float)view == 0);
         assert (cast(double)view == double.min_normal * double.epsilon);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 5e-324L);
+            assert (cast(real)view == 0x8.18995ce7aa0e1b2p-1077L);
 
         view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5B"));
         assert (cast(float)view == 0);
         assert (cast(double)view == double.min_normal * double.epsilon * 18);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 91e-324L);
+            assert (cast(real)view == 0x9.3594d9adeb09a55p-1073L);
 
         view = DecimalView!Args(false, -322, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == double.min_normal * double.epsilon * 20);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e-322L);
+            assert (cast(real)view == 0xa.1ebfb4219491a1fp-1073L);
 
         view = DecimalView!Args(false, -320, BigUIntView!Args.fromHexString("CA1CCB"));
         assert (cast(float)view == 0);
         assert (cast(double)view == double.min_normal * double.epsilon * 26809479897);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 13245643e-320L);
+            assert (cast(real)view == 0xc.7bf065b215888c7p-1043L);
 
         view = DecimalView!Args(false, -319, BigUIntView!Args.fromHexString("33CE7943FB"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 2.22507385851e-308);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 2.22507385851e-308L);
+            assert (cast(real)view == 0x8.000000000b103b6p-1025L);
 
         view = DecimalView!Args(false, -309, BigUIntView!Args.fromHexString("15"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x0.f19c2629ccf53p-1022);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 2.1e-308L);
+            assert (cast(real)view == 0xf.19c2629ccf52fc4p-1026L);
 
         view = DecimalView!Args(false, -340, BigUIntView!Args.fromHexString("AF87023B9BF0EE"));
         assert (cast(float)view == 0);
         assert (cast(double)view == double.min_normal * double.epsilon);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 4.9406564584124654e-324L);
+            assert (cast(real)view == 0xf.fffffffffffff64p-1078L);
 
         view = DecimalView!Args(false, 400, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e400L);
+            assert (cast(real)view == 0xd.a763fc8cb9ff9e6p+1325L);
 
         view = DecimalView!Args(false, 309, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e309L);
+            assert (cast(real)view == 0xb.201833b35d63f73p+1023L);
 
         view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 2e308L);
+            assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
 
         view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 2e308L);
+            assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
 
         view = DecimalView!Args(false, 295, BigUIntView!Args.fromHexString("1059949B7090"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1.7976931348624e308L);
+            assert (cast(real)view == 0x8.00000000006955ap+1021L);
 
         view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("0"));
         assert (cast(float)view == 0);
@@ -2071,25 +2071,25 @@ unittest
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e-325L);
+            assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
 
         view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e-326L);
+            assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
 
         view = DecimalView!Args(false, -500, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e-500L);
+            assert (cast(real)view == 0x8.33ada2003db9a8p-1664L);
 
         view = DecimalView!Args(false, -1000, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e-1000L);
+            assert (cast(real)view == 0x8.68a9188a89e1467p-3325L);
 
         view = DecimalView!Args(false, -4999, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
@@ -2113,49 +2113,49 @@ unittest
         assert (cast(float)view == 1.448997445238699f);
         assert (cast(double)view == 0x1.72f17f1f49aadp0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1.448997445238699L);
+            assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
 
         view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB"));
         assert (cast(float)view == 1.448997445238699f);
         assert (cast(double)view == 0x1.72f17f1f49aadp0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1.448997445238699L);
+            assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
 
         view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e-325L);
+            assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
 
         view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e-326L);
+            assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
 
         view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 1);
         assert (cast(double)view == 1);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1);
+            assert (cast(real)view == 0x8p-3L);
 
         view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3"));
         assert (cast(float)view == 3e-5f);
         assert (cast(double)view == 3e-5);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 3e-5L);
+            assert (cast(real)view == 0xf.ba8826aa8eb4635p-19L);
 
         view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0.1f);
         assert (cast(double)view == 0.1);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0.1L);
+            assert (cast(real)view == 0xc.ccccccccccccccdp-7L);
 
         view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F"));
         assert (cast(float)view == 0.9999999f);
         assert (cast(double)view == 0.9999999);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0.9999999L);
+            assert (cast(real)view == 0xf.ffffe5280d65435p-4L);
     }}
 }
 

From 24711494a74e4f4d7bfd271f3fc0dc7fd1606b38 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 25 Jun 2020 07:40:35 +0700
Subject: [PATCH 023/573] fix issue #285

---
 source/mir/bignum/low_level_view.d | 32 +++++++++++++++---------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index eab5ee1f..9fcd0b78 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1961,55 +1961,55 @@ unittest
     
         view = DecimalView!Args(false, -23, BigUIntView!Args.fromHexString("2209"));
         assert (cast(float)view == 8713e-23f);
-        assert (cast(double)view == 8713e-23);
+        assert (cast(double)view == 0x1.9b75b4e7de2b9p-64);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xc.dbada73ef15c401p-67);
 
         view = DecimalView!Args(false, 300, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == float.infinity);
-        assert (cast(double)view == 1e300);
+        assert (cast(double)view == 0x1.7e43c8800759cp+996);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xb.f21e44003acdd2dp+993);
 
         view = DecimalView!Args(false, 245, BigUIntView!Args.fromHexString("B3A73CEB227"));
         assert (cast(float)view == float.infinity);
-        assert (cast(double)view == 123456789.34567e250);
+        assert (cast(double)view == 0x1.48e3735333cb6p+857);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xa.471b9a999e5b01ep+854);
 
         view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
         assert (cast(float)view == float.infinity);
-        assert (cast(double)view == 943794359898089732078308743689303290943794359843568973207830874368930329.0);
+        assert (cast(double)view == 0x1.117e8e90a0ff7p+239);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.8bf4748507fb99p+236L);
 
         view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5"));
         assert (cast(float)view == 0);
-        assert (cast(double)view == double.min_normal * double.epsilon);
+        assert (cast(double)view == 0x0.0000000000001p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.18995ce7aa0e1b2p-1077L);
 
         view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5B"));
         assert (cast(float)view == 0);
-        assert (cast(double)view == double.min_normal * double.epsilon * 18);
+        assert (cast(double)view == 0x0.0000000000012p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x9.3594d9adeb09a55p-1073L);
 
         view = DecimalView!Args(false, -322, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
-        assert (cast(double)view == double.min_normal * double.epsilon * 20);
+        assert (cast(double)view == 0x0.0000000000014p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xa.1ebfb4219491a1fp-1073L);
 
         view = DecimalView!Args(false, -320, BigUIntView!Args.fromHexString("CA1CCB"));
         assert (cast(float)view == 0);
-        assert (cast(double)view == double.min_normal * double.epsilon * 26809479897);
+        assert (cast(double)view == 0x0.000063df832d9p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xc.7bf065b215888c7p-1043L);
 
         view = DecimalView!Args(false, -319, BigUIntView!Args.fromHexString("33CE7943FB"));
         assert (cast(float)view == 0);
-        assert (cast(double)view == 2.22507385851e-308);
+        assert (cast(double)view == 0x1.000000000162p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.000000000b103b6p-1025L);
 
@@ -2021,7 +2021,7 @@ unittest
 
         view = DecimalView!Args(false, -340, BigUIntView!Args.fromHexString("AF87023B9BF0EE"));
         assert (cast(float)view == 0);
-        assert (cast(double)view == double.min_normal * double.epsilon);
+        assert (cast(double)view == 0x0.0000000000001p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xf.fffffffffffff64p-1078L);
 
@@ -2111,13 +2111,13 @@ unittest
 
         view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB"));
         assert (cast(float)view == 1.448997445238699f);
-        assert (cast(double)view == 0x1.72f17f1f49aadp0);
+        assert (cast(double)view == 0x1.72f17f1f49aadp+0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
 
         view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB"));
         assert (cast(float)view == 1.448997445238699f);
-        assert (cast(double)view == 0x1.72f17f1f49aadp0);
+        assert (cast(double)view == 0x1.72f17f1f49aadp+0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
 
@@ -2135,25 +2135,25 @@ unittest
 
         view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 1);
-        assert (cast(double)view == 1);
+        assert (cast(double)view == 0x1p+0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8p-3L);
 
         view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3"));
         assert (cast(float)view == 3e-5f);
-        assert (cast(double)view == 3e-5);
+        assert (cast(double)view == 0x1.f75104d551d69p-16);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xf.ba8826aa8eb4635p-19L);
 
         view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0.1f);
-        assert (cast(double)view == 0.1);
+        assert (cast(double)view == 0x1.999999999999ap-4);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xc.ccccccccccccccdp-7L);
 
         view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F"));
         assert (cast(float)view == 0.9999999f);
-        assert (cast(double)view == 0.9999999);
+        assert (cast(double)view == 0x1.fffffca501acbp-1);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xf.ffffe5280d65435p-4L);
     }}

From 4c41a3b0170e3479571f1c4933ebf7c102828d28 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 25 Jun 2020 07:47:22 -0400
Subject: [PATCH 024/573] Fix stat docs (#287)

---
 source/mir/math/stat.d | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index f0f4d21b..bdaf56db 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -285,8 +285,8 @@ will have a `double` type if `F` is implicitly convertible to a floating point
 type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
 
 Params:
-    F: controls type of output
-    summation: algorithm for calculating sums (default: Summation.appropriate)
+    F = controls type of output
+    summation = algorithm for calculating sums (default: Summation.appropriate)
 Returns:
     The mean of all the elements in the input, must be floating point or complex type
 
@@ -605,8 +605,8 @@ will have a `double` type if `F` is implicitly convertible to a floating point
 type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
 
 Params:
-    F: controls type of output
-    summation: algorithm for calculating sums (default: Summation.appropriate)
+    F = controls type of output
+    summation = algorithm for calculating sums (default: Summation.appropriate)
 Returns:
     harmonic mean of all the elements of the input, must be floating point or complex type
 
@@ -2188,8 +2188,8 @@ will have a `double` type if `F` is implicitly convertible to a floating point
 type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
 
 Params:
-    F: controls type of output
-    varianceAlgo: algorithm for calculating variance (default: VarianceAlgo.online)
+    F = controls type of output
+    varianceAlgo = algorithm for calculating variance (default: VarianceAlgo.online)
     summation: algorithm for calculating sums (default: Summation.appropriate)
 
 Returns:
@@ -2558,9 +2558,9 @@ By default, if `F` is not floating point type, then the result will have a
 `double` type if `F` is implicitly convertible to a floating point type.
 
 Params:
-    F: controls type of output
-    varianceAlgo: algorithm for calculating variance (default: VarianceAlgo.online)
-    summation: algorithm for calculating sums (default: Summation.appropriate)
+    F = controls type of output
+    varianceAlgo = algorithm for calculating variance (default: VarianceAlgo.online)
+    summation = algorithm for calculating sums (default: Summation.appropriate)
 
 Returns:
     The standard deviation of the input, must be floating point type type
@@ -2905,9 +2905,9 @@ will square the centered values. The default `summarize` function is `mean`,
 which will return the mean of the squared centered values.
 
 Params:
-    centralTendency: function that will produce the value that the input is centered about, default is `mean`
-    transform: function to transform centered values, default squares the centered values
-    summarize: function to summarize the transformed centered values, default is `mean`
+    centralTendency = function that will produce the value that the input is centered about, default is `mean`
+    transform = function to transform centered values, default squares the centered values
+    summarize = function to summarize the transformed centered values, default is `mean`
 
 Returns:
     The dispersion of the input
@@ -2924,7 +2924,7 @@ template dispersion(
     {
         /++
         Params:
-            slice
+            slice = slice
         +/
         @fmamath auto dispersion(Iterator, size_t N, SliceKind kind)(
             Slice!(Iterator, N, kind) slice)

From 3efb7ef888e036e06a50fc5dd9476f1922cae8d3 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Fri, 26 Jun 2020 21:36:24 -0400
Subject: [PATCH 025/573] Add topology UT examples (#289)

---
 source/mir/ndslice/package.d  |   1 +
 source/mir/ndslice/topology.d | 180 +++++++++++++++++++++++++++++++++-
 2 files changed, 180 insertions(+), 1 deletion(-)

diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index c1db06dd..ff59657d 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -170,6 +170,7 @@ $(TR $(TDNW $(SUBMODULE topology) $(BR)
         $(SUBREF topology, universal)
         $(SUBREF topology, unsqueeze)
         $(SUBREF topology, unzip)
+        $(SUBREF topology, vmap)
         $(SUBREF topology, windows)
         $(SUBREF topology, zip)
     )
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index eafe6c46..0594ff40 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2730,6 +2730,57 @@ version(mir_test) unittest
     static assert(is(typeof(a) == typeof(b)));
 }
 
+/// Use map with byDim/alongDim to apply functions to each dimension
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.topology: byDim, alongDim;
+    import mir.ndslice.fuse: fuse;
+    import mir.math.stat: mean;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    // Use byDim/alongDim with map to compute mean of row/column.
+    assert(x.byDim!0.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
+    assert(x.byDim!1.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
+    assert(x.alongDim!1.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
+    assert(x.alongDim!0.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
+}
+
+/++
+Use map with a lambda and with byDim/alongDim, but may need to allocate result. 
+This example uses fuse, which allocates. Note: fuse!1 will transpose the result. 
++/
+version(mir_test)
+@safe pure
+unittest {
+    import mir.ndslice.topology: iota, byDim, alongDim, map;
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.slice: sliced;
+    
+    auto x = [1, 2, 3].sliced;
+    auto y = [1, 2].sliced;
+
+    auto s1 = iota(2, 3).byDim!0.map!(a => a * x).fuse;
+    assert(s1 == [[ 0, 2,  6],
+                  [ 3, 8, 15]]);
+    auto s2 = iota(2, 3).byDim!1.map!(a => a * y).fuse!1;
+    assert(s2 == [[ 0, 1,  2],
+                  [ 6, 8, 10]]);
+    auto s3 = iota(2, 3).alongDim!1.map!(a => a * x).fuse;
+    assert(s1 == [[ 0, 2,  6],
+                  [ 3, 8, 15]]);
+    auto s4 = iota(2, 3).alongDim!0.map!(a => a * y).fuse!1;
+    assert(s2 == [[ 0, 1,  2],
+                  [ 6, 8, 10]]);
+}
+
 ///
 pure version(mir_test) unittest
 {
@@ -2747,7 +2798,6 @@ pure version(mir_test) unittest
     assert(maxAvg(matrix) == 3.5);
 }
 
-
 /++
 Implements the homonym function (also known as `transform`) present
 in many languages of functional flavor. The call `slice.vmap(fun)`
@@ -2902,6 +2952,38 @@ version(none) version(mir_test) unittest
     }
 }
 
+/// Use map with byDim/alongDim to apply functions to each dimension
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse: fuse;
+    import mir.math.stat: mean;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    static struct Callable
+    {
+        double factor;
+        this(double f) {factor = f;}
+        auto opCall(U)(U x) const {return x.mean + factor; }
+        auto lightConst()() const @property { return Mul(factor); }
+    }
+
+    auto callable = Callable(0.0);
+
+    // Use byDim/alongDim with map to compute callable of row/column.
+    assert(x.byDim!0.vmap(callable).all!approxEqual([12.25 / 6, 17.0 / 6]));
+    assert(x.byDim!1.vmap(callable).all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
+    assert(x.alongDim!1.vmap(callable).all!approxEqual([12.25 / 6, 17.0 / 6]));
+    assert(x.alongDim!0.vmap(callable).all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
+}
+
 private auto hideStride
     (Iterator, SliceKind kind)
     (Slice!(Iterator, 1, kind) slice)
@@ -4267,6 +4349,54 @@ version(mir_test) unittest
     assert(z.front.front == iota([4], 1, 5));
 }
 
+/// Use alongDim to calculate column mean/row mean of 2-dimensional slice
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.topology: alongDim;
+    import mir.ndslice.fuse: fuse;
+    import mir.math.stat: mean;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    // Use alongDim with map to compute mean of row/column.
+    assert(x.alongDim!1.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
+    assert(x.alongDim!0.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
+
+    // FIXME
+    // Without using map, computes the mean of the whole slice
+    // assert(x.alongDim!1.mean == x.sliced.mean);
+    // assert(x.alongDim!0.mean == x.sliced.mean);
+}
+
+/++
+Use alongDim and map with a lambda, but may need to allocate result. This example
+uses fuse, which allocates. Note: fuse!1 will transpose the result. 
++/
+version(mir_test)
+@safe pure
+unittest {
+    import mir.ndslice.topology: iota, alongDim, map;
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.slice: sliced;
+    
+    auto x = [1, 2, 3].sliced;
+    auto y = [1, 2].sliced;
+
+    auto s1 = iota(2, 3).alongDim!1.map!(a => a * x).fuse;
+    assert(s1 == [[ 0, 2,  6],
+                  [ 3, 8, 15]]);
+    auto s2 = iota(2, 3).alongDim!0.map!(a => a * y).fuse!1;
+    assert(s2 == [[ 0, 1,  2],
+                  [ 6, 8, 10]]);
+}
+
 /++
 Returns a slice that can be iterated by dimension. Transposes dimensions on top and then packs them.
 
@@ -4562,6 +4692,54 @@ version(mir_test) unittest
     assert(a.front.front == iota([3], 1, 20));
 }
 
+/// Use byDim to calculate column mean/row mean of 2-dimensional slice
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.topology: byDim;
+    import mir.ndslice.fuse: fuse;
+    import mir.math.stat: mean;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+
+    auto x = [
+        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
+        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
+    ].fuse;
+
+    // Use byDim with map to compute mean of row/column.
+    assert(x.byDim!0.map!mean.all!approxEqual([12.25 / 6, 17.0 / 6]));
+    assert(x.byDim!1.map!mean.all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
+
+    // FIXME
+    // Without using map, computes the mean of the whole slice
+    // assert(x.byDim!0.mean == x.sliced.mean);
+    // assert(x.byDim!1.mean == x.sliced.mean);
+}
+
+/++
+Use byDim and map with a lambda, but may need to allocate result. This example
+uses fuse, which allocates. Note: fuse!1 will transpose the result. 
++/
+version(mir_test)
+@safe pure
+unittest {
+    import mir.ndslice.topology: iota, byDim, map;
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.slice: sliced;
+    
+    auto x = [1, 2, 3].sliced;
+    auto y = [1, 2].sliced;
+
+    auto s1 = iota(2, 3).byDim!0.map!(a => a * x).fuse;
+    assert(s1 == [[ 0, 2,  6],
+                  [ 3, 8, 15]]);
+    auto s2 = iota(2, 3).byDim!1.map!(a => a * y).fuse!1;
+    assert(s2 == [[ 0, 1,  2],
+                  [ 6, 8, 10]]);
+}
+
 // Ensure works on canonical
 @safe @nogc pure nothrow
 version(mir_test) unittest

From 7727b0edebb2ca0de5daba391ef80ff9627bf4f0 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Fri, 26 Jun 2020 21:51:03 -0400
Subject: [PATCH 026/573] Add assumeZeroMean VarianceAlgo (#291)

---
 dub.sdl                |   2 +-
 source/mir/math/stat.d | 259 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 257 insertions(+), 4 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 963828d0..9cac1e84 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.7"
 
 buildType "unittest" {
 	buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_test" "mir_bignum_test"
+    versions "mir_test"  "mir_bignum_test"
     dflags "-lowmem"
 }
 buildType "unittest-cov" {
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index bdaf56db..e13461ec 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -1670,8 +1670,25 @@ unittest
 }
 
 /++
-Output range that applies function `fun` to each input before summing
+Passing a centered input to `variance` or `standardDeviation` with the
+`assumeZeroMean` algorithm is equivalent to calculating `variance` or
+`standardDeviation` on the original input.
++/
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+
+    auto x = [1.0, 2, 3, 4, 5, 6].sliced;
+    assert(x.center.variance!"assumeZeroMean".approxEqual(x.variance));
+    assert(x.center.standardDeviation!"assumeZeroMean".approxEqual(x.standardDeviation));
+}
 
+/++
+Output range that applies function `fun` to each input before summing
 +/
 struct MapSummator(alias fun, T, Summation summation) 
     if(isMutable!T)
@@ -1790,7 +1807,12 @@ enum VarianceAlgo
     Calculates variance using a two-pass algorithm whereby the input is first 
     centered and then the sum of squares is calculated from that.
     +/
-    twoPass
+    twoPass,
+
+    /++
+    Calculates variance assuming the mean of the dataseries is zero. 
+    +/
+    assumeZeroMean
 }
 
 private
@@ -2180,6 +2202,233 @@ unittest
     assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
 }
 
+///
+struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
+    if (isMutable!T && varianceAlgo == VarianceAlgo.assumeZeroMean)
+{
+    import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
+
+    private size_t _count;
+    
+    ///
+    size_t count() @property
+    {
+        return _count;
+    }
+    
+    ///
+    F mean(F = T)() @property
+    {
+        return cast(F) 0;
+    }
+
+    ///
+    Summator!(T, summation) centeredSumOfSquares;
+
+    ///
+    this(Range)(Range r)
+        if (isIterable!Range)
+    {
+        this.put(r);
+    }
+
+    ///
+    this()(T x)
+    {
+        this.put(x);
+    }
+
+    ///
+    void put(Range)(Range r)
+        if (isIterable!Range)
+    {
+        foreach(x; r)
+        {
+            this.put(x);
+        }
+    }
+
+    ///
+    void put()(T x)
+    {
+        _count++;
+        centeredSumOfSquares.put(x * x);
+    }
+
+    ///
+    void put()(VarianceAccumulator!(T, varianceAlgo, summation) v)
+    {
+        _count += v.count;
+        centeredSumOfSquares.put(v.centeredSumOfSquares.sum);
+    }
+
+    ///
+    F variance(F = T)(bool isPopulation) @property
+    {
+        if (isPopulation == false)
+            return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
+        else
+            return cast(F) centeredSumOfSquares.sum / cast(F) count;
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+    auto x = a.center;
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
+    v.put(x);
+
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+
+    v.put(4.0);
+    assert(v.variance(PopulationTrueRT).approxEqual(70.76562 / 13));
+    assert(v.variance(PopulationTrueCT).approxEqual(70.76562 / 13));
+    assert(v.variance(PopulationFalseRT).approxEqual(70.76562 / 12));
+    assert(v.variance(PopulationFalseCT).approxEqual(70.76562 / 12));
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+    auto b = a.center;
+    auto x = b[0 .. 6];
+    auto y = b[6 .. $];
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(PopulationTrueRT).approxEqual(13.492188 / 6));
+    assert(v.variance(PopulationTrueCT).approxEqual(13.492188 / 6));
+    assert(v.variance(PopulationFalseRT).approxEqual(13.492188 / 5));
+    assert(v.variance(PopulationFalseCT).approxEqual(13.492188 / 5));
+
+    v.put(y);
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+    auto b = a.center;
+    auto x = b[0 .. 6];
+    auto y = b[6 .. $];
+
+    enum PopulationTrueCT = true;
+    enum PopulationFalseCT = false;
+    bool PopulationTrueRT = true;
+    bool PopulationFalseRT = false;
+
+    VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(PopulationTrueRT).approxEqual(13.492188 / 6));
+    assert(v.variance(PopulationTrueCT).approxEqual(13.492188 / 6));
+    assert(v.variance(PopulationFalseRT).approxEqual(13.492188 / 5));
+    assert(v.variance(PopulationFalseCT).approxEqual(13.492188 / 5));
+
+    VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) w;
+    w.put(y);
+    v.put(w);
+    assert(v.variance(PopulationTrueRT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationTrueCT).approxEqual(54.76562 / 12));
+    assert(v.variance(PopulationFalseRT).approxEqual(54.76562 / 11));
+    assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto a = [1.0 + 3i, 2, 3].sliced;
+    auto x = a.center;
+
+    VarianceAccumulator!(cdouble, VarianceAlgo.assumeZeroMean, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(true).approxEqual((-4.0 - 6i) / 3));
+    assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+    auto x = a.center;
+
+    VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(false).approxEqual(54.76562 / 11));
+
+    v.put(4.0);
+    assert(v.variance(false).approxEqual(70.76562 / 12));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.math.common: approxEqual;
+
+    auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
+    auto b = a.center;
+    auto x = b[0 .. 6];
+    auto y = b[6 .. $];
+
+    VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(false).approxEqual(13.492188 / 5));
+
+    VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) w;
+    w.put(y);
+    v.put(w);
+    assert(v.variance(false).approxEqual(54.76562 / 11));
+}
+
 /++
 Calculates the variance of the input
 
@@ -2344,7 +2593,7 @@ unittest
 
 /// Can also set algorithm type
 version(mir_test)
-@safe pure nothrow
+//@safe pure nothrow
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -2365,6 +2614,10 @@ unittest
     // But the two-pass algorithm provides a consistent answer
     auto z1 = x.variance!"twoPass";
     assert(z1.approxEqual(y));
+
+    // And the assumeZeroMean algorithm is way off
+    auto z2 = x.variance!"assumeZeroMean";
+    assert(z2.approxEqual(1.2e19 / 11));
 }
 
 /// Can also set algorithm or output type

From 681a79c55e4e267f543e986300895631495b2997 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 27 Jun 2020 09:16:58 -0400
Subject: [PATCH 027/573] Add one more vmap example (#292)

---
 source/mir/ndslice/topology.d | 43 +++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 0594ff40..165f9896 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2984,6 +2984,49 @@ unittest
     assert(x.alongDim!0.vmap(callable).all!approxEqual([1, 4.25, 3.25, 1.5, 2.5, 2.125]));
 }
 
+/++
+Use map with a lambda and with byDim/alongDim, but may need to allocate result. 
+This example uses fuse, which allocates. Note: fuse!1 will transpose the result. 
++/
+version(mir_test)
+@safe pure
+unittest {
+    import mir.ndslice.topology: iota, alongDim, map;
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.slice: sliced;
+
+    static struct Mul(T)
+    {
+        T factor;
+        this(T f) { factor = f; }
+        auto opCall(U)(U x) {return x * factor; }
+        auto lightConst()() const @property { return Mul!(typeof(factor.lightConst))(factor.lightConst); }
+    }
+
+    auto a = [1, 2, 3].sliced;
+    auto b = [1, 2].sliced;
+    auto A = Mul!(typeof(a))(a);
+    auto B = Mul!(typeof(b))(b);
+
+    auto x = [
+        [0, 1, 2],
+        [3, 4, 5]
+    ].fuse;
+
+    auto s1 = x.byDim!0.vmap(A).fuse;
+    assert(s1 == [[ 0, 2,  6],
+                  [ 3, 8, 15]]);
+    auto s2 = x.byDim!1.vmap(B).fuse!1;
+    assert(s2 == [[ 0, 1,  2],
+                  [ 6, 8, 10]]);
+    auto s3 = x.alongDim!1.vmap(A).fuse;
+    assert(s1 == [[ 0, 2,  6],
+                  [ 3, 8, 15]]);
+    auto s4 = x.alongDim!0.vmap(B).fuse!1;
+    assert(s2 == [[ 0, 1,  2],
+                  [ 6, 8, 10]]);
+}
+
 private auto hideStride
     (Iterator, SliceKind kind)
     (Slice!(Iterator, 1, kind) slice)

From 7c5e8025e85cb0aeed4755a4cc19ea9860d1d4ca Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 30 Jun 2020 20:29:57 -0400
Subject: [PATCH 028/573] Add map zipped tensor UT (#295)

---
 source/mir/ndslice/topology.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 165f9896..b0b1ffa7 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2684,6 +2684,7 @@ version(mir_test) unittest
     auto z = zip(sl1, sl2);
 
     assert(zip(sl1, sl2).map!"a + b" == sl1 + sl2);
+    assert(zip(sl1, sl2).map!((a, b) => a + b) == sl1 + sl2);
 }
 
 /++

From f193ff6ec8f9db3a1b33f0bb697c282045df14b1 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Fri, 3 Jul 2020 10:56:20 +0700
Subject: [PATCH 029/573] Update README.md

---
 README.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 02108ee3..4cabc6fe 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,9 @@ Mir Algorithm
 #### [API Documentation](http://mir-algorithm.libmir.org)
 
 #### Blogs
-  - Tasty D - [Multidimensional Arrays in D](https://tastyminerals.github.io/tasty-blog/dlang/2020/03/01/how-to-use-external-libraries-in-d-project.html)
-  - Tasty D - [Using External D Libraries in D Scripts and Projects](https://tastyminerals.github.io/tasty-blog/dlang/2020/03/01/how-to-use-external-libraries-in-d-project.html)
+  - Tasty D - [Multidimensional Arrays in D](https://tastyminerals.github.io/tasty-blog/dlang/2020/03/22/multidimensional_arrays_in_d.html)
+  - Tasty D - [Using External D Libraries in D Scripts and Projects](https://tastyminerals.github.io/tasty-blog/dlang/2020/03/01/how_to_use_external_libraries_in_d_project.html)
+  - Tasty D - [Pretty-printing D Arrays](https://tastyminerals.github.io/tasty-blog/dlang/2020/06/25/pretty_printing_arrays.html)
   - Shigeki Karita - [D言語で数値計算 mir-algorithm](https://shigekikarita.github.io/blog/2017/09/22/026.html)
   - Shigeki Karita - [D言語(mir)でNumPyを拡張する](https://qiita.com/ShigekiKarita/items/af84b0ef864608ee1f21) (mir-pybuffer integration)
   - [Mir Blog](http://blog.mir.dlang.io/) (deprecated)

From cc7df88ca35cd172e257e26a09645d36cebc651c Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Fri, 3 Jul 2020 00:19:16 -0400
Subject: [PATCH 030/573] Improve docs for median (#294)

---
 source/mir/math/stat.d | 50 +++++++++++++++++++++++++++++++-----------
 1 file changed, 37 insertions(+), 13 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index e13461ec..0c11f295 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -1229,9 +1229,16 @@ unittest
 /++
 Computes the median of `slice`.
 
+By default, if `F` is not floating point type or complex type, then the result
+will have a `double` type if `F` is implicitly convertible to a floating point 
+type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+
 Can also pass a boolean variable, `allowModify`, that allows the input slice to
 be modified. By default, a reference-counted copy is made. 
 
+Params:
+    F = output type
+    allowModify = Allows the input slice to be modified, default is false
 Returns:
     the median of the slice
 
@@ -1240,8 +1247,16 @@ See_also:
 +/
 template median(F, bool allowModify = false)
 {
-    F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+    /++
+    Params:
+        slice = slice
+    +/
+    meanType!F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
+        static assert (!allowModify ||
+                       isMutable!(DeepElementType!(Slice!(Iterator, N, kind))),
+                           "allowModify must be false or the input must be mutable");
+        alias G = typeof(return);
         size_t len = slice.elementCount;
         assert(len > 0, "median: slice must have length greater than zero");
 
@@ -1253,9 +1268,9 @@ template median(F, bool allowModify = false)
             if (len > 2) {
                 auto val = slice.rcslice.flattened;
                 auto temp = val.lightScope;
-                return .median!(F, true)(temp);
+                return .median!(G, true)(temp);
             } else {
-                return mean!F(slice);
+                return mean!G(slice);
             }
         } else {
             import mir.ndslice.sorting: partitionAt;
@@ -1266,26 +1281,31 @@ template median(F, bool allowModify = false)
                 size_t half_n = len / 2;
                 partitionAt(temp, half_n);
                 if (len % 2 == 1) {
-                    return cast(F) temp[half_n];
+                    return cast(G) temp[half_n];
                 } else {
                     //move largest value in first half of slice to half_n - 1
                     partitionAt(temp[0 .. half_n], half_n - 1);
-                    return (temp[half_n - 1] + temp[half_n]) / cast(F) 2;
+                    return (temp[half_n - 1] + temp[half_n]) / cast(G) 2;
                 }
             } else {
-                return smallMedianImpl!(F)(temp);
+                return smallMedianImpl!(G)(temp);
             }
         }
     }
 }
 
-///
+/// ditto
 template median(bool allowModify = false)
 {
-    sumType!(Slice!(Iterator, N, kind))
-        median
-            (Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice) {
-        return .median!(sumType!(Slice!(Iterator, N, kind)), allowModify)(slice.move);
+    /// ditto
+    meanType!(Slice!(Iterator, N, kind))
+        median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+    {
+        static assert (!allowModify ||
+                       isMutable!(DeepElementType!(Slice!(Iterator, N, kind))),
+                           "allowModify must be false or the input must be mutable");
+        alias F = typeof(return);
+        return .median!(F, allowModify)(slice.move);
     }
 }
 
@@ -1356,7 +1376,7 @@ unittest
 }
 
 /++
-For integral slices, pass output type as template parameter to ensure output
+For integral slices, can pass output type as template parameter to ensure output
 type is correct
 +/
 version(mir_test)
@@ -1366,7 +1386,11 @@ unittest
     import mir.ndslice.slice: sliced;
 
     auto x = [9, 1, 0, 2, 3, 4, 6, 8, 7, 10].sliced;
-    assert(x.median!float == 5);
+    assert(x.median!float == 5f);
+
+    auto y = x.median;
+    assert(y == 5.0);
+    static assert(is(typeof(y) == double));
 }
 
 version(mir_test)

From 1882b59e8fe27d0f2f67497e4abe9c7a28e4bc2d Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Mon, 6 Jul 2020 07:53:40 -0400
Subject: [PATCH 031/573] Make statistical types public (#298)

* Make statistical types public

* Make moment_ops/outputRange_ops public

* Adjust types/ops to package(mir)
---
 source/mir/math/stat.d | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 0c11f295..2b421848 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -26,7 +26,9 @@ import mir.primitives;
 import std.traits: isArray, isMutable, isIterable, isIntegral, CommonType;
 import mir.internal.utility: isFloatingPoint;
 
-package template statType(T, bool checkComplex = true)
+///
+package(mir)
+template statType(T, bool checkComplex = true)
 {
     import mir.internal.utility: isFloatingPoint, isComplex;
 
@@ -131,7 +133,9 @@ unittest
     static assert(is(statType!Foo == double)); // note: this is not ints
 }
 
-package template meanType(T)
+///
+package(mir)
+template meanType(T)
 {
     import mir.math.sum: sumType;
     import mir.internal.utility: isFloatingPoint, isComplex;
@@ -552,7 +556,9 @@ unittest
     assert(x.sliced.mean!float == 29.25 / 12);
 }
 
-package template hmeanType(T)
+///
+package(mir)
+template hmeanType(T)
 {
     import mir.math.sum: sumType;
     
@@ -944,7 +950,9 @@ unittest
     assert(x.gmean.approxEqual(2.60517108));
 }
 
-package template gmeanType(T)
+///
+package(mir)
+template gmeanType(T)
 {
     import mir.math.numeric: prodType;
     
@@ -1839,7 +1847,7 @@ enum VarianceAlgo
     assumeZeroMean
 }
 
-private
+package(mir)
 mixin template moment_ops(T,
                           Summation summation)
 {
@@ -1859,7 +1867,7 @@ mixin template moment_ops(T,
     }
 }
 
-private
+package(mir)
 mixin template outputRange_ops(T)
 {
     ///
@@ -2794,7 +2802,9 @@ unittest
     assert(x.sliced.variance!float.approxEqual(54.76562 / 11));
 }
 
-package template stdevType(T)
+///
+package(mir)
+template stdevType(T)
 {
     import mir.internal.utility: isFloatingPoint;
     

From 147046118233d447d7f6df544dc16cbe604212a0 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 6 Jul 2020 20:50:36 +0700
Subject: [PATCH 032/573] convert tabs to spaces

---
 dub.sdl | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 9cac1e84..1deeadf4 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -8,18 +8,18 @@ license "BSL-1.0"
 dependency "mir-core" version=">=1.1.7"
 
 buildType "unittest" {
-	buildOptions "unittests" "debugMode" "debugInfo"
+    buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_test"  "mir_bignum_test"
     dflags "-lowmem"
 }
 buildType "unittest-cov" {
-	buildOptions "unittests" "coverage" "debugMode" "debugInfo"
-	versions "mir_test"  "mir_bignum_test"
+    buildOptions "unittests" "coverage" "debugMode" "debugInfo"
+    versions "mir_test"  "mir_bignum_test"
     dflags "-lowmem"
 }
 buildType "unittest-release" {
-	buildOptions "unittests" "releaseMode" "optimize" "inline" "noBoundsCheck"
-	versions "mir_test"  "mir_bignum_test"
+    buildOptions "unittests" "releaseMode" "optimize" "inline" "noBoundsCheck"
+    versions "mir_test"  "mir_bignum_test"
     dflags "-lowmem"
 }
 

From f363759a44f56fc47dbacefd50908d5dad094058 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 6 Jul 2020 21:58:22 +0700
Subject: [PATCH 033/573] fix issue #293

---
 source/mir/math/stat.d       | 26 ++++++++++++++++++--------
 source/mir/ndslice/sorting.d |  8 ++++----
 2 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 2b421848..52ff97d0 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -19,12 +19,12 @@ T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
 module mir.math.stat;
 
 import core.lifetime: move;
-import mir.ndslice.slice: Slice, SliceKind;
+import mir.internal.utility: isFloatingPoint;
 import mir.math.common: fmamath;
 import mir.math.sum;
+import mir.ndslice.slice: Slice, SliceKind;
 import mir.primitives;
-import std.traits: isArray, isMutable, isIterable, isIntegral, CommonType;
-import mir.internal.utility: isFloatingPoint;
+import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType;
 
 ///
 package(mir)
@@ -1259,23 +1259,24 @@ template median(F, bool allowModify = false)
     Params:
         slice = slice
     +/
-    meanType!F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+    meanType!F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice) @nogc
     {
         static assert (!allowModify ||
-                       isMutable!(DeepElementType!(Slice!(Iterator, N, kind))),
+                       isMutable!(slice.DeepElement),
                            "allowModify must be false or the input must be mutable");
         alias G = typeof(return);
         size_t len = slice.elementCount;
         assert(len > 0, "median: slice must have length greater than zero");
 
-        import mir.ndslice.topology: flattened;
+        import mir.ndslice.topology: as, flattened;
 
         static if (!allowModify) {
             import mir.ndslice.allocation: rcslice;
             
             if (len > 2) {
-                auto val = slice.rcslice.flattened;
-                auto temp = val.lightScope;
+                auto view = slice.lightScope;
+                auto val = view.as!(Unqual!(slice.DeepElement)).rcslice;
+                auto temp = val.lightScope.flattened;
                 return .median!(G, true)(temp);
             } else {
                 return mean!G(slice);
@@ -1383,6 +1384,15 @@ unittest
     assert(x1.median!(float, true) == 5);
 }
 
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    static immutable x = [9.0, 1, 0, 2, 3];
+    assert(x.sliced.median == 2);
+}
+
 /++
 For integral slices, can pass output type as template parameter to ensure output
 type is correct
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 138fa1e6..7429c75a 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -1042,7 +1042,7 @@ template partitionAt(alias less = "a < b")
                 function is finished.
         +/
         void partitionAt(Iterator, size_t N, SliceKind kind)
-            (Slice!(Iterator, N, kind) slice, size_t nth) @trusted nothrow
+            (Slice!(Iterator, N, kind) slice, size_t nth) @trusted nothrow @nogc
         {
             import mir.qualifier: lightScope;
             import core.lifetime: move;
@@ -1160,7 +1160,7 @@ unittest {
     assert(checkTopNAll([ 0,  2,  7, 16,  2, 20,  1, 11, 17,  5, 22, 17, 25, 13, 14,  5, 22, 21, 24, 14].sliced));
 }
 
-private @trusted pure nothrow
+private @trusted pure nothrow @nogc
 void partitionAtImpl(alias less, Iterator)(
     Iterator loI, 
     Iterator hiI, 
@@ -1372,7 +1372,7 @@ unittest {
     assert(x[nth] == 10);
 }
 
-private @trusted pure nothrow
+private @trusted pure nothrow @nogc
 Iterator partitionAtPartition(alias less, Iterator)(
     ref Iterator frontI, 
     ref Iterator lastI, 
@@ -1439,7 +1439,7 @@ unittest {
     assert(x[n - 1] == x_sort[n - 1]);
 }
 
-private @trusted pure nothrow
+private @trusted pure nothrow @nogc
 Iterator partitionAtPartitionOffMedian(alias less, bool leanRight, Iterator)(
     ref Iterator frontI, 
     ref Iterator lastI, 

From cdfc04bf93bc9a8d9e854bd96d6a5ce6c72971d0 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 6 Jul 2020 22:00:08 +0700
Subject: [PATCH 034/573] fix issue #237

---
 source/mir/ndslice/slice.d | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index b7d3b2a1..d8d3d0d7 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -552,6 +552,24 @@ package move or copy data. The operations are only carried out on lengths, strid
 and pointers. If a slice is defined over a range, only the shift of the initial element
 changes instead of the range.
 
+Mir n-dimensional Slices can be one of the three kinds.
+
+$(H4 Contiguous slice)
+
+Contiguous in memory (or in a user-defined iterator's field) row-major tensor that doesn't store strides because they can be computed on the fly using lengths.
+The row stride is always equaled 1.
+
+$(H4 Canonical slice)
+
+Canonical slice as contiguous in memory (or in a user-defined iterator's field) rows of a row-major tensor, it doesn't store the stride for row dimension because it is always equaled 1.
+BLAS/LAPACK matrices are Canonical but originally have column-major order.
+In the same time you can use 2D Canonical Slices with LAPACK assuming that rows are columns and columns are rows.
+
+$(H4 Universal slice)
+
+A row-major tensor that stores the strides for all dimensions.
+NumPy strides are Universal.
+
 $(H4 Internal Representation for Universal Slices)
 
 Type definition

From 175f14c00a250b057cc52f0285086b6fb4addc04 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 7 Jul 2020 03:28:51 -0400
Subject: [PATCH 035/573] Change mir_test_topN to mir_test (#299)

---
 source/mir/ndslice/sorting.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 7429c75a..91276b4e 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -991,7 +991,7 @@ unittest {
 }
 
 
-version(mir_test_topN)
+version(mir_test)
 @trusted pure nothrow
 unittest
 {

From 9a22141c9de5c2196c68e68f7841fbe1b3b5ff44 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 7 Jul 2020 03:29:06 -0400
Subject: [PATCH 036/573] Fix hmeanType error message (#300)

---
 source/mir/math/stat.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 52ff97d0..171f491b 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -571,7 +571,7 @@ template hmeanType(T)
         alias V = typeof(cast(U) 1 / ((cast(U) 1 / U.init + cast(U) 1 / U.init) / cast(U) 2));
         alias hmeanType = statType!V;
     } else {
-        static assert(0, "meanType: Can't calculate hmean of elements of type " ~ U.stringof);
+        static assert(0, "hmeanType: Can't calculate hmean of elements of type " ~ U.stringof);
     }
 }
 

From d080620302d0f6e5aeacb1eb31aa5d76f37346c2 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 7 Jul 2020 03:29:49 -0400
Subject: [PATCH 037/573] Remove moment_ops/outputRange_ops mixins (#301)

* Remove moment_ops

* Remove outputRange_ops
---
 source/mir/math/stat.d | 91 +++++++++++++++++++++++++++---------------
 1 file changed, 59 insertions(+), 32 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 171f491b..b0a49eb8 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -1857,29 +1857,12 @@ enum VarianceAlgo
     assumeZeroMean
 }
 
-package(mir)
-mixin template moment_ops(T,
-                          Summation summation)
+///
+struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
+    if (isMutable!T && varianceAlgo == VarianceAlgo.naive)
 {
-    ///
-    MeanAccumulator!(T, summation) meanAccumulator;
-
-    ///
-    size_t count() @property
-    {
-        return meanAccumulator.count;
-    }
-
-    ///
-    F mean(F = T)() @property
-    {
-        return meanAccumulator.mean;
-    }
-}
+    import mir.functional: naryFun;
 
-package(mir)
-mixin template outputRange_ops(T)
-{
     ///
     this(Range)(Range r)
         if (isIterable!Range)
@@ -1893,16 +1876,21 @@ mixin template outputRange_ops(T)
     {
         this.put(x);
     }
-}
 
-///
-struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
-    if (isMutable!T && varianceAlgo == VarianceAlgo.naive)
-{
-    import mir.functional: naryFun;
+    ///
+    MeanAccumulator!(T, summation) meanAccumulator;
 
-    mixin moment_ops!(T, summation);
-    mixin outputRange_ops!T;
+    ///
+    size_t count() @property
+    {
+        return meanAccumulator.count;
+    }
+
+    ///
+    F mean(F = T)() @property
+    {
+        return meanAccumulator.mean;
+    }
 
     ///
     Summator!(T, summation) sumOfSquares;
@@ -1971,8 +1959,34 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     if (isMutable!T && 
         varianceAlgo == VarianceAlgo.online)
 {
-    mixin moment_ops!(T, summation);
-    mixin outputRange_ops!T;
+    ///
+    this(Range)(Range r)
+        if (isIterable!Range)
+    {
+        import core.lifetime: move;
+        this.put(r.move);
+    }
+
+    ///
+    this()(T x)
+    {
+        this.put(x);
+    }
+
+    ///
+    MeanAccumulator!(T, summation) meanAccumulator;
+
+    ///
+    size_t count() @property
+    {
+        return meanAccumulator.count;
+    }
+
+    ///
+    F mean(F = T)() @property
+    {
+        return meanAccumulator.mean;
+    }
 
     ///
     Summator!(T, summation) centeredSumOfSquares;
@@ -2173,7 +2187,20 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     import mir.functional: naryFun;
     import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
 
-    mixin moment_ops!(T, summation);
+    ///
+    MeanAccumulator!(T, summation) meanAccumulator;
+
+    ///
+    size_t count() @property
+    {
+        return meanAccumulator.count;
+    }
+
+    ///
+    F mean(F = T)() @property
+    {
+        return meanAccumulator.mean;
+    }
 
     ///
     Summator!(T, summation) centeredSumOfSquares;

From 42d4e6a7fbc316cad4db71037de94fe41a2ec548 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 7 Jul 2020 06:48:47 -0400
Subject: [PATCH 038/573] Make ResolveSummationType package(mir)

---
 source/mir/math/sum.d | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index e7ab248d..f7a8b97c 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1940,7 +1940,8 @@ unittest
     }
 }
 
-package template ResolveSummationType(Summation summation, Range, F)
+package(mir)
+template ResolveSummationType(Summation summation, Range, F)
 {
     static if (summation == Summation.appropriate)
     {
@@ -1983,7 +1984,8 @@ private T summationInitValue(T)()
     }
 }
 
-package template elementType(T)
+package(mir)
+template elementType(T)
 {
     import mir.ndslice.slice: isSlice, DeepElementType;
     import std.traits: Unqual, ForeachType;
@@ -1998,7 +2000,8 @@ package template elementType(T)
     }
 }
 
-package template sumType(Range)
+package(mir)
+template sumType(Range)
 {
     alias T = elementType!Range;
 

From d57b74d20f05a64504a0eafa5a5b6487bf6e8cfb Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Wed, 8 Jul 2020 08:24:12 +0700
Subject: [PATCH 039/573] fix buildkite for dmd/pull/11387 (#303)

---
 source/mir/bignum/low_level_view.d | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 9fcd0b78..1038ea7a 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1891,7 +1891,7 @@ unittest
         assert (cast(float)view == -0);
         assert (cast(double)view == -0x1.1p-511);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == -0x8.80000000000019fp-514);
+            assert (cast(real)view == -0x8.80000000000019fp-514L);
 
         view = DecimalView!Args(true, 293, BigUIntView!Args.fromHexString("36496F6C4ED38"));
         assert (cast(float)view == -float.infinity);
@@ -1903,7 +1903,7 @@ unittest
         assert (cast(float)view == 1);
         assert (cast(double)view == 1);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1);
+            assert (cast(real)view == 1L);
 
         view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3"));
         assert (cast(float)view == 3e-5f);
@@ -1963,19 +1963,19 @@ unittest
         assert (cast(float)view == 8713e-23f);
         assert (cast(double)view == 0x1.9b75b4e7de2b9p-64);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xc.dbada73ef15c401p-67);
+            assert (cast(real)view == 0xc.dbada73ef15c401p-67L);
 
         view = DecimalView!Args(false, 300, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 0x1.7e43c8800759cp+996);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xb.f21e44003acdd2dp+993);
+            assert (cast(real)view == 0xb.f21e44003acdd2dp+993L);
 
         view = DecimalView!Args(false, 245, BigUIntView!Args.fromHexString("B3A73CEB227"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 0x1.48e3735333cb6p+857);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xa.471b9a999e5b01ep+854);
+            assert (cast(real)view == 0xa.471b9a999e5b01ep+854L);
 
         view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
         assert (cast(float)view == float.infinity);
@@ -2059,13 +2059,13 @@ unittest
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0);
+            assert (cast(real)view == 0L);
 
         view = DecimalView!Args(false, 0, BigUIntView!Args.init);
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0);
+            assert (cast(real)view == 0L);
 
         view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
@@ -2095,13 +2095,13 @@ unittest
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0);
+            assert (cast(real)view == 0L);
 
         view = DecimalView!Args(false, -10000, BigUIntView!Args.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0);
+            assert (cast(real)view == 0L);
 
         view = DecimalView!Args(false, -4969, BigUIntView!Args.fromHexString("329659A941466C6B"));
         assert (cast(float)view == 0);

From da1402623ab259012d091a5fe7b5db0058a2aa07 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 9 Jul 2020 03:26:42 -0400
Subject: [PATCH 040/573] Update median implementation (#304)

---
 source/mir/math/stat.d | 48 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index b0a49eb8..90dea653 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -22,7 +22,7 @@ import core.lifetime: move;
 import mir.internal.utility: isFloatingPoint;
 import mir.math.common: fmamath;
 import mir.math.sum;
-import mir.ndslice.slice: Slice, SliceKind;
+import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
 import mir.primitives;
 import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType;
 
@@ -1318,6 +1318,28 @@ template median(bool allowModify = false)
     }
 }
 
+/++
+Params:
+    ar = array
++/
+meanType!(T[]) median(T)(scope const T[] ar...)
+{
+    import mir.ndslice.slice: sliced;
+
+    alias F = typeof(return);
+    return median!(F, false)(ar.sliced);
+}
+
+/++
+Params:
+    withAsSlice = input that satisfies hasAsSlice
++/
+auto median(T)(T withAsSlice)
+    if (hasAsSlice!T)
+{
+    return median(withAsSlice.asSlice);
+}
+
 /// Median of vector
 version(mir_test)
 @safe pure nothrow
@@ -1332,6 +1354,18 @@ unittest
     assert(x1.median == 5);
 }
 
+/// Median of dynamic array
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    auto x0 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5];
+    assert(x0.median == 5);
+
+    auto x1 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10];
+    assert(x1.median == 5);
+}
+
 /// Median of matrix
 version(mir_test)
 @safe pure
@@ -1384,6 +1418,15 @@ unittest
     assert(x1.median!(float, true) == 5);
 }
 
+/// Arbitrary median
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    assert(median(0, 1, 2, 3, 4) == 2);
+}
+
+// @nogc test
 version(mir_test)
 @safe pure nothrow @nogc
 unittest
@@ -1411,6 +1454,7 @@ unittest
     static assert(is(typeof(y) == double));
 }
 
+// additional logic tests
 version(mir_test)
 @safe pure nothrow
 unittest
@@ -1461,6 +1505,7 @@ unittest
     assert(x.median!float.approxEqual(2.5));
 }
 
+// smallMedianImpl tests
 version(mir_test)
 @safe pure nothrow
 unittest
@@ -1534,6 +1579,7 @@ F smallMedianImpl(F, Iterator)(Slice!Iterator slice)
     }
 }
 
+// smallMedianImpl tests
 version(mir_test)
 @safe pure nothrow
 unittest

From 1497ca39cf5bd67d26ed528b5aed337604411d1f Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 9 Jul 2020 07:34:48 -0400
Subject: [PATCH 041/573] Remove dispersion (#305)

---
 source/mir/math/stat.d | 287 -----------------------------------------
 1 file changed, 287 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 90dea653..4685bf2e 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -3260,293 +3260,6 @@ unittest
     assert(x.sliced.standardDeviation!float.approxEqual(sqrt(54.76562 / 11)));
 }
 
-/++
-Calculates the dispersion of the input.
-
-For an input `x`, this function first centers `x` by subtracting each `e` in `x`
-by the result of `centralTendency`, then it transforms the centered values using
-the function `transform`, and then finally summarizes that information using
-the `summarize` funcion. 
-
-The default functions provided are equivalent to calculating the population
-variance. The `centralTendency` default is the `mean` function, which results
-in the input being centered about the mean. The default `transform` function
-will square the centered values. The default `summarize` function is `mean`,
-which will return the mean of the squared centered values.
-
-Params:
-    centralTendency = function that will produce the value that the input is centered about, default is `mean`
-    transform = function to transform centered values, default squares the centered values
-    summarize = function to summarize the transformed centered values, default is `mean`
-
-Returns:
-    The dispersion of the input
-+/
-template dispersion(
-    alias centralTendency = mean,
-    alias transform = "a * a",
-    alias summarize = mean)
-{
-    import mir.functional: naryFun;
-    import mir.ndslice.slice: Slice, SliceKind, sliced, hasAsSlice;
-
-    static if (__traits(isSame, naryFun!transform, transform))
-    {
-        /++
-        Params:
-            slice = slice
-        +/
-        @fmamath auto dispersion(Iterator, size_t N, SliceKind kind)(
-            Slice!(Iterator, N, kind) slice)
-        {
-            import core.lifetime: move;
-            import mir.ndslice.topology: map;
-
-            return summarize(slice.move.center!centralTendency.map!transform);
-        }
-        
-        /// ditto
-        @fmamath auto dispersion(T)(scope const T[] ar...)
-        {
-            return dispersion(ar.sliced);
-        }
-
-        /// ditto
-        @fmamath auto dispersion(T)(T withAsSlice)
-            if (hasAsSlice!T)
-        {
-            return dispersion(withAsSlice.asSlice);
-        }
-    }
-    else
-        alias dispersion = .dispersion!(centralTendency, naryFun!transform, summarize);
-}
-
-///
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-    import mir.math.common: approxEqual;
-    import mir.functional: naryFun;
-
-    assert(dispersion([1.0, 2, 3]).approxEqual(2.0 / 3));
-
-    assert(dispersion([1.0 + 3i, 2, 3]).approxEqual((-4.0 - 6i) / 3));
-
-    assert(dispersion!(mean!float, "a * a", mean!float)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 6));
-    
-    static assert(is(typeof(dispersion!(mean!float, "a ^^ 2", mean!float)([1, 2, 3])) == float));
-}
-
-/// Dispersion of vector
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-    import mir.math.common: approxEqual;
-
-    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
-              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
-
-    assert(x.dispersion.approxEqual(54.76562 / 12));
-}
-
-/// Dispersion of matrix
-version(mir_test)
-@safe pure
-unittest
-{
-    import mir.ndslice.fuse: fuse;
-    import mir.math.common: approxEqual;
-
-    auto x = [
-        [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
-        [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
-    ].fuse;
-
-    assert(x.dispersion.approxEqual(54.76562 / 12));
-}
-
-/// Column dispersion of matrix
-version(mir_test)
-@safe pure
-unittest
-{
-    import mir.ndslice.fuse: fuse;
-    import mir.ndslice.topology: alongDim, byDim, map;
-    import mir.math.common: approxEqual;
-    import mir.algorithm.iteration: all;
-
-    auto x = [
-        [0.0,  1.0, 1.5, 2.0], 
-        [3.5, 4.25, 2.0, 7.5],
-        [5.0,  1.0, 1.5, 0.0]
-    ].fuse;
-    auto result = [13.16667 / 3, 7.041667 / 3, 0.1666667 / 3, 30.16667 / 3];
-
-    // Use byDim or alongDim with map to compute dispersion of row/column.
-    assert(x.byDim!1.map!dispersion.all!approxEqual(result));
-    assert(x.alongDim!0.map!dispersion.all!approxEqual(result));
-
-    // FIXME
-    // Without using map, computes the dispersion of the whole slice
-    // assert(x.byDim!1.dispersion == x.sliced.dispersion);
-    // assert(x.alongDim!0.dispersion == x.sliced.dispersion);
-}
-
-/// Can also set functions to change type of dispersion that is used
-version(mir_test)
-@safe
-unittest
-{
-    import mir.ndslice.slice: sliced;
-    import mir.math.common: approxEqual, fabs, sqrt;
-    import mir.functional: naryFun;
-
-    auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
-              2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
-              
-    alias square = naryFun!"a * a";
-
-    // Other population variance examples
-    assert(x.dispersion.approxEqual(54.76562 / 12));
-    assert(x.dispersion!mean.approxEqual(54.76562 / 12));
-    assert(x.dispersion!(mean, square).approxEqual(54.76562 / 12));
-    assert(x.dispersion!(mean, square, mean).approxEqual(54.76562 / 12));
-    
-    // Population standard deviation
-    assert(x.dispersion!(mean, square, mean).sqrt.approxEqual(sqrt(54.76562 / 12)));
-    
-    // Mean absolute deviation about the mean
-    assert(x.dispersion!(mean, fabs, mean).approxEqual(21.0 / 12));
-    //Mean absolute deviation about the median
-    assert(x.dispersion!(median, fabs, mean).approxEqual(19.25000 / 12));
-    //Median absolute deviation about the mean
-    assert(x.dispersion!(mean, fabs, median).approxEqual(1.43750));
-    //Median absolute deviation about the median
-    assert(x.dispersion!(median, fabs, median).approxEqual(1.25000));
-}
-
-/++
-For integral slices, pass output type to `centralTendency`, `transform`, and 
-`summary` functions as template parameter to ensure output type is correct.
-+/
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-    import mir.math.common: approxEqual;
-    import mir.functional: naryFun;
-
-    auto x = [0, 1, 1, 2, 4, 4,
-              2, 7, 5, 1, 2, 0].sliced;
-
-    alias square = naryFun!"a * a";
-
-    auto y = x.dispersion;
-    assert(y.approxEqual(50.91667 / 12));
-    static assert(is(typeof(y) == double));
-
-    assert(x.dispersion!(mean!float, square, mean!float).approxEqual(50.91667 / 12));
-}
-
-/++
-Dispersion works for complex numbers and other user-defined types (provided that
-the `centralTendency`, `transform`, and `summary` functions are defined for those
-types)
-+/
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-    import mir.math.common: approxEqual;
-
-    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
-    assert(x.dispersion.approxEqual((0.0+10.0i)/ 4));
-}
-
-/// Compute mean tensors along specified dimention of tensors
-version(mir_test)
-@safe pure
-unittest
-{
-    import mir.ndslice.fuse: fuse;
-    import mir.algorithm.iteration: all;
-    import mir.math.common: approxEqual;
-    import mir.ndslice.topology: as, iota, alongDim, map, repeat;
-
-    auto x = [
-        [0.0, 1, 2],
-        [3.0, 4, 5]
-    ].fuse;
-
-    assert(x.dispersion.approxEqual(17.5 / 6));
-
-    auto m0 = [2.25, 2.25, 2.25];
-    assert(x.alongDim!0.map!dispersion.all!approxEqual(m0));
-    assert(x.alongDim!(-2).map!dispersion.all!approxEqual(m0));
-
-    auto m1 = [2.0 / 3, 2.0 / 3];
-    assert(x.alongDim!1.map!dispersion.all!approxEqual(m1));
-    assert(x.alongDim!(-1).map!dispersion.all!approxEqual(m1));
-
-    assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!dispersion.all!approxEqual(repeat(1800.0 / 2, 3, 4, 5)));
-}
-
-/// Arbitrary dispersion
-version(mir_test)
-@safe pure nothrow @nogc
-unittest
-{
-    import mir.math.common: approxEqual;
-    import mir.functional: naryFun;
-
-    alias square = naryFun!"a * a";
-
-    assert(dispersion(1.0, 2, 3).approxEqual(2.0 / 3));
-    assert(dispersion!(mean!float, square, mean!float)(1, 2, 3).approxEqual(2f / 3));
-}
-
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.math.common: approxEqual;
-    assert([1.0, 2, 3, 4].dispersion.approxEqual(5.0 / 4));
-}
-
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.ndslice.topology: iota, alongDim, map;
-    import mir.math.common: approxEqual;
-    import mir.algorithm.iteration: all;
-
-    auto x = iota([2, 2], 1);
-    auto y = x.alongDim!1.map!dispersion;
-    assert(y.all!approxEqual([0.25, 0.25]));
-    static assert(is(meanType!(typeof(y)) == double));
-}
-
-version(mir_test)
-@safe pure @nogc nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-    import mir.math.common: approxEqual;
-
-    static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
-                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
-
-    assert(x.sliced.dispersion.approxEqual(54.76562 / 12));
-}
-
 /++
 A linear regression model with a single explanatory variable.
 +/

From 32371ba2065ebe29cd5b9cdbb45b065bdaf75e3c Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 9 Jul 2020 23:31:21 -0400
Subject: [PATCH 042/573] Increase stat code cov (#306)

* Increase stat code cov

* Rebase, other changes, hopefully fix circleCI
---
 source/mir/math/stat.d | 100 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 92 insertions(+), 8 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 4685bf2e..acd93623 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -544,7 +544,7 @@ unittest
 }
 
 version(mir_test)
-@safe pure @nogc nothrow
+@safe pure nothrow @nogc
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -816,7 +816,7 @@ unittest
 
 /// Arbitrary harmonic mean
 version(mir_test)
-@safe pure @nogc nothrow
+@safe pure nothrow @nogc
 unittest
 {
     import mir.math.common: approxEqual;
@@ -830,7 +830,7 @@ unittest
 }
 
 version(mir_test)
-@safe pure @nogc nothrow
+@safe pure nothrow @nogc
 unittest
 {
     import mir.math.common: approxEqual;
@@ -860,7 +860,7 @@ F nthroot(F)(in F x, in size_t n)
 }
 
 version(mir_test)
-@safe @nogc pure nothrow
+@safe pure nothrow @nogc
 unittest
 {
     import mir.math.common: approxEqual;
@@ -1221,7 +1221,7 @@ unittest
 }
 
 version(mir_test)
-@safe pure @nogc nothrow
+@safe pure nothrow @nogc
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -1259,7 +1259,8 @@ template median(F, bool allowModify = false)
     Params:
         slice = slice
     +/
-    meanType!F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice) @nogc
+    @nogc
+    meanType!F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
         static assert (!allowModify ||
                        isMutable!(slice.DeepElement),
@@ -1436,6 +1437,23 @@ unittest
     assert(x.sliced.median == 2);
 }
 
+// withAsSlice test
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.rc.array: RCArray;
+
+    static immutable a = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5];
+
+    auto x = RCArray!double(11);
+    foreach(i, ref e; x)
+        e = a[i];
+
+    assert(x.median.approxEqual(5));
+}
+
 /++
 For integral slices, can pass output type as template parameter to ensure output
 type is correct
@@ -1775,6 +1793,38 @@ unittest
     assert(x.center.standardDeviation!"assumeZeroMean".approxEqual(x.standardDeviation));
 }
 
+// dynamic array test
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+
+    double[] x = [1.0, 2, 3, 4, 5, 6];
+
+    assert(x.center.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
+}
+
+// withAsSlice test
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+    import mir.rc.array: RCArray;
+
+    static immutable a = [1.0, 2, 3, 4, 5, 6];
+    static immutable result = [-2.5, -1.5, -0.5, 0.5, 1.5, 2.5];
+
+    auto x = RCArray!double(6);
+    foreach(i, ref e; x)
+        e = a[i];
+
+    assert(x.center.all!approxEqual(result));
+}
+
 /++
 Output range that applies function `fun` to each input before summing
 +/
@@ -1836,7 +1886,7 @@ unittest
 }
 
 version(mir_test)
-@safe pure @nogc nothrow
+@safe pure nothrow @nogc
 unittest
 {
     import mir.ndslice.slice: sliced;
@@ -2317,6 +2367,40 @@ unittest
     assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
 }
 
+// dynamic array test
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.rc.array: RCArray;
+
+    double[] x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+                  2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
+
+    auto v = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
+    assert(v.centeredSumOfSquares.sum.approxEqual(54.76562));
+}
+
+// withAsSlice test
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.rc.array: RCArray;
+
+    static immutable a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
+                          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
+
+    auto x = RCArray!double(12);
+    foreach(i, ref e; x)
+        e = a[i];
+
+    auto v = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
+    assert(v.centeredSumOfSquares.sum.approxEqual(54.76562));
+}
+
 ///
 struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     if (isMutable!T && varianceAlgo == VarianceAlgo.assumeZeroMean)
@@ -2872,7 +2956,7 @@ unittest
 }
 
 version(mir_test)
-@safe pure @nogc nothrow
+@safe pure nothrow @nogc
 unittest
 {
     import mir.ndslice.slice: sliced;

From 661f2d811aaee73a8623899255cb09ea330085bd Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sun, 12 Jul 2020 11:58:18 +0700
Subject: [PATCH 043/573] `as` and `retro` for ranges (#307)

* add `as` for ranges

* add support for ranges
---
 source/mir/ndslice/topology.d | 109 +++++++++++++++++++++++++++++++++-
 1 file changed, 107 insertions(+), 2 deletions(-)

diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index b0b1ffa7..d5bc6c05 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2124,9 +2124,9 @@ auto stride(T)(T withAsSlice, ptrdiff_t factor)
 /++
 Reverses order of iteration for all dimensions.
 Params:
-    slice = Unpacked slice.
+    slice = slice, range, or array.
 Returns:
-    Slice with reversed order of iteration for all dimensions.
+    Slice/range with reversed order of iteration for all dimensions.
 See_also: $(SUBREF dynamic, reversed), $(SUBREF dynamic, allReversed).
 +/
 auto retro
@@ -2184,6 +2184,80 @@ auto retro(T)(T withAsSlice)
     return retro(withAsSlice.asSlice);
 }
 
+
+/// ditto
+auto retro(Range)(Range r)
+    if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
+{
+    import std.traits: Unqual;
+
+    static if (is(Unqual!Range == Range))
+    {
+        import core.lifetime: move;
+        static if (is(Range : RetroRange!R, R))
+        {
+            return move(r._source);
+        }
+        else
+        {
+            return RetroRange!Range(move(r));
+        }
+    }
+    else
+    {
+        return .retro!(Unqual!Range)(r);
+    }
+}
+
+/// ditto
+struct RetroRange(Range)
+{
+    import mir.primitives: hasLength;
+
+    ///
+    Range _source;
+
+    private enum hasAccessByRef = __traits(compiles, &_source.front);
+
+    @property 
+    {
+        bool empty()() const { return _source.empty; }
+        static if (hasLength!Range)
+            auto length()() const { return _source.length; }
+        auto ref front()() { return _source.back; }
+        auto ref back()() { return _source.front; }
+        static if (__traits(hasMember, Range, "save"))
+            auto save()() { return RetroRange(_source.save); }
+        alias opDollar = length;
+
+        static if (!hasAccessByRef)
+        {
+            import std.traits: ForeachType;
+
+            void front()(ForeachType!R val)
+            {
+                import mir.functional: forward;
+                _source.back = forward!val;
+            }
+
+            void back()(ForeachType!R val)
+            {
+                import mir.functional: forward;
+                _source.front = forward!val;
+            }
+        }
+    }
+
+    void popFront()() { _source.popBack(); }
+    void popBack()() { _source.popFront(); }
+
+    static if (is(typeof(_source.moveBack())))
+    auto moveFront()() { return _source.moveBack(); }
+
+    static if (is(typeof(_source.moveFront())))
+    auto moveBack()() { return _source.moveFront(); }
+}
+
 ///
 @safe pure nothrow @nogc version(mir_test) unittest
 {
@@ -2198,6 +2272,16 @@ auto retro(T)(T withAsSlice)
     static assert(is(typeof(slice.universal.retro) == typeof(slice.universal)));
 }
 
+/// Ranges
+@safe pure nothrow @nogc version(mir_test) unittest
+{
+    import mir.algorithm.iteration: equal;
+    import std.range: std_iota = iota;
+
+    assert(std_iota(4).retro.equal(iota(4).retro));
+    static assert(is(typeof(std_iota(4).retro.retro) == typeof(std_iota(4))));
+}
+
 /++
 Bitwise slice over an integral slice.
 Params:
@@ -3308,6 +3392,20 @@ template as(T)
     {
         return as(withAsSlice.asSlice);
     }
+
+    /// ditto
+    auto as(Range)(Range r)
+        if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
+    {
+        static if (is(ForeachType!Range == T))
+            return r;
+        else
+        {
+            import core.lifetime: move;
+            import mir.conv: to;
+            return map!(to!T)(r.move);
+        }
+    }
 }
 
 ///
@@ -3341,6 +3439,13 @@ template as(T)
     Slice!(const(double)*, 2) const_matrix = matrix.as!(const double);
 }
 
+/// Ranges
+@safe pure nothrow version(mir_test) unittest
+{
+    import mir.algorithm.iteration: filter, equal;
+    assert(5.iota.filter!"a % 2".as!double.map!"a / 2".equal([0.5, 1.5]));
+}
+
 /++
 Takes a field `source` and a slice `indexes`, and creates a view of source as if its elements were reordered according to indexes.
 `indexes` may include only a subset of the elements of `source` and may also repeat elements.

From 6881e025e2aec417b38ba7e0a45d27517205100a Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 15 Jul 2020 07:20:08 -0400
Subject: [PATCH 044/573] Adjust MovingAverage UT to use MeanAccumulator and be
 @nogc (#308)

* Adjust moving average UT

* Address review

* Remove %= and extra imports
---
 source/mir/math/sum.d | 37 ++++++++++++++++++++++---------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index f7a8b97c..57120a84 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -190,48 +190,55 @@ nothrow @nogc unittest
 
 /// Moving mean
 version(mir_test)
+@safe pure nothrow @nogc
 unittest
 {
-    import mir.ndslice.topology: linspace;
+    import mir.internal.utility: isFloatingPoint;
     import mir.math.sum;
-    import mir.array.allocation: array;
+    import mir.ndslice.topology: linspace;
+    import mir.rc.array: rcarray;
 
-    class MovingAverage
+    struct MovingAverage(T)
+        if (isFloatingPoint!T)
     {
-        Summator!(double, Summation.precise) summator;
+        import mir.math.stat: MeanAccumulator;
+
+        MeanAccumulator!(T, Summation.precise) meanAccumulator;
         double[] circularBuffer;
         size_t frontIndex;
 
-        double avg() @property const
+        @disable this(this);
+
+        auto avg() @property const
         {
-            return summator.sum / circularBuffer.length;
+            return meanAccumulator.mean;
         }
 
         this(double[] buffer)
         {
             assert(buffer.length);
             circularBuffer = buffer;
-            summator = 0;
-            summator.put(buffer);
+            meanAccumulator.put(buffer);
         }
 
         ///operation without rounding
-        void put(double x)
+        void put(T x)
         {
             import mir.utility: swap;
-            summator += x;
+            meanAccumulator.summator += x;
             swap(circularBuffer[frontIndex++], x);
-            summator -= x;
-            frontIndex %= circularBuffer.length;
+            frontIndex = frontIndex == circularBuffer.length ? 0 : frontIndex;
+            meanAccumulator.summator -= x;
         }
     }
 
     /// ma always keeps precise average of last 1000 elements
-    auto ma = new MovingAverage(linspace!double([1000], [0.0, 999]).array);
+    auto x = linspace!double([1000], [0.0, 999]).rcarray;
+    auto ma = MovingAverage!double(x[]);
     assert(ma.avg == (1000 * 999 / 2) / 1000.0);
     /// move by 10 elements
-    foreach(x; linspace!double([10], [1000.0, 1009.0]))
-        ma.put(x);
+    foreach(e; linspace!double([10], [1000.0, 1009.0]))
+        ma.put(e);
     assert(ma.avg == (1010 * 1009 / 2 - 10 * 9 / 2) / 1000.0);
 }
 

From 0ec9eb9926abb02cde1e10e95abab88b26d043aa Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 15 Jul 2020 07:20:42 -0400
Subject: [PATCH 045/573] Minor mir.math.stat fixups (#309)

---
 source/mir/math/stat.d | 154 +++++++++++++++++++++--------------------
 1 file changed, 80 insertions(+), 74 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index acd93623..1260bf8c 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -138,8 +138,7 @@ package(mir)
 template meanType(T)
 {
     import mir.math.sum: sumType;
-    import mir.internal.utility: isFloatingPoint, isComplex;
-    
+
     alias U = sumType!T;
 
     static if (__traits(compiles, {
@@ -461,8 +460,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0, 1, 1, 2, 4, 4,
               2, 7, 5, 1, 2, 0].sliced;
@@ -482,8 +481,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
     assert(x.mean.approxEqual(2.5 + 3.5i));
@@ -533,9 +532,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.topology: iota, alongDim, map;
-    import mir.math.common: approxEqual;
     import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+    import mir.ndslice.topology: iota, alongDim, map;
 
     auto x = iota([2, 2], 1);
     auto y = x.alongDim!1.map!mean;
@@ -763,9 +762,9 @@ version(mir_test)
 pure @safe nothrow
 unittest
 {
+    import mir.math.common: approxEqual;
     import mir.ndslice.topology: repeat;
     import mir.ndslice.slice: sliced;
-    import mir.math.common: approxEqual;
 
     //Set sum algorithm or output type
     auto x = [1, 1e-100, 1, -1e-100].sliced;
@@ -786,8 +785,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [20, 100, 2000, 10, 5, 2].sliced;
 
@@ -807,8 +806,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
     assert(x.hmean.approxEqual(1.97110904 + 3.14849332i));
@@ -926,8 +925,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     GMeanAccumulator!double x;
     x.put([1.0, 2, 3, 4].sliced);
@@ -940,8 +939,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     GMeanAccumulator!float x;
     x.put([1, 2, 3, 4].sliced);
@@ -1031,8 +1030,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     assert(gmean([1.0, 2, 3]).approxEqual(1.81712059));
     
@@ -1046,8 +1045,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [3.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 2.0].sliced;
@@ -1060,8 +1059,8 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice.fuse: fuse;
     import mir.math.common: approxEqual;
+    import mir.ndslice.fuse: fuse;
 
     auto x = [
         [3.0, 1.0, 1.5, 2.0, 3.5, 4.25],
@@ -1076,10 +1075,10 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice.fuse: fuse;
     import mir.algorithm.iteration: all;
-    import mir.ndslice.topology: alongDim, byDim, map;
     import mir.math.common: approxEqual;
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.topology: alongDim, byDim, map;
 
     auto x = [
         [3.0, 1.0, 1.5, 2.0, 3.5, 4.25],
@@ -1102,9 +1101,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
+    import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: repeat;
-    import mir.math.common: approxEqual;
 
     auto x = [5120.0, 7340032, 32, 3758096384].sliced;
 
@@ -1122,8 +1121,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [5, 1, 1, 2, 4, 4,
               2, 7, 5, 1, 2, 10].sliced;
@@ -1157,9 +1156,9 @@ version(mir_test)
 unittest
 {
     import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
     import mir.ndslice.fuse: fuse;
     import mir.ndslice.topology: alongDim, iota, map;
-    import mir.math.common: approxEqual;
     
     auto x = [
         [1.0, 2, 3],
@@ -1199,6 +1198,7 @@ version(mir_test)
 unittest
 {
     import mir.math.common: approxEqual;
+
     assert(gmean(1.0, 2, 3).approxEqual(1.81712059));
     assert(gmean!float(1, 2, 3).approxEqual(1.81712059));
 }
@@ -1208,6 +1208,7 @@ version(mir_test)
 unittest
 {
     import mir.math.common: approxEqual;
+
     assert([1.0, 2, 3, 4].gmean.approxEqual(2.21336384));
 }
 
@@ -1224,8 +1225,8 @@ version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     static immutable x = [3.0, 1.0, 1.5, 2.0, 3.5, 4.25,
                           2.0, 7.5, 5.0, 1.0, 1.5, 2.0];
@@ -1387,11 +1388,11 @@ version(mir_test)
 @safe pure
 unittest
 {
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
     import mir.ndslice.fuse: fuse;
     import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: alongDim, byDim, map;
-    import mir.algorithm.iteration: all;
-    import mir.math.common: approxEqual;
 
     auto x = [
         [0.0, 1.0, 1.5, 2.0, 3.5, 4.25], 
@@ -1433,6 +1434,7 @@ version(mir_test)
 unittest
 {
     import mir.ndslice.slice: sliced;
+
     static immutable x = [9.0, 1, 0, 2, 3];
     assert(x.sliced.median == 2);
 }
@@ -1477,8 +1479,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [3, 3, 2, 0, 2, 0].sliced;
     assert(x.median!float.approxEqual(2));
@@ -1528,8 +1530,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x0 = [9.0, 1, 0, 2, 3].sliced;
     assert(x0.median.approxEqual(2));
@@ -1555,8 +1557,8 @@ F smallMedianImpl(F, Iterator)(Slice!Iterator slice)
     assert(n > 0, "smallMedianImpl: slice must have elementCount greater than 0");
     assert(n <= 5, "smallMedianImpl: slice must have elementCount of 5 or less");
 
-    import mir.ndslice.sorting: medianOf;
     import mir.functional: naryFun;
+    import mir.ndslice.sorting: medianOf;
     import mir.utility: swapStars;
 
     auto sliceI0 = slice._iterator;
@@ -1602,8 +1604,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x0 = [9.0, 1, 0, 2, 3].sliced;
     assert(x0.smallMedianImpl!double.approxEqual(2));
@@ -1650,8 +1652,8 @@ template center(alias centralTendency = mean!(Summation.appropriate))
         Slice!(Iterator, N, kind) slice)
     {
         import core.lifetime: move;
-        import mir.ndslice.topology: vmap;
         import mir.ndslice.internal: LeftOp, ImplicitlyUnqual;
+        import mir.ndslice.topology: vmap;
 
         auto m = centralTendency(slice.lightScope);
         alias T = typeof(m);
@@ -1677,9 +1679,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.algorithm.iteration: all;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [1.0, 2, 3, 4, 5, 6].sliced;
     assert(x.center.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
@@ -1707,9 +1709,9 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice: fuse;
     import mir.algorithm.iteration: all;
     import mir.math.common: approxEqual;
+    import mir.ndslice: fuse;
     
     auto x = [
         [0.0, 1, 2], 
@@ -1784,9 +1786,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.algorithm.iteration: all;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [1.0, 2, 3, 4, 5, 6].sliced;
     assert(x.center.variance!"assumeZeroMean".approxEqual(x.variance));
@@ -1860,8 +1862,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: powi;
+    import mir.ndslice.slice: sliced;
 
     alias f = (double x) => (powi(x, 2));
     MapSummator!(f, double, Summation.pairwise) x;
@@ -1904,8 +1906,8 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.ndslice.fuse: fuse;
+    import mir.ndslice.slice: sliced;
 
     alias f = (double x) => (x + 1);
     MapSummator!(f, double, Summation.pairwise) x;
@@ -2025,8 +2027,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2100,7 +2102,6 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     void put()(T x)
     {
-        size_t oldCount = count;
         T delta = x;
         if (count > 0) {
             delta -= meanAccumulator.mean;
@@ -2136,8 +2137,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2167,8 +2168,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
     auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2196,8 +2197,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
     auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2227,8 +2228,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [1.0 + 3i, 2, 3].sliced;
 
@@ -2242,8 +2243,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2260,8 +2261,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25].sliced;
     auto y = [2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2306,8 +2307,8 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
          Slice!(Iterator, N, kind) slice)
     {
         import core.lifetime: move;
-        import mir.ndslice.topology: vmap, map;
         import mir.ndslice.internal: LeftOp;
+        import mir.ndslice.topology: vmap, map;
 
         meanAccumulator.put(slice.lightScope);
         centeredSumOfSquares.put(slice.move.vmap(LeftOp!("-", T)(meanAccumulator.mean)).map!(naryFun!"a * a"));
@@ -2349,8 +2350,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2476,8 +2477,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2508,8 +2509,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2540,8 +2541,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2574,8 +2575,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto a = [1.0 + 3i, 2, 3].sliced;
     auto x = a.center;
@@ -2590,8 +2591,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2609,8 +2610,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2657,6 +2658,7 @@ template variance(
         if (isIterable!Range)
     {
         import core.lifetime: move;
+
         alias G = typeof(return);
         auto varianceAccumulator = VarianceAccumulator!(G, varianceAlgo, ResolveSummationType!(summation, Range, G))(r.move);
         return varianceAccumulator.variance(isPopulation);
@@ -2688,6 +2690,7 @@ template variance(
         if(isIterable!Range)
     {
         import core.lifetime: move;
+
         alias F = typeof(return);
         return .variance!(F, varianceAlgo, summation)(r.move, isPopulation);
     }
@@ -2720,8 +2723,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     assert(variance([1.0, 2, 3]).approxEqual(2.0 / 2));
     assert(variance([1.0, 2, 3], true).approxEqual(2.0 / 3));
@@ -2738,8 +2741,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2752,8 +2755,8 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice.fuse: fuse;
     import mir.math.common: approxEqual;
+    import mir.ndslice.fuse: fuse;
 
     auto x = [
         [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
@@ -2768,10 +2771,10 @@ version(mir_test)
 @safe pure
 unittest
 {
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
     import mir.ndslice.fuse: fuse;
     import mir.ndslice.topology: alongDim, byDim, map;
-    import mir.math.common: approxEqual;
-    import mir.algorithm.iteration: all;
 
     auto x = [
         [0.0,  1.0, 1.5, 2.0], 
@@ -2792,11 +2795,11 @@ unittest
 
 /// Can also set algorithm type
 version(mir_test)
-//@safe pure nothrow
+@safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -2824,9 +2827,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
+    import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: repeat;
-    import mir.math.common: approxEqual;
 
     //Set population variance, variance algorithm, sum algorithm or output type
 
@@ -2868,8 +2871,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0, 1, 1, 2, 4, 4,
               2, 7, 5, 1, 2, 0].sliced;
@@ -2889,8 +2892,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
     assert(x.variance.approxEqual((0.0+10.0i)/ 3));
@@ -2901,9 +2904,9 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice.fuse: fuse;
     import mir.algorithm.iteration: all;
     import mir.math.common: approxEqual;
+    import mir.ndslice.fuse: fuse;
     import mir.ndslice.topology: as, iota, alongDim, map, repeat;
 
     auto x = [
@@ -2938,6 +2941,7 @@ version(mir_test)
 unittest
 {
     import mir.math.common: approxEqual;
+
     assert([1.0, 2, 3, 4].variance.approxEqual(5.0 / 3));
 }
 
@@ -2945,9 +2949,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.topology: iota, alongDim, map;
-    import mir.math.common: approxEqual;
     import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+    import mir.ndslice.topology: iota, alongDim, map;
 
     auto x = iota([2, 2], 1);
     auto y = x.alongDim!1.map!variance;
@@ -2959,8 +2963,8 @@ version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
 
     static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
                           2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
@@ -3064,6 +3068,7 @@ template standardDeviation(
         if(isIterable!Range)
     {
         import core.lifetime: move;
+
         alias F = typeof(return);
         return .standardDeviation!(F, varianceAlgo, summation)(r.move, isPopulation);
     }
@@ -3096,8 +3101,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.slice: sliced;
 
     assert(standardDeviation([1.0, 2, 3]).approxEqual(sqrt(2.0 / 2)));
     assert(standardDeviation([1.0, 2, 3], true).approxEqual(sqrt(2.0 / 3)));
@@ -3112,8 +3117,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -3126,8 +3131,8 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice.fuse: fuse;
     import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.fuse: fuse;
 
     auto x = [
         [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
@@ -3142,10 +3147,10 @@ version(mir_test)
 @safe pure
 unittest
 {
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual, sqrt;
     import mir.ndslice.fuse: fuse;
     import mir.ndslice.topology: alongDim, byDim, map;
-    import mir.math.common: approxEqual, sqrt;
-    import mir.algorithm.iteration: all;
 
     auto x = [
         [0.0,  1.0, 1.5, 2.0], 
@@ -3169,8 +3174,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.slice: sliced;
 
     auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
               2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
@@ -3194,9 +3199,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
+    import mir.math.common: approxEqual, sqrt;
     import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: repeat;
-    import mir.math.common: approxEqual, sqrt;
 
     //Set population standard deviation, standardDeviation algorithm, sum algorithm or output type
 
@@ -3238,8 +3243,8 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.slice: sliced;
 
     auto x = [0, 1, 1, 2, 4, 4,
               2, 7, 5, 1, 2, 0].sliced;
@@ -3275,9 +3280,9 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.ndslice.fuse: fuse;
     import mir.algorithm.iteration: all;
     import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.fuse: fuse;
     import mir.ndslice.topology: as, iota, alongDim, map, repeat;
 
     auto x = [
@@ -3304,6 +3309,7 @@ version(mir_test)
 unittest
 {
     import mir.math.common: sqrt;
+
     assert(standardDeviation(1.0, 2, 3) == 1.0);
     assert(standardDeviation!float(1, 2, 3) == 1f);
 }
@@ -3320,9 +3326,9 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.ndslice.topology: iota, alongDim, map;
-    import mir.math.common: approxEqual, sqrt;
     import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.topology: iota, alongDim, map;
 
     auto x = iota([2, 2], 1);
     auto y = x.alongDim!1.map!standardDeviation;
@@ -3334,8 +3340,8 @@ version(mir_test)
 @safe pure @nogc nothrow
 unittest
 {
-    import mir.ndslice.slice: sliced;
     import mir.math.common: approxEqual, sqrt;
+    import mir.ndslice.slice: sliced;
 
     static immutable x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
                           2.0, 7.5, 5.0, 1.0, 1.5, 0.0];

From 52bc0e938960376681481d45ff3029c226f4f01b Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 16 Jul 2020 21:50:40 +0700
Subject: [PATCH 046/573] workaround for _freshEmpty

---
 source/mir/algorithm/iteration.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index a9bfb6d4..3a8edd3a 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -3801,7 +3801,7 @@ struct Filter(alias pred, Range)
     void popFront() scope
     {
         assert(!_input.empty, "Attempting to popFront an empty Filter.");
-        assert(_freshEmpty, "Attempting to pop the front of a Filter without calling '.empty' method ahead.");
+        version(assert) assert(_freshEmpty, "Attempting to pop the front of a Filter without calling '.empty' method ahead.");
         version(assert) _freshEmpty = false;
         _input.popFront;
     }
@@ -3809,7 +3809,7 @@ struct Filter(alias pred, Range)
     auto ref front() @property
     {
         assert(!_input.empty, "Attempting to fetch the front of an empty Filter.");
-        assert(_freshEmpty, "Attempting to fetch the front of a Filter without calling '.empty' method ahead.");
+        version(assert) assert(_freshEmpty, "Attempting to fetch the front of a Filter without calling '.empty' method ahead.");
         return _input.front;
     }
 

From 01676b2090d19f2413ac475c7da53a2b2b4057f3 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 29 Jul 2020 09:15:41 +0700
Subject: [PATCH 047/573] add inf/nan parsing

---
 source/mir/bignum/decimal.d | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index cb61fe1f..efb1bad7 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -110,6 +110,19 @@ bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize
     }
     else
     {
+        if (str.length == 2)
+        {
+            if ((d == 'i' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"nf"))
+            {
+                key = DecimalExponentKey.infinity;
+                return true;
+            }
+            if ((d == 'n' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"an"))
+            {
+                key = DecimalExponentKey.nan;
+                return true;
+            }
+        }
         return false;
     }
 
@@ -194,6 +207,10 @@ enum DecimalExponentKey
     D = 'D' - '0',
     ///
     E = 'E' - '0',
+    ///
+    infinity,
+    ///
+    nan,
 }
 
 ///
@@ -228,6 +245,15 @@ unittest
     assert(cast(double) decimal.view == 0);
     assert(key == DecimalExponentKey.E);
 
+    assert("-nan".parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == 0);
+    assert(decimal.coefficient.sign);
+    assert(key == DecimalExponentKey.nan);
+
+    assert("inf".parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == 0);
+    assert(key == DecimalExponentKey.infinity);
+
     assert(!"3.3.4".parseDecimal(decimal, key));
     assert(!"3.4.".parseDecimal(decimal, key));
     assert(!"4.".parseDecimal(decimal, key));

From 00a94e0ad542886fd4036e4162042c47c302fb61 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 31 Jul 2020 13:32:04 +0700
Subject: [PATCH 048/573] fix small string

---
 source/mir/small_string.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 9bea8488..d6545050 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -109,8 +109,9 @@ extern(D):
             version(D_Exceptions) throw exception;
             else assert(0, errorMsg);
         }
+        _data = '\0';
         if (__ctfe)
-            (cast(ubyte[])_data)[0 .. str.length] = cast(ubyte[]) str;
+            (cast(char[])_data)[0 .. str.length] = cast(char[]) str;
         else
             memcpy(_data.ptr, str.ptr, str.length);
         return this;

From fee0bda38dccf83a9e960290eb5b28821f40ea94 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 31 Jul 2020 13:32:14 +0700
Subject: [PATCH 049/573] add comment to bigint

---
 source/mir/bignum/integer.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index df10bcbc..0835327e 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -382,6 +382,7 @@ struct BigInt(size_t maxSize64)
     }
 
     /++
+    Returns: overflow flag
     +/
     bool copyFrom(W, WordEndian endian)(BigIntView!(const W, endian) view)
     {

From ec9bf53ca2930ae7dd897aad4c3b1f463f23d0c9 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 31 Jul 2020 13:41:33 +0700
Subject: [PATCH 050/573] improve .array function

---
 source/mir/array/allocation.d | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index dd4047e7..dff1c277 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -102,9 +102,10 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticAr
         else
         {
             auto it = result;
-            foreach(ref f; r)
+            foreach(f; r)
             {
-                emplaceRef!E(it[0], f);
+                import mir.functional: forward;
+                emplaceRef!E(it[0], forward(f));
                 it = it[1 .. $];
             }
         }
@@ -114,18 +115,30 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticAr
     else
     {
         import mir.appender: ScopedBuffer;
-        ScopedBuffer!E a;
+        ScopedBuffer!(Unqual!E) a;
         static if (isInputRange!Range)
             for (; !r.empty; r.popFront)
                 a.put(r.front);
         else
         static if (isPointer!Range)
+        {
+            import mir.functional: forward;
             foreach (e; *r)
-                a.put(e);
+                a.put(forward!e);
+        }
         else
+        {
+            import mir.functional: forward;
             foreach (e; r)
-                a.put(e);
-        return .array(a.data);
+                a.put(forward!e);
+        }
+
+        return (() @trusted {
+            import std.array: uninitializedArray;
+            auto ret = uninitializedArray!(Unqual!E[])(a.length);
+            a.moveDataAndEmplaceTo(ret);
+            return ret;
+        })();
     }
 }
 

From 428eb01cf27acbd601cd5eef55c5c34f8409f55c Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 31 Jul 2020 13:49:24 +0700
Subject: [PATCH 051/573] add trusted assign to small_string and small_array

---
 source/mir/small_array.d  |  8 ++++++++
 source/mir/small_string.d | 10 ++++++++++
 2 files changed, 18 insertions(+)

diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index a108a72a..4a45bec3 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -169,6 +169,14 @@ struct SmallArray(T, uint maxLength)
         return this;
     }
 
+    ///
+    void trustedAssign(V[] array) @trusted
+    {
+        _data[0 .. _length] = T.init;
+        _length = cast(uint) array.length;
+        _data[0 .. _length] = array;
+    }
+
     ///
     ref typeof(this) append(T elem)
     {
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index d6545050..0cbfe249 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -179,6 +179,16 @@ extern(D):
         return this;
     }
 
+    /// ditto
+    void trustedAssign(scope const(char)[] str) return @trusted nothrow
+    {
+        _data = '\0';
+        if (__ctfe)
+            (cast(char[])_data)[0 .. str.length] = cast(char[]) str;
+        else
+            memcpy(_data.ptr, str.ptr, str.length);
+    }
+
     ///
     ref typeof(this) append(char c) @trusted
     {

From d4dbde5ca516ac547fff200dcdc0cba07477fba2 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 31 Jul 2020 14:14:21 +0700
Subject: [PATCH 052/573] fixup

---
 source/mir/array/allocation.d | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index dff1c277..7e635245 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -102,10 +102,10 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticAr
         else
         {
             auto it = result;
-            foreach(f; r)
+            foreach (f; r)
             {
                 import mir.functional: forward;
-                emplaceRef!E(it[0], forward(f));
+                emplaceRef!E(it[0], forward!f);
                 it = it[1 .. $];
             }
         }
@@ -122,13 +122,11 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticAr
         else
         static if (isPointer!Range)
         {
-            import mir.functional: forward;
             foreach (e; *r)
                 a.put(forward!e);
         }
         else
         {
-            import mir.functional: forward;
             foreach (e; r)
                 a.put(forward!e);
         }

From 4418e9f653d9d838f567ceda5f1c79ce88360ae4 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sat, 1 Aug 2020 21:39:30 +0700
Subject: [PATCH 053/573] fix small string

---
 source/mir/small_string.d | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 0cbfe249..06a5bf99 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -109,11 +109,11 @@ extern(D):
             version(D_Exceptions) throw exception;
             else assert(0, errorMsg);
         }
-        _data = '\0';
         if (__ctfe)
-            (cast(char[])_data)[0 .. str.length] = cast(char[]) str;
+            _data[0 .. str.length] =  str;
         else
             memcpy(_data.ptr, str.ptr, str.length);
+        _data[str.length .. $] = '\0';
         return this;
     }
 
@@ -184,7 +184,7 @@ extern(D):
     {
         _data = '\0';
         if (__ctfe)
-            (cast(char[])_data)[0 .. str.length] = cast(char[]) str;
+            _data[0 .. str.length] =  str;
         else
             memcpy(_data.ptr, str.ptr, str.length);
     }

From 0a32920b5da2ec89b184a477557173b55180e287 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 2 Aug 2020 12:16:22 +0700
Subject: [PATCH 054/573] fix small string and small array for betterC

---
 source/mir/small_array.d  | 10 ++++++++--
 source/mir/small_string.d | 10 ++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 4a45bec3..20cd1d5c 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -181,7 +181,10 @@ struct SmallArray(T, uint maxLength)
     ref typeof(this) append(T elem)
     {
         if (_length == maxLength)
-            throw exception;
+        {
+            version(D_Exceptions) throw exception;
+            else assert(0, errorMsg);
+        }
         _data[_length++] = elem;
         return this;
     }
@@ -190,7 +193,10 @@ struct SmallArray(T, uint maxLength)
     ref typeof(this) append(V[] array)
     {
         if (_length + array.length > maxLength)
-            throw exception;
+        {
+            version(D_Exceptions) throw exception;
+            else assert(0, errorMsg);
+        }
         _data[_length .. _length + array.length] = array;
         _length += array.length;
         return this;
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 06a5bf99..533ddbeb 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -194,7 +194,10 @@ extern(D):
     {
         auto length = asArray.length;
         if (length == maxLength)
-            throw exception;
+        {
+            version(D_Exceptions) throw exception;
+            else assert(0, errorMsg);
+        }
         _data[length] = c;
         return this;
     }
@@ -204,7 +207,10 @@ extern(D):
     {
         auto length = asArray.length;
         if (length + str.length > maxLength)
-            throw exception;
+        {
+            version(D_Exceptions) throw exception;
+            else assert(0, errorMsg);
+        }
         if (__ctfe)
             _data[length .. str.length + length] = str;
         else

From 81e6c3abaf530a627fe647bf4de51f48693e0122 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 2 Aug 2020 12:26:46 +0700
Subject: [PATCH 055/573] make small array/string internals public

---
 source/mir/small_array.d  | 2 +-
 source/mir/small_string.d | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 20cd1d5c..55fa8c1c 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -20,7 +20,7 @@ struct SmallArray(T, uint maxLength)
 {
     import std.traits: Unqual, isIterable, isImplicitlyConvertible;
 
-    private uint _length;
+    uint _length;
     T[maxLength] _data;
 
 	static SmallArray deserialize(S)(S data)
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 533ddbeb..a930cc92 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -32,7 +32,7 @@ struct SmallString(uint maxLength)
     import std.traits: Unqual, isIterable;
 
     // maxLength bytes
-    private char[maxLength] _data = '\0';
+    char[maxLength] _data = '\0';
 
 extern(D):
 

From 02f28ee5e4885fcd5a416fb430fe1a5e2bec80d8 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Tue, 4 Aug 2020 17:40:53 +0700
Subject: [PATCH 056/573] add serde and reflection modules (#310)

* add serde and reflection modules

* update index.d and meson.build
---
 index.d                 |   2 +
 meson.build             |   2 +
 source/mir/reflection.d | 400 ++++++++++++++++++++++++++++++++++++++++
 source/mir/serde.d      | 256 +++++++++++++++++++++++++
 4 files changed, 660 insertions(+)
 create mode 100644 source/mir/reflection.d
 create mode 100644 source/mir/serde.d

diff --git a/index.d b/index.d
index 2d544fa4..cc4042dd 100644
--- a/index.d
+++ b/index.d
@@ -19,6 +19,8 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,small_string)) $(TD Generic Small Strings))
     $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
     $(TR $(TDNW $(MREF mir,range)) $(TD Ranges))
+    $(TR $(TDNW $(MREF mir,serde)) $(TD Utilities for serialization libraries ))
+    $(TR $(TDNW $(MREF mir,reflection)) $(TD Compile time reflection utilities ))
     $(LEADINGROW Date and time)
     $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability))
     $(LEADINGROW NDarrays and Algorithms)
diff --git a/meson.build b/meson.build
index 4cbd77cd..bb6f8423 100644
--- a/meson.build
+++ b/meson.build
@@ -63,6 +63,8 @@ sources_list = [
     'mir/rc/package',
     'mir/rc/ptr',
     'mir/rc/slim_ptr',
+    'mir/reflection',
+    'mir/serde',
     'mir/series',
     'mir/small_array',
     'mir/small_string',
diff --git a/source/mir/reflection.d b/source/mir/reflection.d
new file mode 100644
index 00000000..ce04ff5d
--- /dev/null
+++ b/source/mir/reflection.d
@@ -0,0 +1,400 @@
+/++
++/
+module mir.reflection;
+
+import std.meta;
+import std.traits: Parameters, isSomeFunction, FunctionAttribute, functionAttributes;
+
+/++
+Checks if member is field.
++/
+template isField(T, string member)
+{
+    T aggregate;
+    enum bool isField = __traits(compiles, __traits(getMember, aggregate, member).offsetof);
+}
+
+///
+version(mir_test)
+unittest
+{
+    struct D
+    {
+        int gi;
+    }
+
+    struct I
+    {
+        int f;
+
+        D base;
+        alias base this;
+
+        void gi(double ) @property {}
+        void gi(uint ) @property {}
+    }
+
+    struct S
+    {
+        int d;
+
+        I i;
+        alias i this;
+
+        int gm() @property {return 0;}
+        int gc() const @property {return 0;}
+        void gs(int) @property {}
+    }
+
+    static assert(!isField!(S, "gi"));
+    static assert(!isField!(S, "gs"));
+    static assert(!isField!(S, "gc"));
+    static assert(!isField!(S, "gm"));
+    static assert(!isField!(S, "gi"));
+    static assert(isField!(S, "d"));
+    static assert(isField!(S, "f"));
+    static assert(isField!(S, "i"));
+}
+
+///  with classes
+version(mir_test)
+unittest
+{
+    class I
+    {
+        int f;
+
+        void gi(double ) @property {}
+        void gi(uint ) @property {}
+    }
+
+    class S
+    {
+        int d;
+
+        I i;
+        alias i this;
+
+        int gm() @property {return 0;}
+        int gc() const @property {return 0;}
+        void gs(int) @property {}
+    }
+
+    static assert(!isField!(S, "gi"));
+    static assert(!isField!(S, "gs"));
+    static assert(!isField!(S, "gc"));
+    static assert(!isField!(S, "gm"));
+    static assert(isField!(S, "d"));
+    static assert(isField!(S, "f"));
+    static assert(isField!(S, "i"));
+}
+
+/++
+Checks if member is property.
++/
+template isProperty(T, string member)
+{
+    T aggregate;
+
+    static if (__traits(compiles, isSomeFunction!(__traits(getMember, aggregate, member))))
+    {
+        static if (isSomeFunction!(__traits(getMember, aggregate, member)))
+        {
+            enum bool isProperty = isPropertyImpl!(__traits(getMember, aggregate, member));
+        }
+        else
+        {
+            enum bool isProperty = false;
+        }
+    }
+    else
+        enum bool isProperty = false;
+}
+
+///
+version(mir_test)
+unittest
+{
+    struct D
+    {
+        int y;
+
+        void gf(double ) @property {}
+        void gf(uint ) @property {}
+    }
+
+    struct I
+    {
+        int f;
+
+        D base;
+        alias base this;
+
+        void gi(double ) @property {}
+        void gi(uint ) @property {}
+    }
+
+    struct S
+    {
+        int d;
+
+        I i;
+        alias i this;
+
+        int gm() @property {return 0;}
+        int gc() const @property {return 0;}
+        void gs(int) @property {}
+    }
+
+    static assert(isProperty!(S, "gf"));
+    static assert(isProperty!(S, "gi"));
+    static assert(isProperty!(S, "gs"));
+    static assert(isProperty!(S, "gc"));
+    static assert(isProperty!(S, "gm"));
+    static assert(!isProperty!(S, "d"));
+    static assert(!isProperty!(S, "f"));
+    static assert(!isProperty!(S, "y"));
+}
+
+/++
+Returns: list of the setter properties.
+
+Note: The implementation ignores templates.
++/
+template getSetters(T, string member)
+{
+    alias getSetters = Filter!(hasSingleArgument, Filter!(isPropertyImpl, __traits(getOverloads, T, member)));
+}
+
+///
+version(mir_test)
+unittest
+{
+    struct I
+    {
+        int f;
+
+        void gi(double ) @property {}
+        void gi(uint ) @property {}
+    }
+
+    struct S
+    {
+        int d;
+
+        I i;
+        alias i this;
+
+        int gm() @property {return 0;}
+        int gc() const @property {return 0;}
+        void gs(int) @property {}
+    }
+
+    static assert(getSetters!(S, "gi").length == 2);
+    static assert(getSetters!(S, "gs").length == 1);
+    static assert(getSetters!(S, "gc").length == 0);
+    static assert(getSetters!(S, "gm").length == 0);
+    static assert(getSetters!(S, "d").length == 0);
+    static assert(getSetters!(S, "f").length == 0);
+}
+
+/++
+Returns: list of the serializable (public getters) members.
++/
+enum string[] SerializableMembers(T) = [Filter!(ApplyLeft!(Serializable, T), FieldsAndProperties!T)];
+
+///
+version(mir_test)
+unittest
+{
+    struct D
+    {
+        int y;
+
+        int gf() @property {return 0;}
+    }
+
+    struct I
+    {
+        int f;
+
+        D base;
+        alias base this;
+
+        int gi() @property {return 0;}
+    }
+
+    struct S
+    {
+        int d;
+
+        package int p;
+
+        int gm() @property {return 0;}
+
+        private int q;
+
+        I i;
+        alias i this;
+
+        int gc() const @property {return 0;}
+        void gs(int) @property {}
+    }
+
+    static assert(SerializableMembers!S == ["y", "gf", "f", "gi", "d", "gm", "gc"]);
+    static assert(SerializableMembers!(const S) == ["y", "f", "d", "gc"]);
+}
+
+/++
+Returns: list of the deserializable (public setters) members.
++/
+enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), FieldsAndProperties!T)];
+
+///
+version(mir_test)
+unittest
+{
+    struct I
+    {
+        int f;
+        void ga(int) @property {}
+    }
+
+    struct S
+    {
+        int d;
+        package int p;
+
+        int gm() @property {return 0;}
+        void gm(int) @property {}
+
+        private int q;
+
+        I i;
+        alias i this;
+
+
+        void gc(int, int) @property {}
+        void gc(int) @property {}
+    }
+
+    S s;
+    // s.gc(0);
+
+    static assert (DeserializableMembers!S == ["f", "ga", "d", "gm", "gc"]);
+    static assert (DeserializableMembers!(const S) == []);
+}
+
+// This trait defines what members should be serialized -
+// public members that are either readable and writable or getter properties
+private template Serializable(T, string member)
+{
+    static if (!isPublic!(T, member))
+        enum Serializable = false;
+    else
+        enum Serializable = isReadable!(T, member); // any readable is good
+}
+
+private enum bool hasSingleArgument(alias fun) = Parameters!fun.length == 1;
+private enum bool hasZeroArguments(alias fun) = Parameters!fun.length == 0;
+
+// This trait defines what members should be serialized -
+// public members that are either readable and writable or setter properties
+private template Deserializable(T, string member)
+{
+    static if (!isPublic!(T, member))
+        enum Deserializable = false;
+    else
+    static if (isReadableAndWritable!(T, member))
+        enum Deserializable = true;
+    else
+    static if (getSetters!(T, member).length == 1)
+        enum Deserializable =  is(typeof((ref T val){ __traits(getMember, val, member) = Parameters!(getSetters!(T, member)[0])[0].init; }));
+    else
+        enum Deserializable = false;
+}
+
+private enum FieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(FieldsAndPropertiesImpl!T)));
+
+private template FieldsAndPropertiesImpl(T)
+{
+    alias isProperty = ApplyLeft!(.isProperty, T);
+    alias isField = ApplyLeft!(.isField, T);
+    static if (__traits(getAliasThis, T).length)
+    {
+        T aggregate;
+        alias baseMembers = FieldsAndPropertiesImpl!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))));
+        alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
+        alias FieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isField, members), Filter!(isProperty, members));
+        
+    }
+    else
+    {
+        alias members = __traits(allMembers, T);
+        alias FieldsAndPropertiesImpl = AliasSeq!(Filter!(isField, members), Filter!(isProperty, members));
+    }
+}
+
+// check if the member is readable
+private template isReadable(T, string member)
+{
+    T aggregate;
+    enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, aggregate, member)); });
+}
+
+// check if the member is readable/writeble?
+private template isReadableAndWritable(T, string member)
+{
+    T aggregate;
+    enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, aggregate, member) = __traits(getMember, aggregate, member));
+}
+
+private template isPublic(T, string member)
+{
+    T aggregate;
+    enum bool isPublic = !__traits(getProtection, __traits(getMember, aggregate, member)).privateOrPackage;
+}
+
+// check if the member is property
+private template isSetter(T, string member)
+{
+    T aggregate;
+    static if (__traits(compiles, isSomeFunction!(__traits(getMember, aggregate, member))))
+    {
+        static if (isSomeFunction!(__traits(getMember, aggregate, member)))
+        {
+            enum bool isSetter = getSetters!(T, member).length > 0;;
+        }
+        else
+        {
+            enum bool isSetter = false;
+        }
+    }
+    else
+        enum bool isSetter = false;
+}
+
+private template isGetter(T, string member)
+{
+    T aggregate;
+    static if (__traits(compiles, isSomeFunction!(__traits(getMember, aggregate, member))))
+    {
+        static if (isSomeFunction!(__traits(getMember, aggregate, member)))
+        {
+            enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member))).length == 1;
+        }
+        else
+        {
+            enum bool isGetter = false;
+        }
+    }
+    else
+        enum bool isGetter = false;
+}
+
+private enum bool isPropertyImpl(alias member) = (functionAttributes!member & FunctionAttribute.property) != 0;
+
+private bool privateOrPackage()(string protection)
+{
+    return protection == "private" || protection == "package";
+}
diff --git a/source/mir/serde.d b/source/mir/serde.d
new file mode 100644
index 00000000..0e71df0a
--- /dev/null
+++ b/source/mir/serde.d
@@ -0,0 +1,256 @@
+module mir.serde;
+
+import std.traits: getUDAs, hasUDA;
+
+private template getUDA(alias symbol, alias attribute)
+{
+    private alias all = getUDAs!(symbol, attribute);
+    static assert(all.length == 1, "Exactly one " ~ attribute.stringof ~ " attribute is allowed");
+    enum getUDA = all[0];
+}
+
+/++
+Attribute for key overloading during Serialization and Deserialization.
+The first argument overloads the key value during serialization unless `serdeKeyOut` is given.
++/
+struct serdeKeys
+{
+    ///
+    immutable(char[])[] keys;
+
+@safe pure nothrow:
+    ///
+    this(string[] keys...) { this.keys = keys.idup; }
+    ///
+    @disable this();
+}
+
+/++
+Attribute for key overloading during serialization.
++/
+struct serdeKeyOut
+{
+    ///
+    string key;
+
+@safe pure nothrow @nogc:
+    ///
+    this(string key) { this.key = key; }
+}
+
+/++
++/
+template getSerdeKeysIn(alias symbol)
+{
+    static if (hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn))
+        enum immutable(char[])[] getSerdeKeysIn = null;
+    else
+    static if (hasUDA!(symbol, serdeKeys))
+        enum immutable(char[])[] getSerdeKeysIn = getUDA!(symbol, serdeKeys).keys;
+    else
+        enum immutable(char[])[] getSerdeKeysIn = [__traits(identifier, symbol)];
+}
+
+///
+version(mir_test)
+unittest
+{
+    struct S
+    {
+        int f;
+
+        @serdeKeys("D", "t")
+        int d;
+
+        @serdeIgnore
+        int i;
+
+        @serdeIgnoreIn
+        int ii;
+
+        @serdeIgnoreOut
+        int io;
+
+        void p(int) @property {}
+    }
+
+    static assert(getSerdeKeysIn!(S.f) == ["f"]);
+    static assert(getSerdeKeysIn!(S.d) == ["D", "t"]);
+    static assert(getSerdeKeysIn!(S.i) == null);
+    static assert(getSerdeKeysIn!(S.ii) == null);
+    static assert(getSerdeKeysIn!(S.io) == ["io"]);
+    static assert(getSerdeKeysIn!(S.p) == ["p"]);
+}
+
+/++
++/
+template serdeGetKeyOut(alias symbol)
+{
+    static if (hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreOut))
+        enum string serdeGetKeyOut = null;
+    else
+    static if (hasUDA!(symbol, serdeKeyOut))
+        enum string serdeGetKeyOut = getUDA!(symbol, serdeKeyOut).key;
+    else
+    static if (hasUDA!(symbol, serdeKeys))
+        enum string serdeGetKeyOut = getUDA!(symbol, serdeKeys).keys[0];
+    else
+        enum string serdeGetKeyOut = __traits(identifier, symbol);
+}
+
+///
+version(mir_test)
+unittest
+{
+    struct S
+    {
+        int f;
+
+        @serdeKeys("D", "t")
+        int d;
+
+        @serdeIgnore
+        int i;
+
+        @serdeIgnoreIn
+        int ii;
+
+        @serdeIgnoreOut
+        int io;
+
+        @serdeKeys("P")
+        @serdeKeyOut("")
+        void p(int) @property {}
+    }
+
+    static assert(serdeGetKeyOut!(S.f) == "f");
+    static assert(serdeGetKeyOut!(S.d) == "D");
+    static assert(serdeGetKeyOut!(S.i) is null);
+    static assert(serdeGetKeyOut!(S.ii) == "ii");
+    static assert(serdeGetKeyOut!(S.io) is null);
+    static assert(serdeGetKeyOut!(S.p) !is null);
+    static assert(serdeGetKeyOut!(S.p) == "");
+}
+
+/++
+Attribute to ignore field.
++/
+enum serdeIgnore;
+
+/++
+Attribute to ignore field during deserialization.
++/
+enum serdeIgnoreIn;
+
+/++
+Attribute to ignore field during serialization.
++/
+enum serdeIgnoreOut;
+
+/++
+Attribute to ignore a field during deserialization when equals to its default value.
+Do not use it on void initialized fields or aggregates with void initialized fields, recursively.
++/
+enum serdeIgnoreDefault;
+
+///
+version(mir_test)
+unittest
+{
+    struct S
+    {
+        @serdeIgnoreDefault
+        double d = 0; // skips field if 0 during deserialization
+    }
+
+    import std.traits: hasUDA;
+
+    static assert(hasUDA!(S.d, serdeIgnoreDefault));
+}
+
+/++
++/
+
+/++
+Serialization proxy.
++/
+struct serdeProxy(T);
+
+///
+version(mir_test)
+unittest
+{
+    import mir.small_string;
+
+    struct S
+    {
+        @serdeProxy!(SmallString!32)
+        double d;
+    }
+
+    import std.traits: hasUDA, getUDAs;
+
+    static assert(hasUDA!(S.d, serdeProxy));
+    static assert(hasUDA!(S.d, serdeProxy!(SmallString!32)));
+    static assert(getUDAs!(S.d, serdeProxy).length == 1);
+}
+
+/++
+Can be applied only to fields that can be constructed from strings.
+Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation.
+Use this attributes only for strings that would not be used after the input data deallocation.
++/
+enum serdeScopeStringProxy;
+
+/++
+Attributes to out conditional ignore field during serialization.
+
+The predicate should be aplied to the aggregate type itself, not to the member.
++/
+struct serdeIgnoreOutIf(alias pred);
+
+/++
+Allows to use flexible deserialization rules such as conversion from input string to numeric types.
++/
+enum serdeFlexible;
+
+/++
+Allows serialize / deserialize fields like arrays.
+
+A range or a container should be iterable for serialization.
+Following code should compile:
+------
+foreach(ref value; yourRangeOrContainer)
+{
+    ...
+}
+------
+
+`put(value)` method is used for deserialization.
+
+See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn)
++/
+enum serdeLikeList;
+
+/++
+Allows serialize / deserialize fields like objects.
+
+Object should have `opApply` method to allow serialization.
+Following code should compile:
+------
+foreach(key, value; yourObject)
+{
+    ...
+}
+------
+Object should have only one `opApply` method with 2 argument to allow automatic value type deduction.
+
+`opIndexAssign` or `opIndex` is used for deserialization to support required syntax:
+-----
+yourObject["key"] = value;
+-----
+Multiple value types is supported for deserialization.
+
+See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn)
++/
+enum serdeLikeStruct;

From 94247bdb72bab2536841b60cac5a600490af839f Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 5 Aug 2020 09:44:23 +0700
Subject: [PATCH 057/573] fix issue #311

---
 source/mir/algorithm/iteration.d | 47 ++++++++++++++++++++++++--------
 1 file changed, 36 insertions(+), 11 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 3a8edd3a..6684e543 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -1369,16 +1369,17 @@ template minmaxPos(alias pred = "a < b")
         if (!slice.anyEmpty)
         {
             size_t[2][N] ret;
-            auto it = slice._iterator;
-            Iterator[2] iterator = [it, it];
-            minmaxPosImpl!(pred, Iterator, N, kind)(ret, iterator, lightScope(slice));
+            auto scopeSlice = lightScope(slice);
+            auto it = scopeSlice._iterator;
+            LightScopeOf!Iterator[2] iterator = [it, it];
+            minmaxPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
             foreach (i; Iota!N)
             {
                 pret[0]._lengths[i] = ret[i][0];
                 pret[1]._lengths[i] = ret[i][1];
             }
-            pret[0]._iterator = iterator[0];
-            pret[1]._iterator = iterator[1];
+            pret[0]._iterator = slice._iterator + (iterator[0] - scopeSlice._iterator);
+            pret[1]._iterator = slice._iterator + (iterator[1] - scopeSlice._iterator);
         }
         auto strides = slice.strides;
         foreach(i; Iota!(0, pret[0].S))
@@ -1447,9 +1448,10 @@ template minmaxIndex(alias pred = "a < b")
             {
                 ret[i][1] = ret[i][0] = shape[i];
             }
-            auto it = slice._iterator;
-            Iterator[2] iterator = [it, it];
-            minmaxPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, lightScope(slice));
+            auto scopeSlice = lightScope(slice);
+            auto it = scopeSlice._iterator;
+            LightScopeOf!Iterator[2] iterator = [it, it];
+            minmaxPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
             foreach (i; Iota!N)
             {
                 pret[0][i] = slice._lengths[i] - ret[i][0];
@@ -1506,10 +1508,12 @@ template minPos(alias pred = "a < b")
     @optmath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)
         minPos(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
-        typeof(return) ret = { _iterator : slice._iterator };
+        typeof(return) ret;
+        auto iterator = slice.lightScope._iterator;
         if (!slice.anyEmpty)
         {
-            minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret._lengths, ret._iterator, lightScope(slice));
+            minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret._lengths, iterator, lightScope(slice));
+            ret._iterator = slice._iterator + (iterator - slice.lightScope._iterator);
         }
         auto strides = slice.strides;
         foreach(i; Iota!(0, ret.S))
@@ -1581,7 +1585,9 @@ template minIndex(alias pred = "a < b")
         if (!slice.anyEmpty)
         {
             ret = slice.shape;
-            minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, slice._iterator, lightScope(slice));
+            auto scopeSlice = lightScope(slice);
+            auto iterator = scopeSlice._iterator;
+            minPosImpl!(pred, LightScopeOf!Iterator, N, kind)(ret, iterator, scopeSlice);
             foreach (i; Iota!N)
                 ret[i] = slice._lengths[i] - ret[i];
         }
@@ -3993,3 +3999,22 @@ unittest
     static assert(is(typeof(arr.fold!((a, b) => a)(0))));
     assert(arr.length == 1);
 }
+
+unittest
+{
+    import mir.rc.array: RCArray;
+    import mir.algorithm.iteration: minmaxPos, minPos, maxPos, minmaxIndex, minIndex, maxIndex;
+
+    static immutable a = [0.0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
+
+    auto x = RCArray!double(12);
+    foreach(i, ref e; x)
+        e = a[i];
+    auto y = x.asSlice;
+    auto z0 = y.minmaxPos;
+    auto z1 = y.minPos;
+    auto z2 = y.maxPos;
+    auto z3 = y.minmaxIndex;
+    auto z4 = y.minIndex;
+    auto z5 = y.maxIndex;
+}

From fe6d3ebcf2f3fad424d4204d7976d0442a57d538 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 5 Aug 2020 16:57:33 +0700
Subject: [PATCH 058/573] fix construction from null for rc types

---
 source/mir/rc/array.d    | 9 +++++++++
 source/mir/rc/context.d  | 5 -----
 source/mir/rc/ptr.d      | 5 +++++
 source/mir/rc/slim_ptr.d | 5 +++++
 4 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 1a0b87d4..3d00a4ca 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -46,6 +46,11 @@ struct mir_rcarray(T)
         rhs._payload = t;
     }
 
+    ///
+    this(typeof(null))
+    {
+    }
+
     ///
     mixin CommonRCImpl;
 
@@ -786,4 +791,8 @@ unittest
     auto ars = [S("123"), S("422")];
     alias R = mir_rcarray!S;
     auto rc = ars.rcarray!S;
+
+    RCArray!int value = null;
+    value = null;
 }
+
diff --git a/source/mir/rc/context.d b/source/mir/rc/context.d
index 74118dfc..57a724f2 100644
--- a/source/mir/rc/context.d
+++ b/source/mir/rc/context.d
@@ -171,11 +171,6 @@ mir_rc_context* mir_rc_create(
 ///
 package mixin template CommonRCImpl()
 {
-    ///
-    this(typeof(null))
-    {
-    }
-
     ///
     ref opAssign(typeof(null)) scope return
     {
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index 838e334f..893fb121 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -90,6 +90,11 @@ struct mir_rcptr(T)
         rhs._context = t1;
     }
 
+    ///
+    this(typeof(null))
+    {
+    }
+
     ///
     mixin CommonRCImpl;
 
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index 3366aa5a..5d3a4cbc 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -86,6 +86,11 @@ struct mir_slim_rcptr(T)
         rhs._value = t;
     }
 
+    ///
+    this(typeof(null))
+    {
+    }
+
     ///
     mixin CommonRCImpl;
 

From 98773c572aa38c01c5a6e24d115ee163655ea403 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 7 Aug 2020 12:30:59 +0700
Subject: [PATCH 059/573] add more serde attributes

---
 source/mir/serde.d | 146 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 136 insertions(+), 10 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 0e71df0a..9babbf11 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -40,15 +40,29 @@ struct serdeKeyOut
 
 /++
 +/
-template getSerdeKeysIn(alias symbol)
+template serdeGetKeysIn(alias symbol)
 {
     static if (hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn))
-        enum immutable(char[])[] getSerdeKeysIn = null;
+        enum immutable(char[])[] serdeGetKeysIn = null;
     else
     static if (hasUDA!(symbol, serdeKeys))
-        enum immutable(char[])[] getSerdeKeysIn = getUDA!(symbol, serdeKeys).keys;
+        enum immutable(char[])[] serdeGetKeysIn = getUDA!(symbol, serdeKeys).keys;
     else
-        enum immutable(char[])[] getSerdeKeysIn = [__traits(identifier, symbol)];
+        enum immutable(char[])[] serdeGetKeysIn = [__traits(identifier, symbol)];
+}
+
+///
+immutable(char[])[] serdeGetKeysIn(T)(T value)
+    if (is(T == enum))
+{
+    import std.traits: EnumMembers;
+    foreach (i, member; EnumMembers!T)
+    {
+        alias all = __traits(getAttributes, EnumMembers!T[i]);
+        if (value == member)
+            return .serdeGetKeysIn!(EnumMembers!T[i]);
+    }
+    assert(0);
 }
 
 ///
@@ -74,12 +88,28 @@ unittest
         void p(int) @property {}
     }
 
-    static assert(getSerdeKeysIn!(S.f) == ["f"]);
-    static assert(getSerdeKeysIn!(S.d) == ["D", "t"]);
-    static assert(getSerdeKeysIn!(S.i) == null);
-    static assert(getSerdeKeysIn!(S.ii) == null);
-    static assert(getSerdeKeysIn!(S.io) == ["io"]);
-    static assert(getSerdeKeysIn!(S.p) == ["p"]);
+    static assert(serdeGetKeysIn!(S.f) == ["f"]);
+    static assert(serdeGetKeysIn!(S.d) == ["D", "t"]);
+    static assert(serdeGetKeysIn!(S.i) == null);
+    static assert(serdeGetKeysIn!(S.ii) == null);
+    static assert(serdeGetKeysIn!(S.io) == ["io"]);
+    static assert(serdeGetKeysIn!(S.p) == ["p"]);
+}
+
+///
+unittest
+{
+    enum E
+    {
+        @serdeKeys("A", "alpha")
+        a,
+        @serdeKeys("B", "beta")
+        b,
+        c,
+    }
+
+    static assert (serdeGetKeysIn(E.a) == ["A", "alpha"]);
+    static assert (serdeGetKeysIn(E.c) == ["c"]);
 }
 
 /++
@@ -98,6 +128,20 @@ template serdeGetKeyOut(alias symbol)
         enum string serdeGetKeyOut = __traits(identifier, symbol);
 }
 
+///ditto
+immutable(char)[] serdeGetKeyOut(T)(T value)
+    if (is(T == enum))
+{
+    import std.traits: EnumMembers;
+    foreach (i, member; EnumMembers!T)
+    {
+        alias all = __traits(getAttributes, EnumMembers!T[i]);
+        if (value == member)
+            return .serdeGetKeyOut!(EnumMembers!T[i]);
+    }
+    assert(0);
+}
+
 ///
 version(mir_test)
 unittest
@@ -132,6 +176,24 @@ unittest
     static assert(serdeGetKeyOut!(S.p) == "");
 }
 
+///
+unittest
+{
+    enum E
+    {
+        @serdeKeys("A", "alpha")
+        a,
+        @serdeKeys("B", "beta")
+        @serdeKeyOut("o")
+        b,
+        c,
+    }
+
+    static assert (serdeGetKeyOut(E.a) == "A");
+    static assert (serdeGetKeyOut(E.b) == "o");
+    static assert (serdeGetKeyOut(E.c) == "c");
+}
+
 /++
 Attribute to ignore field.
 +/
@@ -254,3 +316,67 @@ Multiple value types is supported for deserialization.
 See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn)
 +/
 enum serdeLikeStruct;
+
+/++
+Ignore keys for object and enum members.
+Should be applied to members or enum type itself.
++/
+enum serdeIgnoreCase;
+
+///
+bool hasSerdeIgnoreCase(T)(T value)
+    if (is(T == enum))
+{
+    static if (hasUDA!(T, serdeIgnoreCase))
+    {
+        return true;
+    }
+    else
+    {
+        import std.traits: EnumMembers;
+        foreach (i, member; EnumMembers!T)
+        {
+            alias all = __traits(getAttributes, EnumMembers!T[i]);
+            if (value == member)
+                return hasUDA!(EnumMembers!T[i], serdeIgnoreCase);
+        }
+        assert(0);
+    }
+}
+
+///
+unittest
+{
+    enum E
+    {
+        @serdeIgnoreCase
+        a,
+        b,
+        @serdeIgnoreCase
+        c,
+        d,
+    }
+
+    static assert(hasSerdeIgnoreCase(E.a));
+    static assert(!hasSerdeIgnoreCase(E.b));
+    static assert(hasSerdeIgnoreCase(E.c));
+    static assert(!hasSerdeIgnoreCase(E.d));
+}
+
+///
+unittest
+{
+    @serdeIgnoreCase
+    enum E
+    {
+        a,
+        b,
+        c,
+        d,
+    }
+
+    static assert(hasSerdeIgnoreCase(E.a));
+    static assert(hasSerdeIgnoreCase(E.b));
+    static assert(hasSerdeIgnoreCase(E.c));
+    static assert(hasSerdeIgnoreCase(E.d));
+}

From c3d9c7d7e9928ba72ab529f130f97ca57293e515 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sun, 9 Aug 2020 10:55:09 +0700
Subject: [PATCH 060/573] update serde/reflection (#314)

---
 source/mir/reflection.d | 24 ++++++++++++++----
 source/mir/serde.d      | 56 +++++++++++++++++++++++++++++++++--------
 2 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/source/mir/reflection.d b/source/mir/reflection.d
index ce04ff5d..6ac4323a 100644
--- a/source/mir/reflection.d
+++ b/source/mir/reflection.d
@@ -3,17 +3,31 @@
 module mir.reflection;
 
 import std.meta;
-import std.traits: Parameters, isSomeFunction, FunctionAttribute, functionAttributes;
+import std.traits: getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes;
 
 /++
-Checks if member is field.
+Returns: single UDA.
 +/
-template isField(T, string member)
+template getUDA(alias symbol, alias attribute)
 {
-    T aggregate;
-    enum bool isField = __traits(compiles, __traits(getMember, aggregate, member).offsetof);
+    private alias all = getUDAs!(symbol, attribute);
+    static if (all.length != 1)
+        static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required");
+    else
+    {
+        static if (is(typeof(all[0])))
+            enum getUDA = all[0];
+        else
+            alias getUDA = all[0];
+    }
 }
 
+/++
+Checks if member is field.
++/
+enum bool isField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; });
+
+
 ///
 version(mir_test)
 unittest
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 9babbf11..88cae386 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1,13 +1,7 @@
 module mir.serde;
 
-import std.traits: getUDAs, hasUDA;
-
-private template getUDA(alias symbol, alias attribute)
-{
-    private alias all = getUDAs!(symbol, attribute);
-    static assert(all.length == 1, "Exactly one " ~ attribute.stringof ~ " attribute is allowed");
-    enum getUDA = all[0];
-}
+import mir.reflection: getUDA;
+import std.traits: hasUDA, TemplateArgsOf;
 
 /++
 Attribute for key overloading during Serialization and Deserialization.
@@ -250,13 +244,17 @@ unittest
         double d;
     }
 
-    import std.traits: hasUDA, getUDAs;
+    import std.traits: hasUDA;
 
     static assert(hasUDA!(S.d, serdeProxy));
     static assert(hasUDA!(S.d, serdeProxy!(SmallString!32)));
-    static assert(getUDAs!(S.d, serdeProxy).length == 1);
+    static assert(is(serdeGetProxy!(S.d) == SmallString!32));
 }
 
+/++
++/
+alias serdeGetProxy(alias symbol) = TemplateArgsOf!(getUDA!(symbol, serdeProxy))[0];
+
 /++
 Can be applied only to fields that can be constructed from strings.
 Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation.
@@ -380,3 +378,41 @@ unittest
     static assert(hasSerdeIgnoreCase(E.c));
     static assert(hasSerdeIgnoreCase(E.d));
 }
+
+/++
+Can be applied only to strings fields.
+Does not allocate new data when deserializeing. Raw ASDF data is used for strings instead of new memory allocation.
+Use this attributes only for strings that would not be used after ASDF deallocation.
++/
+enum serdeScoped;
+
+/++
+Attribute that force deserializer to throw an exception that the field was not found in the input.
++/
+enum serdeRequired;
+
+
+/++
+Attributes for in transformation.
+Return type of in transformation must be implicitly convertable to the type of the field.
+In transformation would be applied after serialization proxy if any.
+
++/
+struct serdeTransformIn(alias fun) {}
+
+/++
+Returns: unary function of underlaying alias of $(LREF serdeTransformIn)
++/
+alias serdeGetTransformIn(alias value) = unaryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformIn))[0]);
+
+/++
+Attributes for out transformation.
+Return type of out transformation may be differ from the type of the field.
+Out transformation would be applied before serialization proxy if any.
++/
+struct serdeTransformOut(alias fun) {}
+
+/++
+Returns: unary function of underlaying alias of $(LREF serdeTransformOut)
++/
+alias serdeGetTransformOut(alias value) = unaryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]);

From e4817600bc009325526ece3f7bdc66ba6626127a Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 9 Aug 2020 10:58:16 +0700
Subject: [PATCH 061/573] fix nary fun

---
 source/mir/serde.d | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 88cae386..c6482b25 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1,5 +1,6 @@
 module mir.serde;
 
+import mir.functional: naryFun;
 import mir.reflection: getUDA;
 import std.traits: hasUDA, TemplateArgsOf;
 
@@ -403,7 +404,7 @@ struct serdeTransformIn(alias fun) {}
 /++
 Returns: unary function of underlaying alias of $(LREF serdeTransformIn)
 +/
-alias serdeGetTransformIn(alias value) = unaryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformIn))[0]);
+alias serdeGetTransformIn(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformIn))[0]);
 
 /++
 Attributes for out transformation.
@@ -415,4 +416,4 @@ struct serdeTransformOut(alias fun) {}
 /++
 Returns: unary function of underlaying alias of $(LREF serdeTransformOut)
 +/
-alias serdeGetTransformOut(alias value) = unaryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]);
+alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]);

From 1aee3847e898be4d68037e33c7aa199a8577879c Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 9 Aug 2020 11:41:08 +0700
Subject: [PATCH 062/573] fix reflection

---
 source/mir/reflection.d | 35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/source/mir/reflection.d b/source/mir/reflection.d
index 6ac4323a..bdc71af6 100644
--- a/source/mir/reflection.d
+++ b/source/mir/reflection.d
@@ -27,7 +27,6 @@ Checks if member is field.
 +/
 enum bool isField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; });
 
-
 ///
 version(mir_test)
 unittest
@@ -108,13 +107,13 @@ Checks if member is property.
 +/
 template isProperty(T, string member)
 {
-    T aggregate;
+    T* aggregate;
 
-    static if (__traits(compiles, isSomeFunction!(__traits(getMember, aggregate, member))))
+    static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
     {
-        static if (isSomeFunction!(__traits(getMember, aggregate, member)))
+        static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
         {
-            enum bool isProperty = isPropertyImpl!(__traits(getMember, aggregate, member));
+            enum bool isProperty = isPropertyImpl!(__traits(getMember, *aggregate, member));
         }
         else
         {
@@ -336,7 +335,7 @@ private template FieldsAndPropertiesImpl(T)
     alias isField = ApplyLeft!(.isField, T);
     static if (__traits(getAliasThis, T).length)
     {
-        T aggregate;
+        T* aggregate;
         alias baseMembers = FieldsAndPropertiesImpl!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))));
         alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
         alias FieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isField, members), Filter!(isProperty, members));
@@ -352,30 +351,30 @@ private template FieldsAndPropertiesImpl(T)
 // check if the member is readable
 private template isReadable(T, string member)
 {
-    T aggregate;
-    enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, aggregate, member)); });
+    T* aggregate;
+    enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, *aggregate, member)); });
 }
 
 // check if the member is readable/writeble?
 private template isReadableAndWritable(T, string member)
 {
-    T aggregate;
-    enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, aggregate, member) = __traits(getMember, aggregate, member));
+    T* aggregate;
+    enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, *aggregate, member) = __traits(getMember, *aggregate, member));
 }
 
 private template isPublic(T, string member)
 {
-    T aggregate;
-    enum bool isPublic = !__traits(getProtection, __traits(getMember, aggregate, member)).privateOrPackage;
+    T* aggregate;
+    enum bool isPublic = !__traits(getProtection, __traits(getMember, *aggregate, member)).privateOrPackage;
 }
 
 // check if the member is property
 private template isSetter(T, string member)
 {
-    T aggregate;
-    static if (__traits(compiles, isSomeFunction!(__traits(getMember, aggregate, member))))
+    T* aggregate;
+    static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
     {
-        static if (isSomeFunction!(__traits(getMember, aggregate, member)))
+        static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
         {
             enum bool isSetter = getSetters!(T, member).length > 0;;
         }
@@ -390,10 +389,10 @@ private template isSetter(T, string member)
 
 private template isGetter(T, string member)
 {
-    T aggregate;
-    static if (__traits(compiles, isSomeFunction!(__traits(getMember, aggregate, member))))
+    T* aggregate;
+    static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
     {
-        static if (isSomeFunction!(__traits(getMember, aggregate, member)))
+        static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
         {
             enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member))).length == 1;
         }

From a080e7bdc38e78ed43acfe3daccd5c1a6fa785a2 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 9 Aug 2020 12:00:13 +0700
Subject: [PATCH 063/573] add serdeGetIgnoreOutIf

---
 source/mir/serde.d | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index c6482b25..b6ade24e 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -270,6 +270,10 @@ The predicate should be aplied to the aggregate type itself, not to the member.
 +/
 struct serdeIgnoreOutIf(alias pred);
 
+/++
++/
+alias serdeGetIgnoreOutIf(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIf))[0]);
+
 /++
 Allows to use flexible deserialization rules such as conversion from input string to numeric types.
 +/

From 0194c234b6d51d551e887930f5fee510a7ae3ba5 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 9 Aug 2020 22:23:26 +0700
Subject: [PATCH 064/573] add serdeDoc

---
 source/mir/serde.d | 188 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 180 insertions(+), 8 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index b6ade24e..8f7421eb 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1,8 +1,77 @@
 module mir.serde;
 
 import mir.functional: naryFun;
-import mir.reflection: getUDA;
-import std.traits: hasUDA, TemplateArgsOf;
+import mir.reflection;
+import std.traits: TemplateArgsOf, EnumMembers, hasUDA;
+import std.meta: Filter, anySatisfy;
+
+/++
+Attribute for documentation.
++/
+struct serdeDoc
+{
+    ///
+    string text;
+    ///
+    this()(string text) { this.text = text; }
+    ///
+    @disable this();
+}
+
+/++
++/
+template serdeGetDoc(alias symbol)
+{
+    static if (hasUDA!(symbol, serdeDoc))
+        enum string serdeGetDoc = getUDA!(symbol, serdeDoc).text;
+    else
+        enum string serdeGetDoc = null;
+}
+
+/// ditto
+string serdeGetDoc(T)(T value)
+    if (is(T == enum))
+{
+    import std.traits: EnumMembers;
+    final switch (value)
+    {
+        foreach (i, member; EnumMembers!T)
+        {
+            case member:
+            alias all = __traits(getAttributes, EnumMembers!T[i]);
+            static if (hasUDA!(EnumMembers!T[i], serdeDoc))
+                return getUDA!(EnumMembers!T[i], serdeDoc).text;
+            else
+                return null;
+        }
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    enum E
+    {
+        @serdeDoc("alpha")
+        a,
+        @serdeDoc("beta")
+        b,
+        c,
+    }
+
+    static assert(serdeGetDoc(E.a) == "alpha");
+    static assert(serdeGetDoc(E.b) == "beta");
+    static assert(serdeGetDoc(E.c) is null);
+
+    struct S
+    {
+        @serdeDoc("alpha")
+        int a;
+    }
+
+    static assert(serdeGetDoc!(S.a) == "alpha");
+}
 
 /++
 Attribute for key overloading during Serialization and Deserialization.
@@ -46,18 +115,26 @@ template serdeGetKeysIn(alias symbol)
         enum immutable(char[])[] serdeGetKeysIn = [__traits(identifier, symbol)];
 }
 
-///
+/// ditto
 immutable(char[])[] serdeGetKeysIn(T)(T value)
     if (is(T == enum))
 {
     import std.traits: EnumMembers;
-    foreach (i, member; EnumMembers!T)
+    final switch (value)
     {
-        alias all = __traits(getAttributes, EnumMembers!T[i]);
-        if (value == member)
-            return .serdeGetKeysIn!(EnumMembers!T[i]);
+        foreach (i, member; EnumMembers!T)
+        {
+            case member:
+            alias all = __traits(getAttributes, EnumMembers!T[i]);
+            static if (hasUDA!(EnumMembers!T[i], serdeIgnore) || hasUDA!(EnumMembers!T[i], serdeIgnoreIn))
+                return null;
+            else
+            static if (hasUDA!(EnumMembers!T[i], serdeKeys))
+                return getUDA!(EnumMembers!T[i], serdeKeys).keys;
+            else
+                return [__traits(identifier, EnumMembers!T[i])];
+        }
     }
-    assert(0);
 }
 
 ///
@@ -92,6 +169,7 @@ unittest
 }
 
 ///
+version(mir_test)
 unittest
 {
     enum E
@@ -172,6 +250,7 @@ unittest
 }
 
 ///
+version(mir_test)
 unittest
 {
     enum E
@@ -348,6 +427,7 @@ bool hasSerdeIgnoreCase(T)(T value)
 }
 
 ///
+version(mir_test)
 unittest
 {
     enum E
@@ -367,6 +447,7 @@ unittest
 }
 
 ///
+version(mir_test)
 unittest
 {
     @serdeIgnoreCase
@@ -421,3 +502,94 @@ struct serdeTransformOut(alias fun) {}
 Returns: unary function of underlaying alias of $(LREF serdeTransformOut)
 +/
 alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]);
+
+/++
++/
+bool serdeParseEnum(E)(const char[] str, out E res)
+{
+    static if (hasUDA!(E, serdeIgnoreCase))
+    {
+        import mir.format: stringBuf;
+        stringBuf buf;
+        buf << str;
+        auto ustr = buf.data.fastToUpperInPlace;
+    }
+    else
+    {
+        alias ustr = str;
+    }
+    switch(ustr)
+    {
+        foreach(i, member; EnumMembers!E)
+        {{
+            enum initKeys = serdeGetKeysIn(EnumMembers!E[i]);
+            static if (hasUDA!(E, serdeIgnoreCase))
+            {
+                import mir.ndslice.topology: map;
+                import mir.array.allocation: array;
+                enum keys = initKeys.map!fastLazyToUpper.map!array.array;
+            }
+            else
+            {
+                enum keys = initKeys;
+            }
+            static assert (keys.length, "At least one input enum key is required");
+            static foreach (key; keys)
+            {
+                case key:
+            }
+            res = member;
+            return true;
+        }}
+        default:
+            return false;
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    @serdeIgnoreCase // supported for the whole type
+    enum E
+    {
+        @serdeKeys("A", "alpha")
+        a,
+        @serdeKeys("B", "beta")
+        b,
+        c,
+    }
+
+    auto e = E.c;
+    assert(serdeParseEnum("a", e));
+    assert(e == E.a);
+    assert(serdeParseEnum("alpha", e));
+    assert(e == E.a);
+    assert(serdeParseEnum("BETA", e));
+    assert(e == E.b);
+    assert(serdeParseEnum("b", e));
+    assert(e == E.b);
+    assert(serdeParseEnum("C", e));
+    assert(e == E.c);
+}
+
+private:
+
+auto fastLazyToUpper()(const(char)[] name)
+{
+    import mir.ndslice.topology: map;
+    return name.map!fastToUpper;
+}
+
+auto fastToUpper()(char a)
+{   // std.ascii may not be inlined
+    return 'a' <= a && a <= 'z' ? cast(char)(a ^ 0x20) : a;
+}
+
+@safe pure nothrow @nogc
+char[] fastToUpperInPlace()(scope return char[] a)
+{
+    foreach(ref char e; a)
+        e = e.fastToUpper;
+    return a;
+}

From 03ef8e981df5799dca3709b16da6ee8e316b0f0b Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Mon, 10 Aug 2020 08:23:08 +0700
Subject: [PATCH 065/573] extend mir.reflection (#315)

---
 source/mir/reflection.d | 235 +++++++++++++++++++++++++++++++++++++++-
 source/mir/serde.d      |  68 ------------
 2 files changed, 234 insertions(+), 69 deletions(-)

diff --git a/source/mir/reflection.d b/source/mir/reflection.d
index bdc71af6..0c58d1e0 100644
--- a/source/mir/reflection.d
+++ b/source/mir/reflection.d
@@ -3,7 +3,240 @@
 module mir.reflection;
 
 import std.meta;
-import std.traits: getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes;
+import std.traits: hasUDA, getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes;
+
+/++
+Match types like `std.typeconst: Nullable`.
++/
+template isStdNullable(T)
+{
+    import std.traits : hasMember;
+
+    enum bool isStdNullable =
+        hasMember!(T, "isNull") &&
+        hasMember!(T, "get") &&
+        hasMember!(T, "nullify") &&
+        is(typeof(__traits(getMember, T, "isNull")) == bool) &&
+        !is(typeof(__traits(getMember, T, "get")) == void) &&
+        is(typeof(__traits(getMember, T, "nullify")) == void);
+}
+
+///
+unittest
+{
+    import std.typecons;
+    assert(isStdNullable!(Nullable!double));
+}
+
+///  Attribute for deprecated API
+struct reflectDeprecated(string target)
+{
+    string info;
+}
+
+/// Attribute to rename methods, types and functions
+template ReflectName(string target)
+{
+    ///
+    struct ReflectName(Args...)
+    {
+        ///
+        string name;
+    }
+}
+
+/// ditto
+template reflectName(string target = null, Args...)
+{
+    ///
+    alias TargetName = ReflectName!target;
+    ///
+    auto reflectName(string name)
+    {
+        return TargetName!Args(name);
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    enum E { A, B, C }
+
+    struct S
+    {
+        @reflectName("A")
+        int a;
+
+        @reflectName!"c++"("B")
+        int b;
+
+        @reflectName!("C",  double)("cd")
+        @reflectName!("C",  float)("cf")
+        F c(F)()
+        {
+            return b;
+        }
+    }
+
+    import std.traits: hasUDA;
+
+    alias UniName = ReflectName!null;
+    alias CppName = ReflectName!"c++";
+    alias CName = ReflectName!"C";
+
+    static assert(hasUDA!(S.a, UniName!()("A")));
+    static assert(hasUDA!(S.b, CppName!()("B")));
+
+    // static assert(hasUDA!(S.c, ReflectName)); // doesn't work for now
+    static assert(hasUDA!(S.c, CName));
+    static assert(hasUDA!(S.c, CName!double));
+    static assert(hasUDA!(S.c, CName!float));
+    static assert(hasUDA!(S.c, CName!double("cd")));
+    static assert(hasUDA!(S.c, CName!float("cf")));
+}
+
+/// Attribute to rename methods, types and functions
+template ReflectMeta(string target)
+{
+    ///
+    struct ReflectMeta(Args...)
+    {
+        ///
+        Args args;
+    }
+}
+
+/// ditto
+template reflectMeta(string target = null)
+{
+    ///
+    alias TargetMeta = ReflectMeta!target;
+    ///
+    auto reflectMeta(Args...)(Args args)
+    {
+        return TargetMeta!Args(args);
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    enum E { A, B, C }
+
+    struct S
+    {
+        @reflectMeta(E.A)
+        int a;
+        @reflectMeta!"c++"(E.C)
+        int b;
+    }
+
+    import std.traits: hasUDA;
+
+    alias UniMeta = ReflectMeta!null;
+    alias CppMeta = ReflectMeta!"c++";
+
+    static assert(hasUDA!(S.a, UniMeta!E(E.A)));
+    static assert(hasUDA!(S.b, CppMeta!E(E.C)));
+}
+
+/++
+Attribute to ignore a reflection target
++/
+template reflectIgnore(string target)
+{
+    enum reflectIgnore;
+}
+
+///
+version(mir_test)
+unittest
+{
+    struct S
+    {
+        @reflectIgnore!"c++"
+        int a;
+    }
+
+    import std.traits: hasUDA;
+    static assert(hasUDA!(S.a, reflectIgnore!"c++"));
+}
+
+/++
+Attribute for documentation.
++/
+struct reflectDoc
+{
+    ///
+    string text;
+    ///
+    reflectTestDoc test;
+}
+
+/++
+Unittest documentation (line)
++/
+struct reflectTestDoc
+{
+    string text;
+}
+
+/++
++/
+template serdeGetDoc(alias symbol)
+{
+    static if (hasUDA!(symbol, reflectDoc))
+        enum reflectDoc serdeGetDoc = getUDA!(symbol, reflectDoc);
+    else
+        enum reflectDoc serdeGetDoc = reflectDoc.init;
+}
+
+/// ditto
+reflectDoc serdeGetDoc(T)(T value)
+    if (is(T == enum))
+{
+    import std.traits: EnumMembers;
+    final switch (value)
+    {
+        foreach (i, member; EnumMembers!T)
+        {
+            case member:
+            alias all = __traits(getAttributes, EnumMembers!T[i]);
+            static if (hasUDA!(EnumMembers!T[i], reflectDoc))
+                return getUDA!(EnumMembers!T[i], reflectDoc);
+            else
+                return reflectDoc.init;
+        }
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    enum E
+    {
+        @reflectDoc("alpha")
+        a,
+        @reflectDoc("beta", reflectTestDoc("test line"))
+        b,
+        c,
+    }
+
+    static assert(serdeGetDoc(E.a) == reflectDoc("alpha"));
+    static assert(serdeGetDoc(E.b) == reflectDoc("beta", reflectTestDoc("test line")));
+    static assert(serdeGetDoc(E.c).text is null);
+
+    struct S
+    {
+        @reflectDoc("alpha")
+        int a;
+    }
+
+    static assert(serdeGetDoc!(S.a) == reflectDoc("alpha"));
+}
 
 /++
 Returns: single UDA.
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 8f7421eb..0235def1 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -5,74 +5,6 @@ import mir.reflection;
 import std.traits: TemplateArgsOf, EnumMembers, hasUDA;
 import std.meta: Filter, anySatisfy;
 
-/++
-Attribute for documentation.
-+/
-struct serdeDoc
-{
-    ///
-    string text;
-    ///
-    this()(string text) { this.text = text; }
-    ///
-    @disable this();
-}
-
-/++
-+/
-template serdeGetDoc(alias symbol)
-{
-    static if (hasUDA!(symbol, serdeDoc))
-        enum string serdeGetDoc = getUDA!(symbol, serdeDoc).text;
-    else
-        enum string serdeGetDoc = null;
-}
-
-/// ditto
-string serdeGetDoc(T)(T value)
-    if (is(T == enum))
-{
-    import std.traits: EnumMembers;
-    final switch (value)
-    {
-        foreach (i, member; EnumMembers!T)
-        {
-            case member:
-            alias all = __traits(getAttributes, EnumMembers!T[i]);
-            static if (hasUDA!(EnumMembers!T[i], serdeDoc))
-                return getUDA!(EnumMembers!T[i], serdeDoc).text;
-            else
-                return null;
-        }
-    }
-}
-
-///
-version(mir_test)
-unittest
-{
-    enum E
-    {
-        @serdeDoc("alpha")
-        a,
-        @serdeDoc("beta")
-        b,
-        c,
-    }
-
-    static assert(serdeGetDoc(E.a) == "alpha");
-    static assert(serdeGetDoc(E.b) == "beta");
-    static assert(serdeGetDoc(E.c) is null);
-
-    struct S
-    {
-        @serdeDoc("alpha")
-        int a;
-    }
-
-    static assert(serdeGetDoc!(S.a) == "alpha");
-}
-
 /++
 Attribute for key overloading during Serialization and Deserialization.
 The first argument overloads the key value during serialization unless `serdeKeyOut` is given.

From 0fd4c8b0eaa9352d99c6364a6def6dc1340f9dc9 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 10 Aug 2020 08:31:33 +0700
Subject: [PATCH 066/573] update serdeGetKeyOut

---
 source/mir/serde.d | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 0235def1..4bce5e48 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -134,17 +134,19 @@ template serdeGetKeyOut(alias symbol)
 }
 
 ///ditto
-immutable(char)[] serdeGetKeyOut(T)(T value)
+string serdeGetKeyOut(T)(T value)
     if (is(T == enum))
 {
     import std.traits: EnumMembers;
-    foreach (i, member; EnumMembers!T)
+    final switch (value)
     {
-        alias all = __traits(getAttributes, EnumMembers!T[i]);
-        if (value == member)
-            return .serdeGetKeyOut!(EnumMembers!T[i]);
+        foreach (i, member; EnumMembers!T)
+        {
+            case member:
+                {alias all = __traits(getAttributes, EnumMembers!T[i]);}
+                return .serdeGetKeyOut!(EnumMembers!T[i]);
+        }
     }
-    assert(0);
 }
 
 ///

From 71a0d86d9b888f441f5c852cd484e33b229910b1 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 10 Aug 2020 11:40:25 +0700
Subject: [PATCH 067/573] fix serdeKeys attributes

---
 source/mir/serde.d | 73 +++++++++++++++++++++++-----------------------
 1 file changed, 37 insertions(+), 36 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 4bce5e48..37ed0693 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -3,7 +3,6 @@ module mir.serde;
 import mir.functional: naryFun;
 import mir.reflection;
 import std.traits: TemplateArgsOf, EnumMembers, hasUDA;
-import std.meta: Filter, anySatisfy;
 
 /++
 Attribute for key overloading during Serialization and Deserialization.
@@ -12,13 +11,11 @@ The first argument overloads the key value during serialization unless `serdeKey
 struct serdeKeys
 {
     ///
-    immutable(char[])[] keys;
+    immutable(string)[] keys;
 
-@safe pure nothrow:
+@system pure nothrow @nogc:
     ///
-    this(string[] keys...) { this.keys = keys.idup; }
-    ///
-    @disable this();
+    this(immutable(string)[] keys...) { this.keys = keys; }
 }
 
 /++
@@ -39,32 +36,31 @@ struct serdeKeyOut
 template serdeGetKeysIn(alias symbol)
 {
     static if (hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn))
-        enum immutable(char[])[] serdeGetKeysIn = null;
+        enum immutable(string)[] serdeGetKeysIn = null;
     else
     static if (hasUDA!(symbol, serdeKeys))
-        enum immutable(char[])[] serdeGetKeysIn = getUDA!(symbol, serdeKeys).keys;
+        enum immutable(string)[] serdeGetKeysIn = getUDA!(symbol, serdeKeys).keys;
     else
-        enum immutable(char[])[] serdeGetKeysIn = [__traits(identifier, symbol)];
+        enum immutable(string)[] serdeGetKeysIn = [__traits(identifier, symbol)];
 }
 
 /// ditto
-immutable(char[])[] serdeGetKeysIn(T)(T value)
+immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc
     if (is(T == enum))
 {
-    import std.traits: EnumMembers;
+    foreach (i, member; EnumMembers!T)
+    {{
+        alias all = __traits(getAttributes, EnumMembers!T[i]);
+    }}
+
+    import std.meta: staticMap;
+    static immutable ret = [staticMap!(.serdeGetKeysIn, EnumMembers!T)];
     final switch (value)
     {
         foreach (i, member; EnumMembers!T)
         {
             case member:
-            alias all = __traits(getAttributes, EnumMembers!T[i]);
-            static if (hasUDA!(EnumMembers!T[i], serdeIgnore) || hasUDA!(EnumMembers!T[i], serdeIgnoreIn))
-                return null;
-            else
-            static if (hasUDA!(EnumMembers!T[i], serdeKeys))
-                return getUDA!(EnumMembers!T[i], serdeKeys).keys;
-            else
-                return [__traits(identifier, EnumMembers!T[i])];
+                return ret[i];
         }
     }
 }
@@ -117,6 +113,28 @@ unittest
     static assert (serdeGetKeysIn(E.c) == ["c"]);
 }
 
+///ditto
+@trusted pure nothrow @nogc
+string serdeGetKeyOut(T)(const T value)
+    if (is(T == enum))
+{
+    foreach (i, member; EnumMembers!T)
+    {{
+        alias all = __traits(getAttributes, EnumMembers!T[i]);
+    }}
+
+    import std.meta: staticMap;
+    static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
+    final switch (value)
+    {
+        foreach (i, member; EnumMembers!T)
+        {
+            case member:
+                return ret[i];
+        }
+    }
+}
+
 /++
 +/
 template serdeGetKeyOut(alias symbol)
@@ -133,22 +151,6 @@ template serdeGetKeyOut(alias symbol)
         enum string serdeGetKeyOut = __traits(identifier, symbol);
 }
 
-///ditto
-string serdeGetKeyOut(T)(T value)
-    if (is(T == enum))
-{
-    import std.traits: EnumMembers;
-    final switch (value)
-    {
-        foreach (i, member; EnumMembers!T)
-        {
-            case member:
-                {alias all = __traits(getAttributes, EnumMembers!T[i]);}
-                return .serdeGetKeyOut!(EnumMembers!T[i]);
-        }
-    }
-}
-
 ///
 version(mir_test)
 unittest
@@ -349,7 +351,6 @@ bool hasSerdeIgnoreCase(T)(T value)
     }
     else
     {
-        import std.traits: EnumMembers;
         foreach (i, member; EnumMembers!T)
         {
             alias all = __traits(getAttributes, EnumMembers!T[i]);

From 11284edd38e0cddafc18575883ea17eb872ff046 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 10 Aug 2020 11:56:45 +0700
Subject: [PATCH 068/573] update reflection

---
 source/mir/reflection.d | 105 ++++++++++++++++++++++++++++++----------
 1 file changed, 80 insertions(+), 25 deletions(-)

diff --git a/source/mir/reflection.d b/source/mir/reflection.d
index 0c58d1e0..7777009a 100644
--- a/source/mir/reflection.d
+++ b/source/mir/reflection.d
@@ -3,7 +3,7 @@
 module mir.reflection;
 
 import std.meta;
-import std.traits: hasUDA, getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes;
+import std.traits: hasUDA, getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers;
 
 /++
 Match types like `std.typeconst: Nullable`.
@@ -171,16 +171,6 @@ struct reflectDoc
 {
     ///
     string text;
-    ///
-    reflectTestDoc test;
-}
-
-/++
-Unittest documentation (line)
-+/
-struct reflectTestDoc
-{
-    string text;
 }
 
 /++
@@ -188,26 +178,25 @@ struct reflectTestDoc
 template serdeGetDoc(alias symbol)
 {
     static if (hasUDA!(symbol, reflectDoc))
-        enum reflectDoc serdeGetDoc = getUDA!(symbol, reflectDoc);
+        enum string serdeGetDoc = getUDA!(symbol, reflectDoc).text;
     else
-        enum reflectDoc serdeGetDoc = reflectDoc.init;
+        enum string serdeGetDoc = null;
 }
 
 /// ditto
-reflectDoc serdeGetDoc(T)(T value)
+string serdeGetDoc(T)(T value)
     if (is(T == enum))
 {
-    import std.traits: EnumMembers;
+    foreach (i, member; EnumMembers!T)
+    {{
+        alias all = __traits(getAttributes, EnumMembers!T[i]);
+    }}
     final switch (value)
     {
         foreach (i, member; EnumMembers!T)
         {
             case member:
-            alias all = __traits(getAttributes, EnumMembers!T[i]);
-            static if (hasUDA!(EnumMembers!T[i], reflectDoc))
-                return getUDA!(EnumMembers!T[i], reflectDoc);
-            else
-                return reflectDoc.init;
+                return serdeGetDoc!(EnumMembers!T[i]);
         }
     }
 }
@@ -220,14 +209,14 @@ unittest
     {
         @reflectDoc("alpha")
         a,
-        @reflectDoc("beta", reflectTestDoc("test line"))
+        @reflectDoc("beta")
         b,
         c,
     }
 
-    static assert(serdeGetDoc(E.a) == reflectDoc("alpha"));
-    static assert(serdeGetDoc(E.b) == reflectDoc("beta", reflectTestDoc("test line")));
-    static assert(serdeGetDoc(E.c).text is null);
+    static assert(serdeGetDoc(E.a) == "alpha");
+    static assert(serdeGetDoc(E.b) == "beta");
+    static assert(serdeGetDoc(E.c) is null);
 
     struct S
     {
@@ -235,7 +224,73 @@ unittest
         int a;
     }
 
-    static assert(serdeGetDoc!(S.a) == reflectDoc("alpha"));
+    static assert(serdeGetDoc!(S.a) == "alpha");
+}
+
+/++
+Attribute for extern unit-test.
++/
+struct reflectUnittest(string target)
+{
+    ///
+    string text;
+}
+
+/++
++/
+template serdeGetUnittest(string target, alias symbol)
+{
+    static if (hasUDA!(symbol, reflectUnittest!target))
+        enum string serdeGetUnittest = getUDA!(symbol, reflectUnittest).text;
+    else
+        enum string serdeGetUnittest = null;
+}
+
+/// ditto
+template serdeGetUnittest(string target)
+{
+    string serdeGetUnittest(T)(T value)
+        if (is(T == enum))
+    {
+        foreach (i, member; EnumMembers!T)
+        {{
+            alias all = __traits(getAttributes, EnumMembers!T[i]);
+        }}
+        final switch (value)
+        {
+            foreach (i, member; EnumMembers!T)
+            {
+                case member:
+                    return .serdeGetUnittest!(target, EnumMembers!T[i]);
+            }
+        }
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    enum E
+    {
+        @reflectUnittest!"c++"("assert(E::a == 0);")
+        a,
+        @reflectUnittest!"c++"("assert(E::b == 1);")
+        b,
+        c,
+    }
+
+    static assert(serdeGetUnittest!"c++"(E.a) == "assert(E::a == 0);");
+    static assert(serdeGetUnittest!"c++"(E.b) == "assert(E::b == 1);");
+    static assert(serdeGetUnittest!"c++"(E.c) is null);
+
+    struct S
+    {
+        @reflectUnittest!"c++"("alpha")
+        int a;
+    }
+
+    static assert(serdeGetUnittest!("c++", S.a) == "alpha");
 }
 
 /++

From 79c8bd5cbdfc42c012ebd2da56cbdb9e69184d78 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Thu, 13 Aug 2020 00:53:09 +0700
Subject: [PATCH 069/573] Generic Interpolant (#316)

* add mir.interpolate.spline

* add meson build
---
 meson.build                      |   1 +
 source/mir/interpolate/generic.d | 193 +++++++++++++++++++++++++++++++
 source/mir/interpolate/package.d |   1 +
 3 files changed, 195 insertions(+)
 create mode 100644 source/mir/interpolate/generic.d

diff --git a/meson.build b/meson.build
index bb6f8423..97d6963a 100644
--- a/meson.build
+++ b/meson.build
@@ -28,6 +28,7 @@ sources_list = [
     'mir/graph/package',
     'mir/graph/tarjan',
     'mir/interpolate/constant',
+    'mir/interpolate/generic',
     'mir/interpolate/linear',
     'mir/interpolate/package',
     'mir/interpolate/polynomial',
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
new file mode 100644
index 00000000..de712b1b
--- /dev/null
+++ b/source/mir/interpolate/generic.d
@@ -0,0 +1,193 @@
+/++
+$(H2 Generic Piecewise Interpolant)
+
+See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
+
+License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Copyright: Copyright © 2020, Kaleidic Associates Advisory Limited and Symmetry Investments
+Authors:   Ilya Yaroshenko
+
+Macros:
+SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
++/
+module mir.interpolate.generic;
+
+@optmath:
+
+///
+version(mir_test)
+@safe pure @nogc unittest
+{
+    import mir.ndslice;
+    import mir.math.common: approxEqual;
+
+    struct S
+    {
+        int value;
+
+        this()(int value)
+        {
+            this.value = value;
+        }
+
+        int opCall(uint derivative : 0, X)(int x0, int x1, X x) const
+        {
+            return value;
+        }
+
+        enum uint derivativeOrder = 0;
+    }
+
+    static immutable x = [0, 1, 2, 3];
+    static immutable y = [S(10), S(20), S(30)];
+
+    auto interpolant = generic(x.rcslice, y.rcslice!(const S));
+
+    assert(interpolant(-1) == 10);
+    assert(interpolant(0) == 10);
+    assert(interpolant(0.5) == 10);
+
+    assert(interpolant(1) == 20);
+    assert(interpolant(1.5) == 20);
+
+    assert(interpolant(2) == 30);
+    assert(interpolant(3) == 30);
+    assert(interpolant(3.4) == 30);
+    assert(interpolant(3) == 30);
+    assert(interpolant(4) == 30);
+}
+
+
+import core.lifetime: move; 
+import mir.internal.utility;
+import mir.functional;
+import mir.interpolate;
+import mir.math.common: optmath;
+import mir.ndslice.slice;
+import mir.primitives;
+import mir.rc.array;
+import mir.utility: min, max;
+import std.meta: AliasSeq, staticMap;
+import std.traits;
+public import mir.interpolate: atInterval;
+
+/++
+Constructs multivariate generic interpolant with nodes on rectilinear grid.
+
+Params:
+    grid = `x` values for interpolant
+    values = `f(x)` values for interpolant
+
+Constraints:
+    `grid`, `values` must have the same length >= 1
+
+Returns: $(LREF Generic)
++/
+Generic!(X, F) generic(X, F)
+    (Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) values)
+{
+    return typeof(return)(forward!grid, values.move);
+}
+
+/++
+Multivariate generic interpolant with nodes on rectilinear grid.
++/
+struct Generic(X, F)
+{
+@optmath:
+
+    /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
+    Slice!(RCI!(const F)) _data;
+    /// Grid iterators. $(RED For internal use.)
+    RCI!(immutable X) _grid;
+
+extern(D):
+
+    /++
+    +/
+    this(Slice!(RCI!(immutable X)) grid, Slice!(RCI!(const F)) data) @safe @nogc
+    {
+        import core.lifetime: move;
+        enum  msg_min =  "generic interpolant: minimal allowed length for the grid equals 2.";
+        enum  msg_eq =  "generic interpolant: X length and Y values length + 1 should be equal.";
+        version(D_Exceptions)
+        {
+            static immutable exc_min = new Exception(msg_min);
+            static immutable exc_eq = new Exception(msg_eq);
+        }
+        if (grid.length < 2)
+        {
+            version(D_Exceptions) throw exc_min;
+            else assert(0, msg_min);
+        }
+        if (grid.length != data._lengths[0] + 1)
+        {
+            version(D_Exceptions) throw exc_eq;
+            else assert(0, msg_eq);
+        }
+        _grid = move(grid._iterator);
+        _data = move(data);
+    }
+
+@trusted:
+
+    ///
+    Generic lightConst()() const @property { return *cast(Generic*)&this; }
+
+    ///
+    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property
+        if (dimension == 0)
+    {
+        return _grid.lightConst.sliced(_data._lengths[0]);
+    }
+
+    ///
+    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+        if (dimension == 0)
+    {
+        return _grid._iterator[0 .. _data._lengths[0]];
+    }
+
+    /++
+    Returns: intervals count.
+    +/
+    size_t intervalCount(size_t dimension = 0)() scope const @property
+        if (dimension == 0)
+    {
+        assert(_data._lengths[0] > 1);
+        return _data._lengths[0] - 0;
+    }
+
+    ///
+    size_t[1] gridShape()() scope const @property
+    {
+        return _data.shape;
+    }
+
+    ///
+    enum uint derivativeOrder = F.derivativeOrder;
+
+    ///
+    template opCall(uint derivative = 0)
+        if (derivative == 0)
+    {
+        /++
+        `(x)` operator.
+        Complexity:
+            `O(log(grid.length))`
+        +/
+        auto opCall(X)(in X x) const
+        {
+            return opCall!(X)(RefTuple!(size_t, X)(this.findInterval(x), x));
+        }
+
+        ///
+        auto opCall(X)(RefTuple!(size_t, X) tuple) const
+        {
+            X x = tuple[1];
+            size_t idx = tuple[0];
+            return _data[idx].opCall!derivative(_grid[idx], _grid[idx + 1], x);
+        }
+    }
+}
diff --git a/source/mir/interpolate/package.d b/source/mir/interpolate/package.d
index 1f580494..ca7a4832 100644
--- a/source/mir/interpolate/package.d
+++ b/source/mir/interpolate/package.d
@@ -4,6 +4,7 @@ $(H1 Interpolation Algorithms)
 $(BOOKTABLE $(H2 Interpolation modules),
 $(TR $(TH Module) $(TH Interpolation kind))
 $(T2M constant, Constant Interpolant)
+$(T2M generic, Generic Piecewise Interpolant)
 $(T2M linear, Linear Interpolant)
 $(T2M polynomial, Lagrange Barycentric Interpolant)
 $(T2M spline, Piecewise Cubic Hermite Interpolant Spline: C2 (with contiguous second derivative), cardinal, monotone (aka PCHIP), double-quadratic, Akima)

From a9ab48c769579941a4e42c0e7d026cba47b7ab11 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 13 Aug 2020 14:19:19 +0700
Subject: [PATCH 070/573] fixup reflectDoc and udas

---
 source/mir/interpolate/generic.d |  5 +++--
 source/mir/reflection.d          | 24 ++++++++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index de712b1b..5aa72505 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -22,7 +22,7 @@ version(mir_test)
     import mir.ndslice;
     import mir.math.common: approxEqual;
 
-    struct S
+    struct PieceInterpolant
     {
         int value;
 
@@ -39,7 +39,8 @@ version(mir_test)
         enum uint derivativeOrder = 0;
     }
 
-    static immutable x = [0, 1, 2, 3];
+    alias S = PieceInterpolant;
+    static immutable x = [0, 1, 2, 3]; // can be also an array of floating point numbers
     static immutable y = [S(10), S(20), S(30)];
 
     auto interpolant = generic(x.rcslice, y.rcslice!(const S));
diff --git a/source/mir/reflection.d b/source/mir/reflection.d
index 7777009a..8da9a170 100644
--- a/source/mir/reflection.d
+++ b/source/mir/reflection.d
@@ -171,6 +171,18 @@ struct reflectDoc
 {
     ///
     string text;
+
+@safe pure nothrow @nogc:
+
+    this(string text)
+    {
+        this.text = text;
+    }
+
+    this(const typeof(this) other)
+    {
+        this.text = other.text;
+    }
 }
 
 /++
@@ -234,6 +246,18 @@ struct reflectUnittest(string target)
 {
     ///
     string text;
+
+@safe pure nothrow @nogc:
+
+    this(string text)
+    {
+        this.text = text;
+    }
+
+    this(const typeof(this) other)
+    {
+        this.text = other.text;
+    }
 }
 
 /++

From 87663fc0ab54b27d3a0b45fe1886f5ae2440a818 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 13 Aug 2020 16:16:07 +0700
Subject: [PATCH 071/573] extend mir.reflection

---
 source/mir/reflection.d | 116 ++++++++++++++++++++++++++++------------
 1 file changed, 82 insertions(+), 34 deletions(-)

diff --git a/source/mir/reflection.d b/source/mir/reflection.d
index 8da9a170..676c9ec1 100644
--- a/source/mir/reflection.d
+++ b/source/mir/reflection.d
@@ -48,11 +48,10 @@ template ReflectName(string target)
 /// ditto
 template reflectName(string target = null, Args...)
 {
-    ///
-    alias TargetName = ReflectName!target;
     ///
     auto reflectName(string name)
     {
+        alias TargetName = ReflectName!target;
         return TargetName!Args(name);
     }
 }
@@ -110,11 +109,10 @@ template ReflectMeta(string target)
 /// ditto
 template reflectMeta(string target = null)
 {
-    ///
-    alias TargetMeta = ReflectMeta!target;
     ///
     auto reflectMeta(Args...)(Args args)
     {
+        alias TargetMeta = ReflectMeta!target;
         return TargetMeta!Args(args);
     }
 }
@@ -164,39 +162,79 @@ unittest
     static assert(hasUDA!(S.a, reflectIgnore!"c++"));
 }
 
-/++
-Attribute for documentation.
-+/
-struct reflectDoc
+/// Attribute to rename methods, types and functions
+struct ReflectDoc(string target)
 {
     ///
     string text;
+    ///
+    reflectUnittest!target test;
 
-@safe pure nothrow @nogc:
+    ///
+    @safe pure nothrow @nogc
+    this(string docstr)
+    {
+        this.text = text;
+    }
 
-    this(string text)
+    ///
+    @safe pure nothrow @nogc
+    this(string text, reflectUnittest!target test)
     {
         this.text = text;
+        this.test = test;
     }
 
-    this(const typeof(this) other)
+    ///
+    void toString(W)(scope ref W w) scope const
     {
-        this.text = other.text;
+        w.put(cast()this.text);
+
+        if (this.test.text.length)
+        {
+            w.put("\nExample usage:\n");
+            w.put(cast()this.test.text);
+        }
+    }
+
+    ///
+    @safe pure nothrow
+    string toString()() scope const
+    {
+        return this.text ~ "\nExample usage:\n" ~ this.test.text;
     }
 }
 
 /++
+Attribute for documentation.
 +/
-template serdeGetDoc(alias symbol)
+template reflectDoc(string target = null)
 {
-    static if (hasUDA!(symbol, reflectDoc))
-        enum string serdeGetDoc = getUDA!(symbol, reflectDoc).text;
+    ///
+    ReflectDoc!target reflectDoc(string text)
+    {
+        return ReflectDoc!target(text);
+    }
+
+    ///
+    ReflectDoc!target reflectDoc(string text, reflectUnittest!target test)
+    {
+        return ReflectDoc!target(text, test);
+    }
+}
+
+/++
++/
+template reflectGetDocs(string target, alias symbol)
+{
+    static if (hasUDA!(symbol, ReflectDoc!target))
+        static immutable(ReflectDoc!target[]) reflectGetDocs = [getUDAs!(symbol, ReflectDoc!target)];
     else
-        enum string serdeGetDoc = null;
+        static immutable(ReflectDoc!target[]) reflectGetDocs = null;
 }
 
 /// ditto
-string serdeGetDoc(T)(T value)
+immutable(ReflectDoc!target)[] reflectGetDocs(string target, T)(T value)
     if (is(T == enum))
 {
     foreach (i, member; EnumMembers!T)
@@ -206,10 +244,10 @@ string serdeGetDoc(T)(T value)
     final switch (value)
     {
         foreach (i, member; EnumMembers!T)
-        {
+        {{
             case member:
-                return serdeGetDoc!(EnumMembers!T[i]);
-        }
+                return .reflectGetDocs!(target, EnumMembers!T[i]);
+        }}
     }
 }
 
@@ -221,22 +259,32 @@ unittest
     {
         @reflectDoc("alpha")
         a,
+        @reflectDoc!"C#"("Beta", reflectUnittest!"C#"("some c# code"))
         @reflectDoc("beta")
         b,
         c,
     }
 
-    static assert(serdeGetDoc(E.a) == "alpha");
-    static assert(serdeGetDoc(E.b) == "beta");
-    static assert(serdeGetDoc(E.c) is null);
+    alias Doc = ReflectDoc!null;
+    alias CSDoc = ReflectDoc!"C#";
+
+    static assert(reflectGetDocs!null(E.a) == [Doc("alpha")]);
+    static assert(reflectGetDocs!"C#"(E.b) == [CSDoc("Beta", reflectUnittest!"C#"("some c# code"))]);
+    static assert(reflectGetDocs!null(E.b) == [Doc("beta")]);
+    static assert(reflectGetDocs!null(E.c) is null);
 
     struct S
     {
         @reflectDoc("alpha")
+        @reflectDoc!"C#"("Alpha")
         int a;
     }
 
-    static assert(serdeGetDoc!(S.a) == "alpha");
+    static assert(reflectGetDocs!(null, S.a) == [Doc("alpha")]);
+    static assert(reflectGetDocs!("C#", S.a) == [CSDoc("Alpha")]);
+
+    import std.conv: to;
+    static assert(CSDoc("Beta", reflectUnittest!"C#"("some c# code")).to!string == "Beta\nExample usage:\nsome c# code");
 }
 
 /++
@@ -262,18 +310,18 @@ struct reflectUnittest(string target)
 
 /++
 +/
-template serdeGetUnittest(string target, alias symbol)
+template reflectGetUnittest(string target, alias symbol)
 {
     static if (hasUDA!(symbol, reflectUnittest!target))
-        enum string serdeGetUnittest = getUDA!(symbol, reflectUnittest).text;
+        enum string reflectGetUnittest = getUDA!(symbol, reflectUnittest).text;
     else
-        enum string serdeGetUnittest = null;
+        enum string reflectGetUnittest = null;
 }
 
 /// ditto
-template serdeGetUnittest(string target)
+template reflectGetUnittest(string target)
 {
-    string serdeGetUnittest(T)(T value)
+    string reflectGetUnittest(T)(T value)
         if (is(T == enum))
     {
         foreach (i, member; EnumMembers!T)
@@ -285,7 +333,7 @@ template serdeGetUnittest(string target)
             foreach (i, member; EnumMembers!T)
             {
                 case member:
-                    return .serdeGetUnittest!(target, EnumMembers!T[i]);
+                    return .reflectGetUnittest!(target, EnumMembers!T[i]);
             }
         }
     }
@@ -304,9 +352,9 @@ unittest
         c,
     }
 
-    static assert(serdeGetUnittest!"c++"(E.a) == "assert(E::a == 0);");
-    static assert(serdeGetUnittest!"c++"(E.b) == "assert(E::b == 1);");
-    static assert(serdeGetUnittest!"c++"(E.c) is null);
+    static assert(reflectGetUnittest!"c++"(E.a) == "assert(E::a == 0);");
+    static assert(reflectGetUnittest!"c++"(E.b) == "assert(E::b == 1);");
+    static assert(reflectGetUnittest!"c++"(E.c) is null);
 
     struct S
     {
@@ -314,7 +362,7 @@ unittest
         int a;
     }
 
-    static assert(serdeGetUnittest!("c++", S.a) == "alpha");
+    static assert(reflectGetUnittest!("c++", S.a) == "alpha");
 }
 
 /++

From 35b28fa2461ee6bf3757a308bb80b7619a31319b Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 13 Aug 2020 19:58:45 +0700
Subject: [PATCH 072/573] move mir.reflection to mir-core

---
 dub.sdl                 |   2 +-
 index.d                 |   1 -
 meson.build             |   1 -
 source/mir/reflection.d | 773 ----------------------------------------
 4 files changed, 1 insertion(+), 776 deletions(-)
 delete mode 100644 source/mir/reflection.d

diff --git a/dub.sdl b/dub.sdl
index 1deeadf4..e46a06bb 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "Copyright © 2016 - 2019, Ilya Yaroshenko; see also information per file."
 license "BSL-1.0"
 
-dependency "mir-core" version=">=1.1.7"
+dependency "mir-core" version=">=1.1.9"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
diff --git a/index.d b/index.d
index cc4042dd..c0f5236e 100644
--- a/index.d
+++ b/index.d
@@ -20,7 +20,6 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
     $(TR $(TDNW $(MREF mir,range)) $(TD Ranges))
     $(TR $(TDNW $(MREF mir,serde)) $(TD Utilities for serialization libraries ))
-    $(TR $(TDNW $(MREF mir,reflection)) $(TD Compile time reflection utilities ))
     $(LEADINGROW Date and time)
     $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability))
     $(LEADINGROW NDarrays and Algorithms)
diff --git a/meson.build b/meson.build
index 97d6963a..bf24b792 100644
--- a/meson.build
+++ b/meson.build
@@ -64,7 +64,6 @@ sources_list = [
     'mir/rc/package',
     'mir/rc/ptr',
     'mir/rc/slim_ptr',
-    'mir/reflection',
     'mir/serde',
     'mir/series',
     'mir/small_array',
diff --git a/source/mir/reflection.d b/source/mir/reflection.d
deleted file mode 100644
index 676c9ec1..00000000
--- a/source/mir/reflection.d
+++ /dev/null
@@ -1,773 +0,0 @@
-/++
-+/
-module mir.reflection;
-
-import std.meta;
-import std.traits: hasUDA, getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers;
-
-/++
-Match types like `std.typeconst: Nullable`.
-+/
-template isStdNullable(T)
-{
-    import std.traits : hasMember;
-
-    enum bool isStdNullable =
-        hasMember!(T, "isNull") &&
-        hasMember!(T, "get") &&
-        hasMember!(T, "nullify") &&
-        is(typeof(__traits(getMember, T, "isNull")) == bool) &&
-        !is(typeof(__traits(getMember, T, "get")) == void) &&
-        is(typeof(__traits(getMember, T, "nullify")) == void);
-}
-
-///
-unittest
-{
-    import std.typecons;
-    assert(isStdNullable!(Nullable!double));
-}
-
-///  Attribute for deprecated API
-struct reflectDeprecated(string target)
-{
-    string info;
-}
-
-/// Attribute to rename methods, types and functions
-template ReflectName(string target)
-{
-    ///
-    struct ReflectName(Args...)
-    {
-        ///
-        string name;
-    }
-}
-
-/// ditto
-template reflectName(string target = null, Args...)
-{
-    ///
-    auto reflectName(string name)
-    {
-        alias TargetName = ReflectName!target;
-        return TargetName!Args(name);
-    }
-}
-
-///
-version(mir_test)
-unittest
-{
-    enum E { A, B, C }
-
-    struct S
-    {
-        @reflectName("A")
-        int a;
-
-        @reflectName!"c++"("B")
-        int b;
-
-        @reflectName!("C",  double)("cd")
-        @reflectName!("C",  float)("cf")
-        F c(F)()
-        {
-            return b;
-        }
-    }
-
-    import std.traits: hasUDA;
-
-    alias UniName = ReflectName!null;
-    alias CppName = ReflectName!"c++";
-    alias CName = ReflectName!"C";
-
-    static assert(hasUDA!(S.a, UniName!()("A")));
-    static assert(hasUDA!(S.b, CppName!()("B")));
-
-    // static assert(hasUDA!(S.c, ReflectName)); // doesn't work for now
-    static assert(hasUDA!(S.c, CName));
-    static assert(hasUDA!(S.c, CName!double));
-    static assert(hasUDA!(S.c, CName!float));
-    static assert(hasUDA!(S.c, CName!double("cd")));
-    static assert(hasUDA!(S.c, CName!float("cf")));
-}
-
-/// Attribute to rename methods, types and functions
-template ReflectMeta(string target)
-{
-    ///
-    struct ReflectMeta(Args...)
-    {
-        ///
-        Args args;
-    }
-}
-
-/// ditto
-template reflectMeta(string target = null)
-{
-    ///
-    auto reflectMeta(Args...)(Args args)
-    {
-        alias TargetMeta = ReflectMeta!target;
-        return TargetMeta!Args(args);
-    }
-}
-
-///
-version(mir_test)
-unittest
-{
-    enum E { A, B, C }
-
-    struct S
-    {
-        @reflectMeta(E.A)
-        int a;
-        @reflectMeta!"c++"(E.C)
-        int b;
-    }
-
-    import std.traits: hasUDA;
-
-    alias UniMeta = ReflectMeta!null;
-    alias CppMeta = ReflectMeta!"c++";
-
-    static assert(hasUDA!(S.a, UniMeta!E(E.A)));
-    static assert(hasUDA!(S.b, CppMeta!E(E.C)));
-}
-
-/++
-Attribute to ignore a reflection target
-+/
-template reflectIgnore(string target)
-{
-    enum reflectIgnore;
-}
-
-///
-version(mir_test)
-unittest
-{
-    struct S
-    {
-        @reflectIgnore!"c++"
-        int a;
-    }
-
-    import std.traits: hasUDA;
-    static assert(hasUDA!(S.a, reflectIgnore!"c++"));
-}
-
-/// Attribute to rename methods, types and functions
-struct ReflectDoc(string target)
-{
-    ///
-    string text;
-    ///
-    reflectUnittest!target test;
-
-    ///
-    @safe pure nothrow @nogc
-    this(string docstr)
-    {
-        this.text = text;
-    }
-
-    ///
-    @safe pure nothrow @nogc
-    this(string text, reflectUnittest!target test)
-    {
-        this.text = text;
-        this.test = test;
-    }
-
-    ///
-    void toString(W)(scope ref W w) scope const
-    {
-        w.put(cast()this.text);
-
-        if (this.test.text.length)
-        {
-            w.put("\nExample usage:\n");
-            w.put(cast()this.test.text);
-        }
-    }
-
-    ///
-    @safe pure nothrow
-    string toString()() scope const
-    {
-        return this.text ~ "\nExample usage:\n" ~ this.test.text;
-    }
-}
-
-/++
-Attribute for documentation.
-+/
-template reflectDoc(string target = null)
-{
-    ///
-    ReflectDoc!target reflectDoc(string text)
-    {
-        return ReflectDoc!target(text);
-    }
-
-    ///
-    ReflectDoc!target reflectDoc(string text, reflectUnittest!target test)
-    {
-        return ReflectDoc!target(text, test);
-    }
-}
-
-/++
-+/
-template reflectGetDocs(string target, alias symbol)
-{
-    static if (hasUDA!(symbol, ReflectDoc!target))
-        static immutable(ReflectDoc!target[]) reflectGetDocs = [getUDAs!(symbol, ReflectDoc!target)];
-    else
-        static immutable(ReflectDoc!target[]) reflectGetDocs = null;
-}
-
-/// ditto
-immutable(ReflectDoc!target)[] reflectGetDocs(string target, T)(T value)
-    if (is(T == enum))
-{
-    foreach (i, member; EnumMembers!T)
-    {{
-        alias all = __traits(getAttributes, EnumMembers!T[i]);
-    }}
-    final switch (value)
-    {
-        foreach (i, member; EnumMembers!T)
-        {{
-            case member:
-                return .reflectGetDocs!(target, EnumMembers!T[i]);
-        }}
-    }
-}
-
-///
-version(mir_test)
-unittest
-{
-    enum E
-    {
-        @reflectDoc("alpha")
-        a,
-        @reflectDoc!"C#"("Beta", reflectUnittest!"C#"("some c# code"))
-        @reflectDoc("beta")
-        b,
-        c,
-    }
-
-    alias Doc = ReflectDoc!null;
-    alias CSDoc = ReflectDoc!"C#";
-
-    static assert(reflectGetDocs!null(E.a) == [Doc("alpha")]);
-    static assert(reflectGetDocs!"C#"(E.b) == [CSDoc("Beta", reflectUnittest!"C#"("some c# code"))]);
-    static assert(reflectGetDocs!null(E.b) == [Doc("beta")]);
-    static assert(reflectGetDocs!null(E.c) is null);
-
-    struct S
-    {
-        @reflectDoc("alpha")
-        @reflectDoc!"C#"("Alpha")
-        int a;
-    }
-
-    static assert(reflectGetDocs!(null, S.a) == [Doc("alpha")]);
-    static assert(reflectGetDocs!("C#", S.a) == [CSDoc("Alpha")]);
-
-    import std.conv: to;
-    static assert(CSDoc("Beta", reflectUnittest!"C#"("some c# code")).to!string == "Beta\nExample usage:\nsome c# code");
-}
-
-/++
-Attribute for extern unit-test.
-+/
-struct reflectUnittest(string target)
-{
-    ///
-    string text;
-
-@safe pure nothrow @nogc:
-
-    this(string text)
-    {
-        this.text = text;
-    }
-
-    this(const typeof(this) other)
-    {
-        this.text = other.text;
-    }
-}
-
-/++
-+/
-template reflectGetUnittest(string target, alias symbol)
-{
-    static if (hasUDA!(symbol, reflectUnittest!target))
-        enum string reflectGetUnittest = getUDA!(symbol, reflectUnittest).text;
-    else
-        enum string reflectGetUnittest = null;
-}
-
-/// ditto
-template reflectGetUnittest(string target)
-{
-    string reflectGetUnittest(T)(T value)
-        if (is(T == enum))
-    {
-        foreach (i, member; EnumMembers!T)
-        {{
-            alias all = __traits(getAttributes, EnumMembers!T[i]);
-        }}
-        final switch (value)
-        {
-            foreach (i, member; EnumMembers!T)
-            {
-                case member:
-                    return .reflectGetUnittest!(target, EnumMembers!T[i]);
-            }
-        }
-    }
-}
-
-///
-version(mir_test)
-unittest
-{
-    enum E
-    {
-        @reflectUnittest!"c++"("assert(E::a == 0);")
-        a,
-        @reflectUnittest!"c++"("assert(E::b == 1);")
-        b,
-        c,
-    }
-
-    static assert(reflectGetUnittest!"c++"(E.a) == "assert(E::a == 0);");
-    static assert(reflectGetUnittest!"c++"(E.b) == "assert(E::b == 1);");
-    static assert(reflectGetUnittest!"c++"(E.c) is null);
-
-    struct S
-    {
-        @reflectUnittest!"c++"("alpha")
-        int a;
-    }
-
-    static assert(reflectGetUnittest!("c++", S.a) == "alpha");
-}
-
-/++
-Returns: single UDA.
-+/
-template getUDA(alias symbol, alias attribute)
-{
-    private alias all = getUDAs!(symbol, attribute);
-    static if (all.length != 1)
-        static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required");
-    else
-    {
-        static if (is(typeof(all[0])))
-            enum getUDA = all[0];
-        else
-            alias getUDA = all[0];
-    }
-}
-
-/++
-Checks if member is field.
-+/
-enum bool isField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; });
-
-///
-version(mir_test)
-unittest
-{
-    struct D
-    {
-        int gi;
-    }
-
-    struct I
-    {
-        int f;
-
-        D base;
-        alias base this;
-
-        void gi(double ) @property {}
-        void gi(uint ) @property {}
-    }
-
-    struct S
-    {
-        int d;
-
-        I i;
-        alias i this;
-
-        int gm() @property {return 0;}
-        int gc() const @property {return 0;}
-        void gs(int) @property {}
-    }
-
-    static assert(!isField!(S, "gi"));
-    static assert(!isField!(S, "gs"));
-    static assert(!isField!(S, "gc"));
-    static assert(!isField!(S, "gm"));
-    static assert(!isField!(S, "gi"));
-    static assert(isField!(S, "d"));
-    static assert(isField!(S, "f"));
-    static assert(isField!(S, "i"));
-}
-
-///  with classes
-version(mir_test)
-unittest
-{
-    class I
-    {
-        int f;
-
-        void gi(double ) @property {}
-        void gi(uint ) @property {}
-    }
-
-    class S
-    {
-        int d;
-
-        I i;
-        alias i this;
-
-        int gm() @property {return 0;}
-        int gc() const @property {return 0;}
-        void gs(int) @property {}
-    }
-
-    static assert(!isField!(S, "gi"));
-    static assert(!isField!(S, "gs"));
-    static assert(!isField!(S, "gc"));
-    static assert(!isField!(S, "gm"));
-    static assert(isField!(S, "d"));
-    static assert(isField!(S, "f"));
-    static assert(isField!(S, "i"));
-}
-
-/++
-Checks if member is property.
-+/
-template isProperty(T, string member)
-{
-    T* aggregate;
-
-    static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
-    {
-        static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
-        {
-            enum bool isProperty = isPropertyImpl!(__traits(getMember, *aggregate, member));
-        }
-        else
-        {
-            enum bool isProperty = false;
-        }
-    }
-    else
-        enum bool isProperty = false;
-}
-
-///
-version(mir_test)
-unittest
-{
-    struct D
-    {
-        int y;
-
-        void gf(double ) @property {}
-        void gf(uint ) @property {}
-    }
-
-    struct I
-    {
-        int f;
-
-        D base;
-        alias base this;
-
-        void gi(double ) @property {}
-        void gi(uint ) @property {}
-    }
-
-    struct S
-    {
-        int d;
-
-        I i;
-        alias i this;
-
-        int gm() @property {return 0;}
-        int gc() const @property {return 0;}
-        void gs(int) @property {}
-    }
-
-    static assert(isProperty!(S, "gf"));
-    static assert(isProperty!(S, "gi"));
-    static assert(isProperty!(S, "gs"));
-    static assert(isProperty!(S, "gc"));
-    static assert(isProperty!(S, "gm"));
-    static assert(!isProperty!(S, "d"));
-    static assert(!isProperty!(S, "f"));
-    static assert(!isProperty!(S, "y"));
-}
-
-/++
-Returns: list of the setter properties.
-
-Note: The implementation ignores templates.
-+/
-template getSetters(T, string member)
-{
-    alias getSetters = Filter!(hasSingleArgument, Filter!(isPropertyImpl, __traits(getOverloads, T, member)));
-}
-
-///
-version(mir_test)
-unittest
-{
-    struct I
-    {
-        int f;
-
-        void gi(double ) @property {}
-        void gi(uint ) @property {}
-    }
-
-    struct S
-    {
-        int d;
-
-        I i;
-        alias i this;
-
-        int gm() @property {return 0;}
-        int gc() const @property {return 0;}
-        void gs(int) @property {}
-    }
-
-    static assert(getSetters!(S, "gi").length == 2);
-    static assert(getSetters!(S, "gs").length == 1);
-    static assert(getSetters!(S, "gc").length == 0);
-    static assert(getSetters!(S, "gm").length == 0);
-    static assert(getSetters!(S, "d").length == 0);
-    static assert(getSetters!(S, "f").length == 0);
-}
-
-/++
-Returns: list of the serializable (public getters) members.
-+/
-enum string[] SerializableMembers(T) = [Filter!(ApplyLeft!(Serializable, T), FieldsAndProperties!T)];
-
-///
-version(mir_test)
-unittest
-{
-    struct D
-    {
-        int y;
-
-        int gf() @property {return 0;}
-    }
-
-    struct I
-    {
-        int f;
-
-        D base;
-        alias base this;
-
-        int gi() @property {return 0;}
-    }
-
-    struct S
-    {
-        int d;
-
-        package int p;
-
-        int gm() @property {return 0;}
-
-        private int q;
-
-        I i;
-        alias i this;
-
-        int gc() const @property {return 0;}
-        void gs(int) @property {}
-    }
-
-    static assert(SerializableMembers!S == ["y", "gf", "f", "gi", "d", "gm", "gc"]);
-    static assert(SerializableMembers!(const S) == ["y", "f", "d", "gc"]);
-}
-
-/++
-Returns: list of the deserializable (public setters) members.
-+/
-enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), FieldsAndProperties!T)];
-
-///
-version(mir_test)
-unittest
-{
-    struct I
-    {
-        int f;
-        void ga(int) @property {}
-    }
-
-    struct S
-    {
-        int d;
-        package int p;
-
-        int gm() @property {return 0;}
-        void gm(int) @property {}
-
-        private int q;
-
-        I i;
-        alias i this;
-
-
-        void gc(int, int) @property {}
-        void gc(int) @property {}
-    }
-
-    S s;
-    // s.gc(0);
-
-    static assert (DeserializableMembers!S == ["f", "ga", "d", "gm", "gc"]);
-    static assert (DeserializableMembers!(const S) == []);
-}
-
-// This trait defines what members should be serialized -
-// public members that are either readable and writable or getter properties
-private template Serializable(T, string member)
-{
-    static if (!isPublic!(T, member))
-        enum Serializable = false;
-    else
-        enum Serializable = isReadable!(T, member); // any readable is good
-}
-
-private enum bool hasSingleArgument(alias fun) = Parameters!fun.length == 1;
-private enum bool hasZeroArguments(alias fun) = Parameters!fun.length == 0;
-
-// This trait defines what members should be serialized -
-// public members that are either readable and writable or setter properties
-private template Deserializable(T, string member)
-{
-    static if (!isPublic!(T, member))
-        enum Deserializable = false;
-    else
-    static if (isReadableAndWritable!(T, member))
-        enum Deserializable = true;
-    else
-    static if (getSetters!(T, member).length == 1)
-        enum Deserializable =  is(typeof((ref T val){ __traits(getMember, val, member) = Parameters!(getSetters!(T, member)[0])[0].init; }));
-    else
-        enum Deserializable = false;
-}
-
-private enum FieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(FieldsAndPropertiesImpl!T)));
-
-private template FieldsAndPropertiesImpl(T)
-{
-    alias isProperty = ApplyLeft!(.isProperty, T);
-    alias isField = ApplyLeft!(.isField, T);
-    static if (__traits(getAliasThis, T).length)
-    {
-        T* aggregate;
-        alias baseMembers = FieldsAndPropertiesImpl!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))));
-        alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
-        alias FieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isField, members), Filter!(isProperty, members));
-        
-    }
-    else
-    {
-        alias members = __traits(allMembers, T);
-        alias FieldsAndPropertiesImpl = AliasSeq!(Filter!(isField, members), Filter!(isProperty, members));
-    }
-}
-
-// check if the member is readable
-private template isReadable(T, string member)
-{
-    T* aggregate;
-    enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, *aggregate, member)); });
-}
-
-// check if the member is readable/writeble?
-private template isReadableAndWritable(T, string member)
-{
-    T* aggregate;
-    enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, *aggregate, member) = __traits(getMember, *aggregate, member));
-}
-
-private template isPublic(T, string member)
-{
-    T* aggregate;
-    enum bool isPublic = !__traits(getProtection, __traits(getMember, *aggregate, member)).privateOrPackage;
-}
-
-// check if the member is property
-private template isSetter(T, string member)
-{
-    T* aggregate;
-    static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
-    {
-        static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
-        {
-            enum bool isSetter = getSetters!(T, member).length > 0;;
-        }
-        else
-        {
-            enum bool isSetter = false;
-        }
-    }
-    else
-        enum bool isSetter = false;
-}
-
-private template isGetter(T, string member)
-{
-    T* aggregate;
-    static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
-    {
-        static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
-        {
-            enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member))).length == 1;
-        }
-        else
-        {
-            enum bool isGetter = false;
-        }
-    }
-    else
-        enum bool isGetter = false;
-}
-
-private enum bool isPropertyImpl(alias member) = (functionAttributes!member & FunctionAttribute.property) != 0;
-
-private bool privateOrPackage()(string protection)
-{
-    return protection == "private" || protection == "package";
-}

From af3875dcfc2adf799982ebccfcae79ad52b7db22 Mon Sep 17 00:00:00 2001
From: Mikaela Allan <me@mikaela.ca>
Date: Wed, 19 Aug 2020 18:49:11 -0700
Subject: [PATCH 073/573] add required types for C++ iterator traits (#317)

Co-authored-by: Mikaela Allan <mallan@symmetryinvestments.com>
---
 include/mir/series.h | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/include/mir/series.h b/include/mir/series.h
index 59e8cbec..c7e39ddf 100644
--- a/include/mir/series.h
+++ b/include/mir/series.h
@@ -2,6 +2,7 @@
 
 #define MIR_SERIES
 
+#include <iterator>
 #include <map>
 #include <stdexcept>
 #include "mir/ndslice.h"
@@ -261,6 +262,12 @@ struct mir_series
 
     struct ThisIterator
     {
+        using value_type = Data;
+        using difference_type = mir_ptrdiff_t;
+        using reference = Observation;
+        using pointer = void;
+        using iterator_category = std::input_iterator_tag;
+
         mir_size_t _index = 0;
         mir_series _series;
         ThisIterator& operator++() noexcept { ++_index; return *this;}
@@ -272,7 +279,7 @@ struct mir_series
         bool operator>=(const ThisIterator& rhs) const noexcept { return _index >= rhs._index; }
         bool operator<=(const ThisIterator& rhs) const noexcept { return _index <= rhs._index; }
 
-        Observation operator*() noexcept
+        reference operator*() noexcept
         {
             static_assert(kind == mir_slice_kind::contiguous && N == 1, "The method is defined only for 1-dimensional slice.");
             return _series[_index];

From 99977c00809a1fd57f39a894032341f8fed5f4a8 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 30 Aug 2020 11:39:28 +0700
Subject: [PATCH 074/573] rename indexes to indices

---
 source/mir/algorithm/iteration.d   | 20 +++----
 source/mir/graph/tarjan.d          |  2 +-
 source/mir/interpolate/constant.d  |  8 +--
 source/mir/interpolate/linear.d    | 10 ++--
 source/mir/interpolate/spline.d    | 10 ++--
 source/mir/ndslice/chunks.d        |  6 +-
 source/mir/ndslice/concatenation.d | 12 ++--
 source/mir/ndslice/dynamic.d       | 10 ++--
 source/mir/ndslice/field.d         |  8 +--
 source/mir/ndslice/fuse.d          |  4 +-
 source/mir/ndslice/iterator.d      | 62 ++++++++++----------
 source/mir/ndslice/mutation.d      | 10 ++--
 source/mir/ndslice/ndfield.d       | 40 ++++++-------
 source/mir/ndslice/package.d       |  2 +-
 source/mir/ndslice/slice.d         | 92 +++++++++++++++---------------
 source/mir/ndslice/sorting.d       |  2 +-
 source/mir/ndslice/topology.d      | 48 ++++++++--------
 17 files changed, 173 insertions(+), 173 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 6684e543..7bffe740 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -22,8 +22,8 @@ $(T2 isSymmetric, Checks if the matrix is symmetric.)
 $(T2 maxIndex, Finds index of the maximum.)
 $(T2 maxPos, Finds backward index of the maximum.)
 $(T2 minIndex, Finds index of the minimum.)
-$(T2 minmaxIndex, Finds indexes of the minimum and the maximum.)
-$(T2 minmaxPos, Finds backward indexes of the minimum and the maximum.)
+$(T2 minmaxIndex, Finds indices of the minimum and the maximum.)
+$(T2 minmaxPos, Finds backward indices of the minimum and the maximum.)
 $(T2 minPos, Finds backward index of the minimum.)
 $(T2 nBitsToCount, Сount bits until set bit count is reached.)
 $(T2 reduce, Accumulates all elements.)
@@ -1415,8 +1415,8 @@ unittest
 }
 
 /++
-Finds a backward indexes such that
-`slice[indexes[0]]` is minimal and `slice[indexes[1]]` is maximal elements in the slice.
+Finds a backward indices such that
+`slice[indices[0]]` is minimal and `slice[indices[1]]` is maximal elements in the slice.
 
 Params:
     pred = A predicate.
@@ -1474,11 +1474,11 @@ unittest
         -3, -2, 7, 8,
         ].sliced(3, 4);
 
-    auto indexes = s.minmaxIndex;
+    auto indices = s.minmaxIndex;
 
-    assert(indexes == [[1, 1], [2, 3]]);
-    assert(s[indexes[0]] == -4);
-    assert(s[indexes[1]] ==  8);
+    assert(indices == [[1, 1], [2, 3]]);
+    assert(s[indices[0]] == -4);
+    assert(s[indices[1]] ==  8);
 }
 
 /++
@@ -1728,7 +1728,7 @@ template findIndex(alias pred)
         slices = One or more slices.
     Returns:
         Multidimensional index such that the predicate is true.
-        Index equals `size_t.max`, if the predicate evaluates `false` for all indexes.
+        Index equals `size_t.max`, if the predicate evaluates `false` for all indices.
     Constraints:
         All slices must have the same shape.
     +/
@@ -1824,7 +1824,7 @@ template find(alias pred)
         slices = One or more slices.
     Returns:
         Multidimensional backward index such that the predicate is true.
-        Backward index equals zeros, if the predicate evaluates `false` for all indexes.
+        Backward index equals zeros, if the predicate evaluates `false` for all indices.
     Constraints:
         All slices must have the same shape.
     +/
diff --git a/source/mir/graph/tarjan.d b/source/mir/graph/tarjan.d
index 43e0e668..53e707a1 100644
--- a/source/mir/graph/tarjan.d
+++ b/source/mir/graph/tarjan.d
@@ -35,7 +35,7 @@ Params:
     RC = nogc mode, refcounted output
     graph = components (ndslice) sorted in the direction of traversal of the graph. Each component is an array of indeces.
 Returns:
-    components (ndslice of arrays of indexes)
+    components (ndslice of arrays of indices)
 
 Note:
     The implementation returns components sorted in the direction of traversal of the graph.
diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 9be38ce3..1521fbd6 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -161,15 +161,15 @@ extern(D):
         auto opCall(X...)(in X xs) scope const @trusted
             if (X.length == N)
         {
-            size_t[N] indexes;
+            size_t[N] indices;
             foreach(i; Iota!N)
             {
                 static if (isInterval!(typeof(xs[i])))
-                    indexes[i] = xs[i][1];
+                    indices[i] = xs[i][1];
                 else
-                    indexes[i] = _data._lengths[i] > 1 ? this.findInterval!i(xs[i]) : 0;
+                    indices[i] = _data._lengths[i] > 1 ? this.findInterval!i(xs[i]) : 0;
             }
-            return _data[indexes];
+            return _data[indices];
         }
     }
 }
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 15a72371..07987687 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -265,7 +265,7 @@ struct Linear(F, size_t N = 1, X = F)
             import mir.ndslice.topology: iota;
             alias Kernel = AliasCall!(LinearKernel!F, "opCall", derivative);
 
-            size_t[N] indexes;
+            size_t[N] indices;
             Kernel[N] kernels;
 
             enum rp2d = derivative;
@@ -274,15 +274,15 @@ struct Linear(F, size_t N = 1, X = F)
             {
                 static if (isInterval!(typeof(xs[i])))
                 {
-                    indexes[i] = xs[i][1];
+                    indices[i] = xs[i][1];
                     auto x = xs[i][0];
                 }
                 else
                 { 
                     alias x = xs[i];
-                    indexes[i] = this.findInterval!i(x);
+                    indices[i] = this.findInterval!i(x);
                 }
-                kernels[i] = LinearKernel!F(_grid[i][indexes[i]], _grid[i][indexes[i] + 1], x);
+                kernels[i] = LinearKernel!F(_grid[i][indices[i]], _grid[i][indices[i] + 1], x);
             }
 
             align(64) F[2 ^^ N][derivative + 1] local;
@@ -297,7 +297,7 @@ struct Linear(F, size_t N = 1, X = F)
                 }
                 else
                 {
-                    from += strides[i] * indexes[i];
+                    from += strides[i] * indices[i];
                     load!(i - 1)(from, to);
                     from += strides[i];
                     enum s = 2 ^^ (N - 1 - i);
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 4bd68829..d9ee411c 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -805,22 +805,22 @@ struct Spline(F, size_t N = 1, X = F)
             alias Kernel = AliasCall!(SplineKernel!F, "opCall", derivative);
             enum rp2d = derivative == 3 ? 2 : derivative;
 
-            size_t[N] indexes;
+            size_t[N] indices;
             Kernel[N] kernels;
 
             foreach(i; Iota!N)
             {
                 static if (isInterval!(typeof(xs[i])))
                 {
-                    indexes[i] = xs[i][1];
+                    indices[i] = xs[i][1];
                     auto x = xs[i][0];
                 }
                 else
                 {
                     alias x = xs[i];
-                    indexes[i] = this.findInterval!i(x);
+                    indices[i] = this.findInterval!i(x);
                 }
-                kernels[i] = SplineKernel!F(_grid[i][indexes[i]], _grid[i][indexes[i] + 1], x);
+                kernels[i] = SplineKernel!F(_grid[i][indices[i]], _grid[i][indices[i] + 1], x);
             }
 
             align(64) F[2 ^^ N * 2 ^^ N][2] local;
@@ -836,7 +836,7 @@ struct Spline(F, size_t N = 1, X = F)
                 }
                 else
                 {
-                    from += strides[i] * indexes[i];
+                    from += strides[i] * indices[i];
                     load!(i - 1)(from, to);
                     from += strides[i];
                     enum s = 2 ^^ (N - 1 - i);
diff --git a/source/mir/ndslice/chunks.d b/source/mir/ndslice/chunks.d
index a33198b3..0dd27966 100644
--- a/source/mir/ndslice/chunks.d
+++ b/source/mir/ndslice/chunks.d
@@ -82,7 +82,7 @@ Chunks!([0], Iterator, N, kind) chunks(Iterator, size_t N, SliceKind kind)(Slice
     // 0 1 2 | 3 4 5 | 6 7 8 | 9 10
     auto ch = sl.chunks(3);
 
-    static assert(isChunks!(typeof(ch)) == [0]); // isChunks returns dimension indexes
+    static assert(isChunks!(typeof(ch)) == [0]); // isChunks returns dimension indices
 
     assert(ch.length == 4);
     assert(ch.shape == cast(size_t[1])[4]);
@@ -160,7 +160,7 @@ version(mir_test) unittest
 
     assert (ch[$ - 1, $ - 1] == [[98, 99], [108, 109]]);
 
-    static assert(isChunks!(typeof(ch)) == [1, 0]); // isChunks returns dimension indexes
+    static assert(isChunks!(typeof(ch)) == [1, 0]); // isChunks returns dimension indices
 
     assert(ch.length == 3);
     assert(ch.length!1 == 4);
@@ -554,7 +554,7 @@ struct ChunksDollar()
 /++
 Checks if T is $(LREF Chunks) type.
 Returns:
-    array of dimension indexes.
+    array of dimension indices.
 +/
 template isChunks(T)
 {
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index f87bb2fa..82d612a1 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -355,17 +355,17 @@ struct Concatenation(size_t dim, Slices...)
     }
 
     /// Simplest multidimensional random access primitive
-    auto opIndex()(size_t[N] indexes...)
+    auto opIndex()(size_t[N] indices...)
     {
         foreach(i, ref slice; _slices[0 .. $-1])
         {
-            ptrdiff_t diff = indexes[dim] - slice.length!dim;
+            ptrdiff_t diff = indices[dim] - slice.length!dim;
             if (diff < 0)
-                return slice[indexes];
-            indexes[dim] = diff;
+                return slice[indices];
+            indices[dim] = diff;
         }
-        assert(indexes[dim] < _slices[$-1].length!dim);
-        return _slices[$-1][indexes];
+        assert(indices[dim] < _slices[$-1].length!dim);
+        return _slices[$-1][indices];
     }
 }
 
diff --git a/source/mir/ndslice/dynamic.d b/source/mir/ndslice/dynamic.d
index 9ce0d1bb..0c107ade 100644
--- a/source/mir/ndslice/dynamic.d
+++ b/source/mir/ndslice/dynamic.d
@@ -480,8 +480,8 @@ N-dimensional transpose operator.
 Brings selected dimensions to the first position.
 Params:
     slice = input slice
-    Dimensions = indexes of dimensions to be brought to the first position
-    dimensions = indexes of dimensions to be brought to the first position
+    Dimensions = indices of dimensions to be brought to the first position
+    dimensions = indices of dimensions to be brought to the first position
 Returns:
     n-dimensional slice
 See_also: $(LREF swapped), $(LREF everted)
@@ -654,8 +654,8 @@ Reverses the direction of iteration for selected dimensions.
 
 Params:
     _slice = input slice
-    Dimensions = indexes of dimensions to reverse order of iteration
-    dimensions = indexes of dimensions to reverse order of iteration
+    Dimensions = indices of dimensions to reverse order of iteration
+    dimensions = indices of dimensions to reverse order of iteration
 Returns:
     n-dimensional slice
 +/
@@ -804,7 +804,7 @@ private enum _stridedCode = q{
 Multiplies the stride of the selected dimension by a factor.
 
 Params:
-    Dimensions = indexes of dimensions to be strided
+    Dimensions = indices of dimensions to be strided
     dimension = indexe of a dimension to be strided
     factor = step extension factors
 Returns:
diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d
index 84a8c25d..be31722e 100644
--- a/source/mir/ndslice/field.d
+++ b/source/mir/ndslice/field.d
@@ -696,14 +696,14 @@ struct ndIotaField(size_t N)
     ///
     size_t[N] opIndex()(size_t index) const
     {
-        size_t[N] indexes;
+        size_t[N] indices;
         foreach_reverse (i; Iota!(N - 1))
         {
-            indexes[i + 1] = index % _lengths[i];
+            indices[i + 1] = index % _lengths[i];
             index /= _lengths[i];
         }
-        indexes[0] = index;
-        return indexes;
+        indices[0] = index;
+        return indices;
     }
 }
 
diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index f2f3becc..48e06200 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -28,7 +28,7 @@ Fuses ndrange `r` into GC-allocated ($(LREF fuse)) or RC-allocated ($(LREF rcfus
 Can be used to join rows or columns into a matrix.
 
 Params:
-    Dimensions = (optional) indexes of dimensions to be brought to the first position
+    Dimensions = (optional) indices of dimensions to be brought to the first position
 Returns:
     ndslice
 +/
@@ -134,7 +134,7 @@ Can be used to join rows or columns into a matrix.
 
 Params:
     T = output type of ndslice elements
-    Dimensions = (optional) indexes of dimensions to be brought to the first position
+    Dimensions = (optional) indices of dimensions to be brought to the first position
 Returns:
     ndslice
 +/
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index ada9fc55..0e07190c 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -1266,7 +1266,7 @@ struct IndexIterator(Iterator, Field)
 }
 
 /++
-Iterates chunks in a sliceable using an iterator composed of indexes.
+Iterates chunks in a sliceable using an iterator composed of indices.
 
 Definition:
 ----
@@ -1310,7 +1310,7 @@ struct SubSliceIterator(Iterator, Sliceable)
 }
 
 /++
-Iterates chunks in a sliceable using an iterator composed of indexes stored consequently.
+Iterates chunks in a sliceable using an iterator composed of indices stored consequently.
 
 Definition:
 ----
@@ -1516,7 +1516,7 @@ auto FlattenedIterator__map(Iterator, size_t N, SliceKind kind, alias fun)(Flatt
 {
     import mir.ndslice.topology: map;
     auto slice = it._slice.map!fun;
-    return FlattenedIterator!(TemplateArgsOf!(typeof(slice)))(it._indexes, slice);
+    return FlattenedIterator!(TemplateArgsOf!(typeof(slice)))(it._indices, slice);
 }
 
 version(mir_test) unittest
@@ -1538,20 +1538,20 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
 {
 @optmath:
     ///
-    ptrdiff_t[N] _indexes;
+    ptrdiff_t[N] _indices;
     ///
     Slice!(Iterator, N, kind) _slice;
 
     ///
     auto lightConst()() const @property
     {
-        return FlattenedIterator!(LightConstOf!Iterator, N, kind)(_indexes, _slice.lightConst);
+        return FlattenedIterator!(LightConstOf!Iterator, N, kind)(_indices, _slice.lightConst);
     }
 
     ///
     auto lightImmutable()() immutable @property
     {
-        return FlattenedIterator!(LightImmutableOf!Iterator, N, kind)(_indexes, _slice.lightImmutable);
+        return FlattenedIterator!(LightImmutableOf!Iterator, N, kind)(_indices, _slice.lightImmutable);
     }
 
     ///
@@ -1560,18 +1560,18 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
     private ptrdiff_t getShift()(ptrdiff_t n)
     {
         ptrdiff_t _shift;
-        n += _indexes[$ - 1];
+        n += _indices[$ - 1];
         foreach_reverse (i; Iota!(1, N))
         {
             immutable v = n / ptrdiff_t(_slice._lengths[i]);
             n %= ptrdiff_t(_slice._lengths[i]);
             static if (i == _slice.S)
-                _shift += (n - _indexes[i]);
+                _shift += (n - _indices[i]);
             else
-                _shift += (n - _indexes[i]) * _slice._strides[i];
-            n = _indexes[i - 1] + v;
+                _shift += (n - _indices[i]) * _slice._strides[i];
+            n = _indices[i - 1] + v;
         }
-        _shift += (n - _indexes[0]) * _slice._strides[0];
+        _shift += (n - _indices[0]) * _slice._strides[0];
         return _shift;
     }
 
@@ -1589,28 +1589,28 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
                 mixin(op ~ `_slice._iterator;`);
             else
                 mixin(`_slice._iterator ` ~ op[0] ~ `= _slice._strides[i];`);
-            mixin (op ~ `_indexes[i];`);
+            mixin (op ~ `_indices[i];`);
             static if (i)
             {
                 static if (op == "++")
                 {
-                    if (_indexes[i] < _slice._lengths[i])
+                    if (_indices[i] < _slice._lengths[i])
                         return;
                     static if (i == _slice.S)
                         _slice._iterator -= _slice._lengths[i];
                     else
                         _slice._iterator -= _slice._lengths[i] * _slice._strides[i];
-                    _indexes[i] = 0;
+                    _indices[i] = 0;
                 }
                 else
                 {
-                    if (_indexes[i] >= 0)
+                    if (_indices[i] >= 0)
                         return;
                     static if (i == _slice.S)
                         _slice._iterator += _slice._lengths[i];
                     else
                         _slice._iterator += _slice._lengths[i] * _slice._strides[i];
-                    _indexes[i] = _slice._lengths[i] - 1;
+                    _indices[i] = _slice._lengths[i] - 1;
                 }
             }
         }
@@ -1631,26 +1631,26 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
     void opOpAssign(string op : "+")(ptrdiff_t n) scope
     {
         ptrdiff_t _shift;
-        n += _indexes[$ - 1];
+        n += _indices[$ - 1];
         foreach_reverse (i; Iota!(1, N))
         {
             immutable v = n / ptrdiff_t(_slice._lengths[i]);
             n %= ptrdiff_t(_slice._lengths[i]);
             static if (i == _slice.S)
-                _shift += (n - _indexes[i]);
+                _shift += (n - _indices[i]);
             else
-                _shift += (n - _indexes[i]) * _slice._strides[i];
-            _indexes[i] = n;
-            n = _indexes[i - 1] + v;
+                _shift += (n - _indices[i]) * _slice._strides[i];
+            _indices[i] = n;
+            n = _indices[i - 1] + v;
         }
-        _shift += (n - _indexes[0]) * _slice._strides[0];
-        _indexes[0] = n;
+        _shift += (n - _indices[0]) * _slice._strides[0];
+        _indices[0] = n;
         foreach_reverse (i; Iota!(1, N))
         {
-            if (_indexes[i] >= 0)
+            if (_indices[i] >= 0)
                 break;
-            _indexes[i] += _slice._lengths[i];
-            _indexes[i - 1]--;
+            _indices[i] += _slice._lengths[i];
+            _indices[i - 1]--;
         }
         _slice._iterator += _shift;
     }
@@ -1668,11 +1668,11 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
 
     ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const
     {
-        ptrdiff_t ret = this._indexes[0] - right._indexes[0];
+        ptrdiff_t ret = this._indices[0] - right._indices[0];
         foreach (i; Iota!(1, N))
         {
             ret *= _slice._lengths[i];
-            ret += this._indexes[i] - right._indexes[i];
+            ret += this._indices[i] - right._indices[i];
         }
         return ret;
     }
@@ -1680,7 +1680,7 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
     bool opEquals()(scope ref const typeof(this) right) scope const
     {
         foreach_reverse (i; Iota!N)
-            if (this._indexes[i] != right._indexes[i])
+            if (this._indices[i] != right._indices[i])
                 return false;
         return true;
     }
@@ -1688,9 +1688,9 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
     ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const
     {
         foreach (i; Iota!(N - 1))
-            if (auto ret = this._indexes[i] - right._indexes[i])
+            if (auto ret = this._indices[i] - right._indices[i])
                 return ret;
-        return this._indexes[$ - 1] - right._indexes[$ - 1];
+        return this._indices[$ - 1] - right._indices[$ - 1];
     }
 }
 
diff --git a/source/mir/ndslice/mutation.d b/source/mir/ndslice/mutation.d
index 955d9c67..10265f94 100644
--- a/source/mir/ndslice/mutation.d
+++ b/source/mir/ndslice/mutation.d
@@ -25,20 +25,20 @@ Copies n-dimensional minor.
 void copyMinor(size_t N, IteratorFrom, SliceKind KindFrom, IteratorTo, SliceKind KindTo, IndexIterator)(
     Slice!(IteratorFrom, N, KindFrom) from,
     Slice!(IteratorTo, N, KindTo) to,
-    Slice!IndexIterator[N] indexes...
+    Slice!IndexIterator[N] indices...
 )
 in {
     import mir.internal.utility: Iota;
     static foreach (i; Iota!N)
-        assert(indexes[i].length == to.length!i);
+        assert(indices[i].length == to.length!i);
 }
 do {
     static if (N == 1)
-        to[] = from[indexes[0]];
+        to[] = from[indices[0]];
     else
-    foreach (i; 0 .. indexes[0].length)
+    foreach (i; 0 .. indices[0].length)
     {
-        copyMinor!(N - 1)(from[indexes[0][i]], to[i], indexes[1 .. N]);
+        copyMinor!(N - 1)(from[indices[0][i]], to[i], indices[1 .. N]);
     }
 }
 
diff --git a/source/mir/ndslice/ndfield.d b/source/mir/ndslice/ndfield.d
index 35b2539e..e6b603db 100644
--- a/source/mir/ndslice/ndfield.d
+++ b/source/mir/ndslice/ndfield.d
@@ -29,28 +29,28 @@ import mir.ndslice.slice;
 import mir.primitives;
 import std.meta;
 
-private template _indexes(NdFields...)
+private template _indices(NdFields...)
 {
     static if (NdFields.length == 0)
-        enum _indexes = "";
+        enum _indices = "";
     else
     {
         alias Next = NdFields[0 .. $ - 1];
         enum i = Next.length;
-        enum _indexes = ._indexes!Next ~
-    "_fields[" ~ i.stringof ~ "][" ~ _indexes_range!([staticMap!(DimensionCount, Next)].sum, DimensionCount!(NdFields[$ - 1])) ~ "], ";
+        enum _indices = ._indices!Next ~
+    "_fields[" ~ i.stringof ~ "][" ~ _indices_range!([staticMap!(DimensionCount, Next)].sum, DimensionCount!(NdFields[$ - 1])) ~ "], ";
     }
 }
 
-private template _indexes_range(size_t begin, size_t count)
+private template _indices_range(size_t begin, size_t count)
 {
     static if (count == 0)
-        enum _indexes_range = "";
+        enum _indices_range = "";
     else
     {
         enum next = count - 1;
         enum elem = begin + next;
-        enum _indexes_range = ._indexes_range!(begin, next) ~ "indexes[" ~ elem.stringof ~ "], ";
+        enum _indices_range = ._indices_range!(begin, next) ~ "indices[" ~ elem.stringof ~ "], ";
     }
 }
 
@@ -124,21 +124,21 @@ struct Cartesian(NdFields...)
     }
 
     ///
-    auto opIndex(size_t[N] indexes...)
+    auto opIndex(size_t[N] indices...)
     {
         import mir.functional : refTuple;
-        return mixin("refTuple(" ~ _indexes!(NdFields) ~ ")");
+        return mixin("refTuple(" ~ _indices!(NdFields) ~ ")");
     }
 }
 
-private template _kr_indexes(size_t n)
+private template _kr_indices(size_t n)
 {
     static if (n == 0)
-        enum _kr_indexes = "";
+        enum _kr_indices = "";
     else
     {
         enum i = n - 1;
-        enum _kr_indexes = ._kr_indexes!i ~ "_fields[" ~ i.stringof ~ "][ind[" ~ i.stringof ~ "]], ";
+        enum _kr_indices = ._kr_indices!i ~ "_fields[" ~ i.stringof ~ "][ind[" ~ i.stringof ~ "]], ";
     }
 }
 
@@ -219,7 +219,7 @@ struct Kronecker(alias fun, NdFields...)
     }
 
     ///
-    auto ref opIndex()(size_t[N] indexes...)
+    auto ref opIndex()(size_t[N] indices...)
     {   
         static if (N > 1)
             size_t[N][NdFields.length] ind;
@@ -242,14 +242,14 @@ struct Kronecker(alias fun, NdFields...)
                 {
                     foreach(i; Iota!N)
                     {
-                        ind[f][i] = indexes[i] % s[i];
-                        indexes[i] /= s[i];
+                        ind[f][i] = indices[i] % s[i];
+                        indices[i] /= s[i];
                     }
                 }
                 else
                 {
-                    ind[f] = indexes[0] % s[0];
-                    indexes[0] /= s[0];
+                    ind[f] = indices[0] % s[0];
+                    indices[0] /= s[0];
                 }
             }
             else
@@ -257,14 +257,14 @@ struct Kronecker(alias fun, NdFields...)
                 static if (N > 1)
                 {
                     foreach(i; Iota!N)
-                        ind[f][i] = indexes[i];
+                        ind[f][i] = indices[i];
                 }
                 else
                 {
-                    ind[f] = indexes[0];
+                    ind[f] = indices[0];
                 }
             }
         }
-        return mixin("fun(" ~ _kr_indexes!(ind.length) ~ ")");
+        return mixin("fun(" ~ _kr_indices!(ind.length) ~ ")");
     }
 }
diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index ff59657d..28dd0fb1 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -19,7 +19,7 @@
 
 3. `mixin template`s may be used for pretty error message formatting.
 
-4. `Exception`s/`enforce`s should no be used to check indexes and lengths.
+4. `Exception`s/`enforce`s should no be used to check indices and lengths.
     Exceptions are only allowed for algorithms where validation of input data is
     too complicated for the user. `reshape` function is a good example of a case
     where Exceptions are required.
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index d8d3d0d7..c290a912 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -523,13 +523,13 @@ $(TR $(TD An $(B interval) is a part of a sequence of type `i .. j`.)
 $(TR $(TD An $(B index) is a part of a sequence of type `i`.)
     $(STD `3`, `$-1`))
 $(TR $(TD A $(B partially defined slice) is a sequence composed of
-    $(B intervals) and $(B indexes) with an overall length strictly less than `N`.)
+    $(B intervals) and $(B indices) with an overall length strictly less than `N`.)
     $(STD `[3]`, `[0..$]`, `[3, 3]`, `[0..$,0..3]`, `[0..$,2]`))
 $(TR $(TD A $(B fully defined index) is a sequence
-    composed only of $(B indexes) with an overall length equal to `N`.)
+    composed only of $(B indices) with an overall length equal to `N`.)
     $(STD `[2,3,1]`))
 $(TR $(TD A $(B fully defined slice) is an empty sequence
-    or a sequence composed of $(B indexes) and at least one
+    or a sequence composed of $(B indices) and at least one
     $(B interval) with an overall length equal to `N`.)
     $(STD `[]`, `[3..$,0..3,0..$-1]`, `[2,0..$,1]`))
 $(TR $(TD An $(B indexed slice) is syntax sugar for $(SUBREF topology, indexed) and $(SUBREF topology, cartesian).)
@@ -779,47 +779,47 @@ package(mir):
         return _stride!dimension * (_lengths[dimension] - 1);
     }
 
-    size_t indexStride(size_t I)(size_t[I] _indexes) @safe scope const
+    size_t indexStride(size_t I)(size_t[I] _indices) @safe scope const
     {
-        static if (_indexes.length)
+        static if (_indices.length)
         {
             static if (kind == Contiguous)
             {
                 enum E = I - 1;
-                assert(_indexes[E] < _lengths[E], indexError!(E, N));
+                assert(_indices[E] < _lengths[E], indexError!(E, N));
                 ptrdiff_t ball = this._stride!E;
-                ptrdiff_t stride = _indexes[E] * ball;
+                ptrdiff_t stride = _indices[E] * ball;
                 foreach_reverse (i; Iota!E) //static
                 {
                     ball *= _lengths[i + 1];
-                    assert(_indexes[i] < _lengths[i], indexError!(i, N));
-                    stride += ball * _indexes[i];
+                    assert(_indices[i] < _lengths[i], indexError!(i, N));
+                    stride += ball * _indices[i];
                 }
             }
             else
             static if (kind == Canonical)
             {
                 enum E = I - 1;
-                assert(_indexes[E] < _lengths[E], indexError!(E, N));
+                assert(_indices[E] < _lengths[E], indexError!(E, N));
                 static if (I == N)
-                    size_t stride = _indexes[E];
+                    size_t stride = _indices[E];
                 else
-                    size_t stride = _strides[E] * _indexes[E];
+                    size_t stride = _strides[E] * _indices[E];
                 foreach_reverse (i; Iota!E) //static
                 {
-                    assert(_indexes[i] < _lengths[i], indexError!(i, N));
-                    stride += _strides[i] * _indexes[i];
+                    assert(_indices[i] < _lengths[i], indexError!(i, N));
+                    stride += _strides[i] * _indices[i];
                 }
             }
             else
             {
                 enum E = I - 1;
-                assert(_indexes[E] < _lengths[E], indexError!(E, N));
-                size_t stride = _strides[E] * _indexes[E];
+                assert(_indices[E] < _lengths[E], indexError!(E, N));
+                size_t stride = _strides[E] * _indices[E];
                 foreach_reverse (i; Iota!E) //static
                 {
-                    assert(_indexes[i] < _lengths[i], indexError!(i, N));
-                    stride += _strides[i] * _indexes[i];
+                    assert(_indices[i] < _lengths[i], indexError!(i, N));
+                    stride += _strides[i] * _indices[i];
                 }
             }
             return stride;
@@ -1050,16 +1050,16 @@ public:
     }
 
     /// `opIndex` overload for const slice
-    auto ref opIndex(Indexes...)(Indexes indexes) const @trusted
+    auto ref opIndex(Indexes...)(Indexes indices) const @trusted
             if (isPureSlice!Indexes || isIndexedSlice!Indexes)
     {
-        return lightConst.opIndex(indexes);
+        return lightConst.opIndex(indices);
     }
     /// `opIndex` overload for immutable slice
-    auto ref opIndex(Indexes...)(Indexes indexes) immutable @trusted
+    auto ref opIndex(Indexes...)(Indexes indices) immutable @trusted
             if (isPureSlice!Indexes || isIndexedSlice!Indexes)
     {
-        return lightImmutable.opIndex(indexes);
+        return lightImmutable.opIndex(indices);
     }
 
     static if (allSatisfy!(isPointer, Iterator, Labels))
@@ -2099,55 +2099,55 @@ public:
     /++
     $(BOLD Fully defined index)
     +/
-    auto ref opIndex()(size_t[N] _indexes...) scope return @trusted
+    auto ref opIndex()(size_t[N] _indices...) scope return @trusted
     {
-        return _iterator[indexStride(_indexes)];
+        return _iterator[indexStride(_indices)];
     }
 
     /// ditto
-    auto ref opIndex()(size_t[N] _indexes...) scope return const @trusted
+    auto ref opIndex()(size_t[N] _indices...) scope return const @trusted
     {
-        static if (is(typeof(_iterator[indexStride(_indexes)])))
-            return _iterator[indexStride(_indexes)];
+        static if (is(typeof(_iterator[indexStride(_indices)])))
+            return _iterator[indexStride(_indices)];
         else
-            return .lightConst(.lightScope(_iterator))[indexStride(_indexes)];
+            return .lightConst(.lightScope(_iterator))[indexStride(_indices)];
     }
 
     /// ditto
-    auto ref opIndex()(size_t[N] _indexes...) scope return immutable @trusted
+    auto ref opIndex()(size_t[N] _indices...) scope return immutable @trusted
     {
-        static if (is(typeof(_iterator[indexStride(_indexes)])))
-            return _iterator[indexStride(_indexes)];
+        static if (is(typeof(_iterator[indexStride(_indices)])))
+            return _iterator[indexStride(_indices)];
         else
-            return .lightImmutable(.lightScope(_iterator))[indexStride(_indexes)];
+            return .lightImmutable(.lightScope(_iterator))[indexStride(_indices)];
     }
 
     /++
     $(BOLD Partially defined index)
     +/
-    auto opIndex(size_t I)(size_t[I] _indexes...) scope return @trusted
+    auto opIndex(size_t I)(size_t[I] _indices...) scope return @trusted
         if (I && I < N)
     {
         enum size_t diff = N - I;
         alias Ret = Slice!(Iterator, diff, diff == 1 && kind == Canonical ? Contiguous : kind);
         static if (I < S)
-            return Ret(_lengths[I .. N], _strides[I .. S], _iterator + indexStride(_indexes));
+            return Ret(_lengths[I .. N], _strides[I .. S], _iterator + indexStride(_indices));
         else
-            return Ret(_lengths[I .. N], _iterator + indexStride(_indexes));
+            return Ret(_lengths[I .. N], _iterator + indexStride(_indices));
     }
 
     /// ditto
-    auto opIndex(size_t I)(size_t[I] _indexes...) scope return const
+    auto opIndex(size_t I)(size_t[I] _indices...) scope return const
         if (I && I < N)
     {
-        return this.lightConst.opIndex(_indexes);
+        return this.lightConst.opIndex(_indices);
     }
 
     /// ditto
-    auto opIndex(size_t I)(size_t[I] _indexes...) scope return immutable
+    auto opIndex(size_t I)(size_t[I] _indices...) scope return immutable
         if (I && I < N)
     {
-        return this.lightImmutable.opIndex(_indexes);
+        return this.lightImmutable.opIndex(_indices);
     }
 
     /++
@@ -2958,14 +2958,14 @@ public:
         /++
         Assignment of a value (e.g. a number) to a $(B fully defined index).
         +/
-        auto ref opIndexAssign(T)(T value, size_t[N] _indexes...) scope return @trusted
+        auto ref opIndexAssign(T)(T value, size_t[N] _indices...) scope return @trusted
         {
             // check assign safety
             static auto ref fun(ref DeepElement t, ref T v) @safe
             {
                 return t = v;
             }
-            return _iterator[indexStride(_indexes)] = value;
+            return _iterator[indexStride(_indices)] = value;
         }
 
         static if (doUnittest)
@@ -2991,14 +2991,14 @@ public:
         /++
         Op Assignment `op=` of a value (e.g. a number) to a $(B fully defined index).
         +/
-        auto ref opIndexOpAssign(string op, T)(T value, size_t[N] _indexes...) scope return @trusted
+        auto ref opIndexOpAssign(string op, T)(T value, size_t[N] _indices...) scope return @trusted
         {
             // check op safety
             static auto ref fun(ref DeepElement t, ref T v) @safe
             {
                 return mixin(`t` ~ op ~ `= v`);
             }
-            auto str = indexStride(_indexes);
+            auto str = indexStride(_indices);
             static if (op == "^^" && isFloatingPoint!DeepElement && isFloatingPoint!(typeof(value)))
             {
                 import mir.math.common: pow;
@@ -3227,7 +3227,7 @@ public:
         /++
         Increment `++` and Decrement `--` operators for a $(B fully defined index).
         +/
-        auto ref opIndexUnary(string op)(size_t[N] _indexes...) scope return
+        auto ref opIndexUnary(string op)(size_t[N] _indices...) scope return
             @trusted
             // @@@workaround@@@ for Issue 16473
             //if (op == `++` || op == `--`)
@@ -3237,7 +3237,7 @@ public:
             {
                 return mixin(op ~ `t`);
             }
-            return mixin (op ~ `_iterator[indexStride(_indexes)]`);
+            return mixin (op ~ `_iterator[indexStride(_indices)]`);
         }
 
         static if (doUnittest)
@@ -3342,7 +3342,7 @@ pure nothrow version(mir_test) unittest
     ++tensor[];
     tensor[] -= 1;
 
-    // `opIndexAssing` accepts only fully defined indexes and slices.
+    // `opIndexAssing` accepts only fully defined indices and slices.
     // Use an additional empty slice `[]`.
     static assert(!__traits(compiles, tensor[0 .. 2] *= 2));
 
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 91276b4e..e3017b7e 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -670,7 +670,7 @@ index is a sorted array of indices into the original
 range. This technique is similar to sorting, but it is more flexible
 because (1) it allows "sorting" of immutable collections, (2) allows
 binary search even if the original collection does not offer random
-access, (3) allows multiple indexes, each on a different predicate,
+access, (3) allows multiple indices, each on a different predicate,
 and (4) may be faster when dealing with large objects. However, using
 an index may also be slower under certain circumstances due to the
 extra indirection, and is always larger than a sorting-based solution
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index d5bc6c05..a7c20441 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -72,12 +72,12 @@ $(T2 map, Multidimensional functional map.)
 $(T2 member, Field (element's member) projection.)
 $(T2 orthogonalReduceField, Functional deep-element wise reduce of a slice composed of fields or iterators.)
 $(T2 pairwise, Pairwise map for vectors.)
-$(T2 pairwiseMapSubSlices, Maps pairwise indexes pairs to subslices.)
+$(T2 pairwiseMapSubSlices, Maps pairwise index pairs to subslices.)
 $(T2 retro, Reverses order of iteration for all dimensions.)
 $(T2 slide, Sliding map for vectors.)
 $(T2 stairs, Two functions to pack, unpack, and iterate triangular and symmetric matrix storage.)
 $(T2 stride, Strides 1-dimensional slice.)
-$(T2 subSlices, Maps indexes pairs to subslices.)
+$(T2 subSlices, Maps index pairs to subslices.)
 $(T2 triplets, Constructs a lazy view of triplets with `left`, `center`, and `right` members. The topology is usefull for Math and Physics.)
 $(T2 unzip, Selects a slice from a zipped slice.)
 $(T2 zip, Zips slices into a slice of refTuples.)
@@ -623,7 +623,7 @@ Params:
     start = value of the first element in a slice (optional for integer `I`)
     stride = value of the stride between elements (optional)
 Returns:
-    n-dimensional slice composed of indexes
+    n-dimensional slice composed of indices
 See_also: $(LREF ndiota)
 +/
 Slice!(IotaIterator!I, N)
@@ -1515,9 +1515,9 @@ Slice!(FlattenedIterator!(Iterator, N, kind))
 {
     import core.lifetime: move;
     size_t[typeof(return).N] lengths;
-    sizediff_t[typeof(return)._iterator._indexes.length] indexes;
+    sizediff_t[typeof(return)._iterator._indices.length] indices;
     lengths[0] = slice.elementCount;
-    return typeof(return)(lengths, FlattenedIterator!(Iterator, N, kind)(indexes, slice.move));
+    return typeof(return)(lengths, FlattenedIterator!(Iterator, N, kind)(indices, slice.move));
 }
 
 /// ditto
@@ -1588,7 +1588,7 @@ version(mir_test) unittest
     elems.popFrontExactly(2);
     assert(elems.front == 2);
     /// `_index` is available only for canonical and universal ndslices.
-    assert(elems._iterator._indexes == [0, 2]);
+    assert(elems._iterator._indices == [0, 2]);
 
     elems.popBackExactly(2);
     assert(elems.back == 9);
@@ -1603,7 +1603,7 @@ version(mir_test) unittest
 
     for (auto elems = slice.universal.flattened; !elems.empty; elems.popFront)
     {
-        ptrdiff_t[2] index = elems._iterator._indexes;
+        ptrdiff_t[2] index = elems._iterator._indices;
         elems.front = index[0] * 10 + index[1] * 3;
     }
     assert(slice ==
@@ -1774,7 +1774,7 @@ Params:
     N = dimension count
     lengths = list of dimension lengths
 Returns:
-    `N`-dimensional slice composed of indexes
+    `N`-dimensional slice composed of indices
 See_also: $(LREF iota)
 +/
 Slice!(FieldIterator!(ndIotaField!N), N)
@@ -3447,12 +3447,12 @@ template as(T)
 }
 
 /++
-Takes a field `source` and a slice `indexes`, and creates a view of source as if its elements were reordered according to indexes.
-`indexes` may include only a subset of the elements of `source` and may also repeat elements.
+Takes a field `source` and a slice `indices`, and creates a view of source as if its elements were reordered according to indices.
+`indices` may include only a subset of the elements of `source` and may also repeat elements.
 
 Params:
     source = a filed, source of data. `source` must be an array or a pointer, or have `opIndex` primitive. Full random access range API is not required.
-    indexes = a slice, source of indexes.
+    indices = a slice, source of indices.
 Returns:
     n-dimensional slice with the same kind, shape and strides.
 
@@ -3460,38 +3460,38 @@ See_also: `indexed` is similar to $(LREF vmap), but a field (`[]`) is used inste
 +/
 Slice!(IndexIterator!(Iterator, Field), N, kind)
     indexed(Field, Iterator, size_t N, SliceKind kind)
-    (Field source, Slice!(Iterator, N, kind) indexes)
+    (Field source, Slice!(Iterator, N, kind) indices)
 {
     import core.lifetime: move;
     return typeof(return)(
-            indexes._structure,
+            indices._structure,
             IndexIterator!(Iterator, Field)(
-                indexes._iterator.move,
+                indices._iterator.move,
                 source));
 }
 
 /// ditto
-auto indexed(Field, S)(Field source, S[] indexes)
+auto indexed(Field, S)(Field source, S[] indices)
 {
-    return indexed(source, indexes.sliced);
+    return indexed(source, indices.sliced);
 }
 
 /// ditto
-auto indexed(Field, S)(Field source, S indexes)
+auto indexed(Field, S)(Field source, S indices)
     if (hasAsSlice!S)
 {
-    return indexed(source, indexes.asSlice);
+    return indexed(source, indices.asSlice);
 }
 
 ///
 @safe pure nothrow version(mir_test) unittest
 {
     auto source = [1, 2, 3, 4, 5];
-    auto indexes = [4, 3, 1, 2, 0, 4];
-    auto ind = source.indexed(indexes);
+    auto indices = [4, 3, 1, 2, 0, 4];
+    auto ind = source.indexed(indices);
     assert(ind == [5, 4, 2, 3, 1, 5]);
 
-    assert(ind.retro == source.indexed(indexes.retro));
+    assert(ind.retro == source.indexed(indices.retro));
 
     ind[3] += 10; // for index 2
     //                0  1   2  3  4
@@ -3499,10 +3499,10 @@ auto indexed(Field, S)(Field source, S indexes)
 }
 
 /++
-Maps indexes pairs to subslices.
+Maps index pairs to subslices.
 Params:
     sliceable = pointer, array, ndslice, series, or something sliceable with `[a .. b]`.
-    slices = ndslice composed of indexes pairs.
+    slices = ndslice composed of index pairs.
 Returns:
     ndslice composed of subslices.
 See_also: $(LREF chopped), $(LREF pairwise).
@@ -3551,7 +3551,7 @@ auto subSlices(S, Sliceable)(Sliceable sliceable, S slices)
 }
 
 /++
-Maps indexes pairs to subslices.
+Maps index pairs to subslices.
 Params:
     bounds = ndslice composed of consequent (`a_i <= a_(i+1)`) pairwise index bounds.
     sliceable = pointer, array, ndslice, series, or something sliceable with `[a_i .. a_(i+1)]`.

From 66f0078e435186ad15840237b7f7618ae3ec134e Mon Sep 17 00:00:00 2001
From: Mathias LANG <pro.mathias.lang@gmail.com>
Date: Tue, 1 Sep 2020 17:56:33 +0200
Subject: [PATCH 075/573] Adapt code to support -preview=in (#318)

When the switch is provided, '[auto] ref in' becomes illegal in the parser,
so instead just use const scope.
---
 source/mir/ndslice/internal.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index f24a06df..0d4faf44 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -259,7 +259,7 @@ template DynamicArrayDimensionsCount(T)
         enum size_t DynamicArrayDimensionsCount = 0;
 }
 
-bool isPermutation(size_t N)(auto ref in size_t[N] perm)
+bool isPermutation(size_t N)(auto ref const scope size_t[N] perm)
 {
     int[N] mask;
     return isValidPartialPermutationImpl(perm, mask);
@@ -328,7 +328,7 @@ private enum isReference(P) =
 alias ImplicitlyUnqual(T) = Select!(isImplicitlyConvertible!(T, Unqual!T), Unqual!T, T);
 alias ImplicitlyUnqual(T : T*) = T*;
 
-size_t lengthsProduct(size_t N)(auto ref in size_t[N] lengths)
+size_t lengthsProduct(size_t N)(auto ref const scope size_t[N] lengths)
 {
     size_t length = lengths[0];
     foreach (i; Iota!(1, N))

From f53ef5b63201239fb234f92feca3ec715c27e231 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 6 Sep 2020 20:04:01 +0700
Subject: [PATCH 076/573] rework small string and small array

---
 source/mir/small_array.d  | 381 +++++++++++++++++++-------------------
 source/mir/small_string.d |   3 +
 2 files changed, 197 insertions(+), 187 deletions(-)

diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 55fa8c1c..36b57141 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -10,254 +10,261 @@ Authors:   Ilya Yaroshenko
 +/
 module mir.small_array;
 
+import mir.serde: serdeProxy;
+
 private static immutable errorMsg = "Cannot create SmallArray: input range exceeds maximum allowed length.";
 version(D_Exceptions)
     private static immutable exception = new Exception(errorMsg);
 
 ///
-struct SmallArray(T, uint maxLength)
+template SmallArray(T, uint maxLength)
     if (maxLength)
 {
-    import std.traits: Unqual, isIterable, isImplicitlyConvertible;
+    ///
+    @serdeProxy!(T[])
+    struct SmallArray
+    {
+        import std.traits: Unqual, isIterable, isImplicitlyConvertible;
 
-    uint _length;
-    T[maxLength] _data;
+        uint _length;
+        T[maxLength] _data;
 
-	static SmallArray deserialize(S)(S data)
-	{
-        import asdf.serialization: deserialize;
-        return SmallArray(data.deserialize!(T[]));
-	}
+        static SmallArray deserialize(S)(S data)
+        {
+            import asdf.serialization: deserialize;
+            return SmallArray(data.deserialize!(T[]));
+        }
 
-	void serialize(S)(ref S serializer)
-	{
-		serializer.putValue(asArray);
-	}
+        void serialize(S)(ref S serializer)
+        {
+            serializer.putValue(asArray);
+        }
 
-    static if (isImplicitlyConvertible!(const T, T))
-        alias V = const T;
-    else
-        alias V = T;
+        static if (isImplicitlyConvertible!(const T, T))
+            alias V = const T;
+        else
+            alias V = T;
 
-@safe pure @nogc:
+    @safe pure @nogc:
 
-    /// Constructor
-    this(typeof(null))
-    {
-    }
+        /// Constructor
+        this(typeof(null))
+        {
+        }
 
-    /// ditto
-    this(V[] array)
-    {
-        this.opAssign(array);
-    }
+        /// ditto
+        this(V[] array)
+        {
+            this.opAssign(array);
+        }
 
-    /// ditto
-    this(const SmallArray array) nothrow
-    {
-        this.opAssign(array);
-    }
+        /// ditto
+        this(const SmallArray array) nothrow
+        {
+            this.opAssign(array);
+        }
 
-    /// ditto
-    this(uint n)(const SmallArray!(T, n) array)
-    {
-        this.opAssign(array);
-    }
+        /// ditto
+        this(uint n)(const SmallArray!(T, n) array)
+        {
+            this.opAssign(array);
+        }
 
-    /// ditto
-    this(uint n)(ref const SmallArray!(T, n) array)
-    {
-        this.opAssign(array);
-    }
+        /// ditto
+        this(uint n)(ref const SmallArray!(T, n) array)
+        {
+            this.opAssign(array);
+        }
 
-    /// ditto
-    this(Range)(auto ref Range array)
-        if (isIterable!Range)
-    {
-        foreach(ref c; array)
+        /// ditto
+        this(Range)(auto ref Range array)
+            if (isIterable!Range)
         {
-            if (_length > _data.length)
+            foreach(ref c; array)
+            {
+                if (_length > _data.length)
+                {
+                    version(D_Exceptions) throw exception;
+                    else assert(0, errorMsg);
+                }
+                _data[_length++] = c;
+            }
+        }
+
+        /// `=` operator
+        ref typeof(this) opAssign(typeof(null)) return
+        {
+            _length = 0;
+            _data = T.init;
+            return this;
+        }
+
+        /// ditto
+        ref typeof(this) opAssign(V[] array) return @trusted
+        {
+            if (array.length > maxLength)
             {
                 version(D_Exceptions) throw exception;
                 else assert(0, errorMsg);
             }
-            _data[_length++] = c;
+            _data[0 .. _length] = T.init;
+            _length = cast(uint) array.length;
+            _data[0 .. _length] = array;
+            return this;
         }
-    }
 
-    /// `=` operator
-    ref typeof(this) opAssign(typeof(null)) return
-    {
-        _length = 0;
-        _data = T.init;
-        return this;
-    }
+        /// ditto
+        ref typeof(this) opAssign(ref const SmallArray rhs) return nothrow
+        {
+            _length = rhs._length;
+            _data = rhs._data;
+            return this;
+        }
+        
+        /// ditto
+        ref typeof(this) opAssign(const SmallArray rhs) return nothrow
+        {
+            _length = rhs._length;
+            _data = rhs._data;
+            return this;
+        }
 
-    /// ditto
-    ref typeof(this) opAssign(V[] array) return @trusted
-    {
-        if (array.length > maxLength)
+        /// ditto
+        ref typeof(this) opAssign(uint n)(ref const SmallArray!(T, n) rhs) return
+            if (n != maxLength)
         {
-            version(D_Exceptions) throw exception;
-            else assert(0, errorMsg);
+            static if (n < maxLength)
+            {
+                _data[0 .. n] = rhs._data;
+                if (_length > n)
+                    _data[n .. _length] = T.init;
+            }
+            else
+            {
+                if (rhs._length > maxLength)
+                {
+                    version(D_Exceptions) throw exception;
+                    else assert(0, errorMsg);
+                }
+                _data = rhs._data[0 .. maxLength];
+            }
+            _length = rhs._length;
+            return this;
         }
-        _data[0 .. _length] = T.init;
-        _length = cast(uint) array.length;
-        _data[0 .. _length] = array;
-        return this;
-    }
 
-    /// ditto
-    ref typeof(this) opAssign(ref const SmallArray rhs) return nothrow
-    {
-        _length = rhs._length;
-        _data = rhs._data;
-        return this;
-    }
-    
-    /// ditto
-    ref typeof(this) opAssign(const SmallArray rhs) return nothrow
-    {
-        _length = rhs._length;
-        _data = rhs._data;
-        return this;
-    }
+        /// ditto
+        ref typeof(this) opAssign(uint n)(const SmallArray!(T, n) rhs) return
+            if (n != maxLength)
+        {
+            static if (n < maxLength)
+            {
+                _data[0 .. n] = rhs._data;
+                if (_length > n)
+                    _data[n .. _length] = T.init;
+            }
+            else
+            {
+                if (rhs._length > maxLength)
+                {
+                    version(D_Exceptions) throw exception;
+                    else assert(0, errorMsg);
+                }
+                _data = rhs._data[0 .. maxLength];
+            }
+            _length = rhs._length;
+            return this;
+        }
 
-    /// ditto
-    ref typeof(this) opAssign(uint n)(ref const SmallArray!(T, n) rhs) return
-        if (n != maxLength)
-    {
-        static if (n < maxLength)
+        ///
+        void trustedAssign(V[] array) @trusted
         {
-            _data[0 .. n] = rhs._data;
-            if (_length > n)
-                _data[n .. _length] = T.init;
+            _data[0 .. _length] = T.init;
+            _length = cast(uint) array.length;
+            _data[0 .. _length] = array;
         }
-        else
+
+        ///
+        ref typeof(this) append(T elem)
         {
-            if (rhs._length > maxLength)
+            if (_length == maxLength)
             {
                 version(D_Exceptions) throw exception;
                 else assert(0, errorMsg);
             }
-            _data = rhs._data[0 .. maxLength];
+            _data[_length++] = elem;
+            return this;
         }
-        _length = rhs._length;
-        return this;
-    }
 
-    /// ditto
-    ref typeof(this) opAssign(uint n)(const SmallArray!(T, n) rhs) return
-        if (n != maxLength)
-    {
-        static if (n < maxLength)
-        {
-            _data[0 .. n] = rhs._data;
-            if (_length > n)
-                _data[n .. _length] = T.init;
-        }
-        else
+        ///
+        ref typeof(this) append(V[] array)
         {
-            if (rhs._length > maxLength)
+            if (_length + array.length > maxLength)
             {
                 version(D_Exceptions) throw exception;
                 else assert(0, errorMsg);
             }
-            _data = rhs._data[0 .. maxLength];
+            _data[_length .. _length + array.length] = array;
+            _length += array.length;
+            return this;
         }
-        _length = rhs._length;
-        return this;
-    }
 
-    ///
-    void trustedAssign(V[] array) @trusted
-    {
-        _data[0 .. _length] = T.init;
-        _length = cast(uint) array.length;
-        _data[0 .. _length] = array;
-    }
+        /// ditto
+        alias put = append;
 
-    ///
-    ref typeof(this) append(T elem)
-    {
-        if (_length == maxLength)
-        {
-            version(D_Exceptions) throw exception;
-            else assert(0, errorMsg);
-        }
-        _data[_length++] = elem;
-        return this;
-    }
+        /// ditto
+        alias opOpAssign(string op : "~") = append;
 
-    ///
-    ref typeof(this) append(V[] array)
-    {
-        if (_length + array.length > maxLength)
+        ///
+        SmallArray concat(V[] array) scope const
         {
-            version(D_Exceptions) throw exception;
-            else assert(0, errorMsg);
+            SmallArray c = this;
+            c.append(array);
+            return c; 
         }
-        _data[_length .. _length + array.length] = array;
-        _length += array.length;
-        return this;
-    }
 
-    /// ditto
-    alias put = append;
+        /// ditto
+        alias opBinary(string op : "~") = concat;
 
-    /// ditto
-    alias opOpAssign(string op : "~") = append;
 
-    ///
-    SmallArray concat(V[] array) scope const
-    {
-        SmallArray c = this;
-        c.append(array);
-        return c; 
-    }
+    scope nothrow:
 
-    /// ditto
-    alias opBinary(string op : "~") = concat;
+        /++
+        Returns an scope common array.
 
+        The property is used as common array representation self alias.
 
-scope nothrow:
-
-    /++
-    Returns an scope common array.
-
-    The property is used as common array representation self alias.
-
-    The alias helps with `[]`, `[i]`, `[i .. j]`, `==`, and `!=` operations and implicit conversion to strings.
-    +/
-    inout(T)[] asArray() inout @trusted return @property
-    {
-        return _data[0 .. _length];
-    }
+        The alias helps with `[]`, `[i]`, `[i .. j]`, `==`, and `!=` operations and implicit conversion to strings.
+        +/
+        inout(T)[] asArray() inout @trusted return @property
+        {
+            return _data[0 .. _length];
+        }
 
-    alias asArray this;
+        alias asArray this;
 
-const:
+    const:
 
-    /++
-    Checks if the array is empty (null).
-    +/
-    C opCast(C)() const
-        if (is(Unqual!C == bool))
-    {
-        return _length != 0;
-    }
+        /++
+        Checks if the array is empty (null).
+        +/
+        C opCast(C)() const
+            if (is(Unqual!C == bool))
+        {
+            return _length != 0;
+        }
 
-    /// Hash implementation
-    size_t toHash()
-    {
-        return hashOf(asArray);
-    }
+        /// Hash implementation
+        size_t toHash()
+        {
+            return hashOf(asArray);
+        }
 
-    /// ditto
-    auto opCmp()(V[] array)
-    {
-        import mir.algorithm.iteration: cmp;
-        return cmp(asArray, array);
+        /// ditto
+        auto opCmp()(V[] array)
+        {
+            import mir.algorithm.iteration: cmp;
+            return cmp(asArray, array);
+        }
     }
 }
 
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index a930cc92..0ff61936 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -13,6 +13,8 @@ Authors:   Ilya Yaroshenko
 +/
 module mir.small_string;
 
+import mir.serde: serdeScopeStringProxy;
+
 private extern (C) @system nothrow @nogc pure size_t strnlen_s(scope const char* s, size_t n);
 
 private static immutable errorMsg = "Cannot create SmallString: input string exceeds maximum allowed length.";
@@ -24,6 +26,7 @@ extern(C++, "mir"):
 /++
 Self-contained generic Small String implementaton.
 +/
+@serdeScopeStringProxy
 struct SmallString(uint maxLength)
     if (maxLength)
 {

From f86401775fb8a700266cadba2ff4e018e5917c22 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 6 Sep 2020 20:04:20 +0700
Subject: [PATCH 077/573] extend serde API

---
 source/mir/serde.d | 408 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 407 insertions(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 37ed0693..b09050d4 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -408,10 +408,19 @@ Use this attributes only for strings that would not be used after ASDF deallocat
 enum serdeScoped;
 
 /++
-Attribute that force deserializer to throw an exception that the field was not found in the input.
+Attribute that force deserializer to throw an exception that the field hasn't been not found in the input.
 +/
 enum serdeRequired;
 
+/++
+Attribute that allow deserializer to do not throw an exception if the field hasn't been not found in the input.
++/
+enum serdeOptional;
+
+/++
+Attribute that allow deserializer to don't throw an exception that the field matches multiple keys in the object.
++/
+enum serdeAllowMultiple;
 
 /++
 Attributes for in transformation.
@@ -508,6 +517,403 @@ unittest
     assert(e == E.c);
 }
 
+/++
+Deserialization member type
++/
+template serdeDeserializationMemberType(T, string member)
+{
+    import mir.reflection: isField;
+    import std.traits: Unqual, Parameters;
+    T* aggregate;
+    static if (isField!(T, member))
+    {
+        alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member));
+    }
+    else
+    static if (__traits(compiles, &__traits(getMember, *aggregate, member)()))
+    {
+        alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member)());
+    }
+    else
+    {
+        alias serdeDeserializationMemberType = Unqual!(Parameters!(__traits(getMember, *aggregate, member))[0]);
+    }
+}
+
+/// ditto
+template serdeDeserializationMemberType(T)
+{
+    ///
+    alias serdeDeserializationMemberType(string member) = .serdeDeserializationMemberType!(T, member);
+}
+
+
+/++
+Is deserializable member
++/
+template serdeIsDeserializable(T)
+{
+    import mir.serde: serdeGetKeysIn;
+    ///
+    enum bool serdeIsDeserializable(string member) = serdeGetKeysIn!(__traits(getMember, T, member)).length > 0;
+}
+
+///
+unittest
+{
+    import mir.serde;
+
+    static struct S
+    {
+        @serdeIgnore
+        int i;
+
+        @serdeKeys("a", "b")
+        int a;
+    }
+
+    alias serdeIsDeserializableInS = serdeIsDeserializable!S;
+    static assert (!serdeIsDeserializableInS!"i");
+    static assert (serdeIsDeserializableInS!"a");
+}
+
+/++
+Final proxy type
++/
+template serdeGetFinalProxy(T)
+{
+    import mir.serde: serdeProxy, serdeGetProxy;
+    import std.traits: hasUDA;
+    static if (is(T == class) || is(T == struct) || is(T == union) || is(T == interface))
+    {
+        static if (hasUDA!(T, serdeProxy))
+        {
+            alias serdeGetFinalProxy = .serdeGetFinalProxy!(serdeGetProxy!T);
+        }
+        else
+        {
+            alias serdeGetFinalProxy = T;
+        }
+    }
+    else
+    {
+        alias serdeGetFinalProxy = T;
+    }
+}
+
+///
+unittest
+{
+    import mir.serde;
+
+    @serdeProxy!string
+    static struct A {}
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+    static assert (is(serdeGetFinalProxy!C == string), serdeGetFinalProxy!C.stringof);
+    static assert (is(serdeGetFinalProxy!string == string));
+}
+
+/++
+Final proxy type deserializable members
++/
+template serdeFinalProxyDeserializableMembers(T)
+{
+    import std.meta: Filter, aliasSeqOf;
+    import mir.reflection: DeserializableMembers;
+    alias P = serdeGetFinalProxy!T;
+    static if (is(P == struct) || is(P == class) || is(P == interface) || is(P == union))
+        enum string[] serdeFinalProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
+    else
+        enum string[] serdeFinalProxyDeserializableMembers = null;
+}
+
+///
+unittest
+{
+    import mir.serde;
+
+    static struct A
+    {
+        @serdeIgnore
+        int i;
+
+        @serdeKeys("a", "b")
+        int m;
+    }
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+    static assert (serdeFinalProxyDeserializableMembers!C == ["m"]);
+}
+
+/++
+Deserialization member final proxy type
++/
+template serdeFinalDeserializationMemberType(T, string member)
+{
+    import std.traits: hasUDA;
+    import mir.serde: serdeProxy, serdeGetProxy;
+    static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
+    {
+        alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
+    }
+    else
+    {
+        alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeDeserializationMemberType!(T, member));
+    }
+}
+
+/// ditto
+template serdeFinalDeserializationMemberType(T)
+{
+    ///
+    alias serdeFinalDeserializationMemberType(string member) = .serdeFinalDeserializationMemberType!(T, member);
+}
+
+///
+unittest
+{
+    import mir.serde;
+
+    static struct A
+    {
+        
+    }
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+
+    @serdeProxy!double
+    struct E {}
+
+    struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    static assert (is(serdeFinalDeserializationMemberType!(D, "c") == A));
+    static assert (is(serdeFinalDeserializationMemberType!(D, "d") == double));
+}
+
+/++
+Deserialization members final proxy types
++/
+template serdeDeserializationFinalProxyMemberTypes(T)
+{
+    import std.meta: NoDuplicates, staticMap, aliasSeqOf;
+    alias serdeDeserializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalProxyDeserializableMembers!T))));
+}
+
+///
+unittest
+{
+    import mir.serde;
+
+    static struct A {}
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+    @serdeProxy!B
+    static struct E {}
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    import std.meta: AliasSeq;
+    static assert (is(serdeDeserializationFinalProxyMemberTypes!D == AliasSeq!A));
+}
+
+private template serdeDeserializationFinalProxyMemberTypesRecurseImpl(T...)
+{
+    import std.meta: NoDuplicates, staticMap;
+    alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalProxyMemberTypes, T));
+    static if (F.length == T.length)
+        alias serdeDeserializationFinalProxyMemberTypesRecurseImpl = T;
+    else
+        alias serdeDeserializationFinalProxyMemberTypesRecurseImpl  = .serdeDeserializationFinalProxyMemberTypesRecurseImpl!F;
+}
+
+/++
+Deserialization members final proxy types (recursive)
++/
+alias serdeDeserializationFinalProxyMemberTypesRecurse(T) = serdeDeserializationFinalProxyMemberTypesRecurseImpl!(serdeGetFinalProxy!T);
+
+///
+unittest
+{
+    import mir.serde;
+
+    static struct A { double g; }
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+    @serdeProxy!B
+    static struct E {}
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    @serdeProxy!D
+    static struct F {}
+
+    import std.meta: AliasSeq;
+    static assert (is(serdeDeserializationFinalProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
+}
+
+/++
+Deserialization members final proxy keys (recursive)
++/
+template serdeGetDeserializatinKeysRecurse(T)
+{
+    import mir.algorithm.iteration: uniq, equal;
+    import mir.array.allocation: array;
+    import mir.ndslice.sorting: sort;
+    import mir.reflection: DeserializableMembers;
+    import std.meta: staticMap, aliasSeqOf;
+    enum string[] serdeGetDeserializatinKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalProxyDeserializableMembers, serdeDeserializationFinalProxyMemberTypesRecurse!T))].sort.uniq!equal.array;
+}
+
+///
+unittest
+{
+    import mir.serde;
+
+    static struct A { double g; float d; }
+
+    @serdeProxy!A
+    static struct B {  int f; }
+
+    @serdeProxy!B
+    static struct C {  int f; }
+
+    @serdeProxy!B
+    static struct E {  int f; }
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    @serdeProxy!D
+    static struct F { int f; }
+
+    static assert (serdeGetDeserializatinKeysRecurse!F == ["c", "d", "g"]);
+}
+
+/++
+UDA used to force deserializer to initilize members in the order of their definition in the target object/structure.
+
+See_also: %(LREF SerdeOrderedDummy)
++/
+enum serdeOrderedIn;
+
+/++
+UDA used to force serializer to output members in the alphabetical order of their output keys.
++/
+enum serdeAlphabetOut;
+
+/++
+A dummy structure usefull %(LREF serdeOrderedIn) support.
++/
+struct SerdeOrderedDummy(T)
+    if (is(serdeGetFinalProxy!T == T) && (is(T == class) || is(T == struct) || is(T == union) || is(T == interface)))
+{
+    import mir.serde: serdeRequired;
+    import std.traits: hasUDA;
+    import mir.reflection: isField;
+
+    private T* __SerdeOrderedDummyTarget;
+
+    ///
+    this(T* target)
+    {
+        __SerdeOrderedDummyTarget = target;
+    }
+
+    static foreach (i, member; serdeFinalProxyDeserializableMembers!T)
+        static if (isField!(T, member))
+            mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
+        else
+            mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
+
+    /// Initialize target members
+    void serdeFinalizeWithFlags(ref scope const SerdeFlags!(typeof(this)) flags)
+    {
+        static foreach (i, member; serdeFinalProxyDeserializableMembers!T)
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, flags, member))
+                __traits(getMember, *__SerdeOrderedDummyTarget, member) = __traits(getMember, this, member);
+    }
+}
+
+///
+unittest
+{
+    import mir.serde;
+    import std.traits;
+
+    static struct S
+    {
+        private double _d;
+
+        @serdeProxy!int
+        void d(double v) @property { _d = v; }
+
+        string s;
+    }
+
+    static assert(is(typeof(SerdeOrderedDummy!S.init.d) == double), SerdeOrderedDummy!S.init.d);
+    static assert(is(typeof(SerdeOrderedDummy!S.init.s) == string));
+    static assert(hasUDA!(S.d, serdeProxy));
+    static assert(hasUDA!(SerdeOrderedDummy!S.d, serdeProxy));
+}
+
+/++
+A dummy structure passed to `.serdeFinalizeWithFlags` finalizer method.
++/
+struct SerdeFlags(T)
+{
+    static foreach(member; serdeFinalProxyDeserializableMembers!T)
+        mixin("bool " ~ member ~ ";");
+}
+
 private:
 
 auto fastLazyToUpper()(const(char)[] name)

From 979f6a086fa500a1e9796b3d13f650610e4aa779 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 7 Sep 2020 21:20:13 +0700
Subject: [PATCH 078/573] rework SerdeOrderedDummy

---
 source/mir/serde.d | 107 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 81 insertions(+), 26 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index b09050d4..cca77576 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -522,7 +522,6 @@ Deserialization member type
 +/
 template serdeDeserializationMemberType(T, string member)
 {
-    import mir.reflection: isField;
     import std.traits: Unqual, Parameters;
     T* aggregate;
     static if (isField!(T, member))
@@ -553,7 +552,6 @@ Is deserializable member
 +/
 template serdeIsDeserializable(T)
 {
-    import mir.serde: serdeGetKeysIn;
     ///
     enum bool serdeIsDeserializable(string member) = serdeGetKeysIn!(__traits(getMember, T, member)).length > 0;
 }
@@ -561,7 +559,6 @@ template serdeIsDeserializable(T)
 ///
 unittest
 {
-    import mir.serde;
 
     static struct S
     {
@@ -582,7 +579,6 @@ Final proxy type
 +/
 template serdeGetFinalProxy(T)
 {
-    import mir.serde: serdeProxy, serdeGetProxy;
     import std.traits: hasUDA;
     static if (is(T == class) || is(T == struct) || is(T == union) || is(T == interface))
     {
@@ -604,7 +600,6 @@ template serdeGetFinalProxy(T)
 ///
 unittest
 {
-    import mir.serde;
 
     @serdeProxy!string
     static struct A {}
@@ -625,7 +620,6 @@ Final proxy type deserializable members
 template serdeFinalProxyDeserializableMembers(T)
 {
     import std.meta: Filter, aliasSeqOf;
-    import mir.reflection: DeserializableMembers;
     alias P = serdeGetFinalProxy!T;
     static if (is(P == struct) || is(P == class) || is(P == interface) || is(P == union))
         enum string[] serdeFinalProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
@@ -636,7 +630,6 @@ template serdeFinalProxyDeserializableMembers(T)
 ///
 unittest
 {
-    import mir.serde;
 
     static struct A
     {
@@ -662,7 +655,6 @@ Deserialization member final proxy type
 template serdeFinalDeserializationMemberType(T, string member)
 {
     import std.traits: hasUDA;
-    import mir.serde: serdeProxy, serdeGetProxy;
     static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
     {
         alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
@@ -683,7 +675,6 @@ template serdeFinalDeserializationMemberType(T)
 ///
 unittest
 {
-    import mir.serde;
 
     static struct A
     {
@@ -724,7 +715,6 @@ template serdeDeserializationFinalProxyMemberTypes(T)
 ///
 unittest
 {
-    import mir.serde;
 
     static struct A {}
 
@@ -767,7 +757,6 @@ alias serdeDeserializationFinalProxyMemberTypesRecurse(T) = serdeDeserialization
 ///
 unittest
 {
-    import mir.serde;
 
     static struct A { double g; }
 
@@ -803,7 +792,6 @@ template serdeGetDeserializatinKeysRecurse(T)
     import mir.algorithm.iteration: uniq, equal;
     import mir.array.allocation: array;
     import mir.ndslice.sorting: sort;
-    import mir.reflection: DeserializableMembers;
     import std.meta: staticMap, aliasSeqOf;
     enum string[] serdeGetDeserializatinKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalProxyDeserializableMembers, serdeDeserializationFinalProxyMemberTypesRecurse!T))].sort.uniq!equal.array;
 }
@@ -811,7 +799,6 @@ template serdeGetDeserializatinKeysRecurse(T)
 ///
 unittest
 {
-    import mir.serde;
 
     static struct A { double g; float d; }
 
@@ -850,43 +837,107 @@ UDA used to force serializer to output members in the alphabetical order of thei
 +/
 enum serdeAlphabetOut;
 
+private enum isCompositeType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface);
+
 /++
 A dummy structure usefull %(LREF serdeOrderedIn) support.
 +/
 struct SerdeOrderedDummy(T)
     if (is(serdeGetFinalProxy!T == T) && (is(T == class) || is(T == struct) || is(T == union) || is(T == interface)))
 {
-    import mir.serde: serdeRequired;
     import std.traits: hasUDA;
-    import mir.reflection: isField;
 
-    private T* __SerdeOrderedDummyTarget;
+    private SerdeFlags!(typeof(this)) _flags;
 
-    ///
-    this(T* target)
+    this(T value)
     {
-        __SerdeOrderedDummyTarget = target;
+        static foreach (member; serdeFinalProxyDeserializableMembers!T)
+        {
+            static if (isField!(T, member))
+            {
+                __traits(getMember, this, member) = __traits(getMember, value, member);
+            }
+        }
     }
 
+public:
+
     static foreach (i, member; serdeFinalProxyDeserializableMembers!T)
+    {
         static if (isField!(T, member))
-            mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
+        {
+            static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
+            {
+                mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
+            }
+            else
+            static if (isCompositeType!(typeof(__traits(getMember, T, member))))
+            {
+                static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy))
+                {
+                    mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
+                }
+                else
+                static if (__traits(compiles, {
+                    mixin("enum SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");");
+                }))
+                {
+                    mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");");
+                }
+            }
+            else
+            {
+                mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
+            }
+        }
         else
+        {
             mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
+        }
+    }
 
     /// Initialize target members
     void serdeFinalizeWithFlags(ref scope const SerdeFlags!(typeof(this)) flags)
     {
-        static foreach (i, member; serdeFinalProxyDeserializableMembers!T)
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, flags, member))
-                __traits(getMember, *__SerdeOrderedDummyTarget, member) = __traits(getMember, this, member);
+        _flags = flags;
+    }
+
+    /// Initialize target members
+    void serdeFinalizeTarget(ref T value, ref scope SerdeFlags!T flags)
+    {
+        import core.lifetime: move;
+        static foreach (member; serdeFinalProxyDeserializableMembers!T)
+            __traits(getMember, flags, member) = __traits(getMember, _flags, member);
+        static foreach (member; serdeFinalProxyDeserializableMembers!T)
+        {{
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, _flags, member))
+            {
+                static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
+                {
+                    alias M = typeof(__traits(getMember, value, member));
+                    SerdeFlags!M memberFlags;
+                    __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags);
+                    static if(__traits(hasMember, M, "serdeFinalizeWithFlags"))
+                    {
+                        __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags);
+                    }
+                    static if(__traits(hasMember, M, "serdeFinalize"))
+                    {
+                        __traits(getMember, value, member).serdeFinalize();
+                    }
+                }
+                else
+                {
+                    __traits(getMember, value, member) = move(__traits(getMember, this, member));
+                }
+            }
+        }}
     }
 }
 
 ///
 unittest
 {
-    import mir.serde;
     import std.traits;
 
     static struct S
@@ -910,8 +961,12 @@ A dummy structure passed to `.serdeFinalizeWithFlags` finalizer method.
 +/
 struct SerdeFlags(T)
 {
-    static foreach(member; serdeFinalProxyDeserializableMembers!T)
-        mixin("bool " ~ member ~ ";");
+    static if (is(T : SerdeOrderedDummy!I, I))
+        static foreach(member; serdeFinalProxyDeserializableMembers!I)
+            mixin("bool " ~ member ~ ";");
+    else
+        static foreach(member; serdeFinalProxyDeserializableMembers!T)
+            mixin("bool " ~ member ~ ";");
 }
 
 private:

From 6cc2bf696c072f186e18e28bdf38094d26a79db6 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Mon, 7 Sep 2020 22:05:46 +0700
Subject: [PATCH 079/573] fixup

---
 source/mir/serde.d | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index cca77576..af3117b8 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -873,7 +873,9 @@ public:
             else
             static if (isCompositeType!(typeof(__traits(getMember, T, member))))
             {
-                static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy))
+                static if (
+                    hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy) ||
+                    hasUDA!(typeof(__traits(getMember, T, member)), serdeScopeStringProxy))
                 {
                     mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
                 }
@@ -884,6 +886,10 @@ public:
                 {
                     mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");");
                 }
+                else
+                {
+                    mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ ";");
+                }
             }
             else
             {

From 22f775693e9c222c017537658cd9489d96f06425 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 9 Sep 2020 15:48:05 +0700
Subject: [PATCH 080/573] externd serdeFinalizeTarget

---
 source/mir/serde.d | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index af3117b8..71bac8ea 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -847,7 +847,8 @@ struct SerdeOrderedDummy(T)
 {
     import std.traits: hasUDA;
 
-    private SerdeFlags!(typeof(this)) _flags;
+    @serdeIgnore
+    SerdeFlags!(typeof(this)) __serdeFlags;
 
     this(T value)
     {
@@ -905,18 +906,19 @@ public:
     /// Initialize target members
     void serdeFinalizeWithFlags(ref scope const SerdeFlags!(typeof(this)) flags)
     {
-        _flags = flags;
+        __serdeFlags = flags;
     }
 
     /// Initialize target members
     void serdeFinalizeTarget(ref T value, ref scope SerdeFlags!T flags)
     {
+        import std.traits: hasElaborateAssign;
         import core.lifetime: move;
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
-            __traits(getMember, flags, member) = __traits(getMember, _flags, member);
+            __traits(getMember, flags, member) = __traits(getMember, __serdeFlags, member);
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
         {{
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, _flags, member))
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -934,10 +936,17 @@ public:
                 }
                 else
                 {
-                    __traits(getMember, value, member) = move(__traits(getMember, this, member));
+                    static if (hasElaborateAssign!(typeof(__traits(getMember, this, member))))
+                        __traits(getMember, value, member) = move(__traits(getMember, this, member));
+                    else
+                        __traits(getMember, value, member) = __traits(getMember, this, member);
                 }
             }
         }}
+        static if(__traits(hasMember, T, "serdeFinalizeWithDummy"))
+        {
+            value.serdeFinalizeWithDummy(this);
+        }
     }
 }
 

From 5a69da1cf22446ad08bd6e3736497a050eb6f8c8 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 9 Sep 2020 21:44:11 +0700
Subject: [PATCH 081/573] add serdeFromDymmyByUser

---
 source/mir/serde.d | 44 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 71bac8ea..15b09ea8 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -832,6 +832,13 @@ See_also: %(LREF SerdeOrderedDummy)
 +/
 enum serdeOrderedIn;
 
+/++
+UDA used to force deserializer to skip the member final deserialization.
+A user should finalize the member deserialize using the dummy object provided in `serdeFinalizeWithDummy(ref SerdeOrderedDummy!(typeof(this)) dummy)` struct method
+and dummy method `serdeFinalizeTargetMember`.
++/
+enum serdeFromDummyByUser;
+
 /++
 UDA used to force serializer to output members in the alphabetical order of their output keys.
 +/
@@ -913,10 +920,10 @@ public:
     void serdeFinalizeTarget(ref T value, ref scope SerdeFlags!T flags)
     {
         import std.traits: hasElaborateAssign;
-        import core.lifetime: move;
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
             __traits(getMember, flags, member) = __traits(getMember, __serdeFlags, member);
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
+            static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
         {{
             if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
             {
@@ -937,7 +944,10 @@ public:
                 else
                 {
                     static if (hasElaborateAssign!(typeof(__traits(getMember, this, member))))
+                    {
+                        import core.lifetime: move;
                         __traits(getMember, value, member) = move(__traits(getMember, this, member));
+                    }
                     else
                         __traits(getMember, value, member) = __traits(getMember, this, member);
                 }
@@ -948,6 +958,38 @@ public:
             value.serdeFinalizeWithDummy(this);
         }
     }
+
+    /// Initialize target member
+    void serdeFinalizeTargetMember(string member)(ref T value)
+    {
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
+            {
+                static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
+                {
+                    alias M = typeof(__traits(getMember, value, member));
+                    SerdeFlags!M memberFlags;
+                    __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags);
+                    static if(__traits(hasMember, M, "serdeFinalizeWithFlags"))
+                    {
+                        __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags);
+                    }
+                    static if(__traits(hasMember, M, "serdeFinalize"))
+                    {
+                        __traits(getMember, value, member).serdeFinalize();
+                    }
+                }
+                else
+                {
+                    static if (hasElaborateAssign!(typeof(__traits(getMember, this, member))))
+                    {
+                        import core.lifetime: move;
+                        __traits(getMember, value, member) = move(__traits(getMember, this, member));
+                    }
+                    else
+                        __traits(getMember, value, member) = __traits(getMember, this, member);
+                }
+            }
+    }
 }
 
 ///

From 149a6b53d173981ab45dae7c53724205492b253d Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 11 Sep 2020 18:40:50 +0700
Subject: [PATCH 082/573] extend formatting

---
 source/mir/appender.d     | 63 ++++++++++++++++++++++++++++
 source/mir/format.d       | 80 +++++++++++++++++++++++++++++++----
 source/mir/parse.d        | 87 +++++++++++++++++++++++++--------------
 source/mir/serde.d        |  6 +--
 source/mir/small_string.d | 19 ++-------
 5 files changed, 196 insertions(+), 59 deletions(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index 6772f833..9cb8de1f 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -267,3 +267,66 @@ version (mir_test) unittest
     buf.popBackN(2);
     assert(buf.data == "cs");
 }
+
+///
+struct UnsafeArrayBuffer(T)
+{
+    import std.traits: isImplicitlyConvertible;
+
+    ///
+    T[] buffer;
+    ///
+    size_t length;
+
+    ///
+    void put(T a)
+    {
+        import core.lifetime: move;
+        assert(length < buffer.length);
+        buffer[length++] = move(a);
+    }
+
+    static if (isImplicitlyConvertible!(const T, T))
+        private alias E = const T;
+    else
+        private alias E = T;
+
+    ///
+    void put(E[] a)
+    {
+        import core.lifetime: move;
+        assert(buffer.length >= a.length + length);
+        buffer[length .. length + a.length] = a;
+        length += a.length;
+    }
+
+    ///
+    inout(T)[] data() inout @property @safe scope
+    {
+        return buffer[0 .. length];
+    }
+
+    ///
+    void popBackN(size_t n)
+    {
+        sizediff_t t = length - n;
+        if (t < 0)
+            assert(0, "UnsafeBuffer.popBackN: n is too large.");
+        buffer[t .. length]._mir_destroy;
+        length = t;
+    }
+}
+
+///
+@safe pure nothrow @nogc
+version (mir_test) unittest
+{
+    char[4] array;
+    auto buf = UnsafeArrayBuffer!char(array);
+    buf.put('c');
+    buf.put("str");
+    assert(buf.data == "cstr");
+
+    buf.popBackN(2);
+    assert(buf.data == "cs");
+}
diff --git a/source/mir/format.d b/source/mir/format.d
index 7e89f943..78d80e0a 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -6,8 +6,74 @@ Authors:   Ilya Yaroshenko
 +/
 module mir.format;
 
+/// `mir.conv: to` extension.
+version(mir_test)
+@safe pure @nogc
+unittest
+{
+    import mir.conv: to;
+    import mir.small_string;
+    alias S = SmallString!32;
+
+    assert(123.0.to!S == "123");
+    assert(123.to!(immutable S) == "123");
+    assert(true.to!S == "true");
+    assert(true.to!string == "true");
+
+    assert((cast(S)"str")[] == "str");
+
+    assert(S("str").to!(const(char)[]) == "str"); // scope result
+    assert(S("str").to!(char[]) == "str"); // scope result
+}
+
+/// ditto
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.conv: to;
+    import mir.small_string;
+    alias S = SmallString!32;
+
+    assert(123.0.to!string == "123");
+    assert(123.to!(char[]) == "123");
+
+    assert(S("str").to!string == "str"); // GC allocated result
+}
+
 import std.traits;
 
+/// Concatenated string results
+string text(string separator = "", A...)(auto ref A args)
+        if (A.length > 0)
+{
+    static if (A.length == 1)
+    {
+        import mir.functional: forward;
+        import mir.conv: to;
+        return to!string(forward!args);
+    }
+    else
+    {
+        import mir.appender: ScopedBuffer;
+        ScopedBuffer!char buffer;
+        foreach (i, ref arg; args)
+        {
+            buffer.print(arg);
+            static if (separator.length && i + 1 < args.length)
+                buffer.printStaticStringInternal!(char, separator);
+        }
+        return buffer.data.idup;
+    }
+}
+
+///
+@safe pure nothrow unittest
+{
+    assert(text("str ", true, " ", 100, " ", 124.1) == "str true 100 124.1");
+    assert(text!" "("str", true, 100, 124.1) == "str true 100 124.1");
+}
+
 import mir.format_impl;
 
 ///
@@ -44,7 +110,7 @@ alias dstringBuf = _stringBuf!dchar;
 mixin template StreamFormatOp(C)
 {
     ///
-    ref typeof(this) opBinary(string op : "<<", T)(scope ref const T c) scope
+    ref typeof(this) opBinary(string op : "<<", T)(ref const T c) scope
     {
         return print!C(this, c);
     }
@@ -497,7 +563,7 @@ ref W print(C = char, W, T)(scope return ref W w, const T c)
 
 /// ditto
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, scope ref const T c)
+ref W print(C = char, W, T)(scope return ref W w, ref const T c)
     if (is(T == struct) || is(T == union))
 {
     static if (__traits(hasMember, T, "toString"))
@@ -513,7 +579,7 @@ ref W print(C = char, W, T)(scope return ref W w, scope ref const T c)
         else
         static if (is(typeof(w.put(c.toString))))
             w.put(c.toString);
-        else static assert(0, T.stringof ~ ".toString definition is wrong: 'const scope' qualifier may be missing.");
+        else static assert(0, T.stringof ~ ".toString definition is wrong: 'const' qualifier may be missing.");
         return w;
     }
     else
@@ -638,10 +704,10 @@ ref W print(C = char, W, T)(scope return ref W w, scope const T c)
 @safe pure nothrow
 version (mir_test) unittest
 {
-    static class A { scope void toString(C, W)(scope ref W w) const { w.put(C('a')); } }
-    static class S { scope void toString(W)(scope ref W w) const { w.put("s"); } }
-    static class D { scope void toString(Dg)(scope Dg sink) const { sink("d"); } }
-    static class F { scope const(char)[] toString()() const return { return "f"; } }
+    static class A { void toString(C, W)(scope ref W w) const { w.put(C('a')); } }
+    static class S { void toString(W)(scope ref W w) const { w.put("s"); } }
+    static class D { void toString(Dg)(scope Dg sink) const { sink("d"); } }
+    static class F { const(char)[] toString()() const return { return "f"; } }
     static class G { const(char)[] s = "g"; alias s this; }
 
     import mir.appender: ScopedBuffer;
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 3ebe49b7..e7ecb374 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -7,6 +7,22 @@ Copyright: Symmetry Investments and Kaleidic Associates
 +/
 module mir.parse;
 
+/// `mir.conv: to` extension.
+version(mir_test)
+@safe pure @nogc
+unittest
+{
+    import mir.conv: to;
+
+    assert("123.0".to!double == 123);
+    assert("123".to!int == 123);
+
+    import mir.small_string;
+    alias S = SmallString!32;
+    assert(S("123.0").to!double == 123);
+    assert(S("123").to!(immutable int) == 123);
+}
+
 import mir.primitives;
 import std.range.primitives: isInputRange;
 import std.traits: isMutable, isFloatingPoint, isSomeChar;
@@ -131,76 +147,83 @@ version(mir_test)
 }
 
 /++
-Integer parsing utilities.
+Single character parsing utilities.
 
 Returns: true if success and false otherwise.
 +/
-bool parse(T : byte, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && !__traits(isUnsigned, T))
+bool parse(T, Range)(scope ref Range r, scope ref T value)
+    if (isInputRange!Range && isSomeChar!T)
 {
-    int lvalue;
-    auto ret = parse!(int, Range)(r, lvalue);
-    value = cast(byte) lvalue;
-    return ret && value == lvalue;
+    if (r.empty)
+        return false;
+    value = r.front;
+    r.popFront;
+    return true;
 }
-/// ditto
-bool parse(T : short, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && !__traits(isUnsigned, T))
+
+///
+version(mir_test) @safe pure nothrow @nogc
+unittest
+{
+    auto s = "str";
+    char c;
+    assert(parse(s, c));
+    assert(c == 's');
+    assert(s == "tr");
+}
+
+/++
+Integer parsing utilities.
+
+Returns: true if success and false otherwise.
++/
+bool parse(T, Range)(scope ref Range r, scope ref T value)
+    if ((is(T == byte) || is(T == short)) && isInputRange!Range && !__traits(isUnsigned, T))
 {
     int lvalue;
     auto ret = parse!(int, Range)(r, lvalue);
-    value = cast(short) lvalue;
+    value = cast(T) lvalue;
     return ret && value == lvalue;
 }
 
 /// ditto
 pragma(inline, false)
-bool parse(T : int, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && !__traits(isUnsigned, T))
+bool parse(T, Range)(scope ref Range r, scope ref T value)
+    if (is(T == int) && isInputRange!Range && !__traits(isUnsigned, T))
 {
     return parseSignedImpl!(int, Range)(r, value);
 }
 
 /// ditto
 pragma(inline, false)
-bool parse(T : long, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && !__traits(isUnsigned, T))
+bool parse(T, Range)(scope ref Range r, scope ref T value)
+    if (is(T == long) && isInputRange!Range && !__traits(isUnsigned, T))
 {
     return parseSignedImpl!(long, Range)(r, value);
 }
 
 /// ditto
-bool parse(T : ubyte, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && __traits(isUnsigned, T))
-{
-    uint lvalue;
-    auto ret = parse!(uint, Range)(r, lvalue);
-    value = cast(ubyte) lvalue;
-    return ret && value == lvalue;
-}
-
-/// ditto
-bool parse(T : ushort, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && __traits(isUnsigned, T))
+bool parse(T, Range)(scope ref Range r, scope ref T value)
+    if ((is(T == ubyte) || is(T == ushort)) && isInputRange!Range && __traits(isUnsigned, T))
 {
     uint lvalue;
     auto ret = parse!(uint, Range)(r, lvalue);
-    value = cast(ushort) lvalue;
+    value = cast(T) lvalue;
     return ret && value == lvalue;
 }
 
 /// ditto
 pragma(inline, false)
-bool parse(T : uint, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && __traits(isUnsigned, T))
+bool parse(T, Range)(scope ref Range r, scope ref T value)
+    if (is(T == uint) && isInputRange!Range && __traits(isUnsigned, T))
 {
     return parseUnsignedImpl!(uint, Range)(r, value);
 }
 
 /// ditto
 pragma(inline, false)
-bool parse(T : ulong, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && __traits(isUnsigned, T))
+bool parse(T, Range)(scope ref Range r, scope ref T value)
+    if (is(T == ulong) && isInputRange!Range && __traits(isUnsigned, T))
 {
     return parseUnsignedImpl!(ulong, Range)(r, value);
 }
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 15b09ea8..a4b7e028 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -276,7 +276,7 @@ Can be applied only to fields that can be constructed from strings.
 Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation.
 Use this attributes only for strings that would not be used after the input data deallocation.
 +/
-enum serdeScopeStringProxy;
+deprecated("use @serdeScoped @serdeProxy!(const(char)[]) instead") enum serdeScopeStringProxy;
 
 /++
 Attributes to out conditional ignore field during serialization.
@@ -881,9 +881,7 @@ public:
             else
             static if (isCompositeType!(typeof(__traits(getMember, T, member))))
             {
-                static if (
-                    hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy) ||
-                    hasUDA!(typeof(__traits(getMember, T, member)), serdeScopeStringProxy))
+                static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy))
                 {
                     mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";");
                 }
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 0ff61936..c037570a 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -13,7 +13,7 @@ Authors:   Ilya Yaroshenko
 +/
 module mir.small_string;
 
-import mir.serde: serdeScopeStringProxy;
+import mir.serde: serdeScoped, serdeProxy;
 
 private extern (C) @system nothrow @nogc pure size_t strnlen_s(scope const char* s, size_t n);
 
@@ -26,7 +26,7 @@ extern(C++, "mir"):
 /++
 Self-contained generic Small String implementaton.
 +/
-@serdeScopeStringProxy
+@serdeScoped @serdeProxy!(const(char)[])
 struct SmallString(uint maxLength)
     if (maxLength)
 {
@@ -37,20 +37,7 @@ struct SmallString(uint maxLength)
     // maxLength bytes
     char[maxLength] _data = '\0';
 
-extern(D):
-
-	static SmallString deserialize(S)(S data)
-	{
-        import asdf.serialization: deserialize;
-        return SmallString(data.deserialize!(const(char)[]));
-	}
-
-	void serialize(S)(ref S serializer)
-	{
-		serializer.putValue(asArray);
-	}
-
-@safe pure @nogc:
+extern(D) @safe pure @nogc:
 
     /// Constructor
     this(typeof(null))

From cff865a766cca67b070ef03cd5b4c1af4baf3e6f Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 11 Sep 2020 18:51:31 +0700
Subject: [PATCH 083/573] update deps

---
 dub.sdl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index e46a06bb..60197672 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "Copyright © 2016 - 2019, Ilya Yaroshenko; see also information per file."
 license "BSL-1.0"
 
-dependency "mir-core" version=">=1.1.9"
+dependency "mir-core" version=">=1.1.12"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"

From 7392616c74fafe96f886978bdf8c8f1d738bb267 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 11 Sep 2020 19:13:40 +0700
Subject: [PATCH 084/573] update serdeFinalizeTarget

---
 source/mir/serde.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index a4b7e028..eec26e3b 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -923,7 +923,7 @@ public:
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
             static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
         {{
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
+            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -960,7 +960,7 @@ public:
     /// Initialize target member
     void serdeFinalizeTargetMember(string member)(ref T value)
     {
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
+            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {

From 288cd06e227796f173bc096a7ea2854b41a526a2 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 11 Sep 2020 20:49:09 +0700
Subject: [PATCH 085/573] fixup unittests

---
 source/mir/format.d | 5 +++--
 source/mir/parse.d  | 1 +
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 78d80e0a..33e018b8 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -22,8 +22,9 @@ unittest
 
     assert((cast(S)"str")[] == "str");
 
-    assert(S("str").to!(const(char)[]) == "str"); // scope result
-    assert(S("str").to!(char[]) == "str"); // scope result
+    auto str = S("str");
+    assert(str.to!(const(char)[]) == "str"); // scope result
+    assert(str.to!(char[]) == "str"); // scope result
 }
 
 /// ditto
diff --git a/source/mir/parse.d b/source/mir/parse.d
index e7ecb374..d779af63 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -16,6 +16,7 @@ unittest
 
     assert("123.0".to!double == 123);
     assert("123".to!int == 123);
+    assert("123".to!byte == 123);
 
     import mir.small_string;
     alias S = SmallString!32;

From dfb58a8ee70a36870f7b65a484d071eaa12ee34c Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sat, 12 Sep 2020 12:10:02 +0700
Subject: [PATCH 086/573] fix SerdeOrderedDummy

---
 source/mir/serde.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index eec26e3b..ff6c506f 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -863,7 +863,8 @@ struct SerdeOrderedDummy(T)
         {
             static if (isField!(T, member))
             {
-                __traits(getMember, this, member) = __traits(getMember, value, member);
+                static if (__traits(compiles, {__traits(getMember, this, member) = __traits(getMember, value, member);}))
+                    __traits(getMember, this, member) = __traits(getMember, value, member);
             }
         }
     }

From ccaebed62d09450d83c69a31406f895588197ff7 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 15 Sep 2020 23:22:27 +0700
Subject: [PATCH 087/573] revert serdeOptional support

---
 source/mir/serde.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index ff6c506f..0a5db88f 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -924,7 +924,7 @@ public:
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
             static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
         {{
-            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -961,7 +961,7 @@ public:
     /// Initialize target member
     void serdeFinalizeTargetMember(string member)(ref T value)
     {
-            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {

From 570eb2d1770dd26b1f2442d61573842d4c99b18c Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 15 Sep 2020 23:24:07 +0700
Subject: [PATCH 088/573] add serdeOptional support

---
 source/mir/serde.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 0a5db88f..ff6c506f 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -924,7 +924,7 @@ public:
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
             static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
         {{
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
+            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -961,7 +961,7 @@ public:
     /// Initialize target member
     void serdeFinalizeTargetMember(string member)(ref T value)
     {
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
+            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {

From 23620c6026a03b55cbd9a675f80aafd2f84e0366 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 17 Sep 2020 21:32:37 +0700
Subject: [PATCH 089/573] version(mir_test) for mir.date

---
 source/mir/date.d | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/source/mir/date.d b/source/mir/date.d
index 7a8897c0..43013b3c 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -32,6 +32,7 @@ module mir.date;
 import std.range.primitives : isOutputRange;
 import std.traits : isSomeChar, Unqual;
 
+version(mir_test)
 version(D_Exceptions)
 version(unittest) import std.exception : assertThrown;
 
@@ -2152,6 +2153,7 @@ version (mir_test)
     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
 }
 
+version (mir_test)
 @safe unittest
 {
     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
@@ -2257,6 +2259,7 @@ do
     }
 }
 
+version (mir_test)
 @safe unittest
 {
     // Test A.D.
@@ -2365,6 +2368,7 @@ string monthToString(Month month) @safe pure @nogc nothrow
     return _monthNames[month - Month.jan];
 }
 
+version (mir_test)
 @safe unittest
 {
     assert(monthToString(Month.jan) == "Jan");
@@ -2381,6 +2385,7 @@ string monthToString(Month month) @safe pure @nogc nothrow
     assert(monthToString(Month.dec) == "Dec");
 }
 
+version (mir_test)
 version(unittest)
 {
     // All of these helper arrays are sorted in ascending order.

From aa160940825773626ff122499c376fc887b12922 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 18 Sep 2020 08:20:27 +0700
Subject: [PATCH 090/573] backport

---
 source/mir/serde.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index ff6c506f..0a5db88f 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -924,7 +924,7 @@ public:
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
             static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
         {{
-            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -961,7 +961,7 @@ public:
     /// Initialize target member
     void serdeFinalizeTargetMember(string member)(ref T value)
     {
-            if (!hasUDA!(__traits(getMember, T, member), serdeOptional) || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {

From 116207043b2b3f55978423865837a8ad993a08ad Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 18 Sep 2020 10:02:11 +0700
Subject: [PATCH 091/573] add serdeRealOrderedDummy and unify SerdeOrderedDummy

---
 source/mir/serde.d | 30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 0a5db88f..3183b6d4 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -828,10 +828,23 @@ unittest
 /++
 UDA used to force deserializer to initilize members in the order of their definition in the target object/structure.
 
-See_also: %(LREF SerdeOrderedDummy)
+The attribute force deserializer to create a dummy type (recursively), initializer its fields and then assign them to
+to the object members (fields and setters) in the order of their definition.
+
+See_also: $(LREF SerdeOrderedDummy), $(LREF serdeRealOrderedIn).
 +/
 enum serdeOrderedIn;
 
+/++
+UDA used to force deserializer to initilize members in the order of their definition in the target object/structure.
+
+Unlike $(LREF serdeOrderedIn) `serdeRealOrderedDummy` force deserialzier to iterate all DOM keys for each object deserialization member.
+It is slower but more universal approach.
+
+See_also: $(LREF serdeOrderedIn).
++/
+enum serdeRealOrderedDummy;
+
 /++
 UDA used to force deserializer to skip the member final deserialization.
 A user should finalize the member deserialize using the dummy object provided in `serdeFinalizeWithDummy(ref SerdeOrderedDummy!(typeof(this)) dummy)` struct method
@@ -847,9 +860,9 @@ enum serdeAlphabetOut;
 private enum isCompositeType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface);
 
 /++
-A dummy structure usefull %(LREF serdeOrderedIn) support.
+A dummy structure usefull $(LREF serdeOrderedIn) support.
 +/
-struct SerdeOrderedDummy(T)
+struct SerdeOrderedDummy(T, bool __optionalByDefault = false)
     if (is(serdeGetFinalProxy!T == T) && (is(T == class) || is(T == struct) || is(T == union) || is(T == interface)))
 {
     import std.traits: hasUDA;
@@ -857,7 +870,12 @@ struct SerdeOrderedDummy(T)
     @serdeIgnore
     SerdeFlags!(typeof(this)) __serdeFlags;
 
-    this(T value)
+    static if (__optionalByDefault)
+        alias __serdeOptionalRequired = serdeRequired;
+    else
+        alias __serdeOptionalRequired = serdeOptional;
+
+    this()(T value)
     {
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
         {
@@ -924,7 +942,7 @@ public:
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
             static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
         {{
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -961,7 +979,7 @@ public:
     /// Initialize target member
     void serdeFinalizeTargetMember(string member)(ref T value)
     {
-            if (hasUDA!(__traits(getMember, T, member), serdeRequired) || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {

From 4f82eee32698eb4e78de437be530acebd771bbae Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 18 Sep 2020 10:34:47 +0700
Subject: [PATCH 092/573] add SerdeException and deserializeValueMemberImpl

---
 source/mir/serde.d | 137 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 136 insertions(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 3183b6d4..981e56b6 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -4,6 +4,43 @@ import mir.functional: naryFun;
 import mir.reflection;
 import std.traits: TemplateArgsOf, EnumMembers, hasUDA;
 
+version (D_Exceptions)
+{
+    /++
+    Serde Exception
+    +/
+    class SerdeException : Exception
+    {
+        ///
+        this(
+            string msg,
+            string file = __FILE__,
+            size_t line = __LINE__,
+            Throwable next = null) pure nothrow @nogc @safe 
+        {
+            super(msg, file, line, next);
+        }
+
+        ///
+        this(
+            string msg,
+            Throwable next,
+            string file = __FILE__,
+            size_t line = __LINE__,
+            ) pure nothrow @nogc @safe 
+        {
+            this(msg, file, line, next);
+        }
+
+        SerdeException toMutable() @trusted pure nothrow @nogc const
+        {
+            return cast() this;
+        }
+
+        alias toMutable this;
+    }
+}
+
 /++
 Attribute for key overloading during Serialization and Deserialization.
 The first argument overloads the key value during serialization unless `serdeKeyOut` is given.
@@ -843,7 +880,7 @@ It is slower but more universal approach.
 
 See_also: $(LREF serdeOrderedIn).
 +/
-enum serdeRealOrderedDummy;
+enum serdeRealOrderedIn;
 
 /++
 UDA used to force deserializer to skip the member final deserialization.
@@ -1043,6 +1080,104 @@ struct SerdeFlags(T)
             mixin("bool " ~ member ~ ";");
 }
 
+/++
++/
+template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped)
+{
+    ///
+    SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, ref T value, ref SerdeFlags!T requiredFlags, ref Context args)
+    {
+        import mir.conv: to;
+        import std.traits: Select, Unqual, Parameters;
+
+        static immutable excm(string member) = new SerdeException("ASDF deserialisation: multiple keys for member '" ~ member ~ "' in " ~ T.stringof ~ " are not allowed.");
+
+        static if (!hasUDA!(__traits(getMember, value, member), serdeAllowMultiple))
+            if (__traits(getMember, requiredFlags, member))
+                return excm!member;
+
+        __traits(getMember, requiredFlags, member) = true;
+
+        static if(!__traits(compiles, {__traits(getMember, value, member) = __traits(getMember, value, member);}))
+        {
+            alias Type = Unqual!(Parameters!(__traits(getMember, value, member)));
+        }
+        else
+        {
+            alias Type = typeof(__traits(getMember, value, member));
+        }
+
+        static if(hasUDA!(__traits(getMember, value, member), serdeLikeList))
+        {
+            static assert(hasUDA!(__traits(getMember, value, member), serdeProxy), V.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
+            serdeGetProxy!(__traits(getMember, value, member)) proxy;
+            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, proxy));
+            alias Fun = Select!(S, deserializeScoped, deserializeValue);
+            foreach(v; data.byElement)
+            {
+                proxy = proxy.init;
+                if (auto exc = Fun(v, proxy, args))
+                    return exc;
+                __traits(getMember, value, member).put(proxy);
+            }
+        }
+        else
+        static if(hasUDA!(__traits(getMember, value, member), serdeLikeStruct))
+        {
+            static assert(hasUDA!(__traits(getMember, value, member), serdeProxy), V.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
+            serdeGetProxy!(__traits(getMember, value, member)) proxy;
+            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, proxy));
+            alias Fun = Select!(S, deserializeScoped, deserializeValue);
+            foreach(v; data.byKeyValue)
+            {
+                proxy = proxy.init;
+                if (auto exc = Fun(v.value, proxy, args))
+                    return exc;
+                __traits(getMember, value, member)[v.key.idup] = proxy;
+            }
+        }
+        else
+        static if(hasUDA!(__traits(getMember, value, member), serdeProxy))
+        {
+            serdeGetProxy!(__traits(getMember, value, member)) proxy;
+            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, proxy));
+            alias Fun = Select!(S, deserializeScoped, deserializeValue);
+
+            if (auto exc = Fun(data, proxy))
+                return exc;
+            __traits(getMember, value, member) = proxy.to!Type;
+        }
+        else
+        static if(__traits(compiles, {__traits(getMember, value, member) = __traits(getMember, value, member);}) && __traits(compiles, {auto ptr = &__traits(getMember, value, member); }))
+        {
+            enum S = hasUDA!(__traits(getMember, value, member), serdeLikeStruct) && __traits(compiles, deserializeScoped(data, __traits(getMember, value, member)));
+            alias Fun = Select!(S, deserializeScoped, deserializeValue);
+
+            if (auto exc = Fun(data, __traits(getMember, value, member)))
+                return exc;
+        }
+        else
+        {
+            Type val;
+
+            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, val));
+            alias Fun = Select!(S, deserializeScoped, deserializeValue);
+
+            if (auto exc = Fun(data, val))
+                return exc;
+            __traits(getMember, value, member) = val;
+        }
+
+        static if(hasUDA!(__traits(getMember, value, member), serdeTransformIn))
+        {
+            alias f = serdeGetTransformIn!(__traits(getMember, value, member));
+            f(__traits(getMember, value, member));
+        }
+
+        return null;
+    }
+}
+
 private:
 
 auto fastLazyToUpper()(const(char)[] name)

From 80e7d170d2feb3e498973080007a5c2e92ae211b Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 18 Sep 2020 13:11:17 +0700
Subject: [PATCH 093/573] rework serdeDeserializationMemberType and
 deserializeValueMemberImpl

---
 source/mir/serde.d | 122 ++++++++++++++++++++++++---------------------
 1 file changed, 64 insertions(+), 58 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 981e56b6..5d6018a5 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -566,7 +566,7 @@ template serdeDeserializationMemberType(T, string member)
         alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member));
     }
     else
-    static if (__traits(compiles, &__traits(getMember, *aggregate, member)()))
+    static if (__traits(compiles, &__traits(getMember, *aggregate, member)()) || __traits(getOverloads, *aggregate, member).length > 1)
     {
         alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member)());
     }
@@ -986,11 +986,11 @@ public:
                     alias M = typeof(__traits(getMember, value, member));
                     SerdeFlags!M memberFlags;
                     __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags);
-                    static if(__traits(hasMember, M, "serdeFinalizeWithFlags"))
+                    static if (__traits(hasMember, M, "serdeFinalizeWithFlags"))
                     {
                         __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags);
                     }
-                    static if(__traits(hasMember, M, "serdeFinalize"))
+                    static if (__traits(hasMember, M, "serdeFinalize"))
                     {
                         __traits(getMember, value, member).serdeFinalize();
                     }
@@ -1007,7 +1007,7 @@ public:
                 }
             }
         }}
-        static if(__traits(hasMember, T, "serdeFinalizeWithDummy"))
+        static if (__traits(hasMember, T, "serdeFinalizeWithDummy"))
         {
             value.serdeFinalizeWithDummy(this);
         }
@@ -1023,11 +1023,11 @@ public:
                     alias M = typeof(__traits(getMember, value, member));
                     SerdeFlags!M memberFlags;
                     __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags);
-                    static if(__traits(hasMember, M, "serdeFinalizeWithFlags"))
+                    static if (__traits(hasMember, M, "serdeFinalizeWithFlags"))
                     {
                         __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags);
                     }
-                    static if(__traits(hasMember, M, "serdeFinalize"))
+                    static if (__traits(hasMember, M, "serdeFinalize"))
                     {
                         __traits(getMember, value, member).serdeFinalize();
                     }
@@ -1079,16 +1079,40 @@ struct SerdeFlags(T)
         static foreach(member; serdeFinalProxyDeserializableMembers!T)
             mixin("bool " ~ member ~ ";");
 }
-
+        
 /++
 +/
 template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped)
 {
     ///
-    SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, ref T value, ref SerdeFlags!T requiredFlags, ref Context args)
+    SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, ref T value, ref SerdeFlags!T requiredFlags, ref Context context)
     {
+        import core.lifetime: move;
         import mir.conv: to;
-        import std.traits: Select, Unqual, Parameters;
+
+        enum likeList = hasUDA!(__traits(getMember, value, member), serdeLikeList);
+        enum likeStruct  = hasUDA!(__traits(getMember, value, member), serdeLikeStruct);
+        enum hasProxy = hasUDA!(__traits(getMember, value, member), serdeProxy);
+        enum hasScoped = hasUDA!(__traits(getMember, value, member), serdeScoped);
+
+        static assert (likeList + likeStruct <= 1, T.stringof ~ "." ~ member ~ " can't have both @serdeLikeStruct and @serdeLikeList attributes");
+        static assert (hasProxy >= likeStruct, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
+        static assert (hasProxy >= likeList, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
+
+        alias Member = serdeDeserializationMemberType!(T, member);
+
+        static if (hasProxy)
+            alias Temporal = serdeGetProxy!(__traits(getMember, value, member));
+        else
+            alias Temporal = Member;
+
+        static if (hasScoped)
+            static if (__traits(compiles, { Temporal temporal; deserializeScoped(data, temporal); }))
+                alias impl = deserializeScoped;
+            else
+                alias impl = deserializeValue;
+        else
+            alias impl = deserializeValue;
 
         static immutable excm(string member) = new SerdeException("ASDF deserialisation: multiple keys for member '" ~ member ~ "' in " ~ T.stringof ~ " are not allowed.");
 
@@ -1098,80 +1122,62 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
 
         __traits(getMember, requiredFlags, member) = true;
 
-        static if(!__traits(compiles, {__traits(getMember, value, member) = __traits(getMember, value, member);}))
+        static if (likeList)
         {
-            alias Type = Unqual!(Parameters!(__traits(getMember, value, member)));
-        }
-        else
-        {
-            alias Type = typeof(__traits(getMember, value, member));
-        }
-
-        static if(hasUDA!(__traits(getMember, value, member), serdeLikeList))
-        {
-            static assert(hasUDA!(__traits(getMember, value, member), serdeProxy), V.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
-            serdeGetProxy!(__traits(getMember, value, member)) proxy;
-            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, proxy));
-            alias Fun = Select!(S, deserializeScoped, deserializeValue);
-            foreach(v; data.byElement)
+            foreach(elem; data.byElement)
             {
-                proxy = proxy.init;
-                if (auto exc = Fun(v, proxy, args))
+                Temporal temporal;
+                if (auto exc = impl(elem, temporal, context))
                     return exc;
-                __traits(getMember, value, member).put(proxy);
+                __traits(getMember, value, member).put(move(temporal));
             }
         }
         else
-        static if(hasUDA!(__traits(getMember, value, member), serdeLikeStruct))
+        static if (likeStruct)
         {
-            static assert(hasUDA!(__traits(getMember, value, member), serdeProxy), V.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
-            serdeGetProxy!(__traits(getMember, value, member)) proxy;
-            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, proxy));
-            alias Fun = Select!(S, deserializeScoped, deserializeValue);
-            foreach(v; data.byKeyValue)
+            foreach(v; data.byKeyValue(context))
             {
-                proxy = proxy.init;
-                if (auto exc = Fun(v.value, proxy, args))
+                Temporal temporal;
+                if (auto exc = impl(v.value, temporal, context))
                     return exc;
-                __traits(getMember, value, member)[v.key.idup] = proxy;
+                __traits(getMember, value, member)[v.key.idup] = move(temporal);
             }
         }
         else
-        static if(hasUDA!(__traits(getMember, value, member), serdeProxy))
+        static if (hasProxy)
         {
-            serdeGetProxy!(__traits(getMember, value, member)) proxy;
-            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, proxy));
-            alias Fun = Select!(S, deserializeScoped, deserializeValue);
-
-            if (auto exc = Fun(data, proxy))
+            Temporal temporal;
+            if (auto exc = impl(data, temporal, context))
                 return exc;
-            __traits(getMember, value, member) = proxy.to!Type;
+            __traits(getMember, value, member) = to!(serdeDeserializationMemberType!(T, member))(move(temporal));
         }
         else
-        static if(__traits(compiles, {__traits(getMember, value, member) = __traits(getMember, value, member);}) && __traits(compiles, {auto ptr = &__traits(getMember, value, member); }))
+        static if (isField!(T, member))
         {
-            enum S = hasUDA!(__traits(getMember, value, member), serdeLikeStruct) && __traits(compiles, deserializeScoped(data, __traits(getMember, value, member)));
-            alias Fun = Select!(S, deserializeScoped, deserializeValue);
-
-            if (auto exc = Fun(data, __traits(getMember, value, member)))
+            if (auto exc = impl(data, __traits(getMember, value, member), context))
                 return exc;
         }
         else
         {
-            Type val;
-
-            enum S = hasUDA!(__traits(getMember, value, member), serdeScoped) && __traits(compiles, deserializeScoped(data, val));
-            alias Fun = Select!(S, deserializeScoped, deserializeValue);
-
-            if (auto exc = Fun(data, val))
+            Member temporal;
+            if (auto exc = impl(data, temporal, context))
                 return exc;
-            __traits(getMember, value, member) = val;
+            __traits(getMember, value, member) = move(temporal);
         }
 
-        static if(hasUDA!(__traits(getMember, value, member), serdeTransformIn))
+        static if (hasUDA!(__traits(getMember, value, member), serdeTransformIn))
         {
-            alias f = serdeGetTransformIn!(__traits(getMember, value, member));
-            f(__traits(getMember, value, member));
+            alias transform = serdeGetTransformIn!(__traits(getMember, value, member));
+            static if (isField!(T, member))
+            {
+                transform(__traits(getMember, value, member));
+            }
+            else
+            {
+                auto temporal = __traits(getMember, value, member);
+                transform(temporal);
+                __traits(getMember, value, member) = move(temporal);
+            }
         }
 
         return null;

From fdb3def7326592f9c89e98f405338f2a144098ad Mon Sep 17 00:00:00 2001
From: Ilya <ilyayaroshenko@gmail.com>
Date: Sun, 20 Sep 2020 22:30:14 +0700
Subject: [PATCH 094/573] extend Variant

---
 source/mir/variant.d | 159 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 150 insertions(+), 9 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 3af71f32..8d2bcf85 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -16,6 +16,24 @@ version (D_Exceptions)
 
 private enum int alignOf(T) = T.alignof;
 
+private enum hasToHash(T) = __traits(hasMember, T, "toHash");
+private enum hasToHash(T) = __traits(hasMember, T, "opCmp");
+private enum hasToHash(T) = __traits(hasMember, T, "opEquals");
+private enum isPOD(T) = __traits(isPOD, T);
+
+private template LargestAlignOf(T...)
+{
+    static if (T.length == 0)
+        enum size_t LargestAlignOf = 0;
+    else
+    {
+        enum tail = LargestAlignOf!(T[1 .. $]);
+        enum size_t LargestAlignOf = T[0].alignof > tail ? T[0].alignof : tail;
+    }
+}
+
+private enum Sizeof(T) = T.sizeof;
+
 /++
 Variant Type (aka Algebraic Type) with clever member access.
 
@@ -24,16 +42,16 @@ Compatible with BetterC mode.
 struct Variant(Types...)
     if (Types.length)
 {
-    import mir.utility: max, swap;
     import mir.conv: emplaceRef;
-    import std.meta: anySatisfy, staticMap;
-    import std.traits: Largest, hasElaborateDestructor, hasElaborateAssign, hasElaborateCopyConstructor;
+    import mir.utility: max, swap;
+    import std.meta: anySatisfy, allSatisfy, templateOr, staticMap;
+    import std.traits: Largest, hasElaborateDestructor, hasElaborateAssign, hasElaborateCopyConstructor, isEqualityComparable, isOrderingComparable;
 
     private alias _Types = Types;
 
     align(max(4, staticMap!(alignOf, Types)))
-    private ubyte[Largest!Types.sizeof] payload;
     private uint type; // 0 for unininit value, index = type - 1
+    private align(LargestAlignOf!Types) ubyte[Largest!Types.sizeof] payload;
     private enum hasDestructor = anySatisfy!(hasElaborateDestructor, Types);
 
     static if (hasDestructor)
@@ -123,10 +141,13 @@ struct Variant(Types...)
                 else
                     assert(0, variantNulllExceptionMsg);
             }
-            version(D_Exceptions)
-                throw variantException;
             else
-                assert(0, variantExceptionMsg);
+            {
+                version(D_Exceptions)
+                    throw variantException;
+                else
+                    assert(0, variantExceptionMsg);
+            }
         }
         return trustedGet!T;
     }
@@ -154,12 +175,82 @@ struct Variant(Types...)
     {
         return type;
     }
+
+    static if (allSatisfy!(templateOr!(isPOD, hasToHash), Types))
+    /++
+    +/
+    size_t toHash()
+    {
+        static if (this.sizeof <= 16)
+        {
+            return hashOf(payload, type);
+        }
+        else
+        static if (allSatisfy!(isPOD, Types))
+        {
+            static immutable sizes = [size_t(0), staticMap!(Sizeof, Types)];
+            return hashOf(payload[0 .. sizes[type]], type);
+        }
+        else
+        switch (type)
+        {
+            static foreach (i, T; Types)
+            {
+                case i + 1:
+                    return hashOf(trustedGet!T, type);
+            }
+            default:
+                return 0;
+        }
+    }
+
+    static if (allSatisfy!(isEqualityComparable, Types))
+    /++
+    +/
+    auto opEquals(ref const typeof(this) rhs) const
+    {
+        if (this.type != rhs.type)
+            return false;
+        switch (type)
+        {
+            static foreach (i, T; Types)
+            {
+                case i + 1:
+                    return this.trustedGet!T == rhs.trustedGet!T;
+            }
+            default:
+                return true;
+        }
+    } 
+
+    static if (allSatisfy!(isOrderingComparable, Types))
+    /++
+    +/
+    auto opCmp(ref const typeof(this) rhs) const
+    {
+        if (int d = this.type - rhs.type)
+            return d;
+        switch (type)
+        {
+            static foreach (i, T; Types)
+            {
+                case i + 1:
+                    static if (__traits(hasMember, T, "opCmp"))
+                        return this.trustedGet!T.opCmp(rhs.trustedGet!T);
+                    else
+                        return this.trustedGet!T < rhs.trustedGet!T ? -1 :
+                               this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
+            }
+            default:
+                return 0;
+        }
+    }
 }
 
 /++
 Params:
     visitor = a function name alias
-    forceAllTypes = if `true` checks at compile time, that the member can be called for all types.
+    forceAllTypes = if `true` checks at compile time, that the visitor can be called for all types.
 +/
 template visit(alias visitor, bool forceAllTypes = true)
 {
@@ -194,6 +285,32 @@ template visit(alias visitor, bool forceAllTypes = true)
     }
 }
 
+///
+version(mir_test)
+unittest
+{
+    static struct S { int a; }
+
+    Variant!(S, double) variant;
+    variant = 1.0;
+    variant.visit!((ref value, b) => value += b, false)(2);
+    assert (variant.get!double == 3);
+
+    alias fun = (ref value) {
+        static if (is(typeof(value) == S))
+            value.a += 2;
+        else
+           value += 2;
+    };
+
+    variant.visit!fun;
+    assert (variant.get!double == 5);
+
+    variant = S(4);
+    variant.visit!fun;
+    assert (variant.get!S.a == 6);
+}
+
 /++
 Params:
     visitor = a function name alias
@@ -202,7 +319,7 @@ template optionalVisit(alias visitor)
 {
     import std.traits: Unqual;
     ///
-    bool optionalVisit(Result, V, Args...)(out Result result, auto ref V variant, auto ref Args args)
+    bool optionalVisit(Result, V, Args...)(ref Result result, auto ref V variant, auto ref Args args) @property
         if (is(Unqual!V : Variant!Types, Types))
     {
         import core.lifetime: forward;
@@ -220,3 +337,27 @@ template optionalVisit(alias visitor)
         }
     }
 }
+
+///
+version(mir_test)
+unittest
+{
+    static struct S { int a; }
+
+    Variant!(S, double) variant;
+
+    double result = 100;
+
+    // do nothing because of variant isn't initialized
+    optionalVisit!((ref value) => value)(result, variant);
+    assert(result == 100);
+
+    variant = S(2);
+    // do nothing because of lambda can't compile
+    optionalVisit!((ref value) => value)(result, variant);
+    assert(result == 100);
+
+    variant = 3.0;
+    optionalVisit!((ref value) => value)(result, variant);
+    assert (result == 3);
+}

From b1ce7f93a6eab0483241f844fbcf4aeebc01fb8b Mon Sep 17 00:00:00 2001
From: Ilya <ilyayaroshenko@gmail.com>
Date: Mon, 21 Sep 2020 10:54:17 +0700
Subject: [PATCH 095/573] add tests and fixes for Variant

---
 source/mir/variant.d | 79 ++++++++++++++++++++++++++++++++++----------
 1 file changed, 61 insertions(+), 18 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 8d2bcf85..e402df9d 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -17,21 +17,8 @@ version (D_Exceptions)
 private enum int alignOf(T) = T.alignof;
 
 private enum hasToHash(T) = __traits(hasMember, T, "toHash");
-private enum hasToHash(T) = __traits(hasMember, T, "opCmp");
-private enum hasToHash(T) = __traits(hasMember, T, "opEquals");
+private enum hasOpEquals(T) = __traits(hasMember, T, "opEquals");
 private enum isPOD(T) = __traits(isPOD, T);
-
-private template LargestAlignOf(T...)
-{
-    static if (T.length == 0)
-        enum size_t LargestAlignOf = 0;
-    else
-    {
-        enum tail = LargestAlignOf!(T[1 .. $]);
-        enum size_t LargestAlignOf = T[0].alignof > tail ? T[0].alignof : tail;
-    }
-}
-
 private enum Sizeof(T) = T.sizeof;
 
 /++
@@ -49,9 +36,8 @@ struct Variant(Types...)
 
     private alias _Types = Types;
 
-    align(max(4, staticMap!(alignOf, Types)))
     private uint type; // 0 for unininit value, index = type - 1
-    private align(LargestAlignOf!Types) ubyte[Largest!Types.sizeof] payload;
+    private align(max(staticMap!(alignOf, Types))) ubyte[Largest!Types.sizeof] payload;
     private enum hasDestructor = anySatisfy!(hasElaborateDestructor, Types);
 
     static if (hasDestructor)
@@ -188,7 +174,15 @@ struct Variant(Types...)
         else
         static if (allSatisfy!(isPOD, Types))
         {
-            static immutable sizes = [size_t(0), staticMap!(Sizeof, Types)];
+            static if (payload.length <= ubyte.max)
+                alias UInt = ubyte;
+            else
+            static if (payload.length <= ushort.max)
+                alias UInt = ushort;
+            else
+                alias UInt = uint;
+
+            static immutable UInt[Types.length + 1] sizes = [0, staticMap!(Sizeof, Types)];
             return hashOf(payload[0 .. sizes[type]], type);
         }
         else
@@ -204,7 +198,7 @@ struct Variant(Types...)
         }
     }
 
-    static if (allSatisfy!(isEqualityComparable, Types))
+    static if (allSatisfy!(templateOr!(isPOD, hasOpEquals), Types))
     /++
     +/
     auto opEquals(ref const typeof(this) rhs) const
@@ -247,6 +241,53 @@ struct Variant(Types...)
     }
 }
 
+/// Test for opCmp, opEqual, toHash
+version(mir_test)
+@safe pure @nogc nothrow
+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() { return hashOf(payload); }
+
+    static if (hasOpEquals)
+        auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(payload.ptr, rhs.payload.ptr, payload.length); }
+        auto opCmp(ref const typeof(this) rhs) { return payload == rhs.payload; }
+    }
+
+    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(hasMember, T, "toHash") == isPOD || hasToHash);
+        static assert (__traits(hasMember, T, "opEquals") == isPOD || hasOpEquals);
+        static assert (__traits(hasMember, T, "opCmp"));
+    }}
+}
+
 /++
 Params:
     visitor = a function name alias
@@ -287,6 +328,7 @@ template visit(alias visitor, bool forceAllTypes = true)
 
 ///
 version(mir_test)
+@safe pure @nogc
 unittest
 {
     static struct S { int a; }
@@ -340,6 +382,7 @@ template optionalVisit(alias visitor)
 
 ///
 version(mir_test)
+@safe pure @nogc nothrow
 unittest
 {
     static struct S { int a; }

From f47606d5822163b5f18eb88cc109d6ede1259a2d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 30 Sep 2020 21:27:46 +0700
Subject: [PATCH 096/573] update for mir-ion

---
 source/mir/bignum/decimal.d        |  2 ++
 source/mir/bignum/integer.d        |  4 +++-
 source/mir/bignum/low_level_view.d |  2 +-
 source/mir/exception.d             |  4 ++--
 source/mir/serde.d                 | 13 +++++++++++++
 source/mir/small_array.d           | 10 +++++++++-
 6 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index efb1bad7..d800cd43 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -6,6 +6,7 @@ Note:
 +/
 module mir.bignum.decimal;
 
+import mir.serde: serdeProxy, serdeScoped;
 import std.traits: isSomeChar;
 
 /++
@@ -13,6 +14,7 @@ Stack-allocated decimal type.
 Params:
     maxSize64 = count of 64bit words in coefficient
 +/
+@serdeScoped @serdeProxy!(const(char)[])
 struct Decimal(size_t maxSize64)
     if (maxSize64 && maxSize64 <= ushort.max)
 {
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 0835327e..288b08d8 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -4,15 +4,17 @@ Note:
 +/
 module mir.bignum.integer;
 
-import std.traits;
 import mir.bitop;
+import mir.serde: serdeProxy, serdeScoped;
 import mir.utility;
+import std.traits;
 
 /++
 Stack-allocated big signed integer.
 Params:
     maxSize64 = count of 64bit words in coefficient
 +/
+@serdeScoped @serdeProxy!(const(char)[])
 struct BigInt(size_t maxSize64)
     if (maxSize64 && maxSize64 <= ushort.max)
 {
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 1038ea7a..b1ede754 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -891,7 +891,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Params:
         value = (out) unsigned integer
-    Returns: $(SUBREF exception, IonErrorCode)
+    Returns: true on success
     +/
     bool get(U)(scope out U value)
         @safe pure nothrow @nogc const
diff --git a/source/mir/exception.d b/source/mir/exception.d
index a98289c0..8bed5107 100644
--- a/source/mir/exception.d
+++ b/source/mir/exception.d
@@ -214,7 +214,7 @@ mixin template MirThrowableImpl()
     +/
     @nogc @safe pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
     {
-        super((() @trusted => cast(immutable) initilizePayload(_payload, msg))(), file, line, nextInChain);
+        super((() @trusted => cast(immutable) mirExceptionInitilizePayloadImpl(_payload, msg))(), file, line, nextInChain);
     }
 
     /// ditto
@@ -262,7 +262,7 @@ private enum maxMsgLen = 447;
 
 pragma(inline, false)
 pure nothrow @nogc @safe
-private const(char)[] initilizePayload(ref return char[maxMsgLen] payload, scope const(char)[] msg)
+const(char)[] mirExceptionInitilizePayloadImpl(ref return char[maxMsgLen] payload, scope const(char)[] msg)
 {
     import mir.internal.memory: malloc;
     import core.stdc.string: memcpy;
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 5d6018a5..2d31aad4 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -39,6 +39,19 @@ version (D_Exceptions)
 
         alias toMutable this;
     }
+
+    /++
+    Serde Exception with formatting support
+    +/
+    class SerdeMirException : SerdeException
+    {
+        import mir.exception: MirThrowableImpl, mirExceptionInitilizePayloadImpl;
+
+        enum maxMsgLen = 447;
+
+        ///
+        mixin MirThrowableImpl;
+    }
 }
 
 /++
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 36b57141..bd23ca19 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -186,15 +186,23 @@ template SmallArray(T, uint maxLength)
         ///
         ref typeof(this) append(T elem)
         {
+            import core.lifetime: move;
             if (_length == maxLength)
             {
                 version(D_Exceptions) throw exception;
                 else assert(0, errorMsg);
             }
-            _data[_length++] = elem;
+            _data[_length++] = move(elem);
             return this;
         }
 
+        ///
+        void trustedAppend(T elem)
+        {
+            import core.lifetime: move;
+            _data[_length++] = move(elem);
+        }
+
         ///
         ref typeof(this) append(V[] array)
         {

From 5d6d707c6696e9291a20990ecb9bc078233192cb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 1 Oct 2020 21:49:59 +0700
Subject: [PATCH 097/573] rework serdeGetDeserializatinKeysRecurse

---
 source/mir/serde.d | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 2d31aad4..653fe47b 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -834,16 +834,30 @@ unittest
     static assert (is(serdeDeserializationFinalProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
 }
 
+package string[] sortUniqKeys()(string[] keys)
+    @safe pure nothrow
+{
+    import mir.algorithm.iteration: uniq;
+    import mir.array.allocation: array;
+    import mir.ndslice.sorting: sort;
+
+    return keys
+        .sort!((a, b) {
+            if (sizediff_t d = a.length - b.length)
+                return d < 0;
+            return a < b;
+        })
+        .uniq
+        .array;
+}
+
 /++
 Deserialization members final proxy keys (recursive)
 +/
 template serdeGetDeserializatinKeysRecurse(T)
 {
-    import mir.algorithm.iteration: uniq, equal;
-    import mir.array.allocation: array;
-    import mir.ndslice.sorting: sort;
     import std.meta: staticMap, aliasSeqOf;
-    enum string[] serdeGetDeserializatinKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalProxyDeserializableMembers, serdeDeserializationFinalProxyMemberTypesRecurse!T))].sort.uniq!equal.array;
+    enum string[] serdeGetDeserializatinKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalProxyDeserializableMembers, serdeDeserializationFinalProxyMemberTypesRecurse!T))].sortUniqKeys;
 }
 
 ///

From e987ead1f2ca8fb99411445bc68be8743197780a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 2 Oct 2020 17:05:18 +0700
Subject: [PATCH 098/573] rework serdeGetDeserializatinKeysRecurse

---
 source/mir/ndslice/sorting.d |   2 +-
 source/mir/serde.d           | 216 +++++++++++++++++++++++++++++++++--
 2 files changed, 208 insertions(+), 10 deletions(-)

diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index e3017b7e..de260a53 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -295,7 +295,7 @@ void quickSortImpl(alias less, Iterator)(Slice!Iterator slice) @trusted
 
         static if (naive > 1)
         {
-            if (slice.length <= naive)
+            if (slice.length <= naive || __ctfe)
             {
                 auto p = r;
                 --p;
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 653fe47b..3cd58c8b 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -629,8 +629,8 @@ Final proxy type
 +/
 template serdeGetFinalProxy(T)
 {
-    import std.traits: hasUDA;
-    static if (is(T == class) || is(T == struct) || is(T == union) || is(T == interface))
+    import std.traits: hasUDA, isAggregateType;
+    static if (isAggregateType!T || is(T == enum))
     {
         static if (hasUDA!(T, serdeProxy))
         {
@@ -664,6 +664,66 @@ unittest
     static assert (is(serdeGetFinalProxy!string == string));
 }
 
+/++
+Final deep proxy type
++/
+template serdeGetFinalDeepProxy(T)
+{
+    import std.traits: Unqual, hasUDA, isAggregateType, isArray, ForeachType;
+    static if (isAggregateType!T || is(T == enum))
+    {
+        static if (hasUDA!(T, serdeProxy))
+        {
+            alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(serdeGetProxy!T);
+        }
+        else
+        {
+            alias serdeGetFinalDeepProxy = T;
+        }
+    }
+    else
+    static if (isArray!T)
+    {
+        alias E = Unqual!(ForeachType!T);
+        static if (isAggregateType!E || is(T == enum))
+            alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E;
+        else
+            alias serdeGetFinalDeepProxy = T;
+    }
+    else
+    static if (is(T == V[K], K, V))
+    {
+        alias E = Unqual!V;
+        static if (isAggregateType!E || is(T == enum))
+            alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E;
+        else
+            alias serdeGetFinalDeepProxy = T;
+    }
+    else
+    {
+        alias serdeGetFinalDeepProxy = T;
+    }
+}
+
+///
+unittest
+{
+
+    @serdeProxy!string
+    static struct A {}
+
+    enum E {a,b,c}
+
+    @serdeProxy!(A[E])
+    static struct B {}
+
+    @serdeProxy!(B[])
+    static struct C {}
+
+    static assert (is(serdeGetFinalDeepProxy!C == string), serdeGetFinalDeepProxy!C.stringof);
+    static assert (is(serdeGetFinalDeepProxy!string == string));
+}
+
 /++
 Final proxy type deserializable members
 +/
@@ -699,6 +759,42 @@ unittest
     static assert (serdeFinalProxyDeserializableMembers!C == ["m"]);
 }
 
+/++
+Final deep proxy type deserializable members
++/
+template serdeFinalDeepProxyDeserializableMembers(T)
+{
+    import std.traits: isAggregateType;
+    import std.meta: Filter, aliasSeqOf;
+    alias P = serdeGetFinalDeepProxy!T;
+    static if (isAggregateType!P)
+        enum string[] serdeFinalDeepProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
+    else
+        enum string[] serdeFinalDeepProxyDeserializableMembers = null;
+}
+
+///
+unittest
+{
+
+    static struct A
+    {
+        @serdeIgnore
+        int i;
+
+        @serdeKeys("a", "b")
+        int m;
+    }
+
+    @serdeProxy!(A[string])
+    static struct B {}
+
+    @serdeProxy!(B[])
+    static struct C {}
+
+    static assert (serdeFinalDeepProxyDeserializableMembers!C == ["m"]);
+}
+
 /++
 Deserialization member final proxy type
 +/
@@ -789,6 +885,44 @@ unittest
     static assert (is(serdeDeserializationFinalProxyMemberTypes!D == AliasSeq!A));
 }
 
+/++
+Deserialization members final deep proxy types
++/
+template serdeDeserializationFinalDeepProxyMemberTypes(T)
+{
+    import std.meta: NoDuplicates, staticMap, aliasSeqOf;
+    alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T))));
+}
+
+///
+unittest
+{
+
+    static struct A {}
+
+    @serdeProxy!(A[])
+    static struct B {}
+
+    enum R {a, b, c}
+
+    @serdeProxy!(B[R])
+    static struct C {}
+
+    @serdeProxy!(B[string])
+    static struct E {}
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    import std.meta: AliasSeq;
+    static assert (is(serdeDeserializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeDeserializationFinalDeepProxyMemberTypes!D);
+}
+
 private template serdeDeserializationFinalProxyMemberTypesRecurseImpl(T...)
 {
     import std.meta: NoDuplicates, staticMap;
@@ -834,6 +968,51 @@ unittest
     static assert (is(serdeDeserializationFinalProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
 }
 
+private template serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl(T...)
+{
+    import std.meta: NoDuplicates, staticMap;
+    alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalDeepProxyMemberTypes, T));
+    static if (F.length == T.length)
+        alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl = T;
+    else
+        alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl  = .serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!F;
+}
+
+/++
+Deserialization members final deep proxy types (recursive)
++/
+alias serdeDeserializationFinalDeepProxyMemberTypesRecurse(T) = serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T);
+
+///
+unittest
+{
+
+    static struct A { double g; }
+
+    @serdeProxy!(A[])
+    static struct B {}
+
+    @serdeProxy!(B[string])
+    static struct C {}
+
+    @serdeProxy!B
+    static struct E {}
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!(E[])
+        int d;
+    }
+
+    @serdeProxy!D
+    static struct F {}
+
+    import std.meta: AliasSeq;
+    static assert (is(serdeDeserializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
+}
+
 package string[] sortUniqKeys()(string[] keys)
     @safe pure nothrow
 {
@@ -851,13 +1030,32 @@ package string[] sortUniqKeys()(string[] keys)
         .array;
 }
 
+
+private template serdeGetKeysIn2(T)
+{
+    // T* value;
+    enum string[] serdeGetKeysIn2(string member) = serdeGetKeysIn!(__traits(getMember, T, member));
+}
+
+private template serdeFinalDeepProxyDeserializableMemberKeys(T)
+{
+    import std.meta: staticMap, aliasSeqOf;
+    import std.traits: isAggregateType;
+    import mir.ndslice.fuse: fuseCells;
+
+    static if (isAggregateType!T)
+        enum string[] serdeFinalDeepProxyDeserializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeysIn2!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))];
+    else
+        enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null;
+}
+
 /++
 Deserialization members final proxy keys (recursive)
 +/
 template serdeGetDeserializatinKeysRecurse(T)
 {
     import std.meta: staticMap, aliasSeqOf;
-    enum string[] serdeGetDeserializatinKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalProxyDeserializableMembers, serdeDeserializationFinalProxyMemberTypesRecurse!T))].sortUniqKeys;
+    enum string[] serdeGetDeserializatinKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxyDeserializableMemberKeys, serdeDeserializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys;
 }
 
 ///
@@ -869,17 +1067,17 @@ unittest
     @serdeProxy!A
     static struct B {  int f; }
 
-    @serdeProxy!B
-    static struct C {  int f; }
+    @serdeProxy!(B[string])
+    static union C {  int f; }
 
-    @serdeProxy!B
-    static struct E {  int f; }
+    @serdeProxy!(B[])
+    static interface E {  int f() @property; }
 
-    static struct D
+    static class D
     {
         C c;
 
-        @serdeProxy!E
+        @serdeProxy!(E[])
         int d;
     }
 

From 2c44e9762e7be3975210d128ded45ccd9d313880 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 7 Oct 2020 12:58:13 +0700
Subject: [PATCH 099/573] update mir.serde

---
 .gitignore                 |  1 +
 source/mir/ndslice/slice.d |  3 +++
 source/mir/rc/array.d      |  3 +++
 source/mir/rc/context.d    |  7 +++++++
 source/mir/serde.d         | 11 ++++++++---
 source/mir/small_array.d   |  3 +++
 6 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6a00f79a..6ed6bbb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@ test.d
 docgen
 _build_dir_
 .VSCodeCounter
+builddir/
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index c290a912..50647503 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -696,6 +696,9 @@ struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_
     /// Data element type
     alias DeepElement = typeof(Iterator.init[size_t.init]);
 
+    ///
+    alias serdeKeysProxy = DeepElementType;
+
     /// Label Iterators types
     alias Labels = Labels_;
 
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 3d00a4ca..b088e95d 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -38,6 +38,9 @@ struct mir_rcarray(T)
     package alias ThisTemplate = .mir_rcarray;
     package alias _thisPtr = _payload;
 
+    ///
+    alias serdeKeysProxy = T;
+
     ///
     void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe
     {
diff --git a/source/mir/rc/context.d b/source/mir/rc/context.d
index 57a724f2..b873d438 100644
--- a/source/mir/rc/context.d
+++ b/source/mir/rc/context.d
@@ -242,6 +242,13 @@ package mixin template CommonRCImpl()
         return *cast(typeof(return)*)&this;
     }
 
+    /// ditto
+    C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc const @system
+        if (isImplicitlyConvertible!(immutable(T)*, Q*))
+    {
+        return *cast(typeof(return)*)&this;
+    }
+
     ///
     pragma(inline, true)
     bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 3cd58c8b..f0d5db11 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -63,7 +63,7 @@ struct serdeKeys
     ///
     immutable(string)[] keys;
 
-@system pure nothrow @nogc:
+@trusted pure nothrow @nogc:
     ///
     this(immutable(string)[] keys...) { this.keys = keys; }
 }
@@ -452,8 +452,8 @@ unittest
 
 /++
 Can be applied only to strings fields.
-Does not allocate new data when deserializeing. Raw ASDF data is used for strings instead of new memory allocation.
-Use this attributes only for strings that would not be used after ASDF deallocation.
+Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation.
+Use this attributes only for strings or arrays that would not be used after deallocation.
 +/
 enum serdeScoped;
 
@@ -677,6 +677,11 @@ template serdeGetFinalDeepProxy(T)
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(serdeGetProxy!T);
         }
         else
+        static if (__traits(hasMember, T, "serdeKeysProxy"))
+        {
+            alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(T.serdeKeysProxy);
+        }
+        else
         {
             alias serdeGetFinalDeepProxy = T;
         }
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index bd23ca19..c1122e05 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -29,6 +29,9 @@ template SmallArray(T, uint maxLength)
         uint _length;
         T[maxLength] _data;
 
+        ///
+        alias serdeKeysProxy = T;
+
         static SmallArray deserialize(S)(S data)
         {
             import asdf.serialization: deserialize;

From 5bc9a2777a2c594523f151a237db5c7a9ec28ffd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 7 Oct 2020 12:59:09 +0700
Subject: [PATCH 100/573] remove deserializeValueMemberImpl docs

---
 source/mir/serde.d | 2 --
 1 file changed, 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index f0d5db11..d0c7cec3 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1310,8 +1310,6 @@ struct SerdeFlags(T)
             mixin("bool " ~ member ~ ";");
 }
         
-/++
-+/
 template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped)
 {
     ///

From 9a23d58772bde95618ea00481639b6fcea6ba4c3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 7 Oct 2020 15:25:24 +0700
Subject: [PATCH 101/573] fixup

---
 source/mir/rc/context.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/rc/context.d b/source/mir/rc/context.d
index b873d438..7b036530 100644
--- a/source/mir/rc/context.d
+++ b/source/mir/rc/context.d
@@ -244,7 +244,7 @@ package mixin template CommonRCImpl()
 
     /// ditto
     C opCast(C : ThisTemplate!Q, Q)() pure nothrow @nogc const @system
-        if (isImplicitlyConvertible!(immutable(T)*, Q*))
+        if (isImplicitlyConvertible!(immutable(T)*, Q*) && !isImplicitlyConvertible!(const(T)*, Q*))
     {
         return *cast(typeof(return)*)&this;
     }

From 8b7d9d895d35943ee0efb4c1d4e106f266c69927 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 12 Oct 2020 00:56:29 +0700
Subject: [PATCH 102/573] update with mir.enum

---
 dub.sdl                         |   2 +-
 source/mir/format.d             |  29 ++++-----
 source/mir/interpolate/spline.d |   1 -
 source/mir/serde.d              | 104 ++++++++++++--------------------
 4 files changed, 51 insertions(+), 85 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 60197672..b4cb44da 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "Copyright © 2016 - 2019, Ilya Yaroshenko; see also information per file."
 license "BSL-1.0"
 
-dependency "mir-core" version=">=1.1.12"
+dependency "mir-core" version=">=1.1.13"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
diff --git a/source/mir/format.d b/source/mir/format.d
index 33e018b8..ae37ba59 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -340,25 +340,20 @@ ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Arg
 ref W print(C = char, W, T)(scope return ref W w, const T c)
     if (is(T == enum))
 {
+    import mir.enums: getEnumIndex, enumStrings;
+    import mir.utility: _expect;
+
     static assert(!is(OriginalType!T == enum));
-    string s;
-    S: switch (c)
+    uint index = void;
+    if (getEnumIndex(c, index)._expect(true))
     {
-        static foreach(member; __traits(allMembers, T))
-        {
-            case __traits(getMember, T, member):
-            s = member;
-            break S;
-        }
-        default:
-            static immutable C[] str = T.stringof;
-            w.put(str[]);
-            w.put('(');
-            print(w, cast(OriginalType!T) c);
-            w.put(')');
-            return w;
-    }
-    w.put(s);
+        w.put(enumStrings!T[index]);
+        return w;
+    }
+    static immutable C[] str = T.stringof ~ "(";
+    w.put(str[]);
+    print(w, cast(OriginalType!T) c);
+    w.put(')');
     return w;
 }
 
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index d9ee411c..c2fe8db3 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -956,7 +956,6 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
 
     auto n = points.length;
 
-
     typeof(slopes[0]) first, last;
 
     auto xd = points.diff;
diff --git a/source/mir/serde.d b/source/mir/serde.d
index d0c7cec3..25c7ec1e 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -82,6 +82,8 @@ struct serdeKeyOut
 }
 
 /++
+Returns:
+    immutable array of the input keys for the symbol or enum value
 +/
 template serdeGetKeysIn(alias symbol)
 {
@@ -105,14 +107,11 @@ immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc
 
     import std.meta: staticMap;
     static immutable ret = [staticMap!(.serdeGetKeysIn, EnumMembers!T)];
-    final switch (value)
-    {
-        foreach (i, member; EnumMembers!T)
-        {
-            case member:
-                return ret[i];
-        }
-    }
+    import mir.enums: getEnumIndex;
+    uint index = void;
+    if (getEnumIndex(value, index))
+        return ret[index];
+    assert(0);
 }
 
 ///
@@ -163,29 +162,9 @@ unittest
     static assert (serdeGetKeysIn(E.c) == ["c"]);
 }
 
-///ditto
-@trusted pure nothrow @nogc
-string serdeGetKeyOut(T)(const T value)
-    if (is(T == enum))
-{
-    foreach (i, member; EnumMembers!T)
-    {{
-        alias all = __traits(getAttributes, EnumMembers!T[i]);
-    }}
-
-    import std.meta: staticMap;
-    static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
-    final switch (value)
-    {
-        foreach (i, member; EnumMembers!T)
-        {
-            case member:
-                return ret[i];
-        }
-    }
-}
-
 /++
+Returns:
+    output key for the symbol or enum value
 +/
 template serdeGetKeyOut(alias symbol)
 {
@@ -201,6 +180,25 @@ template serdeGetKeyOut(alias symbol)
         enum string serdeGetKeyOut = __traits(identifier, symbol);
 }
 
+///ditto
+@safe pure nothrow @nogc
+string serdeGetKeyOut(T)(const T value)
+    if (is(T == enum))
+{
+    foreach (i, member; EnumMembers!T)
+    {{
+        alias all = __traits(getAttributes, EnumMembers!T[i]);
+    }}
+
+    import std.meta: staticMap;
+    import mir.enums: getEnumIndex;
+    static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
+    uint index = void;
+    if (getEnumIndex(value, index))
+        return ret[index];
+    assert(0);
+}
+
 ///
 version(mir_test)
 unittest
@@ -499,45 +497,19 @@ alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value
 
 /++
 +/
-bool serdeParseEnum(E)(const char[] str, out E res)
+bool serdeParseEnum(E)(const char[] str, ref E res)
+    @safe pure nothrow @nogc
 {
-    static if (hasUDA!(E, serdeIgnoreCase))
-    {
-        import mir.format: stringBuf;
-        stringBuf buf;
-        buf << str;
-        auto ustr = buf.data.fastToUpperInPlace;
-    }
-    else
-    {
-        alias ustr = str;
-    }
-    switch(ustr)
+    import mir.enums: getEnumIndexFromKey, unsafeEnumFromIndex;
+    import mir.utility: _expect;
+
+    uint index = void;
+    if (getEnumIndexFromKey!(E, hasUDA!(E, serdeIgnoreCase), serdeGetKeysIn)(str, index)._expect(true))
     {
-        foreach(i, member; EnumMembers!E)
-        {{
-            enum initKeys = serdeGetKeysIn(EnumMembers!E[i]);
-            static if (hasUDA!(E, serdeIgnoreCase))
-            {
-                import mir.ndslice.topology: map;
-                import mir.array.allocation: array;
-                enum keys = initKeys.map!fastLazyToUpper.map!array.array;
-            }
-            else
-            {
-                enum keys = initKeys;
-            }
-            static assert (keys.length, "At least one input enum key is required");
-            static foreach (key; keys)
-            {
-                case key:
-            }
-            res = member;
-            return true;
-        }}
-        default:
-            return false;
+        res = unsafeEnumFromIndex!E(index);
+        return true;
     }
+    return false;
 }
 
 ///

From 520ddceaede9da782389f93875ca2ef8c6ec494b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 12 Oct 2020 09:06:58 +0700
Subject: [PATCH 103/573] backport for older compilers

---
 source/mir/serde.d | 73 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 25c7ec1e..168bfe0d 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -107,11 +107,25 @@ immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc
 
     import std.meta: staticMap;
     static immutable ret = [staticMap!(.serdeGetKeysIn, EnumMembers!T)];
+    static if (__VERSION__ < 2093)
+    {
+        final switch (value)
+        {
+            foreach (i, member; EnumMembers!T)
+            {
+                case member:
+                    return ret[i];
+            }
+        }
+    }
+    else
+    {
     import mir.enums: getEnumIndex;
     uint index = void;
     if (getEnumIndex(value, index))
         return ret[index];
     assert(0);
+    }
 }
 
 ///
@@ -190,6 +204,21 @@ string serdeGetKeyOut(T)(const T value)
         alias all = __traits(getAttributes, EnumMembers!T[i]);
     }}
 
+    static if (__VERSION__ < 2093)
+    {
+        import std.meta: staticMap;
+        static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
+        final switch (value)
+        {
+            foreach (i, member; EnumMembers!T)
+            {
+                case member:
+                    return ret[i];
+            }
+        }
+    }
+    else
+    {
     import std.meta: staticMap;
     import mir.enums: getEnumIndex;
     static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
@@ -197,6 +226,7 @@ string serdeGetKeyOut(T)(const T value)
     if (getEnumIndex(value, index))
         return ret[index];
     assert(0);
+    }
 }
 
 ///
@@ -500,6 +530,48 @@ alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value
 bool serdeParseEnum(E)(const char[] str, ref E res)
     @safe pure nothrow @nogc
 {
+    static if (__VERSION__ < 2093)
+    {
+        static if (hasUDA!(E, serdeIgnoreCase))
+        {
+            import mir.format: stringBuf;
+            stringBuf buf;
+            buf << str;
+            auto ustr = buf.data.fastToUpperInPlace;
+        }
+        else
+        {
+            alias ustr = str;
+        }
+        switch(ustr)
+        {
+            foreach(i, member; EnumMembers!E)
+            {{
+                enum initKeys = serdeGetKeysIn(EnumMembers!E[i]);
+                static if (hasUDA!(E, serdeIgnoreCase))
+                {
+                    import mir.ndslice.topology: map;
+                    import mir.array.allocation: array;
+                    enum keys = initKeys.map!fastLazyToUpper.map!array.array;
+                }
+                else
+                {
+                    enum keys = initKeys;
+                }
+                static assert (keys.length, "At least one input enum key is required");
+                static foreach (key; keys)
+                {
+                    case key:
+                }
+                res = member;
+                return true;
+            }}
+            default:
+                return false;
+        }
+    }
+    else
+    {
     import mir.enums: getEnumIndexFromKey, unsafeEnumFromIndex;
     import mir.utility: _expect;
 
@@ -510,6 +582,7 @@ bool serdeParseEnum(E)(const char[] str, ref E res)
         return true;
     }
     return false;
+    }
 }
 
 ///

From c84468cee8c5ffa90d9e5389b0f0ecacbefeaa47 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 12 Oct 2020 21:02:01 +0700
Subject: [PATCH 104/573] add proxy to date

---
 source/mir/date.d | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source/mir/date.d b/source/mir/date.d
index 43013b3c..5b94b0b1 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -29,6 +29,7 @@ $(TR $(TD Other) $(TD
 +/
 module mir.date;
 
+import mir.serde: serdeProxy, serdeScoped;
 import std.range.primitives : isOutputRange;
 import std.traits : isSomeChar, Unqual;
 
@@ -409,6 +410,8 @@ struct YearMonthDay
  +/
 extern(C++, "boost", "gregorian")
 extern(C++, class)
+@serdeScoped
+@serdeProxy!(const(char)[])
 struct date
 {
 extern(D):

From ea40e1b5aab8529e738450201ead6dc3c8c1150c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 13 Oct 2020 14:36:52 +0700
Subject: [PATCH 105/573] update mir.format

---
 source/mir/bignum/integer.d |   8 +-
 source/mir/format.d         | 277 +++++++++++++++++++++---------------
 2 files changed, 166 insertions(+), 119 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 288b08d8..149df980 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -51,17 +51,17 @@ struct BigInt(size_t maxSize64)
     ///
     this(ulong data)
     {
-        auto d = view.leastSignificantFirst;
         static if (size_t.sizeof == ulong.sizeof)
         {
-            d.front = data;
             length = 1;
+            view.leastSignificantFirst[0] = data;
         }
         else
         {
-            d.front = cast(uint) data;
-            d[1] = cast(uint) (data >> 32);
             length = 2;
+            auto d = view.leastSignificantFirst;
+            d[0] = cast(uint) data;
+            d[1] = cast(uint) (data >> 32);
         }
         normalize;
     }
diff --git a/source/mir/format.d b/source/mir/format.d
index ae37ba59..3b17b2aa 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -62,7 +62,9 @@ string text(string separator = "", A...)(auto ref A args)
         {
             buffer.print(arg);
             static if (separator.length && i + 1 < args.length)
-                buffer.printStaticStringInternal!(char, separator);
+            {
+                buffer.printStaticString!char(separator);
+            }
         }
         return buffer.data.idup;
     }
@@ -240,7 +242,7 @@ struct HexAddress(T)
         enum N = T.sizeof * 2;
         static if(isFastBuffer!W)
         {
-            w.advance(printHexAddress(value, w.getBuffer(N).getStaticBuf!N, cast(bool) switchLU));
+            w.advance(printHexAddress(value, w.getStaticBuf!N, cast(bool) switchLU));
         }
         else
         {
@@ -252,50 +254,56 @@ struct HexAddress(T)
 }
 
 /++
-Note: Non-ASCII Unicode characters are encoded as sequence of \xXX bytes. This may be fixed in the future.
 +/
-pragma(inline, false)
-ref W printEscaped(C = char, W)(scope return ref W w, scope const(char)[] str)
+enum EscapeFormat
 {
-    // TODO: replace with Mir implementation.
-    w.put('\"');
-    foreach (char c; str[])
-    {
-        if (c >= 0x20)
+    json,
+    ionSymbol,
+    ion,
+}
+
+enum escapeFormatQuote(EscapeFormat escapeFormat) = escapeFormat == EscapeFormat.ionSymbol ? '\'' : '\"';
+
+/++
++/
+ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope return ref W w, scope const(C)[] str)
+{
+    import mir.utility: _expect;
+    foreach (C c; str)
+    {
+        if (_expect(c == escapeFormatQuote!escapeFormat || c == '\\', false))
+            goto E;
+        if (_expect(c < ' ', false))
+            goto C;
+    P:
+        w.put(c);
+        continue;
+    E:
         {
-            if (c < 0x7F)
-            {
-                if (c == '\"' || c == '\\')
-                {
-                L:
-                    w.put('\\');
-                }
-                w.put(c);
-            }
-            else
-            {
-            M:
-                printStaticStringInternal!(C, "\\x")(w);
-                print!C(w, HexAddress!ubyte(cast(ubyte)c));
-            }
+            C[2] pair;
+            pair[0] = '\\';
+            pair[1] = c;
+            w.printStaticString!C(pair);
+            continue;
         }
-        else
+    C:
+        if (c == '\t' || c == '\f' || c == '\b')
+            goto P;
+        if (c == '\n')
         {
-            switch(c)
-            {
-                case '\n': c = 'n'; goto L;
-                case '\r': c = 'r'; goto L;
-                case '\t': c = 't'; goto L;
-                case '\a': c = 'a'; goto L;
-                case '\b': c = 'b'; goto L;
-                case '\f': c = 'f'; goto L;
-                case '\v': c = 'v'; goto L;
-                case '\0': c = '0'; goto L;
-                default: goto M;
-            }
+            c = 'n';
+            goto E;
+        }
+        if (c == '\r')
+        {
+            c = 'r';
+            goto E;
         }
+        static if (escapeFormat == EscapeFormat.json)
+            put_uXXXX!C(w, cast(char)c);
+        else
+            put_xXX!C(w, cast(char)c);
     }
-    w.put('\"');
     return w;
 }
 
@@ -303,24 +311,84 @@ ref W printEscaped(C = char, W)(scope return ref W w, scope const(char)[] str)
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
+ 
     import mir.appender: ScopedBuffer;
     ScopedBuffer!char w;
-    assert(w.printEscaped("Hi\t" ~ `"@nogc"`).data == `"Hi\t\"@nogc\""`, w.data);
+    assert(w.printEscaped("Hi \f\t\b \\\r\n" ~ `"@nogc"`).data == "Hi \f\t\b \\\\\\r\\n\\\"@nogc\\\"", w.data);
     w.reset;
-    assert(w.printEscaped("\xF3").data == `"\xF3"`, w.data);
+    assert(w.printEscaped("\x03").data == `\x03`, w.data);
+}
+
+/++
+Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
++/
+ref W put_xXX(C = char, W)(scope return ref W w, char c)
+{
+    ubyte[2] spl;
+    spl[0] = c >> 4;
+    spl[1] = c & 0xF;
+    C[4] buffer;
+    buffer[0] = '\\';
+    buffer[1] = 'x';
+    buffer[2] = cast(ubyte)(spl[0] < 10 ? spl[0] + '0' : spl[0] - 10 + 'A');
+    buffer[3] = cast(ubyte)(spl[1] < 10 ? spl[1] + '0' : spl[1] - 10 + 'A');
+    return w.printStaticString(buffer);
+}
+
+/++
+Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
++/
+ref W put_uXXXX(C = char, W)(scope return ref W w, char c)
+{
+    ubyte[2] spl;
+    spl[0] = c >> 4;
+    spl[1] = c & 0xF;
+    C[6] buffer;
+    buffer[0] = '\\';
+    buffer[1] = 'u';
+    buffer[2] = '0';
+    buffer[3] = '0';
+    buffer[4] = cast(ubyte)(spl[0] < 10 ? spl[0] + '0' : spl[0] - 10 + 'A');
+    buffer[5] = cast(ubyte)(spl[1] < 10 ? spl[1] + '0' : spl[1] - 10 + 'A');
+    return w.printStaticString(buffer);
+}
+
+/++
+Decodes ushort `c` to the form `uXXXX`, where `XXXX` is 2 hexadecimal characters.
++/
+ref W put_uXXXX(C = char, W)(scope return ref W w, ushort c)
+{
+    ubyte[4] spl;
+    spl[0] = (c >> 12) & 0xF;
+    spl[1] = (c >> 8) & 0xF;
+    spl[2] = (c >> 4) & 0xF;
+    spl[3] = c & 0xF;
+    C[6] buffer;
+    buffer[0] = '\\';
+    buffer[1] = 'u';
+    buffer[2] = cast(ubyte)(spl[0] < 10 ? spl[0] + '0' : spl[0] - 10 + 'A');
+    buffer[3] = cast(ubyte)(spl[1] < 10 ? spl[1] + '0' : spl[1] - 10 + 'A');
+    buffer[4] = cast(ubyte)(spl[2] < 10 ? spl[2] + '0' : spl[2] - 10 + 'A');
+    buffer[5] = cast(ubyte)(spl[3] < 10 ? spl[3] + '0' : spl[3] - 10 + 'A');
+    return w.printStaticString(buffer);
 }
 
 ///
-ref W printElement(C = char, W, T)(scope return ref W w, scope auto ref const T c)
+ref W printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope return ref W w, scope const(C)[] c)
+    if (isSomeChar!C)
 {
-    static if (isSomeString!T)
-    {
-        return printEscaped!C(w, c);
-    }
-    else
-    {
-        return print!C(w, c);
-    }
+    static immutable C[1] quote = '\"';
+    return w
+        .printStaticString!C(quote)
+        .printEscaped!C(c)
+        .printStaticString!C(quote);
+}
+
+///
+ref W printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)(scope return ref W w, scope auto ref const T c)
+    if (!isSomeString!T)
+{
+    return w.print!C(c);
 }
 
 /++
@@ -331,9 +399,9 @@ ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Arg
 {
     foreach(i, ref c; args)
         static if (i < Args.length - 1)
-            print!C(w, c);
+            w.print!C(c);
         else
-            return print!C(w, c);
+            return w.print!C(c);
 }
 
 ///
@@ -352,7 +420,7 @@ ref W print(C = char, W, T)(scope return ref W w, const T c)
     }
     static immutable C[] str = T.stringof ~ "(";
     w.put(str[]);
-    print(w, cast(OriginalType!T) c);
+    print!C(w, cast(OriginalType!T) c);
     w.put(')');
     return w;
 }
@@ -379,7 +447,7 @@ ref W print(C = char, W)(scope return ref W w, bool c)
     enum N = 5;
     static if(isFastBuffer!W)
     {
-        w.advance(printBoolean(c, w.getBuffer(N).getStaticBuf!N));
+        w.advance(printBoolean(c, w.getStaticBuf!N));
     }
     else
     {
@@ -414,11 +482,12 @@ ref W print(C = char, W, V, K)(scope return ref W w, scope const V[K] c)
     foreach (ref key, ref value; c)
     {
         if (!first)
-            printStaticStringInternal!(C, sep)(w);
+            w.printStaticString!C(sep);
         first = false;
-        printElement!C(w, key);
-        printStaticStringInternal!(C, mid)(w);
-        printElement!C(w, value);
+        w
+            .printElement!C(key)
+            .printStaticString!C(mid)
+            .printElement!C(value);
     }
     w.put(right);
     return w;
@@ -447,7 +516,7 @@ ref W print(C = char, W, T)(scope return ref W w, scope const(T)[] c)
     foreach (ref e; c)
     {
         if (!first)
-            printStaticStringInternal!(C, sep)(w);
+            w.printStaticString!C(sep);
         first = false;
         printElement!C(w, e);
     }
@@ -461,8 +530,8 @@ version (mir_test) unittest
 {
     import mir.appender: ScopedBuffer;
     ScopedBuffer!char w;
-    string[2] array = ["a\ta", "b"];
-    assert(w.print(array[]).data == `["a\ta", "b"]`, w.data);
+    string[2] array = ["a\na", "b"];
+    assert(w.print(array[]).data == `["a\na", "b"]`, w.data);
 }
 
 /// ditto
@@ -484,8 +553,8 @@ ref W print(C = char, W)(scope return ref W w, char c)
         else
         {
         M:
-            printStaticStringInternal!(C, "\\x")(w);
-            print!C(w, HexAddress!ubyte(cast(ubyte)c));
+            w.printStaticString!C(`\x`);
+            w.print!C(HexAddress!ubyte(cast(ubyte)c));
         }
     }
     else
@@ -554,7 +623,7 @@ ref W print(C = char, W, T)(scope return ref W w, const T c)
     if(is(T == float) || is(T == double) || is(T == real))
 {
     auto ff = FormattedFloating!T(c);
-    return print!C(w, ff);
+    return w.print!C(ff);
 }
 
 /// ditto
@@ -582,7 +651,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
     static if (__traits(compiles, { scope const(C)[] string_of_c = c; }))
     {
         scope const(C)[] string_of_c = c;
-        return print(w, string_of_c);
+        return w.print!C(string_of_c);
     }
     else
     static if (hasIterableLightConst!T)
@@ -595,9 +664,9 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
         foreach (ref e; c.lightConst)
         {
             if (!first)
-                printStaticStringInternal!(C, sep)(w);
+                w.printStaticString!C(sep);
             first = false;
-            print!C(w, e);
+            w.printStaticString!C(e);
         }
         w.put(right);
         return w;
@@ -611,7 +680,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
         foreach (i, ref e; c.tupleof)
         {
             static if (i)
-                printStaticStringInternal!(C, sep)(w);
+                w.printStaticString!C(sep);
             print!C(w, e);
         }
         w.put(right);
@@ -648,10 +717,11 @@ pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
     if (is(T == class) || is(T == interface))
 {
+    static immutable C[4] Null = "null";
     static if (__traits(hasMember, T, "toString") || __traits(compiles, { scope const(C)[] string_of_c = c; }))
     {
         if (c is null)
-            printStaticStringInternal!(C, "null")(w);
+            w.printStaticString!C(Null);
         else
         static if (is(typeof(c.toString!C(w))))
             c.toString!C(w);
@@ -668,7 +738,7 @@ ref W print(C = char, W, T)(scope return ref W w, scope const T c)
         static if (__traits(compiles, { scope const(C)[] string_of_c = c; }))
         {
             scope const(C)[] string_of_c = c;
-            return print(w, string_of_c);
+            return w.print!C(string_of_c);
         }
         else static assert(0, T.stringof ~ ".toString definition is wrong: 'const scope' qualifier may be missing.");
     }
@@ -683,7 +753,7 @@ ref W print(C = char, W, T)(scope return ref W w, scope const T c)
         foreach (ref e; c.lightConst)
         {
             if (!first)
-                printStaticStringInternal!(C, sep)(w);
+                w.printStaticString!C(sep);
             first = false;
             print!C(w, e);
         }
@@ -711,37 +781,41 @@ version (mir_test) unittest
     assert(stringBuf() << new A() << new S() << new D() << new F() << new G() << getData == "asdfg");
 }
 
-private template hasIterableLightConst(T)
+///
+ref W printStaticString(C, size_t N, W)(scope return ref W w, ref scope const C[N] c)
+    if (is(C == char) || is(C == wchar) || is(C == dchar))
 {
-    static if (__traits(hasMember, T, "lightConst"))
+    static if (isFastBuffer!W)
     {
-        enum hasIterableLightConst = isIterable!(ReturnType!((const T t) => t.lightConst));
+        enum immutable(ForeachType!(typeof(w.getBuffer(size_t.init))))[] value = c;
+        w.getStaticBuf!(value.length) = value;
+        w.advance(c.length);
     }
     else
     {
-        enum hasIterableLightConst = false;
+        w.put(c[]);
     }
+    return w;
 }
 
-private ref W printStaticStringInternal(C, immutable(C)[] c, W)(scope return ref W w)
-    if (C.sizeof * c.length <= 512)
+private template hasIterableLightConst(T)
 {
-    static if (isFastBuffer!W)
-    {
-        printStaticString!c(w.getBuffer(c.length).getStaticBuf!(c.length));
-        w.advance(c.length);
-    }
-    else
-    static if (c.length <= 4)
+    static if (__traits(hasMember, T, "lightConst"))
     {
-        static foreach(i; 0 .. c.length)
-            w.put(c[i]);
+        enum hasIterableLightConst = isIterable!(ReturnType!((const T t) => t.lightConst));
     }
     else
     {
-        w.put(c[]);
+        enum hasIterableLightConst = false;
     }
-    return w;
+}
+
+private ref C[N] getStaticBuf(size_t N, C, W)(scope return ref W w)
+    if (isFastBuffer!W)
+{
+    auto buf = w.getBuffer(N);
+    assert(buf.length >= N);
+    return buf.ptr[0 .. N];
 }
 
 private @trusted ref C[N] getStaticBuf(size_t N, C)(scope return ref C[] buf)
@@ -826,30 +900,3 @@ size_t printBoolean(C)(bool c, ref C[5] buf)
         return 5;
     }
 }
-
-///
-size_t printStaticString(string str, C)(scope ref C[str.length] buf)
-    if((is(C == char) || is(C == wchar) || is(C == dchar)) && (C[str.length]).sizeof <= 512)
-{
-    version(LDC) pragma(inline, true);
-    static foreach (i, e; str) buf[i] = e;
-    return buf.length;
-}
-
-/// ditto
-size_t printStaticString(wstring str, C)(scope ref C[str.length] buf)
-    if((is(C == wchar) || is(C == dchar)) && (C[str.length]).sizeof <= 512)
-{
-    version(LDC) pragma(inline, true);
-    static foreach (i, e; str) buf[i] = e;
-    return buf.length;
-}
-
-/// ditto
-size_t printStaticString(dstring str)(scope ref dchar[str.length] buf)
-    if((dchar[str.length]).sizeof <= 512)
-{
-    version(LDC) pragma(inline, true);
-    static foreach (i, e; str) buf[i] = e;
-    return buf.length;
-}

From ed88ca3d1b64657ee5a118569726e350d11e7c87 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 20 Oct 2020 09:10:19 +0700
Subject: [PATCH 106/573] update license

---
 LICENSE                              | 30 ++++++++++------------------
 dub.sdl                              |  6 +++---
 include/mir/ndslice.h                |  6 +++---
 index.d                              |  2 +-
 meson.build                          |  2 +-
 source/mir/algorithm/iteration.d     | 11 +++++-----
 source/mir/algorithm/setops.d        |  5 +++--
 source/mir/appender.d                |  4 ++--
 source/mir/array/allocation.d        |  6 +++---
 source/mir/combinatorics/package.d   |  2 +-
 source/mir/container/binaryheap.d    |  4 ++--
 source/mir/date.d                    |  4 ++--
 source/mir/format.d                  |  6 ++----
 source/mir/graph/package.d           |  6 +++---
 source/mir/graph/tarjan.d            |  6 +++---
 source/mir/interpolate/constant.d    |  6 +++---
 source/mir/interpolate/generic.d     |  6 +++---
 source/mir/interpolate/linear.d      |  6 +++---
 source/mir/interpolate/package.d     |  4 ++--
 source/mir/interpolate/polynomial.d  |  6 +++---
 source/mir/interpolate/spline.d      |  6 +++---
 source/mir/math/func/expdigamma.d    |  2 +-
 source/mir/math/numeric.d            |  4 ++--
 source/mir/math/stat.d               |  4 ++--
 source/mir/math/sum.d                |  4 ++--
 source/mir/ndslice/allocation.d      |  6 +++---
 source/mir/ndslice/chunks.d          |  6 +++---
 source/mir/ndslice/concatenation.d   |  6 +++---
 source/mir/ndslice/connect/cpython.d |  6 +++---
 source/mir/ndslice/dynamic.d         |  6 +++---
 source/mir/ndslice/field.d           |  6 +++---
 source/mir/ndslice/filling.d         |  6 +++---
 source/mir/ndslice/fuse.d            |  6 +++---
 source/mir/ndslice/iterator.d        |  6 +++---
 source/mir/ndslice/mutation.d        |  6 +++---
 source/mir/ndslice/ndfield.d         |  6 +++---
 source/mir/ndslice/package.d         |  6 +++---
 source/mir/ndslice/slice.d           |  6 +++---
 source/mir/ndslice/sorting.d         |  6 +++---
 source/mir/ndslice/topology.d        |  6 +++---
 source/mir/ndslice/traits.d          |  6 +++---
 source/mir/numeric.d                 |  4 ++--
 source/mir/parse.d                   |  6 +++---
 source/mir/polynomial.d              |  4 ++--
 source/mir/range.d                   |  6 +++---
 source/mir/series.d                  |  4 ++--
 source/mir/small_array.d             |  4 ++--
 source/mir/small_string.d            |  4 ++--
 source/mir/type_info.d               |  4 ++--
 49 files changed, 135 insertions(+), 145 deletions(-)

diff --git a/LICENSE b/LICENSE
index 36b7cd93..6ff8988b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,23 +1,13 @@
-Boost Software License - Version 1.0 - August 17th, 2003
+   Copyright 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
+   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
 
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
+       http://www.apache.org/licenses/LICENSE-2.0
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+   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.
diff --git a/dub.sdl b/dub.sdl
index b4cb44da..26685646 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -1,9 +1,9 @@
 name "mir-algorithm"
 description "Dlang Core Library"
 
-authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "Andrei Alexandrescu and Phobos Team (original Phobos code)"
-copyright "Copyright © 2016 - 2019, Ilya Yaroshenko; see also information per file."
-license "BSL-1.0"
+authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And others" "mir.date and a bit of other code is based on Phobos"
+copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
+license "Apache-2.0"
 
 dependency "mir-core" version=">=1.1.13"
 
diff --git a/include/mir/ndslice.h b/include/mir/ndslice.h
index 9f73e75d..f071c781 100644
--- a/include/mir/ndslice.h
+++ b/include/mir/ndslice.h
@@ -4,9 +4,9 @@
 The module provides wrappers for $(SUBREF slice, Slice) that
 can be used as arguments for C++ functions.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2017-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 */
 
 #ifndef MIR_NDSLICE
diff --git a/index.d b/index.d
index c0f5236e..9abb5483 100644
--- a/index.d
+++ b/index.d
@@ -58,7 +58,7 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,ndslice,connect,cpython)) $(TD Utilities for $(HTTPS docs.python.org/3/c-api/buffer.html, Python Buffer Protocol)))
 )
 
-Copyright: Copyright © 2016-, Ilya Yaroshenko.
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 
 Macros:
         TITLE=Mir Algorithm
diff --git a/meson.build b/meson.build
index bf24b792..f4ce95e1 100644
--- a/meson.build
+++ b/meson.build
@@ -1,4 +1,4 @@
-project('mir-algorithm', 'cpp', 'd', version : '3.7.0', license: 'BSL-1.0',
+project('mir-algorithm', 'cpp', 'd', version : '3.7.0', license: 'Apache-2.0')
         default_options : ['cpp_std=c++1z'])
 
 description = 'Mir Algorithm - Dlang Core Library'
diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 7bffe740..fa404178 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -35,12 +35,13 @@ Transform function is represented by $(NDSLICEREF topology, map).
 All operators are suitable to change slices using `ref` argument qualification in a function declaration.
 Note, that string lambdas in Mir are `auto ref` functions.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-2018, Ilya Yaroshenko, 2018-, Mir community
-Authors:   Ilya Yaroshenko, John Michael Hall, Andrei Alexandrescu (original Phobos code)
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko, John Michael Hall, Andrei Alexandrescu (original Phobos code)
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-Copyright: Andrei Alexandrescu 2008-. Ilya Yaroshenko 2017-
-License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
 Authors: , Ilya Yaroshenko (Mir & BetterC rework).
 Source: $(PHOBOSSRC std/algorithm/_iteration.d)
 Macros:
diff --git a/source/mir/algorithm/setops.d b/source/mir/algorithm/setops.d
index 5ba1c785..4ec593c0 100644
--- a/source/mir/algorithm/setops.d
+++ b/source/mir/algorithm/setops.d
@@ -2,8 +2,9 @@
 /**
 This is a submodule of $(MREF mir, algorithm). It contains `nothrow` `@nogc` BetterC alternative to `MultiwayMerge` from `std.algorithm.setops`.
 
-Copyright: Andrei Alexandrescu 2008-.
-License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+
 Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (Mir & BetterC rework, optimization).
  */
 module mir.algorithm.setops;
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 9cb8de1f..319e92c8 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -1,8 +1,8 @@
 /++
 $(H1 Scoped Buffer)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko
 +/
 module mir.appender;
 
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index 7e635245..4c1cb452 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -12,11 +12,11 @@ $(TR $(TH Function Name) $(TH Description)
     ))
 )
 
-Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-, and Ilya Yaroshenko (Mir rework)  2018-
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
-Authors:   $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis
+Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis
 
 Source: $(PHOBOSSRC std/_array.d)
 */
diff --git a/source/mir/combinatorics/package.d b/source/mir/combinatorics/package.d
index 85aa5949..3f9e9895 100644
--- a/source/mir/combinatorics/package.d
+++ b/source/mir/combinatorics/package.d
@@ -3,7 +3,7 @@ This module contains various combinatorics algorithms.
 
 Authors: Sebastian Wilzbach, Ilya Yaroshenko
 
-License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 */
 module mir.combinatorics;
 
diff --git a/source/mir/container/binaryheap.d b/source/mir/container/binaryheap.d
index 6f660458..2b79ecce 100644
--- a/source/mir/container/binaryheap.d
+++ b/source/mir/container/binaryheap.d
@@ -6,9 +6,9 @@ Current implementation is suitable for Mir/BetterC concepts.
 
 This module is a submodule of $(MREF mir, container).
 
-Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-License: Distributed under the Boost Software License, Version 1.0.
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
 Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (Mir & BetterC rework).
 */
diff --git a/source/mir/date.d b/source/mir/date.d
index 5b94b0b1..15395c15 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -24,8 +24,8 @@ $(TR $(TD Other) $(TD
     $(LREF DateTimeException)
 ))
 ))
-    License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
-    Authors:   $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like and BetterC rework)
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like and BetterC rework)
 +/
 module mir.date;
 
diff --git a/source/mir/format.d b/source/mir/format.d
index 3b17b2aa..5b4ad0ca 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -1,8 +1,8 @@
 /++
 $(H1 @nogc Formatting Utilities)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko
 +/
 module mir.format;
 
@@ -42,8 +42,6 @@ unittest
     assert(S("str").to!string == "str"); // GC allocated result
 }
 
-import std.traits;
-
 /// Concatenated string results
 string text(string separator = "", A...)(auto ref A args)
         if (A.length > 0)
diff --git a/source/mir/graph/package.d b/source/mir/graph/package.d
index d2e94967..3f6cdd63 100644
--- a/source/mir/graph/package.d
+++ b/source/mir/graph/package.d
@@ -1,9 +1,9 @@
 /++
 Basic routines to work with graphs.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2018-, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, graph, $1)$(NBSP)
diff --git a/source/mir/graph/tarjan.d b/source/mir/graph/tarjan.d
index 53e707a1..3e1000ba 100644
--- a/source/mir/graph/tarjan.d
+++ b/source/mir/graph/tarjan.d
@@ -3,9 +3,9 @@ This is a submodule of $(MREF mir,graph).
 
 Tarjan's strongly connected components algorithm.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2018-, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 1521fbd6..36335ed3 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -3,9 +3,9 @@ $(H2 Constant Interpolation)
 
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2017, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index 5aa72505..63f0a11b 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -3,9 +3,9 @@ $(H2 Generic Piecewise Interpolant)
 
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2020, Kaleidic Associates Advisory Limited and Symmetry Investments
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 07987687..28ded947 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -3,9 +3,9 @@ $(H2 Linear Interpolation)
 
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2017, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/package.d b/source/mir/interpolate/package.d
index ca7a4832..b55d2f21 100644
--- a/source/mir/interpolate/package.d
+++ b/source/mir/interpolate/package.d
@@ -10,8 +10,8 @@ $(T2M polynomial, Lagrange Barycentric Interpolant)
 $(T2M spline, Piecewise Cubic Hermite Interpolant Spline: C2 (with contiguous second derivative), cardinal, monotone (aka PCHIP), double-quadratic, Akima)
 )
 ]
-Copyright: Copyright © 2019, Symmetry Investments & Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir,interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index 04643ea7..a0b31cfb 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -3,9 +3,9 @@ $(H2 Lagrange Barycentric Interpolation)
 
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2019, Symmetry Investments & Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index c2fe8db3..59372cfd 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -5,9 +5,9 @@ The module provides common C2 splines, monotone (PCHIP) splines, Akima splines a
 
 See_also: $(LREF SplineType), $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2017, Kaleidic Associates Advisory Limited and Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/math/func/expdigamma.d b/source/mir/math/func/expdigamma.d
index 608682e5..07a1dc34 100644
--- a/source/mir/math/func/expdigamma.d
+++ b/source/mir/math/func/expdigamma.d
@@ -1,5 +1,5 @@
 /**
-License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
 Authors: Ilya Yaroshenko
 */
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index ffd82daa..8b6001c0 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -1,8 +1,8 @@
 /++
 This module contains simple numeric algorithms.
-License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 Authors: Ilya Yaroshenko
-Copyright: 2015-, Ilya Yaroshenko; 2017 Symmetry Investments Group and Kaleidic Associates Advisory Limited.
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 Sponsors: This work has been sponsored by $(SUBREF http://symmetryinvestments.com, Symmetry Investments) and Kaleidic Associates.
 +/
 module mir.math.numeric;
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 1260bf8c..73be7eb1 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -5,11 +5,11 @@ Note that used specialized summing algorithms execute more primitive operations
 than vanilla summation. Therefore, if in certain cases maximum speed is required
 at expense of precision, one can use $(REF_ALTTEXT $(TT Summation.fast), Summation.fast, mir, math, sum)$(NBSP).
 
-License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
 Authors: Shigeki Karita (original numir code), Ilya Yaroshenko, John Michael Hall
 
-Copyright: 2019 Symmetry Investments Group and Kaleidic Associates Advisory Limited.
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, math, $1)$(NBSP)
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 57120a84..f7275856 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1,11 +1,11 @@
 /++
 This module contains summation algorithms.
 
-License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
 Authors: Ilya Yaroshenko
 
-Copyright: Copyright © 2015-, Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 +/
 module mir.math.sum;
 
diff --git a/source/mir/ndslice/allocation.d b/source/mir/ndslice/allocation.d
index bcbf39c0..5158066f 100644
--- a/source/mir/ndslice/allocation.d
+++ b/source/mir/ndslice/allocation.d
@@ -43,9 +43,9 @@ $(T2 stdcUninitAlignedSlice, Allocates an uninitialized aligned slice using CRun
 $(T2 stdcFreeAlignedSlice, Frees memory using CRuntime)
 )
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/chunks.d b/source/mir/ndslice/chunks.d
index 0dd27966..97dff53c 100644
--- a/source/mir/ndslice/chunks.d
+++ b/source/mir/ndslice/chunks.d
@@ -6,9 +6,9 @@ $(LREF Chunks) structure is multidimensional random access range with slicing.
 
 $(SUBREF slice, slicedField), $(SUBREF slice, slicedNdField) can be used to construct ndslice view on top of $(LREF Chunks).
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index 82d612a1..f5db696d 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -19,9 +19,9 @@ $(T2 padWrap, Pads with the wrap of the slice along the axis. The first values a
 )
 
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2017-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 See_also: $(SUBMODULE fuse) submodule.
 
diff --git a/source/mir/ndslice/connect/cpython.d b/source/mir/ndslice/connect/cpython.d
index cf774c65..895bb335 100644
--- a/source/mir/ndslice/connect/cpython.d
+++ b/source/mir/ndslice/connect/cpython.d
@@ -1,9 +1,9 @@
 /++
 Utilities for $(LINK2 https://docs.python.org/3/c-api/buffer.html, Python Buffer Protocol).
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2017-, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/dynamic.d b/source/mir/ndslice/dynamic.d
index 0c107ade..7817fd2f 100644
--- a/source/mir/ndslice/dynamic.d
+++ b/source/mir/ndslice/dynamic.d
@@ -64,11 +64,11 @@ is identical to that of $(LREF strided).
 Bifacial interface of $(LREF dropOne) and $(LREF dropBackOne)
 is identical to that of $(LREF reversed).
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
-Copyright: Copyright © 2016, Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-Authors:   Ilya Yaroshenko
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d
index be31722e..bc79c9ea 100644
--- a/source/mir/ndslice/field.d
+++ b/source/mir/ndslice/field.d
@@ -21,9 +21,9 @@ $(T2 SparseField, Used for mutable DOK sparse matrixes )
 
 
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/filling.d b/source/mir/ndslice/filling.d
index b0657baf..d151a9a7 100644
--- a/source/mir/ndslice/filling.d
+++ b/source/mir/ndslice/filling.d
@@ -3,9 +3,9 @@ This is a submodule of $(MREF mir,ndslice).
 
 Initialisation routines.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: 2019 Symmetry Investments Group and Kaleidic Associates Advisory Limited.
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index 48e06200..7d03ba82 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -3,9 +3,9 @@ This is a submodule of $(MREF mir,ndslice).
 
 Allocation routines that construct ndslices from ndranges.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2018-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 See_also: $(SUBMODULE concatenation) submodule.
 
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 0e07190c..cf87c45d 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -25,9 +25,9 @@ $(T2 TripletIterator, $(SUBREF topology, triplets))
 $(T2 ZipIterator, $(SUBREF topology, zip))
 )
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/mutation.d b/source/mir/ndslice/mutation.d
index 10265f94..d1d81b54 100644
--- a/source/mir/ndslice/mutation.d
+++ b/source/mir/ndslice/mutation.d
@@ -7,9 +7,9 @@ $(BOOKTABLE $(H2 Function),
 $(TR $(TH Function Name) $(TH Description))
 )
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2020-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/ndfield.d b/source/mir/ndslice/ndfield.d
index e6b603db..90fb32e7 100644
--- a/source/mir/ndslice/ndfield.d
+++ b/source/mir/ndslice/ndfield.d
@@ -12,9 +12,9 @@ $(T2 Kronecker, $(SUBREF topology, kronecker))
 
 See_also: $(SUBREF concatenation, concatenation).
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index 28dd0fb1..5f729047 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -477,9 +477,9 @@ whole set of standard D library, so the functions he creates will be as
 efficient as if they were written in C.
 
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 Acknowledgements: John Loughran Colvin
 
 Macros:
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 50647503..a61c2516 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -5,9 +5,9 @@ Safety_note:
     User-defined iterators should care about their safety except bounds checks.
     Bounds are checked in ndslice code.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 $(BOOKTABLE $(H2 Definitions),
 $(TR $(TH Name) $(TH Description))
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index de260a53..e1c8afbf 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -14,9 +14,9 @@ See_also: $(SUBREF topology, flattened)
 
 `isSorted` and `isStrictlyMonotonic`
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Andrei Alexandrescu 2008-2016, Ilya Yaroshenko 2016-, 
-Authors:   Andrei Alexandrescu (Phobos), Ilya Yaroshenko (API, rework, Mir adoptation)
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Andrei Alexandrescu (Phobos), Ilya Yaroshenko (API, rework, Mir adoptation)
 
 Macros:
     SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index a7c20441..2d7884ce 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -95,9 +95,9 @@ Examples of use of subspace selectors are available for selectors,
 $(SUBREF slice, Slice.shape), and $(SUBREF slice, Slice.elementCount).
 
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2016-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko, John Michael Hall, Shigeki Karita (original numir code)
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko, John Michael Hall, Shigeki Karita (original numir code)
 
 Sponsors: Part of this work has been sponsored by $(LINK2 http://symmetryinvestments.com, Symmetry Investments) and Kaleidic Associates.
 
diff --git a/source/mir/ndslice/traits.d b/source/mir/ndslice/traits.d
index b9b7c14a..85e18018 100644
--- a/source/mir/ndslice/traits.d
+++ b/source/mir/ndslice/traits.d
@@ -19,9 +19,9 @@ $(T2 isUniversalMatrix, Test if type is a universal two-dimensional slice.)
 $(T2 isIterator, Test if type is a random access iterator.)
 )
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright $(COPYRIGHT) 2016-, Ilya Yaroshenko, John Hall
-Authors:   John Hall
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: John Hall
 
 
 Macros:
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index a90d0e07..3f710f37 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -3,8 +3,8 @@ Base numeric algorithms.
 
 Reworked part of `std.numeric`.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors:   Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot)
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot)
 +/
 module mir.numeric;
 
diff --git a/source/mir/parse.d b/source/mir/parse.d
index d779af63..deb3c990 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -1,9 +1,9 @@
 /++
 $(H1 @nogc and nothrow Parsing Utilities)
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors:   Ilya Yaroshenko
-Copyright: Symmetry Investments and Kaleidic Associates
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 +/
 module mir.parse;
 
diff --git a/source/mir/polynomial.d b/source/mir/polynomial.d
index 75bf8ceb..641d3db6 100644
--- a/source/mir/polynomial.d
+++ b/source/mir/polynomial.d
@@ -1,8 +1,8 @@
 /++
 Polynomial ref-counted structure.
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors:   Ilya Yaroshenko
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko
 +/
 module mir.polynomial;
 
diff --git a/source/mir/range.d b/source/mir/range.d
index c487cbb2..6f88f9dc 100644
--- a/source/mir/range.d
+++ b/source/mir/range.d
@@ -3,9 +3,9 @@ Ranges.
 
 See_also: $(MREF mir,_primitives).
 
-License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Copyright: Copyright © 2017-, Ilya Yaroshenko
-Authors:   Ilya Yaroshenko, Phobos Authors
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko, Phobos Authors
 +/
 module mir.range;
 
diff --git a/source/mir/series.d b/source/mir/series.d
index 870cfe9a..c72c153e 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -6,8 +6,8 @@ It is aimed to construct index or time-series using Mir and Phobos algorithms.
 
 Public_imports: $(MREF mir,ndslice,slice).
 
-Copyright: Copyright © 2017, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index c1122e05..f0cf62b1 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -5,8 +5,8 @@ The module contains self-contained generic small array implementaton.
 
 $(LREF SmallArray) supports ASDF - Json Serialisation Library.
 
-Copyright: Copyright © 2019, Symmetry Investments, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 +/
 module mir.small_array;
 
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index c037570a..ed1bec9e 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -8,8 +8,8 @@ $(LREF SmallString) supports ASDF - Json Serialisation Library.
 See also `include/mir/small_series.h` for a C++ version of this type.
 Both C++ and D implementations have the same ABI and name mangling.
 
-Copyright: Copyright © 2019, Symmetry Investments, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 +/
 module mir.small_string;
 
diff --git a/source/mir/type_info.d b/source/mir/type_info.d
index cfc4c51e..804a3947 100644
--- a/source/mir/type_info.d
+++ b/source/mir/type_info.d
@@ -3,8 +3,8 @@ $(H1 Type Information)
 
 Type Information implementation compatible with BetterC mode.
 
-Copyright: Copyright © 2019, Kaleidic Associates Advisory Limited
-Authors:   Ilya Yaroshenko
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
 
 Macros:
 NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)

From e7975ec105b287bad1f033f036f0c14c72396a63 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 20 Oct 2020 09:13:48 +0700
Subject: [PATCH 107/573] fix format

---
 source/mir/format.d | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source/mir/format.d b/source/mir/format.d
index 5b4ad0ca..115fbd1e 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -6,6 +6,8 @@ Authors: Ilya Yaroshenko
 +/
 module mir.format;
 
+import std.traits;
+
 /// `mir.conv: to` extension.
 version(mir_test)
 @safe pure @nogc

From 13130146ad25cce82acbdd63fcf6e30979f7f402 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 20 Oct 2020 09:35:02 +0700
Subject: [PATCH 108/573] add GC support to mir.variant

---
 source/mir/variant.d | 52 +++++++++++++++++++++++++-------------------
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index e402df9d..c16ffca6 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -14,8 +14,6 @@ version (D_Exceptions)
     private static immutable variantMemberException = new Exception(variantMemberExceptionMsg);
 }
 
-private enum int alignOf(T) = T.alignof;
-
 private enum hasToHash(T) = __traits(hasMember, T, "toHash");
 private enum hasOpEquals(T) = __traits(hasMember, T, "opEquals");
 private enum isPOD(T) = __traits(isPOD, T);
@@ -36,8 +34,14 @@ struct Variant(Types...)
 
     private alias _Types = Types;
 
+    union
+    {
+        private Types payload;
+        private ubyte[Largest!Types.sizeof] bytes;
+    }
+
     private uint type; // 0 for unininit value, index = type - 1
-    private align(max(staticMap!(alignOf, Types))) ubyte[Largest!Types.sizeof] payload;
+
     private enum hasDestructor = anySatisfy!(hasElaborateDestructor, Types);
 
     static if (hasDestructor)
@@ -80,7 +84,7 @@ struct Variant(Types...)
     {
         static if (hasDestructor)
             __dtor;
-        payload[] = 0;
+        bytes[] = 0;
         type = 0;
     }
 
@@ -93,12 +97,12 @@ struct Variant(Types...)
         type = i + 1;
         static if (__traits(isZeroInit, T))
         {
-            payload[] = 0;
+            bytes[] = 0;
         }
         else
         {
             emplaceRef(trustedGet!T);
-            payload[T.sizeof .. $] = 0;
+            bytes[T.sizeof .. $] = 0;
         }
         swap(trustedGet!T, value);
     }
@@ -115,7 +119,8 @@ struct Variant(Types...)
 
     static foreach (i, T; Types)
     ///
-    ref inout(T) get(E : T)() @property return inout
+    ref inout(T) get(E)() @property return inout
+        if (is(E == T))
     {
         import mir.utility: _expect;
         if (_expect(i + 1 != type, false))
@@ -140,10 +145,11 @@ struct Variant(Types...)
 
     static foreach (i, T; Types)
     /// Zero cost always nothrow `get` alternative
-    ref inout(T) trustedGet(E : T)() @trusted @property return inout nothrow
+    ref inout(T) trustedGet(E)() @trusted @property return inout nothrow
+        if (is(E == T))
     {
         assert (i + 1 == type);
-        return *cast(inout(T)*)payload.ptr;
+        return payload[i];
     }
 
     /++
@@ -167,23 +173,25 @@ struct Variant(Types...)
     +/
     size_t toHash()
     {
-        static if (this.sizeof <= 16)
-        {
-            return hashOf(payload, type);
-        }
-        else
         static if (allSatisfy!(isPOD, Types))
         {
-            static if (payload.length <= ubyte.max)
-                alias UInt = ubyte;
-            else
-            static if (payload.length <= ushort.max)
-                alias UInt = ushort;
+            static if (this.sizeof <= 16)
+            {
+                return hashOf(bytes, type);
+            }
             else
-                alias UInt = uint;
+            {
+                static if (this.sizeof <= ubyte.max)
+                    alias UInt = ubyte;
+                else
+                static if (this.sizeof <= ushort.max)
+                    alias UInt = ushort;
+                else
+                    alias UInt = uint;
 
-            static immutable UInt[Types.length + 1] sizes = [0, staticMap!(Sizeof, Types)];
-            return hashOf(payload[0 .. sizes[type]], type);
+                static immutable UInt[Types.length + 1] sizes = [0, staticMap!(Sizeof, Types)];
+                return hashOf(bytes[0 .. sizes[type]], type);
+            }
         }
         else
         switch (type)

From 0cdd7e00a69ebe6c612fefca190da5e2d95eb5c8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 22 Oct 2020 15:06:01 +0700
Subject: [PATCH 109/573] fix variant and printelement

---
 source/mir/format.d  | 4 ++--
 source/mir/variant.d | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 115fbd1e..1fba728d 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -380,7 +380,7 @@ ref W printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
     static immutable C[1] quote = '\"';
     return w
         .printStaticString!C(quote)
-        .printEscaped!C(c)
+        .printEscaped!(C, escapeFormat)(c)
         .printStaticString!C(quote);
 }
 
@@ -717,7 +717,7 @@ pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
     if (is(T == class) || is(T == interface))
 {
-    static immutable C[4] Null = "null";
+    enum C[4] Null = "null";
     static if (__traits(hasMember, T, "toString") || __traits(compiles, { scope const(C)[] string_of_c = c; }))
     {
         if (c is null)
diff --git a/source/mir/variant.d b/source/mir/variant.d
index c16ffca6..2c1ea427 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -36,8 +36,8 @@ struct Variant(Types...)
 
     union
     {
-        private Types payload;
         private ubyte[Largest!Types.sizeof] bytes;
+        private Types payload;
     }
 
     private uint type; // 0 for unininit value, index = type - 1

From fc9ba9d4ea6fcf06c6beac91a276e41f1d3930cd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 27 Oct 2020 10:05:01 +0700
Subject: [PATCH 110/573] fix meson

---
 meson.build | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/meson.build b/meson.build
index f4ce95e1..e69af3b4 100644
--- a/meson.build
+++ b/meson.build
@@ -1,4 +1,4 @@
-project('mir-algorithm', 'cpp', 'd', version : '3.7.0', license: 'Apache-2.0')
+project('mir-algorithm', 'cpp', 'd', version : '3.7.0', license: 'Apache-2.0',
         default_options : ['cpp_std=c++1z'])
 
 description = 'Mir Algorithm - Dlang Core Library'

From 74533aa7f7e6c3f172878491a872dcd8fa508cb4 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 29 Oct 2020 18:35:34 +0700
Subject: [PATCH 111/573] variant: add match, tryMatch, getMember, tryGetMember

---
 dub.sdl              |   2 +-
 source/mir/variant.d | 133 +++++++++++++++++++++++++++++++++++++++----
 2 files changed, 124 insertions(+), 11 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 26685646..3dca3223 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.1.13"
+dependency "mir-core" version=">=1.1.14"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
diff --git a/source/mir/variant.d b/source/mir/variant.d
index 2c1ea427..34ea279d 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -3,14 +3,16 @@ This module implements a generic variant type.
 +/
 module mir.variant;
 
+import mir.functional: naryFun;
+
 private static immutable variantExceptionMsg = "mir.variant: the variant stores other type then requested.";
-private static immutable variantNulllExceptionMsg = "mir.variant: the variant is empty and doesn't store any value.";
+private static immutable variantNullExceptionMsg = "mir.variant: the variant is empty and doesn't store any value.";
 private static immutable variantMemberExceptionMsg = "mir.variant: the variant is stores the 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 variantNulllException = new Exception(variantNulllExceptionMsg);
+    private static immutable variantNullException = new Exception(variantNullExceptionMsg);
     private static immutable variantMemberException = new Exception(variantMemberExceptionMsg);
 }
 
@@ -128,9 +130,9 @@ struct Variant(Types...)
             if (i == 0)
             {
                 version(D_Exceptions)
-                    throw variantNulllException;
+                    throw variantNullException;
                 else
-                    assert(0, variantNulllExceptionMsg);
+                    assert(0, variantNullExceptionMsg);
             }
             else
             {
@@ -296,12 +298,123 @@ unittest
     }}
 }
 
+/++
+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 match(visitors...) = visit!(naryFun!visitors, true);
+
+///
+@safe pure @nogc
+unittest
+{
+    alias Number = Variant!(int, double);
+
+    Number x = 23;
+    Number y = 1.0;
+
+    assert(x.match!((int v) => true, (float v) => false));
+    assert(y.match!((int v) => false, (float v) => true));
+}
+
+/++
+Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
++/
+alias tryMatch(visitors...) = visit!(naryFun!visitors, false);
+
+///
+@safe pure @nogc
+unittest
+{
+    alias Number = Variant!(int, double);
+
+    Number x = 23;
+
+    assert(x.tryMatch!((int v) => true));
+}
+
+/++
+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) = visit!(getMemberHandler!member, true);
+
+///
+@safe pure @nogc
+unittest
+{
+    static struct S { int bar(int a) { return a; }}
+    static struct C { alias bar = (double a) => a * 2; }
+
+    alias V = Variant!(S, C);
+
+    V x = S();
+    V y = C();
+
+    assert(x.getMember!"bar"(2) == 2);
+    assert(y.getMember!"bar"(2) == 4);
+}
+
+/++
+Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the member visitor.
+Throws: Exception
++/
+alias tryGetMember(string member) = visit!(getMemberHandler!member, false);
+
+///
+version(mir_test)
+@safe pure @nogc
+unittest
+{
+    static struct S { int bar(int a) { return a; }}
+    static struct C { alias Bar = (double a) => a * 2; }
+
+    alias V = Variant!(S, C);
+
+    V x = S();
+    V y = C();
+
+    assert(x.tryGetMember!"bar"(2) == 2);
+    assert(y.tryGetMember!"Bar"(2) == 4);
+}
+
+///
+version(mir_test)
+@safe pure @nogc
+unittest
+{
+    alias Number = Variant!(int, double);
+
+    Number x = Number(23);
+    Number y = Number(1.0);
+
+    assert(x.match!((int v) => true, (float v) => false));
+    assert(y.match!((int v) => false, (float v) => true));
+}
+
+private template getMemberHandler(string member)
+{
+    ///
+    auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args)
+    {
+        static if (Args.length == 0)
+        {
+            return __traits(getMember, value, member);
+        }
+        else
+        {
+            import core.lifetime: forward;
+            return __traits(getMember, value, member)(forward!args);
+        }
+    }
+}
+
 /++
 Params:
     visitor = a function name alias
-    forceAllTypes = if `true` checks at compile time, that the visitor can be called for all types.
+    exhaustive = if `true` checks at compile time, that the visitor can be called for all types.
 +/
-template visit(alias visitor, bool forceAllTypes = true)
+template visit(alias visitor, bool exhaustive = true)
 {
     import std.traits: Unqual;
     ///
@@ -313,17 +426,17 @@ template visit(alias visitor, bool forceAllTypes = true)
         {
             case 0:
                 version(D_Exceptions)
-                    throw variantNulllException;
+                    throw variantNullException;
                 else
-                    assert(0, variantNulllExceptionMsg);
+                    assert(0, variantNullExceptionMsg);
             static foreach (i, T; V._Types)
-            static if (forceAllTypes || __traits(compiles, visitor(variant.trustedGet!T, forward!args)))
+            static if (exhaustive || __traits(compiles, visitor(variant.trustedGet!T, forward!args)))
             {
                 case i + 1:
                     return visitor(variant.trustedGet!T, forward!args);
             }
             else
-            static if (forceAllTypes)
+            static if (exhaustive)
                 static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ T.stringof ~ " and additional arguments " ~ Args.stringof);
             default:
                 version(D_Exceptions)

From 53e176cab9fa0f470a8428d7f5bb1e880f2c7046 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 30 Oct 2020 20:50:56 +0700
Subject: [PATCH 112/573] improve variant

---
 source/mir/variant.d | 253 ++++++++++++++++++++++++++++++-------------
 1 file changed, 176 insertions(+), 77 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 34ea279d..fd42e4d0 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -32,14 +32,14 @@ struct Variant(Types...)
     import mir.conv: emplaceRef;
     import mir.utility: max, swap;
     import std.meta: anySatisfy, allSatisfy, templateOr, staticMap;
-    import std.traits: Largest, hasElaborateDestructor, hasElaborateAssign, hasElaborateCopyConstructor, isEqualityComparable, isOrderingComparable;
+    import std.traits: Largest, isCopyable, hasElaborateDestructor, hasElaborateAssign, hasElaborateCopyConstructor, isEqualityComparable, isOrderingComparable;
 
     private alias _Types = Types;
 
     union
     {
-        private ubyte[Largest!Types.sizeof] bytes;
         private Types payload;
+        private ubyte[Largest!Types.sizeof] bytes;
     }
 
     private uint type; // 0 for unininit value, index = type - 1
@@ -51,43 +51,126 @@ struct Variant(Types...)
     {
         S: switch (type)
         {
-            static foreach (i, T; Types) static if (hasElaborateDestructor!T)
+            static foreach (i, T; Types)
+            static if (hasElaborateDestructor!T)
             {
-                case i + 1:
+                case i:
                     .destroy!false(trustedGet!T);
                     break S;
             }
-            default: break;
+            default: return;
         }
     }
 
+    static if (!__traits(compiles, (){ Types[0] arg; }))
+        @disable this();
+
+    static if (!allSatisfy!(isCopyable, Types))
+        @disable this(this);
+    else
     static if (anySatisfy!(hasElaborateCopyConstructor, Types))
-    this(this)
     {
-        S: switch (type)
+        ///
+        this(ref return scope typeof(this) rhs)
         {
-            static foreach (i, T; Types) static if (hasElaborateCopyConstructor!T)
+            this.bytes = rhs.bytes;
+            this.type = rhs.type;
+            S: switch (type)
             {
-                case i + 1:
-                    trustedGet!T.__xpostblit();
-                    break S;
+                static foreach (i, T; Types)
+                static if (hasElaborateCopyConstructor!T)
+                {
+                    case i:
+                        this.trustedGet!T.emplaceRef(rhs.trustedGet!T);
+                        return;
+                }
+                default: return;
             }
-            default: break;
         }
-    }
 
-    ///
-    this(typeof(null))
-    {
+        /// ditto
+        this(ref return scope const typeof(this) rhs) const
+        {
+            this.bytes = rhs.bytes;
+            this.type = rhs.type;
+            S: switch (type)
+            {
+                static foreach (i, T; Types)
+                static if (hasElaborateCopyConstructor!T)
+                {
+                    case i:
+                        this.mutableTrustedGet!T.emplaceRef!(const T)(rhs.trustedGet!T);
+                        return;
+                }
+                default: return;
+            }
+        }
+
+        /// ditto
+        this(ref return scope immutable typeof(this) rhs) immutable
+        {
+            this.bytes = rhs.bytes;
+            this.type = rhs.type;
+            S: switch (type)
+            {
+                static foreach (i, T; Types)
+                static if (hasElaborateCopyConstructor!T)
+                {
+                    case i:
+                        this.mutableTrustedGet!T.emplaceRef!(immutable T)(rhs.trustedGet!T);
+                        return;
+                }
+                default: return;
+            }
+        }
     }
 
-    ///
-    void opAssign(typeof(null))
+    static if (is(Types[0] == typeof(null)))
     {
-        static if (hasDestructor)
-            __dtor;
-        bytes[] = 0;
-        type = 0;
+        /// Defined if the first type is `typeof(null)`
+        bool isNull() const { return type == 0; }
+        /// ditto
+        void nullify() { this = null; }
+        /// ditto
+        static if (allSatisfy!(isCopyable, Types[1 .. $]))
+        auto get()
+        {
+            if (!type)
+            {
+                version(D_Exceptions)
+                    throw variantNullException;
+                else
+                    assert(0, variantNullExceptionMsg);
+            }
+
+            Variant!(Types[1 .. $]) ret;
+            ret.type = this.type - 1;
+
+            static if (anySatisfy!(hasElaborateCopyConstructor, Types))
+            {
+                ret.bytes = 0;
+                S: switch (type)
+                {
+                    static foreach (i, T; Types)
+                    {
+                        static if (hasElaborateCopyConstructor!T)
+                        {
+                            case i:
+                                ret.trustedGet!T.emplaceRef(this.trustedGet!T);
+                                break S;
+                        }
+                    }
+                    default:
+                        ret.bytes = this.bytes[0 .. ret.bytes.length];
+                }
+            }
+            else
+            {
+                ret.bytes = this.bytes[0 .. ret.bytes.length];
+            }
+
+            return ret;
+        }
     }
 
     static foreach (i, T; Types)
@@ -96,7 +179,7 @@ struct Variant(Types...)
     {
         static if (hasDestructor)
             __dtor;
-        type = i + 1;
+        type = i;
         static if (__traits(isZeroInit, T))
         {
             bytes[] = 0;
@@ -113,7 +196,7 @@ struct Variant(Types...)
     ///
     this(T value)
     {
-        type = i + 1;
+        type = i;
         static if (!__traits(isZeroInit, T))
             emplaceRef(trustedGet!T);
         swap(trustedGet!T, value);
@@ -125,22 +208,12 @@ struct Variant(Types...)
         if (is(E == T))
     {
         import mir.utility: _expect;
-        if (_expect(i + 1 != type, false))
+        if (_expect(i != type, false))
         {
-            if (i == 0)
-            {
-                version(D_Exceptions)
-                    throw variantNullException;
-                else
-                    assert(0, variantNullExceptionMsg);
-            }
+            version(D_Exceptions)
+                throw variantException;
             else
-            {
-                version(D_Exceptions)
-                    throw variantException;
-                else
-                    assert(0, variantExceptionMsg);
-            }
+                assert(0, variantExceptionMsg);
         }
         return trustedGet!T;
     }
@@ -150,22 +223,22 @@ struct Variant(Types...)
     ref inout(T) trustedGet(E)() @trusted @property return inout nothrow
         if (is(E == T))
     {
-        assert (i + 1 == type);
+        assert (i == type);
         return payload[i];
     }
 
-    /++
-    Returns: true for the unassigned instance.
-    +/
-    bool empty() nothrow const @property
+    static foreach (i, T; Types)
+    private ref T mutableTrustedGet(E)() @trusted @property return const nothrow
+        if (is(E == T))
     {
-        return type == 0;
+        assert (i == type);
+        return *cast(Types[i]*)&payload[i];
     }
 
     /++
-    Returns: zero if the instance is unassigned and type index starting with 1 otherwise.
+    Returns: zero based type index.
     +/
-    uint typeId() nothrow const @property
+    uint typeId() @nogc nothrow const @property
     {
         return type;
     }
@@ -200,11 +273,10 @@ struct Variant(Types...)
         {
             static foreach (i, T; Types)
             {
-                case i + 1:
+                case i:
                     return hashOf(trustedGet!T, type);
             }
-            default:
-                return 0;
+            default: assert(0);
         }
     }
 
@@ -219,11 +291,10 @@ struct Variant(Types...)
         {
             static foreach (i, T; Types)
             {
-                case i + 1:
+                case i:
                     return this.trustedGet!T == rhs.trustedGet!T;
             }
-            default:
-                return true;
+            default: assert(0);
         }
     } 
 
@@ -232,21 +303,20 @@ struct Variant(Types...)
     +/
     auto opCmp(ref const typeof(this) rhs) const
     {
-        if (int d = this.type - rhs.type)
+        if (auto d = int(this.type) - int(rhs.type))
             return d;
         switch (type)
         {
             static foreach (i, T; Types)
             {
-                case i + 1:
+                case i:
                     static if (__traits(hasMember, T, "opCmp"))
                         return this.trustedGet!T.opCmp(rhs.trustedGet!T);
                     else
                         return this.trustedGet!T < rhs.trustedGet!T ? -1 :
                                this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
             }
-            default:
-                return 0;
+            default: assert(0);
         }
     }
 }
@@ -305,7 +375,7 @@ ensuring that all types are handled by the visiting functions.
 alias match(visitors...) = visit!(naryFun!visitors, true);
 
 ///
-@safe pure @nogc
+@safe pure @nogc nothrow
 unittest
 {
     alias Number = Variant!(int, double);
@@ -317,6 +387,36 @@ unittest
     assert(y.match!((int v) => false, (float v) => true));
 }
 
+
+/// Special `typeof(null)` support for the first argument.
+@safe pure @nogc
+unittest
+{
+    alias Number = Variant!(typeof(null), int, double);
+
+    Number z = null; // default
+    Number x = 23;
+    Number y = 1.0;
+
+    () nothrow {
+        assert(x.match!((int v) => true, (float v) => false));
+        assert(y.match!((int v) => false, (float v) => true));
+        assert(z.match!((typeof(null) v) => true, (v) => false));
+    } ();
+
+    auto xx = x.get;
+    static assert (is(typeof(xx) == Variant!(int, double)));
+    assert(xx.match!((int v) => v, (float v) => 0) == 23);
+
+    x = null;
+    y.nullify;
+
+    assert(x.isNull);
+    assert(y.isNull);
+    assert(z.isNull);
+    assert(z == y);
+}
+
 /++
 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
 +/
@@ -340,10 +440,10 @@ ensuring that all types are handled by the visiting handler.
 alias getMember(string member) = visit!(getMemberHandler!member, true);
 
 ///
-@safe pure @nogc
+@safe pure @nogc nothrow
 unittest
 {
-    static struct S { int bar(int a) { return a; }}
+    static struct S { auto bar(int a) { return a; }}
     static struct C { alias bar = (double a) => a * 2; }
 
     alias V = Variant!(S, C);
@@ -380,7 +480,7 @@ unittest
 
 ///
 version(mir_test)
-@safe pure @nogc
+@safe pure @nogc nothrow
 unittest
 {
     alias Number = Variant!(int, double);
@@ -424,25 +524,24 @@ template visit(alias visitor, bool exhaustive = true)
         import core.lifetime: forward;
         switch (variant.type)
         {
-            case 0:
-                version(D_Exceptions)
-                    throw variantNullException;
-                else
-                    assert(0, variantNullExceptionMsg);
             static foreach (i, T; V._Types)
-            static if (exhaustive || __traits(compiles, visitor(variant.trustedGet!T, forward!args)))
             {
-                case i + 1:
-                    return visitor(variant.trustedGet!T, forward!args);
+                case i:
+                    static if (__traits(compiles, visitor(variant.trustedGet!T, forward!args)))
+                        return visitor(variant.trustedGet!T, forward!args);
+                    else
+                    static if (exhaustive && !is(T == typeof(null)))
+                        static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ T.stringof ~ " and additional arguments " ~ Args.stringof);
+                    else
+                    static if (is(T == typeof(null)) && i == 0)
+                        assert(0, "Null " ~ V.stringof);
+                    else
+                    version(D_Exceptions)
+                        throw variantMemberException;
+                    else
+                        assert(0, variantMemberExceptionMsg);
             }
-            else
-            static if (exhaustive)
-                static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ T.stringof ~ " and additional arguments " ~ Args.stringof);
-            default:
-                version(D_Exceptions)
-                    throw variantMemberException;
-                else
-                    assert(0, variantMemberExceptionMsg);
+            default: assert(0);
         }
     }
 }
@@ -491,7 +590,7 @@ template optionalVisit(alias visitor)
             static foreach (i, T; V._Types)
             static if (__traits(compiles, result = visitor(variant.trustedGet!T, forward!args)))
             {
-                case i + 1:
+                case i:
                     result = visitor(variant.trustedGet!T, forward!args);
                     return true;
             }

From b1267e93f05b9901f8401b305e2730a08577feae Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 31 Oct 2020 21:07:34 +0700
Subject: [PATCH 113/573] extend mir.variant

---
 source/mir/variant.d | 421 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 346 insertions(+), 75 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index fd42e4d0..2319cf1f 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -16,10 +16,19 @@ version (D_Exceptions)
     private static immutable variantMemberException = new Exception(variantMemberExceptionMsg);
 }
 
-private enum hasToHash(T) = __traits(hasMember, T, "toHash");
+private alias ConstOf(T) = const T;
+private enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a; } );
+private enum canRemoveConst(T) = canConstructWith!(const T, T);
+private enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
 private enum hasOpEquals(T) = __traits(hasMember, T, "opEquals");
+private enum hasToHash(T) = __traits(hasMember, T, "toHash");
+private enum isCopyable(S) = __traits(isCopyable, S); 
 private enum isPOD(T) = __traits(isPOD, T);
 private enum Sizeof(T) = T.sizeof;
+private enum hasMutableConstruction(T) = __traits(compiles, (T a) { auto b = a; } );
+private enum hasConstConstruction(T) = __traits(compiles, (const T a) { auto b = a; } );
+private enum hasImmutableConstruction(T) = __traits(compiles, (immutable T a) { auto b = a; } );
+private enum hasSemiImmutableConstruction(T) = __traits(compiles, (const T a) { immutable b = a; } );
 
 /++
 Variant Type (aka Algebraic Type) with clever member access.
@@ -32,7 +41,7 @@ struct Variant(Types...)
     import mir.conv: emplaceRef;
     import mir.utility: max, swap;
     import std.meta: anySatisfy, allSatisfy, templateOr, staticMap;
-    import std.traits: Largest, isCopyable, hasElaborateDestructor, hasElaborateAssign, hasElaborateCopyConstructor, isEqualityComparable, isOrderingComparable;
+    import std.traits: Select, isAssignable, CopyTypeQualifiers, Largest, hasElaborateDestructor, hasElaborateAssign, hasElaborateCopyConstructor, isEqualityComparable, isOrderingComparable;
 
     private alias _Types = Types;
 
@@ -46,6 +55,96 @@ struct Variant(Types...)
 
     private enum hasDestructor = anySatisfy!(hasElaborateDestructor, Types);
 
+    static foreach (i, T; Types)
+    {
+        /// Zero cost always nothrow `get` alternative
+        ref inout(T) trustedGet(E)() @trusted @property return inout nothrow
+            if (is(E == T))
+        {
+            assert (i == type);
+            return payload[i];
+        }
+
+        private ref T mutableTrustedGet(E)() @trusted @property return const nothrow
+            if (is(E == T))
+        {
+            assert (i == type);
+            return *cast(Types[i]*)&payload[i];
+        }
+
+        ///
+        ref inout(T) get(E)() @property return inout
+            if (is(E == T))
+        {
+            import mir.utility: _expect;
+            if (_expect(i != type, false))
+            {
+                version(D_Exceptions)
+                    throw variantException;
+                else
+                    assert(0, variantExceptionMsg);
+            }
+            return trustedGet!T;
+        }
+        
+        static if (hasMutableConstruction!T)
+        ///
+        this(T value)
+        {
+            import core.lifetime: move;
+            type = i;
+            emplaceRef(payload[i], move(value));
+        }
+
+        static if (!hasSemiImmutableConstruction!T)
+        {
+            static if (hasConstConstruction!T)
+            ///
+            this(const T value) const
+            {
+                type = i;
+                emplaceRef!(const T)(mutableTrustedGet!T, value);
+            }
+
+            static if (hasImmutableConstruction!T)
+            ///
+            this(immutable T value) immutable
+            {
+                type = i;
+                emplaceRef!(immutable T)(mutableTrustedGet!T, value);
+            }
+        }
+        else
+        {
+            ///
+            this(const T value) immutable
+            {
+                type = i;
+                emplaceRef!(immutable T)(mutableTrustedGet!T, value);
+            }
+        }
+
+        static if (__traits(compiles, (ref T a, ref T b) { swap(a, b); }))
+        ///
+        ref opAssign(T value) return
+        {
+            static if (__traits(hasMember, this, "__dtor"))
+                __dtor;
+            type = i;
+            static if (__traits(isZeroInit, T))
+            {
+                bytes[] = 0;
+            }
+            else
+            {
+                emplaceRef(payload[i]);
+                bytes[T.sizeof .. $] = 0;
+            }
+            swap(payload[i], value);
+            return this;
+        }
+    }
+
     static if (hasDestructor)
     ~this() @safe
     {
@@ -62,16 +161,24 @@ struct Variant(Types...)
         }
     }
 
+    private enum allCopyable = allSatisfy!(isCopyable, Types);
+
     static if (!__traits(compiles, (){ Types[0] arg; }))
         @disable this();
 
-    static if (!allSatisfy!(isCopyable, Types))
+    static if (!allCopyable)
         @disable this(this);
     else
     static if (anySatisfy!(hasElaborateCopyConstructor, Types))
     {
-        ///
-        this(ref return scope typeof(this) rhs)
+        private enum allCanRemoveConst = allSatisfy!(canRemoveConst, Types);
+        private enum allHaveConstConstruction = allSatisfy!(hasConstConstruction, Types);
+        private enum allHaveImmutableConstruction = allSatisfy!(hasImmutableConstruction, Types);
+        private enum allHaveMutableConstruction = allSatisfy!(hasMutableConstruction, Types);
+        private enum allHaveSemiImmutableConstruction = allSatisfy!(hasSemiImmutableConstruction, Types);
+
+        static if (allHaveMutableConstruction)
+        this(return ref scope typeof(this) rhs)
         {
             this.bytes = rhs.bytes;
             this.type = rhs.type;
@@ -81,15 +188,15 @@ struct Variant(Types...)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        this.trustedGet!T.emplaceRef(rhs.trustedGet!T);
+                        this.payload[i].emplaceRef(rhs.payload[i]);
                         return;
                 }
                 default: return;
             }
         }
 
-        /// ditto
-        this(ref return scope const typeof(this) rhs) const
+        static if (allHaveConstConstruction)
+        this(return ref scope const typeof(this) rhs) const
         {
             this.bytes = rhs.bytes;
             this.type = rhs.type;
@@ -99,15 +206,33 @@ struct Variant(Types...)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        this.mutableTrustedGet!T.emplaceRef!(const T)(rhs.trustedGet!T);
+                        this.mutableTrustedGet!T.emplaceRef!(const T)(rhs.payload[i]);
                         return;
                 }
                 default: return;
             }
         }
 
-        /// ditto
-        this(ref return scope immutable typeof(this) rhs) immutable
+        static if (allHaveImmutableConstruction)
+        this(return ref scope immutable typeof(this) rhs) immutable
+        {
+            this.bytes = rhs.bytes;
+            this.type = rhs.type;
+            S: switch (type)
+            {
+                static foreach (i, T; Types)
+                static if (hasElaborateCopyConstructor!T)
+                {
+                    case i:
+                        this.mutableTrustedGet!T.emplaceRef!(immutable T)(rhs.payload[i]);
+                        return;
+                }
+                default: return;
+            }
+        }
+
+        static if (allHaveSemiImmutableConstruction)
+        this(return ref scope const typeof(this) rhs) immutable
         {
             this.bytes = rhs.bytes;
             this.type = rhs.type;
@@ -117,12 +242,47 @@ struct Variant(Types...)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        this.mutableTrustedGet!T.emplaceRef!(immutable T)(rhs.trustedGet!T);
+                        emplaceRef!(immutable T)(this.mutableTrustedGet!T, rhs.trustedGet!T);
                         return;
                 }
                 default: return;
             }
         }
+
+        static if (allCanRemoveConst && allHaveMutableConstruction)
+        this(return ref scope const typeof(this) rhs)
+        {
+            this.bytes = rhs.bytes;
+            this.type = rhs.type;
+            S: switch (type)
+            {
+                static foreach (i, T; Types)
+                static if (hasElaborateCopyConstructor!T)
+                {
+                    case i:
+                        this.trustedGet!T.emplaceRef(rhs.trustedGet!T);
+                        return;
+                }
+                default: return;
+            }
+        }
+
+        static if (allCanRemoveConst && allHaveMutableConstruction)
+        ref opAssign(return ref scope const typeof(this) rhs) return
+        {
+            static if (__traits(hasMember, this, "__dtor"))
+                __dtor;
+            // don't need emplace here
+            __ctor(rhs);
+            return this;
+        }
+
+        static if (allCanRemoveConst && allHaveMutableConstruction)
+        ref opAssign(typeof(this) rhs) return
+        {
+            swap(this, rhs);
+            return this;
+        }
     }
 
     static if (is(Types[0] == typeof(null)))
@@ -173,68 +333,6 @@ struct Variant(Types...)
         }
     }
 
-    static foreach (i, T; Types)
-    ///
-    void opAssign(T value)
-    {
-        static if (hasDestructor)
-            __dtor;
-        type = i;
-        static if (__traits(isZeroInit, T))
-        {
-            bytes[] = 0;
-        }
-        else
-        {
-            emplaceRef(trustedGet!T);
-            bytes[T.sizeof .. $] = 0;
-        }
-        swap(trustedGet!T, value);
-    }
-
-    static foreach (i, T; Types)
-    ///
-    this(T value)
-    {
-        type = i;
-        static if (!__traits(isZeroInit, T))
-            emplaceRef(trustedGet!T);
-        swap(trustedGet!T, value);
-    }
-
-    static foreach (i, T; Types)
-    ///
-    ref inout(T) get(E)() @property return inout
-        if (is(E == T))
-    {
-        import mir.utility: _expect;
-        if (_expect(i != type, false))
-        {
-            version(D_Exceptions)
-                throw variantException;
-            else
-                assert(0, variantExceptionMsg);
-        }
-        return trustedGet!T;
-    }
-
-    static foreach (i, T; Types)
-    /// Zero cost always nothrow `get` alternative
-    ref inout(T) trustedGet(E)() @trusted @property return inout nothrow
-        if (is(E == T))
-    {
-        assert (i == type);
-        return payload[i];
-    }
-
-    static foreach (i, T; Types)
-    private ref T mutableTrustedGet(E)() @trusted @property return const nothrow
-        if (is(E == T))
-    {
-        assert (i == type);
-        return *cast(Types[i]*)&payload[i];
-    }
-
     /++
     Returns: zero based type index.
     +/
@@ -246,7 +344,7 @@ struct Variant(Types...)
     static if (allSatisfy!(templateOr!(isPOD, hasToHash), Types))
     /++
     +/
-    size_t toHash()
+    size_t toHash() const
     {
         static if (allSatisfy!(isPOD, Types))
         {
@@ -368,6 +466,179 @@ unittest
     }}
 }
 
+/// const propogation
+@safe pure nothrow @nogc
+unittest
+{
+    static struct S { immutable(ubyte)* value; }
+    static struct C { immutable(uint)* value; }
+
+    alias V = Variant!(S, C);
+    const V v = S();
+    V w = v;
+    w = v;
+
+    immutable f = V(S());
+    auto t = immutable V(S());
+    // auto j = immutable V(t);
+    // auto i = const V(t);
+}
+
+/// ditto
+@safe pure nothrow @nogc
+unittest
+{
+    static struct S {
+        uint* value;
+        this(return ref scope const typeof(this) rhs) {}
+    }
+    static struct C { const(uint)* value; }
+
+    alias V = Variant!(S, C);
+    const V v = S();
+    V w = v;
+    w = S();
+    w = cast(const) V.init;
+    w = v;
+
+    const f = V(S());
+    auto t = const V(f);
+}
+
+
+@safe pure nothrow @nogc
+unittest
+{
+    static struct S {
+        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 C { immutable(uint)* value; }
+
+    S s;
+    S r = s;
+    r = s;
+    r = S.init;
+
+    alias V = Variant!(S, C);
+    V v = S();
+    V w = v;
+    w = S();
+    w = V.init;
+    w = v;
+
+    immutable V e = S();
+    auto t = immutable V(S());
+    auto j = const V(t);
+    auto h = t;
+
+    immutable V l = C();
+    auto g = immutable V(C());
+}
+
+@safe pure nothrow @nogc
+unittest
+{
+    static struct S {
+        uint* value;
+        this(return ref scope typeof(this) rhs) {}
+        this(return ref scope const typeof(this) rhs) const {}
+        this(return ref scope const typeof(this) rhs) immutable {}
+    }
+    static struct C { immutable(uint)* value; }
+
+    S s;
+    S r = s;
+    r = s;
+    r = S.init;
+
+    alias V = Variant!(S, C);
+    V v = S();
+    V w = v;
+    w = S();
+    w = V.init;
+    w = v;
+
+    {
+        const V e = S();
+        const k = w;
+        auto t = const V(k);
+        auto j = immutable V(k);
+    }
+
+    immutable V e = S();
+    immutable k = w;
+    auto t = immutable V(S());
+    auto j = const V(t);
+    auto h = t;
+
+    immutable V l = C();
+    import core.lifetime;
+    auto g = immutable V(C());
+    immutable b = immutable V(s);
+}
+
+@safe pure nothrow @nogc
+unittest
+{
+    static struct S {
+        immutable(uint)* value;
+        this(return ref scope typeof(this) rhs) {}
+        this(return ref scope const typeof(this) rhs) immutable {}
+    }
+    static struct C { immutable(uint)* value; }
+
+    S s;
+    S r = s;
+    r = s;
+    r = S.init;
+
+    alias V = Variant!(S, C);
+    V v = S();
+    V w = v;
+    w = S();
+    w = V.init;
+    w = v;
+
+    immutable V e = S();
+    immutable f = V(S());
+    immutable k = w;
+    auto t = immutable V(S());
+    auto j = const V(t);
+    auto h = t;
+
+    immutable V l = C();
+    import core.lifetime;
+    immutable n = w.move;
+    auto g = immutable V(C());
+    immutable b = immutable V(s);
+}
+
+@safe pure nothrow @nogc
+unittest
+{
+    static struct S {
+        uint* value;
+        this(this) @safe pure nothrow @nogc {}
+        // void opAssign(typeof(this) rhs) {}
+    }
+    static struct C { const(uint)* value; }
+
+    S s;
+    S r = s;
+    r = s;
+    r = S.init;
+
+    alias V = Variant!(S, C);
+    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.

From e68083cd00f1e3f03e19a334803aae9581132160 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 1 Nov 2020 15:24:38 +0700
Subject: [PATCH 114/573] add self-referencing

---
 source/mir/variant.d | 121 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 105 insertions(+), 16 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 2319cf1f..e35e2ef8 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -20,30 +20,48 @@ private alias ConstOf(T) = const T;
 private enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a; } );
 private enum canRemoveConst(T) = canConstructWith!(const T, T);
 private enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
+private enum hasConstConstruction(T) = __traits(compiles, (const T a) { auto b = a; } );
+private enum hasImmutableConstruction(T) = __traits(compiles, (immutable T a) { auto b = a; } );
+private enum hasMutableConstruction(T) = __traits(compiles, (T a) { auto b = a; } );
 private enum hasOpEquals(T) = __traits(hasMember, T, "opEquals");
+private enum hasSemiImmutableConstruction(T) = __traits(compiles, (const T a) { immutable b = a; } );
 private enum hasToHash(T) = __traits(hasMember, T, "toHash");
 private enum isCopyable(S) = __traits(isCopyable, S); 
 private enum isPOD(T) = __traits(isPOD, T);
+private enum isVariant(T) = is(T == Variant!Args, Args...);
 private enum Sizeof(T) = T.sizeof;
-private enum hasMutableConstruction(T) = __traits(compiles, (T a) { auto b = a; } );
-private enum hasConstConstruction(T) = __traits(compiles, (const T a) { auto b = a; } );
-private enum hasImmutableConstruction(T) = __traits(compiles, (immutable T a) { auto b = a; } );
-private enum hasSemiImmutableConstruction(T) = __traits(compiles, (const T a) { immutable b = a; } );
+
+/++
+Dummy type for $(LREF Variant) self-referencing.
++/
+struct This;
 
 /++
 Variant Type (aka Algebraic Type) with clever member access.
 
 Compatible with BetterC mode.
 +/
-struct Variant(Types...)
-    if (Types.length)
+struct Variant(_Types...)
+    if (_Types.length)
 {
     import mir.conv: emplaceRef;
     import mir.utility: max, swap;
-    import std.meta: anySatisfy, allSatisfy, templateOr, staticMap;
-    import std.traits: Select, isAssignable, CopyTypeQualifiers, Largest, hasElaborateDestructor, hasElaborateAssign, hasElaborateCopyConstructor, isEqualityComparable, isOrderingComparable;
+    import std.meta: AliasSeq, anySatisfy, allSatisfy, templateOr, staticMap;
+    import std.typecons: ReplaceTypeUnless;
+    import std.traits:
+        Select,
+        CopyTypeQualifiers,
+        hasElaborateAssign,
+        hasElaborateCopyConstructor,
+        hasElaborateDestructor,
+        isAssignable,
+        isEqualityComparable,
+        isOrderingComparable,
+        Largest
+        ;
 
-    private alias _Types = Types;
+    ///
+    alias Types = AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), _Types));
 
     union
     {
@@ -143,6 +161,30 @@ struct Variant(Types...)
             swap(payload[i], value);
             return this;
         }
+
+        static if (isPOD!T || hasOpEquals!T)
+        /++
+        +/
+        auto opEquals()(auto ref const T rhs) const
+        {
+            if (type != i)
+                return false;
+            return trustedGet!T == rhs;
+        } 
+
+        static if (isOrderingComparable!T)
+        /++
+        +/
+        auto opCmp()(auto ref const T rhs) const
+        {
+            if (auto d = int(type) - int(rhs.type))
+                return d;
+            static if (__traits(hasMember, T, "opCmp"))
+                return trustedGet!T.opCmp(rhs);
+            else
+                return trustedGet!T < rhs ? -1 :
+                    trustedGet!T > rhs ? +1 : 0;
+        }
     }
 
     static if (hasDestructor)
@@ -291,8 +333,9 @@ struct Variant(Types...)
         bool isNull() const { return type == 0; }
         /// ditto
         void nullify() { this = null; }
+
+        static if (allSatisfy!(isCopyable, Types[1 .. $]) && Types.length != 2)
         /// ditto
-        static if (allSatisfy!(isCopyable, Types[1 .. $]))
         auto get()
         {
             if (!type)
@@ -331,6 +374,10 @@ struct Variant(Types...)
 
             return ret;
         }
+
+        static if (Types.length == 2)
+        /// ditto
+        alias get = get!(Types[1]);
     }
 
     /++
@@ -381,7 +428,7 @@ struct Variant(Types...)
     static if (allSatisfy!(templateOr!(isPOD, hasOpEquals), Types))
     /++
     +/
-    auto opEquals(ref const typeof(this) rhs) const
+    auto opEquals()(auto ref const typeof(this) rhs) const
     {
         if (this.type != rhs.type)
             return false;
@@ -399,7 +446,7 @@ struct Variant(Types...)
     static if (allSatisfy!(isOrderingComparable, Types))
     /++
     +/
-    auto opCmp(ref const typeof(this) rhs) const
+    auto opCmp()(auto ref const typeof(this) rhs) const
     {
         if (auto d = int(this.type) - int(rhs.type))
             return d;
@@ -419,6 +466,47 @@ struct Variant(Types...)
     }
 }
 
+// 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 unittest
+{
+    import std.typecons : Tuple, tuple;
+
+    // A tree is either a leaf or a branch of two other trees
+    alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*));
+    alias Leafs = Tree!int.Types[1];
+
+    Tree!int tree = tuple(new Tree!int(42), new Tree!int(43));
+    Tree!int* right = tree.get!Leafs[1];
+    assert(*right == 43);
+}
+
+///ditto
+@safe pure unittest
+{
+    // An object is a double, a string, or a hash of objects
+    alias Obj = Variant!(double, string, This[string]);
+    alias Map = Obj.Types[2];
+
+    Obj obj = "hello";
+    assert(obj.get!string == "hello");
+    obj = 42.0;
+    assert(obj.get!double == 42);
+    obj = ["customer": Obj("John"), "paid": Obj(23.95)];
+    assert(obj.get!Map["customer"] == "John");
+}
+
 /// Test for opCmp, opEqual, toHash
 version(mir_test)
 @safe pure @nogc nothrow
@@ -461,8 +549,8 @@ unittest
                 C!(size1, isPOD, hasToHash, hasOpEquals),
                 C!(size2, isPOD, hasToHash, hasOpEquals));
         static assert (__traits(hasMember, T, "toHash") == isPOD || hasToHash);
-        static assert (__traits(hasMember, T, "opEquals") == isPOD || hasOpEquals);
-        static assert (__traits(hasMember, T, "opCmp"));
+        static assert (__traits(compiles, T.init == T.init) == isPOD || hasOpEquals);
+        static assert (__traits(compiles, T.init <= T.init));
     }}
 }
 
@@ -491,6 +579,7 @@ unittest
     static struct S {
         uint* value;
         this(return ref scope const typeof(this) rhs) {}
+        void opAssign(return ref scope const typeof(this) rhs) {}
     }
     static struct C { const(uint)* value; }
 
@@ -795,7 +884,7 @@ template visit(alias visitor, bool exhaustive = true)
         import core.lifetime: forward;
         switch (variant.type)
         {
-            static foreach (i, T; V._Types)
+            static foreach (i, T; V.Types)
             {
                 case i:
                     static if (__traits(compiles, visitor(variant.trustedGet!T, forward!args)))
@@ -858,7 +947,7 @@ template optionalVisit(alias visitor)
         import core.lifetime: forward;
         switch (variant.type)
         {
-            static foreach (i, T; V._Types)
+            static foreach (i, T; V.Types)
             static if (__traits(compiles, result = visitor(variant.trustedGet!T, forward!args)))
             {
                 case i:

From 59a2ee811cab4212f3fb334c8960be14cb175823 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 1 Nov 2020 15:39:36 +0700
Subject: [PATCH 115/573] force NoDuplicates for Variant

---
 source/mir/variant.d | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index e35e2ef8..68b738aa 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -42,11 +42,11 @@ Variant Type (aka Algebraic Type) with clever member access.
 Compatible with BetterC mode.
 +/
 struct Variant(_Types...)
-    if (_Types.length)
+	if (_Types.length)
 {
     import mir.conv: emplaceRef;
     import mir.utility: max, swap;
-    import std.meta: AliasSeq, anySatisfy, allSatisfy, templateOr, staticMap;
+    import std.meta: AliasSeq, anySatisfy, allSatisfy, NoDuplicates, templateOr, staticMap;
     import std.typecons: ReplaceTypeUnless;
     import std.traits:
         Select,
@@ -60,6 +60,8 @@ struct Variant(_Types...)
         Largest
         ;
 
+    static assert(is(NoDuplicates!_Types == _Types), "Varaint of " ~ _Types.stringof ~ " constains type duplicates");
+
     ///
     alias Types = AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), _Types));
 

From 64764a75428ff9c0edc64486f979b1e223105de6 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 3 Nov 2020 00:02:27 +0700
Subject: [PATCH 116/573] cclic variant

---
 source/mir/variant.d | 262 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 244 insertions(+), 18 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 68b738aa..d87d9185 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -4,6 +4,7 @@ This module implements a generic variant type.
 module mir.variant;
 
 import mir.functional: naryFun;
+import std.meta: allSatisfy;
 
 private static immutable variantExceptionMsg = "mir.variant: the variant stores other type then requested.";
 private static immutable variantNullExceptionMsg = "mir.variant: the variant is empty and doesn't store any value.";
@@ -18,6 +19,7 @@ version (D_Exceptions)
 
 private alias ConstOf(T) = const T;
 private enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a; } );
+private enum canImplicitlyRemoveConst(T) = __traits(compiles, {static T _function_(ref const T a) { return a; }} );
 private enum canRemoveConst(T) = canConstructWith!(const T, T);
 private enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
 private enum hasConstConstruction(T) = __traits(compiles, (const T a) { auto b = a; } );
@@ -28,28 +30,160 @@ private enum hasSemiImmutableConstruction(T) = __traits(compiles, (const T a) {
 private enum hasToHash(T) = __traits(hasMember, T, "toHash");
 private enum isCopyable(S) = __traits(isCopyable, S); 
 private enum isPOD(T) = __traits(isPOD, T);
-private enum isVariant(T) = is(T == Variant!Args, Args...);
+private enum isVariant(T) = __traits(hasMember, T, "__isVariant");
 private enum Sizeof(T) = T.sizeof;
 
+private 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 .. $]);
+    }
+}
+
+private template TryRemoveConst(T)
+{
+    import std.traits: Unqual;
+    alias U = Unqual!T;
+    static if (canImplicitlyRemoveConst!U)
+    {
+        alias TryRemoveConst = U;
+    }
+    else
+    {
+        alias TryRemoveConst = T;
+    }
+}
+
+unittest
+{
+    static assert(is(TryRemoveConst!(const int) == int));
+}
+
+private enum bool TypeCmp(A, B) =
+    is(A == B) ? false:
+    is(A == typeof(null)) ? true:
+    A.sizeof < B.sizeof ? true:
+    A.sizeof > B.sizeof ? false:
+    A.mangleof < B.mangleof;
+
+
+private template staticMerge(alias cmp, int half, Seq...)
+{
+    static if (half == 0 || half == Seq.length)
+    {
+        alias staticMerge = Seq;
+    }
+    else
+    {
+        import std.meta: AliasSeq;
+        static if (cmp!(Seq[0], Seq[half]))
+        {
+            alias staticMerge = AliasSeq!(Seq[0],
+                staticMerge!(cmp, half - 1, Seq[1 .. $]));
+        }
+        else
+        {
+            alias staticMerge = AliasSeq!(Seq[half],
+                staticMerge!(cmp, half, Seq[0 .. half], Seq[half + 1 .. $]));
+        }
+    }
+}
+
+private template staticSort(alias cmp, Seq...)
+{
+    static if (Seq.length < 2)
+    {
+        alias staticSort = Seq;
+    }
+    else
+    {
+        import std.meta: AliasSeq;
+        private alias btm = staticSort!(cmp, Seq[0 .. $ / 2]);
+        private alias top = staticSort!(cmp, Seq[$ / 2 .. $]);
+
+        static if (cmp!(btm[$ - 1], top[0]))
+            alias staticSort = AliasSeq!(btm, top); // already ascending
+        else static if (cmp!(top[$ - 1], btm[0]))
+            alias staticSort = AliasSeq!(top, btm); // already descending
+        else
+            alias staticSort = staticMerge!(cmp, Seq.length / 2, btm, top);
+    }
+}
+
+private template isInstanceOf(alias S)
+{
+    enum isInstanceOf(T) = is(T == S!Args, Args...);
+}
+
+private struct VariantSet(Sets...);
+
 /++
 Dummy type for $(LREF Variant) self-referencing.
 +/
-struct This;
+struct SetAlias(uint id);
+///ditto
+alias This = SetAlias!0;
+
+
 
 /++
-Variant Type (aka Algebraic Type) with clever member access.
++/
+template TypeSet(T...)
+{
+    import std.meta: NoDuplicates;
+    // sort types by siezeof and them mangleof
+    static if (is(NoDuplicates!T == T))
+        static if (staticIsSorted!(TypeCmp, T))
+            struct TypeSet;
+        else
+            alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T));
+    else
+        alias TypeSet = TypeSet!(NoDuplicates!T);
+}
 
-Compatible with BetterC mode.
+
+unittest
+{
+    struct S {}
+    alias C = S;
+    alias Int = int;
+    static assert(__traits(isSame, TypeSet!(S, int), TypeSet!(Int, C)));
+    static assert(__traits(isSame, TypeSet!(S, int, int), TypeSet!(Int, C)));
+    static assert(!__traits(isSame, TypeSet!(uint, S), TypeSet!(int, S)));
+}
+
+
+/++
 +/
-struct Variant(_Types...)
-	if (_Types.length)
+template Variants(Sets...)
+    if (allSatisfy!(isInstanceOf!TypeSet, Sets))
+{
+    import std.meta: staticMap;
+    import mir.internal.utility: Iota;
+
+    private alias TypeSetsInst(uint id) = TypeSets!(id, Sets);
+    ///
+    alias Variants = staticMap!(TypeSetsInst, Iota!(Sets.length));
+}
+
+///
+struct TypeSets(uint id, Sets...)
+    if (allSatisfy!(isInstanceOf!TypeSet, Sets))
 {
     import mir.conv: emplaceRef;
     import mir.utility: max, swap;
-    import std.meta: AliasSeq, anySatisfy, allSatisfy, NoDuplicates, templateOr, staticMap;
+    import std.meta: AliasSeq, anySatisfy, allSatisfy, staticMap, templateOr;
+    import std.traits: TemplateArgsOf;
     import std.typecons: ReplaceTypeUnless;
     import std.traits:
-        Select,
         CopyTypeQualifiers,
         hasElaborateAssign,
         hasElaborateCopyConstructor,
@@ -57,13 +191,27 @@ struct Variant(_Types...)
         isAssignable,
         isEqualityComparable,
         isOrderingComparable,
-        Largest
+        Largest,
+        Select,
+        TemplateArgsOf
         ;
 
-    static assert(is(NoDuplicates!_Types == _Types), "Varaint of " ~ _Types.stringof ~ " constains type duplicates");
+    private template ApplyAliasesImpl(uint length, Types...)
+    {
+        static if (length == 0)
+            alias ApplyAliasesImpl = Types;
+        else
+        {
+            enum next  = length - 1;
+            import std.typecons: ReplaceTypeUnless;
+            alias ApplyAliasesImpl = ApplyAliasesImpl!(next,
+                ReplaceTypeUnless!(isVariant, SetAlias!next, TypeSets!(next, Sets), Types));
+        }
+    }
+
+    private enum __isVariant;
 
-    ///
-    alias Types = AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), _Types));
+    alias Types = ApplyAliasesImpl!(Sets.length, TemplateArgsOf!(Sets[id]));
 
     union
     {
@@ -461,13 +609,21 @@ struct Variant(_Types...)
                         return this.trustedGet!T.opCmp(rhs.trustedGet!T);
                     else
                         return this.trustedGet!T < rhs.trustedGet!T ? -1 :
-                               this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
+                            this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
             }
             default: assert(0);
         }
     }
 }
 
+/++
+Variant Type (aka TypeSets Type) with clever member access.
+
+Compatible with BetterC mode.
++/
+
+alias Variant(T...) = TypeSets!(0, TypeSet!T);
+
 // example from std.variant
 /**
 $(H4 Self-Referential Types)
@@ -487,7 +643,7 @@ be arbitrarily complex.
 
     // A tree is either a leaf or a branch of two other trees
     alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*));
-    alias Leafs = Tree!int.Types[1];
+    alias Leafs = Tuple!(Tree!int*, Tree!int*);
 
     Tree!int tree = tuple(new Tree!int(42), new Tree!int(43));
     Tree!int* right = tree.get!Leafs[1];
@@ -499,7 +655,9 @@ be arbitrarily complex.
 {
     // An object is a double, a string, or a hash of objects
     alias Obj = Variant!(double, string, This[string]);
-    alias Map = Obj.Types[2];
+    alias Map = Obj[string];
+
+    pragma(msg, Obj);
 
     Obj obj = "hello";
     assert(obj.get!string == "hello");
@@ -510,7 +668,8 @@ be arbitrarily complex.
 }
 
 /// Test for opCmp, opEqual, toHash
-version(mir_test)
+// version(mir_test)
+version(none)
 @safe pure @nogc nothrow
 unittest
 {
@@ -871,6 +1030,15 @@ private template getMemberHandler(string member)
     }
 }
 
+// AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), _Types))
+
+private template VariantReturnTypes(T...)
+{
+    import std.meta: NoDuplicates, staticMap;
+
+    alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T));
+}
+
 /++
 Params:
     visitor = a function name alias
@@ -881,9 +1049,67 @@ template visit(alias visitor, bool exhaustive = true)
     import std.traits: Unqual;
     ///
     auto ref visit(V, Args...)(auto ref V variant, auto ref Args args)
-        if (is(Unqual!V : Variant!Types, Types))
+        if (isVariant!V)
     {
         import core.lifetime: forward;
+
+        // template VariantReturnTypesImpl(T)
+        // {
+        //     static if (__traits(compiles, visitor(variant.trustedGet!T, forward!args)))
+        //     {
+        //         alias Ret = typeof(visitor(variant.trustedGet!T, forward!args));
+        //         static if (is(Ret == void))
+        //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
+        //         else
+        //             alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!Ret);
+        //     }
+        //     else
+        //     {
+        //         alias VariantReturnTypesImpl = AliasSeq!();
+        //     }
+        // }
+
+        // alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V.Types));
+
+        // static if (AllReturnTypes.length == 0 || is(AllReturnTypes == AliasSeq!(typeof(null))))
+        // {
+        //     alias ThisReturnType = void;
+        // }
+        // else
+        // static if (AllReturnTypes.length == 2 && (is(AllReturnTypes[0] == typeof(null)) || is(AllReturnTypes[1] == typeof(null))))
+        // {
+        //     alias ThisReturnType = Variant!(typeof(null), OtherType);
+        // }
+
+        // template VariantReturnTypesImpl(T)
+        // {
+        //     import std.traits: Unqual, CopyTypeQualifiers;
+        //     import std.meta: AliasSeq;
+
+        //     static if (__traits(compiles, visitor(variant.trustedGet!T, forward!args)))
+        //     {
+        //         alias Ret = typeof(visitor(variant.trustedGet!T, forward!args));
+        //         static if (is(Ret == void))
+        //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
+        //         else
+        //         static if (is(Unqual!Ret == Variant!InnerTypes, InnerTypes))
+        //         {
+        //             alias CopyTypeQualifiers1(Y) = CopyTypeQualifiers(Ret, Y);
+        //             alias VariantReturnTypesImpl = staticMap!(CopyTypeQualifiers1, Ret.Types);
+        //         }
+        //         else
+        //             alias VariantReturnTypesImpl = AliasSeq!Ret;
+        //     }
+        //     else
+        //     {
+        //         alias VariantReturnTypesImpl = AliasSeq!();
+        //     }
+        // }
+
+        // import std.meta: staticMap;
+        // alias Types = VariantReturnTypes!(staticMap!(VariantReturnTypesImpl, V.Types));
+
+        // V.Types;
         switch (variant.type)
         {
             static foreach (i, T; V.Types)
@@ -944,7 +1170,7 @@ template optionalVisit(alias visitor)
     import std.traits: Unqual;
     ///
     bool optionalVisit(Result, V, Args...)(ref Result result, auto ref V variant, auto ref Args args) @property
-        if (is(Unqual!V : Variant!Types, Types))
+        if (isVariant!V)
     {
         import core.lifetime: forward;
         switch (variant.type)

From 3eda51896acf756d6855441475c1c949b4c6c3e5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 3 Nov 2020 10:59:34 +0700
Subject: [PATCH 117/573] extend mir.variant

---
 source/mir/variant.d | 506 ++++++++++++++++++++++++++++---------------
 1 file changed, 334 insertions(+), 172 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index d87d9185..78bc463e 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -169,14 +169,27 @@ template Variants(Sets...)
     import std.meta: staticMap;
     import mir.internal.utility: Iota;
 
-    private alias TypeSetsInst(uint id) = TypeSets!(id, Sets);
+    private alias TypeSetsInst(uint id) = Algebraic!(id, Sets);
     ///
     alias Variants = staticMap!(TypeSetsInst, Iota!(Sets.length));
 }
 
+private static struct __Null()
+{
+    @safe pure nothrow @nogc const:
+        size_t toHash() {return 0;}
+        bool opEquals(__Null) { return true; }
+        int opCmp(__Null) { return 0; }
+
+    typeof(null) __self() @property { return null; }
+    typeof(null) __self(typeof(null)) @property { return null; }
+    this(typeof(null)) inout {}
+    string toString() { return "null"; }
+}
+
 ///
-struct TypeSets(uint id, Sets...)
-    if (allSatisfy!(isInstanceOf!TypeSet, Sets))
+struct Algebraic(uint _setId, _TypeSets...)
+    if (allSatisfy!(isInstanceOf!TypeSet, _TypeSets))
 {
     import mir.conv: emplaceRef;
     import mir.utility: max, swap;
@@ -196,63 +209,105 @@ struct TypeSets(uint id, Sets...)
         TemplateArgsOf
         ;
 
-    private template ApplyAliasesImpl(uint length, Types...)
+    private template __ApplyAliasesImpl(uint length, Types...)
     {
         static if (length == 0)
-            alias ApplyAliasesImpl = Types;
+            alias __ApplyAliasesImpl = Types;
         else
         {
             enum next  = length - 1;
             import std.typecons: ReplaceTypeUnless;
-            alias ApplyAliasesImpl = ApplyAliasesImpl!(next,
-                ReplaceTypeUnless!(isVariant, SetAlias!next, TypeSets!(next, Sets), Types));
+            alias __ApplyAliasesImpl = __ApplyAliasesImpl!(next,
+                ReplaceTypeUnless!(isVariant, SetAlias!next, Algebraic!(next, _TypeSets), Types));
         }
     }
 
     private enum __isVariant;
 
-    alias Types = ApplyAliasesImpl!(Sets.length, TemplateArgsOf!(Sets[id]));
+    ///
+    alias __Types = __ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId]));
 
     union
     {
-        private Types payload;
-        private ubyte[Largest!Types.sizeof] bytes;
+        static if (__Types.length)
+        {
+            static if (is(__Types[0] == typeof(null)))
+            {
+                private alias __Payload =  AliasSeq!(__Null!(), __Types[1 .. $]);
+            }
+            else
+                alias __Payload = __Types;
+            private __Payload __payload;
+            private ubyte[Largest!__Payload.sizeof] __bytes;
+        }
+        else
+        {
+            alias __Payload = __Types;
+            private __Payload __payload;
+            private ubyte[0] __bytes;
+        }
     }
 
-    private uint type; // 0 for unininit value, index = type - 1
+    static if (__Types.length)
+    {
+        static if (__Types.length > 1)
+        {
+            // 0 for unininit value, index = type - 1
+            static if (__bytes.length == 1)
+                private ubyte __type; 
+            else
+            static if (__bytes.length == 2)
+                private ushort __type;
+            else
+                private uint __type;
+        }
+        else
+        {
+            private enum uint __type = 0;
+        }
+    }
 
-    private enum hasDestructor = anySatisfy!(hasElaborateDestructor, Types);
+    private enum __hasDestructor = anySatisfy!(hasElaborateDestructor, __Types);
 
-    static foreach (i, T; Types)
+    static foreach (i, T; __Types)
     {
         /// Zero cost always nothrow `get` alternative
-        ref inout(T) trustedGet(E)() @trusted @property return inout nothrow
+        auto ref __trustedGet(E)() @trusted @property return inout nothrow
             if (is(E == T))
         {
-            assert (i == type);
-            return payload[i];
+            assert (i == __type);
+            static if (is(T == typeof(null)))
+                return null;
+            else
+                return __payload[i];
         }
 
-        private ref T mutableTrustedGet(E)() @trusted @property return const nothrow
+        private ref T __mutableTrustedGet(E)() @trusted @property return const nothrow
             if (is(E == T))
         {
-            assert (i == type);
-            return *cast(Types[i]*)&payload[i];
+            assert (i == __type);
+            return *cast(__Types[i]*)&__payload[i];
         }
 
         ///
-        ref inout(T) get(E)() @property return inout
+        auto ref get(E)() @property return inout
             if (is(E == T))
         {
             import mir.utility: _expect;
-            if (_expect(i != type, false))
+            static if (__Types.length > 1)
             {
-                version(D_Exceptions)
-                    throw variantException;
-                else
-                    assert(0, variantExceptionMsg);
+                if (_expect(i != __type, false))
+                {
+                    version(D_Exceptions)
+                        throw variantException;
+                    else
+                        assert(0, variantExceptionMsg);
+                }
             }
-            return trustedGet!T;
+            static if (is(T == typeof(null)))
+                return null;
+            else
+                return __trustedGet!T;
         }
         
         static if (hasMutableConstruction!T)
@@ -260,8 +315,9 @@ struct TypeSets(uint id, Sets...)
         this(T value)
         {
             import core.lifetime: move;
-            type = i;
-            emplaceRef(payload[i], move(value));
+            static if (__Types.length > 1)
+                __type = i;
+            emplaceRef(__payload[i], move(value));
         }
 
         static if (!hasSemiImmutableConstruction!T)
@@ -270,16 +326,18 @@ struct TypeSets(uint id, Sets...)
             ///
             this(const T value) const
             {
-                type = i;
-                emplaceRef!(const T)(mutableTrustedGet!T, value);
+                static if (__Types.length > 1)
+                    __type = i;
+                emplaceRef!(const T)(__mutableTrustedGet!T, value);
             }
 
             static if (hasImmutableConstruction!T)
             ///
             this(immutable T value) immutable
             {
-                type = i;
-                emplaceRef!(immutable T)(mutableTrustedGet!T, value);
+                static if (__Types.length > 1)
+                    __type = i;
+                emplaceRef!(immutable T)(__mutableTrustedGet!T, value);
             }
         }
         else
@@ -287,8 +345,9 @@ struct TypeSets(uint id, Sets...)
             ///
             this(const T value) immutable
             {
-                type = i;
-                emplaceRef!(immutable T)(mutableTrustedGet!T, value);
+                static if (__Types.length > 1)
+                    __type = i;
+                emplaceRef!(immutable T)(__mutableTrustedGet!T, value);
             }
         }
 
@@ -298,17 +357,19 @@ struct TypeSets(uint id, Sets...)
         {
             static if (__traits(hasMember, this, "__dtor"))
                 __dtor;
-            type = i;
+            static if (__Types.length > 1)
+                __type = i;
             static if (__traits(isZeroInit, T))
             {
-                bytes[] = 0;
+                __bytes[] = 0;
             }
             else
             {
-                emplaceRef(payload[i]);
-                bytes[T.sizeof .. $] = 0;
+                emplaceRef(__payload[i]);
+                __bytes[T.sizeof .. $] = 0;
             }
-            swap(payload[i], value);
+            static if (!is(T == typeof(null)))
+                swap(__payload[i], value);
             return this;
         }
 
@@ -317,9 +378,10 @@ struct TypeSets(uint id, Sets...)
         +/
         auto opEquals()(auto ref const T rhs) const
         {
-            if (type != i)
-                return false;
-            return trustedGet!T == rhs;
+            static if (__Types.length > 1)
+                if (__type != i)
+                    return false;
+            return __trustedGet!T == rhs;
         } 
 
         static if (isOrderingComparable!T)
@@ -327,139 +389,146 @@ struct TypeSets(uint id, Sets...)
         +/
         auto opCmp()(auto ref const T rhs) const
         {
-            if (auto d = int(type) - int(rhs.type))
-                return d;
+            static if (__Types.length > 1)
+                if (auto d = int(__type) - int(rhs.__type))
+                    return d;
             static if (__traits(hasMember, T, "opCmp"))
-                return trustedGet!T.opCmp(rhs);
+                return __trustedGet!T.opCmp(rhs);
             else
-                return trustedGet!T < rhs ? -1 :
-                    trustedGet!T > rhs ? +1 : 0;
+                return __trustedGet!T < rhs ? -1 :
+                    __trustedGet!T > rhs ? +1 : 0;
         }
     }
 
-    static if (hasDestructor)
+    static if (__hasDestructor)
     ~this() @safe
     {
-        S: switch (type)
+        S: switch (__type)
         {
-            static foreach (i, T; Types)
+            static foreach (i, T; __Types)
             static if (hasElaborateDestructor!T)
             {
                 case i:
-                    .destroy!false(trustedGet!T);
+                    .destroy!false(__trustedGet!T);
                     break S;
             }
             default: return;
         }
     }
 
-    private enum allCopyable = allSatisfy!(isCopyable, Types);
+    private enum allCopyable = allSatisfy!(isCopyable, __Types);
 
-    static if (!__traits(compiles, (){ Types[0] arg; }))
-        @disable this();
+    static if (__Types.length)
+        static if (!__traits(compiles, (){ __Types[0] arg; }))
+            @disable this();
 
     static if (!allCopyable)
         @disable this(this);
     else
-    static if (anySatisfy!(hasElaborateCopyConstructor, Types))
+    static if (anySatisfy!(hasElaborateCopyConstructor, __Types))
     {
-        private enum allCanRemoveConst = allSatisfy!(canRemoveConst, Types);
-        private enum allHaveConstConstruction = allSatisfy!(hasConstConstruction, Types);
-        private enum allHaveImmutableConstruction = allSatisfy!(hasImmutableConstruction, Types);
-        private enum allHaveMutableConstruction = allSatisfy!(hasMutableConstruction, Types);
-        private enum allHaveSemiImmutableConstruction = allSatisfy!(hasSemiImmutableConstruction, Types);
+        private enum __allCanRemoveConst = allSatisfy!(canRemoveConst, __Types);
+        private enum __allHaveConstConstruction = allSatisfy!(hasConstConstruction, __Types);
+        private enum __allHaveImmutableConstruction = allSatisfy!(hasImmutableConstruction, __Types);
+        private enum __allHaveMutableConstruction = allSatisfy!(hasMutableConstruction, __Types);
+        private enum __allHaveSemiImmutableConstruction = allSatisfy!(hasSemiImmutableConstruction, __Types);
 
-        static if (allHaveMutableConstruction)
+        static if (__allHaveMutableConstruction)
         this(return ref scope typeof(this) rhs)
         {
-            this.bytes = rhs.bytes;
-            this.type = rhs.type;
-            S: switch (type)
+            this.__bytes = rhs.__bytes;
+            static if (__Types.length > 1)
+                this.__type = rhs.__type;
+            S: switch (__type)
             {
-                static foreach (i, T; Types)
+                static foreach (i, T; __Types)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        this.payload[i].emplaceRef(rhs.payload[i]);
+                        this.__payload[i].emplaceRef(rhs.__payload[i]);
                         return;
                 }
                 default: return;
             }
         }
 
-        static if (allHaveConstConstruction)
+        static if (__allHaveConstConstruction)
         this(return ref scope const typeof(this) rhs) const
         {
-            this.bytes = rhs.bytes;
-            this.type = rhs.type;
-            S: switch (type)
+            this.__bytes = rhs.__bytes;
+            static if (__Types.length > 1)
+                this.__type = rhs.__type;
+            S: switch (__type)
             {
-                static foreach (i, T; Types)
+                static foreach (i, T; __Types)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        this.mutableTrustedGet!T.emplaceRef!(const T)(rhs.payload[i]);
+                        this.__mutableTrustedGet!T.emplaceRef!(const T)(rhs.__payload[i]);
                         return;
                 }
                 default: return;
             }
         }
 
-        static if (allHaveImmutableConstruction)
+        static if (__allHaveImmutableConstruction)
         this(return ref scope immutable typeof(this) rhs) immutable
         {
-            this.bytes = rhs.bytes;
-            this.type = rhs.type;
-            S: switch (type)
+            this.__bytes = rhs.__bytes;
+            static if (__Types.length > 1)
+                this.__type = rhs.__type;
+            S: switch (__type)
             {
-                static foreach (i, T; Types)
+                static foreach (i, T; __Types)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        this.mutableTrustedGet!T.emplaceRef!(immutable T)(rhs.payload[i]);
+                        this.__mutableTrustedGet!T.emplaceRef!(immutable T)(rhs.__payload[i]);
                         return;
                 }
                 default: return;
             }
         }
 
-        static if (allHaveSemiImmutableConstruction)
+        static if (__allHaveSemiImmutableConstruction)
         this(return ref scope const typeof(this) rhs) immutable
         {
-            this.bytes = rhs.bytes;
-            this.type = rhs.type;
-            S: switch (type)
+            this.__bytes = rhs.__bytes;
+            static if (__Types.length > 1)
+                this.__type = rhs.__type;
+            S: switch (__type)
             {
-                static foreach (i, T; Types)
+                static foreach (i, T; __Types)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        emplaceRef!(immutable T)(this.mutableTrustedGet!T, rhs.trustedGet!T);
+                        emplaceRef!(immutable T)(this.__mutableTrustedGet!T, rhs.__trustedGet!T);
                         return;
                 }
                 default: return;
             }
         }
 
-        static if (allCanRemoveConst && allHaveMutableConstruction)
+        static if (__allCanRemoveConst && __allHaveMutableConstruction)
         this(return ref scope const typeof(this) rhs)
         {
-            this.bytes = rhs.bytes;
-            this.type = rhs.type;
-            S: switch (type)
+            this.__bytes = rhs.__bytes;
+            static if (__Types.length > 1)
+                this.__type = rhs.__type;
+            S: switch (__type)
             {
-                static foreach (i, T; Types)
+                static foreach (i, T; __Types)
                 static if (hasElaborateCopyConstructor!T)
                 {
                     case i:
-                        this.trustedGet!T.emplaceRef(rhs.trustedGet!T);
+                        this.__trustedGet!T.emplaceRef(rhs.__trustedGet!T);
                         return;
                 }
                 default: return;
             }
         }
 
-        static if (allCanRemoveConst && allHaveMutableConstruction)
+        static if (__allCanRemoveConst && __allHaveMutableConstruction)
         ref opAssign(return ref scope const typeof(this) rhs) return
         {
             static if (__traits(hasMember, this, "__dtor"))
@@ -469,7 +538,7 @@ struct TypeSets(uint id, Sets...)
             return this;
         }
 
-        static if (allCanRemoveConst && allHaveMutableConstruction)
+        static if (__allCanRemoveConst && __allHaveMutableConstruction)
         ref opAssign(typeof(this) rhs) return
         {
             swap(this, rhs);
@@ -477,77 +546,86 @@ struct TypeSets(uint id, Sets...)
         }
     }
 
-    static if (is(Types[0] == typeof(null)))
+    static if (is(__Types[0] == typeof(null)))
     {
         /// Defined if the first type is `typeof(null)`
-        bool isNull() const { return type == 0; }
+        bool isNull() const { return __type == 0; }
         /// ditto
         void nullify() { this = null; }
 
-        static if (allSatisfy!(isCopyable, Types[1 .. $]) && Types.length != 2)
+        static if (allSatisfy!(isCopyable, __Types[1 .. $]) && __Types.length != 2)
         /// ditto
         auto get()
         {
-            if (!type)
+            if (!__type)
             {
                 version(D_Exceptions)
                     throw variantNullException;
                 else
                     assert(0, variantNullExceptionMsg);
             }
-
-            Variant!(Types[1 .. $]) ret;
-            ret.type = this.type - 1;
-
-            static if (anySatisfy!(hasElaborateCopyConstructor, Types))
+            static if (__Types.length > 1)
             {
-                ret.bytes = 0;
-                S: switch (type)
+                Variant!(__Types[1 .. $]) ret;
+                static if (ret.__Types.length > 1)
+                    ret.__type = this.__type - 1;
+
+                static if (anySatisfy!(hasElaborateCopyConstructor, __Types))
                 {
-                    static foreach (i, T; Types)
+                    ret.__bytes = 0;
+                    S: switch (__type)
                     {
-                        static if (hasElaborateCopyConstructor!T)
+                        static foreach (i, T; __Types)
                         {
-                            case i:
-                                ret.trustedGet!T.emplaceRef(this.trustedGet!T);
-                                break S;
+                            static if (hasElaborateCopyConstructor!T)
+                            {
+                                case i:
+                                    ret.__trustedGet!T.emplaceRef(this.__trustedGet!T);
+                                    break S;
+                            }
                         }
+                        default:
+                            ret.__bytes = this.__bytes[0 .. ret.__bytes.length];
                     }
-                    default:
-                        ret.bytes = this.bytes[0 .. ret.bytes.length];
                 }
-            }
-            else
-            {
-                ret.bytes = this.bytes[0 .. ret.bytes.length];
-            }
+                else
+                {
+                    ret.__bytes = this.__bytes[0 .. ret.__bytes.length];
+                }
 
-            return ret;
+                return ret;
+            }
         }
 
-        static if (Types.length == 2)
+        static if (__Types.length == 2)
         /// ditto
-        alias get = get!(Types[1]);
+        alias get = get!(__Types[1]);
     }
 
+    static if (__Types.length)
     /++
     Returns: zero based type index.
     +/
-    uint typeId() @nogc nothrow const @property
+    uint __typeId() @nogc nothrow const @property
     {
-        return type;
+        return __type;
     }
 
-    static if (allSatisfy!(templateOr!(isPOD, hasToHash), Types))
+    static if (allSatisfy!(templateOr!(isPOD, hasToHash), __Types))
     /++
     +/
     size_t toHash() const
     {
-        static if (allSatisfy!(isPOD, Types))
+        static if (allSatisfy!(isPOD, __Types))
         {
+            static if (__Types.length == 0 || is(__Types == AliasSeq!(typeof(null))))
+            {
+                return 0;
+            }
+            else
             static if (this.sizeof <= 16)
             {
-                return hashOf(bytes, type);
+                return hashOf(__bytes, __type);
             }
             else
             {
@@ -559,70 +637,113 @@ struct TypeSets(uint id, Sets...)
                 else
                     alias UInt = uint;
 
-                static immutable UInt[Types.length + 1] sizes = [0, staticMap!(Sizeof, Types)];
-                return hashOf(bytes[0 .. sizes[type]], type);
+                static immutable UInt[__Types.length + 1] sizes = [0, staticMap!(Sizeof, __Types)];
+                return hashOf(__bytes[0 .. sizes[__type]], __type);
             }
         }
         else
-        switch (type)
+        switch (__type)
         {
-            static foreach (i, T; Types)
+            static foreach (i, T; __Types)
             {
                 case i:
-                    return hashOf(trustedGet!T, type);
+                    return hashOf(__trustedGet!T, i);
             }
             default: assert(0);
         }
     }
 
-    static if (allSatisfy!(templateOr!(isPOD, hasOpEquals), Types))
+    static if (allSatisfy!(templateOr!(isPOD, hasOpEquals), __Types))
     /++
     +/
     auto opEquals()(auto ref const typeof(this) rhs) const
     {
-        if (this.type != rhs.type)
-            return false;
-        switch (type)
+        static if (__Types.length == 0)
+        {
+            return true;
+        }
+        else
         {
-            static foreach (i, T; Types)
+            if (this.__type != rhs.__type)
+                return false;
+            switch (__type)
             {
-                case i:
-                    return this.trustedGet!T == rhs.trustedGet!T;
+                static foreach (i, T; __Types)
+                {
+                    case i:
+                        return this.__trustedGet!T == rhs.__trustedGet!T;
+                }
+                default: assert(0);
             }
-            default: assert(0);
         }
-    } 
+    }
 
-    static if (allSatisfy!(isOrderingComparable, Types))
+    static if (allSatisfy!(isOrderingComparable, __Types))
     /++
     +/
     auto opCmp()(auto ref const typeof(this) rhs) const
     {
-        if (auto d = int(this.type) - int(rhs.type))
-            return d;
-        switch (type)
+        static if (__Types.length == 0)
         {
-            static foreach (i, T; Types)
+            return 0;
+        }
+        else
+        {
+            if (auto d = int(this.__type) - int(rhs.__type))
+                return d;
+            switch (__type)
             {
-                case i:
-                    static if (__traits(hasMember, T, "opCmp"))
-                        return this.trustedGet!T.opCmp(rhs.trustedGet!T);
-                    else
-                        return this.trustedGet!T < rhs.trustedGet!T ? -1 :
-                            this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
+                static foreach (i, T; __Types)
+                {
+                    case i:
+                        static if (__traits(hasMember, T, "opCmp"))
+                            return this.__trustedGet!T.opCmp(rhs.__trustedGet!T);
+                        else
+                            return this.__trustedGet!T < rhs.__trustedGet!T ? -1 :
+                                this.__trustedGet!T > rhs.__trustedGet!T ? +1 : 0;
+                }
+                default: assert(0);
+            }
+        }
+    }
+
+    ///
+    void toString(C, W)(scope ref W w)
+    {
+        static if (__Types.length == 0)
+        {
+            return w.put(cast(immutable C[])"Algebraic");
+        }
+        else
+        {
+            import mir.format: print;
+            switch (__type)
+            {
+                static foreach (i, T; __Types)
+                {
+                    case i:
+                        print(w, __trustedGet!T);
+                }
+                default: assert(0);
             }
-            default: assert(0);
         }
     }
 }
 
 /++
-Variant Type (aka TypeSets Type) with clever member access.
+Variant Type (aka Algebraic Type) with clever member access.
 
 Compatible with BetterC mode.
 +/
 
-alias Variant(T...) = TypeSets!(0, TypeSet!T);
+alias Variant(T...) = Algebraic!(0, TypeSet!T);
+
+/++
+Nullable variant Type (aka Algebraic Type) with clever member access.
+
+Compatible with BetterC mode.
++/
+alias Nullable(T...) = Variant!(typeof(null), T);
 
 // example from std.variant
 /**
@@ -677,7 +798,7 @@ unittest
 
     static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true)
     {
-        ubyte[payloadSize] payload;
+        ubyte[payloadSize] __payload;
 
     const:
 
@@ -691,11 +812,11 @@ unittest
 
 
     static if (hasToHash)
-        size_t toHash() { return hashOf(payload); }
+        size_t toHash() { return hashOf(__payload); }
 
     static if (hasOpEquals)
-        auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(payload.ptr, rhs.payload.ptr, payload.length); }
-        auto opCmp(ref const typeof(this) rhs) { return payload == rhs.payload; }
+        auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(__payload.ptr, rhs.__payload.ptr, __payload.length); }
+        auto opCmp(ref const typeof(this) rhs) { return __payload == rhs.__payload; }
     }
 
     static foreach (size1; [1, 2, 4, 8, 10, 16, 20])
@@ -938,6 +1059,47 @@ unittest
     assert(z == y);
 }
 
+// Type with copy constructor
+@safe pure nothrow @nogc 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;
+
+    assert(a.get!S.n == 0);
+    assert(b.get!S.n == 1);
+}
+
+// Empty type set
+@safe pure nothrow @nogc unittest 
+{
+    Variant!() a;
+    auto b = a;
+    assert(a.toHash == 0);
+    assert(a == b);
+    assert(a <= b && b >= a);
+    static assert(typeof(a).sizeof == 1);
+}
+
+// Nullable only type set
+@safe pure nothrow @nogc unittest 
+{
+    Variant!(typeof(null)) a;
+    auto b = a;
+    assert(a.toHash == 0);
+    assert(a == b);
+    assert(a <= b && b >= a);
+    static assert(typeof(a).sizeof == 1);
+}
+
 /++
 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
 +/
@@ -1030,7 +1192,7 @@ private template getMemberHandler(string member)
     }
 }
 
-// AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), _Types))
+// AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), __Types))
 
 private template VariantReturnTypes(T...)
 {
@@ -1055,9 +1217,9 @@ template visit(alias visitor, bool exhaustive = true)
 
         // template VariantReturnTypesImpl(T)
         // {
-        //     static if (__traits(compiles, visitor(variant.trustedGet!T, forward!args)))
+        //     static if (__traits(compiles, visitor(variant.__trustedGet!T, forward!args)))
         //     {
-        //         alias Ret = typeof(visitor(variant.trustedGet!T, forward!args));
+        //         alias Ret = typeof(visitor(variant.__trustedGet!T, forward!args));
         //         static if (is(Ret == void))
         //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
         //         else
@@ -1069,7 +1231,7 @@ template visit(alias visitor, bool exhaustive = true)
         //     }
         // }
 
-        // alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V.Types));
+        // alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V.__Types));
 
         // static if (AllReturnTypes.length == 0 || is(AllReturnTypes == AliasSeq!(typeof(null))))
         // {
@@ -1086,9 +1248,9 @@ template visit(alias visitor, bool exhaustive = true)
         //     import std.traits: Unqual, CopyTypeQualifiers;
         //     import std.meta: AliasSeq;
 
-        //     static if (__traits(compiles, visitor(variant.trustedGet!T, forward!args)))
+        //     static if (__traits(compiles, visitor(variant.__trustedGet!T, forward!args)))
         //     {
-        //         alias Ret = typeof(visitor(variant.trustedGet!T, forward!args));
+        //         alias Ret = typeof(visitor(variant.__trustedGet!T, forward!args));
         //         static if (is(Ret == void))
         //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
         //         else
@@ -1107,19 +1269,19 @@ template visit(alias visitor, bool exhaustive = true)
         // }
 
         // import std.meta: staticMap;
-        // alias Types = VariantReturnTypes!(staticMap!(VariantReturnTypesImpl, V.Types));
+        // alias Types = VariantReturnTypes!(staticMap!(VariantReturnTypesImpl, V.__Types));
 
-        // V.Types;
-        switch (variant.type)
+        // V.__Types;
+        switch (variant.__type)
         {
-            static foreach (i, T; V.Types)
+            static foreach (i, T; V.__Types)
             {
                 case i:
-                    static if (__traits(compiles, visitor(variant.trustedGet!T, forward!args)))
-                        return visitor(variant.trustedGet!T, forward!args);
+                    static if (__traits(compiles, visitor(variant.__trustedGet!T, forward!args)))
+                        return visitor(variant.__trustedGet!T, forward!args);
                     else
                     static if (exhaustive && !is(T == typeof(null)))
-                        static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ T.stringof ~ " and additional arguments " ~ Args.stringof);
+                        static assert(0, V.stringof ~ ": the visitor cann't be caled with __type (first argument) " ~ T.stringof ~ " and additional arguments " ~ Args.stringof);
                     else
                     static if (is(T == typeof(null)) && i == 0)
                         assert(0, "Null " ~ V.stringof);
@@ -1173,13 +1335,13 @@ template optionalVisit(alias visitor)
         if (isVariant!V)
     {
         import core.lifetime: forward;
-        switch (variant.type)
+        switch (variant.__type)
         {
-            static foreach (i, T; V.Types)
-            static if (__traits(compiles, result = visitor(variant.trustedGet!T, forward!args)))
+            static foreach (i, T; V.__Types)
+            static if (__traits(compiles, result = visitor(variant.__trustedGet!T, forward!args)))
             {
                 case i:
-                    result = visitor(variant.trustedGet!T, forward!args);
+                    result = visitor(variant.__trustedGet!T, forward!args);
                     return true;
             }
             default:

From e1ba595edb5a849fca87c0c2c27d93a8b71fadfe Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 3 Nov 2020 13:23:49 +0700
Subject: [PATCH 118/573] extend mir.variant

---
 source/mir/variant.d | 233 +++++++++++++++++++++++--------------------
 1 file changed, 125 insertions(+), 108 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 78bc463e..9a97f71e 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -18,6 +18,7 @@ version (D_Exceptions)
 }
 
 private alias ConstOf(T) = const T;
+private enum Alignof(T) = T.alignof;
 private enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a; } );
 private enum canImplicitlyRemoveConst(T) = __traits(compiles, {static T _function_(ref const T a) { return a; }} );
 private enum canRemoveConst(T) = canConstructWith!(const T, T);
@@ -25,7 +26,7 @@ private enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
 private enum hasConstConstruction(T) = __traits(compiles, (const T a) { auto b = a; } );
 private enum hasImmutableConstruction(T) = __traits(compiles, (immutable T a) { auto b = a; } );
 private enum hasMutableConstruction(T) = __traits(compiles, (T a) { auto b = a; } );
-private enum hasOpEquals(T) = __traits(hasMember, T, "opEquals");
+private enum hasOpPostMove(T) = __traits(hasMember, T, "opPostMove");
 private enum hasSemiImmutableConstruction(T) = __traits(compiles, (const T a) { immutable b = a; } );
 private enum hasToHash(T) = __traits(hasMember, T, "toHash");
 private enum isCopyable(S) = __traits(isCopyable, S); 
@@ -67,55 +68,14 @@ unittest
     static assert(is(TryRemoveConst!(const int) == int));
 }
 
-private enum bool TypeCmp(A, B) =
-    is(A == B) ? false:
+private template TypeCmp(A, B)
+{
+    enum bool TypeCmp = is(A == B) ? false:
     is(A == typeof(null)) ? true:
+    is(B == typeof(null)) ? false:
     A.sizeof < B.sizeof ? true:
     A.sizeof > B.sizeof ? false:
     A.mangleof < B.mangleof;
-
-
-private template staticMerge(alias cmp, int half, Seq...)
-{
-    static if (half == 0 || half == Seq.length)
-    {
-        alias staticMerge = Seq;
-    }
-    else
-    {
-        import std.meta: AliasSeq;
-        static if (cmp!(Seq[0], Seq[half]))
-        {
-            alias staticMerge = AliasSeq!(Seq[0],
-                staticMerge!(cmp, half - 1, Seq[1 .. $]));
-        }
-        else
-        {
-            alias staticMerge = AliasSeq!(Seq[half],
-                staticMerge!(cmp, half, Seq[0 .. half], Seq[half + 1 .. $]));
-        }
-    }
-}
-
-private template staticSort(alias cmp, Seq...)
-{
-    static if (Seq.length < 2)
-    {
-        alias staticSort = Seq;
-    }
-    else
-    {
-        import std.meta: AliasSeq;
-        private alias btm = staticSort!(cmp, Seq[0 .. $ / 2]);
-        private alias top = staticSort!(cmp, Seq[$ / 2 .. $]);
-
-        static if (cmp!(btm[$ - 1], top[0]))
-            alias staticSort = AliasSeq!(btm, top); // already ascending
-        else static if (cmp!(top[$ - 1], btm[0]))
-            alias staticSort = AliasSeq!(top, btm); // already descending
-        else
-            alias staticSort = staticMerge!(cmp, Seq.length / 2, btm, top);
-    }
 }
 
 private template isInstanceOf(alias S)
@@ -132,21 +92,22 @@ struct SetAlias(uint id);
 ///ditto
 alias This = SetAlias!0;
 
-
-
 /++
 +/
 template TypeSet(T...)
 {
-    import std.meta: NoDuplicates;
+    import std.meta: NoDuplicates, staticSort, staticMap;
     // sort types by siezeof and them mangleof
-    static if (is(NoDuplicates!T == T))
-        static if (staticIsSorted!(TypeCmp, T))
-            struct TypeSet;
+    static if (is(staticMap!(TryRemoveConst, T) == T))
+        static if (is(NoDuplicates!T == T))
+            static if (staticIsSorted!(TypeCmp, T))
+                struct TypeSet;
+            else
+                alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T));
         else
-            alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T));
+            alias TypeSet = TypeSet!(NoDuplicates!T);
     else
-        alias TypeSet = TypeSet!(NoDuplicates!T);
+        alias TypeSet = staticMap!(TryRemoveConst, T);
 }
 
 
@@ -191,21 +152,16 @@ private static struct __Null()
 struct Algebraic(uint _setId, _TypeSets...)
     if (allSatisfy!(isInstanceOf!TypeSet, _TypeSets))
 {
+    import core.lifetime: moveEmplace;
     import mir.conv: emplaceRef;
-    import mir.utility: max, swap;
     import std.meta: AliasSeq, anySatisfy, allSatisfy, staticMap, templateOr;
-    import std.traits: TemplateArgsOf;
     import std.typecons: ReplaceTypeUnless;
     import std.traits:
-        CopyTypeQualifiers,
-        hasElaborateAssign,
         hasElaborateCopyConstructor,
         hasElaborateDestructor,
-        isAssignable,
         isEqualityComparable,
         isOrderingComparable,
         Largest,
-        Select,
         TemplateArgsOf
         ;
 
@@ -229,34 +185,29 @@ struct Algebraic(uint _setId, _TypeSets...)
 
     union
     {
-        static if (__Types.length)
-        {
-            static if (is(__Types[0] == typeof(null)))
-            {
-                private alias __Payload =  AliasSeq!(__Null!(), __Types[1 .. $]);
-            }
-            else
-                alias __Payload = __Types;
-            private __Payload __payload;
-            private ubyte[Largest!__Payload.sizeof] __bytes;
-        }
+        static if (is(__Types[0] == typeof(null)))
+            private alias __Payload =  AliasSeq!(__Null!(), __Types[1 .. $]);
         else
-        {
-            alias __Payload = __Types;
-            private __Payload __payload;
+            private alias __Payload = __Types;
+
+        private __Payload __payload;
+
+        static if (__Types.length == 0 || is(__Types == AliasSeq!(typeof(null))))
             private ubyte[0] __bytes;
-        }
+        else
+            private ubyte[Largest!__Payload.sizeof] __bytes;
     }
 
     static if (__Types.length)
     {
         static if (__Types.length > 1)
         {
-            // 0 for unininit value, index = type - 1
-            static if (__bytes.length == 1)
+            import mir.utility: max;
+            private enum __alignof = max(staticMap!(Alignof, __Payload));
+            static if ((__bytes.length | __alignof) & 1)
                 private ubyte __type; 
             else
-            static if (__bytes.length == 2)
+            static if ((__bytes.length | __alignof) & 2)
                 private ushort __type;
             else
                 private uint __type;
@@ -267,8 +218,6 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    private enum __hasDestructor = anySatisfy!(hasElaborateDestructor, __Types);
-
     static foreach (i, T; __Types)
     {
         /// Zero cost always nothrow `get` alternative
@@ -304,20 +253,17 @@ struct Algebraic(uint _setId, _TypeSets...)
                         assert(0, variantExceptionMsg);
                 }
             }
-            static if (is(T == typeof(null)))
-                return null;
-            else
-                return __trustedGet!T;
+            return __trustedGet!T;
         }
         
         static if (hasMutableConstruction!T)
         ///
-        this(T value)
+        this(T value) @trusted
         {
-            import core.lifetime: move;
             static if (__Types.length > 1)
                 __type = i;
-            emplaceRef(__payload[i], move(value));
+            static if (!is(T == typeof(null)))
+                moveEmplace(value, __payload[i]);
         }
 
         static if (!hasSemiImmutableConstruction!T)
@@ -351,29 +297,28 @@ struct Algebraic(uint _setId, _TypeSets...)
             }
         }
 
-        static if (__traits(compiles, (ref T a, ref T b) { swap(a, b); }))
+        static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); }))
         ///
-        ref opAssign(T value) return
+        ref opAssign(T value) return @trusted
         {
             static if (__traits(hasMember, this, "__dtor"))
                 __dtor;
             static if (__Types.length > 1)
                 __type = i;
-            static if (__traits(isZeroInit, T))
+            static if (__traits(isZeroInit, T) || is(T == typeof(null)))
             {
                 __bytes[] = 0;
             }
             else
             {
-                emplaceRef(__payload[i]);
-                __bytes[T.sizeof .. $] = 0;
+                __bytes[__Payload[i].sizeof .. $] = 0;
             }
             static if (!is(T == typeof(null)))
-                swap(__payload[i], value);
+                moveEmplace(value, __payload[i]);
             return this;
         }
 
-        static if (isPOD!T || hasOpEquals!T)
+        static if (isEqualityComparable!T)
         /++
         +/
         auto opEquals()(auto ref const T rhs) const
@@ -400,7 +345,7 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (__hasDestructor)
+    static if (anySatisfy!(hasElaborateDestructor, __Types))
     ~this() @safe
     {
         S: switch (__type)
@@ -416,6 +361,23 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
+
+    static if (anySatisfy!(hasOpPostMove, __Types))
+    void opPostMove(const ref typeof(this) old)
+    {
+        S: switch (__type)
+        {
+            static foreach (i, T; __Types)
+            static if (hasOpPostMove!T)
+            {
+                case i:
+                    this.__payload[i].opPostMove(old.__payload[i]);
+                    return;
+            }
+            default: return;
+        }
+    }
+
     private enum allCopyable = allSatisfy!(isCopyable, __Types);
 
     static if (__Types.length)
@@ -539,10 +501,9 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
 
         static if (__allCanRemoveConst && __allHaveMutableConstruction)
-        ref opAssign(typeof(this) rhs) return
+        ref opAssign(typeof(this) rhs) return @trusted
         {
-            swap(this, rhs);
-            return this;
+            moveEmplace(rhs, this);
         }
     }
 
@@ -568,7 +529,7 @@ struct Algebraic(uint _setId, _TypeSets...)
             {
                 Variant!(__Types[1 .. $]) ret;
                 static if (ret.__Types.length > 1)
-                    ret.__type = this.__type - 1;
+                    ret.__type = cast(typeof(ret.__type))(this.__type - 1);
 
                 static if (anySatisfy!(hasElaborateCopyConstructor, __Types))
                 {
@@ -599,7 +560,10 @@ struct Algebraic(uint _setId, _TypeSets...)
 
         static if (__Types.length == 2)
         /// ditto
-        alias get = get!(__Types[1]);
+        ref inout(__Types[1]) get() inout
+        {
+            return get!(__Types[1]);
+        }
     }
 
     static if (__Types.length)
@@ -653,7 +617,7 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (allSatisfy!(templateOr!(isPOD, hasOpEquals), __Types))
+    static if (allSatisfy!(isEqualityComparable, __Types))
     /++
     +/
     auto opEquals()(auto ref const typeof(this) rhs) const
@@ -735,7 +699,6 @@ Variant Type (aka Algebraic Type) with clever member access.
 
 Compatible with BetterC mode.
 +/
-
 alias Variant(T...) = Algebraic!(0, TypeSet!T);
 
 /++
@@ -778,8 +741,6 @@ be arbitrarily complex.
     alias Obj = Variant!(double, string, This[string]);
     alias Map = Obj[string];
 
-    pragma(msg, Obj);
-
     Obj obj = "hello";
     assert(obj.get!string == "hello");
     obj = 42.0;
@@ -954,6 +915,8 @@ unittest
 @safe pure nothrow @nogc
 unittest
 {
+    import core.lifetime: move;
+
     static struct S {
         immutable(uint)* value;
         this(return ref scope typeof(this) rhs) {}
@@ -1029,12 +992,28 @@ unittest
     assert(y.match!((int v) => false, (float v) => true));
 }
 
-
 /// Special `typeof(null)` support for the first argument.
 @safe pure @nogc
 unittest
 {
-    alias Number = Variant!(typeof(null), int, double);
+    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);
+}
+
+/// ditto
+@safe pure @nogc
+unittest
+{
+    alias Number = Nullable!(int, double);
 
     Number z = null; // default
     Number x = 23;
@@ -1049,6 +1028,7 @@ unittest
     auto xx = x.get;
     static assert (is(typeof(xx) == Variant!(int, double)));
     assert(xx.match!((int v) => v, (float v) => 0) == 23);
+    assert(xx.match!((ref v) => v) == 23);
 
     x = null;
     y.nullify;
@@ -1089,10 +1069,10 @@ unittest
     static assert(typeof(a).sizeof == 1);
 }
 
-// Nullable only type set
+// Empty nullable type set
 @safe pure nothrow @nogc unittest 
 {
-    Variant!(typeof(null)) a;
+    Nullable!() a;
     auto b = a;
     assert(a.toHash == 0);
     assert(a == b);
@@ -1100,6 +1080,43 @@ unittest
     static assert(typeof(a).sizeof == 1);
 }
 
+// Small types
+@safe pure nothrow @nogc unittest 
+{
+    struct S { ubyte d; }
+    static assert(Nullable!(byte, char, S).sizeof == 2);
+}
+
+// Alignment
+@safe pure nothrow @nogc unittest 
+{
+    struct S { ubyte[3] d; }
+    static assert(Nullable!(ushort, wchar, S).sizeof == 4);
+}
+
+// opPostMove support
+version(mir_test)
+@safe pure @nogc nothrow
+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.get!S.s == 1);
+}
+
 /++
 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
 +/

From 1b9ec9f776ce3f1d1d00eacaaeefbaf3e539cc5b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 4 Nov 2020 18:45:31 +0700
Subject: [PATCH 119/573] fix format

---
 source/mir/format.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 1fba728d..a851942f 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -664,9 +664,9 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
         foreach (ref e; c.lightConst)
         {
             if (!first)
-                w.printStaticString!C(sep);
+                printStaticString!C(w, sep);
             first = false;
-            w.printStaticString!C(e);
+            print!C(w, e);
         }
         w.put(right);
         return w;

From 103a30647b79796d1ca6d2bd7128a638b7c4457d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 11 Nov 2020 11:06:12 +0700
Subject: [PATCH 120/573] fix interpolation

---
 source/mir/interpolate/constant.d | 2 +-
 source/mir/interpolate/linear.d   | 2 +-
 source/mir/interpolate/spline.d   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 36335ed3..a452e50d 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -80,7 +80,7 @@ struct Constant(F, size_t N = 1, X = F)
     /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
     Slice!(RCI!(const F), N) _data;
     /// Grid iterators. $(RED For internal use.)
-    RCI!(immutable X)[N] _grid;
+    Repeat!(N, RCI!(immutable X)) _grid;
 
 extern(D):
 
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 28ded947..e1725d8f 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -180,7 +180,7 @@ struct Linear(F, size_t N = 1, X = F)
     /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
     Slice!(RCI!(const F), N) _data;
     /// Grid iterators. $(RED For internal use.)
-    RCI!(immutable X)[N] _grid;
+    Repeat!(N, RCI!(immutable X)) _grid;
 
 @optmath extern(D):
 
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 59372cfd..f2191359 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -614,7 +614,7 @@ struct Spline(F, size_t N = 1, X = F)
     /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
     Slice!(RCI!(F[2 ^^ N]), N) _data;
     /// Grid iterators. $(RED For internal use.)
-    RCI!(immutable X)[N] _grid;
+    Repeat!(N, RCI!(immutable X)) _grid;
 
 @fmamath extern(D):
 

From 75d1baced970aae1a1f308c85fc0c13143ecd73c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 11 Nov 2020 11:06:25 +0700
Subject: [PATCH 121/573] update variant

---
 source/mir/variant.d | 748 ++++++++++++++++++++++++-------------------
 1 file changed, 420 insertions(+), 328 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 9a97f71e..b3781242 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -23,17 +23,32 @@ private enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a
 private enum canImplicitlyRemoveConst(T) = __traits(compiles, {static T _function_(ref const T a) { return a; }} );
 private enum canRemoveConst(T) = canConstructWith!(const T, T);
 private enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
-private enum hasConstConstruction(T) = __traits(compiles, (const T a) { auto b = a; } );
-private enum hasImmutableConstruction(T) = __traits(compiles, (immutable T a) { auto b = a; } );
-private enum hasMutableConstruction(T) = __traits(compiles, (T a) { auto b = a; } );
 private enum hasOpPostMove(T) = __traits(hasMember, T, "opPostMove");
-private enum hasSemiImmutableConstruction(T) = __traits(compiles, (const T a) { immutable b = a; } );
 private enum hasToHash(T) = __traits(hasMember, T, "toHash");
 private enum isCopyable(S) = __traits(isCopyable, S); 
 private enum isPOD(T) = __traits(isPOD, T);
-private enum isVariant(T) = __traits(hasMember, T, "__isVariant");
+private enum isVariant(T) = __traits(hasMember, T, "_isVariant");
 private enum Sizeof(T) = T.sizeof;
 
+private enum hasInoutConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope inout S rhs) inout { this.a = rhs.a; } }} );
+private enum hasConstConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) const { this.a = rhs.a; } }} );
+private enum hasImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope immutable S rhs) immutable { this.a = rhs.a; } }} );
+private enum hasMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope S rhs) { this.a = rhs.a; } }} );
+private enum hasSemiImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) immutable { this.a = rhs.a; } }} );
+private enum hasSemiMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) { this.a = rhs.a; } }} );
+
+@safe unittest
+{
+    static struct S { this(ref return scope inout S) inout {} }
+    static inout(S) _function_(ref inout S a) { return S(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);
+    // static assert(!hasInoutConstruction!C2);
+}
+
 private template staticIsSorted(alias cmp, Seq...)
 {
     static if (Seq.length <= 1)
@@ -135,12 +150,12 @@ template Variants(Sets...)
     alias Variants = staticMap!(TypeSetsInst, Iota!(Sets.length));
 }
 
-private static struct __Null()
+private static struct _Null()
 {
     @safe pure nothrow @nogc const:
         size_t toHash() {return 0;}
-        bool opEquals(__Null) { return true; }
-        int opCmp(__Null) { return 0; }
+        bool opEquals(_Null) { return true; }
+        int opCmp(_Null) { return 0; }
 
     typeof(null) __self() @property { return null; }
     typeof(null) __self(typeof(null)) @property { return null; }
@@ -157,6 +172,7 @@ struct Algebraic(uint _setId, _TypeSets...)
     import std.meta: AliasSeq, anySatisfy, allSatisfy, staticMap, templateOr;
     import std.typecons: ReplaceTypeUnless;
     import std.traits:
+        hasElaborateAssign,
         hasElaborateCopyConstructor,
         hasElaborateDestructor,
         isEqualityComparable,
@@ -165,77 +181,89 @@ struct Algebraic(uint _setId, _TypeSets...)
         TemplateArgsOf
         ;
 
-    private template __ApplyAliasesImpl(uint length, Types...)
+    private template _ApplyAliasesImpl(uint length, Types...)
     {
         static if (length == 0)
-            alias __ApplyAliasesImpl = Types;
+            alias _ApplyAliasesImpl = Types;
         else
         {
             enum next  = length - 1;
             import std.typecons: ReplaceTypeUnless;
-            alias __ApplyAliasesImpl = __ApplyAliasesImpl!(next,
+            alias _ApplyAliasesImpl = _ApplyAliasesImpl!(next,
                 ReplaceTypeUnless!(isVariant, SetAlias!next, Algebraic!(next, _TypeSets), Types));
         }
     }
 
-    private enum __isVariant;
+    private enum _isVariant;
 
     ///
-    alias __Types = __ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId]));
-
-    union
-    {
-        static if (is(__Types[0] == typeof(null)))
-            private alias __Payload =  AliasSeq!(__Null!(), __Types[1 .. $]);
-        else
-            private alias __Payload = __Types;
-
-        private __Payload __payload;
+    alias _Types = _ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId]));
 
-        static if (__Types.length == 0 || is(__Types == AliasSeq!(typeof(null))))
-            private ubyte[0] __bytes;
-        else
-            private ubyte[Largest!__Payload.sizeof] __bytes;
-    }
+    static if (is(_Types[0] == typeof(null)))
+        private alias _Payload =  AliasSeq!(_Null!(), _Types[1 .. $]);
+    else
+        private alias _Payload = _Types;
 
-    static if (__Types.length)
+    private static union _Storage
     {
-        static if (__Types.length > 1)
+        _Payload payload;
+
+        static if (_Types.length == 0 || is(_Types == AliasSeq!(typeof(null))))
         {
-            import mir.utility: max;
-            private enum __alignof = max(staticMap!(Alignof, __Payload));
-            static if ((__bytes.length | __alignof) & 1)
-                private ubyte __type; 
-            else
-            static if ((__bytes.length | __alignof) & 2)
-                private ushort __type;
-            else
-                private uint __type;
+            ubyte[0] bytes;
+            static if (_Types.length)
+                enum uint _type = 0;
         }
         else
+        struct
         {
-            private enum uint __type = 0;
+            ubyte[Largest!_Payload.sizeof] bytes;
+
+            static if (_Types.length > 1)
+            {
+                import mir.utility: max;
+                enum _alignof = max(staticMap!(Alignof, _Payload));
+                static if ((bytes.length | _alignof) & 1)
+                    ubyte _type;
+                else
+                static if ((bytes.length | _alignof) & 2)
+                    ushort _type;
+                else
+                    uint _type;
+            }
+            else
+            {
+                enum uint _type = 0;
+            }
         }
+    
+        static if (bytes.length && _Types.length)
+            ubyte[bytes.length + _type.sizeof] allBytes;
+        else
+            alias allBytes = bytes;
     }
 
-    static foreach (i, T; __Types)
+    private _Storage _storage;
+
+    static foreach (i, T; _Types)
     {
         /// Zero cost always nothrow `get` alternative
-        auto ref __trustedGet(E)() @trusted @property return inout nothrow
+        auto ref _trustedGet(E)() @trusted @property return inout nothrow
             if (is(E == T))
         {
-            assert (i == __type);
+            assert (i == _storage._type);
             static if (is(T == typeof(null)))
                 return null;
             else
-                return __payload[i];
+                return _storage.payload[i];
         }
 
-        private ref T __mutableTrustedGet(E)() @trusted @property return const nothrow
+        private ref T _mutableTrustedGet(E)() @trusted @property return const nothrow
             if (is(E == T))
         {
-            assert (i == __type);
-            return *cast(__Types[i]*)&__payload[i];
+            import std.traits: Unqual;
+            assert (i == _storage._type, T.stringof);
+            return *cast(Unqual!(_Types[i])*)&_storage.payload[i];
         }
 
         ///
@@ -243,9 +271,9 @@ struct Algebraic(uint _setId, _TypeSets...)
             if (is(E == T))
         {
             import mir.utility: _expect;
-            static if (__Types.length > 1)
+            static if (_Types.length > 1)
             {
-                if (_expect(i != __type, false))
+                if (_expect(i != _storage._type, false))
                 {
                     version(D_Exceptions)
                         throw variantException;
@@ -253,47 +281,47 @@ struct Algebraic(uint _setId, _TypeSets...)
                         assert(0, variantExceptionMsg);
                 }
             }
-            return __trustedGet!T;
+            return _trustedGet!T;
         }
-        
+
         static if (hasMutableConstruction!T)
         ///
         this(T value) @trusted
         {
-            static if (__Types.length > 1)
-                __type = i;
+            static if (_Types.length > 1)
+                _storage._type = i;
             static if (!is(T == typeof(null)))
-                moveEmplace(value, __payload[i]);
+                moveEmplace(value, _storage.payload[i]);
         }
 
-        static if (!hasSemiImmutableConstruction!T)
+        static if (hasSemiImmutableConstruction!T)
+        {
+            ///
+            this(const T value) immutable
+            {
+                static if (_Types.length > 1)
+                    _storage._type = i;
+                emplaceRef!(immutable T)(_mutableTrustedGet!T, value);
+            }
+        }
+        else
         {
             static if (hasConstConstruction!T)
             ///
             this(const T value) const
             {
-                static if (__Types.length > 1)
-                    __type = i;
-                emplaceRef!(const T)(__mutableTrustedGet!T, value);
+                static if (_Types.length > 1)
+                    _storage._type = i;
+                emplaceRef!(const T)(_mutableTrustedGet!T, value);
             }
 
             static if (hasImmutableConstruction!T)
             ///
             this(immutable T value) immutable
             {
-                static if (__Types.length > 1)
-                    __type = i;
-                emplaceRef!(immutable T)(__mutableTrustedGet!T, value);
-            }
-        }
-        else
-        {
-            ///
-            this(const T value) immutable
-            {
-                static if (__Types.length > 1)
-                    __type = i;
-                emplaceRef!(immutable T)(__mutableTrustedGet!T, value);
+                static if (_Types.length > 1)
+                    _storage._type = i;
+                emplaceRef!(immutable T)(_mutableTrustedGet!T, value);
             }
         }
 
@@ -301,20 +329,13 @@ struct Algebraic(uint _setId, _TypeSets...)
         ///
         ref opAssign(T value) return @trusted
         {
-            static if (__traits(hasMember, this, "__dtor"))
-                __dtor;
-            static if (__Types.length > 1)
-                __type = i;
-            static if (__traits(isZeroInit, T) || is(T == typeof(null)))
-            {
-                __bytes[] = 0;
-            }
-            else
-            {
-                __bytes[__Payload[i].sizeof .. $] = 0;
-            }
+            static if (anySatisfy!(hasElaborateDestructor, _Types))
+                this.__dtor();
             static if (!is(T == typeof(null)))
-                moveEmplace(value, __payload[i]);
+                moveEmplace(value, _storage.payload[i]);
+            _storage.bytes[_Payload[i].sizeof .. $] = 0;
+            static if (_Types.length > 1)
+                _storage._type = i;
             return this;
         }
 
@@ -323,10 +344,10 @@ struct Algebraic(uint _setId, _TypeSets...)
         +/
         auto opEquals()(auto ref const T rhs) const
         {
-            static if (__Types.length > 1)
-                if (__type != i)
+            static if (_Types.length > 1)
+                if (_storage._type != i)
                     return false;
-            return __trustedGet!T == rhs;
+            return _trustedGet!T == rhs;
         } 
 
         static if (isOrderingComparable!T)
@@ -334,262 +355,277 @@ struct Algebraic(uint _setId, _TypeSets...)
         +/
         auto opCmp()(auto ref const T rhs) const
         {
-            static if (__Types.length > 1)
-                if (auto d = int(__type) - int(rhs.__type))
+            import mir.internal.utility: isFloatingPoint;
+            static if (_Types.length > 1)
+                if (auto d = int(_storage._type) - int(rhs._storage._type))
                     return d;
+            static if (__traits(compiles, __cmp(_trustedGet!T, rhs)))
+                return __cmp(_trustedGet!T, rhs);
+            else
             static if (__traits(hasMember, T, "opCmp"))
-                return __trustedGet!T.opCmp(rhs);
+                return _trustedGet!T.opCmp(rhs);
             else
-                return __trustedGet!T < rhs ? -1 :
-                    __trustedGet!T > rhs ? +1 : 0;
+            static if (isFloatingPoint!T)
+                return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs;
+            else
+                return _trustedGet!T < rhs ? -1 :
+                    _trustedGet!T > rhs ? +1 : 0;
         }
     }
 
-    static if (anySatisfy!(hasElaborateDestructor, __Types))
+    static if (anySatisfy!(hasElaborateDestructor, _Types))
     ~this() @safe
     {
-        S: switch (__type)
+        S: switch (_storage._type)
         {
-            static foreach (i, T; __Types)
+            static foreach (i, T; _Types)
             static if (hasElaborateDestructor!T)
             {
                 case i:
-                    .destroy!false(__trustedGet!T);
+                    _mutableTrustedGet!T.__xdtor;
                     break S;
             }
-            default: return;
+            default:
         }
+        version(mir_secure_memory)
+            _storage.allBytes = 0xCC;
     }
 
-
-    static if (anySatisfy!(hasOpPostMove, __Types))
+    static if (anySatisfy!(hasOpPostMove, _Types))
     void opPostMove(const ref typeof(this) old)
     {
-        S: switch (__type)
+        S: switch (_storage._type)
         {
-            static foreach (i, T; __Types)
+            static foreach (i, T; _Types)
             static if (hasOpPostMove!T)
             {
                 case i:
-                    this.__payload[i].opPostMove(old.__payload[i]);
+                    this._storage.payload[i].opPostMove(old._storage.payload[i]);
                     return;
             }
             default: return;
         }
     }
+    // static if (anySatisfy!(hasElaborateAssign, _Types))
+    // pragma(msg, typeof(&opAssign));
+
+    private enum allCopyable = allSatisfy!(isCopyable, _Types);
 
-    private enum allCopyable = allSatisfy!(isCopyable, __Types);
+    static if (_Types.length)
+    {
+        static if (!__traits(compiles, (){ _Types[0] arg; }))
+        {
 
-    static if (__Types.length)
-        static if (!__traits(compiles, (){ __Types[0] arg; }))
             @disable this();
+        }
+
+    }
 
     static if (!allCopyable)
         @disable this(this);
     else
-    static if (anySatisfy!(hasElaborateCopyConstructor, __Types))
+    static if (anySatisfy!(hasElaborateCopyConstructor, _Types))
     {
-        private enum __allCanRemoveConst = allSatisfy!(canRemoveConst, __Types);
-        private enum __allHaveConstConstruction = allSatisfy!(hasConstConstruction, __Types);
-        private enum __allHaveImmutableConstruction = allSatisfy!(hasImmutableConstruction, __Types);
-        private enum __allHaveMutableConstruction = allSatisfy!(hasMutableConstruction, __Types);
-        private enum __allHaveSemiImmutableConstruction = allSatisfy!(hasSemiImmutableConstruction, __Types);
-
-        static if (__allHaveMutableConstruction)
-        this(return ref scope typeof(this) rhs)
+        // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, _Types);
+        // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, _Types);
+        // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction;
+
+        private static union _StorageI(uint i)
         {
-            this.__bytes = rhs.__bytes;
-            static if (__Types.length > 1)
-                this.__type = rhs.__type;
-            S: switch (__type)
-            {
-                static foreach (i, T; __Types)
-                static if (hasElaborateCopyConstructor!T)
-                {
-                    case i:
-                        this.__payload[i].emplaceRef(rhs.__payload[i]);
-                        return;
-                }
-                default: return;
-            }
+            _Payload[i] payload;
+            ubyte[_Payload[i].sizeof] bytes;
         }
 
-        static if (__allHaveConstConstruction)
-        this(return ref scope const typeof(this) rhs) const
+        private ref trustedBytes() inout @trusted
         {
-            this.__bytes = rhs.__bytes;
-            static if (__Types.length > 1)
-                this.__type = rhs.__type;
-            S: switch (__type)
-            {
-                static foreach (i, T; __Types)
-                static if (hasElaborateCopyConstructor!T)
-                {
-                    case i:
-                        this.__mutableTrustedGet!T.emplaceRef!(const T)(rhs.__payload[i]);
-                        return;
-                }
-                default: return;
-            }
+            return *cast(ubyte[_storage.bytes.length]*)&this._storage.bytes;
         }
 
-        static if (__allHaveImmutableConstruction)
-        this(return ref scope immutable typeof(this) rhs) immutable
+        private void _copyCtorSwitch(this This, RhsAlgebraic)(return ref scope RhsAlgebraic rhs)
         {
-            this.__bytes = rhs.__bytes;
-            static if (__Types.length > 1)
-                this.__type = rhs.__type;
-            S: switch (__type)
+            switch (_storage._type)
             {
-                static foreach (i, T; __Types)
-                static if (hasElaborateCopyConstructor!T)
+                static foreach (i, T; _Types)
+                static if (!is(T == typeof(null)) && hasElaborateCopyConstructor!T)
                 {
-                    case i:
-                        this.__mutableTrustedGet!T.emplaceRef!(immutable T)(rhs.__payload[i]);
+                    case i: {
+                        import std.traits: CopyTypeQualifiers;
+                        CopyTypeQualifiers!(RhsAlgebraic, _StorageI!i) storage = { rhs._storage.payload[i] };
+                        trustedBytes[0 .. storage.bytes.length] = storage.bytes;
                         return;
+                    }
                 }
                 default: return;
             }
         }
 
-        static if (__allHaveSemiImmutableConstruction)
-        this(return ref scope const typeof(this) rhs) immutable
+        static if (allSatisfy!(hasInoutConstruction, _Types))
+        this(return ref scope inout Algebraic rhs) inout
         {
-            this.__bytes = rhs.__bytes;
-            static if (__Types.length > 1)
-                this.__type = rhs.__type;
-            S: switch (__type)
+            this._storage.allBytes = rhs._storage.allBytes;
+            _copyCtorSwitch(rhs);
+        }
+        else
+        {
+            static if (allSatisfy!(hasMutableConstruction, _Types))
+            this(return ref scope Algebraic rhs)
             {
-                static foreach (i, T; __Types)
-                static if (hasElaborateCopyConstructor!T)
-                {
-                    case i:
-                        emplaceRef!(immutable T)(this.__mutableTrustedGet!T, rhs.__trustedGet!T);
-                        return;
-                }
-                default: return;
+                this._storage.allBytes = rhs._storage.allBytes;
+                _copyCtorSwitch(rhs);
             }
-        }
 
-        static if (__allCanRemoveConst && __allHaveMutableConstruction)
-        this(return ref scope const typeof(this) rhs)
-        {
-            this.__bytes = rhs.__bytes;
-            static if (__Types.length > 1)
-                this.__type = rhs.__type;
-            S: switch (__type)
+            static if (allSatisfy!(hasConstConstruction, _Types))
+            this(return ref scope const Algebraic rhs) const
             {
-                static foreach (i, T; __Types)
-                static if (hasElaborateCopyConstructor!T)
-                {
-                    case i:
-                        this.__trustedGet!T.emplaceRef(rhs.__trustedGet!T);
-                        return;
-                }
-                default: return;
+                this._storage.allBytes = rhs._storage.allBytes;
+                _copyCtorSwitch(rhs);
             }
-        }
 
-        static if (__allCanRemoveConst && __allHaveMutableConstruction)
-        ref opAssign(return ref scope const typeof(this) rhs) return
-        {
-            static if (__traits(hasMember, this, "__dtor"))
-                __dtor;
-            // don't need emplace here
-            __ctor(rhs);
-            return this;
-        }
+            static if (allSatisfy!(hasImmutableConstruction, _Types))
+            this(return ref scope immutable Algebraic rhs) immutable
+            {
+                this._storage.allBytes = rhs._storage.allBytes;
+                _copyCtorSwitch(rhs);
+            }
 
-        static if (__allCanRemoveConst && __allHaveMutableConstruction)
-        ref opAssign(typeof(this) rhs) return @trusted
-        {
-            moveEmplace(rhs, this);
+            static if (allSatisfy!(hasSemiImmutableConstruction, _Types))
+            this(return ref scope const Algebraic rhs) immutable
+            {
+                this._storage.allBytes = rhs._storage.allBytes;
+                _copyCtorSwitch(rhs);
+            }
+
+            static if (allSatisfy!(hasSemiMutableConstruction, _Types))
+            this(return ref scope const Algebraic rhs)
+            {
+                this._storage.allBytes = rhs._storage.allBytes;
+                _copyCtorSwitch(rhs);
+            }
         }
     }
 
-    static if (is(__Types[0] == typeof(null)))
+    static if (is(_Types[0] == typeof(null)))
     {
+        ///
+        bool opCast(C)() const
+            if (is(C == bool))
+        {
+            return _storage._type != 0;
+        }
         /// Defined if the first type is `typeof(null)`
-        bool isNull() const { return __type == 0; }
+        bool isNull() const { return _storage._type == 0; }
         /// ditto
         void nullify() { this = null; }
 
-        static if (allSatisfy!(isCopyable, __Types[1 .. $]) && __Types.length != 2)
+        static if (allSatisfy!(isCopyable, _Types[1 .. $]) && _Types.length != 2)
         /// ditto
-        auto get()
+        auto get()()
         {
-            if (!__type)
+            if (!_storage._type)
             {
                 version(D_Exceptions)
                     throw variantNullException;
                 else
                     assert(0, variantNullExceptionMsg);
             }
-            static if (__Types.length > 1)
+            static if (_Types.length > 1)
             {
-                Variant!(__Types[1 .. $]) ret;
-                static if (ret.__Types.length > 1)
-                    ret.__type = cast(typeof(ret.__type))(this.__type - 1);
-
-                static if (anySatisfy!(hasElaborateCopyConstructor, __Types))
+                Algebraic!(
+                    _setId,
+                    _TypeSets[0 .. _setId],
+                    TypeSet!(TemplateArgsOf!(_TypeSets[_setId])[1 .. $]),
+                    _TypeSets[_setId + 1 .. $]
+                ) ret;
+                static if (ret._Types.length > 1)
+                    ret._storage._type = cast(typeof(ret._storage._type))(this._storage._type - 1);
+
+                static if (anySatisfy!(hasElaborateCopyConstructor, _Types))
                 {
-                    ret.__bytes = 0;
-                    S: switch (__type)
+                    ret._storage.bytes = 0;
+                    S: switch (_storage._type)
                     {
-                        static foreach (i, T; __Types)
+                        static foreach (i, T; _Types)
                         {
                             static if (hasElaborateCopyConstructor!T)
                             {
                                 case i:
-                                    ret.__trustedGet!T.emplaceRef(this.__trustedGet!T);
+                                    ret._trustedGet!T.emplaceRef(this._trustedGet!T);
                                     break S;
                             }
                         }
                         default:
-                            ret.__bytes = this.__bytes[0 .. ret.__bytes.length];
+                            ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
                     }
                 }
                 else
                 {
-                    ret.__bytes = this.__bytes[0 .. ret.__bytes.length];
+                    ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
                 }
 
                 return ret;
             }
         }
 
-        static if (__Types.length == 2)
-        /// ditto
-        ref inout(__Types[1]) get() inout
+        static if (_Types.length == 2)
         {
-            return get!(__Types[1]);
+            /++
+            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`.
+            +/
+            ref inout(_Types[1]) get() inout
+            {
+                assert(_storage._type, "Called `get' on null Nullable!(" ~ _Types[1].stringof ~ ").");
+                return _trustedGet!(_Types[1]);
+            }
+
+            /// ditto
+            @property inout(_Types[1]) get()(inout(_Types[1]) fallback) inout @safe pure nothrow
+            {
+                return isNull ? fallback : get();
+            }
+
+            /// ditto
+            @property auto get(U)(inout(U) fallback) inout @safe pure nothrow
+            {
+                return isNull ? fallback : get();
+            }
         }
     }
 
-    static if (__Types.length)
+    static if (_Types.length)
     /++
     Returns: zero based type index.
     +/
-    uint __typeId() @nogc nothrow const @property
+    uint _typeId() @nogc nothrow const @property
     {
-        return __type;
+        return _storage._type;
     }
 
-    static if (allSatisfy!(templateOr!(isPOD, hasToHash), __Types))
+    static if (allSatisfy!(templateOr!(isPOD, hasToHash), _Types))
     /++
     +/
     size_t toHash() const
     {
-        static if (allSatisfy!(isPOD, __Types))
+        static if (allSatisfy!(isPOD, _Types))
         {
-            static if (__Types.length == 0 || is(__Types == AliasSeq!(typeof(null))))
+            static if (_Types.length == 0 || is(_Types == AliasSeq!(typeof(null))))
             {
                 return 0;
             }
             else
             static if (this.sizeof <= 16)
             {
-                return hashOf(__bytes, __type);
+                return hashOf(_storage.bytes, _storage._type);
             }
             else
             {
@@ -601,70 +637,100 @@ struct Algebraic(uint _setId, _TypeSets...)
                 else
                     alias UInt = uint;
 
-                static immutable UInt[__Types.length + 1] sizes = [0, staticMap!(Sizeof, __Types)];
-                return hashOf(__bytes[0 .. sizes[__type]], __type);
+                static immutable UInt[_Types.length + 1] sizes = [0, staticMap!(Sizeof, _Types)];
+                return hashOf(_storage.bytes[0 .. sizes[_storage._type]], _storage._type);
             }
         }
         else
-        switch (__type)
+        switch (_storage._type)
         {
-            static foreach (i, T; __Types)
+            static foreach (i, T; _Types)
             {
                 case i:
-                    return hashOf(__trustedGet!T, i);
+                    return hashOf(_trustedGet!T, i);
             }
             default: assert(0);
         }
     }
 
-    static if (allSatisfy!(isEqualityComparable, __Types))
+    static if (allSatisfy!(isEqualityComparable, _Types))
     /++
     +/
-    auto opEquals()(auto ref const typeof(this) rhs) const
+    auto opEquals(const typeof(this) rhs) const
+    {
+        static if (_Types.length == 0)
+        {
+            return true;
+        }
+        else
+        {
+            if (this._storage._type != rhs._storage._type)
+                return false;
+            switch (_storage._type)
+            {
+                static foreach (i, T; _Types)
+                {
+                    case i:
+                        return this._trustedGet!T == rhs._trustedGet!T;
+                }
+                default: assert(0);
+            }
+        }
+    }
+
+    static if (allSatisfy!(isEqualityComparable, _Types))
+    ///ditto
+    auto opEquals(ref const typeof(this) rhs) const
     {
-        static if (__Types.length == 0)
+        static if (_Types.length == 0)
         {
             return true;
         }
         else
         {
-            if (this.__type != rhs.__type)
+            if (this._storage._type != rhs._storage._type)
                 return false;
-            switch (__type)
+            switch (_storage._type)
             {
-                static foreach (i, T; __Types)
+                static foreach (i, T; _Types)
                 {
                     case i:
-                        return this.__trustedGet!T == rhs.__trustedGet!T;
+                        return this._trustedGet!T == rhs._trustedGet!T;
                 }
                 default: assert(0);
             }
         }
     }
 
-    static if (allSatisfy!(isOrderingComparable, __Types))
+    static if (allSatisfy!(isOrderingComparable, _Types))
     /++
     +/
     auto opCmp()(auto ref const typeof(this) rhs) const
     {
-        static if (__Types.length == 0)
+        static if (_Types.length == 0)
         {
             return 0;
         }
         else
         {
-            if (auto d = int(this.__type) - int(rhs.__type))
+            import mir.internal.utility: isFloatingPoint;
+            if (auto d = int(this._storage._type) - int(rhs._storage._type))
                 return d;
-            switch (__type)
+            switch (_storage._type)
             {
-                static foreach (i, T; __Types)
+                static foreach (i, T; _Types)
                 {
                     case i:
+                        static if (__traits(compiles, __cmp(_trustedGet!T, rhs._trustedGet!T)))
+                            return __cmp(_trustedGet!T, rhs._trustedGet!T);
+                        else
                         static if (__traits(hasMember, T, "opCmp"))
-                            return this.__trustedGet!T.opCmp(rhs.__trustedGet!T);
+                            return this._trustedGet!T.opCmp(rhs._trustedGet!T);
                         else
-                            return this.__trustedGet!T < rhs.__trustedGet!T ? -1 :
-                                this.__trustedGet!T > rhs.__trustedGet!T ? +1 : 0;
+                        static if (isFloatingPoint!T)
+                            return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs._trustedGet!T;
+                        return this._trustedGet!T < rhs._trustedGet!T ? -1 :
+                            this._trustedGet!T > rhs._trustedGet!T ? +1 : 0;
                 }
                 default: assert(0);
             }
@@ -674,19 +740,19 @@ struct Algebraic(uint _setId, _TypeSets...)
     ///
     void toString(C, W)(scope ref W w)
     {
-        static if (__Types.length == 0)
+        static if (_Types.length == 0)
         {
             return w.put(cast(immutable C[])"Algebraic");
         }
         else
         {
             import mir.format: print;
-            switch (__type)
+            switch (_storage._type)
             {
-                static foreach (i, T; __Types)
+                static foreach (i, T; _Types)
                 {
                     case i:
-                        print(w, __trustedGet!T);
+                        print(w, _trustedGet!T);
                 }
                 default: assert(0);
             }
@@ -759,7 +825,7 @@ unittest
 
     static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true)
     {
-        ubyte[payloadSize] __payload;
+        ubyte[payloadSize] _payload;
 
     const:
 
@@ -773,11 +839,11 @@ unittest
 
 
     static if (hasToHash)
-        size_t toHash() { return hashOf(__payload); }
+        size_t toHash() { return hashOf(_payload); }
 
     static if (hasOpEquals)
-        auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(__payload.ptr, rhs.__payload.ptr, __payload.length); }
-        auto opCmp(ref const typeof(this) rhs) { return __payload == rhs.__payload; }
+        auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); }
+        auto opCmp(ref const typeof(this) rhs) { return _payload == rhs._payload; }
     }
 
     static foreach (size1; [1, 2, 4, 8, 10, 16, 20])
@@ -801,16 +867,16 @@ unittest
 @safe pure nothrow @nogc
 unittest
 {
-    static struct S { immutable(ubyte)* value; }
-    static struct C { immutable(uint)* value; }
+    static struct S1 { immutable(ubyte)* value; }
+    static struct C1 { immutable(uint)* value; }
 
-    alias V = Variant!(S, C);
-    const V v = S();
+    alias V = Variant!(S1, C1);
+    const V v = S1();
     V w = v;
     w = v;
 
-    immutable f = V(S());
-    auto t = immutable V(S());
+    immutable f = V(S1());
+    auto t = immutable V(S1());
     // auto j = immutable V(t);
     // auto i = const V(t);
 }
@@ -819,21 +885,21 @@ unittest
 @safe pure nothrow @nogc
 unittest
 {
-    static struct S {
+    static struct S2 {
         uint* value;
         this(return ref scope const typeof(this) rhs) {}
-        void opAssign(return ref scope const typeof(this) rhs) {}
+        ref opAssign(typeof(this) rhs) return { return this; }
     }
-    static struct C { const(uint)* value; }
+    static struct C2 { const(uint)* value; }
 
-    alias V = Variant!(S, C);
-    const V v = S();
+    alias V = Variant!(S2, C2);
+    const V v = S2();
     V w = v;
-    w = S();
-    w = cast(const) V.init;
+    w = S2();
     w = v;
+    w = cast(const) V.init;
 
-    const f = V(S());
+    const f = V(S2());
     auto t = const V(f);
 }
 
@@ -841,74 +907,98 @@ unittest
 @safe pure nothrow @nogc
 unittest
 {
-    static struct S {
+    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 C { immutable(uint)* value; }
+    static struct C3 { immutable(uint)* value; }
 
-    S s;
-    S r = s;
+    S3 s;
+    S3 r = s;
     r = s;
-    r = S.init;
+    r = S3.init;
 
-    alias V = Variant!(S, C);
-    V v = S();
+    alias V = Variant!(S3, C3);
+    V v = S3();
     V w = v;
-    w = S();
+    w = S3();
     w = V.init;
     w = v;
 
-    immutable V e = S();
-    auto t = immutable V(S());
+    immutable V e = S3();
+    auto t = immutable V(S3());
     auto j = const V(t);
     auto h = t;
 
-    immutable V l = C();
-    auto g = immutable V(C());
+    immutable V l = C3();
+    auto g = immutable V(C3());
 }
 
+    static struct S {
+        uint* value;
+        // this(return ref scope typeof(this) rhs) {}
+        // this(return ref scope const typeof(this) rhs) const {}
+        this(return ref scope const typeof(this) rhs) @safe pure immutable {}
+
+        immutable(S) bat()  @safe pure
+        {
+            return this;
+        }
+
+        S bat()  @safe pure immutable
+        {
+            return this;
+        }
+    }
+
+    pragma (msg, "hasInoutConstruction!S");
+    pragma (msg, hasInoutConstruction!S);
+    pragma (msg, hasInoutConstruction!S);
+    pragma (msg, hasInoutConstruction!S);
+    pragma (msg, hasInoutConstruction!S);
+
+
+
 @safe pure nothrow @nogc
 unittest
 {
-    static struct S {
+    static struct S4 {
         uint* value;
-        this(return ref scope typeof(this) rhs) {}
-        this(return ref scope const typeof(this) rhs) const {}
-        this(return ref scope const typeof(this) rhs) immutable {}
+        this(return ref scope const typeof(this) rhs) pure immutable {}
     }
-    static struct C { immutable(uint)* value; }
+    static struct C4 { immutable(uint)* value; }
 
-    S s;
-    S r = s;
+
+    S4 s;
+    S4 r = s;
     r = s;
-    r = S.init;
+    r = S4.init;
 
-    alias V = Variant!(S, C);
-    V v = S();
+    alias V = Variant!(S4, C4);
+    V v = S4();
     V w = v;
-    w = S();
+    w = S4();
     w = V.init;
     w = v;
 
     {
-        const V e = S();
+        const V e = S4();
         const k = w;
         auto t = const V(k);
         auto j = immutable V(k);
     }
 
-    immutable V e = S();
+    immutable V e = S4();
     immutable k = w;
-    auto t = immutable V(S());
+    auto t = immutable V(S4());
     auto j = const V(t);
     auto h = t;
 
-    immutable V l = C();
+    immutable V l = C4();
     import core.lifetime;
-    auto g = immutable V(C());
+    auto g = immutable V(C4());
     immutable b = immutable V(s);
 }
 
@@ -917,36 +1007,36 @@ unittest
 {
     import core.lifetime: move;
 
-    static struct S {
+    static struct S5 {
         immutable(uint)* value;
         this(return ref scope typeof(this) rhs) {}
         this(return ref scope const typeof(this) rhs) immutable {}
     }
-    static struct C { immutable(uint)* value; }
+    static struct C5 { immutable(uint)* value; }
 
-    S s;
-    S r = s;
+    S5 s;
+    S5 r = s;
     r = s;
-    r = S.init;
+    r = S5.init;
 
-    alias V = Variant!(S, C);
-    V v = S();
+    alias V = Variant!(S5, C5);
+    V v = S5();
     V w = v;
-    w = S();
+    w = S5();
     w = V.init;
     w = v;
 
-    immutable V e = S();
-    immutable f = V(S());
+    immutable V e = S5();
+    immutable f = V(S5());
     immutable k = w;
-    auto t = immutable V(S());
+    auto t = immutable V(S5());
     auto j = const V(t);
     auto h = t;
 
-    immutable V l = C();
+    immutable V l = C5();
     import core.lifetime;
     immutable n = w.move;
-    auto g = immutable V(C());
+    auto g = immutable V(C5());
     immutable b = immutable V(s);
 }
 
@@ -1040,7 +1130,8 @@ unittest
 }
 
 // Type with copy constructor
-@safe pure nothrow @nogc unittest 
+@safe pure nothrow// @nogc 
+unittest 
 {
     static struct S
     {
@@ -1054,8 +1145,9 @@ unittest
     Variant!S a = S();
     auto b = a;
 
-    assert(a.get!S.n == 0);
-    assert(b.get!S.n == 1);
+    import mir.conv;
+    assert(b.get!S.n == 1, b.get!S.n.to!string);
+    assert(a.get!S.n == 0, a.get!S.n.to!string);
 }
 
 // Empty type set
@@ -1209,7 +1301,7 @@ private template getMemberHandler(string member)
     }
 }
 
-// AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), __Types))
+// AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), _Types))
 
 private template VariantReturnTypes(T...)
 {
@@ -1234,9 +1326,9 @@ template visit(alias visitor, bool exhaustive = true)
 
         // template VariantReturnTypesImpl(T)
         // {
-        //     static if (__traits(compiles, visitor(variant.__trustedGet!T, forward!args)))
+        //     static if (__traits(compiles, visitor(variant._trustedGet!T, forward!args)))
         //     {
-        //         alias Ret = typeof(visitor(variant.__trustedGet!T, forward!args));
+        //         alias Ret = typeof(visitor(variant._trustedGet!T, forward!args));
         //         static if (is(Ret == void))
         //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
         //         else
@@ -1248,7 +1340,7 @@ template visit(alias visitor, bool exhaustive = true)
         //     }
         // }
 
-        // alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V.__Types));
+        // alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V._Types));
 
         // static if (AllReturnTypes.length == 0 || is(AllReturnTypes == AliasSeq!(typeof(null))))
         // {
@@ -1265,9 +1357,9 @@ template visit(alias visitor, bool exhaustive = true)
         //     import std.traits: Unqual, CopyTypeQualifiers;
         //     import std.meta: AliasSeq;
 
-        //     static if (__traits(compiles, visitor(variant.__trustedGet!T, forward!args)))
+        //     static if (__traits(compiles, visitor(variant._trustedGet!T, forward!args)))
         //     {
-        //         alias Ret = typeof(visitor(variant.__trustedGet!T, forward!args));
+        //         alias Ret = typeof(visitor(variant._trustedGet!T, forward!args));
         //         static if (is(Ret == void))
         //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
         //         else
@@ -1286,19 +1378,19 @@ template visit(alias visitor, bool exhaustive = true)
         // }
 
         // import std.meta: staticMap;
-        // alias Types = VariantReturnTypes!(staticMap!(VariantReturnTypesImpl, V.__Types));
+        // alias Types = VariantReturnTypes!(staticMap!(VariantReturnTypesImpl, V._Types));
 
-        // V.__Types;
-        switch (variant.__type)
+        // V._Types;
+        switch (variant._storage._type)
         {
-            static foreach (i, T; V.__Types)
+            static foreach (i, T; V._Types)
             {
                 case i:
-                    static if (__traits(compiles, visitor(variant.__trustedGet!T, forward!args)))
-                        return visitor(variant.__trustedGet!T, forward!args);
+                    static if (exhaustive && !is(T == typeof(null)) || __traits(compiles, visitor(variant._trustedGet!T, forward!args)))
+                        return visitor(variant._trustedGet!T, forward!args);
                     else
                     static if (exhaustive && !is(T == typeof(null)))
-                        static assert(0, V.stringof ~ ": the visitor cann't be caled with __type (first argument) " ~ T.stringof ~ " and additional arguments " ~ Args.stringof);
+                        static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ typeof(variant._trustedGet!T()).stringof ~ (Args.length ? (" and additional arguments " ~ Args.stringof) : ""));
                     else
                     static if (is(T == typeof(null)) && i == 0)
                         assert(0, "Null " ~ V.stringof);
@@ -1352,13 +1444,13 @@ template optionalVisit(alias visitor)
         if (isVariant!V)
     {
         import core.lifetime: forward;
-        switch (variant.__type)
+        switch (variant._storage._type)
         {
-            static foreach (i, T; V.__Types)
-            static if (__traits(compiles, result = visitor(variant.__trustedGet!T, forward!args)))
+            static foreach (i, T; V._Types)
+            static if (__traits(compiles, result = visitor(variant._trustedGet!T, forward!args)))
             {
                 case i:
-                    result = visitor(variant.__trustedGet!T, forward!args);
+                    result = visitor(variant._trustedGet!T, forward!args);
                     return true;
             }
             default:

From dfaab9ddb1425d798d2a391778a8a67c11a60537 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 11 Nov 2020 15:12:17 +0700
Subject: [PATCH 122/573] minor fixes

---
 source/mir/appender.d         | 18 +++++-------------
 source/mir/math/sum.d         |  2 +-
 source/mir/ndslice/topology.d |  2 +-
 3 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index 319e92c8..f7d1b359 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -7,17 +7,7 @@ Authors: Ilya Yaroshenko
 module mir.appender;
 
 // import std.traits: isAssignable, hasElaborateDestructorhasElaborateCopyConstructor, hasElaborateAssign;
-
-package void _mir_destroy(T)(T[] ar)
-{
-    static if (__traits(hasMember, T, "__xdtor"))
-        foreach (ref e; ar)
-            static if (__traits(isSame, T, __traits(parent, e.__xdtor)))
-            {
-                pragma(inline, false)
-                e.__xdtor();
-            }
-}
+import mir.conv: _mir_destroy = xdestroy;
 
 private extern(C) @system nothrow @nogc pure void* memcpy(scope void* s1, scope const void* s2, size_t n);
 
@@ -116,6 +106,8 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     {
         import mir.internal.memory: free;
         data._mir_destroy;
+        version(mir_secure_memory)
+            _currentLength = 0;
         (() @trusted { if (_buffer.ptr) free(cast(void*)_buffer.ptr); })();
     }
 
@@ -150,8 +142,8 @@ struct ScopedBuffer(T, size_t bytes = 4096)
         auto d = prepare(1);
         static if (isMutable!T)
         {
-            import core.lifetime: move;
-            emplaceRef!(Unqual!T)(d[cl], e.move);
+            import core.lifetime: moveEmplace;
+            ()@trusted{moveEmplace(e, d[cl]);}();
         }
         else
         {
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index f7275856..3a4c3c61 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -45,7 +45,7 @@ unittest
 version(mir_test)
 unittest
 {
-    import std.traits : isFloatingPoint;
+    import mir.internal.utility: isFloatingPoint;
     static struct Quaternion(F)
         if (isFloatingPoint!F)
     {
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 2d7884ce..cf190554 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -3057,7 +3057,7 @@ unittest
         double factor;
         this(double f) {factor = f;}
         auto opCall(U)(U x) const {return x.mean + factor; }
-        auto lightConst()() const @property { return Mul(factor); }
+        auto lightConst()() const @property { return Callable(factor); }
     }
 
     auto callable = Callable(0.0);

From 636f285e3f2665a93d110ba973f752c8945c8e39 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 11 Nov 2020 16:44:28 +0700
Subject: [PATCH 123/573] update variant

---
 source/mir/variant.d | 467 ++++++++++++++++++++-----------------------
 1 file changed, 218 insertions(+), 249 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index b3781242..013862ff 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -46,7 +46,6 @@ private enum hasSemiMutableConstruction(T) = __traits(compiles, {static struct S
     static assert(hasInoutConstruction!(immutable(uint)[]));
     static assert(hasInoutConstruction!(typeof(null)));
     static assert(hasInoutConstruction!S);
-    // static assert(!hasInoutConstruction!C2);
 }
 
 private template staticIsSorted(alias cmp, Seq...)
@@ -113,6 +112,7 @@ template TypeSet(T...)
 {
     import std.meta: NoDuplicates, staticSort, staticMap;
     // sort types by siezeof 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))
@@ -178,7 +178,8 @@ struct Algebraic(uint _setId, _TypeSets...)
         isEqualityComparable,
         isOrderingComparable,
         Largest,
-        TemplateArgsOf
+        TemplateArgsOf,
+        Unqual
         ;
 
     private template _ApplyAliasesImpl(uint length, Types...)
@@ -245,134 +246,6 @@ struct Algebraic(uint _setId, _TypeSets...)
 
     private _Storage _storage;
 
-    static foreach (i, T; _Types)
-    {
-        /// Zero cost always nothrow `get` alternative
-        auto ref _trustedGet(E)() @trusted @property return inout nothrow
-            if (is(E == T))
-        {
-            assert (i == _storage._type);
-            static if (is(T == typeof(null)))
-                return null;
-            else
-                return _storage.payload[i];
-        }
-
-        private ref T _mutableTrustedGet(E)() @trusted @property return const nothrow
-            if (is(E == T))
-        {
-            import std.traits: Unqual;
-            assert (i == _storage._type, T.stringof);
-            return *cast(Unqual!(_Types[i])*)&_storage.payload[i];
-        }
-
-        ///
-        auto ref get(E)() @property return inout
-            if (is(E == T))
-        {
-            import mir.utility: _expect;
-            static if (_Types.length > 1)
-            {
-                if (_expect(i != _storage._type, false))
-                {
-                    version(D_Exceptions)
-                        throw variantException;
-                    else
-                        assert(0, variantExceptionMsg);
-                }
-            }
-            return _trustedGet!T;
-        }
-
-        static if (hasMutableConstruction!T)
-        ///
-        this(T value) @trusted
-        {
-            static if (_Types.length > 1)
-                _storage._type = i;
-            static if (!is(T == typeof(null)))
-                moveEmplace(value, _storage.payload[i]);
-        }
-
-        static if (hasSemiImmutableConstruction!T)
-        {
-            ///
-            this(const T value) immutable
-            {
-                static if (_Types.length > 1)
-                    _storage._type = i;
-                emplaceRef!(immutable T)(_mutableTrustedGet!T, value);
-            }
-        }
-        else
-        {
-            static if (hasConstConstruction!T)
-            ///
-            this(const T value) const
-            {
-                static if (_Types.length > 1)
-                    _storage._type = i;
-                emplaceRef!(const T)(_mutableTrustedGet!T, value);
-            }
-
-            static if (hasImmutableConstruction!T)
-            ///
-            this(immutable T value) immutable
-            {
-                static if (_Types.length > 1)
-                    _storage._type = i;
-                emplaceRef!(immutable T)(_mutableTrustedGet!T, value);
-            }
-        }
-
-        static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); }))
-        ///
-        ref opAssign(T value) return @trusted
-        {
-            static if (anySatisfy!(hasElaborateDestructor, _Types))
-                this.__dtor();
-            static if (!is(T == typeof(null)))
-                moveEmplace(value, _storage.payload[i]);
-            _storage.bytes[_Payload[i].sizeof .. $] = 0;
-            static if (_Types.length > 1)
-                _storage._type = i;
-            return this;
-        }
-
-        static if (isEqualityComparable!T)
-        /++
-        +/
-        auto opEquals()(auto ref const T rhs) const
-        {
-            static if (_Types.length > 1)
-                if (_storage._type != i)
-                    return false;
-            return _trustedGet!T == rhs;
-        } 
-
-        static if (isOrderingComparable!T)
-        /++
-        +/
-        auto opCmp()(auto ref const T rhs) const
-        {
-            import mir.internal.utility: isFloatingPoint;
-            static if (_Types.length > 1)
-                if (auto d = int(_storage._type) - int(rhs._storage._type))
-                    return d;
-            static if (__traits(compiles, __cmp(_trustedGet!T, rhs)))
-                return __cmp(_trustedGet!T, rhs);
-            else
-            static if (__traits(hasMember, T, "opCmp"))
-                return _trustedGet!T.opCmp(rhs);
-            else
-            static if (isFloatingPoint!T)
-                return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs;
-            else
-                return _trustedGet!T < rhs ? -1 :
-                    _trustedGet!T > rhs ? +1 : 0;
-        }
-    }
-
     static if (anySatisfy!(hasElaborateDestructor, _Types))
     ~this() @safe
     {
@@ -406,22 +279,21 @@ struct Algebraic(uint _setId, _TypeSets...)
             default: return;
         }
     }
-    // static if (anySatisfy!(hasElaborateAssign, _Types))
-    // pragma(msg, typeof(&opAssign));
-
-    private enum allCopyable = allSatisfy!(isCopyable, _Types);
 
     static if (_Types.length)
     {
         static if (!__traits(compiles, (){ _Types[0] arg; }))
         {
-
             @disable this();
         }
+    }
 
+    private ref trustedBytes() inout @trusted
+    {
+        return *cast(ubyte[_storage.bytes.length]*)&this._storage.bytes;
     }
 
-    static if (!allCopyable)
+    static if (!allSatisfy!(isCopyable, _Types))
         @disable this(this);
     else
     static if (anySatisfy!(hasElaborateCopyConstructor, _Types))
@@ -436,11 +308,6 @@ struct Algebraic(uint _setId, _TypeSets...)
             ubyte[_Payload[i].sizeof] bytes;
         }
 
-        private ref trustedBytes() inout @trusted
-        {
-            return *cast(ubyte[_storage.bytes.length]*)&this._storage.bytes;
-        }
-
         private void _copyCtorSwitch(this This, RhsAlgebraic)(return ref scope RhsAlgebraic rhs)
         {
             switch (_storage._type)
@@ -504,104 +371,6 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (is(_Types[0] == typeof(null)))
-    {
-        ///
-        bool opCast(C)() const
-            if (is(C == bool))
-        {
-            return _storage._type != 0;
-        }
-        /// Defined if the first type is `typeof(null)`
-        bool isNull() const { return _storage._type == 0; }
-        /// ditto
-        void nullify() { this = null; }
-
-        static if (allSatisfy!(isCopyable, _Types[1 .. $]) && _Types.length != 2)
-        /// ditto
-        auto get()()
-        {
-            if (!_storage._type)
-            {
-                version(D_Exceptions)
-                    throw variantNullException;
-                else
-                    assert(0, variantNullExceptionMsg);
-            }
-            static if (_Types.length > 1)
-            {
-                Algebraic!(
-                    _setId,
-                    _TypeSets[0 .. _setId],
-                    TypeSet!(TemplateArgsOf!(_TypeSets[_setId])[1 .. $]),
-                    _TypeSets[_setId + 1 .. $]
-                ) ret;
-                static if (ret._Types.length > 1)
-                    ret._storage._type = cast(typeof(ret._storage._type))(this._storage._type - 1);
-
-                static if (anySatisfy!(hasElaborateCopyConstructor, _Types))
-                {
-                    ret._storage.bytes = 0;
-                    S: switch (_storage._type)
-                    {
-                        static foreach (i, T; _Types)
-                        {
-                            static if (hasElaborateCopyConstructor!T)
-                            {
-                                case i:
-                                    ret._trustedGet!T.emplaceRef(this._trustedGet!T);
-                                    break S;
-                            }
-                        }
-                        default:
-                            ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
-                    }
-                }
-                else
-                {
-                    ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
-                }
-
-                return ret;
-            }
-        }
-
-        static if (_Types.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`.
-            +/
-            ref inout(_Types[1]) get() inout
-            {
-                assert(_storage._type, "Called `get' on null Nullable!(" ~ _Types[1].stringof ~ ").");
-                return _trustedGet!(_Types[1]);
-            }
-
-            /// ditto
-            @property inout(_Types[1]) get()(inout(_Types[1]) fallback) inout @safe pure nothrow
-            {
-                return isNull ? fallback : get();
-            }
-
-            /// ditto
-            @property auto get(U)(inout(U) fallback) inout @safe pure nothrow
-            {
-                return isNull ? fallback : get();
-            }
-        }
-    }
-
     static if (_Types.length)
     /++
     Returns: zero based type index.
@@ -758,6 +527,216 @@ struct Algebraic(uint _setId, _TypeSets...)
             }
         }
     }
+
+    static if (is(_Types[0] == typeof(null)))
+    {
+        ///
+        bool opCast(C)() const
+            if (is(C == bool))
+        {
+            return _storage._type != 0;
+        }
+        /// Defined if the first type is `typeof(null)`
+        bool isNull() const { return _storage._type == 0; }
+        /// ditto
+        void nullify() { this = null; }
+
+        static if (allSatisfy!(isCopyable, _Types[1 .. $]) && _Types.length != 2)
+        /// ditto
+        auto get()()
+        {
+            if (!_storage._type)
+            {
+                version(D_Exceptions)
+                    throw variantNullException;
+                else
+                    assert(0, variantNullExceptionMsg);
+            }
+            static if (_Types.length > 1)
+            {
+                Algebraic!(
+                    _setId,
+                    _TypeSets[0 .. _setId],
+                    TypeSet!(TemplateArgsOf!(_TypeSets[_setId])[1 .. $]),
+                    _TypeSets[_setId + 1 .. $]
+                ) ret;
+                static if (ret._Types.length > 1)
+                    ret._storage._type = cast(typeof(ret._storage._type))(this._storage._type - 1);
+
+                static if (anySatisfy!(hasElaborateCopyConstructor, _Types))
+                {
+                    ret._storage.bytes = 0;
+                    S: switch (_storage._type)
+                    {
+                        static foreach (i, T; _Types)
+                        {
+                            static if (hasElaborateCopyConstructor!T)
+                            {
+                                case i:
+                                    ret._trustedGet!T.emplaceRef(this._trustedGet!T);
+                                    break S;
+                            }
+                        }
+                        default:
+                            ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
+                    }
+                }
+                else
+                {
+                    ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
+                }
+
+                return ret;
+            }
+        }
+
+        static if (_Types.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`.
+            +/
+            ref inout(_Types[1]) get() inout
+            {
+                assert(_storage._type, "Called `get' on null Nullable!(" ~ _Types[1].stringof ~ ").");
+                return _trustedGet!(_Types[1]);
+            }
+
+            /// ditto
+            @property inout(_Types[1]) get()(inout(_Types[1]) fallback) inout @safe pure nothrow
+            {
+                return isNull ? fallback : get();
+            }
+
+            /// ditto
+            @property auto get(U)(inout(U) fallback) inout @safe pure nothrow
+            {
+                return isNull ? fallback : get();
+            }
+        }
+    }
+
+    static foreach (i, T; _Types)
+    {
+        /// Zero cost always nothrow `get` alternative
+        auto ref _trustedGet(E)() @trusted @property return inout nothrow
+            if (is(E == T))
+        {
+            assert (i == _storage._type);
+            static if (is(T == typeof(null)))
+                return null;
+            else
+                return _storage.payload[i];
+        }
+
+        private ref T _mutableTrustedGet(E)() @trusted @property return const nothrow
+            if (is(E == T))
+        {
+            assert (i == _storage._type, T.stringof);
+            return *cast(Unqual!(_Types[i])*)&_storage.payload[i];
+        }
+
+        ///
+        auto ref get(E)() @property return inout
+            if (is(E == T))
+        {
+            import mir.utility: _expect;
+            static if (_Types.length > 1)
+            {
+                if (_expect(i != _storage._type, false))
+                {
+                    version(D_Exceptions)
+                        throw variantException;
+                    else
+                        assert(0, variantExceptionMsg);
+                }
+            }
+            return _trustedGet!T;
+        }
+
+        private void inoutTrustedCtor(ref scope inout T rhs) inout @trusted
+        {
+            trustedBytes[0 .. _Payload[i].sizeof] = *cast(ubyte[_Payload[i].sizeof]*)&rhs;
+            trustedBytes[_Payload[i].sizeof .. $] = 0;
+            static if (_Types.length > 1)
+                *cast(typeof(_Storage._type)*)&_storage._type = i;
+            static if (hasOpPostMove!T)
+                (*cast(Unqual!T*)&(_trustedGet!T())).opPostMove(rhs);
+            static if (hasElaborateDestructor!T)
+                emplaceRef(*cast(Unqual!T*)&rhs);
+        }
+
+        static if (hasMutableConstruction!T)
+        this(T rhs)
+        {
+            inoutTrustedCtor(rhs);
+        }
+
+        static if (hasConstConstruction!T)
+        this(const T rhs) const
+        {
+            inoutTrustedCtor(rhs);
+        }
+
+        static if (hasImmutableConstruction!T)
+        this(immutable T rhs) immutable
+        {
+            inoutTrustedCtor(rhs);
+        }
+
+        static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); }))
+        ///
+        ref opAssign(T rhs) return @trusted
+        {
+            static if (anySatisfy!(hasElaborateDestructor, _Types))
+                this.__dtor();
+            inoutTrustedCtor(rhs);
+            return this;
+        }
+
+        static if (isEqualityComparable!T)
+        /++
+        +/
+        auto opEquals( const T rhs) const
+        {
+            static if (_Types.length > 1)
+                if (_storage._type != i)
+                    return false;
+            return _trustedGet!T == rhs;
+        } 
+
+        static if (isOrderingComparable!T)
+        /++
+        +/
+        auto opCmp( const T rhs) const
+        {
+            import mir.internal.utility: isFloatingPoint;
+            static if (_Types.length > 1)
+                if (auto d = int(_storage._type) - int(i))
+                    return d;
+            static if (__traits(compiles, __cmp(_trustedGet!T, rhs)))
+                return __cmp(_trustedGet!T, rhs);
+            else
+            static if (__traits(hasMember, T, "opCmp"))
+                return _trustedGet!T.opCmp(rhs);
+            else
+            static if (isFloatingPoint!T)
+                return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs;
+            else
+                return _trustedGet!T < rhs ? -1 :
+                    _trustedGet!T > rhs ? +1 : 0;
+        }
+    }
 }
 
 /++
@@ -953,14 +932,6 @@ unittest
         }
     }
 
-    pragma (msg, "hasInoutConstruction!S");
-    pragma (msg, hasInoutConstruction!S);
-    pragma (msg, hasInoutConstruction!S);
-    pragma (msg, hasInoutConstruction!S);
-    pragma (msg, hasInoutConstruction!S);
-
-
-
 @safe pure nothrow @nogc
 unittest
 {
@@ -1301,8 +1272,6 @@ private template getMemberHandler(string member)
     }
 }
 
-// AliasSeq!(ReplaceTypeUnless!(isVariant, This, typeof(this), _Types))
-
 private template VariantReturnTypes(T...)
 {
     import std.meta: NoDuplicates, staticMap;

From 29d9bbff09a84e7bb10474a11bb38d5928f04ad2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 12 Nov 2020 00:14:11 +0700
Subject: [PATCH 124/573] implement optinonal patterns

---
 source/mir/variant.d | 493 ++++++++++++++++++++++---------------------
 1 file changed, 253 insertions(+), 240 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index 013862ff..acf50480 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -87,6 +87,8 @@ private 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;
@@ -152,24 +154,33 @@ template Variants(Sets...)
 
 private static struct _Null()
 {
-    @safe pure nothrow @nogc const:
-        size_t toHash() {return 0;}
-        bool opEquals(_Null) { return true; }
-        int opCmp(_Null) { return 0; }
-
+@safe pure nothrow @nogc const:
+    size_t toHash() {return 0;}
+    bool opEquals(_Null) { return true; }
+    int opCmp(_Null) { return 0; }
     typeof(null) __self() @property { return null; }
     typeof(null) __self(typeof(null)) @property { return null; }
     this(typeof(null)) inout {}
     string toString() { return "null"; }
 }
 
+private static struct _Void()
+{
+ @safe pure nothrow @nogc const:
+    size_t toHash() {return 0;}
+    bool opEquals(_Void) { return true; }
+    int opCmp(_Void) { return 0; }
+    void __self() @property { return; }
+    string toString() { return "void"; }
+}
+
 ///
 struct Algebraic(uint _setId, _TypeSets...)
     if (allSatisfy!(isInstanceOf!TypeSet, _TypeSets))
 {
     import core.lifetime: moveEmplace;
     import mir.conv: emplaceRef;
-    import std.meta: AliasSeq, anySatisfy, allSatisfy, staticMap, templateOr;
+    import std.meta: AliasSeq, anySatisfy, allSatisfy, staticMap, templateOr, Replace;
     import std.typecons: ReplaceTypeUnless;
     import std.traits:
         hasElaborateAssign,
@@ -198,60 +209,57 @@ struct Algebraic(uint _setId, _TypeSets...)
     private enum _isVariant;
 
     ///
-    alias _Types = _ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId]));
+    alias AllowedTypes = _ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId]));
 
-    static if (is(_Types[0] == typeof(null)))
-        private alias _Payload =  AliasSeq!(_Null!(), _Types[1 .. $]);
-    else
-        private alias _Payload = _Types;
+    private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes));
 
     private static union _Storage
     {
         _Payload payload;
 
-        static if (_Types.length == 0 || is(_Types == AliasSeq!(typeof(null))))
+        static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
         {
             ubyte[0] bytes;
-            static if (_Types.length)
-                enum uint _type = 0;
+            static if (AllowedTypes.length)
+                enum uint id = 0;
         }
         else
         struct
         {
             ubyte[Largest!_Payload.sizeof] bytes;
 
-            static if (_Types.length > 1)
+            static if (AllowedTypes.length > 1)
             {
                 import mir.utility: max;
                 enum _alignof = max(staticMap!(Alignof, _Payload));
                 static if ((bytes.length | _alignof) & 1)
-                    ubyte _type;
+                    ubyte id;
                 else
                 static if ((bytes.length | _alignof) & 2)
-                    ushort _type;
+                    ushort id;
                 else
-                    uint _type;
+                    uint id;
             }
             else
             {
-                enum uint _type = 0;
+                enum uint id = 0;
             }
         }
     
-        static if (bytes.length && _Types.length)
-            ubyte[bytes.length + _type.sizeof] allBytes;
+        static if (bytes.length && AllowedTypes.length)
+            ubyte[bytes.length + id.sizeof] allBytes;
         else
             alias allBytes = bytes;
     }
 
     private _Storage _storage;
 
-    static if (anySatisfy!(hasElaborateDestructor, _Types))
+    static if (anySatisfy!(hasElaborateDestructor, AllowedTypes))
     ~this() @safe
     {
-        S: switch (_storage._type)
+        S: switch (_storage.id)
         {
-            static foreach (i, T; _Types)
+            static foreach (i, T; AllowedTypes)
             static if (hasElaborateDestructor!T)
             {
                 case i:
@@ -264,12 +272,12 @@ struct Algebraic(uint _setId, _TypeSets...)
             _storage.allBytes = 0xCC;
     }
 
-    static if (anySatisfy!(hasOpPostMove, _Types))
+    static if (anySatisfy!(hasOpPostMove, AllowedTypes))
     void opPostMove(const ref typeof(this) old)
     {
-        S: switch (_storage._type)
+        S: switch (_storage.id)
         {
-            static foreach (i, T; _Types)
+            static foreach (i, T; AllowedTypes)
             static if (hasOpPostMove!T)
             {
                 case i:
@@ -280,9 +288,9 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (_Types.length)
+    static if (AllowedTypes.length)
     {
-        static if (!__traits(compiles, (){ _Types[0] arg; }))
+        static if (!__traits(compiles, (){ AllowedTypes[0] arg; }))
         {
             @disable this();
         }
@@ -293,13 +301,13 @@ struct Algebraic(uint _setId, _TypeSets...)
         return *cast(ubyte[_storage.bytes.length]*)&this._storage.bytes;
     }
 
-    static if (!allSatisfy!(isCopyable, _Types))
+    static if (!allSatisfy!(isCopyable, AllowedTypes))
         @disable this(this);
     else
-    static if (anySatisfy!(hasElaborateCopyConstructor, _Types))
+    static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
     {
-        // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, _Types);
-        // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, _Types);
+        // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes);
+        // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes);
         // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction;
 
         private static union _StorageI(uint i)
@@ -310,10 +318,10 @@ struct Algebraic(uint _setId, _TypeSets...)
 
         private void _copyCtorSwitch(this This, RhsAlgebraic)(return ref scope RhsAlgebraic rhs)
         {
-            switch (_storage._type)
+            switch (_storage.id)
             {
-                static foreach (i, T; _Types)
-                static if (!is(T == typeof(null)) && hasElaborateCopyConstructor!T)
+                static foreach (i, T; AllowedTypes)
+                static if (!is(T == typeof(null)) && !is(T == void) && hasElaborateCopyConstructor!T)
                 {
                     case i: {
                         import std.traits: CopyTypeQualifiers;
@@ -326,7 +334,7 @@ struct Algebraic(uint _setId, _TypeSets...)
             }
         }
 
-        static if (allSatisfy!(hasInoutConstruction, _Types))
+        static if (allSatisfy!(hasInoutConstruction, AllowedTypes))
         this(return ref scope inout Algebraic rhs) inout
         {
             this._storage.allBytes = rhs._storage.allBytes;
@@ -334,35 +342,35 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
         else
         {
-            static if (allSatisfy!(hasMutableConstruction, _Types))
+            static if (allSatisfy!(hasMutableConstruction, AllowedTypes))
             this(return ref scope Algebraic rhs)
             {
                 this._storage.allBytes = rhs._storage.allBytes;
                 _copyCtorSwitch(rhs);
             }
 
-            static if (allSatisfy!(hasConstConstruction, _Types))
+            static if (allSatisfy!(hasConstConstruction, AllowedTypes))
             this(return ref scope const Algebraic rhs) const
             {
                 this._storage.allBytes = rhs._storage.allBytes;
                 _copyCtorSwitch(rhs);
             }
 
-            static if (allSatisfy!(hasImmutableConstruction, _Types))
+            static if (allSatisfy!(hasImmutableConstruction, AllowedTypes))
             this(return ref scope immutable Algebraic rhs) immutable
             {
                 this._storage.allBytes = rhs._storage.allBytes;
                 _copyCtorSwitch(rhs);
             }
 
-            static if (allSatisfy!(hasSemiImmutableConstruction, _Types))
+            static if (allSatisfy!(hasSemiImmutableConstruction, AllowedTypes))
             this(return ref scope const Algebraic rhs) immutable
             {
                 this._storage.allBytes = rhs._storage.allBytes;
                 _copyCtorSwitch(rhs);
             }
 
-            static if (allSatisfy!(hasSemiMutableConstruction, _Types))
+            static if (allSatisfy!(hasSemiMutableConstruction, AllowedTypes))
             this(return ref scope const Algebraic rhs)
             {
                 this._storage.allBytes = rhs._storage.allBytes;
@@ -371,30 +379,30 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (_Types.length)
+    static if (AllowedTypes.length)
     /++
     Returns: zero based type index.
     +/
-    uint _typeId() @nogc nothrow const @property
+    uint id() @nogc nothrow const @property
     {
-        return _storage._type;
+        return _storage.id;
     }
 
-    static if (allSatisfy!(templateOr!(isPOD, hasToHash), _Types))
+    static if (allSatisfy!(templateOr!(isPOD, hasToHash), AllowedTypes))
     /++
     +/
     size_t toHash() const
     {
-        static if (allSatisfy!(isPOD, _Types))
+        static if (allSatisfy!(isPOD, AllowedTypes))
         {
-            static if (_Types.length == 0 || is(_Types == AliasSeq!(typeof(null))))
+            static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
             {
                 return 0;
             }
             else
             static if (this.sizeof <= 16)
             {
-                return hashOf(_storage.bytes, _storage._type);
+                return hashOf(_storage.bytes, _storage.id);
             }
             else
             {
@@ -406,38 +414,38 @@ struct Algebraic(uint _setId, _TypeSets...)
                 else
                     alias UInt = uint;
 
-                static immutable UInt[_Types.length + 1] sizes = [0, staticMap!(Sizeof, _Types)];
-                return hashOf(_storage.bytes[0 .. sizes[_storage._type]], _storage._type);
+                static immutable UInt[_Payload.length + 1] sizes = [0, staticMap!(Sizeof, _Payload)];
+                return hashOf(_storage.bytes[0 .. sizes[_storage.id]], _storage.id);
             }
         }
         else
-        switch (_storage._type)
+        switch (_storage.id)
         {
-            static foreach (i, T; _Types)
+            static foreach (i, T; AllowedTypes)
             {
                 case i:
-                    return hashOf(_trustedGet!T, i);
+                    return hashOf(_storage.payload[i], i);
             }
             default: assert(0);
         }
     }
 
-    static if (allSatisfy!(isEqualityComparable, _Types))
+    static if (allSatisfy!(isEqualityComparable, AllowedTypes))
     /++
     +/
     auto opEquals(const typeof(this) rhs) const
     {
-        static if (_Types.length == 0)
+        static if (AllowedTypes.length == 0)
         {
             return true;
         }
         else
         {
-            if (this._storage._type != rhs._storage._type)
+            if (this._storage.id != rhs._storage.id)
                 return false;
-            switch (_storage._type)
+            switch (_storage.id)
             {
-                static foreach (i, T; _Types)
+                static foreach (i, T; AllowedTypes)
                 {
                     case i:
                         return this._trustedGet!T == rhs._trustedGet!T;
@@ -447,21 +455,21 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (allSatisfy!(isEqualityComparable, _Types))
+    static if (allSatisfy!(isEqualityComparable, AllowedTypes))
     ///ditto
     auto opEquals(ref const typeof(this) rhs) const
     {
-        static if (_Types.length == 0)
+        static if (AllowedTypes.length == 0)
         {
             return true;
         }
         else
         {
-            if (this._storage._type != rhs._storage._type)
+            if (this._storage.id != rhs._storage.id)
                 return false;
-            switch (_storage._type)
+            switch (_storage.id)
             {
-                static foreach (i, T; _Types)
+                static foreach (i, T; AllowedTypes)
                 {
                     case i:
                         return this._trustedGet!T == rhs._trustedGet!T;
@@ -471,23 +479,23 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (allSatisfy!(isOrderingComparable, _Types))
+    static if (allSatisfy!(isOrderingComparable, AllowedTypes))
     /++
     +/
     auto opCmp()(auto ref const typeof(this) rhs) const
     {
-        static if (_Types.length == 0)
+        static if (AllowedTypes.length == 0)
         {
             return 0;
         }
         else
         {
             import mir.internal.utility: isFloatingPoint;
-            if (auto d = int(this._storage._type) - int(rhs._storage._type))
+            if (auto d = int(this._storage.id) - int(rhs._storage.id))
                 return d;
-            switch (_storage._type)
+            switch (_storage.id)
             {
-                static foreach (i, T; _Types)
+                static foreach (i, T; AllowedTypes)
                 {
                     case i:
                         static if (__traits(compiles, __cmp(_trustedGet!T, rhs._trustedGet!T)))
@@ -509,16 +517,16 @@ struct Algebraic(uint _setId, _TypeSets...)
     ///
     void toString(C, W)(scope ref W w)
     {
-        static if (_Types.length == 0)
+        static if (AllowedTypes.length == 0)
         {
             return w.put(cast(immutable C[])"Algebraic");
         }
         else
         {
             import mir.format: print;
-            switch (_storage._type)
+            switch (_storage.id)
             {
-                static foreach (i, T; _Types)
+                static foreach (i, T; AllowedTypes)
                 {
                     case i:
                         print(w, _trustedGet!T);
@@ -528,31 +536,31 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (is(_Types[0] == typeof(null)))
+    static if (is(AllowedTypes[0] == typeof(null)))
     {
         ///
         bool opCast(C)() const
             if (is(C == bool))
         {
-            return _storage._type != 0;
+            return _storage.id != 0;
         }
         /// Defined if the first type is `typeof(null)`
-        bool isNull() const { return _storage._type == 0; }
+        bool isNull() const { return _storage.id == 0; }
         /// ditto
         void nullify() { this = null; }
 
-        static if (allSatisfy!(isCopyable, _Types[1 .. $]) && _Types.length != 2)
+        static if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2)
         /// ditto
         auto get()()
         {
-            if (!_storage._type)
+            if (!_storage.id)
             {
                 version(D_Exceptions)
                     throw variantNullException;
                 else
                     assert(0, variantNullExceptionMsg);
             }
-            static if (_Types.length > 1)
+            static if (AllowedTypes.length > 1)
             {
                 Algebraic!(
                     _setId,
@@ -560,15 +568,15 @@ struct Algebraic(uint _setId, _TypeSets...)
                     TypeSet!(TemplateArgsOf!(_TypeSets[_setId])[1 .. $]),
                     _TypeSets[_setId + 1 .. $]
                 ) ret;
-                static if (ret._Types.length > 1)
-                    ret._storage._type = cast(typeof(ret._storage._type))(this._storage._type - 1);
+                static if (ret.AllowedTypes.length > 1)
+                    ret._storage.id = cast(typeof(ret._storage.id))(this._storage.id - 1);
 
-                static if (anySatisfy!(hasElaborateCopyConstructor, _Types))
+                static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
                 {
                     ret._storage.bytes = 0;
-                    S: switch (_storage._type)
+                    S: switch (_storage.id)
                     {
-                        static foreach (i, T; _Types)
+                        static foreach (i, T; AllowedTypes)
                         {
                             static if (hasElaborateCopyConstructor!T)
                             {
@@ -590,7 +598,7 @@ struct Algebraic(uint _setId, _TypeSets...)
             }
         }
 
-        static if (_Types.length == 2)
+        static if (AllowedTypes.length == 2)
         {
             /++
             Gets the value if not null. If `this` is in the null state, and the optional
@@ -606,14 +614,14 @@ struct Algebraic(uint _setId, _TypeSets...)
             Returns:
                 The value held internally by this `Nullable`.
             +/
-            ref inout(_Types[1]) get() inout
+            ref inout(AllowedTypes[1]) get() inout
             {
-                assert(_storage._type, "Called `get' on null Nullable!(" ~ _Types[1].stringof ~ ").");
-                return _trustedGet!(_Types[1]);
+                assert(_storage.id, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ").");
+                return _trustedGet!(AllowedTypes[1]);
             }
 
             /// ditto
-            @property inout(_Types[1]) get()(inout(_Types[1]) fallback) inout @safe pure nothrow
+            @property inout(AllowedTypes[1]) get()(inout(AllowedTypes[1]) fallback) inout @safe pure nothrow
             {
                 return isNull ? fallback : get();
             }
@@ -626,24 +634,33 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static foreach (i, T; _Types)
+    static foreach (i, T; AllowedTypes)
     {
         /// Zero cost always nothrow `get` alternative
         auto ref _trustedGet(E)() @trusted @property return inout nothrow
             if (is(E == T))
         {
-            assert (i == _storage._type);
+            assert (i == _storage.id);
             static if (is(T == typeof(null)))
                 return null;
+            else
+            static if (is(T == void))
+                return;
             else
                 return _storage.payload[i];
         }
 
-        private ref T _mutableTrustedGet(E)() @trusted @property return const nothrow
+        private auto ref T _mutableTrustedGet(E)() @trusted @property return const nothrow
             if (is(E == T))
         {
-            assert (i == _storage._type, T.stringof);
-            return *cast(Unqual!(_Types[i])*)&_storage.payload[i];
+            assert (i == _storage.id, T.stringof);
+            static if (is(T == typeof(null)))
+                return null;
+            else
+            static if (is(T == void))
+                return;
+            else
+                return *cast(Unqual!(AllowedTypes[i])*)&_storage.payload[i];
         }
 
         ///
@@ -651,9 +668,9 @@ struct Algebraic(uint _setId, _TypeSets...)
             if (is(E == T))
         {
             import mir.utility: _expect;
-            static if (_Types.length > 1)
+            static if (AllowedTypes.length > 1)
             {
-                if (_expect(i != _storage._type, false))
+                if (_expect(i != _storage.id, false))
                 {
                     version(D_Exceptions)
                         throw variantException;
@@ -664,12 +681,15 @@ struct Algebraic(uint _setId, _TypeSets...)
             return _trustedGet!T;
         }
 
+        static if (!is(T == void))
+        {
+
         private void inoutTrustedCtor(ref scope inout T rhs) inout @trusted
         {
             trustedBytes[0 .. _Payload[i].sizeof] = *cast(ubyte[_Payload[i].sizeof]*)&rhs;
             trustedBytes[_Payload[i].sizeof .. $] = 0;
-            static if (_Types.length > 1)
-                *cast(typeof(_Storage._type)*)&_storage._type = i;
+            static if (AllowedTypes.length > 1)
+                *cast(typeof(_Storage.id)*)&_storage.id = i;
             static if (hasOpPostMove!T)
                 (*cast(Unqual!T*)&(_trustedGet!T())).opPostMove(rhs);
             static if (hasElaborateDestructor!T)
@@ -698,7 +718,7 @@ struct Algebraic(uint _setId, _TypeSets...)
         ///
         ref opAssign(T rhs) return @trusted
         {
-            static if (anySatisfy!(hasElaborateDestructor, _Types))
+            static if (anySatisfy!(hasElaborateDestructor, AllowedTypes))
                 this.__dtor();
             inoutTrustedCtor(rhs);
             return this;
@@ -709,8 +729,8 @@ struct Algebraic(uint _setId, _TypeSets...)
         +/
         auto opEquals( const T rhs) const
         {
-            static if (_Types.length > 1)
-                if (_storage._type != i)
+            static if (AllowedTypes.length > 1)
+                if (_storage.id != i)
                     return false;
             return _trustedGet!T == rhs;
         } 
@@ -721,8 +741,8 @@ struct Algebraic(uint _setId, _TypeSets...)
         auto opCmp( const T rhs) const
         {
             import mir.internal.utility: isFloatingPoint;
-            static if (_Types.length > 1)
-                if (auto d = int(_storage._type) - int(i))
+            static if (AllowedTypes.length > 1)
+                if (auto d = int(_storage.id) - int(i))
                     return d;
             static if (__traits(compiles, __cmp(_trustedGet!T, rhs)))
                 return __cmp(_trustedGet!T, rhs);
@@ -736,6 +756,7 @@ struct Algebraic(uint _setId, _TypeSets...)
                 return _trustedGet!T < rhs ? -1 :
                     _trustedGet!T > rhs ? +1 : 0;
         }
+        }
     }
 }
 
@@ -1038,7 +1059,7 @@ unittest
 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 match(visitors...) = visit!(naryFun!visitors, true);
+alias match(visitors...) = visit!(naryFun!visitors, Exhaustive.compileTime);
 
 ///
 @safe pure @nogc nothrow
@@ -1182,8 +1203,9 @@ unittest
 
 /++
 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
 +/
-alias tryMatch(visitors...) = visit!(naryFun!visitors, false);
+alias tryMatch(visitors...) = visit!(naryFun!visitors, Exhaustive.exception);
 
 ///
 @safe pure @nogc
@@ -1196,11 +1218,42 @@ unittest
     assert(x.tryMatch!((int v) => true));
 }
 
+/++
+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 
++/
+alias optionalMatch(visitors...) = visit!(naryFun!visitors, Exhaustive.nullable);
+
+///
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    static struct S { int a; }
+
+    Variant!(S, double) variant;
+
+    alias optionalMatchInst = optionalMatch!((ref value) => value + 0);
+
+    // do nothing because of variant isn't initialized
+    Nullable!double result = optionalMatchInst(variant);
+    assert(result.isNull);
+
+    variant = S(2);
+    // do nothing because of lambda can't compile
+    result = optionalMatchInst(variant);
+    assert(result == null);
+
+    variant = 3.0;
+    result = optionalMatchInst(variant);
+    assert (result == 3.0);
+}
+
 /++
 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) = visit!(getMemberHandler!member, true);
+alias getMember(string member) = visit!(getMemberHandler!member, Exhaustive.compileTime);
 
 ///
 @safe pure @nogc nothrow
@@ -1214,15 +1267,17 @@ unittest
     V x = S();
     V y = C();
 
+    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);
+    assert(y.getMember!"bar"(2) == 4.0);
 }
 
 /++
 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the member visitor.
-Throws: Exception
+Throws: Exception if member can't be accessed with provided arguments
 +/
-alias tryGetMember(string member) = visit!(getMemberHandler!member, false);
+alias tryGetMember(string member) = visit!(getMemberHandler!member, Exhaustive.exception);
 
 ///
 version(mir_test)
@@ -1237,8 +1292,10 @@ unittest
     V x = S();
     V y = C();
 
+    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);
+    assert(y.tryGetMember!"Bar"(2) == 4.0);
 }
 
 ///
@@ -1255,6 +1312,13 @@ unittest
     assert(y.match!((int v) => false, (float v) => true));
 }
 
+/++
+Behaves as $(LREF match) 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 member can't be called with provided arguments 
++/
+alias optionalGetMember(string member) = visit!(getMemberHandler!member, Exhaustive.nullable);
+
+
 private template getMemberHandler(string member)
 {
     ///
@@ -1279,12 +1343,14 @@ private template VariantReturnTypes(T...)
     alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T));
 }
 
-/++
-Params:
-    visitor = a function name alias
-    exhaustive = if `true` checks at compile time, that the visitor can be called for all types.
-+/
-template visit(alias visitor, bool exhaustive = true)
+enum Exhaustive
+{
+    compileTime,
+    exception,
+    nullable,
+}
+
+private template visit(alias visitor, Exhaustive exhaustive)
 {
     import std.traits: Unqual;
     ///
@@ -1292,89 +1358,88 @@ template visit(alias visitor, bool exhaustive = true)
         if (isVariant!V)
     {
         import core.lifetime: forward;
+        import std.meta: NoDuplicates, staticMap, AliasSeq;
 
-        // template VariantReturnTypesImpl(T)
-        // {
-        //     static if (__traits(compiles, visitor(variant._trustedGet!T, forward!args)))
-        //     {
-        //         alias Ret = typeof(visitor(variant._trustedGet!T, forward!args));
-        //         static if (is(Ret == void))
-        //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
-        //         else
-        //             alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!Ret);
-        //     }
-        //     else
-        //     {
-        //         alias VariantReturnTypesImpl = AliasSeq!();
-        //     }
-        // }
-
-        // alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V._Types));
-
-        // static if (AllReturnTypes.length == 0 || is(AllReturnTypes == AliasSeq!(typeof(null))))
-        // {
-        //     alias ThisReturnType = void;
-        // }
-        // else
-        // static if (AllReturnTypes.length == 2 && (is(AllReturnTypes[0] == typeof(null)) || is(AllReturnTypes[1] == typeof(null))))
-        // {
-        //     alias ThisReturnType = Variant!(typeof(null), OtherType);
-        // }
-
-        // template VariantReturnTypesImpl(T)
-        // {
-        //     import std.traits: Unqual, CopyTypeQualifiers;
-        //     import std.meta: AliasSeq;
-
-        //     static if (__traits(compiles, visitor(variant._trustedGet!T, forward!args)))
-        //     {
-        //         alias Ret = typeof(visitor(variant._trustedGet!T, forward!args));
-        //         static if (is(Ret == void))
-        //             alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
-        //         else
-        //         static if (is(Unqual!Ret == Variant!InnerTypes, InnerTypes))
-        //         {
-        //             alias CopyTypeQualifiers1(Y) = CopyTypeQualifiers(Ret, Y);
-        //             alias VariantReturnTypesImpl = staticMap!(CopyTypeQualifiers1, Ret.Types);
-        //         }
-        //         else
-        //             alias VariantReturnTypesImpl = AliasSeq!Ret;
-        //     }
-        //     else
-        //     {
-        //         alias VariantReturnTypesImpl = AliasSeq!();
-        //     }
-        // }
-
-        // import std.meta: staticMap;
-        // alias Types = VariantReturnTypes!(staticMap!(VariantReturnTypesImpl, V._Types));
-
-        // V._Types;
-        switch (variant._storage._type)
+        template allArgs(T)
         {
-            static foreach (i, T; V._Types)
+            static if (is(T == void))
+                alias allArgs = forward!args;
+            else
+            static if (is(T == typeof(null)))
+                alias allArgs = AliasSeq!(null, forward!args);
+            else
+            {
+                ref fun() @property @trusted
+                {
+                    return variant._trustedGet!T;
+                }
+                alias allArgs = AliasSeq!(fun, forward!args);
+            }
+        }
+
+        template VariantReturnTypesImpl(T)
+        {
+            static if (__traits(compiles, visitor(allArgs!T)))
+                alias VariantReturnTypesImpl = TryRemoveConst!(typeof(visitor(allArgs!T)));
+            else
+                alias VariantReturnTypesImpl = AliasSeq!();
+        }
+
+        static if (exhaustive == Exhaustive.nullable)
+            alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, V.AllowedTypes));
+        else
+            alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V.AllowedTypes));
+
+        switch (variant._storage.id)
+        {
+            static foreach (i, T; V.AllowedTypes)
             {
                 case i:
-                    static if (exhaustive && !is(T == typeof(null)) || __traits(compiles, visitor(variant._trustedGet!T, forward!args)))
-                        return visitor(variant._trustedGet!T, forward!args);
-                    else
-                    static if (exhaustive && !is(T == typeof(null)))
-                        static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ typeof(variant._trustedGet!T()).stringof ~ (Args.length ? (" and additional arguments " ~ Args.stringof) : ""));
+                    static if (__traits(compiles, visitor(allArgs!T)))
+                    {
+                        static if (AllReturnTypes.length == 1)
+                        {
+                            return visitor(allArgs!T);
+                        }
+                        else
+                        {
+                            static if (is(VariantReturnTypesImpl!T == void))
+                            {
+                                visitor(allArgs!T);
+                                return Variant!AllReturnTypes.init;
+                            }
+                            else
+                            {
+                                return Variant!AllReturnTypes(visitor(allArgs!T));
+                            }
+                        }
+                    }
                     else
-                    static if (is(T == typeof(null)) && i == 0)
-                        assert(0, "Null " ~ V.stringof);
+                    static if (exhaustive == Exhaustive.compileTime)
+                    {
+                        static if (is(T == typeof(null)))
+                            assert(0, "Null " ~ V.stringof);
+                        else
+                            static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ typeof(variant._trustedGet!T()).stringof ~ (Args.length ? (" and additional arguments " ~ Args.stringof) : ""));
+                    }
                     else
-                    version(D_Exceptions)
-                        throw variantMemberException;
+                    static if (exhaustive == Exhaustive.nullable)
+                    {
+                        return Variant!AllReturnTypes(null);
+                    }
                     else
-                        assert(0, variantMemberExceptionMsg);
+                    {
+                        version(D_Exceptions)
+                            throw variantMemberException;
+                        else
+                            assert(0, variantMemberExceptionMsg);
+                    }
             }
             default: assert(0);
         }
     }
 }
 
-///
 version(mir_test)
 @safe pure @nogc
 unittest
@@ -1383,7 +1448,7 @@ unittest
 
     Variant!(S, double) variant;
     variant = 1.0;
-    variant.visit!((ref value, b) => value += b, false)(2);
+    variant.tryMatch!((ref value, b) => value += b)(2);
     assert (variant.get!double == 3);
 
     alias fun = (ref value) {
@@ -1393,62 +1458,10 @@ unittest
            value += 2;
     };
 
-    variant.visit!fun;
+    variant.tryMatch!fun;
     assert (variant.get!double == 5);
 
     variant = S(4);
-    variant.visit!fun;
+    variant.tryMatch!fun;
     assert (variant.get!S.a == 6);
 }
-
-/++
-Params:
-    visitor = a function name alias
-+/
-template optionalVisit(alias visitor)
-{
-    import std.traits: Unqual;
-    ///
-    bool optionalVisit(Result, V, Args...)(ref Result result, auto ref V variant, auto ref Args args) @property
-        if (isVariant!V)
-    {
-        import core.lifetime: forward;
-        switch (variant._storage._type)
-        {
-            static foreach (i, T; V._Types)
-            static if (__traits(compiles, result = visitor(variant._trustedGet!T, forward!args)))
-            {
-                case i:
-                    result = visitor(variant._trustedGet!T, forward!args);
-                    return true;
-            }
-            default:
-                return false;
-        }
-    }
-}
-
-///
-version(mir_test)
-@safe pure @nogc nothrow
-unittest
-{
-    static struct S { int a; }
-
-    Variant!(S, double) variant;
-
-    double result = 100;
-
-    // do nothing because of variant isn't initialized
-    optionalVisit!((ref value) => value)(result, variant);
-    assert(result == 100);
-
-    variant = S(2);
-    // do nothing because of lambda can't compile
-    optionalVisit!((ref value) => value)(result, variant);
-    assert(result == 100);
-
-    variant = 3.0;
-    optionalVisit!((ref value) => value)(result, variant);
-    assert (result == 3);
-}

From 972ffb5848d6078f0ffd3587744f46a37e801229 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 12 Nov 2020 23:38:43 +0700
Subject: [PATCH 125/573]  ensure cyclic referecing for variant

---
 source/mir/variant.d | 103 +++++++++++++++++--------------------------
 1 file changed, 41 insertions(+), 62 deletions(-)

diff --git a/source/mir/variant.d b/source/mir/variant.d
index acf50480..8b0d877b 100644
--- a/source/mir/variant.d
+++ b/source/mir/variant.d
@@ -1,5 +1,5 @@
 /++
-This module implements a generic variant type.
+This module implements a generic variant and nullable types.
 +/
 module mir.variant;
 
@@ -104,9 +104,9 @@ private struct VariantSet(Sets...);
 /++
 Dummy type for $(LREF Variant) self-referencing.
 +/
-struct SetAlias(uint id);
+struct SetAlias(uint id) {}
 ///ditto
-alias This = SetAlias!0;
+struct This {}
 
 /++
 +/
@@ -140,6 +140,7 @@ unittest
 
 
 /++
+Cyclic referencing
 +/
 template Variants(Sets...)
     if (allSatisfy!(isInstanceOf!TypeSet, Sets))
@@ -152,6 +153,22 @@ template Variants(Sets...)
     alias Variants = staticMap!(TypeSetsInst, Iota!(Sets.length));
 }
 
+/// 
+version(all) unittest
+{
+    import std.meta: AliasSeq;
+
+    alias V = Variants!(
+        TypeSet!(string, long, SetAlias!1*), // string, long, and V[1][]
+        TypeSet!(SetAlias!0[], int), // int and V[0]*
+    );
+
+    alias A = V[0];
+    alias B = V[1];
+
+    A arr = new B([A("hey"), A(100)]);
+}
+
 private static struct _Null()
 {
 @safe pure nothrow @nogc const:
@@ -178,10 +195,11 @@ private static struct _Void()
 struct Algebraic(uint _setId, _TypeSets...)
     if (allSatisfy!(isInstanceOf!TypeSet, _TypeSets))
 {
+    private enum _isVariant;
+
     import core.lifetime: moveEmplace;
     import mir.conv: emplaceRef;
     import std.meta: AliasSeq, anySatisfy, allSatisfy, staticMap, templateOr, Replace;
-    import std.typecons: ReplaceTypeUnless;
     import std.traits:
         hasElaborateAssign,
         hasElaborateCopyConstructor,
@@ -193,23 +211,21 @@ struct Algebraic(uint _setId, _TypeSets...)
         Unqual
         ;
 
-    private template _ApplyAliasesImpl(uint length, Types...)
+    private template _ApplyAliasesImpl(int length, Types...)
     {
+        import std.typecons: ReplaceTypeUnless;
         static if (length == 0)
-            alias _ApplyAliasesImpl = Types;
+            alias _ApplyAliasesImpl = ReplaceTypeUnless!(isVariant, This, Algebraic!(_setId, _TypeSets), Types);
         else
         {
             enum next  = length - 1;
-            import std.typecons: ReplaceTypeUnless;
             alias _ApplyAliasesImpl = _ApplyAliasesImpl!(next,
                 ReplaceTypeUnless!(isVariant, SetAlias!next, Algebraic!(next, _TypeSets), Types));
         }
     }
 
-    private enum _isVariant;
-
     ///
-    alias AllowedTypes = _ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId]));
+    alias AllowedTypes = AliasSeq!(_ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId])));
 
     private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes));
 
@@ -379,16 +395,6 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (AllowedTypes.length)
-    /++
-    Returns: zero based type index.
-    +/
-    uint id() @nogc nothrow const @property
-    {
-        return _storage.id;
-    }
-
-    static if (allSatisfy!(templateOr!(isPOD, hasToHash), AllowedTypes))
     /++
     +/
     size_t toHash() const
@@ -430,7 +436,6 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (allSatisfy!(isEqualityComparable, AllowedTypes))
     /++
     +/
     auto opEquals(const typeof(this) rhs) const
@@ -455,7 +460,6 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (allSatisfy!(isEqualityComparable, AllowedTypes))
     ///ditto
     auto opEquals(ref const typeof(this) rhs) const
     {
@@ -479,7 +483,6 @@ struct Algebraic(uint _setId, _TypeSets...)
         }
     }
 
-    static if (allSatisfy!(isOrderingComparable, AllowedTypes))
     /++
     +/
     auto opCmp()(auto ref const typeof(this) rhs) const
@@ -501,13 +504,14 @@ struct Algebraic(uint _setId, _TypeSets...)
                         static if (__traits(compiles, __cmp(_trustedGet!T, rhs._trustedGet!T)))
                             return __cmp(_trustedGet!T, rhs._trustedGet!T);
                         else
-                        static if (__traits(hasMember, T, "opCmp"))
+                        static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U))
                             return this._trustedGet!T.opCmp(rhs._trustedGet!T);
                         else
-                        static if (isFloatingPoint!T)
-                            return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs._trustedGet!T;
-                        return this._trustedGet!T < rhs._trustedGet!T ? -1 :
-                            this._trustedGet!T > rhs._trustedGet!T ? +1 : 0;
+                        // static if (isFloatingPoint!T)
+                        //     return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs._trustedGet!T;
+                        // else
+                            return this._trustedGet!T < rhs._trustedGet!T ? -1 :
+                                this._trustedGet!T > rhs._trustedGet!T ? +1 : 0;
                 }
                 default: assert(0);
             }
@@ -696,19 +700,16 @@ struct Algebraic(uint _setId, _TypeSets...)
                 emplaceRef(*cast(Unqual!T*)&rhs);
         }
 
-        static if (hasMutableConstruction!T)
         this(T rhs)
         {
             inoutTrustedCtor(rhs);
         }
 
-        static if (hasConstConstruction!T)
         this(const T rhs) const
         {
             inoutTrustedCtor(rhs);
         }
 
-        static if (hasImmutableConstruction!T)
         this(immutable T rhs) immutable
         {
             inoutTrustedCtor(rhs);
@@ -720,14 +721,13 @@ struct Algebraic(uint _setId, _TypeSets...)
         {
             static if (anySatisfy!(hasElaborateDestructor, AllowedTypes))
                 this.__dtor();
-            inoutTrustedCtor(rhs);
+            __ctor(rhs);
             return this;
         }
 
-        static if (isEqualityComparable!T)
         /++
         +/
-        auto opEquals( const T rhs) const
+        auto opEquals()(auto ref const T rhs) const
         {
             static if (AllowedTypes.length > 1)
                 if (_storage.id != i)
@@ -735,10 +735,9 @@ struct Algebraic(uint _setId, _TypeSets...)
             return _trustedGet!T == rhs;
         } 
 
-        static if (isOrderingComparable!T)
         /++
         +/
-        auto opCmp( const T rhs) const
+        auto opCmp()(auto ref const T rhs) const
         {
             import mir.internal.utility: isFloatingPoint;
             static if (AllowedTypes.length > 1)
@@ -747,7 +746,7 @@ struct Algebraic(uint _setId, _TypeSets...)
             static if (__traits(compiles, __cmp(_trustedGet!T, rhs)))
                 return __cmp(_trustedGet!T, rhs);
             else
-            static if (__traits(hasMember, T, "opCmp"))
+            static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U))
                 return _trustedGet!T.opCmp(rhs);
             else
             static if (isFloatingPoint!T)
@@ -787,15 +786,15 @@ 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 unittest
+@safe pure version(all) unittest
 {
-    import std.typecons : Tuple, tuple;
+    import mir.functional: Tuple = RefTuple;
 
-    // A tree is either a leaf or a branch of two other trees
+    // 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 = tuple(new Tree!int(42), new Tree!int(43));
+    Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43));
     Tree!int* right = tree.get!Leafs[1];
     assert(*right == 43);
 }
@@ -816,8 +815,7 @@ be arbitrarily complex.
 }
 
 /// Test for opCmp, opEqual, toHash
-// version(mir_test)
-version(none)
+version(mir_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -857,8 +855,6 @@ unittest
                 double,
                 C!(size1, isPOD, hasToHash, hasOpEquals),
                 C!(size2, isPOD, hasToHash, hasOpEquals));
-        static assert (__traits(hasMember, T, "toHash") == isPOD || hasToHash);
-        static assert (__traits(compiles, T.init == T.init) == isPOD || hasOpEquals);
         static assert (__traits(compiles, T.init <= T.init));
     }}
 }
@@ -936,23 +932,6 @@ unittest
     auto g = immutable V(C3());
 }
 
-    static struct S {
-        uint* value;
-        // this(return ref scope typeof(this) rhs) {}
-        // this(return ref scope const typeof(this) rhs) const {}
-        this(return ref scope const typeof(this) rhs) @safe pure immutable {}
-
-        immutable(S) bat()  @safe pure
-        {
-            return this;
-        }
-
-        S bat()  @safe pure immutable
-        {
-            return this;
-        }
-    }
-
 @safe pure nothrow @nogc
 unittest
 {

From 5b4f30b5322db7b8d70fba8b19b6cc2b88a083ed Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 13 Nov 2020 16:57:33 +0700
Subject: [PATCH 126/573] rework rc, move variant to mir.algebraic of mir-core,
 rework small array and small string

---
 .gitignore                       |    1 +
 index.d                          |    1 -
 meson.build                      |    1 -
 source/mir/algorithm/iteration.d |    6 +-
 source/mir/date.d                |    2 +-
 source/mir/format.d              |   14 +-
 source/mir/ndslice/iterator.d    |  125 ++-
 source/mir/ndslice/slice.d       |   33 +-
 source/mir/rc/array.d            |   49 +-
 source/mir/rc/context.d          |   25 +-
 source/mir/rc/ptr.d              |   36 +-
 source/mir/rc/slim_ptr.d         |   39 +-
 source/mir/small_array.d         |   62 +-
 source/mir/small_string.d        |  105 +--
 source/mir/type_info.d           |   18 +-
 source/mir/variant.d             | 1446 ------------------------------
 16 files changed, 325 insertions(+), 1638 deletions(-)
 delete mode 100644 source/mir/variant.d

diff --git a/.gitignore b/.gitignore
index 6ed6bbb5..4c9b2661 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,4 @@ docgen
 _build_dir_
 .VSCodeCounter
 builddir/
+mir-algorithm-test-secure
diff --git a/index.d b/index.d
index 9abb5483..0a225c6e 100644
--- a/index.d
+++ b/index.d
@@ -11,7 +11,6 @@ $(BOOKTABLE ,
         $(TH Description)
     )
     $(LEADINGROW Accessories)
-    $(TR $(TDNW $(MREF mir,variant)) $(TD Variant Type (aka Algebraic Type) with clever member access))
     $(TR $(TDNW $(MREF mir,exception)) $(TD @nogc MirException with formatting))
     $(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities))
     $(TR $(TDNW $(MREF mir,parse)) $(TD @nogc Parsing Utilities))
diff --git a/meson.build b/meson.build
index e69af3b4..eca0b6ce 100644
--- a/meson.build
+++ b/meson.build
@@ -69,7 +69,6 @@ sources_list = [
     'mir/small_array',
     'mir/small_string',
     'mir/type_info',
-    'mir/variant',
 ]
 
 sources = []
diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index fa404178..2e6760f1 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -2559,7 +2559,7 @@ template cmp(alias pred = "a < b")
         Positive value if the first differing element of `sl2` is less than the corresponding
         element of `sl1` according to `pred`.
     +/
-    ptrdiff_t cmp(A, B)
+    auto cmp(A, B)
         (scope A sl1, scope B sl2)
         if (DimensionCount!A == DimensionCount!B)
     {
@@ -2571,8 +2571,8 @@ template cmp(alias pred = "a < b")
             auto sh1 = sl1.shape;
             auto sh2 = sl2.shape;
             foreach (i; Iota!(DimensionCount!A))
-                if (ptrdiff_t ret = sh1[i] - sh2[i])
-                    return ret;
+                if (sh1[i] != sh2[i])
+                    return sh1[i] > sh2[i] ? 1 : -1;
             return 0;
         }
         if (b)
diff --git a/source/mir/date.d b/source/mir/date.d
index 15395c15..991c6a46 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -1174,7 +1174,7 @@ public:
             fun(w);
         catch (Exception e)
             assert(0, __traits(identifier, fun) ~ " threw.");
-        return w.asArray.idup;
+        return w[].idup;
     }
 
     version(D_BetterC){} else
diff --git a/source/mir/format.d b/source/mir/format.d
index a851942f..4bf32ee9 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -23,10 +23,20 @@ unittest
     assert(true.to!string == "true");
 
     assert((cast(S)"str")[] == "str");
+}
+
+/// `mir.conv: to` extension.
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.conv: to;
+    import mir.small_string;
+    alias S = SmallString!32;
 
     auto str = S("str");
-    assert(str.to!(const(char)[]) == "str"); // scope result
-    assert(str.to!(char[]) == "str"); // scope result
+    assert(str.to!(const(char)[]) == "str"); // GC allocated result
+    assert(str.to!(char[]) == "str"); // GC allocated result
 }
 
 /// ditto
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index cf87c45d..76782b38 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -545,7 +545,8 @@ struct ZipIterator(Iterators...)
     {
         foreach(i, ref val; value.expand)
         {
-            _iterators[i][index] = val;
+            import mir.functional: unref;
+            _iterators[i][index] = unref(val);
         }
         return opIndex(index);
     }
@@ -839,7 +840,66 @@ struct VmapIterator(Iterator, Fun)
         return VmapIterator!(LightImmutableOf!Iterator, LightImmutableOf!Fun)(.lightImmutable(_iterator), .lightImmutable(_fun));
     }
 
-    mixin(map_primitives);
+    import mir.functional: RefTuple, unref;
+
+    auto ref opUnary(string op : "*")()
+    {
+        static if (is(typeof(*_iterator) : RefTuple!T, T...))
+        {
+            auto t = *_iterator;
+            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+        }
+        else
+            return _fun(*_iterator);
+    }
+
+    auto ref opIndex(ptrdiff_t index) scope
+    {
+        static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+        {
+            auto t = _iterator[index];
+            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+        }
+        else
+            return _fun(_iterator[index]);
+    }
+
+    static if (!__traits(compiles, &opIndex(ptrdiff_t.init)))
+    {
+        auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope
+        {
+            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            {
+                auto t = _iterator[index];
+                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value");
+            }
+            else
+                return _fun(_iterator[index]) = value;
+        }
+
+        auto ref opIndexUnary(string op)(ptrdiff_t index)
+        {
+            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            {
+                auto t = _iterator[index];
+                return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            }
+            else
+                return mixin(op ~ "_fun(_iterator[index])");
+        }
+
+        auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index)
+        {
+            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            {
+                auto t = _iterator[index];
+                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value");
+            }
+            else
+                return mixin("_fun(_iterator[index])" ~ op ~ "= value");
+        }
+    }
+    
     mixin(std_ops);
 
     static if (hasZeroShiftFieldMember!Iterator)
@@ -880,7 +940,66 @@ struct MapIterator(Iterator, alias _fun)
     ///
     static alias __map(alias fun1) = MapIterator__map!(Iterator, _fun, pipe!(_fun, fun1));
 
-    mixin(map_primitives);
+    import mir.functional: RefTuple, unref;
+
+    auto ref opUnary(string op : "*")()
+    {
+        static if (is(typeof(*_iterator) : RefTuple!T, T...))
+        {
+            auto t = *_iterator;
+            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+        }
+        else
+            return _fun(*_iterator);
+    }
+
+    auto ref opIndex(ptrdiff_t index) scope
+    {
+        static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+        {
+            auto t = _iterator[index];
+            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+        }
+        else
+            return _fun(_iterator[index]);
+    }
+
+    static if (!__traits(compiles, &opIndex(ptrdiff_t.init)))
+    {
+        auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope
+        {
+            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            {
+                auto t = _iterator[index];
+                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value");
+            }
+            else
+                return _fun(_iterator[index]) = value;
+        }
+
+        auto ref opIndexUnary(string op)(ptrdiff_t index)
+        {
+            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            {
+                auto t = _iterator[index];
+                return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            }
+            else
+                return mixin(op ~ "_fun(_iterator[index])");
+        }
+
+        auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index)
+        {
+            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            {
+                auto t = _iterator[index];
+                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value");
+            }
+            else
+                return mixin("_fun(_iterator[index])" ~ op ~ "= value");
+        }
+    }
+    
     mixin(std_ops);
 
     static if (hasZeroShiftFieldMember!Iterator)
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index a61c2516..64154635 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -441,17 +441,17 @@ struct CoordinateValue(T, size_t N = 1)
     T value;
 
     ///
-    sizediff_t opCmp()(scope auto ref const typeof(this) rht) const
+    int opCmp()(scope auto ref const typeof(this) rht) const
     {
         return cmpCoo(this.index, rht.index);
     }
 }
 
-private sizediff_t cmpCoo(size_t N)(scope const auto ref size_t[N] a, scope const auto ref size_t[N] b)
+private int cmpCoo(size_t N)(scope const auto ref size_t[N] a, scope const auto ref size_t[N] b)
 {
     foreach (i; Iota!(0, N))
         if (auto d = a[i] - b[i])
-            return d;
+            return d > 0 ? 1 : -1;
     return 0;
 }
 
@@ -1994,33 +1994,6 @@ public:
         assert(sl.selectBack!1(2) == sl[0 .. $, $ - 2 .. $]);
     }
 
-    /++
-    Overloading `==` and `!=`
-    +/
-    bool opEquals(scope const ref typeof(this) rslice) @trusted scope const
-    {
-        static if (__traits(isPOD, typeof(this)))
-        {
-            if (this._lengths != rslice._lengths)
-                return false;
-            if (this.strides == rslice.strides && this._iterator == rslice._iterator)
-                return true;
-        }
-
-        import mir.algorithm.iteration : equal;
-        static if (__traits(compiles, this.lightScope))
-        {
-            auto slice1 = this.lightScope;
-            auto slice2 = rslice.lightScope;
-            foreach(i; Iota!(min(slice1.L, slice2.L)))
-                if(slice1.label!i != slice2.label!i)
-                    return false;
-            return equal(slice1.values, slice2.values);
-        }
-        else
-            return equal(*cast(This*)&this, *cast(This*)&rslice);
-    }
-
     ///ditto
     bool opEquals(IteratorR, SliceKind rkind)(auto ref const Slice!(IteratorR, N, rkind) rslice) @trusted scope const
     {
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index b088e95d..cce6ffd4 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -20,18 +20,16 @@ version (D_Exceptions)
 /++
 Thread safe reference counting array.
 
-`__xdtor` if any is used to destruct objects.
-
 The implementation never adds roots into the GC.
 +/
 struct mir_rcarray(T)
 {
     ///
     package T* _payload;
-    package ref inout(mir_rc_context) context() inout scope return pure nothrow @nogc @trusted @property
+    package ref mir_rc_context context() inout scope return pure nothrow @nogc @trusted @property
     {
         assert(_payload);
-        return (cast(inout(mir_rc_context)*)_payload)[-1];
+        return (cast(mir_rc_context*)_payload)[-1];
     }
     package void _reset() { _payload = null; }
 
@@ -60,27 +58,17 @@ struct mir_rcarray(T)
     ///
     ~this() nothrow
     {
-        static if (hasDestructor!T)
+        static if (hasElaborateDestructor!T || hasDestructor!T)
         {
             if (false) // break @safe and pure attributes
             {
                 Unqual!T* object;
-                (*object).__xdtor();
+                (*object).__xdtor;
             }
         }
         if (this)
         {
             (() @trusted { mir_rc_decrease_counter(context); })();
-            debug _reset;
-        }
-    }
-
-    ///
-    this(this) scope @trusted pure nothrow @nogc
-    {
-        if (this)
-        {
-            mir_rc_increase_counter(context);
         }
     }
 
@@ -190,6 +178,35 @@ struct mir_rcarray(T)
     else
         package alias V = T;
 
+    this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._payload = rhs._payload;
+            mir_rc_increase_counter(context);
+        }
+    }
+
+    ///
+    ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc
+    {
+        this = typeof(this).init;
+    }
+
+    ///
+    ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc
+    {
+        this.proxySwap(rhs);
+        return this;
+    }
+
+    ///
+    ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc
+        if (isImplicitlyConvertible!(Q*, T*))
+    {
+        this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
+        return this;
+    }
 }
 
 /// ditto
diff --git a/source/mir/rc/context.d b/source/mir/rc/context.d
index 7b036530..69ab8dee 100644
--- a/source/mir/rc/context.d
+++ b/source/mir/rc/context.d
@@ -59,6 +59,10 @@ void mir_rc_decrease_counter(ref mir_rc_context context) @system nothrow @nogc p
                 mir_rc_delete(context);
             }
         }
+        // else
+        // {
+        //     assert(0);
+        // }
     }
 }
 
@@ -171,27 +175,6 @@ mir_rc_context* mir_rc_create(
 ///
 package mixin template CommonRCImpl()
 {
-    ///
-    ref opAssign(typeof(null)) scope return
-    {
-        this = typeof(this).init;
-    }
-
-    ///
-    ref opAssign(return typeof(this) rhs) scope return @trusted
-    {
-        this.proxySwap(rhs);
-        return this;
-    }
-
-    ///
-    ref opAssign(Q)(return ThisTemplate!Q rhs) scope return pure nothrow @nogc @trusted
-        if (isImplicitlyConvertible!(Q*, T*))
-    {
-        this.proxySwap(*cast(typeof(this)*)&rhs);
-        return this;
-    }
-
     ///
     ThisTemplate!(const T) lightConst()() scope return const @nogc nothrow @trusted @property
     { return *cast(typeof(return)*) &this; }
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index 893fb121..ebad23d4 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -39,9 +39,9 @@ struct mir_rcptr(T)
         package T* _value;
     package mir_rc_context* _context;
 
-    package ref inout(mir_rc_context) context() inout scope return @trusted @property
+    package ref mir_rc_context context() inout scope return @trusted @property
     {
-        return *_context;
+        return *cast(mir_rc_context*)_context;
     }
 
     package void _reset()
@@ -101,12 +101,12 @@ struct mir_rcptr(T)
     ///
     ~this() nothrow
     {
-        static if (hasDestructor!T)
+        static if (hasElaborateDestructor!T || hasDestructor!T)
         {
             if (false) // break @safe and pure attributes
             {
                 Unqual!T* object;
-                (*object).__xdtor();
+                (*object).__xdtor;
             }
         }
         if (this)
@@ -116,14 +116,36 @@ struct mir_rcptr(T)
         }
     }
 
-    ///
-    this(this) scope @trusted pure nothrow @nogc
+    this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
-        if (this)
+        if (rhs)
         {
+            this._value = rhs._value;
+            this._context = rhs._context;
             mir_rc_increase_counter(context);
         }
     }
+
+    ///
+    ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc
+    {
+        this = typeof(this).init;
+    }
+
+    ///
+    ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc
+    {
+        this.proxySwap(rhs);
+        return this;
+    }
+
+    ///
+    ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc
+        if (isImplicitlyConvertible!(Q*, T*))
+    {
+        this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
+        return this;
+    }
 }
 
 ///
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index 5d3a4cbc..0ebdaf23 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -23,8 +23,6 @@ Thread safe reference counting array.
 
 This implementation does not support polymorphism.
 
-`__xdtor` if any is used to destruct objects.
-
 The implementation never adds roots into the GC.
 +/
 struct mir_slim_rcptr(T)
@@ -38,10 +36,10 @@ struct mir_slim_rcptr(T)
     else
         package T* _value;
 
-    package ref inout(mir_rc_context) context() inout scope return pure nothrow @nogc @trusted @property
+    package ref mir_rc_context context() inout scope return pure nothrow @nogc @trusted @property
     {
         assert(_value);
-        return (cast(inout(mir_rc_context)*)_value)[-1];
+        return (cast(mir_rc_context*)_value)[-1];
     }
 
     package void _reset()
@@ -97,12 +95,12 @@ struct mir_slim_rcptr(T)
     ///
     ~this() nothrow
     {
-        static if (hasDestructor!T)
+        static if (hasElaborateDestructor!T || hasDestructor!T)
         {
             if (false) // break @safe and pure attributes
             {
                 Unqual!T* object;
-                (*object).__xdtor();
+                (*object).__xdtor;
             }
         }
         if (this)
@@ -112,14 +110,35 @@ struct mir_slim_rcptr(T)
         }
     }
 
-    ///
-    this(this) scope @trusted pure nothrow @nogc
+    this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
-        if (this)
+        if (rhs)
         {
+            this._value = rhs._value;
             mir_rc_increase_counter(context);
         }
     }
+
+    ///
+    ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc
+    {
+        this = typeof(this).init;
+    }
+
+    ///
+    ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc
+    {
+        this.proxySwap(rhs);
+        return this;
+    }
+
+    ///
+    ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc
+        if (isImplicitlyConvertible!(Q*, T*))
+    {
+        this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
+        return this;
+    }
 }
 
 ///
@@ -144,7 +163,7 @@ template createSlimRC(T)
             }
             _value = cast(typeof(_value))(context + 1);
         } ();
-        import core.lifetime: forward;
+        import mir.functional: forward;
         import mir.conv: emplace;
         cast(void) emplace!T(ret._value, forward!args);
         return ret;
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index f0cf62b1..2c272c42 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -40,7 +40,7 @@ template SmallArray(T, uint maxLength)
 
         void serialize(S)(ref S serializer)
         {
-            serializer.putValue(asArray);
+            serializer.putValue(opIndex);
         }
 
         static if (isImplicitlyConvertible!(const T, T))
@@ -236,9 +236,6 @@ template SmallArray(T, uint maxLength)
         /// ditto
         alias opBinary(string op : "~") = concat;
 
-
-    scope nothrow:
-
         /++
         Returns an scope common array.
 
@@ -246,35 +243,59 @@ template SmallArray(T, uint maxLength)
 
         The alias helps with `[]`, `[i]`, `[i .. j]`, `==`, and `!=` operations and implicit conversion to strings.
         +/
-        inout(T)[] asArray() inout @trusted return @property
+        inout(T)[] opIndex() inout @trusted return scope
         {
             return _data[0 .. _length];
         }
 
-        alias asArray this;
+        ///
+        ref inout(T) opIndex(size_t index) inout scope return
+        {
+            return opIndex[index];
+        }
 
     const:
 
-        /++
-        Checks if the array is empty (null).
-        +/
-        C opCast(C)() const
-            if (is(Unqual!C == bool))
+        ///
+        bool empty() @property
+        {
+            return _length == 0;
+        }
+
+        ///
+        size_t length() @property
         {
-            return _length != 0;
+            return _length;
         }
 
-        /// Hash implementation
+        /// Basic comparison implementation
         size_t toHash()
         {
-            return hashOf(asArray);
+            return hashOf(opIndex);
+        }
+
+        /// ditto
+        bool opEquals()(V[] array)
+        {
+            return opIndex == array;
+        }
+
+        /// ditto
+        bool opEquals(uint rhsMaxLength)(auto ref SmallArray!(T, rhsMaxLength) array)
+        {
+            return opIndex == array.opIndex;
         }
 
         /// ditto
         auto opCmp()(V[] array)
         {
-            import mir.algorithm.iteration: cmp;
-            return cmp(asArray, array);
+            return __cmp(opIndex, array);
+        }
+
+        /// ditto
+        auto opCmp(uint rhsMaxLength)(auto ref SmallArray!(T, rhsMaxLength) array)
+        {
+            return __cmp(opIndex, array.opIndex);
         }
     }
 }
@@ -283,19 +304,18 @@ template SmallArray(T, uint maxLength)
 @safe pure @nogc version(mir_test) unittest
 {
     SmallArray!(char, 16) s16;
-    assert(!s16);
+    assert(s16.empty);
 
     auto s8 = SmallArray!(char, 8)("Hellow!!");
-    assert(s8);
+    assert(!s8.empty);
     assert(s8 == "Hellow!!", s8[]);
 
     s16 = s8;
-    assert(s16 == "Hellow!!", s16);
+    assert(s16 == "Hellow!!", s16[]);
     s16[7] = '@';
     s8 = null;
-    assert(!s8);
+    assert(s8.empty);
     assert(s8 == null);
-    assert(s8 !is null);
     s8 = s16;
     assert(s8 == "Hellow!@");
 
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index ed1bec9e..ad05a033 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -51,19 +51,7 @@ extern(D) @safe pure @nogc:
     }
 
     /// ditto
-    this(const SmallString str) nothrow
-    {
-        this.opAssign(str);
-    }
-
-    /// ditto
-    this(uint n)(const SmallString!n str)
-    {
-        this.opAssign(str);
-    }
-
-    /// ditto
-    this(uint n)(ref scope const SmallString!n str)
+    this(uint n)(auto ref scope const SmallString!n str)
     {
         this.opAssign(str);
     }
@@ -108,21 +96,7 @@ extern(D) @safe pure @nogc:
     }
 
     /// ditto
-    ref typeof(this) opAssign(ref scope const SmallString rhs) return nothrow
-    {
-        _data = rhs._data;
-        return this;
-    }
-    
-    /// ditto
-    ref typeof(this) opAssign(const SmallString rhs) return nothrow
-    {
-        _data = rhs._data;
-        return this;
-    }
-
-    /// ditto
-    ref typeof(this) opAssign(uint n)(ref scope const SmallString!n rhs) return
+    ref typeof(this) opAssign(uint n)(auto ref scope const SmallString!n rhs) return
         if (n != maxLength)
     {
         static if (n < maxLength)
@@ -182,7 +156,7 @@ extern(D) @safe pure @nogc:
     ///
     ref typeof(this) append(char c) @trusted
     {
-        auto length = asArray.length;
+        auto length = opIndex.length;
         if (length == maxLength)
         {
             version(D_Exceptions) throw exception;
@@ -195,7 +169,7 @@ extern(D) @safe pure @nogc:
     ///
     ref typeof(this) append(scope const(char)[] str) @trusted
     {
-        auto length = asArray.length;
+        auto length = opIndex.length;
         if (length + str.length > maxLength)
         {
             version(D_Exceptions) throw exception;
@@ -235,7 +209,7 @@ scope nothrow:
 
     The alias helps with `[]`, `[i]`, `[i .. j]`, `==`, and `!=` operations and implicit conversion to strings.
     +/
-    inout(char)[] asArray() inout @trusted return @property
+    inout(char)[] opIndex() inout @trusted scope return
     {
         size_t i;
         if (__ctfe)
@@ -245,53 +219,57 @@ scope nothrow:
         return _data[0 .. i];
     }
 
-    alias asArray this;
+    ///
+    ref inout(char) opIndex(size_t index) inout scope return
+    {
+        return opIndex[index];
+    }
 
 const:
 
     ///
-    const(char)[] toString() return
+    bool empty() @property
     {
-        return asArray;
+        return _data[0] == 0;
     }
 
-    /// Comparisons operator overloads
-    int opCmp(ref scope const typeof(this) rhs) @trusted
+    ///
+    size_t length() @property
     {
-        if (__ctfe)
-        {
-            foreach (i, ref c; _data)
-                if (auto d = c - rhs._data[i])
-                    return d;
-            return 0;
-        }
-        else
-        {
-            return memcmp(this._data.ptr, rhs._data.ptr, _data.sizeof);
-        }
+        return opIndex.length;
+    }
+
+    ///
+    alias toString = opIndex;
 
+    /// Hash implementation
+    size_t toHash()
+    {
+        return hashOf(opIndex);
+    }
+
+    /// Comparisons operator overloads
+    bool opEquals(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
+    {
+        return opIndex == rhs.opIndex;
     }
 
     /// ditto
-    int opCmp(scope const(char)[] str)
+    bool opEquals()(scope const(char)[] str) scope
     {
-        import mir.algorithm.iteration: cmp;
-        return cast(int) cmp(asArray, str);
+        return opIndex == str;
     }
 
-    /++
-    Checks if the string is empty (null).
-    +/
-    C opCast(C)() const
-        if (is(Unqual!C == bool))
+    /// Comparisons operator overloads
+    int opCmp(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
     {
-        return _data[0] != 0;
+        return __cmp(opIndex, rhs.opIndex);
     }
 
-    /// Hash implementation
-    size_t toHash()
+    /// ditto
+    int opCmp()(scope const(char)[] str) scope
     {
-        return hashOf(_data[]);
+        return __cmp(opIndex, str);
     }
 }
 
@@ -299,19 +277,16 @@ const:
 @safe pure @nogc version(mir_test) unittest
 {
     SmallString!16 s16;
-    assert(!s16);
+    assert(s16.empty);
 
     auto s8 = SmallString!8("Hellow!!");
-    assert(s8);
     assert(s8 == "Hellow!!", s8[]);
 
     s16 = s8;
-    assert(s16 == "Hellow!!", s16);
+    assert(s16 == "Hellow!!", s16[]);
     s16[7] = '@';
     s8 = null;
-    assert(!s8);
-    assert(s8 == null);
-    assert(s8 !is null);
+    assert(s8.empty);
     s8 = s16;
     assert(s8 == "Hellow!@");
 
diff --git a/source/mir/type_info.d b/source/mir/type_info.d
index 804a3947..c8673dcd 100644
--- a/source/mir/type_info.d
+++ b/source/mir/type_info.d
@@ -12,8 +12,6 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 +/
 module mir.type_info;
 
-import std.traits: Unqual;
-
 /++
 +/
 struct mir_type_info
@@ -27,18 +25,12 @@ struct mir_type_info
     int size;
 }
 
-deprecated
-export extern(C) void mir_type_info_init(ref mir_type_info ti, typeof(mir_type_info.init.destructor) destructor, int size)
-    @safe pure nothrow @nogc
-{
-    ti.destructor = destructor;
-    ti.size = size;
-}
-
 /++
 +/
 ref immutable(mir_type_info) mir_get_type_info(T)() @trusted
 {
+    import std.traits: Unqual, hasElaborateDestructor;
+
     static if (is(T == class))
         enum sizeof = __traits(classInstanceSize, T);
     else
@@ -49,7 +41,7 @@ ref immutable(mir_type_info) mir_get_type_info(T)() @trusted
         return mir_get_type_info!(Unqual!T);
     }
     else
-    static if (hasDestructor!T)
+    static if (hasElaborateDestructor!T)
     {
         import std.traits: SetFunctionAttributes, functionAttributes;
         alias fun = void function(void*) @safe pure nothrow @nogc;
@@ -91,6 +83,8 @@ ref immutable(mir_type_info) mir_get_type_info(uint sizeof)()
 
 package template hasDestructor(T)
 {
+    import std.traits: Unqual;
+
     static if (is(T == struct))
     {
         static if (__traits(hasMember, Unqual!T, "__xdtor"))
@@ -111,6 +105,8 @@ package template hasDestructor(T)
 
 package const(void)* mir_get_payload_ptr(T)()
 {
+    import std.traits: Unqual;
+
     static if (!is(T == Unqual!T))
     {
         return mir_get_payload_ptr!(Unqual!T);
diff --git a/source/mir/variant.d b/source/mir/variant.d
deleted file mode 100644
index 8b0d877b..00000000
--- a/source/mir/variant.d
+++ /dev/null
@@ -1,1446 +0,0 @@
-/++
-This module implements a generic variant and nullable types.
-+/
-module mir.variant;
-
-import mir.functional: naryFun;
-import std.meta: allSatisfy;
-
-private static immutable variantExceptionMsg = "mir.variant: the variant stores other type then requested.";
-private static immutable variantNullExceptionMsg = "mir.variant: the variant is empty and doesn't store any value.";
-private static immutable variantMemberExceptionMsg = "mir.variant: the variant is stores the 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 alias ConstOf(T) = const T;
-private enum Alignof(T) = T.alignof;
-private enum canConstructWith(From, To) = __traits(compiles, (From a) { To b = a; } );
-private enum canImplicitlyRemoveConst(T) = __traits(compiles, {static T _function_(ref const T a) { return a; }} );
-private enum canRemoveConst(T) = canConstructWith!(const T, T);
-private enum canRemoveImmutable(T) = canConstructWith!(immutable T, T);
-private enum hasOpPostMove(T) = __traits(hasMember, T, "opPostMove");
-private enum hasToHash(T) = __traits(hasMember, T, "toHash");
-private enum isCopyable(S) = __traits(isCopyable, S); 
-private enum isPOD(T) = __traits(isPOD, T);
-private enum isVariant(T) = __traits(hasMember, T, "_isVariant");
-private enum Sizeof(T) = T.sizeof;
-
-private enum hasInoutConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope inout S rhs) inout { this.a = rhs.a; } }} );
-private enum hasConstConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) const { this.a = rhs.a; } }} );
-private enum hasImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope immutable S rhs) immutable { this.a = rhs.a; } }} );
-private enum hasMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope S rhs) { this.a = rhs.a; } }} );
-private enum hasSemiImmutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) immutable { this.a = rhs.a; } }} );
-private enum hasSemiMutableConstruction(T) = __traits(compiles, {static struct S { T a; this(ref return scope const S rhs) { this.a = rhs.a; } }} );
-
-@safe unittest
-{
-    static struct S { this(ref return scope inout S) inout {} }
-    static inout(S) _function_(ref inout S a) { return S(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);
-}
-
-private 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 .. $]);
-    }
-}
-
-private template TryRemoveConst(T)
-{
-    import std.traits: Unqual;
-    alias U = Unqual!T;
-    static if (canImplicitlyRemoveConst!U)
-    {
-        alias TryRemoveConst = U;
-    }
-    else
-    {
-        alias TryRemoveConst = T;
-    }
-}
-
-unittest
-{
-    static assert(is(TryRemoveConst!(const int) == int));
-}
-
-private 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;
-}
-
-private template isInstanceOf(alias S)
-{
-    enum isInstanceOf(T) = is(T == S!Args, Args...);
-}
-
-private struct VariantSet(Sets...);
-
-/++
-Dummy type for $(LREF Variant) self-referencing.
-+/
-struct SetAlias(uint id) {}
-///ditto
-struct This {}
-
-/++
-+/
-template TypeSet(T...)
-{
-    import std.meta: NoDuplicates, staticSort, staticMap;
-    // sort types by siezeof 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))
-                struct TypeSet;
-            else
-                alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T));
-        else
-            alias TypeSet = TypeSet!(NoDuplicates!T);
-    else
-        alias TypeSet = staticMap!(TryRemoveConst, T);
-}
-
-
-unittest
-{
-    struct S {}
-    alias C = S;
-    alias Int = int;
-    static assert(__traits(isSame, TypeSet!(S, int), TypeSet!(Int, C)));
-    static assert(__traits(isSame, TypeSet!(S, int, int), TypeSet!(Int, C)));
-    static assert(!__traits(isSame, TypeSet!(uint, S), TypeSet!(int, S)));
-}
-
-
-/++
-Cyclic referencing
-+/
-template Variants(Sets...)
-    if (allSatisfy!(isInstanceOf!TypeSet, Sets))
-{
-    import std.meta: staticMap;
-    import mir.internal.utility: Iota;
-
-    private alias TypeSetsInst(uint id) = Algebraic!(id, Sets);
-    ///
-    alias Variants = staticMap!(TypeSetsInst, Iota!(Sets.length));
-}
-
-/// 
-version(all) unittest
-{
-    import std.meta: AliasSeq;
-
-    alias V = Variants!(
-        TypeSet!(string, long, SetAlias!1*), // string, long, and V[1][]
-        TypeSet!(SetAlias!0[], int), // int and V[0]*
-    );
-
-    alias A = V[0];
-    alias B = V[1];
-
-    A arr = new B([A("hey"), A(100)]);
-}
-
-private static struct _Null()
-{
-@safe pure nothrow @nogc const:
-    size_t toHash() {return 0;}
-    bool opEquals(_Null) { return true; }
-    int opCmp(_Null) { return 0; }
-    typeof(null) __self() @property { return null; }
-    typeof(null) __self(typeof(null)) @property { return null; }
-    this(typeof(null)) inout {}
-    string toString() { return "null"; }
-}
-
-private static struct _Void()
-{
- @safe pure nothrow @nogc const:
-    size_t toHash() {return 0;}
-    bool opEquals(_Void) { return true; }
-    int opCmp(_Void) { return 0; }
-    void __self() @property { return; }
-    string toString() { return "void"; }
-}
-
-///
-struct Algebraic(uint _setId, _TypeSets...)
-    if (allSatisfy!(isInstanceOf!TypeSet, _TypeSets))
-{
-    private enum _isVariant;
-
-    import core.lifetime: moveEmplace;
-    import mir.conv: emplaceRef;
-    import std.meta: AliasSeq, anySatisfy, allSatisfy, staticMap, templateOr, Replace;
-    import std.traits:
-        hasElaborateAssign,
-        hasElaborateCopyConstructor,
-        hasElaborateDestructor,
-        isEqualityComparable,
-        isOrderingComparable,
-        Largest,
-        TemplateArgsOf,
-        Unqual
-        ;
-
-    private template _ApplyAliasesImpl(int length, Types...)
-    {
-        import std.typecons: ReplaceTypeUnless;
-        static if (length == 0)
-            alias _ApplyAliasesImpl = ReplaceTypeUnless!(isVariant, This, Algebraic!(_setId, _TypeSets), Types);
-        else
-        {
-            enum next  = length - 1;
-            alias _ApplyAliasesImpl = _ApplyAliasesImpl!(next,
-                ReplaceTypeUnless!(isVariant, SetAlias!next, Algebraic!(next, _TypeSets), Types));
-        }
-    }
-
-    ///
-    alias AllowedTypes = AliasSeq!(_ApplyAliasesImpl!(_TypeSets.length, TemplateArgsOf!(_TypeSets[_setId])));
-
-    private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes));
-
-    private static union _Storage
-    {
-        _Payload payload;
-
-        static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
-        {
-            ubyte[0] bytes;
-            static if (AllowedTypes.length)
-                enum uint id = 0;
-        }
-        else
-        struct
-        {
-            ubyte[Largest!_Payload.sizeof] bytes;
-
-            static if (AllowedTypes.length > 1)
-            {
-                import mir.utility: max;
-                enum _alignof = max(staticMap!(Alignof, _Payload));
-                static if ((bytes.length | _alignof) & 1)
-                    ubyte id;
-                else
-                static if ((bytes.length | _alignof) & 2)
-                    ushort id;
-                else
-                    uint id;
-            }
-            else
-            {
-                enum uint id = 0;
-            }
-        }
-    
-        static if (bytes.length && AllowedTypes.length)
-            ubyte[bytes.length + id.sizeof] allBytes;
-        else
-            alias allBytes = bytes;
-    }
-
-    private _Storage _storage;
-
-    static if (anySatisfy!(hasElaborateDestructor, AllowedTypes))
-    ~this() @safe
-    {
-        S: switch (_storage.id)
-        {
-            static foreach (i, T; AllowedTypes)
-            static if (hasElaborateDestructor!T)
-            {
-                case i:
-                    _mutableTrustedGet!T.__xdtor;
-                    break S;
-            }
-            default:
-        }
-        version(mir_secure_memory)
-            _storage.allBytes = 0xCC;
-    }
-
-    static if (anySatisfy!(hasOpPostMove, AllowedTypes))
-    void opPostMove(const ref typeof(this) old)
-    {
-        S: switch (_storage.id)
-        {
-            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, (){ AllowedTypes[0] arg; }))
-        {
-            @disable this();
-        }
-    }
-
-    private ref trustedBytes() inout @trusted
-    {
-        return *cast(ubyte[_storage.bytes.length]*)&this._storage.bytes;
-    }
-
-    static if (!allSatisfy!(isCopyable, AllowedTypes))
-        @disable this(this);
-    else
-    static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
-    {
-        // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes);
-        // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes);
-        // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction;
-
-        private static union _StorageI(uint i)
-        {
-            _Payload[i] payload;
-            ubyte[_Payload[i].sizeof] bytes;
-        }
-
-        private void _copyCtorSwitch(this This, RhsAlgebraic)(return ref scope RhsAlgebraic rhs)
-        {
-            switch (_storage.id)
-            {
-                static foreach (i, T; AllowedTypes)
-                static if (!is(T == typeof(null)) && !is(T == void) && hasElaborateCopyConstructor!T)
-                {
-                    case i: {
-                        import std.traits: CopyTypeQualifiers;
-                        CopyTypeQualifiers!(RhsAlgebraic, _StorageI!i) storage = { rhs._storage.payload[i] };
-                        trustedBytes[0 .. storage.bytes.length] = storage.bytes;
-                        return;
-                    }
-                }
-                default: return;
-            }
-        }
-
-        static if (allSatisfy!(hasInoutConstruction, AllowedTypes))
-        this(return ref scope inout Algebraic rhs) inout
-        {
-            this._storage.allBytes = rhs._storage.allBytes;
-            _copyCtorSwitch(rhs);
-        }
-        else
-        {
-            static if (allSatisfy!(hasMutableConstruction, AllowedTypes))
-            this(return ref scope Algebraic rhs)
-            {
-                this._storage.allBytes = rhs._storage.allBytes;
-                _copyCtorSwitch(rhs);
-            }
-
-            static if (allSatisfy!(hasConstConstruction, AllowedTypes))
-            this(return ref scope const Algebraic rhs) const
-            {
-                this._storage.allBytes = rhs._storage.allBytes;
-                _copyCtorSwitch(rhs);
-            }
-
-            static if (allSatisfy!(hasImmutableConstruction, AllowedTypes))
-            this(return ref scope immutable Algebraic rhs) immutable
-            {
-                this._storage.allBytes = rhs._storage.allBytes;
-                _copyCtorSwitch(rhs);
-            }
-
-            static if (allSatisfy!(hasSemiImmutableConstruction, AllowedTypes))
-            this(return ref scope const Algebraic rhs) immutable
-            {
-                this._storage.allBytes = rhs._storage.allBytes;
-                _copyCtorSwitch(rhs);
-            }
-
-            static if (allSatisfy!(hasSemiMutableConstruction, AllowedTypes))
-            this(return ref scope const Algebraic rhs)
-            {
-                this._storage.allBytes = rhs._storage.allBytes;
-                _copyCtorSwitch(rhs);
-            }
-        }
-    }
-
-    /++
-    +/
-    size_t toHash() const
-    {
-        static if (allSatisfy!(isPOD, AllowedTypes))
-        {
-            static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
-            {
-                return 0;
-            }
-            else
-            static if (this.sizeof <= 16)
-            {
-                return hashOf(_storage.bytes, _storage.id);
-            }
-            else
-            {
-                static if (this.sizeof <= ubyte.max)
-                    alias UInt = ubyte;
-                else
-                static if (this.sizeof <= ushort.max)
-                    alias UInt = ushort;
-                else
-                    alias UInt = uint;
-
-                static immutable UInt[_Payload.length + 1] sizes = [0, staticMap!(Sizeof, _Payload)];
-                return hashOf(_storage.bytes[0 .. sizes[_storage.id]], _storage.id);
-            }
-        }
-        else
-        switch (_storage.id)
-        {
-            static foreach (i, T; AllowedTypes)
-            {
-                case i:
-                    return hashOf(_storage.payload[i], i);
-            }
-            default: assert(0);
-        }
-    }
-
-    /++
-    +/
-    auto opEquals(const typeof(this) rhs) const
-    {
-        static if (AllowedTypes.length == 0)
-        {
-            return true;
-        }
-        else
-        {
-            if (this._storage.id != rhs._storage.id)
-                return false;
-            switch (_storage.id)
-            {
-                static foreach (i, T; AllowedTypes)
-                {
-                    case i:
-                        return this._trustedGet!T == rhs._trustedGet!T;
-                }
-                default: assert(0);
-            }
-        }
-    }
-
-    ///ditto
-    auto opEquals(ref const typeof(this) rhs) const
-    {
-        static if (AllowedTypes.length == 0)
-        {
-            return true;
-        }
-        else
-        {
-            if (this._storage.id != rhs._storage.id)
-                return false;
-            switch (_storage.id)
-            {
-                static foreach (i, T; AllowedTypes)
-                {
-                    case i:
-                        return this._trustedGet!T == rhs._trustedGet!T;
-                }
-                default: assert(0);
-            }
-        }
-    }
-
-    /++
-    +/
-    auto opCmp()(auto ref const typeof(this) rhs) const
-    {
-        static if (AllowedTypes.length == 0)
-        {
-            return 0;
-        }
-        else
-        {
-            import mir.internal.utility: isFloatingPoint;
-            if (auto d = int(this._storage.id) - int(rhs._storage.id))
-                return d;
-            switch (_storage.id)
-            {
-                static foreach (i, T; AllowedTypes)
-                {
-                    case i:
-                        static if (__traits(compiles, __cmp(_trustedGet!T, rhs._trustedGet!T)))
-                            return __cmp(_trustedGet!T, rhs._trustedGet!T);
-                        else
-                        static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U))
-                            return this._trustedGet!T.opCmp(rhs._trustedGet!T);
-                        else
-                        // static if (isFloatingPoint!T)
-                        //     return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs._trustedGet!T;
-                        // else
-                            return this._trustedGet!T < rhs._trustedGet!T ? -1 :
-                                this._trustedGet!T > rhs._trustedGet!T ? +1 : 0;
-                }
-                default: assert(0);
-            }
-        }
-    }
-
-    ///
-    void toString(C, W)(scope ref W w)
-    {
-        static if (AllowedTypes.length == 0)
-        {
-            return w.put(cast(immutable C[])"Algebraic");
-        }
-        else
-        {
-            import mir.format: print;
-            switch (_storage.id)
-            {
-                static foreach (i, T; AllowedTypes)
-                {
-                    case i:
-                        print(w, _trustedGet!T);
-                }
-                default: assert(0);
-            }
-        }
-    }
-
-    static if (is(AllowedTypes[0] == typeof(null)))
-    {
-        ///
-        bool opCast(C)() const
-            if (is(C == bool))
-        {
-            return _storage.id != 0;
-        }
-        /// Defined if the first type is `typeof(null)`
-        bool isNull() const { return _storage.id == 0; }
-        /// ditto
-        void nullify() { this = null; }
-
-        static if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2)
-        /// ditto
-        auto get()()
-        {
-            if (!_storage.id)
-            {
-                version(D_Exceptions)
-                    throw variantNullException;
-                else
-                    assert(0, variantNullExceptionMsg);
-            }
-            static if (AllowedTypes.length > 1)
-            {
-                Algebraic!(
-                    _setId,
-                    _TypeSets[0 .. _setId],
-                    TypeSet!(TemplateArgsOf!(_TypeSets[_setId])[1 .. $]),
-                    _TypeSets[_setId + 1 .. $]
-                ) ret;
-                static if (ret.AllowedTypes.length > 1)
-                    ret._storage.id = cast(typeof(ret._storage.id))(this._storage.id - 1);
-
-                static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
-                {
-                    ret._storage.bytes = 0;
-                    S: switch (_storage.id)
-                    {
-                        static foreach (i, T; AllowedTypes)
-                        {
-                            static if (hasElaborateCopyConstructor!T)
-                            {
-                                case i:
-                                    ret._trustedGet!T.emplaceRef(this._trustedGet!T);
-                                    break S;
-                            }
-                        }
-                        default:
-                            ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
-                    }
-                }
-                else
-                {
-                    ret._storage.bytes = this._storage.bytes[0 .. ret._storage.bytes.length];
-                }
-
-                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`.
-            +/
-            ref inout(AllowedTypes[1]) get() inout
-            {
-                assert(_storage.id, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ").");
-                return _trustedGet!(AllowedTypes[1]);
-            }
-
-            /// ditto
-            @property inout(AllowedTypes[1]) get()(inout(AllowedTypes[1]) fallback) inout @safe pure nothrow
-            {
-                return isNull ? fallback : get();
-            }
-
-            /// ditto
-            @property auto get(U)(inout(U) fallback) inout @safe pure nothrow
-            {
-                return isNull ? fallback : get();
-            }
-        }
-    }
-
-    static foreach (i, T; AllowedTypes)
-    {
-        /// Zero cost always nothrow `get` alternative
-        auto ref _trustedGet(E)() @trusted @property return inout nothrow
-            if (is(E == T))
-        {
-            assert (i == _storage.id);
-            static if (is(T == typeof(null)))
-                return null;
-            else
-            static if (is(T == void))
-                return;
-            else
-                return _storage.payload[i];
-        }
-
-        private auto ref T _mutableTrustedGet(E)() @trusted @property return const nothrow
-            if (is(E == T))
-        {
-            assert (i == _storage.id, T.stringof);
-            static if (is(T == typeof(null)))
-                return null;
-            else
-            static if (is(T == void))
-                return;
-            else
-                return *cast(Unqual!(AllowedTypes[i])*)&_storage.payload[i];
-        }
-
-        ///
-        auto ref get(E)() @property return inout
-            if (is(E == T))
-        {
-            import mir.utility: _expect;
-            static if (AllowedTypes.length > 1)
-            {
-                if (_expect(i != _storage.id, false))
-                {
-                    version(D_Exceptions)
-                        throw variantException;
-                    else
-                        assert(0, variantExceptionMsg);
-                }
-            }
-            return _trustedGet!T;
-        }
-
-        static if (!is(T == void))
-        {
-
-        private void inoutTrustedCtor(ref scope inout T rhs) inout @trusted
-        {
-            trustedBytes[0 .. _Payload[i].sizeof] = *cast(ubyte[_Payload[i].sizeof]*)&rhs;
-            trustedBytes[_Payload[i].sizeof .. $] = 0;
-            static if (AllowedTypes.length > 1)
-                *cast(typeof(_Storage.id)*)&_storage.id = i;
-            static if (hasOpPostMove!T)
-                (*cast(Unqual!T*)&(_trustedGet!T())).opPostMove(rhs);
-            static if (hasElaborateDestructor!T)
-                emplaceRef(*cast(Unqual!T*)&rhs);
-        }
-
-        this(T rhs)
-        {
-            inoutTrustedCtor(rhs);
-        }
-
-        this(const T rhs) const
-        {
-            inoutTrustedCtor(rhs);
-        }
-
-        this(immutable T rhs) immutable
-        {
-            inoutTrustedCtor(rhs);
-        }
-
-        static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); }))
-        ///
-        ref opAssign(T rhs) return @trusted
-        {
-            static if (anySatisfy!(hasElaborateDestructor, AllowedTypes))
-                this.__dtor();
-            __ctor(rhs);
-            return this;
-        }
-
-        /++
-        +/
-        auto opEquals()(auto ref const T rhs) const
-        {
-            static if (AllowedTypes.length > 1)
-                if (_storage.id != i)
-                    return false;
-            return _trustedGet!T == rhs;
-        } 
-
-        /++
-        +/
-        auto opCmp()(auto ref const T rhs) const
-        {
-            import mir.internal.utility: isFloatingPoint;
-            static if (AllowedTypes.length > 1)
-                if (auto d = int(_storage.id) - 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
-            static if (isFloatingPoint!T)
-                return _trustedGet!T == rhs ? 0 : _trustedGet!T - rhs;
-            else
-                return _trustedGet!T < rhs ? -1 :
-                    _trustedGet!T > rhs ? +1 : 0;
-        }
-        }
-    }
-}
-
-/++
-Variant Type (aka Algebraic Type) with clever member access.
-
-Compatible with BetterC mode.
-+/
-alias Variant(T...) = Algebraic!(0, TypeSet!T);
-
-/++
-Nullable variant Type (aka Algebraic Type) with clever member access.
-
-Compatible with BetterC mode.
-+/
-alias Nullable(T...) = Variant!(typeof(null), T);
-
-// 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(all) unittest
-{
-    import mir.functional: Tuple = RefTuple;
-
-    // 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);
-}
-
-///ditto
-@safe pure unittest
-{
-    // An object is a double, a string, or a hash of objects
-    alias Obj = Variant!(double, string, This[string]);
-    alias Map = Obj[string];
-
-    Obj obj = "hello";
-    assert(obj.get!string == "hello");
-    obj = 42.0;
-    assert(obj.get!double == 42);
-    obj = ["customer": Obj("John"), "paid": Obj(23.95)];
-    assert(obj.get!Map["customer"] == "John");
-}
-
-/// Test for opCmp, opEqual, toHash
-version(mir_test)
-@safe pure @nogc nothrow
-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() { return hashOf(_payload); }
-
-    static if (hasOpEquals)
-        auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); }
-        auto opCmp(ref const typeof(this) rhs) { return _payload == rhs._payload; }
-    }
-
-    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
-unittest
-{
-    static struct S1 { immutable(ubyte)* value; }
-    static struct C1 { immutable(uint)* value; }
-
-    alias V = Variant!(S1, C1);
-    const V v = 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
-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
-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
-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
-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
-unittest
-{
-    static struct S {
-        uint* value;
-        this(this) @safe pure nothrow @nogc {}
-        // void opAssign(typeof(this) rhs) {}
-    }
-    static struct C { const(uint)* value; }
-
-    S s;
-    S r = s;
-    r = s;
-    r = S.init;
-
-    alias V = Variant!(S, C);
-    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 match(visitors...) = visit!(naryFun!visitors, Exhaustive.compileTime);
-
-///
-@safe pure @nogc nothrow
-unittest
-{
-    alias Number = Variant!(int, double);
-
-    Number x = 23;
-    Number y = 1.0;
-
-    assert(x.match!((int v) => true, (float v) => false));
-    assert(y.match!((int v) => false, (float v) => true));
-}
-
-/// Special `typeof(null)` support for the first argument.
-@safe pure @nogc
-unittest
-{
-    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);
-}
-
-/// ditto
-@safe pure @nogc
-unittest
-{
-    alias Number = Nullable!(int, double);
-
-    Number z = null; // default
-    Number x = 23;
-    Number y = 1.0;
-
-    () nothrow {
-        assert(x.match!((int v) => true, (float v) => false));
-        assert(y.match!((int v) => false, (float v) => true));
-        assert(z.match!((typeof(null) v) => true, (v) => false));
-    } ();
-
-    auto xx = x.get;
-    static assert (is(typeof(xx) == Variant!(int, double)));
-    assert(xx.match!((int v) => v, (float v) => 0) == 23);
-    assert(xx.match!((ref v) => v) == 23);
-
-    x = null;
-    y.nullify;
-
-    assert(x.isNull);
-    assert(y.isNull);
-    assert(z.isNull);
-    assert(z == y);
-}
-
-// Type with copy constructor
-@safe pure nothrow// @nogc 
-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(b.get!S.n == 1, b.get!S.n.to!string);
-    assert(a.get!S.n == 0, a.get!S.n.to!string);
-}
-
-// Empty type set
-@safe pure nothrow @nogc unittest 
-{
-    Variant!() a;
-    auto b = a;
-    assert(a.toHash == 0);
-    assert(a == b);
-    assert(a <= b && b >= a);
-    static assert(typeof(a).sizeof == 1);
-}
-
-// Empty nullable type set
-@safe pure nothrow @nogc unittest 
-{
-    Nullable!() 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 unittest 
-{
-    struct S { ubyte d; }
-    static assert(Nullable!(byte, char, S).sizeof == 2);
-}
-
-// Alignment
-@safe pure nothrow @nogc unittest 
-{
-    struct S { ubyte[3] d; }
-    static assert(Nullable!(ushort, wchar, S).sizeof == 4);
-}
-
-// opPostMove support
-version(mir_test)
-@safe pure @nogc nothrow
-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.get!S.s == 1);
-}
-
-/++
-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
-+/
-alias tryMatch(visitors...) = visit!(naryFun!visitors, Exhaustive.exception);
-
-///
-@safe pure @nogc
-unittest
-{
-    alias Number = Variant!(int, double);
-
-    Number x = 23;
-
-    assert(x.tryMatch!((int v) => true));
-}
-
-/++
-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 
-+/
-alias optionalMatch(visitors...) = visit!(naryFun!visitors, Exhaustive.nullable);
-
-///
-version(mir_test)
-@safe pure @nogc nothrow
-unittest
-{
-    static struct S { int a; }
-
-    Variant!(S, double) variant;
-
-    alias optionalMatchInst = optionalMatch!((ref value) => value + 0);
-
-    // do nothing because of variant isn't initialized
-    Nullable!double result = optionalMatchInst(variant);
-    assert(result.isNull);
-
-    variant = S(2);
-    // do nothing because of lambda can't compile
-    result = optionalMatchInst(variant);
-    assert(result == null);
-
-    variant = 3.0;
-    result = optionalMatchInst(variant);
-    assert (result == 3.0);
-}
-
-/++
-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) = visit!(getMemberHandler!member, Exhaustive.compileTime);
-
-///
-@safe pure @nogc nothrow
-unittest
-{
-    static struct S { auto bar(int a) { return a; }}
-    static struct C { alias bar = (double a) => a * 2; }
-
-    alias V = Variant!(S, C);
-
-    V x = S();
-    V y = C();
-
-    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);
-}
-
-/++
-Behaves as $(LREF match) 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) = visit!(getMemberHandler!member, Exhaustive.exception);
-
-///
-version(mir_test)
-@safe pure @nogc
-unittest
-{
-    static struct S { int bar(int a) { return a; }}
-    static struct C { alias Bar = (double a) => a * 2; }
-
-    alias V = Variant!(S, C);
-
-    V x = S();
-    V y = C();
-
-    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);
-}
-
-///
-version(mir_test)
-@safe pure @nogc nothrow
-unittest
-{
-    alias Number = Variant!(int, double);
-
-    Number x = Number(23);
-    Number y = Number(1.0);
-
-    assert(x.match!((int v) => true, (float v) => false));
-    assert(y.match!((int v) => false, (float v) => true));
-}
-
-/++
-Behaves as $(LREF match) 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 member can't be called with provided arguments 
-+/
-alias optionalGetMember(string member) = visit!(getMemberHandler!member, Exhaustive.nullable);
-
-
-private template getMemberHandler(string member)
-{
-    ///
-    auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args)
-    {
-        static if (Args.length == 0)
-        {
-            return __traits(getMember, value, member);
-        }
-        else
-        {
-            import core.lifetime: forward;
-            return __traits(getMember, value, member)(forward!args);
-        }
-    }
-}
-
-private template VariantReturnTypes(T...)
-{
-    import std.meta: NoDuplicates, staticMap;
-
-    alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T));
-}
-
-enum Exhaustive
-{
-    compileTime,
-    exception,
-    nullable,
-}
-
-private template visit(alias visitor, Exhaustive exhaustive)
-{
-    import std.traits: Unqual;
-    ///
-    auto ref visit(V, Args...)(auto ref V variant, auto ref Args args)
-        if (isVariant!V)
-    {
-        import core.lifetime: forward;
-        import std.meta: NoDuplicates, staticMap, AliasSeq;
-
-        template allArgs(T)
-        {
-            static if (is(T == void))
-                alias allArgs = forward!args;
-            else
-            static if (is(T == typeof(null)))
-                alias allArgs = AliasSeq!(null, forward!args);
-            else
-            {
-                ref fun() @property @trusted
-                {
-                    return variant._trustedGet!T;
-                }
-                alias allArgs = AliasSeq!(fun, forward!args);
-            }
-        }
-
-        template VariantReturnTypesImpl(T)
-        {
-            static if (__traits(compiles, visitor(allArgs!T)))
-                alias VariantReturnTypesImpl = TryRemoveConst!(typeof(visitor(allArgs!T)));
-            else
-                alias VariantReturnTypesImpl = AliasSeq!();
-        }
-
-        static if (exhaustive == Exhaustive.nullable)
-            alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, V.AllowedTypes));
-        else
-            alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, V.AllowedTypes));
-
-        switch (variant._storage.id)
-        {
-            static foreach (i, T; V.AllowedTypes)
-            {
-                case i:
-                    static if (__traits(compiles, visitor(allArgs!T)))
-                    {
-                        static if (AllReturnTypes.length == 1)
-                        {
-                            return visitor(allArgs!T);
-                        }
-                        else
-                        {
-                            static if (is(VariantReturnTypesImpl!T == void))
-                            {
-                                visitor(allArgs!T);
-                                return Variant!AllReturnTypes.init;
-                            }
-                            else
-                            {
-                                return Variant!AllReturnTypes(visitor(allArgs!T));
-                            }
-                        }
-                    }
-                    else
-                    static if (exhaustive == Exhaustive.compileTime)
-                    {
-                        static if (is(T == typeof(null)))
-                            assert(0, "Null " ~ V.stringof);
-                        else
-                            static assert(0, V.stringof ~ ": the visitor cann't be caled with type (first argument) " ~ typeof(variant._trustedGet!T()).stringof ~ (Args.length ? (" and additional arguments " ~ Args.stringof) : ""));
-                    }
-                    else
-                    static if (exhaustive == Exhaustive.nullable)
-                    {
-                        return Variant!AllReturnTypes(null);
-                    }
-                    else
-                    {
-                        version(D_Exceptions)
-                            throw variantMemberException;
-                        else
-                            assert(0, variantMemberExceptionMsg);
-                    }
-            }
-            default: assert(0);
-        }
-    }
-}
-
-version(mir_test)
-@safe pure @nogc
-unittest
-{
-    static struct S { int a; }
-
-    Variant!(S, double) variant;
-    variant = 1.0;
-    variant.tryMatch!((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.tryMatch!fun;
-    assert (variant.get!double == 5);
-
-    variant = S(4);
-    variant.tryMatch!fun;
-    assert (variant.get!S.a == 6);
-}

From fa27387fb925ba74f4ef78017c0bb205fdc5ffbb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 13 Nov 2020 19:47:29 +0700
Subject: [PATCH 127/573] fix small array and small string

---
 source/mir/small_array.d  | 14 +++++++++++++-
 source/mir/small_string.d | 15 ++++++++++++++-
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 2c272c42..a143f3d7 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -268,12 +268,24 @@ template SmallArray(T, uint maxLength)
             return _length;
         }
 
-        /// Basic comparison implementation
+        /// Hash implementation
         size_t toHash()
         {
             return hashOf(opIndex);
         }
 
+        /// Comparisons operator overloads
+        bool opEquals(ref scope const SmallArray rhs) scope
+        {
+            return opIndex == rhs.opIndex;
+        }
+
+        /// ditto
+        bool opEquals(SmallArray rhs) scope
+        {
+            return opIndex == rhs.opIndex;
+        }
+
         /// ditto
         bool opEquals()(V[] array)
         {
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index ad05a033..ab8bd920 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -249,7 +249,20 @@ const:
     }
 
     /// Comparisons operator overloads
+    bool opEquals(ref scope const SmallString rhs) scope
+    {
+        return _data == rhs._data;
+    }
+
+    /// ditto
+    bool opEquals(SmallString rhs) scope
+    {
+        return _data == rhs._data;
+    }
+
+    /// ditto
     bool opEquals(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
+        if (rhsMaxLength != maxLength)
     {
         return opIndex == rhs.opIndex;
     }
@@ -260,7 +273,7 @@ const:
         return opIndex == str;
     }
 
-    /// Comparisons operator overloads
+    /// ditto
     int opCmp(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
     {
         return __cmp(opIndex, rhs.opIndex);

From d437ea0f585487ce1a3d04bd871bd0810a776322 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 13 Nov 2020 19:53:05 +0700
Subject: [PATCH 128/573] fix small array

---
 source/mir/small_array.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index a143f3d7..11e131db 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -275,13 +275,13 @@ template SmallArray(T, uint maxLength)
         }
 
         /// Comparisons operator overloads
-        bool opEquals(ref scope const SmallArray rhs) scope
+        bool opEquals(ref const SmallArray rhs)
         {
             return opIndex == rhs.opIndex;
         }
 
         /// ditto
-        bool opEquals(SmallArray rhs) scope
+        bool opEquals(SmallArray rhs)
         {
             return opIndex == rhs.opIndex;
         }

From 9be8796a517df8899fe1d8f6e55fc79657a15d6e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 13 Nov 2020 21:38:32 +0700
Subject: [PATCH 129/573] extend copy constructors

---
 source/mir/rc/array.d    | 20 ++++++++++++++++++++
 source/mir/rc/ptr.d      | 22 ++++++++++++++++++++++
 source/mir/rc/slim_ptr.d | 20 ++++++++++++++++++++
 3 files changed, 62 insertions(+)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index cce6ffd4..870854c3 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -178,6 +178,26 @@ struct mir_rcarray(T)
     else
         package alias V = T;
 
+    static if (is(T == const) || is(T == immutable))
+    this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._payload = cast(typeof(this._payload))rhs._payload;
+            mir_rc_increase_counter(context);
+        }
+    }
+
+    static if (is(T == immutable))
+    this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._payload = cast(typeof(this._payload))rhs._payload;
+            mir_rc_increase_counter(context);
+        }
+    }
+
     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
         if (rhs)
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index ebad23d4..7154140d 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -116,6 +116,28 @@ struct mir_rcptr(T)
         }
     }
 
+    static if (is(T == const) || is(T == immutable))
+    this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._value = cast(typeof(this._value))rhs._value;
+            this._context = cast(typeof(this._context))rhs._context;
+            mir_rc_increase_counter(context);
+        }
+    }
+
+    static if (is(T == immutable))
+    this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._value = cast(typeof(this._value))rhs._value;
+            this._context = cast(typeof(this._context))rhs._context;
+            mir_rc_increase_counter(context);
+        }
+    }
+
     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
         if (rhs)
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index 0ebdaf23..27f6114d 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -110,6 +110,26 @@ struct mir_slim_rcptr(T)
         }
     }
 
+    static if (is(T == const) || is(T == immutable))
+    this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._value = cast(typeof(this._value))rhs._value;
+            mir_rc_increase_counter(context);
+        }
+    }
+
+    static if (is(T == immutable))
+    this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._value = cast(typeof(this._value))rhs._value;
+            mir_rc_increase_counter(context);
+        }
+    }
+
     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
         if (rhs)

From bb93f278c0bd73b06cee022f25652801107789b3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 14 Nov 2020 23:02:59 +0700
Subject: [PATCH 130/573] add more rc constructors

---
 source/mir/rc/array.d    | 10 ++++++++++
 source/mir/rc/ptr.d      | 11 +++++++++++
 source/mir/rc/slim_ptr.d | 10 ++++++++++
 3 files changed, 31 insertions(+)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 870854c3..be34002b 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -198,6 +198,16 @@ struct mir_rcarray(T)
         }
     }
 
+    static if (is(T == immutable))
+    this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._payload = cast(typeof(this._payload))rhs._payload;
+            mir_rc_increase_counter(context);
+        }
+    }
+
     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
         if (rhs)
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index 7154140d..e38ed3cf 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -138,6 +138,17 @@ struct mir_rcptr(T)
         }
     }
 
+    static if (is(T == immutable))
+    this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._value = cast(typeof(this._value))rhs._value;
+            this._context = cast(typeof(this._context))rhs._context;
+            mir_rc_increase_counter(context);
+        }
+    }
+
     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
         if (rhs)
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index 27f6114d..4ee9209e 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -130,6 +130,16 @@ struct mir_slim_rcptr(T)
         }
     }
 
+    static if (is(T == immutable))
+    this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc
+    {
+        if (rhs)
+        {
+            this._value = cast(typeof(this._value))rhs._value;
+            mir_rc_increase_counter(context);
+        }
+    }
+
     this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc
     {
         if (rhs)

From 80e6ae2f62be9a043af681b9fcb491710fb2fd72 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 15 Nov 2020 13:34:06 +0700
Subject: [PATCH 131/573] fixup references

---
 source/mir/ndslice/slice.d | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 64154635..e5bd203e 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1163,7 +1163,7 @@ public:
     /++
     Iterator
     Returns:
-        Iterator (pointer) to the $(LREF Slice.first) element.
+        Iterator (pointer) to the $(LREF .Slice.first) element.
     +/
     auto iterator()() inout scope return @property
     {
@@ -1172,7 +1172,7 @@ public:
 
     static if (kind == Contiguous && isPointer!Iterator)
         /++
-        `ptr` alias is available only if the slice kind is $(LREF Contiguous) contiguous and the $(LREF Slice.iterator) is a pointers.
+        `ptr` alias is available only if the slice kind is $(LREF Contiguous) contiguous and the $(LREF .Slice.iterator) is a pointers.
         +/
         alias ptr = iterator;
     else
@@ -2432,7 +2432,7 @@ public:
     /++
     Duplicates slice.
     Returns: GC-allocated Contiguous mutable slice.
-    See_also: $(LREF Slice.idup)
+    See_also: $(LREF .Slice.idup)
     +/
     Slice!(Unqual!DeepElement*, N)
     dup()() scope @property
@@ -2487,7 +2487,7 @@ public:
     /++
     Duplicates slice.
     Returns: GC-allocated Contiguous immutable slice.
-    See_also: $(LREF Slice.dup)
+    See_also: $(LREF .Slice.dup)
     +/
     Slice!(immutable(DeepElement)*, N)
     idup()() scope @property
@@ -2550,8 +2550,8 @@ public:
         location in slice, adjusted for `Slice._strides`
     See_also:
         $(SUBREF topology, flattened),
-        $(LREF Slice.indexStride),
-        $(LREF Slice.accessFlat)
+        $(LREF .Slice.indexStride),
+        $(LREF .Slice.accessFlat)
     +/
     private
     ptrdiff_t indexStrideValue(ptrdiff_t n) @safe scope const

From 08f70b551011f3dded78931a1032bfc85c042455 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 15 Nov 2020 15:45:38 +0700
Subject: [PATCH 132/573] fix CoordinateValue.opCmp

---
 source/mir/ndslice/slice.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index e5bd203e..8d85783a 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -450,8 +450,8 @@ struct CoordinateValue(T, size_t N = 1)
 private int cmpCoo(size_t N)(scope const auto ref size_t[N] a, scope const auto ref size_t[N] b)
 {
     foreach (i; Iota!(0, N))
-        if (auto d = a[i] - b[i])
-            return d > 0 ? 1 : -1;
+        if (a[i] != b[i])
+            return a[i] > b[i] ? 1 : -1;
     return 0;
 }
 

From 08cb271ebebb1e5eb2065d8f9345ac28f10f7fd6 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 19 Nov 2020 13:49:15 +0700
Subject: [PATCH 133/573] add strided overload

---
 source/mir/ndslice/dynamic.d | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/source/mir/ndslice/dynamic.d b/source/mir/ndslice/dynamic.d
index 7817fd2f..8f783d92 100644
--- a/source/mir/ndslice/dynamic.d
+++ b/source/mir/ndslice/dynamic.d
@@ -810,6 +810,24 @@ Params:
 Returns:
     n-dimensional slice
 +/
+auto strided(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, ptrdiff_t factor)
+{
+    import core.lifetime: move;
+    import std.meta: Repeat;
+    return move(slice).strided!(Iota!N)(Repeat!(N, factor));
+}
+
+///
+@safe pure nothrow version(mir_test) unittest
+{
+    import mir.ndslice.topology: iota;
+    // 0  1   2  3
+    // 4  5   6  7
+    // 8  9  10 11
+    assert(iota(3, 4).strided(2) == [[0, 2], [8, 10]]);
+}
+
+/// ditto
 template strided(Dimensions...)
     if (Dimensions.length)
 {

From 0d4fa872fcc7acd53f53dfca56abc7bb92b9590a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 19 Nov 2020 14:00:18 +0700
Subject: [PATCH 134/573] fix dropToHypercube for 1D

---
 source/mir/ndslice/dynamic.d | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/source/mir/ndslice/dynamic.d b/source/mir/ndslice/dynamic.d
index 8f783d92..47e625af 100644
--- a/source/mir/ndslice/dynamic.d
+++ b/source/mir/ndslice/dynamic.d
@@ -965,7 +965,7 @@ Returns:
     n-dimensional slice
 +/
 Slice!(Iterator, N, kind) dropToHypercube(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
-    if (kind == Canonical || kind == Universal)
+    if (kind == Canonical || kind == Universal || N == 1)
 do
 {
     size_t length = slice._lengths[0];
@@ -979,6 +979,7 @@ do
 
 /// ditto
 Slice!(Iterator, N, Canonical) dropToHypercube(Iterator, size_t N)(Slice!(Iterator, N) slice)
+    if (N > 1)
 {
     import mir.ndslice.topology: canonical;
     return slice.canonical.dropToHypercube;
@@ -997,4 +998,6 @@ Slice!(Iterator, N, Canonical) dropToHypercube(Iterator, size_t N)(Slice!(Iterat
         .universal
         .dropToHypercube
         .shape == cast(size_t[4])[3, 3, 3, 3]);
+
+    assert(4.iota.dropToHypercube == 4.iota);
 }

From e0d48859fd7a535979c5907f5604baf26661ccc9 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 19 Nov 2020 14:08:24 +0700
Subject: [PATCH 135/573] add assumeSameShape and assumeHypercube

---
 source/mir/ndslice/topology.d | 57 +++++++++++++++++++++++++++++++++--
 1 file changed, 55 insertions(+), 2 deletions(-)

diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index cf190554..8e2d4e23 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -43,8 +43,10 @@ $(TR $(TH Function Name) $(TH Description))
 
 $(T2 universal, Converts a slice to universal $(SUBREF slice, SliceKind).)
 $(T2 canonical, Converts a slice to canonical $(SUBREF slice, SliceKind).)
-$(T2 assumeCanonical, Converts a slice to canonical $(SUBREF slice, SliceKind) (unsafe).)
-$(T2 assumeContiguous, Converts a slice to contiguous $(SUBREF slice, SliceKind) (unsafe).)
+$(T2 assumeCanonical, Converts a slice to canonical $(SUBREF slice, SliceKind). Does only `assert` checks.)
+$(T2 assumeContiguous, Converts a slice to contiguous $(SUBREF slice, SliceKind). Does only `assert` checks.)
+$(T2 assumeHypercube, Helps the compiler to use optimisations related to the shape form. Does only `assert` checks.)
+$(T2 assumeSameShape, Helps the compiler to use optimisations related to the shape form. Does only `assert` checks.)
 
 )
 
@@ -418,6 +420,57 @@ version(mir_test) unittest
     assert(assmcontdf.label!1[1] == "Label2");
 }
 
+/++
+Helps the compiler to use optimisations related to the shape form
++/
+void assumeHypercube
+    (Iterator, size_t N, SliceKind kind, Labels...)
+    (ref scope Slice!(Iterator, N, kind, Labels) slice)
+{
+    foreach (i; Iota!(1, N))
+    {
+        assert(slice._lengths[i] == slice._lengths[0]);
+        slice._lengths[i] = slice._lengths[0];
+    }
+}
+
+///
+@safe @nogc pure nothrow version(mir_test) unittest
+{
+    auto b = iota(5, 5);
+    
+    assumeHypercube(b);
+
+    assert(b == iota(5, 5));
+}
+
+/++
+Helps the compiler to use optimisations related to the shape form
++/
+void assumeSameShape(T...)
+    (ref scope T slices)
+    if (allSatisfy!(isSlice, T))
+{
+    foreach (i; Iota!(1, T.length))
+    {
+        assert(slices[i]._lengths == slices[0]._lengths);
+        slices[i]._lengths = slices[0]._lengths;
+    }
+}
+
+///
+@safe @nogc pure nothrow version(mir_test) unittest
+{
+    auto a = iota(5, 5);
+    auto b = iota(5, 5);
+    
+    assumeHypercube(a); // first use this one, if applicable
+    assumeSameShape(a, b); //
+
+    assert(a == iota(5, 5));
+    assert(b == iota(5, 5));
+}
+
 /++
 +/
 auto assumeFieldsHaveZeroShift(Iterator, size_t N, SliceKind kind)

From f44c2e9532fb42a99a093530fdfb12b4932a1089 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 19 Nov 2020 14:11:59 +0700
Subject: [PATCH 136/573] apdate docs

---
 source/mir/ndslice/package.d | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index 5f729047..de4ec097 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -130,6 +130,8 @@ $(TR $(TDNW $(SUBMODULE topology) $(BR)
         $(SUBREF topology, as)
         $(SUBREF topology, assumeCanonical)
         $(SUBREF topology, assumeContiguous)
+        $(SUBREF topology, assumeHypercube)
+        $(SUBREF topology, assumeSameShape)
         $(SUBREF topology, bitpack)
         $(SUBREF topology, bitwise)
         $(SUBREF topology, blocks)

From 92cddf31c5f5476a4cd9e281bac527ceca032ea2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 20 Nov 2020 14:20:12 +0700
Subject: [PATCH 137/573] extend slide and stride

---
 source/mir/ndslice/iterator.d | 104 ++++++++++++++++++++++
 source/mir/ndslice/slice.d    |  40 +++++++++
 source/mir/ndslice/topology.d | 161 ++++++++++++++++++++++++++--------
 3 files changed, 269 insertions(+), 36 deletions(-)

diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 76782b38..4d6ed1fb 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -436,6 +436,110 @@ struct StrideIterator(Iterator)
     assert(*stride == *iota);
 }
 
+auto StrideIterator__map(Iterator, size_t factor, alias fun)(StrideIterator!(Iterator, factor) it)
+{
+    auto iterator = it._iterator._mapIterator!fun;
+    return StrideIterator!(typeof(iterator), factor)(iterator);
+}
+
+/++
+Iterates an iterator with a fixed strides.
+
+`StrideIterator` is used by $(SUBREF topology, stride).
++/
+struct StrideIterator(Iterator, ptrdiff_t factor)
+{
+@optmath:
+    ///
+    enum _stride = factor;
+
+    ///
+    Iterator _iterator;
+
+    ///
+    auto lightConst()() const @property
+    {
+        return StrideIterator!(LightConstOf!Iterator, _stride)(.lightConst(_iterator));
+    }
+
+    ///
+    auto lightImmutable()() immutable @property
+    {
+        return StrideIterator!(LightImmutableOf!Iterator, _stride)(.lightImmutable(_iterator));
+    }
+
+    ///
+    static alias __map(alias fun) = StrideIterator__map!(Iterator, _stride, fun);
+
+    auto ref opUnary(string op : "*")()
+    { return *_iterator; }
+
+    void opUnary(string op)() scope
+        if (op == "--" || op == "++")
+    { mixin("_iterator " ~ op[0] ~ "= _stride;"); }
+
+    auto ref opIndex()(ptrdiff_t index)
+    { return _iterator[index * _stride]; }
+
+    void opOpAssign(string op)(ptrdiff_t index) scope
+        if (op == "-" || op == "+")
+    { mixin("_iterator " ~ op ~ "= index * _stride;"); }
+
+    auto opBinary(string op)(ptrdiff_t index)
+        if (op == "+" || op == "-")
+    {
+        auto ret = this;
+        mixin(`ret ` ~ op ~ `= index;`);
+        return ret;
+    }
+
+    ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const
+    { return (this._iterator - right._iterator) / _stride; }
+
+    bool opEquals()(scope ref const typeof(this) right) scope const
+    { return this._iterator == right._iterator; }
+
+    ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const
+    {
+        static if (isPointer!Iterator)
+            ptrdiff_t ret = this._iterator - right._iterator;
+        else
+            ptrdiff_t ret = this._iterator.opCmp(right._iterator);
+        return _stride >= 0 ? ret : -ret;
+    }
+}
+
+///
+@safe pure nothrow @nogc version(mir_test) unittest
+{
+    IotaIterator!int iota;
+    StrideIterator!(IotaIterator!int, -3) stride;
+
+    iota -= stride._stride;
+    --stride;
+    assert(*stride == *iota);
+
+    iota += stride._stride;
+    ++stride;
+    assert(*stride == *iota);
+
+    assert(stride[7] == iota[7 * stride._stride]);
+
+    iota -= 100 * stride._stride;
+    stride -= 100;
+    assert(*stride == *iota);
+
+    iota += 100 * stride._stride;
+    stride += 100;
+    assert(*stride == *iota);
+
+    assert(*(stride + 10) == *(iota + 10 * stride._stride));
+
+    assert(stride - 1 < stride);
+
+    assert((stride - 5) - stride == -5);
+}
+
 package template _zip_types(Iterators...)
 {
     alias AliasSeq(T...) = T;
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 8d85783a..b93fb927 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1786,11 +1786,51 @@ public:
             auto f = 5;
             assert([2, 3].iota(f).last == f + 2 * 3 - 1);
         }
+
+        static if (kind_ != SliceKind.contiguous)
+        /// Peforms `popFrontAll` for all dimensions
+        void popFrontAll(T)()
+        {
+            assert(!anyEmpty);
+            foreach(d; Iota!N_)
+                popFront!d;
+        }
+
+        static if (doUnittest)
+        ///
+        @safe pure nothrow version(mir_test) unittest
+        {
+            import mir.ndslice.topology: iota, canonical;
+            auto v = [2, 3].iota.canonical;
+            v.popFrontAll;
+            assert(v == [[4, 5]]);
+        }
+
+        static if (kind_ != SliceKind.contiguous)
+        /// Peforms `popBackAll` for all dimensions
+        void popBackAll(T)()
+        {
+            assert(!anyEmpty);
+            foreach(d; Iota!N_)
+                popBackAll!d;
+        }
+
+        static if (doUnittest)
+        ///
+        @safe pure nothrow version(mir_test) unittest
+        {
+            import mir.ndslice.topology: iota, canonical;
+            auto v = [2, 3].iota.canonical;
+            v.popBackAll;
+            assert(v == [[0, 1]]);
+        }
     }
     else
     {
         alias first = front;
         alias last = back;
+        alias popFrontAll = popFront;
+        alias popBackAll = popBack;
     }
 
     /+
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 8e2d4e23..41e43675 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2152,6 +2152,48 @@ do
     }
 }
 
+///ditto
+template stride(size_t factor = 2)
+    if (factor > 1)
+{
+    auto stride
+        (Iterator, size_t N, SliceKind kind)
+        (Slice!(Iterator, N, kind) slice)
+    {
+        static if (N > 1)
+        {
+            return stride(slice.move.ipack!1.map!(.stride!factor));
+        }
+        else
+        static if (kind == Contiguous)
+        {
+            immutable rem = slice._lengths[0] % factor;
+            slice._lengths[0] /= factor;
+            if (rem)
+                slice._lengths[0]++;
+            import core.lifetime: move;
+            return Slice!(StrideIterator!(Iterator, factor), 1, kind)(slice._structure, StrideIterator!(Iterator, factor)(move(slice._iterator)));
+        }
+        else
+        {
+            return .stride(slice.move, factor);
+        }
+    }
+
+    /// ditto
+    auto stride(T)(T[] array)
+    {
+        return stride(array.sliced);
+    }
+
+    /// ditto
+    auto stride(T)(T withAsSlice)
+        if (hasAsSlice!T)
+    {
+        return stride(withAsSlice.asSlice);
+    }
+}
+
 /// ditto
 auto stride(T)(T[] array, ptrdiff_t factor)
 {
@@ -2170,7 +2212,9 @@ auto stride(T)(T withAsSlice, ptrdiff_t factor)
 {
     auto slice = iota(6);
     static immutable str = [0, 2, 4];
-    assert(slice.stride(2) == str);
+    assert(slice.stride(2) == str); // runtime factor
+    assert(slice.stride!2 == str); // compile time factor
+    assert(slice.stride == str); // default compile time factor is 2
     assert(slice.universal.stride(2) == str);
 }
 
@@ -3676,41 +3720,52 @@ Returns:
     n-dimensional slice of elements refTuple
 See_also: $(SUBREF slice, Slice.strides).
 +/
-auto zip
-    (bool sameStrides = false, Slices...)(Slices slices)
-    if (Slices.length > 1 && allSatisfy!(isConvertibleToSlice, Slices))
+template zip(bool sameStrides = false)
 {
-    static if (allSatisfy!(isSlice, Slices))
+    /++
+    Groups slices into a slice of refTuples. The slices must have identical strides or be 1-dimensional.
+    Params:
+        slices = list of slices
+    Returns:
+        n-dimensional slice of elements refTuple
+    See_also: $(SUBREF slice, Slice.strides).
+    +/
+    @optmath
+    auto zip(Slices...)(Slices slices)
+        if (Slices.length > 1 && allSatisfy!(isConvertibleToSlice, Slices))
     {
-        enum N = Slices[0].N;
-        foreach(i, S; Slices[1 .. $])
-        {
-            static assert(S.N == N, "zip: all Slices must have the same dimension count");
-            assert(slices[i + 1]._lengths == slices[0]._lengths, "zip: all slices must have the same lengths");
-            static if (sameStrides)
-                assert(slices[i + 1].strides == slices[0].strides, "zip: all slices must have the same strides when unpacked");
-        }
-        static if (!sameStrides && minElem(staticMap!(kindOf, Slices)) != Contiguous)
+        static if (allSatisfy!(isSlice, Slices))
         {
-            static assert(N == 1, "zip: cannot zip canonical and universal multidimensional slices if `sameStrides` is false");
-            mixin(`return .zip(` ~ _iotaArgs!(Slices.length, "slices[", "].hideStride, ") ~`);`);
+            enum N = Slices[0].N;
+            foreach(i, S; Slices[1 .. $])
+            {
+                static assert(S.N == N, "zip: all Slices must have the same dimension count");
+                assert(slices[i + 1]._lengths == slices[0]._lengths, "zip: all slices must have the same lengths");
+                static if (sameStrides)
+                    assert(slices[i + 1].strides == slices[0].strides, "zip: all slices must have the same strides when unpacked");
+            }
+            static if (!sameStrides && minElem(staticMap!(kindOf, Slices)) != Contiguous)
+            {
+                static assert(N == 1, "zip: cannot zip canonical and universal multidimensional slices if `sameStrides` is false");
+                mixin(`return .zip(` ~ _iotaArgs!(Slices.length, "slices[", "].hideStride, ") ~`);`);
+            }
+            else
+            {
+                enum kind = maxElem(staticMap!(kindOf, Slices));
+                alias Iterator = ZipIterator!(staticMap!(_IteratorOf, Slices));
+                alias Ret = Slice!(Iterator, N, kind);
+                auto structure = Ret._Structure.init;
+                structure[0] = slices[0]._lengths;
+                foreach (i; Iota!(Ret.S))
+                    structure[1][i] = slices[0]._strides[i];
+                return Ret(structure, mixin("Iterator(" ~ _iotaArgs!(Slices.length, "slices[", "]._iterator, ") ~ ")"));
+            }
         }
         else
         {
-            enum kind = maxElem(staticMap!(kindOf, Slices));
-            alias Iterator = ZipIterator!(staticMap!(_IteratorOf, Slices));
-            alias Ret = Slice!(Iterator, N, kind);
-            auto structure = Ret._Structure.init;
-            structure[0] = slices[0]._lengths;
-            foreach (i; Iota!(Ret.S))
-                structure[1][i] = slices[0]._strides[i];
-            return Ret(structure, mixin("Iterator(" ~ _iotaArgs!(Slices.length, "slices[", "]._iterator, ") ~ ")"));
+            return .zip(toSlices!slices);
         }
     }
-    else
-    {
-        return .zip(toSlices!slices);
-    }
 }
 
 ///
@@ -3796,6 +3851,17 @@ pure nothrow version(mir_test) unittest
 
 private enum TotalDim(NdFields...) = [staticMap!(DimensionCount, NdFields)].sum;
 
+private template applyInner(alias fun, size_t N)
+{
+    static if (N == 0)
+        alias applyInner = fun;
+    else
+    {
+        import mir.functional: pipe;
+        alias applyInner = pipe!(zip!true, map!(.applyInner!(fun, N - 1)));
+    }
+}
+
 /++
 Sliding map for vectors.
 Works with packed slices.
@@ -3819,23 +3885,27 @@ template slide(size_t params, alias fun)
         Params:
             slice = An input slice with first dimension pack equals to one (e.g. 1-dimensional for not packed slices).
         Returns:
-            1d-slice composed of `fun(slice[i], ..., slice[i + params - 1])`.
+            1d-slice composed of `F(slice[i], ..., slice[i + params - 1])`, where `F` is self recursion function accross dimensions.
         +/
-        auto slide(Iterator, SliceKind kind)
-            (Slice!(Iterator, 1, kind) slice)
+        auto slide(Iterator, size_t N, SliceKind kind)
+            (Slice!(Iterator, N, kind) slice)
         {
             import core.lifetime: move;
+            static if (N > 1)
+            {
+                return .slide!(params, applyInner!(fun, N - 1))(slice.move.ipack!1.map!slide);
+            }
+            else
             static if (kind == Universal)
             {
                 return .slide!(params, fun)(slice.move.flattened);
             }
             else
             {
-                if (cast(sizediff_t)slice._lengths[0] < sizediff_t(params - 1))
-                    slice._lengths[0] = 0;
-                else
-                    slice._lengths[0] -= params - 1;
-
+                size_t len = slice._lengths[0] - (params - 1);
+                if (sizediff_t(len) <= 0) // overfow
+                    len = 0;
+                slice._lengths[0] = len;
                 alias I = SlideIterator!(Iterator, params, fun);
                 return Slice!I(slice._structure, I(move(slice._iterator)));
             }
@@ -3872,6 +3942,25 @@ version(mir_test) unittest
     assert(sw == [4, 8, 12, 16, 20, 24, 28, 32]);
 }
 
+/++
+ND-use case
++/
+@safe pure nothrow version(mir_test) unittest
+{
+    import mir.functional: recurseTemplatePipe;
+
+    auto data = [4, 5].iota;
+
+    enum dim = 2; // demension count
+    enum factor = 1.0 / 4 ^^ dim;
+    alias scaled = a => a * factor;
+    alias scaleDeepElements  = recurseTemplatePipe!(map, dim, scaled);
+
+    auto sw = scaleDeepElements(data.slide!(3, "a + 2 * b + c"));
+
+    assert(sw == [[6, 7, 8], [11, 12, 13]]);
+}
+
 /++
 Pairwise map for vectors.
 Works with packed slices.

From 7c96ed09bed91b40f15b23b315129c82e488c552 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 20 Nov 2020 14:30:30 +0700
Subject: [PATCH 138/573] fixup

---
 source/mir/ndslice/topology.d | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 41e43675..495bedc0 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2160,6 +2160,7 @@ template stride(size_t factor = 2)
         (Iterator, size_t N, SliceKind kind)
         (Slice!(Iterator, N, kind) slice)
     {
+        import core.lifetime: move;
         static if (N > 1)
         {
             return stride(slice.move.ipack!1.map!(.stride!factor));
@@ -2171,7 +2172,6 @@ template stride(size_t factor = 2)
             slice._lengths[0] /= factor;
             if (rem)
                 slice._lengths[0]++;
-            import core.lifetime: move;
             return Slice!(StrideIterator!(Iterator, factor), 1, kind)(slice._structure, StrideIterator!(Iterator, factor)(move(slice._iterator)));
         }
         else
@@ -2218,6 +2218,15 @@ auto stride(T)(T withAsSlice, ptrdiff_t factor)
     assert(slice.universal.stride(2) == str);
 }
 
+/// ND-compile time
+@safe pure nothrow @nogc version(mir_test) unittest
+{
+    auto slice = iota(4, 6);
+    static immutable str = [[0, 2, 4], [12, 14, 16]];
+    assert(slice.stride!2 == str); // compile time factor
+    assert(slice.stride == str); // default compile time factor is 2
+}
+
 /++
 Reverses order of iteration for all dimensions.
 Params:

From 6319750e5206e7e422fad51a0c17045fc546ee50 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 20 Nov 2020 14:37:55 +0700
Subject: [PATCH 139/573] fixup

---
 source/mir/ndslice/slice.d | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index b93fb927..aaf16858 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -722,7 +722,7 @@ struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_
 
 package(mir):
 
-    enum doUnittest = is(Iterator == int*) && N == 1 && kind == Contiguous;
+    enum doUnittest = is(Iterator == int*) && (N == 1 || N == 2) && kind == Contiguous;
 
     enum hasAccessByRef = __traits(compiles, &_iterator[0]);
 
@@ -1789,7 +1789,7 @@ public:
 
         static if (kind_ != SliceKind.contiguous)
         /// Peforms `popFrontAll` for all dimensions
-        void popFrontAll(T)()
+        void popFrontAll()
         {
             assert(!anyEmpty);
             foreach(d; Iota!N_)
@@ -1808,11 +1808,11 @@ public:
 
         static if (kind_ != SliceKind.contiguous)
         /// Peforms `popBackAll` for all dimensions
-        void popBackAll(T)()
+        void popBackAll()
         {
             assert(!anyEmpty);
             foreach(d; Iota!N_)
-                popBackAll!d;
+                popBack!d;
         }
 
         static if (doUnittest)

From 1602eb6cf6f369d115e7b77c8d1c52560e351e84 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 21 Nov 2020 22:15:31 +0700
Subject: [PATCH 140/573] add dropBorders, slideAlong, withNeighboursSum

---
 source/mir/algorithm/iteration.d | 100 ++++++++-
 source/mir/ndslice/internal.d    |  45 +++-
 source/mir/ndslice/iterator.d    | 112 ++++++++++
 source/mir/ndslice/package.d     |   1 +
 source/mir/ndslice/topology.d    | 367 ++++++++++++++++++++++++-------
 5 files changed, 538 insertions(+), 87 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 2e6760f1..73daf99e 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -10,7 +10,8 @@ $(T2 any, Checks if at least one element satisfy to a predicate.)
 $(T2 cmp, Compares two slices.)
 $(T2 count, Counts elements in a slices according to a predicate.)
 $(T2 each, Iterates all elements.)
-$(T2 eachLower, Iterates lower triangle of matrix.)
+$(T2 each, Iterates all elements.)
+$(T2 eachOnBorder, Iterates elementes on tensors borders and corners.)
 $(T2 eachUploPair, Iterates upper and lower pairs of elements in square matrix.)
 $(T2 eachUpper, Iterates upper triangle of matrix.)
 $(T2 equal, Compares two slices for equality.)
@@ -930,6 +931,103 @@ void eachImpl(alias fun, Slices...)(scope Slices slices)
     while(!slices[0].empty);
 }
 
+/++
+The call `each!(fun)(slice1, ..., sliceN)`
+evaluates `fun` for each set of elements `x1, ..., xN` in
+the borders of `slice1, ..., sliceN` respectively.
+
+`each` allows to iterate multiple slices in the lockstep.
+
+Params:
+    fun = A function.
+Note:
+    $(NDSLICEREF dynamic, transposed) and
+    $(NDSLICEREF topology, pack) can be used to specify dimensions.
++/
+template eachOnBorder(alias fun)
+{
+    import mir.functional: naryFun;
+    static if (__traits(isSame, naryFun!fun, fun))
+    /++
+    Params:
+        slices = One or more slices.
+    +/
+    @optmath void eachOnBorder(Slices...)(Slices slices)
+        if (allSatisfy!(isSlice, Slices))
+    {
+        import mir.ndslice.traits: isContiguousSlice;
+        static if (Slices.length > 1)
+            slices.checkShapesMatch;
+        if (!slices[0].anyEmpty)
+        {
+            alias N = DimensionCount!(Slices[0]);
+            static if (N == 1)
+            {
+                fun(frontOf!slices);
+                if (slices[0].length > 1)
+                    fun(backOf!slices);
+            }
+            else
+            static if (anySatisfy!(isContiguousSlice, Slices))
+            {
+                import mir.ndslice.topology: canonical;
+                template f(size_t i)
+                {
+                    static if (isContiguousSlice!(Slices[i]))
+                        auto f () { return canonical(slices[i]); }
+                    else
+                        alias f = slices[i];
+                }
+                eachOnBorder(staticMap!(f, Iota!(Slices.length)));
+            }
+            else
+            {
+                foreach (dimension; Iota!N)
+                {
+                    eachImpl!fun(frontOfD!(dimension, slices));
+                    foreach_reverse(ref slice; slices)
+                        slice.popFront!dimension;
+                    if (slices[0].empty!dimension)
+                        return;
+                    eachImpl!fun(backOfD!(dimension, slices));
+                    foreach_reverse(ref slice; slices)
+                        slice.popBack!dimension;
+                    if (slices[0].empty!dimension)
+                        return;
+                }
+            }
+        }
+    }
+    else
+        alias eachOnBorder = .eachOnBorder!(naryFun!fun);
+}
+
+///
+@safe pure nothrow 
+version(mir_test) unittest
+{
+    import mir.ndslice.allocation : slice;
+    import mir.ndslice.topology : repeat, iota;
+
+    auto sl = [3, 4].iota.slice;
+    auto zeros = repeat(0, [3, 4]);
+
+    sl.eachOnBorder!"a = b"(zeros);
+
+    assert(sl == 
+        [[0, 0, 0 ,0],
+         [0, 5, 6, 0],
+         [0, 0, 0 ,0]]);
+    
+    sl.eachOnBorder!"a = 1";
+    sl[0].eachOnBorder!"a = 2";
+
+    assert(sl == 
+        [[2, 1, 1, 2],
+         [1, 5, 6, 1],
+         [1, 1, 1 ,1]]);
+}
+
 /++
 The call `each!(fun)(slice1, ..., sliceN)`
 evaluates `fun` for each set of elements `x1, ..., xN` in
diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index 0d4faf44..45f6d8db 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -349,15 +349,56 @@ package(mir) template frontOf(args...)
         enum frontOf = args;
     else
     {
-        alias arg = args[0];
         @optmath @property auto ref ls()()
         {
-            return arg.front;
+            return args[0].front;
         }
         alias frontOf = AliasSeq!(ls, frontOf!(args[1..$]));
     }
 }
 
+package(mir) template backOf(args...)
+{
+    static if (args.length == 0)
+        enum backOf = args;
+    else
+    {
+        @optmath @property auto ref ls()()
+        {
+            return args[0].back;
+        }
+        alias backOf = AliasSeq!(ls, backOf!(args[1..$]));
+    }
+}
+
+package(mir) template frontOfD(size_t dimension, args...)
+{
+    static if (args.length == 0)
+        enum frontOfD = args;
+    else
+    {
+        @optmath @property auto ref ls()()
+        {
+            return args[0].front!dimension;
+        }
+        alias frontOfD = AliasSeq!(ls, frontOfD!(dimension, args[1..$]));
+    }
+}
+
+package(mir) template backOfD(size_t dimension, args...)
+{
+    static if (args.length == 0)
+        enum backOfD = args;
+    else
+    {
+        @optmath @property auto ref ls()()
+        {
+            return args[0].back!dimension;
+        }
+        alias backOfD = AliasSeq!(ls, backOfD!(dimension, args[1..$]));
+    }
+}
+
 package(mir) template frontOfDim(size_t dim, args...)
 {
     static if (args.length == 0)
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 4d6ed1fb..6fe737e7 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -15,6 +15,7 @@ $(T2 IndexIterator, $(SUBREF topology, indexed))
 $(T2 IotaIterator, $(SUBREF topology, iota))
 $(T2 MapIterator, $(SUBREF topology, map))
 $(T2 MemberIterator, $(SUBREF topology, member))
+$(T2 NeighboursIterator, $(SUBREF topology, withNeighboursSum))
 $(T2 RetroIterator, $(SUBREF topology, retro))
 $(T2 SliceIterator, $(SUBREF topology, map) in composition with $(LREF MapIterator) for packed slices.)
 $(T2 SlideIterator, $(SUBREF topology, diff), $(SUBREF topology, pairwise), and $(SUBREF topology, slide).)
@@ -1174,6 +1175,117 @@ auto _vmapIterator(Iterator, Fun)(Iterator iterator, Fun fun)
     assert(y == result);
 }
 
+/++
+`NeighboursIterator` is used by $(SUBREF topology, map).
++/
+struct NeighboursIterator(Iterator, size_t N, alias _fun, bool around)
+{
+    import std.meta: AliasSeq;
+@optmath:
+    ///
+    Iterator _iterator;
+    static if (N)
+        Iterator[2][N] _neighbours;
+    else alias _neighbours = AliasSeq!();
+
+    ///
+    auto lightConst()() const @property
+    {
+        LightConstOf!Iterator[2][N] neighbours;
+        foreach (i; 0 .. N)
+        {
+            neighbours[i][0] = .lightConst(_neighbours[i][0]);
+            neighbours[i][1] = .lightConst(_neighbours[i][1]);
+        }
+        return NeighboursIterator!(LightConstOf!Iterator, N, _fun, around)(.lightConst(_iterator), neighbours);
+    }
+
+    ///
+    auto lightImmutable()() immutable @property
+    {
+        LightImmutableOf!Iterator[2][N] neighbours;
+        foreach (i; 0 .. N)
+        {
+            neighbours[i][0] = .lightImmutable(_neighbours[i][0]);
+            neighbours[i][1] = .lightImmutable(_neighbours[i][1]);
+        }
+        return NeighboursIterator!(LightImmutableOf!Iterator, N, _fun, around)(.lightImmutable(_iterator), neighbours);
+    }
+
+    import mir.functional: RefTuple, _ref;
+
+    private alias RA = Unqual!(typeof(_fun(_iterator[-1], _iterator[+1])));
+    private alias Result = RefTuple!(_zip_types!Iterator, RA);
+
+    auto ref opUnary(string op : "*")()
+    {
+        return opIndex(0);
+    }
+
+    auto ref opIndex(ptrdiff_t index) scope
+    {
+        static if (around)
+            RA result = _fun(_iterator[index - 1], _iterator[index + 1]);
+
+        foreach (i; Iota!N)
+        {
+            static if (i == 0 && !around)
+                RA result = _fun(_neighbours[i][0][index], _neighbours[i][1][index]);
+            else
+                result = _fun(result, _fun(_neighbours[i][0][index], _neighbours[i][1][index]));
+        }
+        static if (__traits(compiles, &_iterator[index]))
+            return Result(_ref(_iterator[index]), result);
+        else
+            return Result(_iterator[index], result);
+    }
+    
+    void opUnary(string op)() scope
+        if (op == "--" || op == "++")
+    {
+        mixin(op ~ "_iterator;");
+        foreach (i; Iota!N)
+        {
+            mixin(op ~ "_neighbours[i][0];");
+            mixin(op ~ "_neighbours[i][1];");
+        }
+    }
+
+    void opOpAssign(string op)(ptrdiff_t index) scope
+        if (op == "-" || op == "+")
+    {
+
+        mixin("_iterator " ~ op ~ "= index;");
+        foreach (i; Iota!N)
+        {
+            mixin("_neighbours[i][0] " ~ op ~ "= index;");
+            mixin("_neighbours[i][1] " ~ op ~ "= index;");
+        }
+    }
+
+    auto opBinary(string op)(ptrdiff_t index)
+        if (op == "+" || op == "-")
+    {
+        auto ret = this;
+        mixin(`ret ` ~ op ~ `= index;`);
+        return ret;
+    }
+
+    ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const
+    { return this._iterator - right._iterator; }
+
+    bool opEquals()(scope ref const typeof(this) right) scope const
+    { return this._iterator == right._iterator; }
+
+    ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const
+    {
+        static if (isPointer!Iterator)
+            return this._iterator - right._iterator;
+        else
+            return this._iterator.opCmp(right._iterator);
+    }
+}
+
 /++
 `MemberIterator` is used by $(SUBREF topology, member).
 +/
diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index de4ec097..f56529e7 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -164,6 +164,7 @@ $(TR $(TDNW $(SUBMODULE topology) $(BR)
         $(SUBREF topology, ReshapeError)
         $(SUBREF topology, retro)
         $(SUBREF topology, slide)
+        $(SUBREF topology, slideAlong)
         $(SUBREF topology, squeeze)
         $(SUBREF topology, stairs)
         $(SUBREF topology, stride)
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 495bedc0..36abd530 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -17,15 +17,17 @@ $(T2 repeat, Slice with identical values)
 $(BOOKTABLE $(H2 Shape Selectors),
 $(TR $(TH Function Name) $(TH Description))
 
-$(T2 squeeze, New slice view of an n-dimensional slice with dimension removed)
-$(T2 unsqueeze, New slice view of an n-dimensional slice with a dimension added)
+$(T2 blocks, n-dimensional slice composed of n-dimensional non-overlapping blocks. If the slice has two dimensions, it is a block matrix.)
 $(T2 diagonal, 1-dimensional slice composed of diagonal elements)
+$(T2 dropBorders, Drops borders for all dimensions.)
 $(T2 reshape, New slice view with changed dimensions)
-$(T2 blocks, n-dimensional slice composed of n-dimensional non-overlapping blocks. If the slice has two dimensions, it is a block matrix.)
+$(T2 squeeze, New slice view of an n-dimensional slice with dimension removed)
+$(T2 unsqueeze, New slice view of an n-dimensional slice with a dimension added)
 $(T2 windows, n-dimensional slice of n-dimensional overlapping windows. If the slice has two dimensions, it is a sliding window.)
 
 )
 
+
 $(BOOKTABLE $(H2 Subspace Selectors),
 $(TR $(TH Function Name) $(TH Description))
 
@@ -76,16 +78,17 @@ $(T2 orthogonalReduceField, Functional deep-element wise reduce of a slice compo
 $(T2 pairwise, Pairwise map for vectors.)
 $(T2 pairwiseMapSubSlices, Maps pairwise index pairs to subslices.)
 $(T2 retro, Reverses order of iteration for all dimensions.)
-$(T2 slide, Sliding map for vectors.)
+$(T2 slide, Lazy convolution for tensors.)
+$(T2 slideAlong, Lazy convolution for tensors.)
 $(T2 stairs, Two functions to pack, unpack, and iterate triangular and symmetric matrix storage.)
 $(T2 stride, Strides 1-dimensional slice.)
 $(T2 subSlices, Maps index pairs to subslices.)
 $(T2 triplets, Constructs a lazy view of triplets with `left`, `center`, and `right` members. The topology is usefull for Math and Physics.)
 $(T2 unzip, Selects a slice from a zipped slice.)
+$(T2 withNeighboursSum, Zip view of elements packed with sum of their neighbours.)
 $(T2 zip, Zips slices into a slice of refTuples.)
 )
 
-
 Subspace selectors serve to generalize and combine other selectors easily.
 For a slice of `Slice!(Iterator, N, kind)` type `slice.pack!K` creates a slice of
 slices of `Slice!(kind, [N - K, K], Iterator)` type by packing
@@ -96,14 +99,12 @@ $(LREF evertPack).
 Examples of use of subspace selectors are available for selectors,
 $(SUBREF slice, Slice.shape), and $(SUBREF slice, Slice.elementCount).
 
-
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 Authors: Ilya Yaroshenko, John Michael Hall, Shigeki Karita (original numir code)
 
 Sponsors: Part of this work has been sponsored by $(LINK2 http://symmetryinvestments.com, Symmetry Investments) and Kaleidic Associates.
 
-
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
@@ -111,7 +112,6 @@ T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
 +/
 module mir.ndslice.topology;
 
-
 import mir.internal.utility;
 import mir.math.common: optmath;
 import mir.ndslice.field;
@@ -656,7 +656,6 @@ evertPack(Iterator, size_t M, SliceKind innerKind, size_t N, SliceKind outerKind
          == Slice!(SliceIterator!(int*, 2, Universal), 1)));
 }
 
-
 ///
 @safe pure nothrow @nogc
 version(mir_test) unittest
@@ -2106,7 +2105,6 @@ auto cycle(size_t loopLength, T)(T[] array, size_t length)
     return cycle!loopLength(array.sliced, length);
 }
 
-
 /// ditto
 auto cycle(size_t loopLength, T)(T withAsSlice, size_t length)
     if (hasAsSlice!T)
@@ -2290,7 +2288,6 @@ auto retro(T)(T withAsSlice)
     return retro(withAsSlice.asSlice);
 }
 
-
 /// ditto
 auto retro(Range)(Range r)
     if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
@@ -2594,7 +2591,6 @@ bytegroup
     return typeof(return)(structure, BytegroupIterator!(Iterator, group, DestinationType)(slice._iterator.move));
 }
 
-
 /// ditto
 auto bytegroup(size_t pack, DestinationType, T)(T[] array)
 {
@@ -2674,7 +2670,7 @@ template map(fun...)
     import mir.functional: adjoin, naryFun, pipe;
     static if (fun.length == 1)
     {
-        static if (__traits(isSame, naryFun!(fun[0]), fun[0]) && !__traits(isSame, naryFun!"a", fun[0]))
+        static if (__traits(isSame, naryFun!(fun[0]), fun[0]))
         {
             alias f = fun[0];
         @optmath:
@@ -2691,6 +2687,7 @@ template map(fun...)
                 import core.lifetime: move;
                 alias MIterator = typeof(_mapIterator!f(slice._iterator));
                 import mir.ndslice.traits: isIterator;
+                alias testIter = typeof(MIterator.init[0]);
                 static assert(isIterator!MIterator, "mir.ndslice.map: probably the lambda function contains a compile time bug.");
                 return Slice!(MIterator, N, kind)(slice._structure, _mapIterator!f(slice._iterator.move));
             }
@@ -2718,37 +2715,7 @@ template map(fun...)
                 return MapRange!(f, ImplicitlyUnqual!Range)(forward!r);
             }
         }
-        else
-        static if (__traits(isSame, naryFun!"a", fun[0]))
-        {
-            ///
-            @optmath auto map(Iterator, size_t N, SliceKind kind)
-                (Slice!(Iterator, N, kind) slice)
-            {
-                return slice;
-            }
-
-            /// ditto
-            auto map(T)(T[] array)
-            {
-                return array.sliced;
-            }
-
-            /// ditto
-            auto map(T)(T withAsSlice)
-                if (hasAsSlice!T)
-            {
-                return withAsSlice.asSlice;
-            }
-
-            /// ditto
-            ImplicitlyUnqual!Range map(Range)(Range r)
-                if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
-            {
-                return r;
-            }
-        }
-        else alias map = .map!(naryFun!fun);
+        else alias map = .map!(staticMap!(naryFun, fun));
     }
     else alias map = .map!(adjoin!fun);
 }
@@ -3872,54 +3839,137 @@ private template applyInner(alias fun, size_t N)
 }
 
 /++
-Sliding map for vectors.
-Works with packed slices.
+Lazy convolution for tensors.
 
-Suitable for simple convolution algorithms.
+Suitable for advanced convolution algorithms.
 
 Params:
-    params = windows length.
-    fun = map functions with `params` arity.
-See_also: $(LREF pairwise), $(LREF diff).
+    params = convolution windows length.
+    fun = one dimensional convolution function with `params` arity.
+    SDimensions = dimensions to perform lazy convolution along. Negative dimensions are supported.
+See_also: $(LREF slide), $(LREF pairwise), $(LREF diff).
 +/
-template slide(size_t params, alias fun)
-    if (params <= 'z' - 'a' + 1)
+template slideAlong(size_t params, alias fun, SDimensions...)
+    if (params <= 'z' - 'a' + 1 && SDimensions.length > 0)
 {
     import mir.functional: naryFun;
 
-    static if (params > 1 && __traits(isSame, naryFun!fun, fun))
+    static if (allSatisfy!(isSizediff_t, SDimensions) && params > 1 && __traits(isSame, naryFun!fun, fun))
     {
     @optmath:
         /++
-        Params:
-            slice = An input slice with first dimension pack equals to one (e.g. 1-dimensional for not packed slices).
-        Returns:
-            1d-slice composed of `F(slice[i], ..., slice[i + params - 1])`, where `F` is self recursion function accross dimensions.
+        Params: slice = ndslice or array
+        Returns: lazy convolution result
         +/
-        auto slide(Iterator, size_t N, SliceKind kind)
+        auto slideAlong(Iterator, size_t N, SliceKind kind)
             (Slice!(Iterator, N, kind) slice)
         {
             import core.lifetime: move;
-            static if (N > 1)
+            static if (N > 1 && kind == Contiguous)
             {
-                return .slide!(params, applyInner!(fun, N - 1))(slice.move.ipack!1.map!slide);
+                return slideAlong(slice.move.canonical);
             }
             else
-            static if (kind == Universal)
+            static if (N == 1 && kind == Universal)
             {
-                return .slide!(params, fun)(slice.move.flattened);
+                return slideAlong(slice.move.flattened);
             }
             else
             {
-                size_t len = slice._lengths[0] - (params - 1);
+                alias Dimensions = staticMap!(ShiftNegativeWith!N, SDimensions);
+                enum dimension = Dimensions[$ - 1];
+                size_t len = slice._lengths[dimension] - (params - 1);
                 if (sizediff_t(len) <= 0) // overfow
                     len = 0;
-                slice._lengths[0] = len;
-                alias I = SlideIterator!(Iterator, params, fun);
-                return Slice!I(slice._structure, I(move(slice._iterator)));
+                slice._lengths[dimension] = len;
+                static if (dimension + 1 == N || kind == Universal)
+                {
+                    alias I = SlideIterator!(Iterator, params, fun);
+                    auto ret = Slice!(I, N, kind)(slice._structure, I(move(slice._iterator)));
+                }
+                else
+                {
+                    alias Z = ZipIterator!(Repeat!(params, Iterator));
+                    Z z;
+                    foreach_reverse (p; Iota!(1, params))
+                        z._iterators[p] = slice._iterator + slice._strides[dimension] * p;
+                    z._iterators[0] = move(slice._iterator);
+                    alias M = MapIterator!(Z, fun);
+                    auto ret = Slice!(M, N, kind)(slice._structure, M(move(z)));
+                }
+                static if (Dimensions.length == 1)
+                {
+                    return ret;
+                }
+                else
+                {
+                    return .slideAlong!(params, fun, Dimensions[0 .. $ - 1])(ret);
+                }
             }
         }
 
+        /// ditto
+        auto slideAlong(S)(S[] slice)
+        {
+            return slideAlong(slice.sliced);
+        }
+
+        /// ditto
+        auto slideAlong(S)(S slice)
+            if (hasAsSlice!S)
+        {
+            return slideAlong(slice.asSlice);
+        }
+    }
+    else
+    static if (params == 1)
+        alias slideAlong = .map!(naryFun!fun);
+    else alias slideAlong = .slideAlong!(params, naryFun!fun, staticMap!(toSizediff_t, SDimensions));
+}
+
+///
+@safe pure nothrow @nogc version(mir_test) unittest
+{
+    auto data = [4, 5].iota;
+
+    alias scaled = a => a * 0.25;
+
+    auto v = data.slideAlong!(3, "a + 2 * b + c", 0).map!scaled;
+    auto h = data.slideAlong!(3, "a + 2 * b + c", 1).map!scaled;
+
+    assert(v == [4, 5].iota[1 .. $ - 1, 0 .. $]);
+    assert(h == [4, 5].iota[0 .. $, 1 .. $ - 1]);
+}
+
+/++
+Lazy convolution for tensors.
+
+Suitable for simple convolution algorithms.
+
+Params:
+    params = windows length.
+    fun = one dimensional convolution function with `params` arity.
+See_also: $(LREF slideAlong), $(LREF withNeighboursSum), $(LREF pairwise), $(LREF diff).
++/
+template slide(size_t params, alias fun)
+    if (params <= 'z' - 'a' + 1)
+{
+    import mir.functional: naryFun;
+
+    static if (params > 1 && __traits(isSame, naryFun!fun, fun))
+    {
+    @optmath:
+        /++
+        Params: slice = ndslice or array
+        Returns: lazy convolution result
+        +/
+        auto slide(Iterator, size_t N, SliceKind kind)
+            (Slice!(Iterator, N, kind) slice)
+        {
+            import core.lifetime: move;
+            return slice.move.slideAlong!(params, fun, Iota!N);
+        }
+
         /// ditto
         auto slide(S)(S[] slice)
         {
@@ -3954,44 +4004,53 @@ version(mir_test) unittest
 /++
 ND-use case
 +/
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_test) unittest
 {
-    import mir.functional: recurseTemplatePipe;
-
     auto data = [4, 5].iota;
 
-    enum dim = 2; // demension count
-    enum factor = 1.0 / 4 ^^ dim;
+    enum factor = 1.0 / 4 ^^ data.shape.length;
     alias scaled = a => a * factor;
-    alias scaleDeepElements  = recurseTemplatePipe!(map, dim, scaled);
 
-    auto sw = scaleDeepElements(data.slide!(3, "a + 2 * b + c"));
+    auto sw = data.slide!(3, "a + 2 * b + c").map!scaled;
 
-    assert(sw == [[6, 7, 8], [11, 12, 13]]);
+    assert(sw == [4, 5].iota[1 .. $ - 1, 1 .. $ - 1]);
 }
 
 /++
-Pairwise map for vectors.
-Works with packed slices.
+Pairwise map for tensors.
+
+The computation is performed on request, when the element is accessed.
 
 Params:
     fun = function to accumulate
     lag = an integer indicating which lag to use
 Returns: lazy ndslice composed of `fun(a_n, a_n+1)` values.
 
-See_also: $(LREF slide), $(LREF subSlices).
+See_also: $(LREF slide), $(LREF slideAlong), $(LREF subSlices).
 +/
 alias pairwise(alias fun, size_t lag = 1) = slide!(lag + 1, fun);
 
 ///
-version(mir_test) unittest
+@safe pure nothrow version(mir_test) unittest
 {
     assert([2, 4, 3, -1].sliced.pairwise!"a + b" == [6, 7, 2]);
 }
 
+/// N-dimensional
+@safe pure nothrow
+version(mir_test) unittest
+{
+    // performs pairwise along each dimension
+    // 0 1 2 3
+    // 4 5 6 7
+    // 8 9 10 11
+    assert([3, 4].iota.pairwise!"a + b" == [[10, 14, 18], [26, 30, 34]]);
+}
+
 /++
-Differences between vector elements.
-Works with packed slices.
+Differences between tensor elements.
+
+The computation is performed on request, when the element is accessed.
 
 Params:
     lag = an integer indicating which lag to use
@@ -4007,6 +4066,24 @@ version(mir_test) unittest
     assert([2, 4, 3, -1].sliced.diff == [2, -1, -4]);
 }
 
+/// N-dimensional
+@safe pure nothrow @nogc
+version(mir_test) unittest
+{
+    // 0 1 2 3
+    // 4 5 6 7     =>
+    // 8 9 10 11
+    
+    // 1 1 1
+    // 1 1 1      =>
+    // 1 1 1
+
+    // 0 0 0
+    // 0 0 0
+
+    assert([3, 4].iota.diff == repeat(0, [2, 3]));
+}
+
 /// packed slices
 version(mir_test) unittest
 {
@@ -4024,6 +4101,132 @@ version(mir_test) unittest
         [1, 1, 1]]);
 }
 
+/++
+Drops borders for all dimensions.
+
+Params:
+    slice = ndslice
+Returns:
+    Tensors with striped borders
+See_also:
+    $(LREF universal),
+    $(LREF assumeCanonical),
+    $(LREF assumeContiguous).
++/
+Slice!(Iterator, N, N == 1 ? Contiguous : Canonical, Labels)
+    dropBorders
+    (Iterator, size_t N, SliceKind kind, Labels...)
+    (Slice!(Iterator, N, kind, Labels) slice)
+    if (kind == Contiguous || kind == Canonical)
+{
+    import core.lifetime: move;
+    auto ret = slice.move.canonical;
+    ret.popFrontAll;
+    ret.popBackAll;
+    return ret;
+}
+
+///
+version(mir_test) unittest
+{
+    assert([4, 5].iota.dropBorders == [[6, 7, 8], [11, 12, 13]]);
+}
+
+/++
+Lazy zip view of elements packed with sum of their neighbours.
+
+Params:
+    fun = neighbours accumulation function.
+See_also: $(LREF slide), $(LREF slideAlong).
++/
+template withNeighboursSum(alias fun = "a + b")
+{
+    import mir.functional: naryFun;
+
+    static if (__traits(isSame, naryFun!fun, fun))
+    {
+    @optmath:
+        /++
+        Params:
+            slice = ndslice or array
+        Returns:
+            Lazy zip view of elements packed with sum of their neighbours.
+        +/
+        auto withNeighboursSum(Iterator, size_t N, SliceKind kind)
+            (Slice!(Iterator, N, kind) slice)
+        {
+            import core.lifetime: move;
+            static if (N > 1 && kind == Contiguous)
+            {
+                return withNeighboursSum(slice.move.canonical);
+            }
+            else
+            static if (N == 1 && kind == Universal)
+            {
+                return withNeighboursSum(slice.move.flattened);
+            }
+            else
+            {
+                enum around = kind != Universal;
+                alias Z = NeighboursIterator!(Iterator, N - around, fun, around);
+
+                size_t shift;
+                foreach (dimension; Iota!N)
+                {
+                    slice._lengths[dimension] -= 2;
+                    if (sizediff_t(slice._lengths[dimension]) <= 0) // overfow
+                        slice._lengths[dimension] = 0;
+                    shift += slice._stride!dimension;
+                }                
+
+                Z z;
+                z._iterator = move(slice._iterator);
+                z._iterator += shift;
+                foreach (dimension; Iota!(N - around))
+                {
+                    z._neighbours[dimension][0] = z._iterator - slice._strides[dimension];
+                    z._neighbours[dimension][1] = z._iterator + slice._strides[dimension];
+                }
+                return Slice!(Z, N, kind)(slice._structure, move(z));
+            }
+        }
+
+        /// ditto
+        auto withNeighboursSum(S)(S[] slice)
+        {
+            return withNeighboursSum(slice.sliced);
+        }
+
+        /// ditto
+        auto withNeighboursSum(S)(S slice)
+            if (hasAsSlice!S)
+        {
+            return withNeighboursSum(slice.asSlice);
+        }
+    }
+    else alias withNeighboursSum = .withNeighboursSum!(naryFun!fun);
+}
+
+///
+@safe pure nothrow @nogc version(mir_test) unittest
+{
+    import mir.ndslice.allocation: slice;
+    import mir.algorithm.iteration: all;
+
+    auto wn = [4, 5].iota.withNeighboursSum;
+    assert(wn.all!"a[0] == a[1] * 0.25");
+    assert(wn.map!"a" == wn.map!"b * 0.25");
+}
+
+@safe pure nothrow @nogc version(mir_test) unittest
+{
+    import mir.ndslice.allocation: slice;
+    import mir.algorithm.iteration: all;
+
+    auto wn = [4, 5].iota.withNeighboursSum.universal;
+    assert(wn.all!"a[0] == a[1] * 0.25");
+    assert(wn.map!"a" == wn.map!"b * 0.25");
+}
 
 /++
 Cartesian product.
@@ -4106,8 +4309,6 @@ version(mir_test) unittest
         ]]);
 }
 
-
-
 /++
 $(LINK2 https://en.wikipedia.org/wiki/Kronecker_product,  Kronecker product).
 
@@ -4565,7 +4766,6 @@ version(mir_test) unittest
     a.popFront;
     assert(a.front.front == iota([3], 1, 20));
 
-
     //  ----------------
     // |  0  1  2  3  4 |
     // |  5  6  7  8  9 |
@@ -5356,7 +5556,6 @@ unittest
     assert([3].iota.unsqueeze(-1) == [3, 1].iota);
     assert([3].iota.unsqueeze!(-1) == [3, 1].iota);
 
-
     assert([3].iota.universal.unsqueeze(-1) == [3, 1].iota);
     assert([3].iota.universal.unsqueeze!(-1) == [3, 1].iota);
     assert([3, 4].iota.unsqueeze(-1) == [3, 4, 1].iota);

From 613784cc60936bcc12a4fa525037b6a1b0cb398f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 23 Nov 2020 11:43:01 +0700
Subject: [PATCH 141/573] add Chequer

---
 source/mir/algorithm/iteration.d | 114 +++++++++++++++++++++++++++----
 source/mir/ndslice/internal.d    |  15 ++++
 source/mir/ndslice/package.d     |   1 +
 source/mir/ndslice/topology.d    |  14 ++--
 4 files changed, 125 insertions(+), 19 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 73daf99e..64910a70 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -9,8 +9,8 @@ $(T2 all, Checks if all elements satisfy to a predicate.)
 $(T2 any, Checks if at least one element satisfy to a predicate.)
 $(T2 cmp, Compares two slices.)
 $(T2 count, Counts elements in a slices according to a predicate.)
-$(T2 each, Iterates all elements.)
-$(T2 each, Iterates all elements.)
+$(T2 each, Iterates elements.)
+$(T2 eachLower, Iterates lower triangle of matrix.)
 $(T2 eachOnBorder, Iterates elementes on tensors borders and corners.)
 $(T2 eachUploPair, Iterates upper and lower pairs of elements in square matrix.)
 $(T2 eachUpper, Iterates upper triangle of matrix.)
@@ -28,6 +28,7 @@ $(T2 minmaxPos, Finds backward indices of the minimum and the maximum.)
 $(T2 minPos, Finds backward index of the minimum.)
 $(T2 nBitsToCount, Сount bits until set bit count is reached.)
 $(T2 reduce, Accumulates all elements.)
+$(T2 Chequer, Chequer color selector to work with $(LREF each) .)
 $(T2 uniq, Iterates over the unique elements in a range or an ndslice, which is assumed sorted.)
 )
 
@@ -64,6 +65,43 @@ import std.meta;
 import std.range.primitives: isInputRange, isBidirectionalRange, isInfinite, isForwardRange, ElementType;
 import std.traits;
 
+/++
+Chequer color selector to work with $(LREF each)
++/
+enum Chequer : bool
+{
+    /// Main diagonal color
+    black,
+    /// First sub-diagonal color
+    red,
+}
+
+///
+version(mir_test) unittest
+{
+    import mir.ndslice.allocation: slice;
+    auto s = [5, 4].slice!int;
+
+    Chequer.black.each!"a = 1"(s);
+    assert(s == [
+        [1, 0, 1, 0],
+        [0, 1, 0, 1],
+        [1, 0, 1, 0],
+        [0, 1, 0, 1],
+        [1, 0, 1, 0],
+    ]);
+
+    Chequer.red.each!((ref b) => b = 2)(s);
+    assert(s == [
+        [1, 2, 1, 2],
+        [2, 1, 2, 1],
+        [1, 2, 1, 2],
+        [2, 1, 2, 1],
+        [1, 2, 1, 2],
+    ]);
+
+}
+
 @optmath:
 
 /+
@@ -931,6 +969,34 @@ void eachImpl(alias fun, Slices...)(scope Slices slices)
     while(!slices[0].empty);
 }
 
+void chequerEachImpl(alias fun, Slices...)(Chequer color, scope Slices slices)
+{
+    foreach(ref slice; slices)
+        assert(!slice.empty);
+    static if (DimensionCount!(Slices[0]) == 1)
+    {
+        if (color)
+        {
+            foreach_reverse(i; Iota!(Slices.length))
+                slices[i].popFront;
+            if (slices[0].empty)
+                return;
+        }
+        eachImpl!fun(strideOf!slices);
+    }
+    else
+    {
+        do
+        {
+            .chequerEachImpl!fun(color, frontOf!slices);
+            color = cast(Chequer)!color;
+            foreach_reverse(i; Iota!(Slices.length))
+                slices[i].popFront;
+        }
+        while(!slices[0].empty);
+    }
+}
+
 /++
 The call `each!(fun)(slice1, ..., sliceN)`
 evaluates `fun` for each set of elements `x1, ..., xN` in
@@ -1046,25 +1112,43 @@ template each(alias fun)
 {
     import mir.functional: naryFun;
     static if (__traits(isSame, naryFun!fun, fun))
-    /++
-    Params:
-        slices = One or more slices, ranges, and arrays.
-    +/
-    @optmath auto each(Slices...)(scope Slices slices)
-        if (Slices.length)
     {
-        static if (Slices.length > 1)
-            slices.checkShapesMatch;
-        static if (areAllContiguousSlices!Slices)
+        /++
+        Params:
+            slices = One or more slices, ranges, and arrays.
+        +/
+        @optmath auto each(Slices...)(scope Slices slices)
+            if (Slices.length && !is(Slices[0] : Chequer))
         {
-            import mir.ndslice.topology: flattened;
-            .each!fun(allFlattened!(allLightScope!slices));
+            static if (Slices.length > 1)
+                slices.checkShapesMatch;
+            static if (areAllContiguousSlices!Slices)
+            {
+                import mir.ndslice.topology: flattened;
+                .each!fun(allFlattened!(allLightScope!slices));
+            }
+            else
+            {
+                if (slices[0].anyEmpty)
+                    return;
+                eachImpl!fun(allLightScope!slices);
+            }
         }
-        else
+
+        /++
+        Iterates elements of selected $(LREF Chequer) color.
+        Params:
+            color = $(LREF Chequer).
+            slices = One or more slices.
+        +/
+        @optmath auto each(Slices...)(Chequer color, scope Slices slices)
+            if (Slices.length && allSatisfy!(isSlice, Slices))
         {
+            static if (Slices.length > 1)
+                slices.checkShapesMatch;
             if (slices[0].anyEmpty)
                 return;
-            eachImpl!fun(allLightScope!slices);
+            chequerEachImpl!fun(color, allLightScope!slices);
         }
     }
     else
diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index 45f6d8db..b0207e78 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -343,6 +343,21 @@ pure nothrow version(mir_test) unittest
     assert(lengthsProduct([3, 4, 5]) == 60);
 }
 
+package(mir) template strideOf(args...)
+{
+    static if (args.length == 0)
+        enum strideOf = args;
+    else
+    {
+        @optmath @property auto ref ls()()
+        {
+            import mir.ndslice.topology: stride;
+            return stride(args[0]);
+        }
+        alias strideOf = AliasSeq!(ls, strideOf!(args[1..$]));
+    }
+}
+
 package(mir) template frontOf(args...)
 {
     static if (args.length == 0)
diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index f56529e7..bd4ab8d5 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -145,6 +145,7 @@ $(TR $(TDNW $(SUBMODULE topology) $(BR)
         $(SUBREF topology, cycle)
         $(SUBREF topology, diagonal)
         $(SUBREF topology, diff)
+        $(SUBREF topology, dropBorders)
         $(SUBREF topology, evertPack)
         $(SUBREF topology, flattened)
         $(SUBREF topology, indexed)
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 36abd530..3a1bba9f 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -4113,14 +4113,20 @@ See_also:
     $(LREF assumeCanonical),
     $(LREF assumeContiguous).
 +/
-Slice!(Iterator, N, N == 1 ? Contiguous : Canonical, Labels)
+Slice!(Iterator, N, N > 1 && kind == Contiguous ? Canonical : Universal, Labels)
     dropBorders
     (Iterator, size_t N, SliceKind kind, Labels...)
     (Slice!(Iterator, N, kind, Labels) slice)
-    if (kind == Contiguous || kind == Canonical)
 {
-    import core.lifetime: move;
-    auto ret = slice.move.canonical;
+    static if (N > 1 && kind == Contiguous)
+    {
+        import core.lifetime: move;
+        auto ret = slice.move.canonical;
+    }
+    else
+    {
+        alias ret = slice;
+    }
     ret.popFrontAll;
     ret.popBackAll;
     return ret;

From a7809422987ed26d128a58ccc6eaef52d1482496 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 23 Nov 2020 11:46:48 +0700
Subject: [PATCH 142/573] fixup

---
 source/mir/ndslice/topology.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 3a1bba9f..6d36c3a0 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -4113,7 +4113,7 @@ See_also:
     $(LREF assumeCanonical),
     $(LREF assumeContiguous).
 +/
-Slice!(Iterator, N, N > 1 && kind == Contiguous ? Canonical : Universal, Labels)
+Slice!(Iterator, N, N > 1 && kind == Contiguous ? Canonical : kind, Labels)
     dropBorders
     (Iterator, size_t N, SliceKind kind, Labels...)
     (Slice!(Iterator, N, kind, Labels) slice)

From 2fa78ddb64d5343ae97a8d5acb0012a37d2ae558 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 24 Nov 2020 13:22:01 +0700
Subject: [PATCH 143/573] update example

---
 source/mir/parse.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/parse.d b/source/mir/parse.d
index deb3c990..cc9b59a2 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -87,6 +87,7 @@ version(mir_test)
     assert("123".fromString!int == 123);
     static assert("-123".fromString!int == -123);
 
+    assert(".5".fromString!float == .5);
     assert("12.3".fromString!double == 12.3);
     assert("12.3".fromString!float == 12.3f);
     assert("12.3".fromString!real == 12.3L);

From 1dacb27cf172fe8847580b58784e72273ec7955c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 24 Nov 2020 19:26:19 +0700
Subject: [PATCH 144/573] fix issue #321

---
 source/mir/ndslice/slice.d | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index aaf16858..f7980a11 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -20,7 +20,7 @@ $(T2 sliced, Creates a slice on top of an iterator, a pointer, or an array's poi
 $(T2 slicedField, Creates a slice on top of a field, a random access range, or an array.)
 $(T2 slicedNdField, Creates a slice on top of an ndField.)
 $(T2 kindOf, Extracts $(LREF SliceKind).)
-$(T2 isSlice, Extracts dimension count from a type. Extracts `null` if the template argument is not a `Slice`.)
+$(T2 isSlice, Checks if the type is `Slice` instance.)
 $(T2 Structure, A tuple of lengths and strides.)
 )
 
@@ -148,14 +148,10 @@ template toSlices(args...)
         alias toSlices = AliasSeq!();
 }
 
-///
-template isSlice(T)
-{
-    static if (is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind))
-        enum bool isSlice = true;
-    else
-        enum bool isSlice = false;
-}
+/++
+Checks if the type is `Slice` instance.
++/
+enum isSlice(T) = is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind);
 
 ///
 @safe pure nothrow @nogc

From 881f6d2331f337bc0e76a26610d5a861529bd5e8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 Nov 2020 16:09:47 +0700
Subject: [PATCH 145/573] fix decimal parsing

---
 source/mir/bignum/decimal.d | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index d800cd43..b5e3e308 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -153,6 +153,7 @@ bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize
                 t = 1;
                 if (str.length == 0)
                 {
+                M:
                     decimal.exponent -= afterDot;
                     return true;
                 }
@@ -184,7 +185,7 @@ bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize
                 {
                     if (t != 1)
                        goto L;
-                    return true;
+                    goto M;
                 }
                 break;
             default:
@@ -263,4 +264,9 @@ unittest
     assert(!"0.".parseDecimal(decimal, key));
     assert(!"00".parseDecimal(decimal, key));
     assert(!"0d".parseDecimal(decimal, key));
+
+    import mir.math.constant: PI;
+    assert("3.141592653589793378e-10".parseDecimal(decimal, key));
+    assert(cast(double) decimal.view == double(PI) / 1e10);
+    assert(key == DecimalExponentKey.e);
 }

From 3311bb42ecb28f7be57194c438efabf4207d230b Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 2 Dec 2020 21:34:45 -0500
Subject: [PATCH 146/573] Fixup docs (#323)

---
 source/mir/math/stat.d | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 73be7eb1..511d82cb 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -2639,8 +2639,7 @@ type or have a `cdouble` type if `F` is implicitly convertible to a complex type
 Params:
     F = controls type of output
     varianceAlgo = algorithm for calculating variance (default: VarianceAlgo.online)
-    summation: algorithm for calculating sums (default: Summation.appropriate)
-
+    summation = algorithm for calculating sums (default: Summation.appropriate)
 Returns:
     The variance of the input, must be floating point or complex type
 +/
@@ -3019,7 +3018,6 @@ Params:
     F = controls type of output
     varianceAlgo = algorithm for calculating variance (default: VarianceAlgo.online)
     summation = algorithm for calculating sums (default: Summation.appropriate)
-
 Returns:
     The standard deviation of the input, must be floating point type type
 +/

From fd723f894549f5965e912f1c610a8ce7bfa17934 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 7 Dec 2020 12:03:50 +0700
Subject: [PATCH 147/573] update docs

---
 source/mir/ndslice/slice.d |  9 +++++++++
 source/mir/serde.d         | 40 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index f7980a11..19dfb4c6 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -4054,3 +4054,12 @@ unittest
         assert(x.accessFlat(i) == m[i, 0]);
     }
 }
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest // check it can be compiled
+{
+    import mir.algebraic;
+    alias S = Slice!(double*, 2);
+    alias D = Variant!S;
+}
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 168bfe0d..c4371250 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1,3 +1,14 @@
+/++
+This implements common de/serialization routines.
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilya Yaroshenko
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
++/
 module mir.serde;
 
 import mir.functional: naryFun;
@@ -588,6 +599,35 @@ bool serdeParseEnum(E)(const char[] str, ref E res)
 ///
 version(mir_test)
 unittest
+{
+    enum E
+    {
+        @serdeKeys("A", "alpha")
+        a,
+        @serdeKeys("B", "beta")
+        b,
+        c,
+    }
+
+    auto e = E.c;
+    assert(serdeParseEnum("A", e));
+    assert(e == E.a);
+    assert(serdeParseEnum("alpha", e));
+    assert(e == E.a);
+    assert(serdeParseEnum("beta", e));
+    assert(e == E.b);
+    assert(serdeParseEnum("B", e));
+    assert(e == E.b);
+    assert(serdeParseEnum("c", e));
+    assert(e == E.c);
+
+    assert(!serdeParseEnum("C", e));
+    assert(!serdeParseEnum("Alpha", e));
+}
+
+/// Case insensitive
+version(mir_test)
+unittest
 {
     @serdeIgnoreCase // supported for the whole type
     enum E

From ac241850d3f7375220db9eeed3e8b4185d1f896d Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sun, 13 Dec 2020 17:58:28 +0700
Subject: [PATCH 148/573] WIP: Ryu (#324)

* fix fromString for NaN and Inf

* update appveyor

* update mir-core dep

* fix doc

* extend bignum.fixed

* add generic ryu (initial)

* update meson

* use mir multiplication

* optimize divide operations

* add Decimal.fromString

* fix docs

* rework Decimal.fromString to constructor

* add BigInt constructor from string

* rewor view fromHexStringImpl

* add decimal parsing and devid by uint for bitint/fixedint

* import bignum from hex string constructors

* improve docs

* BigInt decimal printing

* E->e

* add UInt.toString

* Decimal.toString

* replace FloatingDecimal128 with Decimal!2

* wip

* wip

* WIP

* optimize loops

* wip

* optimize ryu

* ctfe-able ryu

* clean imports

* Use ryu in print

* add notice file

* update notice and dub

* fix 32bit build

* fixup

* check exception

* fixup

* fix

* update travis

* fixup travis

* fix aarch64
---
 .travis.yml                                  |   9 +-
 Dockerfile.aarch64                           |  11 +
 NOTICE                                       |   3 +
 appveyor.yml                                 |   2 +-
 dub.sdl                                      |  13 +-
 meson.build                                  |   1 +
 source/mir/bignum/decimal.d                  | 421 +++++----
 source/mir/bignum/fixed.d                    | 312 ++++++-
 source/mir/bignum/fp.d                       | 137 ++-
 source/mir/bignum/integer.d                  | 169 +++-
 source/mir/bignum/internal/ryu/generic_128.d | 850 +++++++++++++++++++
 source/mir/bignum/low_level_view.d           | 522 +++++++++++-
 source/mir/format.d                          |  41 +-
 source/mir/parse.d                           |  16 +-
 test_travis.sh                               |  13 +
 15 files changed, 2221 insertions(+), 299 deletions(-)
 create mode 100644 Dockerfile.aarch64
 create mode 100644 NOTICE
 create mode 100644 source/mir/bignum/internal/ryu/generic_128.d
 create mode 100644 test_travis.sh

diff --git a/.travis.yml b/.travis.yml
index 108750af..133b88ad 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,6 @@
+sudo: false
+os:
+ - linux
 language: d
 d:
  - ldc
@@ -20,8 +23,12 @@ matrix:
     - {d: dmd-nightly}
     - {d: ldc-beta}
     - {d: gdc}
+  include:
+    - {os: linux, env: ARCH="AARCH64", services: docker}
+    - {os: osx, env: ARCH="x86_64", d: ldc}
+    - {os: osx, env: ARCH="x86_64", d: dmd}
 script:
- - travis_wait 100 dub test --arch "$ARCH" --build=unittest-cov
+ - travis_wait 100 bash -e test_travis.sh
 
 after_success:
  - bash <(curl -s https://codecov.io/bash)
diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64
new file mode 100644
index 00000000..93404b79
--- /dev/null
+++ b/Dockerfile.aarch64
@@ -0,0 +1,11 @@
+FROM resin/aarch64-ubuntu
+RUN apt-get update && apt-get -y install xz-utils libxml2 gcc git pkg-config ninja-build python3-pip
+RUN pip3 install meson
+RUN curl -fsSLO https://github.com/ldc-developers/ldc/releases/download/v1.24.0/ldc2-1.24.0-linux-aarch64.tar.xz \
+ && tar xf ldc2-1.24.0-linux-aarch64.tar.xz \
+ && sudo cp -rf ldc2-1.24.0-linux-aarch64/* /usr/local \
+ && rm -rf ldc*
+COPY source source
+COPY meson.build meson.build
+COPY subprojects subprojects
+RUN ls subprojects && meson build -D with_test_explicit=true --default-library=static && ninja test -C build
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 00000000..106654df
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,3 @@
+### Ryu
+
+[Original Ryu algorithm](https://github.com/ulfjack/ryu) was developed by Ulf Adams.
diff --git a/appveyor.yml b/appveyor.yml
index 558a3386..a91b548c 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,7 +8,7 @@ environment:
   #   DVersion: 2.080.0
   #   arch: x86
   - DC: ldc
-    DVersion: '1.21.0'
+    DVersion: '1.24.0'
     arch: x64
 
 matrix:
diff --git a/dub.sdl b/dub.sdl
index 3dca3223..6fa64c68 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,21 +5,26 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.1.14"
+dependency "mir-core" version=">=1.1.54"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_test"  "mir_bignum_test"
+    versions "mir_bignum_test" "mir_test"
     dflags "-lowmem"
 }
+buildType "unittest-dip1008" {
+    buildOptions "unittests" "debugMode" "debugInfo"
+    versions "mir_bignum_test" "mir_test"
+    dflags "-lowmem" "-preview=dip1008"
+}
 buildType "unittest-cov" {
     buildOptions "unittests" "coverage" "debugMode" "debugInfo"
-    versions "mir_test"  "mir_bignum_test"
+    versions "mir_bignum_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-release" {
     buildOptions "unittests" "releaseMode" "optimize" "inline" "noBoundsCheck"
-    versions "mir_test"  "mir_bignum_test"
+    versions "mir_bignum_test" "mir_test"
     dflags "-lowmem"
 }
 
diff --git a/meson.build b/meson.build
index eca0b6ce..04dff6c7 100644
--- a/meson.build
+++ b/meson.build
@@ -17,6 +17,7 @@ sources_list = [
     'mir/bignum/fp',
     'mir/bignum/integer',
     'mir/bignum/internal/dec2flt_table',
+    'mir/bignum/internal/ryu/generic_128',
     'mir/bignum/low_level_view',
     'mir/combinatorics/package',
     'mir/container/binaryheap',
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index b5e3e308..89a0c636 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -8,6 +8,10 @@ module mir.bignum.decimal;
 
 import mir.serde: serdeProxy, serdeScoped;
 import std.traits: isSomeChar;
+public import mir.bignum.low_level_view: DecimalExponentKey;
+import mir.bignum.low_level_view: ceilLog10Exp2;
+
+private enum expBufferShift = 2 + ceilLog10Exp2(size_t.sizeof * 8);
 
 /++
 Stack-allocated decimal type.
@@ -23,7 +27,7 @@ struct Decimal(size_t maxSize64)
     import std.traits: isMutable, isFloatingPoint;
 
     ///
-    int exponent;
+    sizediff_t exponent;
     ///
     BigInt!maxSize64 coefficient;
 
@@ -39,181 +43,257 @@ struct Decimal(size_t maxSize64)
         return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
     }
 
-    /++
-    Mir parsing supports up-to quadruple precision.
-The conversion error is 0 ULP for normal numbers. 
-    Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
-        if (isFloatingPoint!T && isMutable!T)
+    ///
+    this(C)(scope const(C)[] str) @safe pure @nogc
+        if (isSomeChar!C)
     {
-        return view.opCast!(T, wordNormalized, nonZero);
+        DecimalExponentKey key;
+        if (fromStringImpl(str, key) || key == DecimalExponentKey.nan || key == DecimalExponentKey.infinity)
+            return;
+        static if (__traits(compiles, () @nogc { throw new Exception("Can't parse Decimal."); }))
+        {
+            import mir.exception: MirException;
+            throw new MirException("Can't parse Decimal!" ~ maxSize64.stringof ~ " from string `", str , "`");
+        }
+        else
+        {
+            static immutable exception = new Exception("Can't parse Decimal!" ~ maxSize64.stringof ~ ".");
+            throw exception;
+        }
     }
-}
-
-/++
-Returns: false in case of integer overflow or incorrect input.
-+/
-@trusted @nogc pure nothrow
-bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
-    if (isSomeChar!C)
-{
-    import mir.bignum.low_level_view: MaxWordPow10;
-    import mir.utility: _expect;
-    decimal.exponent = 0;
-    decimal.coefficient.sign = false;
-    decimal.coefficient.length = 0;
-
-    if (_expect(str.length == 0, false))
-        return false;
-    
 
-
-    if (str[0] == '-')
+    static if (maxSize64 == 3)
+    ///
+    version(mir_test) @safe pure @nogc unittest
     {
-        decimal.coefficient.sign = true;
-        str = str[1 .. $];
-        if (_expect(str.length == 0, false))
-            return false;
+        import mir.math.constant: PI;
+        Decimal!2 decimal = "3.141592653589793378e-40"; // constructor
+        assert(cast(double) decimal.view == double(PI) / 1e40);
     }
-    else
-    if (_expect(str[0] == '+', false))
+
+    /++
+    Returns: false in case of overflow or incorrect string.
+    Precondition: non-empty coefficients.
+    +/
+    bool fromStringImpl(C)(scope const(C)[] str, out DecimalExponentKey key)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
     {
-        str = str[1 .. $];
-        if (_expect(str.length == 0, false))
-            return false;
+        import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
+        auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
+        auto ret = work.fromStringImpl(str, key);
+        coefficient.length = cast(uint) work.coefficient.coefficients.length;
+        coefficient.sign = work.sign;
+        exponent = work.exponent;
+        return ret;
     }
 
-    uint d = str[0] - '0';
-    str = str[1 .. $];
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test) 
+    @safe pure nothrow @nogc
+    unittest
+    {
+        import mir.conv: to;
+        Decimal!3 decimal;
+        DecimalExponentKey key;
 
-    size_t v;
-    size_t t = 1;
-    uint afterDot;
-    bool dot;
-    bool hasExponent;
+        assert(decimal.fromStringImpl("1.334", key));
+        assert(cast(double) decimal.view == 1.334);
+        assert(key == DecimalExponentKey.dot);
 
-    if (d == 0)
-    {
-        if (str.length == 0)
-            return true;
-        if (str[0] == '0')
-            return false;
-        goto S;
-    }
-    else
-    if (d < 10)
-    {
-        goto S;
-    }
-    else
-    if (d == '.' - '0')
-    {
-        goto D;
+        assert(decimal.fromStringImpl("+0.334e-5"w, key));
+        assert(cast(double) decimal.view == 0.334e-5);
+        assert(key == DecimalExponentKey.e);
+
+        assert(decimal.fromStringImpl("-334D-5"d, key));
+        assert(cast(double) decimal.view == -334e-5);
+        assert(key == DecimalExponentKey.D);
+
+        assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
+        assert(cast(double) decimal.view == 2482734692817364218734682973648217364981273648923423.0);
+        assert(key == DecimalExponentKey.none);
+
+        assert(decimal.fromStringImpl(".023", key));
+        assert(cast(double) decimal.view == .023);
+        assert(key == DecimalExponentKey.dot);
+
+        assert(decimal.fromStringImpl("0E100", key));
+        assert(cast(double) decimal.view == 0);
+        assert(key == DecimalExponentKey.E);
+
+        assert(decimal.fromStringImpl("-nan", key));
+        assert(decimal.coefficient.length == 0);
+        assert(cast(double) decimal.view == 0);
+        assert(decimal.coefficient.sign);
+        assert(key == DecimalExponentKey.nan);
+
+        assert(decimal.fromStringImpl("inf", key));
+        assert(cast(double) decimal.view == 0);
+        assert(key == DecimalExponentKey.infinity);
+
+        assert(!decimal.fromStringImpl("3.3.4", key));
+        assert(!decimal.fromStringImpl("3.4.", key));
+        assert(!decimal.fromStringImpl("4.", key));
+        assert(!decimal.fromStringImpl(".", key));
+        assert(!decimal.fromStringImpl("0.", key));
+        assert(!decimal.fromStringImpl("00", key));
+        assert(!decimal.fromStringImpl("0d", key));
     }
-    else
+
+    private enum eDecimalLength = 2 + ceilLog10Exp2(coefficient.data.length * (size_t.sizeof * 8)) + expBufferShift;
+
+    private const(C)[] toStringImpl(C = char)(ref scope return C[eDecimalLength] buffer) scope const @safe pure nothrow
+        if(isSomeChar!C && isMutable!C)
     {
-        if (str.length == 2)
+        import mir.utility: _expect;
+        // handle special values
+        if (_expect(exponent == exponent.max, false))
         {
-            if ((d == 'i' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"nf"))
+            immutable(C)[] nan = "nan";
+            immutable(C)[] ninf = "-inf";
+            immutable(C)[] pinf = "+inf";
+            return coefficient.length == 0 ? coefficient.sign ? ninf : pinf : nan;
+        }
+
+        size_t coefficientLength;
+        static if (size_t.sizeof == 8)
+        {
+            if (__ctfe && size_t.sizeof == 8)
             {
-                key = DecimalExponentKey.infinity;
-                return true;
+                uint[coefficient.data.length * 2] data;
+                foreach (i; 0 .. coefficient.length)
+                {
+                    auto l = cast(uint)coefficient.data[i];
+                    auto h = cast(uint)(coefficient.data[i] >> 32);
+                    version (LittleEndian)
+                    {
+                        data[i * 2 + 0] = l;
+                        data[i * 2 + 1] = h;
+                    }
+                    else
+                    {
+                        data[$ - 1 - (i * 2 + 0)] = l;
+                        data[$ - 1 - (i * 2 + 1)] = h;
+                    }
+                }
+                auto work = BigUIntView!uint(data);
+                work = work.topLeastSignificantPart(coefficient.length * 2).normalized;
+                coefficientLength = work.toStringImpl(buffer[0 .. $ - expBufferShift]);
             }
-            if ((d == 'n' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"an"))
+            else
             {
-                key = DecimalExponentKey.nan;
-                return true;
+                BigInt!maxSize64 work = coefficient;
+                coefficientLength = work.view.unsigned.toStringImpl(buffer[0 .. $ - expBufferShift]);
             }
         }
-        return false;
-    }
+        else
+        {
+            BigInt!maxSize64 work = coefficient;
+            coefficientLength = work.view.unsigned.toStringImpl(buffer[0 .. $ - expBufferShift]);
+        }
 
-    for(;;)
-    {
-        import std.stdio;
-        enum mp10 = size_t(10) ^^ MaxWordPow10!size_t;
-        d = str[0] - '0';
-        str = str[1 .. $];
+        assert(coefficientLength);
+        sizediff_t exponent = this.exponent + coefficientLength - 1;
 
-        if (_expect(d <= 10, true))
+        if (coefficientLength > 1)
         {
-            v *= 10;
-    S:
-            t *= 10;
-            v += d;
-            afterDot += dot;
-
-            if (_expect(t == mp10 || str.length == 0, false))
-            {
-            L:
-                auto overflow = decimal.coefficient.opOpAssign!"*"(t, v);
-                if (_expect(overflow, 0))
-                    return false;
-                v = 0;
-                t = 1;
-                if (str.length == 0)
-                {
-                M:
-                    decimal.exponent -= afterDot;
-                    return true;
-                }
-            }
+            auto c = buffer[$ - expBufferShift - coefficientLength];
+            buffer[$ - expBufferShift - coefficientLength] = '.';
+            buffer[$ - expBufferShift - ++coefficientLength] = c;
+        }
 
-            continue;
+        if (coefficient.sign)
+        {
+            buffer[$ - expBufferShift - ++coefficientLength] = '-';
         }
-        else
-        if (key != d) switch (d)
+
+        size_t exponentLength;
         {
-            D:
-            case DecimalExponentKey.dot:
-                key = cast(DecimalExponentKey)d;
-                if (_expect(!dot, true))
-                {
-                    dot = true;
-                    if (str.length)
-                        continue;
-                }
-                break;
-            case DecimalExponentKey.e:
-            case DecimalExponentKey.d:
-            case DecimalExponentKey.E:
-            case DecimalExponentKey.D:
-                key = cast(DecimalExponentKey)d;
-                hasExponent = true;
-                import mir.parse: parse;
-                if (parse(str, decimal.exponent) && str.length == 0)
-                {
-                    if (t != 1)
-                       goto L;
-                    goto M;
-                }
-                break;
-            default:
+            bool expSign = exponent < 0;
+            size_t unsignedExponent = expSign ? -exponent : exponent;
+            size_t i = buffer.length;
+
+            do
+            {
+                buffer[--i] = cast(char)(unsignedExponent % 10 + '0');
+                unsignedExponent /= 10;
+            }
+            while(unsignedExponent);
+            if (expSign)
+                buffer[--i] = '-';
+            exponentLength = buffer.length - i;
         }
-        break;
+
+        buffer[$ - expBufferShift] = 'e';
+
+        assert(exponentLength <= expBufferShift + 1);
+        size_t i;
+        do buffer[$ - (expBufferShift - 1) + i] = buffer[$ - exponentLength + i];
+        while(++i < exponentLength);
+        return buffer[$ - (expBufferShift + coefficientLength) .. $ - (expBufferShift - 1) + exponentLength];
     }
-    return false;
-}
 
-///
-enum DecimalExponentKey
-{
-    ///
-    none = 0,
-    ///
-    dot = '.' - '0',
     ///
-    d = 'd' - '0',
-    ///
-    e = 'e' - '0',
+    immutable(C)[] toString(C = char)() const @safe pure nothrow
+        if(isSomeChar!C && isMutable!C)
+    {
+        C[eDecimalLength] buffer = void;
+        return toStringImpl(buffer).idup;
+    }
+
+    static if (maxSize64 == 3)
     ///
-    D = 'D' - '0',
+    version(mir_bignum_test) @safe pure unittest
+    {
+        auto str = "-3.4010447314490204552169750449563978034784726557588085989975288830070948234680e-13245";
+        auto integer = Decimal!4(str);
+        assert(integer.toString == str, integer.toString);
+
+        integer = Decimal!4.init;
+        assert(integer.toString == "0e0");
+    }
+
     ///
-    E = 'E' - '0',
+    void toString(C = char, W)(scope ref W w) const
+        if(isSomeChar!C && isMutable!C)
+    {
+        C[eDecimalLength] buffer = void;
+        w.put(toStringImpl(buffer));
+    }
+
+    static if (maxSize64 == 3)
+    /// Check @nogc toString impl
+    version(mir_bignum_test) @safe pure @nogc unittest
+    {
+        import mir.format: stringBuf;
+        auto str = "5.28238923728e-876543210";
+        auto integer = Decimal!1(str);
+        stringBuf buffer;
+        buffer << integer;
+        assert(buffer.data == str, buffer.data);
+    }
+
+    /++
+    Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
+    Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+        if (isFloatingPoint!T && isMutable!T)
+    {
+        return view.opCast!(T, wordNormalized, nonZero);
+    }
+
     ///
-    infinity,
+    bool isNaN() const @property
+    {
+        return exponent == exponent.max && coefficient.length;
+    }
+
     ///
-    nan,
+    bool isInfinity() const @property
+    {
+        return exponent == exponent.max && !coefficient.length;
+    }
 }
 
 ///
@@ -221,52 +301,21 @@ version(mir_bignum_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.conv: to;
     Decimal!3 decimal;
     DecimalExponentKey key;
 
-    assert("1.334".parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == 1.334);
-    assert(key == DecimalExponentKey.dot);
-
-    assert("+0.334e-5"w.parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == 0.334e-5);
-    assert(key == DecimalExponentKey.e);
-
-    assert("-334D-5"d.parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == -334e-5);
-    assert(key == DecimalExponentKey.D);
-
-    assert("2482734692817364218734682973648217364981273648923423".parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == 2482734692817364218734682973648217364981273648923423.0);
-    assert(key == DecimalExponentKey.none);
-
-    assert(".023".parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == .023);
-    assert(key == DecimalExponentKey.dot);
-
-    assert("0E100".parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == 0);
-    assert(key == DecimalExponentKey.E);
-
-    assert("-nan".parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == 0);
-    assert(decimal.coefficient.sign);
-    assert(key == DecimalExponentKey.nan);
-
-    assert("inf".parseDecimal(decimal, key));
-    assert(cast(double) decimal.view == 0);
-    assert(key == DecimalExponentKey.infinity);
-
-    assert(!"3.3.4".parseDecimal(decimal, key));
-    assert(!"3.4.".parseDecimal(decimal, key));
-    assert(!"4.".parseDecimal(decimal, key));
-    assert(!".".parseDecimal(decimal, key));
-    assert(!"0.".parseDecimal(decimal, key));
-    assert(!"00".parseDecimal(decimal, key));
-    assert(!"0d".parseDecimal(decimal, key));
-
     import mir.math.constant: PI;
-    assert("3.141592653589793378e-10".parseDecimal(decimal, key));
+    assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
     assert(cast(double) decimal.view == double(PI) / 1e10);
     assert(key == DecimalExponentKey.e);
 }
+
+
+deprecated("use decimal.fromStringImpl insteade")
+@trusted @nogc pure nothrow
+bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
+    if (isSomeChar!C)
+{
+    return decimal.fromStringImpl(str, key);
+}
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 34b3e6b8..9fc7899e 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -24,7 +24,7 @@ struct UInt(size_t size)
     size_t[size / (size_t.sizeof * 8)] data;
 
     ///
-    this(size_t N)(auto ref size_t[N] data)
+    this(size_t N)(auto ref const size_t[N] data)
         if (N <= this.data.length)
     {
         version(LittleEndian)
@@ -33,8 +33,16 @@ struct UInt(size_t size)
             this.data[$ - N .. $] = data;
     }
 
+    ///
+    this(size_t argSize)(auto ref const UInt!argSize arg)
+        if (argSize <= size)
+    {
+        this(arg.data);
+    }
+
     static if (size_t.sizeof == uint.sizeof && data.length % 2 == 0)
-    this()(auto ref ulong[data.length / 2] data)
+    ///
+    this()(auto ref const ulong[data.length / 2] data)
     {
         if (!__ctfe)
         {
@@ -61,7 +69,6 @@ struct UInt(size_t size)
         }
     }
 
-    static if (size >= 64)
     ///
     this(ulong data)
     {
@@ -84,6 +91,91 @@ struct UInt(size_t size)
         view.leastSignificant = data;
     }
 
+    ///
+    this(C)(scope const(C)[] str) @safe pure @nogc
+        if (isSomeChar!C)
+    {
+        if (fromStringImpl(str))
+            return;
+        static if (__traits(compiles, () @nogc { throw new Exception("Can't parse UInt."); }))
+        {
+            import mir.exception: MirException;
+            throw new MirException("Can't parse UInt!" ~ size.stringof ~ " from string `", str , "`.");
+        }
+        else
+        {
+            static immutable exception = new Exception("Can't parse UInt!" ~ size.stringof ~ ".");
+            throw exception;
+        }
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_test) @safe pure @nogc unittest
+    {
+        import mir.math.constant: PI;
+        UInt!256 integer = "34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
+        assert(integer == UInt!256.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8"));
+    }
+
+    /++
+    Returns: false in case of overflow or incorrect string.
+    Precondition: non-empty coefficients.
+    +/
+    bool fromStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        import mir.bignum.low_level_view: BigUIntView;
+        return BigUIntView!size_t(data[]).fromStringImpl(str);
+    }
+
+    ///
+    immutable(C)[] toString(C = char)() const @safe pure nothrow
+        if(isSomeChar!C && isMutable!C)
+    {
+        UInt!size copy = this;
+        auto work = copy.view.normalized;
+        import mir.bignum.low_level_view: ceilLog10Exp2;
+        C[ceilLog10Exp2(data.length * (size_t.sizeof * 8))] buffer = void;
+        return buffer[$ - work.toStringImpl(buffer) .. $].idup;
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test) @safe pure unittest
+    {
+        auto str = "34010447314490204552169750449563978034784726557588085989975288830070948234680";
+        auto integer = UInt!256(str);
+        assert(integer.toString == str);
+
+        integer = UInt!256.init;
+        assert(integer.toString == "0");
+    }
+
+    ///
+    void toString(C = char, W)(scope ref W w) const
+        if(isSomeChar!C && isMutable!C)
+    {
+        UInt!size copy = this;
+        auto work = copy.view.normalized;
+        import mir.bignum.low_level_view: ceilLog10Exp2;
+        C[ceilLog10Exp2(data.length * (size_t.sizeof * 8))] buffer = void;
+        w.put(buffer[$ - work.toStringImpl(buffer) .. $]);
+    }
+
+    static if (size == 128)
+    /// Check @nogc toString impl
+    version(mir_bignum_test) @safe pure @nogc unittest
+    {
+        import mir.format: stringBuf;
+        auto str = "34010447314490204552169750449563978034784726557588085989975288830070948234680";
+        auto integer = UInt!256(str);
+        stringBuf buffer;
+        buffer << integer;
+        assert(buffer.data == str, buffer.data);
+    }
+
     ///
     enum UInt!size max = ((){UInt!size ret; ret.data = size_t.max; return ret;})();
 
@@ -108,16 +200,80 @@ struct UInt(size_t size)
     static UInt!size fromHexString(scope const(char)[] str)
     {
         typeof(return) ret;
-        ret.view.fromHexStringImpl(str);
-        return ret;
+        if (ret.fromHexStringImpl(str))
+            return ret;
+        version(D_Exceptions)
+        {
+            import mir.bignum.low_level_view: hexStringException;
+            throw hexStringException;
+        }
+        else
+            assert(0, hexStringErrorMsg);
+    }
+
+    /++
+    +/
+    bool fromHexStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        return view.fromHexStringImpl(str);
+    }
+
+    /++
+    +/
+    auto opEquals(size_t rhsSize)(auto ref const UInt!rhsSize rhs) const
+    {
+        static if (rhsSize == size)
+            return this.data == rhs.data;
+        else
+        static if (rhsSize > size)
+            return this.toSize!rhsSize.data == rhs.data;
+        else
+            return this.data == rhs.toSize!size.data;
+    }
+
+    /// ditto
+    auto opEquals(ulong rhs) const
+    {
+        return opEquals(UInt!size(rhs));
+    }
+
+    /++
+    +/
+    auto opCmp(UInt!size rhs) const
+    {
+        version (LittleEndian) // workaround for CTFE bug
+        {
+            foreach_reverse(i; 0 .. data.length)
+            {
+                if (this.data[i] < rhs.data[i])
+                    return -1;
+                if (this.data[i] > rhs.data[i])
+                    return +1;
+            }
+            return 0;
+        }
+        else
+        {
+            import mir.algorithm.iteration: cmp;
+            return cmp(this.view.mostSignificantFirst, rhs.view.mostSignificantFirst);
+        }
+    }
+
+    /// ditto
+    auto opCmp(ulong rhs) const
+    {
+        return opCmp(UInt!size(rhs));
     }
 
     /++
     +/
-    auto opCmp(UInt!size rhs)
+    ref UInt!size opAssign(ulong rhs) return
+        @safe pure nothrow @nogc
     {
-        import mir.algorithm.iteration: cmp;
-        return cmp(this.view.mostSignificantFirst, rhs.view.mostSignificantFirst);
+        this.data = UInt!size(rhs).data;
+        return this;
     }
 
     /++
@@ -138,12 +294,68 @@ struct UInt(size_t size)
         return view.opOpAssign!op(rhs);
     }
 
+    static if (size_t.sizeof < ulong.sizeof)
+    /// ditto
+    bool opOpAssign(string op)(ulong rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return opOpAssign!op(UInt!size(rhs));
+    }
+
     /// ditto
     bool opOpAssign(string op, size_t rsize)(UInt!rsize rhs, bool overflow = false)
         @safe pure nothrow @nogc
         if ((op == "+" || op == "-") && rsize < size)
     {
-        return opOpAssign!op(rhs.toSize!size);
+        return opOpAssign!op(rhs.toSize!size, overflow);
+    }
+
+    /++
+    Returns: overflow value of multiplication
+    +/
+    size_t opOpAssign(string op : "*")(size_t rhs, size_t carry = 0)
+        @safe pure nothrow @nogc
+    {
+        return view.opOpAssign!op(rhs, carry);
+    }
+
+    static if (size_t.sizeof == 4)
+    /// ditto
+    auto opOpAssign(string op : "*")(ulong rhs)
+        @safe pure nothrow @nogc
+    {
+        return opOpAssign!op(UInt!64(rhs));
+    }
+
+
+    /++
+    Returns: overflow value of multiplication
+    +/
+    void opOpAssign(string op : "*", size_t rhsSize)(UInt!rhsSize rhs)
+        @safe pure nothrow @nogc
+        if (rhsSize <= size)
+    {
+        this = extendedMul(this, rhs).toSize!size;
+    }
+
+    /++
+    Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation.
+    Precondition: `overflow < rhs`
+    Params:
+        rhs = unsigned value to devide by
+        overflow = initial unsigned overflow
+    Returns:
+        unsigned remainder value (evaluated overflow)
+    +/
+    uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0)
+        @safe pure nothrow @nogc
+    {
+        assert(overflow < rhs);
+        auto work = view.normalized;
+        if (worl.coefficients.length)
+            return work.opOpAssign!op(rhs, overflow);
+        return overflow;
     }
 
     ///
@@ -166,6 +378,23 @@ struct UInt(size_t size)
         assert((a.opBinary!"|"(b)) == UInt!128.fromHexString("ffbffbeffd1affaf75adfff5abf0a39d"));
     }
 
+    ///
+    ref UInt!size opOpAssign(string op)(size_t rhs) nothrow return
+        if (op == "^" || op == "|" || op == "&")
+    {
+        mixin(`view.leastSignificantFirst[0] ` ~ op ~ `= rhs;`);
+        return this;
+    }
+
+    static if (size_t.sizeof < ulong.sizeof)
+    /// ditto
+    ref opOpAssign(string op)(ulong rhs) return
+        @safe pure nothrow @nogc
+        if (op == "^" || op == "|" || op == "&")
+    {
+        return opOpAssign!op(UInt!size(rhs));
+    }
+
     ///
     ref UInt!size opOpAssign(string op)(size_t shift)
         @safe pure nothrow @nogc return
@@ -253,27 +482,53 @@ struct UInt(size_t size)
     }
 
     /++
-    `auto c = a << b`, and `^`, `|`, `&` operations.
+    Binary operations
     +/
-    UInt!size opBinary(string op)(UInt!size rhs)
-        const @safe pure nothrow @nogc
-        if (op == "^" || op == "|" || op == "&")
+    template opBinary(string op)
+        if (op == "^" || op == "|" || op == "&" || op == "+" || op == "-" || op == "*") //  || op == "/" || op == "%"
     {
-        UInt!size ret = this;
-        ret.opOpAssign!op(rhs);
-        return ret;
+        ///
+        UInt!size opBinary(size_t rsize)(UInt!rsize rhs)
+            const @safe pure nothrow @nogc
+            if (rsize <= size)
+        {
+            UInt!size ret = this;
+            ret.opOpAssign!op(rhs);
+            return ret;
+        }
+
+        /// ditto
+        UInt!size opBinary(ulong rhs)
+            const @safe pure nothrow @nogc
+        {
+            UInt!size ret = this;
+            ret.opOpAssign!op(rhs);
+            return ret;
+        }
     }
 
-    /++
-    `auto c = a + b` and `auto c = a - b` operations.
-    +/
-    UInt!size opBinary(string op, size_t rsize)(UInt!rsize rhs)
-        const @safe pure nothrow @nogc
-        if ((op == "+" || op == "-") && rsize <= size)
+    /// ditto
+    template opBinaryRight(string op)
+        if (op == "^" || op == "|" || op == "&" || op == "+" || op == "*")
     {
-        UInt!size ret = this;
-        ret.opOpAssign!op(rhs);
-        return ret;
+        ///
+        UInt!size opBinaryRight(size_t lsize)(UInt!lsize lhs)
+            const @safe pure nothrow @nogc
+            if (lsize < size)
+        {
+            UInt!size ret = this;
+            ret.opOpAssign!op(lhs);
+            return ret;
+        }
+
+        /// ditto
+        UInt!size opBinaryRight(ulong lhs)
+            const @safe pure nothrow @nogc
+        {
+            UInt!size ret = this;
+            ret.opOpAssign!op(lhs);
+            return ret;
+        }
     }
 
     /++
@@ -538,6 +793,13 @@ struct UInt(size_t size)
     }
 }
 
+/++
++/
+UInt!sizeB extendedMulHigh(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
+{
+    return (extendedMul(a, b) >> sizeA).toSize!sizeB;
+}
+
 /++
 +/
 UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index bd3e4821..2a694ee8 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -14,29 +14,151 @@ package enum half(size_t hs) = (){
 }();
 
 /++
-Software floating point bumber.
+Software floating point number.
+
 Params:
     coefficientSize = coefficient size in bits
+
+Note: the implementation doesn't support NaN and Infinity values.
 +/
-struct Fp(size_t coefficientSize)
-    if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
+struct Fp(size_t coefficientSize, Exp = sizediff_t)
+    if ((is(Exp == int) || is(Exp == long)) && coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
 {
     import mir.bignum.fixed: UInt;
 
     bool sign;
-    sizediff_t exponent;
+    Exp exponent;
     UInt!coefficientSize coefficient;
 
     /++
     +/
     nothrow
-    this(bool sign, sizediff_t exponent, UInt!coefficientSize normalizedCoefficient)
+    this(bool sign, Exp exponent, UInt!coefficientSize normalizedCoefficient)
     {
         this.coefficient = normalizedCoefficient;
         this.exponent = exponent;
         this.sign = sign;
     }
 
+    /++
+    Constructs $(LREF Fp) from hardaware floating  point number.
+    Params:
+        value = Hardware floating point number. Special values `nan` and `inf` aren't allowed.
+        normalize = flag to indicate if the normalization should be performed.
+    +/
+    this(T)(const T value, bool normalize = true)
+        @safe pure nothrow @nogc
+        if (isFloatingPoint!T && T.mant_dig <= coefficientSize)
+    {
+        import mir.math.common : fabs;
+        import mir.math.ieee : frexp, signbit, ldexp;
+        assert(value == value);
+        assert(value.fabs < T.infinity);
+        this.sign = value.signbit != 0;
+        if (value == 0)
+            return;
+        T x = value.fabs;
+        int exp;
+        {
+            enum scale = T(2) ^^ T.mant_dig;
+            x = frexp(x, exp) * scale;
+            exp -= T.mant_dig;
+        }
+        static if (T.mant_dig < 64)
+        {
+            this.coefficient = UInt!coefficientSize(cast(ulong)cast(long)x);
+        }
+        else
+        static if (T.mant_dig == 64)
+        {
+            this.coefficient = UInt!coefficientSize(cast(ulong)x);
+        }
+        else
+        {
+            enum scale = T(2) ^^ 64;
+            enum scaleInv = 1 / scale;
+            x *= scaleInv;
+            long high = cast(long) x;
+            if (high > x)
+                --high;
+            x -= high;
+            x *= scale;
+            this.coefficient = (UInt!coefficientSize(ulong(high)) << 64) | cast(ulong)x;
+        }
+        if (normalize)
+        {
+            auto shift = cast(int)this.coefficient.ctlz;
+            exp -= shift;
+            this.coefficient <<= shift;
+        }
+        else
+        {
+            int shift = T.min_exp - T.mant_dig - exp;
+            if (shift > 0)
+            {
+                this.coefficient >>= shift;
+                exp = T.min_exp - T.mant_dig;
+            }
+        }
+        this.exponent = exp;
+    }
+
+    static if (coefficientSize == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc nothrow
+    unittest
+    {
+        enum h = -33.0 * 2.0 ^^ -10;
+        auto f = Fp!64(h);
+        assert(f.sign);
+        assert(f.exponent == -10 - (64 - 6));
+        assert(f.coefficient == 33UL << (64 - 6));
+        assert(cast(double) f == h);
+
+        // CTFE
+        static assert(cast(double) Fp!64(h) == h);
+
+        f = Fp!64(-0.0);
+        assert(f.sign);
+        assert(f.exponent == 0);
+        assert(f.coefficient == 0);
+
+        // subnormals
+        static assert(cast(float) Fp!64(float.min_normal / 2) == float.min_normal / 2);
+        static assert(cast(float) Fp!64(float.min_normal * float.epsilon) == float.min_normal * float.epsilon);
+        // subnormals
+        static assert(cast(double) Fp!64(double.min_normal / 2) == double.min_normal / 2);
+        static assert(cast(double) Fp!64(double.min_normal * double.epsilon) == double.min_normal * double.epsilon);
+        // subnormals
+        static assert(cast(real) Fp!64(real.min_normal / 2) == real.min_normal / 2);
+        static assert(cast(real) Fp!64(real.min_normal * real.epsilon) == real.min_normal * real.epsilon);
+
+        enum d = cast(float) Fp!64(float.min_normal / 2, false);
+
+        // subnormals
+        static assert(cast(float) Fp!64(float.min_normal / 2, false) == float.min_normal / 2, d.stringof);
+        static assert(cast(float) Fp!64(float.min_normal * float.epsilon, false) == float.min_normal * float.epsilon);
+        // subnormals
+        static assert(cast(double) Fp!64(double.min_normal / 2, false) == double.min_normal / 2);
+        static assert(cast(double) Fp!64(double.min_normal * double.epsilon, false) == double.min_normal * double.epsilon);
+        // subnormals
+        static assert(cast(real) Fp!64(real.min_normal / 2, false) == real.min_normal / 2);
+        static assert(cast(real) Fp!64(real.min_normal * real.epsilon, false) == real.min_normal * real.epsilon);
+    }
+
+    static if (coefficientSize == 128)
+    /// Without normalization
+    version(mir_bignum_test)
+    @safe pure @nogc nothrow
+    unittest
+    {
+        auto f = Fp!64(-33.0 * 2.0 ^^ -10, false);
+        assert(f.sign);
+        assert(f.exponent == -10 - (double.mant_dig - 6));
+        assert(f.coefficient == 33UL << (double.mant_dig - 6));
+    }
+
     /++
     +/
     this(size_t size)(UInt!size integer, bool normalizedInteger = false)
@@ -47,7 +169,7 @@ struct Fp(size_t coefficientSize)
         {
             if (normalizedInteger)
             {
-                this(false, sizediff_t(size) - coefficientSize, integer.rightExtend!(coefficientSize - size));
+                this(false, Exp(size) - coefficientSize, integer.rightExtend!(coefficientSize - size));
             }
             else
             {
@@ -177,8 +299,7 @@ struct Fp(size_t coefficientSize)
     T opCast(T, bool noHalf = false)() nothrow const
         if (isFloatingPoint!T)
     {
-        // import mir.math.ieee: ldexp;
-        import std.math: ldexp;
+        import mir.math.ieee: ldexp;
         auto exp = cast()exponent;
         static if (coefficientSize == 32)
         {
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 149df980..7e8adbf5 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -28,8 +28,6 @@ struct BigInt(size_t maxSize64)
     ///
     size_t[ulong.sizeof / size_t.sizeof * maxSize64] data = void;
 
-    @disable this(this);
-
     ///
     this(size_t size)(UInt!size fixedInt)
     {
@@ -66,6 +64,72 @@ struct BigInt(size_t maxSize64)
         normalize;
     }
 
+    ///
+    this(C)(scope const(C)[] str) @safe pure @nogc
+        if (isSomeChar!C)
+    {
+        if (fromStringImpl(str))
+            return;
+        static if (__traits(compiles, () @nogc { throw new Exception("Can't parse BigInt."); }))
+        {
+            import mir.exception: MirException;
+            throw new MirException("Can't parse BigInt!" ~ maxSize64.stringof ~ " from string `", str , "`.");
+        }
+        else
+        {
+            static immutable exception = new Exception("Can't parse BigInt!" ~ maxSize64.stringof ~ ".");
+            throw exception;
+        }
+    }
+
+    ///
+    ref opAssign(ulong data) return
+    {
+        static if (size_t.sizeof == ulong.sizeof)
+        {
+            length = 1;
+            view.leastSignificantFirst[0] = data;
+        }
+        else
+        {
+            length = 2;
+            auto d = view.leastSignificantFirst;
+            d[0] = cast(uint) data;
+            d[1] = cast(uint) (data >> 32);
+        }
+        normalize;
+        return this;
+    }
+
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test) @safe pure @nogc unittest
+    {
+        import mir.math.constant: PI;
+        BigInt!4 integer = "-34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
+        assert(integer.sign);
+        integer.sign = false;
+        assert(integer == BigInt!4.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8"));
+    }
+
+    /++
+    Returns: false in case of overflow or incorrect string.
+    Precondition: non-empty coefficients.
+    +/
+    bool fromStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        auto work = BigIntView!size_t(data[]); 
+        if (work.fromStringImpl(str))
+        {
+            length = cast(uint) work.coefficients.length;
+            sign = work.sign;
+            return true;
+        }
+        return false;
+    }
+
     ///
     BigInt copy() @property
     {
@@ -77,7 +141,7 @@ struct BigInt(size_t maxSize64)
     }
 
     ///
-    bool opEquals(ref const BigInt rhs)
+    bool opEquals()(auto ref const BigInt rhs)
         const @safe pure nothrow @nogc
     {
         return view == rhs.view;
@@ -85,7 +149,7 @@ struct BigInt(size_t maxSize64)
 
     /++
     +/
-    auto opCmp(ref const BigInt rhs) 
+    auto opCmp()(auto ref const BigInt rhs) 
         const @safe pure nothrow @nogc
     {
         return view.opCmp(rhs.view);
@@ -130,7 +194,6 @@ struct BigInt(size_t maxSize64)
 
     /++
     Performs `size_t overflow = (big += overflow) *= scalar` operatrion.
-    Precondition: non-empty coefficients
     Params:
         rhs = unsigned value to multiply by
         overflow = initial overflow value
@@ -152,9 +215,26 @@ struct BigInt(size_t maxSize64)
         return overflow;
     }
 
+    /++
+    Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation.
+    Precondition: `overflow < rhs`
+    Params:
+        rhs = unsigned value to devide by
+        overflow = initial unsigned overflow
+    Returns:
+        unsigned remainder value (evaluated overflow)
+    +/
+    uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0)
+        @safe pure nothrow @nogc
+    {
+        assert(overflow < rhs);
+        if (length)
+            return view.unsigned.opOpAssign!op(rhs, overflow);
+        return overflow;
+    }
+
     /++
     Performs `size_t overflow = (big += overflow) *= fixed` operatrion.
-    Precondition: non-empty coefficients
     Params:
         rhs = unsigned value to multiply by
         overflow = initial overflow value
@@ -205,7 +285,6 @@ struct BigInt(size_t maxSize64)
 
     /++
     Performs `size_t overflow = big *= fixed` operatrion.
-    Precondition: non-empty coefficients
     Params:
         rhs = unsigned value to multiply by
     Returns:
@@ -249,16 +328,27 @@ struct BigInt(size_t maxSize64)
         @trusted pure
     {
         BigInt ret;
-        auto len = str.length / (size_t.sizeof * 2) + (str.length % (size_t.sizeof * 2) != 0);
-        if (len > ret.data.length)
+        if (ret.fromHexStringImpl(str))
+            return ret;
+        version(D_Exceptions)
+            throw hexStringException;
+        else
+            assert(0, hexStringErrorMsg);
+    }
+
+    /++
+    +/
+    bool fromHexStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        auto work = BigIntView!size_t(data);
+        auto ret = work.fromHexStringImpl(str);
+        if (ret)
         {
-            version(D_Exceptions)
-                throw hexStringException;
-            else
-                assert(0, hexStringErrorMsg);
+            length = cast(uint)work.unsigned.coefficients.length;
+            sign = work.sign;
         }
-        ret.length = cast(uint)len;
-        ret.view.unsigned.fromHexStringImpl(str);
         return ret;
     }
 
@@ -419,8 +509,57 @@ struct BigInt(size_t maxSize64)
     {
         return this.copyFrom(BigIntView!(const W, endian)(view));
     }
+
+    ///
+    immutable(C)[] toString(C = char)() const @safe pure nothrow
+        if(isSomeChar!C && isMutable!C)
+    {
+        C[ceilLog10Exp2(data.length * (size_t.sizeof * 8)) + 1] buffer = void;
+        BigInt copy = this;
+        auto len = copy.view.unsigned.toStringImpl(buffer);
+        if (sign)
+            buffer[$ - ++len] = '-';
+        return buffer[$ - len .. $].idup;
+    }
+
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test) @safe pure unittest
+    {
+        auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
+        auto integer = BigInt!4(str);
+        assert(integer.toString == str);
+
+        integer = BigInt!4.init;
+        assert(integer.toString == "0");
+    }
+
+    ///
+    void toString(C = char, W)(scope ref W w) const
+        if(isSomeChar!C && isMutable!C)
+    {
+        C[ceilLog10Exp2(data.length * (size_t.sizeof * 8)) + 1] buffer = void;
+        BigInt copy = this;
+        auto len = copy.view.unsigned.toStringImpl(buffer);
+        if (sign)
+            buffer[$ - ++len] = '-';
+        w.put(buffer[$ - len .. $]);
+    }
+
+    static if (maxSize64 == 3)
+    /// Check @nogc toString impl
+    version(mir_bignum_test) @safe pure @nogc unittest
+    {
+        import mir.format: stringBuf;
+        auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
+        auto integer = BigInt!4(str);
+        stringBuf buffer;
+        buffer << integer;
+        assert(buffer.data == str, buffer.data);
+    }
 }
 
+
 ///
 version(mir_bignum_test)
 unittest
diff --git a/source/mir/bignum/internal/ryu/generic_128.d b/source/mir/bignum/internal/ryu/generic_128.d
new file mode 100644
index 00000000..9d74723e
--- /dev/null
+++ b/source/mir/bignum/internal/ryu/generic_128.d
@@ -0,0 +1,850 @@
+// Converted and then optimised from generic_128.h and generic_128.c
+// Copyright 2018 Ulf Adams (original code https://github.com/ulfjack/ryu)
+// Copyright 2020 Ilya Yaroshenko (2020 D conversion and optimisation)
+// License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+
+// This is a generic 128-bit implementation of float to shortest conversion
+// using the Ryu algorithm. It can handle any IEEE-compatible floating-point
+// type up to 128 bits. In order to use this correctly, you must use the
+// appropriate *_to_fd128 function for the underlying type - DO NOT CAST your
+// input to another floating-point type, doing so will result in incorrect
+// output!
+//
+// For any floating-point type that is not natively defined by the compiler,
+// you can use genericBinaryToDecimal to work directly on the underlying bit
+// representation.
+
+module mir.bignum.internal.ryu.generic_128;
+
+version(BigEndian)
+    static assert (0, "Let us know if you are using Mir on BigEndian target and we will add support for this module.");
+
+debug(ryu) import core.stdc.stdio;
+
+import mir.bignum.decimal: Decimal;
+import mir.bignum.fixed: UInt, extendedMulHigh, extendedMul;
+
+@safe pure nothrow @nogc:
+
+// Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 32768.
+uint pow5bits(const int e)
+{
+    version(LDC) pragma(inline, true);
+    assert(e >= 0);
+    assert(e <= 1 << 15);
+    return cast(uint) (((e * 163391164108059UL) >> 46) + 1);
+}
+
+void mul_128_256_shift(const UInt!128 a, const UInt!256 b, const uint shift, const uint corr, ref UInt!256 result)
+{
+    version(LDC) pragma(inline, true);
+    assert(shift > 0);
+    assert(shift < 256);
+    result = (extendedMul(a, b) >> shift).toSize!256 + corr;
+}
+
+// Computes 5^i in the form required by Ryu, and stores it in the given pointer.
+void generic_computePow5(const uint i, ref UInt!256 result)
+{
+    version(LDC) pragma(inline, true);
+    const uint base = i / POW5_TABLE_SIZE;
+    const uint base2 = base * POW5_TABLE_SIZE;
+    const mul = UInt!256(GENERIC_POW5_SPLIT[base]);
+    if (i == base2)
+    {
+        result = mul;
+    }
+    else
+    {
+        const uint offset = i - base2;
+        const m = UInt!128(GENERIC_POW5_TABLE[offset]);
+        const uint delta = pow5bits(i) - pow5bits(base2);
+        const uint corr = cast(uint) ((POW5_ERRORS[i / 32] >> (2 * (i % 32))) & 3);
+        mul_128_256_shift(m, mul, delta, corr, result);
+    }
+}
+
+version(mir_bignum_test) unittest
+{
+    // We only test a few entries - we could test the fUL table instead, but should we?
+    static immutable uint[10] EXACT_POW5_IDS = [1, 10, 55, 56, 300, 1000, 2345, 3210, 4968 - 3, 4968 - 1];
+
+    static immutable ulong[4][10] EXACT_POW5 = [
+        [                   0u,                    0u,                    0u,  90071992547409920u],
+        [                   0u,                    0u,                    0u,  83886080000000000u],
+        [                   0u, 15708555500268290048u, 14699724349295723422u, 117549435082228750u],
+        [                   0u,  5206161169240293376u,  4575641699882439235u,  73468396926392969u],
+        [ 2042133660145364371u,  9702060195405861314u,  6467325284806654637u, 107597969523956154u],
+        [15128847313296546509u, 11916317791371073891u,   788593023170869613u, 137108429762886488u],
+        [10998857860460266920u,   858411415306315808u, 12732466392391605111u, 136471991906002539u],
+        [ 5404652432687674341u, 18039986361557197657u,  2228774284272261859u,  94370442653226447u],
+        [15313487127299642753u,  9780770376910163681u, 15213531439620567348u,  93317108016191349u],
+        [ 7928436552078881485u,   723697829319983520u,   932817143438521969u,  72903990637649492u],
+    ];
+
+    for (int i = 0; i < 10; i++)
+    {
+        UInt!256 result;
+        generic_computePow5(EXACT_POW5_IDS[i], result);
+        assert(UInt!256(EXACT_POW5[i]) == result);
+    }
+}
+
+// Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
+void generic_computeInvPow5(const uint i, ref UInt!256 result)
+{
+    version(LDC) pragma(inline, true);
+    const uint base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE;
+    const uint base2 = base * POW5_TABLE_SIZE;
+    const mul = UInt!256(GENERIC_POW5_INV_SPLIT[base]); // 1/5^base2
+    if (i == base2)
+    {
+        result = mul + 1;
+    }
+    else
+    {
+        const uint offset = base2 - i;
+        const m = UInt!128(GENERIC_POW5_TABLE[offset]); // 5^offset
+        const uint delta = pow5bits(base2) - pow5bits(i);
+        const uint corr = cast(uint) ((POW5_INV_ERRORS[i / 32] >> (2 * (i % 32))) & 3) + 1;
+        mul_128_256_shift(m, mul, delta, corr, result);
+    }
+}
+
+version(mir_bignum_test) unittest
+{
+    static immutable uint[9] EXACT_INV_POW5_IDS = [10, 55, 56, 300, 1000, 2345, 3210, 4897 - 3, 4897 - 1];
+
+    static immutable ulong[4][10] EXACT_INV_POW5 = [
+        [13362655651931650467u,  3917988799323120213u,  9037289074543890586u, 123794003928538027u],
+        [  983662216614650042u, 15516934687640009097u,  8839031818921249472u,  88342353238919216u],
+        [ 1573859546583440066u,  2691002611772552616u,  6763753280790178510u, 141347765182270746u],
+        [ 1607391579053635167u,   943946735193622172u, 10726301928680150504u,  96512915280967053u],
+        [ 7238603269427345471u, 17319296798264127544u, 14852913523241959878u,  75740009093741608u],
+        [ 2734564866961569744u, 13277212449690943834u, 17231454566843360565u,  76093223027199785u],
+        [ 5348945211244460332u, 14119936934335594321u, 15647307321253222579u, 110040743956546595u],
+        [ 2848579222248330872u, 15087265905644220040u,  4449739884766224405u, 100774177495370196u],
+        [ 1432572115632717323u,  9719393440895634811u,  3482057763655621045u, 128990947194073851u],
+    ];
+
+    for (int i = 0; i < 9; i++)
+    {
+        UInt!256 result;
+        generic_computeInvPow5(EXACT_INV_POW5_IDS[i], result);
+        assert(UInt!256(EXACT_INV_POW5[i]) == result);
+    }
+}
+
+version(LittleEndian)
+    enum fiveReciprocal = UInt!128([0xCCCCCCCCCCCCCCCD, 0xCCCCCCCCCCCCCCCC]);
+else
+    enum fiveReciprocal = UInt!128([0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCD]);
+
+enum baseDiv5 = UInt!128([0x3333333333333333, 0x3333333333333333]);
+
+uint divRem5(size_t size)(ref UInt!size value)
+{
+    auto q = div5(value);
+    auto r = cast(uint)(value - q * 5);
+    value = q;
+    return r;
+}
+
+uint divRem10(size_t size)(ref UInt!size value)
+{
+    auto q = div10(value);
+    auto r = cast(uint)(value - q * 10);
+    value = q;
+    return r;
+}
+
+uint rem5(size_t size)(UInt!size value)
+{
+    return divRem5(value);
+}
+
+uint rem10(size_t size)(UInt!size value)
+{
+    return divRem10(value);
+}
+
+UInt!size div5(size_t size)(UInt!size value)
+{
+    return extendedMulHigh(value, fiveReciprocal.toSize!size) >> 2;
+}
+
+UInt!size div10(size_t size)(UInt!size value)
+{
+    return extendedMulHigh(value, fiveReciprocal.toSize!size) >> 3;
+}
+
+// Returns true if value is divisible by 5^p.
+bool multipleOfPowerOf5(size_t size)(UInt!size value, const uint p)
+{
+    enum fiveReciprocal = .fiveReciprocal.toSize!size;
+    enum baseDiv5 = .baseDiv5.toSize!size;
+    version(LDC) pragma(inline, true);
+    if (value)
+    {
+        for (uint count;; ++count)
+        {
+            if (count >= p)
+                return true;
+            value *= fiveReciprocal;
+            if (value > baseDiv5)
+                return false;
+        }
+    }
+    else
+        return p == 0;
+}
+
+version(mir_bignum_test) unittest
+{
+    assert(multipleOfPowerOf5(UInt!128(1), 0) == true);
+    assert(multipleOfPowerOf5(UInt!128(1), 1) == false);
+    assert(multipleOfPowerOf5(UInt!128(5), 1) == true);
+    assert(multipleOfPowerOf5(UInt!128(25), 2) == true);
+    assert(multipleOfPowerOf5(UInt!128(75), 2) == true);
+    assert(multipleOfPowerOf5(UInt!128(50), 2) == true);
+    assert(multipleOfPowerOf5(UInt!128(51), 2) == false);
+    assert(multipleOfPowerOf5(UInt!128(75), 4) == false);
+}
+
+// Returns true if value is divisible by 2^p.
+bool multipleOfPowerOf2(size_t size)(const UInt!size value, const uint p)
+{
+    version(LDC) pragma(inline, true);
+    return (value & ((UInt!size(1) << p) - 1)) == 0;
+}
+
+version(mir_bignum_test) unittest
+{
+    assert(multipleOfPowerOf5(UInt!128(1), 0) == true);
+    assert(multipleOfPowerOf5(UInt!128(1), 1) == false);
+    assert(multipleOfPowerOf2(UInt!128(2), 1) == true);
+    assert(multipleOfPowerOf2(UInt!128(4), 2) == true);
+    assert(multipleOfPowerOf2(UInt!128(8), 2) == true);
+    assert(multipleOfPowerOf2(UInt!128(12), 2) == true);
+    assert(multipleOfPowerOf2(UInt!128(13), 2) == false);
+    assert(multipleOfPowerOf2(UInt!128(8), 4) == false);
+}
+
+UInt!size mulShift(size_t size)(const UInt!size m, const UInt!256 mul, const uint j)
+{
+    version(LDC) pragma(inline, true);
+    assert(j > 128);
+    return (extendedMul(mul, m) >> 128 >> (j - 128)).toSize!size;
+}
+
+version(mir_bignum_test) unittest
+{
+    UInt!256 m = cast(ulong[4])[0, 0, 2, 0];
+    assert(mulShift(UInt!128(1), m, 129) == 1u);
+    assert(mulShift(UInt!128(12345), m, 129) == 12345u);
+}
+
+version(mir_bignum_test) unittest
+{
+    UInt!256 m = cast(ulong[4])[0, 0, 8, 0];
+    UInt!128 f = (UInt!128(123) << 64) | 321;
+    assert(mulShift(f, m, 131) == f);
+}
+
+// Returns floor(log_10(2^e)).
+uint log10Pow2(const int e)
+{
+    version(LDC) pragma(inline, true);
+    // The first value this approximation fails for is 2^1651 which is just greater than 10^297.
+    assert(e >= 0);
+    assert(e <= 1 << 15);
+    return (e * 0x9A209A84FBCFUL) >> 49;
+}
+
+version(mir_bignum_test) unittest
+{
+    assert(log10Pow2(1) == 0u);
+    assert(log10Pow2(5) == 1u);
+    assert(log10Pow2(1 << 15) == 9864u);
+}
+
+// Returns floor(log_10(5^e)).
+uint log10Pow5(const int e)
+{
+    version(LDC) pragma(inline, true);
+    // The first value this approximation fails for is 5^2621 which is just greater than 10^1832.
+    assert(e >= 0);
+    assert(e <= 1 << 15);
+    return (e * 0xB2EFB2BD8218UL) >> 48;
+}
+
+version(mir_bignum_test) unittest
+{
+    assert(log10Pow5(1) == 0u);
+    assert(log10Pow5(2) == 1u);
+    assert(log10Pow5(3) == 2u);
+    assert(log10Pow5(1 << 15) == 22903u);
+}
+
+debug(ryu)
+private char* s(UInt!128 v)
+{
+    import mir.conv: to;
+    return (v.to!string ~ "\0").ptr;
+}
+
+// Converts the given binary floating point number to the shortest decimal floating point number
+// that still accurately represents it.
+Decimal!(T.mant_dig < 64 ? 1 : 2) genericBinaryToDecimal(T)(const T x)
+{
+    import mir.utility: _expect;
+    import mir.math: signbit, fabs;
+    enum coefficientSize = T.mant_dig <= 64 ? 64 : 128;
+    enum workSize = T.mant_dig < 64 ? 64 : 128;
+    enum wordCount = workSize / 64;
+
+    Decimal!wordCount fd;
+    if (_expect(x != x, false))
+    {
+        fd.coefficient = 1u;
+        fd.exponent = fd.exponent.max;
+    }
+    else
+    if (_expect(x.fabs == T.infinity, false))
+    {
+        fd.exponent = fd.exponent.max;
+    }
+    else
+    if (x)
+    {
+        import mir.bignum.fp: Fp;
+        const fp = Fp!coefficientSize(x, false);
+        int e2 = cast(int) fp.exponent - 2;
+        UInt!workSize m2 = fp.coefficient;
+
+        const bool even = (fp.coefficient & 1) == 0;
+        const bool acceptBounds = even;
+
+        debug(ryu) if (!__ctfe)
+        {
+            printf("-> %s %s * 2^%d\n", (fp.sign ? "-" : "+").ptr, s(m2), e2 + 2);
+        }
+
+        // Step 2: Determine the interval of legal decimal representations.
+        const UInt!workSize mv = m2 << 2;
+        // Implicit bool -> int conversion. True is 1, false is 0.
+        const bool mmShift = fp.coefficient != (UInt!coefficientSize(1) << (T.mant_dig - 1));
+
+        // Step 3: Convert to a decimal power base using 128-bit arithmetic.
+        UInt!workSize vr, vp, vm;
+        int e10;
+        bool vmIsTrailingZeros = false;
+        bool vrIsTrailingZeros = false;
+        if (e2 >= 0)
+        {
+            // I tried special-casing q == 0, but there was no effect on performance.
+            // This expression is slightly faster than max(0, log10Pow2(e2) - 1).
+            const uint q = log10Pow2(e2) - (e2 > 3);
+            e10 = q;
+            const int k = FLOAT_128_POW5_INV_BITCOUNT + pow5bits(q) - 1;
+            const int i = -e2 + q + k;
+            UInt!256 pow5;
+            generic_computeInvPow5(q, pow5);
+            vr = mulShift(mv, pow5, i);
+            vp = mulShift(mv + 2, pow5, i);
+            vm = mulShift(mv - 1 - mmShift, pow5, i);
+            debug(ryu) if (!__ctfe)
+            {
+                printf("%s * 2^%d / 10^%d\n", s(mv), e2, q);
+                printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
+            }
+            // floor(log_5(2^128)) = 55, this is very conservative
+            if (q <= 55)
+            {
+                // Only one of mp, mv, and mm can be a multiple of 5, if any.
+                if (rem5(mv) == 0)
+                {
+                    vrIsTrailingZeros = multipleOfPowerOf5(mv, q - 1);
+                }
+                else
+                if (acceptBounds)
+                {
+                    // Same as min(e2 + (~mm & 1), pow5Factor(mm)) >= q
+                    // <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q
+                    // <=> true && pow5Factor(mm) >= q, since e2 >= q.
+                    vmIsTrailingZeros = multipleOfPowerOf5(mv - 1 - mmShift, q);
+                }
+                else
+                {
+                    // Same as min(e2 + 1, pow5Factor(mp)) >= q.
+                    vp -= multipleOfPowerOf5(mv + 2, q);
+                }
+            }
+        }
+        else
+        {
+            // This expression is slightly faster than max(0, log10Pow5(-e2) - 1).
+            const uint q = log10Pow5(-e2) - (-e2 > 1);
+            e10 = q + e2;
+            const int i = -e2 - q;
+            const int k = pow5bits(i) - FLOAT_128_POW5_BITCOUNT;
+            const int j = q - k;
+            UInt!256 pow5;
+            generic_computePow5(i, pow5);
+            vr = mulShift(mv, pow5, j);
+            vp = mulShift(mv + 2, pow5, j);
+            vm = mulShift(mv - 1 - mmShift, pow5, j);
+            debug(ryu) if (!__ctfe)
+            {
+                printf("%s * 5^%d / 10^%d\n", s(mv), -e2, q);
+                printf("%d %d %d %d\n", q, i, k, j);
+                printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
+            }
+            if (q <= 1)
+            {
+                // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
+                // mv = 4 m2, so it always has at least two trailing 0 bits.
+                vrIsTrailingZeros = true;
+                if (acceptBounds)
+                {
+                    // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1.
+                    vmIsTrailingZeros = mmShift == 1;
+                }
+                else
+                {
+                    // mp = mv + 2, so it always has at least one trailing 0 bit.
+                    --vp;
+                }
+            }
+            else
+            if (q < workSize - 1)
+            {
+                // TODO(ulfjack): Use a tighter bound here.
+                // We need to compute min(ntz(mv), pow5Factor(mv) - e2) >= q-1
+                // <=> ntz(mv) >= q-1  &&  pow5Factor(mv) - e2 >= q-1
+                // <=> ntz(mv) >= q-1    (e2 is negative and -e2 >= q)
+                // <=> (mv & ((1 << (q-1)) - 1)) == 0
+                // We also need to make sure that the left shift does not overflow.
+                vrIsTrailingZeros = multipleOfPowerOf2(mv, q - 1);
+                debug(ryu) if (!__ctfe)
+                {
+                    printf("vr is trailing zeros=%s\n", (vrIsTrailingZeros ? "true" : "false").ptr);
+                }
+            }
+        }
+        debug(ryu) if (!__ctfe)
+        {
+            printf("e10=%d\n", e10);
+            printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
+            printf("vm is trailing zeros=%s\n", (vmIsTrailingZeros ? "true" : "false").ptr);
+            printf("vr is trailing zeros=%s\n", (vrIsTrailingZeros ? "true" : "false").ptr);
+        }
+
+        // Step 4: Find the shortest decimal representation in the interval of legal representations.
+        uint removed = 0;
+        uint lastRemovedDigit = 0;
+        UInt!workSize output;
+
+        for (;;)
+        {
+            auto div10vp = div10(vp);
+            auto div10vm = div10(vm);
+            if (div10vp == div10vm)
+                break;
+            vmIsTrailingZeros &= vm - div10vm * 10 == 0;
+            vrIsTrailingZeros &= lastRemovedDigit == 0;
+            lastRemovedDigit = vr.divRem10;
+            vp = div10vp;
+            vm = div10vm;
+            ++removed;
+        }
+        debug(ryu) if (!__ctfe)
+        {
+            printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
+            printf("d-10=%s\n", (vmIsTrailingZeros ? "true" : "false").ptr);
+            printf("lastRemovedDigit=%d\n", lastRemovedDigit);
+        }
+        if (vmIsTrailingZeros)
+        {
+            for (;;)
+            {
+                auto div10vm = div10(vm);
+                if (vm - div10vm * 10)
+                    break;
+                vrIsTrailingZeros &= lastRemovedDigit == 0;
+                lastRemovedDigit = cast(uint) (vr - div10vm * 10);
+                vr = vp = vm = div10vm;
+                ++removed;
+            }
+        }
+        debug(ryu) if (!__ctfe)
+        {
+            printf("%s %d\n", s(vr), lastRemovedDigit);
+            printf("vr is trailing zeros=%s\n", (vrIsTrailingZeros ? "true" : "false").ptr);
+            printf("lastRemovedDigit=%d\n", lastRemovedDigit);
+        }
+        if (vrIsTrailingZeros && (lastRemovedDigit == 5) && ((vr & 1) == 0))
+        {
+            // Round even if the exact numbers is .....50..0.
+            lastRemovedDigit = 4;
+        }
+        // We need to take vr+1 if vr is outside bounds or we need to round up.
+        output = vr + ((vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || (lastRemovedDigit >= 5));
+
+        const int exp = e10 + removed;
+
+        debug(ryu) if (!__ctfe)
+        {
+            printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm));
+            printf("acceptBounds=%d\n", acceptBounds);
+            printf("vmIsTrailingZeros=%d\n", vmIsTrailingZeros);
+            printf("lastRemovedDigit=%d\n", lastRemovedDigit);
+            printf("vrIsTrailingZeros=%d\n", vrIsTrailingZeros);
+            printf("O=%s\n", s(output));
+            printf("EXP=%d\n", exp);
+        }
+
+        import mir.bignum.integer: BigInt;
+        fd.coefficient = BigInt!wordCount(output);
+        fd.exponent = exp;
+    }
+    fd.coefficient.sign = x.signbit;
+    return fd;
+}
+
+private enum FLOAT_128_POW5_INV_BITCOUNT = 249;
+private enum FLOAT_128_POW5_BITCOUNT = 249;
+private enum POW5_TABLE_SIZE = 56;
+
+// These tables are ~4.5 kByte total, compared to ~160 kByte for the fUL tables.
+
+// There's no way to define 128-bit constants in C, so we use little-endian
+// pairs of 64-bit constants.
+private static immutable ulong[2] [POW5_TABLE_SIZE] GENERIC_POW5_TABLE = [
+    [                   1u,                    0u],
+    [                   5u,                    0u],
+    [                  25u,                    0u],
+    [                 125u,                    0u],
+    [                 625u,                    0u],
+    [                3125u,                    0u],
+    [               15625u,                    0u],
+    [               78125u,                    0u],
+    [              390625u,                    0u],
+    [             1953125u,                    0u],
+    [             9765625u,                    0u],
+    [            48828125u,                    0u],
+    [           244140625u,                    0u],
+    [          1220703125u,                    0u],
+    [          6103515625u,                    0u],
+    [         30517578125u,                    0u],
+    [        152587890625u,                    0u],
+    [        762939453125u,                    0u],
+    [       3814697265625u,                    0u],
+    [      19073486328125u,                    0u],
+    [      95367431640625u,                    0u],
+    [     476837158203125u,                    0u],
+    [    2384185791015625u,                    0u],
+    [   11920928955078125u,                    0u],
+    [   59604644775390625u,                    0u],
+    [  298023223876953125u,                    0u],
+    [ 1490116119384765625u,                    0u],
+    [ 7450580596923828125u,                    0u],
+    [  359414837200037393u,                    2u],
+    [ 1797074186000186965u,                   10u],
+    [ 8985370930000934825u,                   50u],
+    [ 8033366502585570893u,                  252u],
+    [ 3273344365508751233u,                 1262u],
+    [16366721827543756165u,                 6310u],
+    [ 8046632842880574361u,                31554u],
+    [ 3339676066983768573u,               157772u],
+    [16698380334918842865u,               788860u],
+    [ 9704925379756007861u,              3944304u],
+    [11631138751360936073u,             19721522u],
+    [ 2815461535676025517u,             98607613u],
+    [14077307678380127585u,            493038065u],
+    [15046306170771983077u,           2465190328u],
+    [ 1444554559021708921u,          12325951644u],
+    [ 7222772795108544605u,          61629758220u],
+    [17667119901833171409u,         308148791101u],
+    [14548623214327650581u,        1540743955509u],
+    [17402883850509598057u,        7703719777548u],
+    [13227442957709783821u,       38518598887744u],
+    [10796982567420264257u,      192592994438723u],
+    [17091424689682218053u,      962964972193617u],
+    [11670147153572883801u,     4814824860968089u],
+    [ 3010503546735764157u,    24074124304840448u],
+    [15052517733678820785u,   120370621524202240u],
+    [ 1475612373555897461u,   601853107621011204u],
+    [ 7378061867779487305u,  3009265538105056020u],
+    [18443565265187884909u, 15046327690525280101u],
+];
+
+private static immutable ulong[4][89] GENERIC_POW5_SPLIT = [
+    [                    0u,                    0u,                    0u,    72057594037927936u],
+    [                    0u,  5206161169240293376u,  4575641699882439235u,    73468396926392969u],
+    [  3360510775605221349u,  6983200512169538081u,  4325643253124434363u,    74906821675075173u],
+    [ 11917660854915489451u,  9652941469841108803u,   946308467778435600u,    76373409087490117u],
+    [  1994853395185689235u, 16102657350889591545u,  6847013871814915412u,    77868710555449746u],
+    [   958415760277438274u, 15059347134713823592u,  7329070255463483331u,    79393288266368765u],
+    [  2065144883315240188u,  7145278325844925976u, 14718454754511147343u,    80947715414629833u],
+    [  8980391188862868935u, 13709057401304208685u,  8230434828742694591u,    82532576417087045u],
+    [   432148644612782575u,  7960151582448466064u, 12056089168559840552u,    84148467132788711u],
+    [   484109300864744403u, 15010663910730448582u, 16824949663447227068u,    85795995087002057u],
+    [ 14793711725276144220u, 16494403799991899904u, 10145107106505865967u,    87475779699624060u],
+    [ 15427548291869817042u, 12330588654550505203u, 13980791795114552342u,    89188452518064298u],
+    [  9979404135116626552u, 13477446383271537499u, 14459862802511591337u,    90934657454687378u],
+    [ 12385121150303452775u,  9097130814231585614u,  6523855782339765207u,    92715051028904201u],
+    [  1822931022538209743u, 16062974719797586441u,  3619180286173516788u,    94530302614003091u],
+    [ 12318611738248470829u, 13330752208259324507u, 10986694768744162601u,    96381094688813589u],
+    [ 13684493829640282333u,  7674802078297225834u, 15208116197624593182u,    98268123094297527u],
+    [  5408877057066295332u,  6470124174091971006u, 15112713923117703147u,   100192097295163851u],
+    [ 11407083166564425062u, 18189998238742408185u,  4337638702446708282u,   102153740646605557u],
+    [  4112405898036935485u,   924624216579956435u, 14251108172073737125u,   104153790666259019u],
+    [ 16996739107011444789u, 10015944118339042475u,  2395188869672266257u,   106192999311487969u],
+    [  4588314690421337879u,  5339991768263654604u, 15441007590670620066u,   108272133262096356u],
+    [  2286159977890359825u, 14329706763185060248u,  5980012964059367667u,   110391974208576409u],
+    [  9654767503237031099u, 11293544302844823188u, 11739932712678287805u,   112553319146000238u],
+    [ 11362964448496095896u,  7990659682315657680u,   251480263940996374u,   114756980673665505u],
+    [  1423410421096377129u, 14274395557581462179u, 16553482793602208894u,   117003787300607788u],
+    [  2070444190619093137u, 11517140404712147401u, 11657844572835578076u,   119294583757094535u],
+    [  7648316884775828921u, 15264332483297977688u,   247182277434709002u,   121630231312217685u],
+    [ 17410896758132241352u, 10923914482914417070u, 13976383996795783649u,   124011608097704390u],
+    [  9542674537907272703u,  3079432708831728956u, 14235189590642919676u,   126439609438067572u],
+    [ 10364666969937261816u,  8464573184892924210u, 12758646866025101190u,   128915148187220428u],
+    [ 14720354822146013883u, 11480204489231511423u,  7449876034836187038u,   131439155071681461u],
+    [  1692907053653558553u, 17835392458598425233u,  1754856712536736598u,   134012579040499057u],
+    [  5620591334531458755u, 11361776175667106627u, 13350215315297937856u,   136636387622027174u],
+    [ 17455759733928092601u, 10362573084069962561u, 11246018728801810510u,   139311567287686283u],
+    [  2465404073814044982u, 17694822665274381860u,  1509954037718722697u,   142039123822846312u],
+    [  2152236053329638369u, 11202280800589637091u, 16388426812920420176u,    72410041352485523u],
+    [ 17319024055671609028u, 10944982848661280484u,  2457150158022562661u,    73827744744583080u],
+    [ 17511219308535248024u,  5122059497846768077u,  2089605804219668451u,    75273205100637900u],
+    [ 10082673333144031533u, 14429008783411894887u, 12842832230171903890u,    76746965869337783u],
+    [ 16196653406315961184u, 10260180891682904501u, 10537411930446752461u,    78249581139456266u],
+    [ 15084422041749743389u,   234835370106753111u, 16662517110286225617u,    79781615848172976u],
+    [  8199644021067702606u,  3787318116274991885u,  7438130039325743106u,    81343645993472659u],
+    [ 12039493937039359765u,  9773822153580393709u,  5945428874398357806u,    82936258850702722u],
+    [   984543865091303961u,  7975107621689454830u,  6556665988501773347u,    84560053193370726u],
+    [  9633317878125234244u, 16099592426808915028u,  9706674539190598200u,    86215639518264828u],
+    [  6860695058870476186u,  4471839111886709592u,  7828342285492709568u,    87903640274981819u],
+    [ 14583324717644598331u,  4496120889473451238u,  5290040788305728466u,    89624690099949049u],
+    [ 18093669366515003715u, 12879506572606942994u, 18005739787089675377u,    91379436055028227u],
+    [ 17997493966862379937u, 14646222655265145582u, 10265023312844161858u,    93168537870790806u],
+    [ 12283848109039722318u, 11290258077250314935u,  9878160025624946825u,    94992668194556404u],
+    [  8087752761883078164u,  5262596608437575693u, 11093553063763274413u,    96852512843287537u],
+    [ 15027787746776840781u, 12250273651168257752u,  9290470558712181914u,    98748771061435726u],
+    [ 15003915578366724489u,  2937334162439764327u,  5404085603526796602u,   100682155783835929u],
+    [  5225610465224746757u, 14932114897406142027u,  2774647558180708010u,   102653393903748137u],
+    [ 17112957703385190360u, 12069082008339002412u,  3901112447086388439u,   104663226546146909u],
+    [  4062324464323300238u,  3992768146772240329u, 15757196565593695724u,   106712409346361594u],
+    [  5525364615810306701u, 11855206026704935156u, 11344868740897365300u,   108801712734172003u],
+    [  9274143661888462646u,  4478365862348432381u, 18010077872551661771u,   110931922223466333u],
+    [ 12604141221930060148u,  8930937759942591500u,  9382183116147201338u,   113103838707570263u],
+    [ 14513929377491886653u,  1410646149696279084u,   587092196850797612u,   115318278760358235u],
+    [  2226851524999454362u,  7717102471110805679u,  7187441550995571734u,   117576074943260147u],
+    [  5527526061344932763u,  2347100676188369132u, 16976241418824030445u,   119878076118278875u],
+    [  6088479778147221611u, 17669593130014777580u, 10991124207197663546u,   122225147767136307u],
+    [ 11107734086759692041u,  3391795220306863431u, 17233960908859089158u,   124618172316667879u],
+    [  7913172514655155198u, 17726879005381242552u,   641069866244011540u,   127058049470587962u],
+    [ 12596991768458713949u, 15714785522479904446u,  6035972567136116512u,   129545696547750811u],
+    [ 16901996933781815980u,  4275085211437148707u, 14091642539965169063u,   132082048827034281u],
+    [  7524574627987869240u, 15661204384239316051u,  2444526454225712267u,   134668059898975949u],
+    [  8199251625090479942u,  6803282222165044067u, 16064817666437851504u,   137304702024293857u],
+    [  4453256673338111920u, 15269922543084434181u,  3139961729834750852u,   139992966499426682u],
+    [ 15841763546372731299u,  3013174075437671812u,  4383755396295695606u,   142733864029230733u],
+    [  9771896230907310329u,  4900659362437687569u, 12386126719044266361u,    72764212553486967u],
+    [  9420455527449565190u,  1859606122611023693u,  6555040298902684281u,    74188850200884818u],
+    [  5146105983135678095u,  2287300449992174951u,  4325371679080264751u,    75641380576797959u],
+    [ 11019359372592553360u,  8422686425957443718u,  7175176077944048210u,    77122349788024458u],
+    [ 11005742969399620716u,  4132174559240043701u,  9372258443096612118u,    78632314633490790u],
+    [  8887589641394725840u,  8029899502466543662u, 14582206497241572853u,    80171842813591127u],
+    [   360247523705545899u, 12568341805293354211u, 14653258284762517866u,    81741513143625247u],
+    [ 12314272731984275834u,  4740745023227177044u,  6141631472368337539u,    83341915771415304u],
+    [   441052047733984759u,  7940090120939869826u, 11750200619921094248u,    84973652399183278u],
+    [  3436657868127012749u,  9187006432149937667u, 16389726097323041290u,    86637336509772529u],
+    [ 13490220260784534044u, 15339072891382896702u,  8846102360835316895u,    88333593597298497u],
+    [  4125672032094859833u,   158347675704003277u, 10592598512749774447u,    90063061402315272u],
+    [ 12189928252974395775u,  2386931199439295891u,  7009030566469913276u,    91826390151586454u],
+    [  9256479608339282969u,  2844900158963599229u, 11148388908923225596u,    93624242802550437u],
+    [ 11584393507658707408u,  2863659090805147914u,  9873421561981063551u,    95457295292572042u],
+    [ 13984297296943171390u,  1931468383973130608u, 12905719743235082319u,    97326236793074198u],
+    [  5837045222254987499u, 10213498696735864176u, 14893951506257020749u,    99231769968645227u],
+];
+
+// Unfortunately, the results are sometimes off by one or two. We use an additional
+// lookup table to store those cases and adjust the result.
+private static immutable ulong[156] POW5_ERRORS = [
+    0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x9555596400000000u,
+    0x65a6569525565555u, 0x4415551445449655u, 0x5105015504144541u, 0x65a69969a6965964u,
+    0x5054955969959656u, 0x5105154515554145u, 0x4055511051591555u, 0x5500514455550115u,
+    0x0041140014145515u, 0x1005440545511051u, 0x0014405450411004u, 0x0414440010500000u,
+    0x0044000440010040u, 0x5551155000004001u, 0x4554555454544114u, 0x5150045544005441u,
+    0x0001111400054501u, 0x6550955555554554u, 0x1504159645559559u, 0x4105055141454545u,
+    0x1411541410405454u, 0x0415555044545555u, 0x0014154115405550u, 0x1540055040411445u,
+    0x0000000500000000u, 0x5644000000000000u, 0x1155555591596555u, 0x0410440054569565u,
+    0x5145100010010005u, 0x0555041405500150u, 0x4141450455140450u, 0x0000000144000140u,
+    0x5114004001105410u, 0x4444100404005504u, 0x0414014410001015u, 0x5145055155555015u,
+    0x0141041444445540u, 0x0000100451541414u, 0x4105041104155550u, 0x0500501150451145u,
+    0x1001050000004114u, 0x5551504400141045u, 0x5110545410151454u, 0x0100001400004040u,
+    0x5040010111040000u, 0x0140000150541100u, 0x4400140400104110u, 0x5011014405545004u,
+    0x0000000044155440u, 0x0000000010000000u, 0x1100401444440001u, 0x0040401010055111u,
+    0x5155155551405454u, 0x0444440015514411u, 0x0054505054014101u, 0x0451015441115511u,
+    0x1541411401140551u, 0x4155104514445110u, 0x4141145450145515u, 0x5451445055155050u,
+    0x4400515554110054u, 0x5111145104501151u, 0x565a655455500501u, 0x5565555555525955u,
+    0x0550511500405695u, 0x4415504051054544u, 0x6555595965555554u, 0x0100915915555655u,
+    0x5540001510001001u, 0x5450051414000544u, 0x1405010555555551u, 0x5555515555644155u,
+    0x5555055595496555u, 0x5451045004415000u, 0x5450510144040144u, 0x5554155555556455u,
+    0x5051555495415555u, 0x5555554555555545u, 0x0000000010005455u, 0x4000005000040000u,
+    0x5565555555555954u, 0x5554559555555505u, 0x9645545495552555u, 0x4000400055955564u,
+    0x0040000000000001u, 0x4004100100000000u, 0x5540040440000411u, 0x4565555955545644u,
+    0x1140659549651556u, 0x0100000410010000u, 0x5555515400004001u, 0x5955545555155255u,
+    0x5151055545505556u, 0x5051454510554515u, 0x0501500050415554u, 0x5044154005441005u,
+    0x1455445450550455u, 0x0010144055144545u, 0x0000401100000004u, 0x1050145050000010u,
+    0x0415004554011540u, 0x1000510100151150u, 0x0100040400001144u, 0x0000000000000000u,
+    0x0550004400000100u, 0x0151145041451151u, 0x0000400400005450u, 0x0000100044010004u,
+    0x0100054100050040u, 0x0504400005410010u, 0x4011410445500105u, 0x0000404000144411u,
+    0x0101504404500000u, 0x0000005044400400u, 0x0000000014000100u, 0x0404440414000000u,
+    0x5554100410000140u, 0x4555455544505555u, 0x5454105055455455u, 0x0115454155454015u,
+    0x4404110000045100u, 0x4400001100101501u, 0x6596955956966a94u, 0x0040655955665965u,
+    0x5554144400100155u, 0xa549495401011041u, 0x5596555565955555u, 0x5569965959549555u,
+    0x969565a655555456u, 0x0000001000000000u, 0x0000000040000140u, 0x0000040100000000u,
+    0x1415454400000000u, 0x5410415411454114u, 0x0400040104000154u, 0x0504045000000411u,
+    0x0000001000000010u, 0x5554000000001040u, 0x5549155551556595u, 0x1455541055515555u,
+    0x0510555454554541u, 0x9555555555540455u, 0x6455456555556465u, 0x4524565555654514u,
+    0x5554655255559545u, 0x9555455441155556u, 0x0000000051515555u, 0x0010005040000550u,
+    0x5044044040000000u, 0x1045040440010500u, 0x0000400000040000u, 0x0000000000000000u,
+];
+
+private static immutable ulong[4][89] GENERIC_POW5_INV_SPLIT = [
+    [                    0u,                    0u,                    0u,   144115188075855872u ],
+    [  1573859546583440065u,  2691002611772552616u,  6763753280790178510u,   141347765182270746u ],
+    [ 12960290449513840412u, 12345512957918226762u, 18057899791198622765u,   138633484706040742u ],
+    [  7615871757716765416u,  9507132263365501332u,  4879801712092008245u,   135971326161092377u ],
+    [  7869961150745287587u,  5804035291554591636u,  8883897266325833928u,   133360288657597085u ],
+    [  2942118023529634767u, 15128191429820565086u, 10638459445243230718u,   130799390525667397u ],
+    [ 14188759758411913794u,  5362791266439207815u,  8068821289119264054u,   128287668946279217u ],
+    [  7183196927902545212u,  1952291723540117099u, 12075928209936341512u,   125824179589281448u ],
+    [  5672588001402349748u, 17892323620748423487u,  9874578446960390364u,   123407996258356868u ],
+    [  4442590541217566325u,  4558254706293456445u, 10343828952663182727u,   121038210542800766u ],
+    [  3005560928406962566u,  2082271027139057888u, 13961184524927245081u,   118713931475986426u ],
+    [ 13299058168408384786u, 17834349496131278595u,  9029906103900731664u,   116434285200389047u ],
+    [  5414878118283973035u, 13079825470227392078u, 17897304791683760280u,   114198414639042157u ],
+    [ 14609755883382484834u, 14991702445765844156u,  3269802549772755411u,   112005479173303009u ],
+    [ 15967774957605076027u,  2511532636717499923u, 16221038267832563171u,   109854654326805788u ],
+    [  9269330061621627145u,  3332501053426257392u, 16223281189403734630u,   107745131455483836u ],
+    [ 16739559299223642282u,  1873986623300664530u,  6546709159471442872u,   105676117443544318u ],
+    [ 17116435360051202055u,  1359075105581853924u,  2038341371621886470u,   103646834405281051u ],
+    [ 17144715798009627550u,  3201623802661132408u,  9757551605154622431u,   101656519392613377u ],
+    [ 17580479792687825857u,  6546633380567327312u, 15099972427870912398u,    99704424108241124u ],
+    [  9726477118325522902u, 14578369026754005435u, 11728055595254428803u,    97789814624307808u ],
+    [   134593949518343635u,  5715151379816901985u,  1660163707976377376u,    95911971106466306u ],
+    [  5515914027713859358u,  7124354893273815720u,  5548463282858794077u,    94070187543243255u ],
+    [  6188403395862945512u,  5681264392632320838u, 15417410852121406654u,    92263771480600430u ],
+    [ 15908890877468271457u, 10398888261125597540u,  4817794962769172309u,    90492043761593298u ],
+    [  1413077535082201005u, 12675058125384151580u,  7731426132303759597u,    88754338271028867u ],
+    [  1486733163972670293u, 11369385300195092554u, 11610016711694864110u,    87050001685026843u ],
+    [  8788596583757589684u,  3978580923851924802u,  9255162428306775812u,    85378393225389919u ],
+    [  7203518319660962120u, 15044736224407683725u,  2488132019818199792u,    83738884418690858u ],
+    [  4004175967662388707u, 18236988667757575407u, 15613100370957482671u,    82130858859985791u ],
+    [ 18371903370586036463u,    53497579022921640u, 16465963977267203307u,    80553711981064899u ],
+    [ 10170778323887491315u,  1999668801648976001u, 10209763593579456445u,    79006850823153334u ],
+    [ 17108131712433974546u, 16825784443029944237u,  2078700786753338945u,    77489693813976938u ],
+    [ 17221789422665858532u, 12145427517550446164u,  5391414622238668005u,    76001670549108934u ],
+    [  4859588996898795878u,  1715798948121313204u,  3950858167455137171u,    74542221577515387u ],
+    [ 13513469241795711526u,   631367850494860526u, 10517278915021816160u,    73110798191218799u ],
+    [ 11757513142672073111u,  2581974932255022228u, 17498959383193606459u,   143413724438001539u ],
+    [ 14524355192525042817u,  5640643347559376447u,  1309659274756813016u,   140659771648132296u ],
+    [  2765095348461978538u, 11021111021896007722u,  3224303603779962366u,   137958702611185230u ],
+    [ 12373410389187981037u, 13679193545685856195u, 11644609038462631561u,   135309501808182158u ],
+    [ 12813176257562780151u,  3754199046160268020u,  9954691079802960722u,   132711173221007413u ],
+    [ 17557452279667723458u,  3237799193992485824u, 17893947919029030695u,   130162739957935629u ],
+    [ 14634200999559435155u,  4123869946105211004u,  6955301747350769239u,   127663243886350468u ],
+    [  2185352760627740240u,  2864813346878886844u, 13049218671329690184u,   125211745272516185u ],
+    [  6143438674322183002u, 10464733336980678750u,  6982925169933978309u,   122807322428266620u ],
+    [  1099509117817174576u, 10202656147550524081u,   754997032816608484u,   120449071364478757u ],
+    [  2410631293559367023u, 17407273750261453804u, 15307291918933463037u,   118136105451200587u ],
+    [ 12224968375134586697u,  1664436604907828062u, 11506086230137787358u,   115867555084305488u ],
+    [  3495926216898000888u, 18392536965197424288u, 10992889188570643156u,   113642567358547782u ],
+    [  8744506286256259680u,  3966568369496879937u, 18342264969761820037u,   111460305746896569u ],
+    [  7689600520560455039u,  5254331190877624630u,  9628558080573245556u,   109319949786027263u ],
+    [ 11862637625618819436u,  3456120362318976488u, 14690471063106001082u,   107220694767852583u ],
+    [  5697330450030126444u, 12424082405392918899u,   358204170751754904u,   105161751436977040u ],
+    [ 11257457505097373622u, 15373192700214208870u,   671619062372033814u,   103142345693961148u ],
+    [ 16850355018477166700u,  1913910419361963966u,  4550257919755970531u,   101161718304283822u ],
+    [  9670835567561997011u, 10584031339132130638u,  3060560222974851757u,    99219124612893520u ],
+    [  7698686577353054710u, 11689292838639130817u, 11806331021588878241u,    97313834264240819u ],
+    [ 12233569599615692137u,  3347791226108469959u, 10333904326094451110u,    95445130927687169u ],
+    [ 13049400362825383933u, 17142621313007799680u,  3790542585289224168u,    93612312028186576u ],
+    [ 12430457242474442072u,  5625077542189557960u, 14765055286236672238u,    91814688482138969u ],
+    [  4759444137752473128u,  2230562561567025078u,  4954443037339580076u,    90051584438315940u ],
+    [  7246913525170274758u,  8910297835195760709u,  4015904029508858381u,    88322337023761438u ],
+    [ 12854430245836432067u,  8135139748065431455u, 11548083631386317976u,    86626296094571907u ],
+    [  4848827254502687803u,  4789491250196085625u,  3988192420450664125u,    84962823991462151u ],
+    [  7435538409611286684u,   904061756819742353u, 14598026519493048444u,    83331295300025028u ],
+    [ 11042616160352530997u,  8948390828345326218u, 10052651191118271927u,    81731096615594853u ],
+    [ 11059348291563778943u, 11696515766184685544u,  3783210511290897367u,    80161626312626082u ],
+    [  7020010856491885826u,  5025093219346041680u,  8960210401638911765u,    78622294318500592u ],
+    [ 17732844474490699984u,  7820866704994446502u,  6088373186798844243u,    77112521891678506u ],
+    [   688278527545590501u,  3045610706602776618u,  8684243536999567610u,    75631741404109150u ],
+    [  2734573255120657297u,  3903146411440697663u,  9470794821691856713u,    74179396127820347u ],
+    [ 15996457521023071259u,  4776627823451271680u, 12394856457265744744u,    72754940025605801u ],
+    [ 13492065758834518331u,  7390517611012222399u,  1630485387832860230u,   142715675091463768u ],
+    [ 13665021627282055864u,  9897834675523659302u, 17907668136755296849u,   139975126841173266u ],
+    [  9603773719399446181u, 10771916301484339398u, 10672699855989487527u,   137287204938390542u ],
+    [  3630218541553511265u,  8139010004241080614u,  2876479648932814543u,   134650898807055963u ],
+    [  8318835909686377084u,  9525369258927993371u,  2796120270400437057u,   132065217277054270u ],
+    [ 11190003059043290163u, 12424345635599592110u, 12539346395388933763u,   129529188211565064u ],
+    [  8701968833973242276u,   820569587086330727u,  2315591597351480110u,   127041858141569228u ],
+    [  5115113890115690487u, 16906305245394587826u,  9899749468931071388u,   124602291907373862u ],
+    [ 15543535488939245974u, 10945189844466391399u,  3553863472349432246u,   122209572307020975u ],
+    [  7709257252608325038u,  1191832167690640880u, 15077137020234258537u,   119862799751447719u ],
+    [  7541333244210021737u,  9790054727902174575u,  5160944773155322014u,   117561091926268545u ],
+    [ 12297384708782857832u,  1281328873123467374u,  4827925254630475769u,   115303583460052092u ],
+    [ 13243237906232367265u, 15873887428139547641u,  3607993172301799599u,   113089425598968120u ],
+    [ 11384616453739611114u, 15184114243769211033u, 13148448124803481057u,   110917785887682141u ],
+    [ 17727970963596660683u,  1196965221832671990u, 14537830463956404138u,   108787847856377790u ],
+    [ 17241367586707330931u,  8880584684128262874u, 11173506540726547818u,   106698810713789254u ],
+    [  7184427196661305643u, 14332510582433188173u, 14230167953789677901u,   104649889046128358u ],
+];
+
+private static immutable ulong[154] POW5_INV_ERRORS = [
+    0x1144155514145504u, 0x0000541555401141u, 0x0000000000000000u, 0x0154454000000000u,
+    0x4114105515544440u, 0x0001001111500415u, 0x4041411410011000u, 0x5550114515155014u,
+    0x1404100041554551u, 0x0515000450404410u, 0x5054544401140004u, 0x5155501005555105u,
+    0x1144141000105515u, 0x0541500000500000u, 0x1104105540444140u, 0x4000015055514110u,
+    0x0054010450004005u, 0x4155515404100005u, 0x5155145045155555u, 0x1511555515440558u,
+    0x5558544555515555u, 0x0000000000000010u, 0x5004000000000050u, 0x1415510100000010u,
+    0x4545555444514500u, 0x5155151555555551u, 0x1441540144044554u, 0x5150104045544400u,
+    0x5450545401444040u, 0x5554455045501400u, 0x4655155555555145u, 0x1000010055455055u,
+    0x1000004000055004u, 0x4455405104000005u, 0x4500114504150545u, 0x0000000014000000u,
+    0x5450000000000000u, 0x5514551511445555u, 0x4111501040555451u, 0x4515445500054444u,
+    0x5101500104100441u, 0x1545115155545055u, 0x0000000000000000u, 0x1554000000100000u,
+    0x5555545595551555u, 0x5555051851455955u, 0x5555555555555559u, 0x0000400011001555u,
+    0x0000004400040000u, 0x5455511555554554u, 0x5614555544115445u, 0x6455156145555155u,
+    0x5455855455415455u, 0x5515555144555545u, 0x0114400000145155u, 0x0000051000450511u,
+    0x4455154554445100u, 0x4554150141544455u, 0x65955555559a5965u, 0x5555555854559559u,
+    0x9569654559616595u, 0x1040044040005565u, 0x1010010500011044u, 0x1554015545154540u,
+    0x4440555401545441u, 0x1014441450550105u, 0x4545400410504145u, 0x5015111541040151u,
+    0x5145051154000410u, 0x1040001044545044u, 0x4001400000151410u, 0x0540000044040000u,
+    0x0510555454411544u, 0x0400054054141550u, 0x1001041145001100u, 0x0000000140000000u,
+    0x0000000014100000u, 0x1544005454000140u, 0x4050055505445145u, 0x0011511104504155u,
+    0x5505544415045055u, 0x1155154445515554u, 0x0000000000004555u, 0x0000000000000000u,
+    0x5101010510400004u, 0x1514045044440400u, 0x5515519555515555u, 0x4554545441555545u,
+    0x1551055955551515u, 0x0150000011505515u, 0x0044005040400000u, 0x0004001004010050u,
+    0x0000051004450414u, 0x0114001101001144u, 0x0401000001000001u, 0x4500010001000401u,
+    0x0004100000005000u, 0x0105000441101100u, 0x0455455550454540u, 0x5404050144105505u,
+    0x4101510540555455u, 0x1055541411451555u, 0x5451445110115505u, 0x1154110010101545u,
+    0x1145140450054055u, 0x5555565415551554u, 0x1550559555555555u, 0x5555541545045141u,
+    0x4555455450500100u, 0x5510454545554555u, 0x1510140115045455u, 0x1001050040111510u,
+    0x5555454555555504u, 0x9954155545515554u, 0x6596656555555555u, 0x0140410051555559u,
+    0x0011104010001544u, 0x965669659a680501u, 0x5655a55955556955u, 0x4015111014404514u,
+    0x1414155554505145u, 0x0540040011051404u, 0x1010000000015005u, 0x0010054050004410u,
+    0x5041104014000100u, 0x4440010500100001u, 0x1155510504545554u, 0x0450151545115541u,
+    0x4000100400110440u, 0x1004440010514440u, 0x0000115050450000u, 0x0545404455541500u,
+    0x1051051555505101u, 0x5505144554544144u, 0x4550545555515550u, 0x0015400450045445u,
+    0x4514155400554415u, 0x4555055051050151u, 0x1511441450001014u, 0x4544554510404414u,
+    0x4115115545545450u, 0x5500541555551555u, 0x5550010544155015u, 0x0144414045545500u,
+    0x4154050001050150u, 0x5550511111000145u, 0x1114504055000151u, 0x5104041101451040u,
+    0x0010501401051441u, 0x0010501450504401u, 0x4554585440044444u, 0x5155555951450455u,
+    0x0040000400105555u, 0x0000000000000001u,
+];
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index b1ede754..989c6c40 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1,7 +1,7 @@
 /++
 Low-level betterC utilities for big integer arithmetic libraries.
 
-The module provides $(REF BigUIntAccumulator), $(REF BigUIntView), and $(LREF BigIntView).
+The module provides $(REF BigUIntAccumulator), $(REF BigUIntView), and $(LREF BigIntView),  $(REF DecimalView).
 
 Note:
     The module doesn't provide full arithmetic API for now.
@@ -95,6 +95,28 @@ private template MaxFpPow5(T)
         static assert(0, "floating point format isn't supported");
 }
 
+/++
+Fast integer computation of `ceil(log10(exp2(e)))` with 64-bit mantissa precision.
+The result is guaranted to be greater then `log10(exp2(e))`, which is irrational number.
++/
+T ceilLog10Exp2(T)(const T e)
+    @safe pure nothrow @nogc
+    if (is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong))
+{
+    import mir.utility: extMul;
+    auto result = extMul(0x9a209a84fbcff799UL, e);
+    return  cast(T) ((result.high >> 1) + ((result.low != 0) | (result.high & 1)));
+}
+
+///
+version(mir_bignum_test)
+@safe pure nothrow @nogc unittest
+{
+    assert(ceilLog10Exp2(ubyte(10)) == 4); // ubyte
+    assert(ceilLog10Exp2(10U) == 4); // uint
+    assert(ceilLog10Exp2(10UL) == 4); // ulong
+}
+
 /++
 Arbitrary length unsigned integer view.
 +/
@@ -475,30 +497,32 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    static BigUIntView fromHexString(scope const(char)[] str)
+    static BigUIntView fromHexString(C)(scope const(C)[] str)
         @trusted pure
+        if (isSomeChar!C)
     {
         auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
         auto data = new Unqual!W[length];
-        BigUIntView!(Unqual!W, endian)(data).fromHexStringImpl(str);
-        return BigUIntView(cast(W[])data);
+        if (BigUIntView!(Unqual!W, endian)(data).fromHexStringImpl(str))
+            return BigUIntView(cast(W[])data);
+        version(D_Exceptions)
+            throw hexStringException;
+        else
+            assert(0, hexStringErrorMsg);
     }
 
     static if (isMutable!W)
     /++
     +/
-    void fromHexStringImpl(scope const(char)[] str)
-        @safe pure @nogc
+    bool fromHexStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
     {
         pragma(inline, false);
         import mir.utility: _expect;
         if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false))
-        {
-            version(D_Exceptions)
-                throw hexStringException;
-            else
-                assert(0, hexStringErrorMsg);
-        }
+            return false;
+        coefficients = coefficients[0 .. str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0)];
         auto rdata = leastSignificantFirst;
         W current;
         size_t i;
@@ -529,11 +553,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                 case 'e': c = 0xE; break;
                 case 'F':
                 case 'f': c = 0xF; break;
-                default:
-                    version(D_Exceptions)
-                        throw hexStringException;
-                    else
-                        assert(0, hexStringErrorMsg);
+                default: return false;
             }
             enum s = W.sizeof * 8 - 4;
             W cc = cast(W)(W(c) << s);
@@ -552,6 +572,88 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
             current >>>= 4 * (W.sizeof * 2 - i % (W.sizeof * 2));
             rdata.front = current;
         }
+        return true;
+    }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Returns: false in case of overflow or incorrect string.
+    Precondition: non-empty coefficients
+    Note: doesn't support signs.
+    +/
+    bool fromStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        import mir.utility: _expect;
+
+        assert(coefficients.length);
+
+        if (_expect(str.length == 0, false))
+            return false;
+
+        leastSignificant = 0;
+        auto work = topLeastSignificantPart(1);
+
+        uint d = str[0] - '0';
+        str = str[1 .. $];
+
+        W v;
+        W t = 1;
+
+        if (d == 0)
+        {
+            if (str.length == 0)
+                return true;
+            if (str[0] == '0')
+                return false;
+            goto S;
+        }
+        else
+        if (d < 10)
+        {
+            goto S;
+        }
+        else
+            return false;
+
+        for(;;)
+        {
+            enum mp10 = W(10) ^^ MaxWordPow10!W;
+            d = str[0] - '0';
+            str = str[1 .. $];
+            if (_expect(d > 10, false))
+                break;
+            v *= 10;
+    S:
+            t *= 10;
+            v += d;
+
+            if (_expect(t == mp10 || str.length == 0, false))
+            {
+            L:
+                if (auto overflow = work.opOpAssign!"*"(t, v))
+                {
+                    if (_expect(work.coefficients.length < coefficients.length, true))
+                    {
+                        work = topLeastSignificantPart(work.coefficients.length + 1);
+                        work.mostSignificant = overflow;
+                    }
+                    else
+                    {
+                        return false;
+                    }
+                }
+                v = 0;
+                t = 1;
+                if (str.length == 0)
+                {
+                    this = work;
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     static if (isMutable!W && W.sizeof >= 4)
@@ -665,6 +767,47 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return overflow;
     }
 
+    static if (isMutable!W && W.sizeof == 4 || W.sizeof == 8 && endian == TargetEndian)
+    /++
+    Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation.
+    Precondition: non-empty coefficients, `overflow < rhs`
+    Params:
+        rhs = unsigned value to devide by
+        overflow = initial unsigned overflow
+    Returns:
+        unsigned remainder value (evaluated overflow)
+    +/
+    uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0)
+        @safe pure nothrow @nogc
+    {
+        assert(overflow < rhs);
+        assert(coefficients.length);
+        static if (W.sizeof == 4)
+        {
+            auto ns = this.mostSignificantFirst;
+            size_t i;
+            do
+            {
+                auto ext = (ulong(overflow) << 32) ^ ns[i];
+                ns[i] = cast(uint)(ext / rhs);
+                overflow = ext % rhs;
+            }
+            while (++i < ns.length);
+            if (mostSignificant == 0)
+                popMostSignificant;
+            return overflow;
+        }
+        else
+        {
+            auto work = opCast!(BigUIntView!uint);
+            if (work.mostSignificant == 0)
+                work.popMostSignificant;
+            auto remainder = work.opOpAssign!op(rhs, overflow);
+            coefficients = coefficients[0 .. work.coefficients.length / 2 + work.coefficients.length % 2];
+            return remainder;
+        }
+    }
+
     static if (isMutable!W && W.sizeof == size_t.sizeof)
     /++
     Performs `W overflow = (big += overflow) *= scalar` operatrion.
@@ -961,6 +1104,58 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         }
         return rhs == 0;
     }
+
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Params:
+        str = string buffer, the tail paer 
+    Precondition: mutable number with word size at least 4 bytes
+    Postconditoin: the number is destroyed
+    Returns: last N bytes used in the buffer
+    +/
+    size_t toStringImpl(C)(scope C[] str)
+        @safe pure nothrow @nogc
+        if (isSomeChar!C && isMutable!C)
+    {
+        assert(str.length);
+        assert(str.length >= ceilLog10Exp2(coefficients.length * (W.sizeof * 8)));
+
+        size_t i = str.length;
+        while(coefficients.length > 1)
+        {
+            uint rem = this /= 1_000_000_000;
+            foreach (_; 0 .. 9)
+            {
+                str[--i] = cast(char)(rem % 10 + '0');
+                rem /= 10;
+            }
+        }
+
+        W rem = coefficients.length == 1 ? coefficients[0] : W(0);
+        do
+        {
+            str[--i] = cast(char)(rem % 10 + '0');
+            rem /= 10;
+        }
+        while(rem);
+
+        return str.length - i;
+    }
+
+    ///
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        import mir.bignum.integer;
+
+        auto a = BigInt!2("123456789098765432123456789098765432100");
+        char[ceilLog10Exp2(a.data.length * (size_t.sizeof * 8))] buffer;
+        auto len = a.view.unsigned.toStringImpl(buffer);
+        assert(buffer[$ - len .. $] == "123456789098765432123456789098765432100", buffer[$ - len .. $]);
+    }
 }
 
 ///
@@ -1074,6 +1269,81 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         this.sign = sign;
     }
 
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Returns: false in case of overflow or incorrect string.
+    Precondition: non-empty coefficients.
+    +/
+    bool fromStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        import mir.utility: _expect;
+
+        if (_expect(str.length == 0, false))
+            return false;
+
+        if (str[0] == '-')
+        {
+            sign = true;
+            str = str[1 .. $];
+        }
+        else
+        if (_expect(str[0] == '+', false))
+        {
+            str = str[1 .. $];
+        }
+
+        return unsigned.fromStringImpl(str);
+    }
+
+    /++
+    +/
+    static BigIntView fromHexString(C)(scope const(C)[] str)
+        @trusted pure
+        if (isSomeChar!C)
+    {
+        auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
+        auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]);
+        if (ret.fromHexStringImpl(str))
+            return  cast(BigIntView) ret;
+        version(D_Exceptions)
+            throw hexStringException;
+        else
+            assert(0, hexStringErrorMsg);
+    }
+
+    static if (isMutable!W)
+    /++
+    +/
+    bool fromHexStringImpl(C)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        pragma(inline, false);
+        import mir.utility: _expect;
+
+        assert(unsigned.coefficients.length);
+
+        if (_expect(str.length == 0, false))
+            return false;
+
+        sign = false;
+
+        if (str[0] == '-')
+        {
+            sign = true;
+            str = str[1 .. $];
+        }
+        else
+        if (_expect(str[0] == '+', false))
+        {
+            str = str[1 .. $];
+        }
+
+        return unsigned.fromHexStringImpl(str);
+    }
+
     ///
     T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T)
@@ -1090,7 +1360,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     version(mir_bignum_test)
     unittest
     {
-        auto a = cast(double) -BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        auto a = cast(double) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
@@ -1113,7 +1383,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         import mir.bignum.fixed: UInt;
         import mir.bignum.fp: Fp;
 
-        auto fp = cast(Fp!128) -BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        auto fp = cast(Fp!128) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
         assert(fp.sign);
         assert(fp.exponent == 0);
         assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
@@ -1187,7 +1457,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         if (op == "+" || op == "-")
     {
         assert(rhs.coefficients.length > 0);
-        import std.conv;
+        import mir.conv;
         debug assert(this.coefficients.length >= rhs.coefficients.length, this.coefficients.length.to!string ~ " " ~ rhs.coefficients.length.to!string);
         enum sum = op == "+";
         // pos += pos
@@ -1402,6 +1672,20 @@ unittest
     }
 }
 
+///
+version(mir_bignum_test)
+unittest
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.low_level_view: BigUIntView;
+    auto bigView = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
+    auto fixed = UInt!256.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
+    auto overflow = bigView *= fixed;
+    assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398"));
+    assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
+
+}
+
 /++
 An utility type to wrap a local buffer to accumulate unsigned numbers.
 +/
@@ -1589,20 +1873,6 @@ unittest
     }
 }
 
-///
-version(mir_bignum_test)
-unittest
-{
-    import mir.bignum.fixed: UInt;
-    import mir.bignum.low_level_view: BigUIntView;
-    auto bigView = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
-    auto fixed = UInt!256.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
-    auto overflow = bigView *= fixed;
-    assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398"));
-    assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
-
-}
-
 /// Computes `13 * 10^^60`
 version(mir_bignum_test)
 @safe pure
@@ -1618,10 +1888,30 @@ unittest
     assert(accumulator.view == BigUIntView!uint.fromHexString("81704fcef32d3bd8117effd5c4389285b05d000000000000000"));
 }
 
+///
+enum DecimalExponentKey
+{
+    ///
+    none = 0,
+    ///
+    dot = '.' - '0',
+    ///
+    d = 'd' - '0',
+    ///
+    e = 'e' - '0',
+    ///
+    D = 'D' - '0',
+    ///
+    E = 'E' - '0',
+    ///
+    infinity,
+    ///
+    nan,
+}
 
 /++
 +/
-struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
+struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
     if (isUnsigned!W)
 {
     ///
@@ -1631,6 +1921,165 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
     ///
     BigUIntView!(W, endian) coefficient;
 
+    static if (isMutable!W && W.sizeof >= 4)
+    /++
+    Returns: false in case of overflow or incorrect string.
+    Precondition: non-empty coefficients
+    Note: doesn't support signs.
+    +/
+    bool fromStringImpl(C)(scope const(C)[] str, out DecimalExponentKey key)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        pragma(inline, false);
+        import mir.utility: _expect;
+
+        assert(coefficient.coefficients.length);
+
+        coefficient.leastSignificant = 0;
+        auto work = coefficient.topLeastSignificantPart(1);
+
+        if (_expect(str.length == 0, false))
+            return false;
+
+        if (str[0] == '-')
+        {
+            sign = true;
+            str = str[1 .. $];
+            if (_expect(str.length == 0, false))
+                return false;
+        }
+        else
+        if (_expect(str[0] == '+', false))
+        {
+            str = str[1 .. $];
+            if (_expect(str.length == 0, false))
+                return false;
+        }
+
+        uint d = str[0] - '0';
+        str = str[1 .. $];
+
+        W v;
+        W t = 1;
+        uint afterDot;
+        bool dot;
+        bool hasExponent;
+
+        if (d == 0)
+        {
+            if (str.length == 0)
+                return true;
+            if (str[0] == '0')
+                return false;
+            goto S;
+        }
+        else
+        if (d < 10)
+        {
+            goto S;
+        }
+        else
+        if (d == '.' - '0')
+        {
+            goto D;
+        }
+        else
+        {
+            coefficient = coefficient.init;
+            if (str.length == 2)
+            {
+                if ((d == 'i' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"nf"))
+                {
+                    key = DecimalExponentKey.infinity;
+                    return true;
+                }
+                if ((d == 'n' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"an"))
+                {
+                    key = DecimalExponentKey.nan;
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        for(;;)
+        {
+            enum mp10 = W(10) ^^ MaxWordPow10!W;
+            d = str[0] - '0';
+            str = str[1 .. $];
+
+            if (_expect(d <= 10, true))
+            {
+                v *= 10;
+        S:
+                t *= 10;
+                v += d;
+                afterDot += dot;
+
+                if (_expect(t == mp10 || str.length == 0, false))
+                {
+                L:
+                    if (auto overflow = work.opOpAssign!"*"(t, v))
+                    {
+                        if (_expect(work.coefficients.length < coefficient.coefficients.length, true))
+                        {
+                            work = coefficient.topLeastSignificantPart(work.coefficients.length + 1);
+                            work.mostSignificant = overflow;
+                        }
+                        else
+                        {
+                            return false;
+                        }
+                    }
+
+                    v = 0;
+                    t = 1;
+                    if (str.length == 0)
+                    {
+                    M:
+                        exponent -= afterDot;
+                        coefficient = work;
+                        return true;
+                    }
+                }
+
+                continue;
+            }
+            else
+            if (key != d) switch (d)
+            {
+                D:
+                case DecimalExponentKey.dot:
+                    key = cast(DecimalExponentKey)d;
+                    if (_expect(!dot, true))
+                    {
+                        dot = true;
+                        if (str.length)
+                            continue;
+                    }
+                    break;
+                case DecimalExponentKey.e:
+                case DecimalExponentKey.d:
+                case DecimalExponentKey.E:
+                case DecimalExponentKey.D:
+                    key = cast(DecimalExponentKey)d;
+                    hasExponent = true;
+                    import mir.parse: parse;
+                    if (parse(str, exponent) && str.length == 0)
+                    {
+                        if (t != 1)
+                            goto L;
+                        goto M;
+                    }
+                    break;
+                default:
+            }
+            break;
+        }
+        return false;
+    }
+
     ///
     DecimalView!(const W, endian, Exp) lightConst()()
         const @safe pure nothrow @nogc @property
@@ -1648,8 +2097,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = int)
     }
 
     /++
-    Mir parsing supports up-to quadruple precision.
-The conversion error is 0 ULP for normal numbers. 
+    Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
     T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T && isMutable!T)
diff --git a/source/mir/format.d b/source/mir/format.d
index 4bf32ee9..efcbe30d 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -17,7 +17,9 @@ unittest
     import mir.small_string;
     alias S = SmallString!32;
 
-    assert(123.0.to!S == "123");
+    // Floating-point numbers are formatted to
+    // the shortest precise exponential notation.
+    assert(123.0.to!S == "1.23e2");
     assert(123.to!(immutable S) == "123");
     assert(true.to!S == "true");
     assert(true.to!string == "true");
@@ -48,7 +50,9 @@ unittest
     import mir.small_string;
     alias S = SmallString!32;
 
-    assert(123.0.to!string == "123");
+    // Floating-point numbers are formatted to
+    // the shortest precise exponential notation.
+    assert(123.0.to!string == "1.23e2");
     assert(123.to!(char[]) == "123");
 
     assert(S("str").to!string == "str"); // GC allocated result
@@ -83,8 +87,8 @@ string text(string separator = "", A...)(auto ref A args)
 ///
 @safe pure nothrow unittest
 {
-    assert(text("str ", true, " ", 100, " ", 124.1) == "str true 100 124.1");
-    assert(text!" "("str", true, 100, 124.1) == "str true 100 124.1");
+    assert(text("str ", true, " ", 100, " ", 124.1) == "str true 100 1.241e2");
+    assert(text!" "("str", true, 100, 124.1) == "str true 100 1.241e2");
 }
 
 import mir.format_impl;
@@ -147,7 +151,7 @@ version (mir_test) unittest
 {
     auto name = "D";
     auto ver = 2.0;
-    assert(stringBuf() << "Hi " << name << ver << "!\n" << getData == "Hi D2!\n");
+    assert(stringBuf() << "Hi " << name << ver << "!\n" << getData == "Hi D2e0!\n");
 }
 
 ///
@@ -164,7 +168,7 @@ version (mir_test) unittest
 version (mir_test) unittest
 {
     auto name = "D"d;
-    auto ver = 2;
+    auto ver = 2UL;
     assert(dstringBuf() << "Hi "d  << name << ver << "!\n"d << getData == "Hi D2!\n");
 }
 
@@ -414,7 +418,7 @@ ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Arg
             return w.print!C(c);
 }
 
-///
+/// Prints enums
 ref W print(C = char, W, T)(scope return ref W w, const T c)
     if (is(T == enum))
 {
@@ -451,7 +455,7 @@ version (mir_test) unittest
     assert(w.data == "yes", w.data);
 }
 
-/// ditto
+/// Prints boolean
 ref W print(C = char, W)(scope return ref W w, bool c)
 {
     enum N = 5;
@@ -479,7 +483,7 @@ version (mir_test) unittest
     assert(w.print(false).data == `false`, w.data);
 }
 
-/// ditto
+/// Prints associative array
 pragma(inline, false)
 ref W print(C = char, W, V, K)(scope return ref W w, scope const V[K] c)
 {
@@ -513,7 +517,7 @@ version (mir_test) unittest
     assert(w.data == `["a": 1, "b": 2]` || w.data == `["b": 2, "a": 1]`, w.data);
 }
 
-/// ditto
+/// Prints array
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const(T)[] c)
     if (!isSomeChar!T)
@@ -544,7 +548,7 @@ version (mir_test) unittest
     assert(w.print(array[]).data == `["a\na", "b"]`, w.data);
 }
 
-/// ditto
+/// Prints escaped character in the form `'c'`.
 pragma(inline, false)
 ref W print(C = char, W)(scope return ref W w, char c)
 {
@@ -600,7 +604,7 @@ version (mir_test) unittest
         .data == `'\n''\'''a''\xF4'`);
 }
 
-/// ditto
+/// Prints some string
 ref W print(C = char, W)(scope return ref W w, scope const(C)[] c)
     if (isSomeChar!C)
 {
@@ -608,7 +612,7 @@ ref W print(C = char, W)(scope return ref W w, scope const(C)[] c)
     return w;
 }
 
-/// ditto
+/// Prints integers
 ref W print(C = char, W, I)(scope return ref W w, const I c)
     if (isIntegral!I && !is(I == enum))
 {
@@ -628,15 +632,16 @@ ref W print(C = char, W, I)(scope return ref W w, const I c)
     return w;
 }
 
-/// ditto
+/// Prints floating point numbers
 ref W print(C = char, W, T)(scope return ref W w, const T c)
     if(is(T == float) || is(T == double) || is(T == real))
 {
-    auto ff = FormattedFloating!T(c);
-    return w.print!C(ff);
+    import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
+    auto decimal = genericBinaryToDecimal(c);
+    return w.print!C(decimal);
 }
 
-/// ditto
+/// Prints structs and unions
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, ref const T c)
     if (is(T == struct) || is(T == union))
@@ -722,7 +727,7 @@ version (mir_test) unittest
     assert(stringBuf() << A() << S() << D() << F() << G() << getData == "asdfg");
 }
 
-/// ditto
+/// Prints classes and interfaces
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
     if (is(T == class) || is(T == interface))
diff --git a/source/mir/parse.d b/source/mir/parse.d
index cc9b59a2..dd098a4a 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -81,7 +81,7 @@ T fromString(T, C)(scope const(C)[] str)
 }
 
 ///
-version(mir_test)
+version(mir_bignum_test)
 @safe pure @nogc unittest
 {
     assert("123".fromString!int == 123);
@@ -92,6 +92,7 @@ version(mir_test)
     assert("12.3".fromString!float == 12.3f);
     assert("12.3".fromString!real == 12.3L);
     assert("-12.3e-30".fromString!double == -12.3e-30);
+    assert("2.9802322387695312E-8".fromString!double == 2.9802322387695312E-8);
 
     /// Test CTFE support  
     static assert("-12.3e-30".fromString!double == -0x1.f2f280b2414d5p-97);
@@ -124,14 +125,21 @@ bool fromString(T, C)(scope const(C)[] str, ref T value)
 {
     static if (isFloatingPoint!T)
     {
-        import mir.bignum.decimal: Decimal, DecimalExponentKey, parseDecimal;
+        import mir.bignum.decimal: Decimal, DecimalExponentKey;
         import mir.utility: _expect;
 
         Decimal!256 decimal;
         DecimalExponentKey key;
-        auto ret = parseDecimal(str, decimal, key);
+        auto ret = decimal.fromStringImpl(str, key);
         if (_expect(ret, true))
-            value =  cast(T) decimal;
+        {
+            switch(key) with(DecimalExponentKey)
+            {
+                case nan: value = decimal.coefficient.sign ? -T.nan : T.nan; break;
+                case infinity: value = decimal.coefficient.sign ? -T.infinity : T.infinity; break;
+                default: value =  cast(T) decimal; break;
+            }
+        }
         return ret;
     }
     else
diff --git a/test_travis.sh b/test_travis.sh
new file mode 100644
index 00000000..a0085a14
--- /dev/null
+++ b/test_travis.sh
@@ -0,0 +1,13 @@
+echo $ARCH
+if [ "$ARCH" = "AARCH64" ]
+then
+  docker run --rm --privileged multiarch/qemu-user-static:register --reset
+  docker build -t ion-arm64 . -f Dockerfile.aarch64
+else
+  echo $ARCH
+  dub test --arch=$ARCH --build=unittest-cov
+  # if [ \( "$DC" = "ldc2" \) -o \( "$DC" = "ldmd2" \) ]
+  # then
+  #     cd benchmarks/sajson ; dub --build=release-nobounds --compiler=ldmd2 ; cd ../..
+  # fi
+fi

From 46bd77acf31a76ce0ace6e477ad4e7bd98a4a315 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 13 Dec 2020 18:04:45 +0700
Subject: [PATCH 149/573] fix aarch docker

---
 Dockerfile.aarch64 | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64
index 93404b79..462ecdc4 100644
--- a/Dockerfile.aarch64
+++ b/Dockerfile.aarch64
@@ -6,6 +6,7 @@ RUN curl -fsSLO https://github.com/ldc-developers/ldc/releases/download/v1.24.0/
  && sudo cp -rf ldc2-1.24.0-linux-aarch64/* /usr/local \
  && rm -rf ldc*
 COPY source source
+COPY include include
 COPY meson.build meson.build
 COPY subprojects subprojects
 RUN ls subprojects && meson build -D with_test_explicit=true --default-library=static && ninja test -C build

From a1de0abd791eef32acec8abf7fa0ec23d5a56c29 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 14 Dec 2020 12:33:08 +0700
Subject: [PATCH 150/573] Decimal from FP constructors

---
 source/mir/bignum/decimal.d | 42 +++++++++++++++++++++++++++++++++++++
 source/mir/bignum/integer.d | 32 ++++++++++++++++++++++++++++
 source/mir/format.d         |  4 ++--
 3 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 89a0c636..bbdd195e 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -71,6 +71,48 @@ struct Decimal(size_t maxSize64)
         assert(cast(double) decimal.view == double(PI) / 1e40);
     }
 
+    /++
+    Constructs Decimal from the floating point number using the $(HTTPS https://github.com/ulfjack/ryu, Ryu algorithm).
+
+    The number is the shortest decimal representation that being converted back would result the same floating-point number.
+    +/
+    this(T)(const T x)
+        if (isFloatingPoint!T && maxSize64 >= 1 + (T.mant_dig >= 64))
+    {
+        import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
+        this = genericBinaryToDecimal(x);
+    }
+    
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test)
+    @safe pure nothrow @nogc
+    unittest
+    {
+        // float and double can be used to constructed Decimal of any length
+        auto decimal64 = Decimal!1(-1.235e-7);
+        assert(decimal64.exponent == -10);
+        assert(decimal64.coefficient == -1235);
+
+        // real number may need Decimal at least length of 2
+        auto decimal128 = Decimal!2(-1.235e-7L);
+        assert(decimal128.exponent == -10);
+        assert(decimal128.coefficient == -1235);
+
+        decimal128 = Decimal!2(1234e3f);
+        assert(decimal128.exponent == 3);
+        assert(decimal128.coefficient == 1234);
+    }
+
+    ///
+    ref opAssign(size_t rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return
+        if (rhsMaxSize64 < maxSize64)
+    {
+        this.exponent = rhs.exponent;
+        this.coefficient = rhs.coefficient;
+        return this;
+    }
+
     /++
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients.
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 7e8adbf5..d3247860 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -112,6 +112,23 @@ struct BigInt(size_t maxSize64)
         assert(integer == BigInt!4.fromHexString("4b313b23aa560e1b0985f89cbe6df5460860e39a64ba92b4abdd3ee77e4e05b8"));
     }
 
+    ///
+    ref opAssign(size_t rhsMaxSize64)(auto ref scope const BigInt!rhsMaxSize64 rhs) return
+        if (rhsMaxSize64 < maxSize64)
+    {
+        this.sign = rhs.sign;
+        this.length = rhs.length;
+        version(LittleEndian)
+        {
+            data[0 .. length] = rhs.data[0 .. length];
+        }
+        else
+        {
+            data[$ - length .. $] = rhs.data[$ - length .. $];
+        }
+        return this;
+    }
+
     /++
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients.
@@ -147,6 +164,21 @@ struct BigInt(size_t maxSize64)
         return view == rhs.view;
     }
 
+    ///
+    bool opEquals()(size_t rhs, bool rhsSign = false)
+        const @safe pure nothrow @nogc
+    {
+        return rhs == 0 && length == 0 || length == 1 && sign == rhsSign && view.unsigned.leastSignificant == rhs;
+    }
+
+    ///
+    bool opEquals()(sizediff_t rhs)
+        const @safe pure nothrow @nogc
+    {
+        auto sign = rhs < 0;
+        return opEquals(sign ? ulong(-rhs) : ulong(rhs), sign);
+    }
+
     /++
     +/
     auto opCmp()(auto ref const BigInt rhs) 
diff --git a/source/mir/format.d b/source/mir/format.d
index efcbe30d..a843d0dc 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -636,8 +636,8 @@ ref W print(C = char, W, I)(scope return ref W w, const I c)
 ref W print(C = char, W, T)(scope return ref W w, const T c)
     if(is(T == float) || is(T == double) || is(T == real))
 {
-    import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
-    auto decimal = genericBinaryToDecimal(c);
+    import mir.bignum.decimal;
+    auto decimal = Decimal!(T.mant_dig < 64 ? 1 : 2)(c);
     return w.print!C(decimal);
 }
 

From 8797911180ffad0ae0c44e7907a94576a96b2114 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 14 Dec 2020 13:44:32 +0700
Subject: [PATCH 151/573] fix docs

---
 source/mir/bignum/decimal.d | 2 +-
 source/mir/parse.d          | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index bbdd195e..abcbbb45 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -89,7 +89,7 @@ struct Decimal(size_t maxSize64)
     @safe pure nothrow @nogc
     unittest
     {
-        // float and double can be used to constructed Decimal of any length
+        // float and double can be used to construct Decimal of any length
         auto decimal64 = Decimal!1(-1.235e-7);
         assert(decimal64.exponent == -10);
         assert(decimal64.coefficient == -1235);
diff --git a/source/mir/parse.d b/source/mir/parse.d
index dd098a4a..cc03a04c 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -111,7 +111,6 @@ version(mir_bignum_test)
 // https://issues.dlang.org/show_bug.cgi?id=20951
 // https://issues.dlang.org/show_bug.cgi?id=20952
 // https://issues.dlang.org/show_bug.cgi?id=20953
-// https://issues.dlang.org/show_bug.cgi?id=20963
 // https://issues.dlang.org/show_bug.cgi?id=20967
 }
 

From 1e7b2ff922ddc767741520c113736c0c4daf4a0c Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Wed, 16 Dec 2020 22:28:56 +0700
Subject: [PATCH 152/573] human friendly fp spec (#325)

---
 source/mir/bignum/decimal.d        | 162 +++++++++++++++++------------
 source/mir/format.d                | 104 ++++++++++++++++--
 source/mir/format_impl.d           | 137 ++++++++++++------------
 source/mir/ndslice/concatenation.d |   3 -
 source/mir/ndslice/slice.d         |   3 -
 5 files changed, 261 insertions(+), 148 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index abcbbb45..f748ea55 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -11,7 +11,7 @@ import std.traits: isSomeChar;
 public import mir.bignum.low_level_view: DecimalExponentKey;
 import mir.bignum.low_level_view: ceilLog10Exp2;
 
-private enum expBufferShift = 2 + ceilLog10Exp2(size_t.sizeof * 8);
+private enum expBufferLength = 2 + ceilLog10Exp2(size_t.sizeof * 8);
 
 /++
 Stack-allocated decimal type.
@@ -22,6 +22,7 @@ Params:
 struct Decimal(size_t maxSize64)
     if (maxSize64 && maxSize64 <= ushort.max)
 {
+    import mir.format: NumericSpec;
     import mir.bignum.integer;
     import mir.bignum.low_level_view;
     import std.traits: isMutable, isFloatingPoint;
@@ -183,21 +184,50 @@ struct Decimal(size_t maxSize64)
         assert(!decimal.fromStringImpl("0d", key));
     }
 
-    private enum eDecimalLength = 2 + ceilLog10Exp2(coefficient.data.length * (size_t.sizeof * 8)) + expBufferShift;
+    private enum coefficientBufferLength = 2 + ceilLog10Exp2(coefficient.data.length * (size_t.sizeof * 8)); // including dot and sign
+    private enum eDecimalLength = coefficientBufferLength + expBufferLength;
 
-    private const(C)[] toStringImpl(C = char)(ref scope return C[eDecimalLength] buffer) scope const @safe pure nothrow
+    ///
+    immutable(C)[] toString(C = char)(NumericSpec spec = NumericSpec.init) const @safe pure nothrow
         if(isSomeChar!C && isMutable!C)
     {
+        import mir.appender: UnsafeArrayBuffer;
+        C[eDecimalLength] data = void;
+        auto buffer = UnsafeArrayBuffer!C(data);
+        toString(buffer, spec);
+        return buffer.data.idup;
+    }
+
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test) @safe pure unittest
+    {
+        auto str = "-3.4010447314490204552169750449563978034784726557588085989975288830070948234680e-13245";
+        auto decimal = Decimal!4(str);
+        assert(decimal.toString == str, decimal.toString);
+
+        decimal = Decimal!4.init;
+        assert(decimal.toString == "0.0");
+    }
+
+    ///
+    void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
+        if(isSomeChar!C && isMutable!C)
+    {
+        assert(spec == NumericSpec.exponent || spec == NumericSpec.human);
         import mir.utility: _expect;
         // handle special values
         if (_expect(exponent == exponent.max, false))
         {
-            immutable(C)[] nan = "nan";
-            immutable(C)[] ninf = "-inf";
-            immutable(C)[] pinf = "+inf";
-            return coefficient.length == 0 ? coefficient.sign ? ninf : pinf : nan;
+            static immutable C[3] nan = "nan";
+            static immutable C[4] ninf = "-inf";
+            static immutable C[4] pinf = "+inf";
+            w.put(coefficient.length == 0 ? coefficient.sign ? ninf[] : pinf[] : nan[]);
+            return;
         }
 
+        C[coefficientBufferLength] buffer = void;
+
         size_t coefficientLength;
         static if (size_t.sizeof == 8)
         {
@@ -221,87 +251,89 @@ struct Decimal(size_t maxSize64)
                 }
                 auto work = BigUIntView!uint(data);
                 work = work.topLeastSignificantPart(coefficient.length * 2).normalized;
-                coefficientLength = work.toStringImpl(buffer[0 .. $ - expBufferShift]);
+                coefficientLength = work.toStringImpl(buffer);
             }
             else
             {
                 BigInt!maxSize64 work = coefficient;
-                coefficientLength = work.view.unsigned.toStringImpl(buffer[0 .. $ - expBufferShift]);
+                coefficientLength = work.view.unsigned.toStringImpl(buffer);
             }
         }
         else
         {
             BigInt!maxSize64 work = coefficient;
-            coefficientLength = work.view.unsigned.toStringImpl(buffer[0 .. $ - expBufferShift]);
+            coefficientLength = work.view.unsigned.toStringImpl(buffer);
+        }
+
+        static immutable C[10] zeros = "-0.00000.0";
+
+        if (spec.format == NumericSpec.Format.human)
+        {
+            // try print decimal form without exponent
+            // up to 6 digits exluding leading 0. or final .0
+            if (this.exponent >= 0)
+            {
+                if (this.exponent + coefficientLength <= 6)
+                {
+                    buffer[$ - coefficientLength - 1] = '-';
+                    w.put(buffer[$ - coefficientLength - coefficient.sign .. $]);
+                    w.put(zeros[$ - (exponent + 2) .. $]);
+                    return;
+                }
+            }
+            else
+            if (this.exponent < 0)
+            {
+                if (this.exponent >= -6 && coefficientLength <= 6)
+                {
+                    sizediff_t zerosLength = -this.exponent - coefficientLength;
+                    if (zerosLength >= 0)
+                    {
+                        w.put(zeros[!coefficient.sign .. zerosLength + 2 + 1]);
+                        w.put(buffer[$ - coefficientLength .. $]);
+                        return;
+                    }
+                    else
+                    {
+                        buffer[$ - coefficientLength - 1] = '-';
+                        w.put(buffer[$ - coefficientLength  - coefficient.sign .. $ - coefficientLength - zerosLength]);
+                        buffer[$ - coefficientLength - zerosLength - 1] = '.';
+                        w.put(buffer[$ - coefficientLength - zerosLength - 1 .. $]);
+                        return;
+                    }
+                }
+            }
         }
 
         assert(coefficientLength);
+
         sizediff_t exponent = this.exponent + coefficientLength - 1;
 
         if (coefficientLength > 1)
         {
-            auto c = buffer[$ - expBufferShift - coefficientLength];
-            buffer[$ - expBufferShift - coefficientLength] = '.';
-            buffer[$ - expBufferShift - ++coefficientLength] = c;
+            auto c = buffer[$ - coefficientLength];
+            buffer[$ - coefficientLength] = '.';
+            buffer[$ - ++coefficientLength] = c;
         }
 
         if (coefficient.sign)
         {
-            buffer[$ - expBufferShift - ++coefficientLength] = '-';
+            buffer[$ - ++coefficientLength] = '-';
         }
 
-        size_t exponentLength;
-        {
-            bool expSign = exponent < 0;
-            size_t unsignedExponent = expSign ? -exponent : exponent;
-            size_t i = buffer.length;
+        w.put(buffer[$ - coefficientLength .. $]);
 
-            do
-            {
-                buffer[--i] = cast(char)(unsignedExponent % 10 + '0');
-                unsignedExponent /= 10;
-            }
-            while(unsignedExponent);
-            if (expSign)
-                buffer[--i] = '-';
-            exponentLength = buffer.length - i;
-        }
+        C[expBufferLength] expBuffer = void;
+        import mir.format_impl: printSignedToTail;
 
-        buffer[$ - expBufferShift] = 'e';
-
-        assert(exponentLength <= expBufferShift + 1);
-        size_t i;
-        do buffer[$ - (expBufferShift - 1) + i] = buffer[$ - exponentLength + i];
-        while(++i < exponentLength);
-        return buffer[$ - (expBufferShift + coefficientLength) .. $ - (expBufferShift - 1) + exponentLength];
-    }
-
-    ///
-    immutable(C)[] toString(C = char)() const @safe pure nothrow
-        if(isSomeChar!C && isMutable!C)
-    {
-        C[eDecimalLength] buffer = void;
-        return toStringImpl(buffer).idup;
-    }
-
-    static if (maxSize64 == 3)
-    ///
-    version(mir_bignum_test) @safe pure unittest
-    {
-        auto str = "-3.4010447314490204552169750449563978034784726557588085989975288830070948234680e-13245";
-        auto integer = Decimal!4(str);
-        assert(integer.toString == str, integer.toString);
-
-        integer = Decimal!4.init;
-        assert(integer.toString == "0e0");
-    }
+        static if (sizediff_t.sizeof == 8)
+            enum N = 21;
+        else
+            enum N = 11;
 
-    ///
-    void toString(C = char, W)(scope ref W w) const
-        if(isSomeChar!C && isMutable!C)
-    {
-        C[eDecimalLength] buffer = void;
-        w.put(toStringImpl(buffer));
+        auto expLength = printSignedToTail(exponent, buffer[$ - N .. $], '\0');
+        buffer[$ - ++expLength] = 'e';
+        w.put(buffer[$ - expLength .. $]);
     }
 
     static if (maxSize64 == 3)
@@ -310,9 +342,9 @@ struct Decimal(size_t maxSize64)
     {
         import mir.format: stringBuf;
         auto str = "5.28238923728e-876543210";
-        auto integer = Decimal!1(str);
+        auto decimal = Decimal!1(str);
         stringBuf buffer;
-        buffer << integer;
+        buffer << decimal;
         assert(buffer.data == str, buffer.data);
     }
 
diff --git a/source/mir/format.d b/source/mir/format.d
index a843d0dc..8583ce25 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -19,7 +19,7 @@ unittest
 
     // Floating-point numbers are formatted to
     // the shortest precise exponential notation.
-    assert(123.0.to!S == "1.23e2");
+    assert(123.0.to!S == "123.0");
     assert(123.to!(immutable S) == "123");
     assert(true.to!S == "true");
     assert(true.to!string == "true");
@@ -52,7 +52,7 @@ unittest
 
     // Floating-point numbers are formatted to
     // the shortest precise exponential notation.
-    assert(123.0.to!string == "1.23e2");
+    assert(123.0.to!string == "123.0");
     assert(123.to!(char[]) == "123");
 
     assert(S("str").to!string == "str"); // GC allocated result
@@ -87,8 +87,8 @@ string text(string separator = "", A...)(auto ref A args)
 ///
 @safe pure nothrow unittest
 {
-    assert(text("str ", true, " ", 100, " ", 124.1) == "str true 100 1.241e2");
-    assert(text!" "("str", true, 100, 124.1) == "str true 100 1.241e2");
+    assert(text("str ", true, " ", 100, " ", 124.1) == "str true 100 124.1", text("str ", true, " ", 100, " ", 124.1));
+    assert(text!" "("str", true, 100, 124.1) == "str true 100 124.1");
 }
 
 import mir.format_impl;
@@ -151,7 +151,7 @@ version (mir_test) unittest
 {
     auto name = "D";
     auto ver = 2.0;
-    assert(stringBuf() << "Hi " << name << ver << "!\n" << getData == "Hi D2e0!\n");
+    assert(stringBuf() << "Hi " << name << ver << "!\n" << getData == "Hi D2.0!\n");
 }
 
 ///
@@ -178,6 +178,44 @@ version (mir_test) unittest
     assert(stringBuf() << -1234567890 << getData == "-1234567890");
 }
 
+/++
+Mir's numeric format specification
+
+Note: the specification isn't complete an may be extended in the future.
++/
+struct NumericSpec
+{
+    ///
+    enum Format
+    {
+        /++
+        Human-frindly precise output.
+        Examples: `0.000001`, `600000.0`, but `1e-7` and `6e7`.
+        +/
+        human,
+        /++
+        Precise output with explicit exponent.
+        Examples: `1e-6`, `6e6`, `1.23456789e-100`.
+        +/
+        exponent,
+    }
+
+    ///
+    Format format;
+
+    /++
+    Precise output with explicit exponent.
+    Examples: `1e-6`, `6e6`, `1.23456789e-100`.
+    +/
+    enum NumericSpec exponent = NumericSpec(Format.exponent);
+
+    /++
+    Precise output with explicit exponent.
+    Examples: `1e-6`, `6e6`, `1.23456789e-100`.
+    +/
+    enum NumericSpec human = NumericSpec(Format.human);
+}
+
 // 16-bytes
 /// C's compatible format specifier.
 struct FormatSpec
@@ -215,6 +253,7 @@ enum SwitchLU : bool
 }
 
 /++
+Wrapper to format floating point numbers using C's library.
 +/
 struct FormattedFloating(T)
     if(is(T == float) || is(T == double) || is(T == real))
@@ -240,6 +279,31 @@ FormattedFloating!T withFormat(T)(const T value, FormatSpec spec)
     return typeof(return)(value, spec);
 }
 
+/++
+Wrapper to format floating point numbers using C's library.
++/
+struct FormattedNumeric(T)
+    if(is(T == float) || is(T == double) || is(T == real))
+{
+    ///
+    T value;
+    ///
+    NumericSpec spec;
+
+    ///
+    void toString(C = char, W)(scope ref W w) scope const
+    {
+        print(w, value, spec);
+    }
+}
+
+/// ditto
+FormattedNumeric!T withFormat(T)(const T value, NumericSpec spec)
+{
+    version(LDC) pragma(inline);
+    return typeof(return)(value, spec);
+}
+
 /++
 +/
 struct HexAddress(T)
@@ -633,18 +697,42 @@ ref W print(C = char, W, I)(scope return ref W w, const I c)
 }
 
 /// Prints floating point numbers
-ref W print(C = char, W, T)(scope return ref W w, const T c)
+ref W print(C = char, W, T)(scope return ref W w, const T c, NumericSpec spec = NumericSpec.init)
     if(is(T == float) || is(T == double) || is(T == real))
 {
     import mir.bignum.decimal;
     auto decimal = Decimal!(T.mant_dig < 64 ? 1 : 2)(c);
-    return w.print!C(decimal);
+    decimal.toString(w, spec);
+    return w;
+}
+
+///
+version(mir_bignum_test)
+@safe pure nothrow
+unittest
+{
+    assert(stringBuf() << -0.0 << getData == "-0.0");
+    assert(stringBuf() << 0.0 << getData == "0.0");
+    assert(stringBuf() << -0.01 << getData == "-0.01");
+    assert(stringBuf() << 0.0125 << getData == "0.0125");
+    assert(stringBuf() << 0.000003 << getData == "0.000003");
+    assert(stringBuf() << -3e-7 << getData == "-3e-7");
+    assert(stringBuf() << 123456.0 << getData == "123456.0");
+    assert(stringBuf() << 12.3456 << getData == "12.3456");
+    assert(stringBuf() << -0.123456 << getData == "-0.123456");
+    assert(stringBuf() << 0.1234567 << getData == "1.234567e-1");
+    assert(stringBuf() << -1234567.0 << getData == "-1.234567e6");
+    assert(stringBuf() << 1234567890123.0 << getData == "1.234567890123e12");
+    assert(stringBuf() << +double.nan << getData == "nan");
+    assert(stringBuf() << -double.nan << getData == "nan");
+    assert(stringBuf() << +double.infinity << getData == "+inf");
+    assert(stringBuf() << -double.infinity << getData == "-inf");
 }
 
 /// Prints structs and unions
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, ref const T c)
-    if (is(T == struct) || is(T == union))
+    if (is(T == struct) || is(T == union) && !is(T : NumericSpec))
 {
     static if (__traits(hasMember, T, "toString"))
     {
diff --git a/source/mir/format_impl.d b/source/mir/format_impl.d
index 7cba4517..d5c148be 100644
--- a/source/mir/format_impl.d
+++ b/source/mir/format_impl.d
@@ -87,17 +87,17 @@ auto assumePureSafe(T)(T t) @trusted
 
 ////////// FLOATING POINT //////////
 
-pragma(inline, false) size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref char[512] buf)
+size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref char[512] buf)
 {
     return printFloatingPoint(cast(double)c, spec, buf);
 }
 
-pragma(inline, false) size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref char[512] buf)
+size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref char[512] buf)
 {
     return printFloatingPointGen(c, spec, buf);
 }
 
-pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref char[512] buf)
+size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref char[512] buf)
 {
     version (CRuntime_Microsoft)
     {
@@ -105,22 +105,21 @@ pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSp
     }
     else
     {
-        pragma(inline, false);
         return printFloatingPointGen(c, spec, buf);
     }
 }
 
-pragma(inline, false) size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
+size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
 {
     return printFloatingPoint(cast(double)c, spec, buf);
 }
 
-pragma(inline, false) size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
+size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
 {
     return printFloatingPointExtend(c, spec, buf);
 }
 
-pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
+size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref wchar[512] buf)
 {
     version (CRuntime_Microsoft)
     {
@@ -132,17 +131,17 @@ pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSp
     }
 }
 
-pragma(inline, false) size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
+size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
 {
     return printFloatingPoint(cast(double)c, spec, buf);
 }
 
-pragma(inline, false) size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
+size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
 {
     return printFloatingPointExtend(c, spec, buf);
 }
 
-pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
+size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref dchar[512] buf)
 {
     version (CRuntime_Microsoft)
     {
@@ -156,20 +155,20 @@ pragma(inline, false) size_t printFloatingPoint(real c, scope ref const FormatSp
 
 nothrow:
 
-pragma(inline, false) size_t printHexadecimal(uint c, ref char[8] buf, bool upper) { return printHexadecimalGen!(uint, char)(c, buf, upper); }
-pragma(inline, false) size_t printHexadecimal(ulong c, ref char[16] buf, bool upper) { return printHexadecimalGen!(ulong, char)(c, buf, upper); }
+size_t printHexadecimal(uint c, ref char[8] buf, bool upper) { return printHexadecimalGen!(uint, char)(c, buf, upper); }
+size_t printHexadecimal(ulong c, ref char[16] buf, bool upper) { return printHexadecimalGen!(ulong, char)(c, buf, upper); }
 static if (is(ucent))
-pragma(inline, false) size_t printHexadecimal(ucent c, ref char[32] buf, bool upper) { return printHexadecimalGen!(ucent, char)(c, buf, upper); }
+size_t printHexadecimal(ucent c, ref char[32] buf, bool upper) { return printHexadecimalGen!(ucent, char)(c, buf, upper); }
 
-pragma(inline, false) size_t printHexadecimal(uint c, ref wchar[8] buf, bool upper) { return printHexadecimalGen!(uint, wchar)(c, buf, upper); }
-pragma(inline, false) size_t printHexadecimal(ulong c, ref wchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, wchar)(c, buf, upper); }
+size_t printHexadecimal(uint c, ref wchar[8] buf, bool upper) { return printHexadecimalGen!(uint, wchar)(c, buf, upper); }
+size_t printHexadecimal(ulong c, ref wchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, wchar)(c, buf, upper); }
 static if (is(ucent))
-pragma(inline, false) size_t printHexadecimal(ucent c, ref wchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, wchar)(c, buf, upper); }
+size_t printHexadecimal(ucent c, ref wchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, wchar)(c, buf, upper); }
 
-pragma(inline, false) size_t printHexadecimal(uint c, ref dchar[8] buf, bool upper) { return printHexadecimalGen!(uint, dchar)(c, buf, upper); }
-pragma(inline, false) size_t printHexadecimal(ulong c, ref dchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, dchar)(c, buf, upper); }
+size_t printHexadecimal(uint c, ref dchar[8] buf, bool upper) { return printHexadecimalGen!(uint, dchar)(c, buf, upper); }
+size_t printHexadecimal(ulong c, ref dchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, dchar)(c, buf, upper); }
 static if (is(ucent))
-pragma(inline, false) size_t printHexadecimal(ucent c, ref dchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, dchar)(c, buf, upper); }
+size_t printHexadecimal(ucent c, ref dchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, dchar)(c, buf, upper); }
 
 size_t printHexadecimalGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) @trusted
 {
@@ -194,24 +193,24 @@ size_t printHexadecimalGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) @trus
 
                       size_t printHexAddress(ubyte c, ref char[2] buf, bool upper) { return printHexAddressGen!(ubyte, char)(c, buf, upper); }
                       size_t printHexAddress(ushort c, ref char[4] buf, bool upper) { return printHexAddressGen!(ushort, char)(c, buf, upper); }
-pragma(inline, false) size_t printHexAddress(uint c, ref char[8] buf, bool upper) { return printHexAddressGen!(uint, char)(c, buf, upper); }
-pragma(inline, false) size_t printHexAddress(ulong c, ref char[16] buf, bool upper) { return printHexAddressGen!(ulong, char)(c, buf, upper); }
+size_t printHexAddress(uint c, ref char[8] buf, bool upper) { return printHexAddressGen!(uint, char)(c, buf, upper); }
+size_t printHexAddress(ulong c, ref char[16] buf, bool upper) { return printHexAddressGen!(ulong, char)(c, buf, upper); }
 static if (is(ucent))
-pragma(inline, false) size_t printHexAddress(ucent c, ref char[32] buf, bool upper) { return printHexAddressGen!(ucent, char)(c, buf, upper); }
+size_t printHexAddress(ucent c, ref char[32] buf, bool upper) { return printHexAddressGen!(ucent, char)(c, buf, upper); }
 
                       size_t printHexAddress(ubyte c, ref wchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, wchar)(c, buf, upper); }
                       size_t printHexAddress(ushort c, ref wchar[4] buf, bool upper) { return printHexAddressGen!(ushort, wchar)(c, buf, upper); }
-pragma(inline, false) size_t printHexAddress(uint c, ref wchar[8] buf, bool upper) { return printHexAddressGen!(uint, wchar)(c, buf, upper); }
-pragma(inline, false) size_t printHexAddress(ulong c, ref wchar[16] buf, bool upper) { return printHexAddressGen!(ulong, wchar)(c, buf, upper); }
+size_t printHexAddress(uint c, ref wchar[8] buf, bool upper) { return printHexAddressGen!(uint, wchar)(c, buf, upper); }
+size_t printHexAddress(ulong c, ref wchar[16] buf, bool upper) { return printHexAddressGen!(ulong, wchar)(c, buf, upper); }
 static if (is(ucent))
-pragma(inline, false) size_t printHexAddress(ucent c, ref wchar[32] buf, bool upper) { return printHexAddressGen!(ucent, wchar)(c, buf, upper); }
+size_t printHexAddress(ucent c, ref wchar[32] buf, bool upper) { return printHexAddressGen!(ucent, wchar)(c, buf, upper); }
 
                       size_t printHexAddress(ubyte c, ref dchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, dchar)(c, buf, upper); }
                       size_t printHexAddress(ushort c, ref dchar[4] buf, bool upper) { return printHexAddressGen!(ushort, dchar)(c, buf, upper); }
-pragma(inline, false) size_t printHexAddress(uint c, ref dchar[8] buf, bool upper) { return printHexAddressGen!(uint, dchar)(c, buf, upper); }
-pragma(inline, false) size_t printHexAddress(ulong c, ref dchar[16] buf, bool upper) { return printHexAddressGen!(ulong, dchar)(c, buf, upper); }
+size_t printHexAddress(uint c, ref dchar[8] buf, bool upper) { return printHexAddressGen!(uint, dchar)(c, buf, upper); }
+size_t printHexAddress(ulong c, ref dchar[16] buf, bool upper) { return printHexAddressGen!(ulong, dchar)(c, buf, upper); }
 static if (is(ucent))
-pragma(inline, false) size_t printHexAddress(ucent c, ref dchar[32] buf, bool upper) { return printHexAddressGen!(ucent, dchar)(c, buf, upper); }
+size_t printHexAddress(ucent c, ref dchar[32] buf, bool upper) { return printHexAddressGen!(ucent, dchar)(c, buf, upper); }
 
 size_t printHexAddressGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper)
 {
@@ -235,9 +234,9 @@ size_t printHexAddressGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper)
 static immutable hexStringUpper = "0123456789ABCDEF";
 static immutable hexStringLower = "0123456789abcdef";
 
-pragma(inline, false) size_t printBufferShift(size_t length, size_t shift, scope char* ptr) { return printBufferShiftGen!char(length, shift, ptr); }
-pragma(inline, false) size_t printBufferShift(size_t length, size_t shift, scope wchar* ptr) { return printBufferShiftGen!wchar(length, shift, ptr); }
-pragma(inline, false) size_t printBufferShift(size_t length, size_t shift, scope dchar* ptr) { return printBufferShiftGen!dchar(length, shift, ptr); }
+size_t printBufferShift(size_t length, size_t shift, scope char* ptr) { return printBufferShiftGen!char(length, shift, ptr); }
+size_t printBufferShift(size_t length, size_t shift, scope wchar* ptr) { return printBufferShiftGen!wchar(length, shift, ptr); }
+size_t printBufferShift(size_t length, size_t shift, scope dchar* ptr) { return printBufferShiftGen!dchar(length, shift, ptr); }
 
 size_t printBufferShiftGen(C)(size_t length, size_t shift, scope C* ptr) @trusted
 {
@@ -247,36 +246,36 @@ size_t printBufferShiftGen(C)(size_t length, size_t shift, scope C* ptr) @truste
     return length;
 }
 
-pragma(inline, false) size_t printSigned(int c, scope ref char[11] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
-pragma(inline, false) size_t printSigned(long c, scope ref char[21] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(int c, scope ref char[11] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(long c, scope ref char[21] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
 static if (is(cent))
-pragma(inline, false) size_t printSigned(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedGen(c, buf, sign); }
 
-pragma(inline, false) size_t printSigned(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
-pragma(inline, false) size_t printSigned(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
 static if (is(cent))
-pragma(inline, false) size_t printSigned(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); }
 
-pragma(inline, false) size_t printSigned(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
-pragma(inline, false) size_t printSigned(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
 static if (is(cent))
-pragma(inline, false) size_t printSigned(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
+size_t printSigned(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); }
 
 
-pragma(inline, false) size_t printSignedToTail(int c, scope ref char[11] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
-pragma(inline, false) size_t printSignedToTail(long c, scope ref char[21] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(int c, scope ref char[11] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(long c, scope ref char[21] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
 static if (is(cent))
-pragma(inline, false) size_t printSignedToTail(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); }
 
-pragma(inline, false) size_t printSignedToTail(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
-pragma(inline, false) size_t printSignedToTail(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
 static if (is(cent))
-pragma(inline, false) size_t printSignedToTail(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
 
-pragma(inline, false) size_t printSignedToTail(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
-pragma(inline, false) size_t printSignedToTail(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
 static if (is(cent))
-pragma(inline, false) size_t printSignedToTail(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
+size_t printSignedToTail(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); }
 
 size_t printSignedGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) @trusted
 {
@@ -305,35 +304,35 @@ size_t printSignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign)
     return ret;
 }
 
-pragma(inline, false) size_t printUnsigned(uint c, scope ref char[10] buf) { return printUnsignedGen(c, buf); }
-pragma(inline, false) size_t printUnsigned(ulong c, scope ref char[20] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(uint c, scope ref char[10] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(ulong c, scope ref char[20] buf) { return printUnsignedGen(c, buf); }
 static if (is(ucent))
-pragma(inline, false) size_t printUnsigned(ucent c, scope ref char[39] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(ucent c, scope ref char[39] buf) { return printUnsignedGen(c, buf); }
 
-pragma(inline, false) size_t printUnsigned(uint c, scope ref wchar[10] buf) { return printUnsignedGen(c, buf); }
-pragma(inline, false) size_t printUnsigned(ulong c, scope ref wchar[20] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(uint c, scope ref wchar[10] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(ulong c, scope ref wchar[20] buf) { return printUnsignedGen(c, buf); }
 static if (is(ucent))
-pragma(inline, false) size_t printUnsigned(ucent c, scope ref wchar[39] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(ucent c, scope ref wchar[39] buf) { return printUnsignedGen(c, buf); }
 
-pragma(inline, false) size_t printUnsigned(uint c, scope ref dchar[10] buf) { return printUnsignedGen(c, buf); }
-pragma(inline, false) size_t printUnsigned(ulong c, scope ref dchar[20] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(uint c, scope ref dchar[10] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(ulong c, scope ref dchar[20] buf) { return printUnsignedGen(c, buf); }
 static if (is(ucent))
-pragma(inline, false) size_t printUnsigned(ucent c, scope ref dchar[39] buf) { return printUnsignedGen(c, buf); }
+size_t printUnsigned(ucent c, scope ref dchar[39] buf) { return printUnsignedGen(c, buf); }
 
-pragma(inline, false) size_t printUnsignedToTail(uint c, scope ref char[10] buf) { return printUnsignedToTailGen(c, buf); }
-pragma(inline, false) size_t printUnsignedToTail(ulong c, scope ref char[20] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(uint c, scope ref char[10] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(ulong c, scope ref char[20] buf) { return printUnsignedToTailGen(c, buf); }
 static if (is(ucent))
-pragma(inline, false) size_t printUnsignedToTail(ucent c, scope ref char[39] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(ucent c, scope ref char[39] buf) { return printUnsignedToTailGen(c, buf); }
 
-pragma(inline, false) size_t printUnsignedToTail(uint c, scope ref wchar[10] buf) { return printUnsignedToTailGen(c, buf); }
-pragma(inline, false) size_t printUnsignedToTail(ulong c, scope ref wchar[20] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(uint c, scope ref wchar[10] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(ulong c, scope ref wchar[20] buf) { return printUnsignedToTailGen(c, buf); }
 static if (is(ucent))
-pragma(inline, false) size_t printUnsignedToTail(ucent c, scope ref wchar[39] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(ucent c, scope ref wchar[39] buf) { return printUnsignedToTailGen(c, buf); }
 
-pragma(inline, false) size_t printUnsignedToTail(uint c, scope ref dchar[10] buf) { return printUnsignedToTailGen(c, buf); }
-pragma(inline, false) size_t printUnsignedToTail(ulong c, scope ref dchar[20] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(uint c, scope ref dchar[10] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(ulong c, scope ref dchar[20] buf) { return printUnsignedToTailGen(c, buf); }
 static if (is(ucent))
-pragma(inline, false) size_t printUnsignedToTail(ucent c, scope ref dchar[39] buf) { return printUnsignedToTailGen(c, buf); }
+size_t printUnsignedToTail(ucent c, scope ref dchar[39] buf) { return printUnsignedToTailGen(c, buf); }
 
 size_t printUnsignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted
 {
@@ -387,7 +386,7 @@ size_t printUnsignedGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted
 }
 
 nothrow @trusted
-pragma(inline, false) size_t extendASCII(char* from, wchar* to, size_t n)
+size_t extendASCII(char* from, wchar* to, size_t n)
 {
     foreach (i; 0 .. n)
         to[i] = from[i];
@@ -395,7 +394,7 @@ pragma(inline, false) size_t extendASCII(char* from, wchar* to, size_t n)
 }
 
 nothrow @trusted
-pragma(inline, false) size_t extendASCII(char* from, dchar* to, size_t n)
+size_t extendASCII(char* from, dchar* to, size_t n)
 {
     foreach (i; 0 .. n)
         to[i] = from[i];
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index f5db696d..c6672cef 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -285,9 +285,6 @@ struct Concatenation(size_t dim, Slices...)
         return count;
     }
 
-    deprecated("use elementCount instead")
-    alias elementsCount = elementCount;
-
     /// Shape of the concatenation.
     size_t[N] shape()() const @property
     {
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 19dfb4c6..fe318d10 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1907,9 +1907,6 @@ public:
         return len;
     }
 
-    deprecated("use elementCount instead")
-    alias elementsCount = elementCount;
-
     static if (doUnittest)
     /// Regular slice
     @safe @nogc pure nothrow version(mir_test) unittest

From 1db936195cae0f4732e2ff416fe203b02f409870 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 16 Dec 2020 23:50:23 +0700
Subject: [PATCH 153/573] fix decimal parsing

---
 source/mir/bignum/decimal.d        | 31 ++++++++++++++++++++++++++++++
 source/mir/bignum/low_level_view.d |  5 ++++-
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index f748ea55..aa7b8190 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -386,6 +386,37 @@ unittest
 }
 
 
+///
+version(mir_bignum_test) 
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.conv: to;
+    Decimal!3 decimal;
+    DecimalExponentKey key;
+
+    assert(decimal.fromStringImpl("0", key));
+    assert(key == DecimalExponentKey.none);
+    assert(decimal.exponent == 0);
+    assert(decimal.coefficient.length == 0);
+    assert(!decimal.coefficient.sign);
+    assert(cast(double) decimal.coefficient == 0);
+
+    assert(decimal.fromStringImpl("-0.0", key));
+    assert(key == DecimalExponentKey.dot);
+    assert(decimal.exponent == -1);
+    assert(decimal.coefficient.length == 0);
+    assert(decimal.coefficient.sign);
+    assert(cast(double) decimal.coefficient == 0);
+
+    assert(decimal.fromStringImpl("0e0", key));
+    assert(key == DecimalExponentKey.e);
+    assert(decimal.exponent == 0);
+    assert(decimal.coefficient.length == 0);
+    assert(!decimal.coefficient.sign);
+    assert(cast(double) decimal.coefficient == 0);
+}
+
 deprecated("use decimal.fromStringImpl insteade")
 @trusted @nogc pure nothrow
 bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 989c6c40..ddba2f2f 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1969,7 +1969,10 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         if (d == 0)
         {
             if (str.length == 0)
+            {
+                coefficient = coefficient.init;
                 return true;
+            }
             if (str[0] == '0')
                 return false;
             goto S;
@@ -2039,7 +2042,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     {
                     M:
                         exponent -= afterDot;
-                        coefficient = work;
+                        coefficient = work.mostSignificant == 0 ? coefficient.init : work;
                         return true;
                     }
                 }

From c57fc5cf86dc9f4f9cfcc6ee908fca8f908c336f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 20 Dec 2020 12:18:37 +0700
Subject: [PATCH 154/573] optimize multipleOfPowerOf5

---
 source/mir/bignum/internal/ryu/generic_128.d | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/source/mir/bignum/internal/ryu/generic_128.d b/source/mir/bignum/internal/ryu/generic_128.d
index 9d74723e..2903e0a3 100644
--- a/source/mir/bignum/internal/ryu/generic_128.d
+++ b/source/mir/bignum/internal/ryu/generic_128.d
@@ -184,19 +184,13 @@ bool multipleOfPowerOf5(size_t size)(UInt!size value, const uint p)
     enum fiveReciprocal = .fiveReciprocal.toSize!size;
     enum baseDiv5 = .baseDiv5.toSize!size;
     version(LDC) pragma(inline, true);
-    if (value)
+    assert(value);
+    for (uint count = 0;; ++count)
     {
-        for (uint count;; ++count)
-        {
-            if (count >= p)
-                return true;
-            value *= fiveReciprocal;
-            if (value > baseDiv5)
-                return false;
-        }
+        value *= fiveReciprocal;
+        if (value > baseDiv5)
+            return count >= p;
     }
-    else
-        return p == 0;
 }
 
 version(mir_bignum_test) unittest

From 234dd928b07054b3328a77fd8d18d4d6804805b7 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 9 Jan 2021 19:27:16 +0700
Subject: [PATCH 155/573] workaround linking bug

---
 source/mir/ndslice/slice.d | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index fe318d10..86cf44a6 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -2031,18 +2031,38 @@ public:
     bool opEquals(IteratorR, SliceKind rkind)(auto ref const Slice!(IteratorR, N, rkind) rslice) @trusted scope const
     {
         static if (
-               __traits(isPOD, typeof(this))
-            && __traits(isPOD, typeof(rslice))
+               __traits(isPOD, Iterator)
+            && __traits(isPOD, IteratorR)
             && __traits(compiles, this._iterator == rslice._iterator)
             )
         {
             if (this._lengths != rslice._lengths)
                 return false;
-            if (this.strides == rslice.strides && this._iterator == rslice._iterator)
+            if (anyEmpty)
                 return true;
+            if (this._iterator == rslice._iterator)
+            {
+                auto ls = this.strides;
+                auto rs = rslice.strides;
+                foreach (i; Iota!N)
+                {
+                    if (this._lengths[i] != 1 && ls[i] != rs[i])
+                        goto L;
+                }
+                return true;
+            }
+        }
+        L:
+
+        static if (is(Iterator == IotaIterator!UL, UL) && is(IteratorR == Iterator))
+        {
+            return false;
+        }
+        else
+        {
+            import mir.algorithm.iteration : equal;
+            return equal(this.lightScope, rslice.lightScope);
         }
-        import mir.algorithm.iteration : equal;
-        return equal(this.lightScope, rslice.lightScope);
     }
 
     /// ditto

From 3bcfa275077d1a7b02087cb95dbc29370c545f93 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 15 Jan 2021 12:50:14 +0700
Subject: [PATCH 156/573] add mir.algebraic test

---
 source/mir/appender.d | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index f7d1b359..49cbe7ae 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -322,3 +322,26 @@ version (mir_test) unittest
     buf.popBackN(2);
     assert(buf.data == "cs");
 }
+
+version(mir_test)
+unittest
+{
+    import mir.conv: to;
+    import mir.algebraic : Algebraic;
+    static struct S
+    {
+    @safe pure nothrow @nogc:
+        @property string toString() const
+        {
+            return "_";
+        }
+    }
+    Algebraic!(int, string) x;
+    x = 42;
+    assert(x.to!string == "42");
+    x = "abc";
+    assert(x.to!string == "abc");
+    Algebraic!S y;
+    y = S();
+    assert(y.to!string == "_");
+}

From 135293df652f9acfb806e38ffc1f2681935e9d48 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 15 Jan 2021 12:50:27 +0700
Subject: [PATCH 157/573] fix #326

---
 source/mir/bignum/fixed.d | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 9fc7899e..4a3c4642 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -208,7 +208,10 @@ struct UInt(size_t size)
             throw hexStringException;
         }
         else
+        {
+            import mir.bignum.low_level_view: hexStringErrorMsg;
             assert(0, hexStringErrorMsg);
+        }
     }
 
     /++

From 3e8835a25b4606d91fb791d2671d414c019affd9 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sat, 16 Jan 2021 20:30:16 +0700
Subject: [PATCH 158/573] update mir.serde with serialization primitives (#327)

---
 source/mir/serde.d | 413 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 377 insertions(+), 36 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index c4371250..19131f14 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -140,8 +140,7 @@ immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc
 }
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     struct S
     {
@@ -171,8 +170,7 @@ unittest
 }
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     enum E
     {
@@ -241,8 +239,7 @@ string serdeGetKeyOut(T)(const T value)
 }
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     struct S
     {
@@ -275,8 +272,7 @@ unittest
 }
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     enum E
     {
@@ -315,8 +311,7 @@ Do not use it on void initialized fields or aggregates with void initialized fie
 enum serdeIgnoreDefault;
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     struct S
     {
@@ -338,8 +333,7 @@ Serialization proxy.
 struct serdeProxy(T);
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     import mir.small_string;
 
@@ -451,8 +445,7 @@ bool hasSerdeIgnoreCase(T)(T value)
 }
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     enum E
     {
@@ -471,8 +464,7 @@ unittest
 }
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     @serdeIgnoreCase
     enum E
@@ -597,8 +589,7 @@ bool serdeParseEnum(E)(const char[] str, ref E res)
 }
 
 ///
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     enum E
     {
@@ -626,8 +617,7 @@ unittest
 }
 
 /// Case insensitive
-version(mir_test)
-unittest
+version(mir_test) unittest
 {
     @serdeIgnoreCase // supported for the whole type
     enum E
@@ -692,7 +682,7 @@ template serdeIsDeserializable(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct S
@@ -709,6 +699,58 @@ unittest
     static assert (serdeIsDeserializableInS!"a");
 }
 
+/++
+Serialization member type
++/
+template serdeSerializationMemberType(T, string member)
+{
+    import std.traits: Unqual, Parameters;
+    T* aggregate;
+    static if (isField!(T, member))
+    {
+        alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member));
+    }
+    else
+    {
+        alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member)());
+    }
+}
+
+/// ditto
+template serdeSerializationMemberType(T)
+{
+    ///
+    alias serdeSerializationMemberType(string member) = .serdeSerializationMemberType!(T, member);
+}
+
+
+/++
+Is deserializable member
++/
+template serdeIsSerializable(T)
+{
+    ///
+    enum bool serdeIsSerializable(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) !is null;
+}
+
+///
+version(mir_test) unittest
+{
+
+    static struct S
+    {
+        @serdeIgnore
+        int i;
+
+        @serdeKeys("a", "b")
+        int a;
+    }
+
+    alias serdeIsSerializableInS = serdeIsSerializable!S;
+    static assert (!serdeIsSerializableInS!"i");
+    static assert (serdeIsSerializableInS!"a");
+}
+
 /++
 Final proxy type
 +/
@@ -733,7 +775,7 @@ template serdeGetFinalProxy(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     @serdeProxy!string
@@ -796,7 +838,7 @@ template serdeGetFinalDeepProxy(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     @serdeProxy!string
@@ -828,7 +870,7 @@ template serdeFinalProxyDeserializableMembers(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A
@@ -850,7 +892,78 @@ unittest
 }
 
 /++
-Final deep proxy type deserializable members
+Final deep proxy type serializable members
++/
+template serdeFinalDeepProxySerializableMembers(T)
+{
+    import std.traits: isAggregateType;
+    import std.meta: Filter, aliasSeqOf;
+    alias P = serdeGetFinalDeepProxy!T;
+    static if (isAggregateType!P)
+        enum string[] serdeFinalDeepProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
+    else
+        enum string[] serdeFinalDeepProxySerializableMembers = null;
+}
+
+///
+version(mir_test) unittest
+{
+
+    static struct A
+    {
+        @serdeIgnore
+        int i;
+
+        @serdeKeys("a", "b")
+        int m;
+    }
+
+    @serdeProxy!(A[string])
+    static struct B {}
+
+    @serdeProxy!(B[])
+    static struct C {}
+
+    static assert (serdeFinalDeepProxySerializableMembers!C == ["m"]);
+}
+
+/++
+Final proxy type deserializable members
++/
+template serdeFinalProxySerializableMembers(T)
+{
+    import std.meta: Filter, aliasSeqOf;
+    alias P = serdeGetFinalProxy!T;
+    static if (is(P == struct) || is(P == class) || is(P == interface) || is(P == union))
+        enum string[] serdeFinalProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
+    else
+        enum string[] serdeFinalProxySerializableMembers = null;
+}
+
+///
+version(mir_test) unittest
+{
+
+    static struct A
+    {
+        @serdeIgnore
+        int i;
+
+        @serdeKeys("a", "b")
+        int m;
+    }
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+    static assert (serdeFinalProxySerializableMembers!C == ["m"]);
+}
+
+/++
+Final deep proxy type serializable members
 +/
 template serdeFinalDeepProxyDeserializableMembers(T)
 {
@@ -864,7 +977,7 @@ template serdeFinalDeepProxyDeserializableMembers(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A
@@ -909,7 +1022,7 @@ template serdeFinalDeserializationMemberType(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A
@@ -949,7 +1062,7 @@ template serdeDeserializationFinalProxyMemberTypes(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A {}
@@ -975,6 +1088,96 @@ unittest
     static assert (is(serdeDeserializationFinalProxyMemberTypes!D == AliasSeq!A));
 }
 
+/++
+Serialization member final proxy type
++/
+template serdeFinalSerializationMemberType(T, string member)
+{
+    import std.traits: hasUDA;
+    static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
+    {
+        alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
+    }
+    else
+    {
+        alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeSerializationMemberType!(T, member));
+    }
+}
+
+/// ditto
+template serdeFinalSerializationMemberType(T)
+{
+    ///
+    alias serdeFinalSerializationMemberType(string member) = .serdeFinalSerializationMemberType!(T, member);
+}
+
+///
+version(mir_test) unittest
+{
+
+    static struct A
+    {
+        
+    }
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+
+    @serdeProxy!double
+    struct E {}
+
+    struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    static assert (is(serdeFinalSerializationMemberType!(D, "c") == A), serdeFinalSerializationMemberType!(D, "c"));
+    static assert (is(serdeFinalSerializationMemberType!(D, "d") == double));
+}
+
+/++
+Serialization members final proxy types
++/
+template serdeSerializationFinalProxyMemberTypes(T)
+{
+    import std.meta: NoDuplicates, staticMap, aliasSeqOf;
+    alias serdeSerializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalProxySerializableMembers!T))));
+}
+
+///
+version(mir_test) unittest
+{
+
+    static struct A {}
+
+    @serdeProxy!A
+    static struct B {}
+
+    @serdeProxy!B
+    static struct C {}
+
+    @serdeProxy!B
+    static struct E {}
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    import std.meta: AliasSeq;
+    static assert (is(serdeSerializationFinalProxyMemberTypes!D == AliasSeq!A), serdeSerializationFinalProxyMemberTypes!D);
+}
+
 /++
 Deserialization members final deep proxy types
 +/
@@ -985,7 +1188,7 @@ template serdeDeserializationFinalDeepProxyMemberTypes(T)
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A {}
@@ -1013,6 +1216,44 @@ unittest
     static assert (is(serdeDeserializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeDeserializationFinalDeepProxyMemberTypes!D);
 }
 
+/++
+Serialization members final deep proxy types
++/
+template serdeSerializationFinalDeepProxyMemberTypes(T)
+{
+    import std.meta: NoDuplicates, staticMap, aliasSeqOf;
+    alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T))));
+}
+
+///
+version(mir_test) unittest
+{
+
+    static struct A {}
+
+    @serdeProxy!(A[])
+    static struct B {}
+
+    enum R {a, b, c}
+
+    @serdeProxy!(B[R])
+    static struct C {}
+
+    @serdeProxy!(B[string])
+    static struct E {}
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!E
+        int d;
+    }
+
+    import std.meta: AliasSeq;
+    static assert (is(serdeSerializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeSerializationFinalDeepProxyMemberTypes!D);
+}
+
 private template serdeDeserializationFinalProxyMemberTypesRecurseImpl(T...)
 {
     import std.meta: NoDuplicates, staticMap;
@@ -1029,7 +1270,7 @@ Deserialization members final proxy types (recursive)
 alias serdeDeserializationFinalProxyMemberTypesRecurse(T) = serdeDeserializationFinalProxyMemberTypesRecurseImpl!(serdeGetFinalProxy!T);
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A { double g; }
@@ -1058,6 +1299,16 @@ unittest
     static assert (is(serdeDeserializationFinalProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
 }
 
+private template serdeSerializationFinalDeepProxyMemberTypesRecurseImpl(T...)
+{
+    import std.meta: NoDuplicates, staticMap;
+    alias F = NoDuplicates!(T, staticMap!(serdeSerializationFinalDeepProxyMemberTypes, T));
+    static if (F.length == T.length)
+        alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl = T;
+    else
+        alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl  = .serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!F;
+}
+
 private template serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl(T...)
 {
     import std.meta: NoDuplicates, staticMap;
@@ -1074,7 +1325,7 @@ Deserialization members final deep proxy types (recursive)
 alias serdeDeserializationFinalDeepProxyMemberTypesRecurse(T) = serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T);
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A { double g; }
@@ -1103,6 +1354,41 @@ unittest
     static assert (is(serdeDeserializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)));
 }
 
+/++
+Serialization members final deep proxy types (recursive)
++/
+alias serdeSerializationFinalDeepProxyMemberTypesRecurse(T) = serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T);
+
+///
+version(mir_test) unittest
+{
+
+    static struct A { double g; }
+
+    @serdeProxy!(A[])
+    static struct B {}
+
+    @serdeProxy!(B[string])
+    static struct C {}
+
+    @serdeProxy!B
+    static struct E {}
+
+    static struct D
+    {
+        C c;
+
+        @serdeProxy!(E[])
+        int d;
+    }
+
+    @serdeProxy!D
+    static struct F {}
+
+    import std.meta: AliasSeq;
+    static assert (is(serdeSerializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)), serdeSerializationFinalDeepProxyMemberTypesRecurse!F);
+}
+
 package string[] sortUniqKeys()(string[] keys)
     @safe pure nothrow
 {
@@ -1127,6 +1413,11 @@ private template serdeGetKeysIn2(T)
     enum string[] serdeGetKeysIn2(string member) = serdeGetKeysIn!(__traits(getMember, T, member));
 }
 
+private template serdeGetKeyOut2(T)
+{
+    enum string[] serdeGetKeyOut2(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) is null ? null : [serdeGetKeyOut!(__traits(getMember, T, member))];
+}
+
 private template serdeFinalDeepProxyDeserializableMemberKeys(T)
 {
     import std.meta: staticMap, aliasSeqOf;
@@ -1139,17 +1430,67 @@ private template serdeFinalDeepProxyDeserializableMemberKeys(T)
         enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null;
 }
 
+private template serdeFinalDeepProxySerializableMemberKeys(T)
+{
+    import std.meta: staticMap, aliasSeqOf;
+    import std.traits: isAggregateType;
+    import mir.ndslice.fuse: fuseCells;
+
+    static if (isAggregateType!T)
+        enum string[] serdeFinalDeepProxySerializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeyOut2!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))];
+    else
+        enum string[] serdeFinalDeepProxySerializableMemberKeys = null;
+}
+
+/++
+Serialization members final proxy keys (recursive)
++/
+template serdeGetSerializationKeysRecurse(T)
+{
+    import std.meta: staticMap, aliasSeqOf;
+    enum string[] serdeGetSerializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxySerializableMemberKeys, serdeSerializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys;
+}
+
+///
+version(mir_test) unittest
+{
+
+    static struct A { double g; float d; }
+
+    @serdeProxy!A
+    static struct B {  int f; }
+
+    @serdeProxy!(B[string])
+    static union C {  int f; }
+
+    @serdeProxy!(B[])
+    static interface E {  int f() @property; }
+
+    static class D
+    {
+        C c;
+
+        @serdeProxy!(E[])
+        int d;
+    }
+
+    @serdeProxy!D
+    static struct F { int f; }
+
+    static assert (serdeGetSerializationKeysRecurse!F == ["c", "d", "g"]);
+}
+
 /++
 Deserialization members final proxy keys (recursive)
 +/
-template serdeGetDeserializatinKeysRecurse(T)
+template serdeGetDeserializationKeysRecurse(T)
 {
     import std.meta: staticMap, aliasSeqOf;
-    enum string[] serdeGetDeserializatinKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxyDeserializableMemberKeys, serdeDeserializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys;
+    enum string[] serdeGetDeserializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxyDeserializableMemberKeys, serdeDeserializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys;
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
 
     static struct A { double g; float d; }
@@ -1174,7 +1515,7 @@ unittest
     @serdeProxy!D
     static struct F { int f; }
 
-    static assert (serdeGetDeserializatinKeysRecurse!F == ["c", "d", "g"]);
+    static assert (serdeGetDeserializationKeysRecurse!F == ["c", "d", "g"]);
 }
 
 /++
@@ -1362,7 +1703,7 @@ public:
 }
 
 ///
-unittest
+version(mir_test) unittest
 {
     import std.traits;
 

From b4002bd861f422ea984529f220557ac39aa73c8b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 20 Jan 2021 21:09:14 +0700
Subject: [PATCH 159/573] improve serdeGetFinalDeepProxy

---
 source/mir/serde.d | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 19131f14..589634b8 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -181,7 +181,7 @@ version(mir_test) unittest
         c,
     }
 
-    static assert (serdeGetKeysIn(E.a) == ["A", "alpha"]);
+    static assert (serdeGetKeysIn(E.a) == ["A", "alpha"], serdeGetKeysIn(E.a));
     static assert (serdeGetKeysIn(E.c) == ["c"]);
 }
 
@@ -809,6 +809,11 @@ template serdeGetFinalDeepProxy(T)
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(T.serdeKeysProxy);
         }
         else
+        static if (is(T == enum))
+        {
+            alias serdeGetFinalDeepProxy = typeof(null);
+        }
+        else
         {
             alias serdeGetFinalDeepProxy = T;
         }
@@ -817,7 +822,7 @@ template serdeGetFinalDeepProxy(T)
     static if (isArray!T)
     {
         alias E = Unqual!(ForeachType!T);
-        static if (isAggregateType!E || is(T == enum))
+        static if (isAggregateType!E || is(E == enum))
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E;
         else
             alias serdeGetFinalDeepProxy = T;
@@ -826,7 +831,7 @@ template serdeGetFinalDeepProxy(T)
     static if (is(T == V[K], K, V))
     {
         alias E = Unqual!V;
-        static if (isAggregateType!E || is(T == enum))
+        static if (isAggregateType!E || is(E == enum))
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E;
         else
             alias serdeGetFinalDeepProxy = T;
@@ -1454,13 +1459,19 @@ template serdeGetSerializationKeysRecurse(T)
 ///
 version(mir_test) unittest
 {
+    enum Y
+    {
+        a,
+        b,
+        c,
+    }
 
     static struct A { double g; float d; }
 
     @serdeProxy!A
     static struct B {  int f; }
 
-    @serdeProxy!(B[string])
+    @serdeProxy!(B[Y][string])
     static union C {  int f; }
 
     @serdeProxy!(B[])

From bcaf7177ddc8474582d93058c77c9004b82a87b6 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 20 Jan 2021 20:19:54 -0500
Subject: [PATCH 160/573] Address issue 328 (#329)

* Fixup sorting

* Address issue 328
---
 source/mir/math/stat.d       | 11 +++++
 source/mir/ndslice/sorting.d | 87 ++++++++++++++++++++++++++----------
 2 files changed, 74 insertions(+), 24 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 511d82cb..7defaee2 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -1549,6 +1549,17 @@ unittest
     assert(x4.median.approxEqual(1));
 }
 
+// Check issue #328 fixed
+version(mir_test)
+@safe pure nothrow
+unittest {
+    import mir.ndslice.topology: iota;
+
+    auto x = iota(18);
+    auto y = median(x);
+    assert(y == 8.5);
+}
+
 private pure @trusted nothrow @nogc
 F smallMedianImpl(F, Iterator)(Slice!Iterator slice) 
 {
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index e1c8afbf..8f0b9847 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -1099,8 +1099,19 @@ unittest {
     assert(x[nth] == 2);
 }
 
+// Check issue #328 fixed
+version(mir_test)
+@safe pure nothrow
+unittest {
+    import mir.ndslice.slice: sliced;
+
+    auto slice = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17].sliced;
+    partitionAt(slice, 8);
+    partitionAt(slice, 9);
+}
+
 version(unittest) {
-    template checkTopNAll(alias less = "a < b")
+    template checkPartitionAtAll(alias less = "a < b")
     {
         import mir.functional: naryFun;
         import mir.ndslice.slice: SliceKind, Slice;
@@ -1108,7 +1119,7 @@ version(unittest) {
         static if (__traits(isSame, naryFun!less, less))
         {
             @safe pure nothrow
-            static bool checkTopNAll
+            static bool checkPartitionAtAll
                 (Iterator, SliceKind kind)(
                     Slice!(Iterator, 1, kind) x)
             {
@@ -1129,7 +1140,7 @@ version(unittest) {
                 return result;
             }
         } else {
-            alias checkTopNAll = .checkTopNAll!(naryFun!less);
+            alias checkPartitionAtAll = .checkPartitionAtAll!(naryFun!less);
         }
     }
 }
@@ -1139,25 +1150,25 @@ version(mir_test)
 unittest {
     import mir.ndslice.slice: sliced;
 
-    assert(checkTopNAll([2, 2].sliced));
+    assert(checkPartitionAtAll([2, 2].sliced));
     
-    assert(checkTopNAll([3, 1, 5, 2, 0].sliced));
-    assert(checkTopNAll([3, 1, 5, 0, 2].sliced));
-    assert(checkTopNAll([0, 0, 4, 3, 3].sliced));
-    assert(checkTopNAll([5, 1, 5, 1, 5].sliced));
-    assert(checkTopNAll([2, 2, 0, 0, 0].sliced));
-
-    assert(checkTopNAll([ 2, 12, 10,  8,  1, 20, 19,  1,  2,  7].sliced));
-    assert(checkTopNAll([ 4, 18, 16,  0, 15,  6,  2, 17, 10, 16].sliced));
-    assert(checkTopNAll([ 7,  5,  9,  4,  4,  2, 12, 20, 15, 15].sliced));
-
-    assert(checkTopNAll([17, 87, 58, 50, 34, 98, 25, 77, 88, 79].sliced));
-
-    assert(checkTopNAll([ 6,  7, 10, 25,  5, 10,  9,  0,  2, 15,  7,  9, 11,  8, 13, 18, 17, 13, 25, 22].sliced));
-    assert(checkTopNAll([21,  3, 11, 22, 24, 12, 14, 12, 15, 15,  1,  3, 12, 15, 25, 19,  9, 16, 16, 19].sliced));
-    assert(checkTopNAll([22,  6, 18,  0,  1,  8, 13, 13, 16, 19, 23, 17,  4,  6, 12, 24, 15, 20, 11, 17].sliced));
-    assert(checkTopNAll([19, 23, 14,  5, 12,  3, 13,  7, 25, 25, 24,  9, 21, 25, 12, 22, 15, 22,  7, 11].sliced));
-    assert(checkTopNAll([ 0,  2,  7, 16,  2, 20,  1, 11, 17,  5, 22, 17, 25, 13, 14,  5, 22, 21, 24, 14].sliced));
+    assert(checkPartitionAtAll([3, 1, 5, 2, 0].sliced));
+    assert(checkPartitionAtAll([3, 1, 5, 0, 2].sliced));
+    assert(checkPartitionAtAll([0, 0, 4, 3, 3].sliced));
+    assert(checkPartitionAtAll([5, 1, 5, 1, 5].sliced));
+    assert(checkPartitionAtAll([2, 2, 0, 0, 0].sliced));
+
+    assert(checkPartitionAtAll([ 2, 12, 10,  8,  1, 20, 19,  1,  2,  7].sliced));
+    assert(checkPartitionAtAll([ 4, 18, 16,  0, 15,  6,  2, 17, 10, 16].sliced));
+    assert(checkPartitionAtAll([ 7,  5,  9,  4,  4,  2, 12, 20, 15, 15].sliced));
+
+    assert(checkPartitionAtAll([17, 87, 58, 50, 34, 98, 25, 77, 88, 79].sliced));
+
+    assert(checkPartitionAtAll([ 6,  7, 10, 25,  5, 10,  9,  0,  2, 15,  7,  9, 11,  8, 13, 18, 17, 13, 25, 22].sliced));
+    assert(checkPartitionAtAll([21,  3, 11, 22, 24, 12, 14, 12, 15, 15,  1,  3, 12, 15, 25, 19,  9, 16, 16, 19].sliced));
+    assert(checkPartitionAtAll([22,  6, 18,  0,  1,  8, 13, 13, 16, 19, 23, 17,  4,  6, 12, 24, 15, 20, 11, 17].sliced));
+    assert(checkPartitionAtAll([19, 23, 14,  5, 12,  3, 13,  7, 25, 25, 24,  9, 21, 25, 12, 22, 15, 22,  7, 11].sliced));
+    assert(checkPartitionAtAll([ 0,  2,  7, 16,  2, 20,  1, 11, 17,  5, 22, 17, 25, 13, 14,  5, 22, 21, 24, 14].sliced));
 }
 
 private @trusted pure nothrow @nogc
@@ -1372,6 +1383,35 @@ unittest {
     assert(x[nth] == 10);
 }
 
+// Check all partitionAt
+version(mir_test)
+@trusted pure nothrow
+unittest {
+    import mir.ndslice.slice: sliced;
+    import mir.ndslice.allocation: slice;
+
+    static immutable raw  = [ 6,  7, 10, 25,  5, 10,  9,  0,  2, 15,  7,  9, 11,  8, 13, 18, 17, 13, 25, 22];
+    
+    static void fill(T)(T x) {
+        for (size_t i = 0; i < x.length; i++) {
+            x[i] = raw[i];
+        }
+    }
+    auto x = slice!int(raw.length);
+    fill(x);
+    auto x_sort = x.dup;
+    x_sort = x_sort.sort;
+    size_t i = 0;
+    while (i < raw.length) {
+        auto frontI = x._iterator;
+        auto lastI = frontI + x.length - 1;
+        partitionAtImpl!((a, b) => (a < b))(frontI, lastI, i, true);
+        assert(x[i] == x_sort[i]);
+        fill(x);
+        i++;
+    }
+}
+
 private @trusted pure nothrow @nogc
 Iterator partitionAtPartition(alias less, Iterator)(
     ref Iterator frontI, 
@@ -1381,7 +1421,7 @@ Iterator partitionAtPartition(alias less, Iterator)(
 {
     size_t len = lastI - frontI + 1;
 
-    assert(len >= 9 && n < len, "partitionAtImpl: length must be longer than 9 and n must be less than r.length");
+    assert(len >= 9 && n < len, "partitionAtPartition: length must be longer than 9 and n must be less than r.length");
 
     size_t ninth = len / 9;
     size_t pivot = ninth / 2;
@@ -1397,7 +1437,7 @@ Iterator partitionAtPartition(alias less, Iterator)(
     // We have either one straggler on the left, one on the right, or none.
     assert(loI - frontI <= lastI - hiI + 1 || lastI - hiI <= loI - frontI + 1, "partitionAtPartition: straggler check failed for loI, len, hiI");
     assert(loI - frontI >= ninth * 4, "partitionAtPartition: loI - frontI >= ninth * 4");
-    assert(lastI - hiI >= ninth * 4, "partitionAtPartition: lastI - hiI >= ninth * 4");
+    assert((lastI + 1) - hiI >= ninth * 4, "partitionAtPartition: (lastI + 1) - hiI >= ninth * 4");
 
     // Partition in groups of 3, and the mid tertile again in groups of 3
     if (!useSampling) {
@@ -1428,7 +1468,6 @@ version(mir_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
-
     auto x = [ 6,  7, 10, 25,  5, 10,  9,  0,  2, 15,  7,  9, 11,  8, 13, 18, 17, 13, 25, 22].sliced;
     auto x_sort = x.dup;
     x_sort = x_sort.sort;

From bcf73c89dd9d4e4278df686e807fa9bbe580b129 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 31 Jan 2021 13:14:17 +0700
Subject: [PATCH 161/573] improve Date.toStringImpl

---
 source/mir/date.d | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index 991c6a46..eff95488 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -1168,13 +1168,11 @@ public:
     version(D_BetterC){} else
     private string toStringImpl(alias fun)() const @safe pure nothrow
     {
-        import mir.small_string : SmallString;
-        SmallString!16 w;
-        try
-            fun(w);
-        catch (Exception e)
-            assert(0, __traits(identifier, fun) ~ " threw.");
-        return w[].idup;
+        import mir.appender: UnsafeArrayBuffer;
+        char[16] buffer = void;
+        auto w = UnsafeArrayBuffer!char(buffer);
+        fun(w);
+        return w.data.idup;
     }
 
     version(D_BetterC){} else

From b2d7ac3b5a879a63752ca10b4b901056749d0d77 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 25 Feb 2021 11:01:04 +0700
Subject: [PATCH 162/573] fix array comparison primitives

---
 source/mir/rc/array.d    | 25 +++++++++++++++++++++++++
 source/mir/rc/context.d  | 24 ------------------------
 source/mir/rc/ptr.d      | 26 ++++++++++++++++++++++++++
 source/mir/rc/slim_ptr.d | 26 ++++++++++++++++++++++++++
 4 files changed, 77 insertions(+), 24 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index be34002b..09f9409c 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -55,6 +55,31 @@ struct mir_rcarray(T)
     ///
     mixin CommonRCImpl;
 
+    ///
+    pragma(inline, true)
+    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
+    {
+        return !this;
+    }
+
+    /// ditto
+    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc @property
+    {
+        return opIndex() == rhs.opIndex();
+    }
+
+    ///
+    int opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc @property
+    {
+        return __cmp(opIndex(), rhs.opIndex());
+    }
+
+    ///
+    size_t toHash() @trusted scope const pure nothrow @nogc @property
+    {
+        return hashOf(opIndex());
+    }
+
     ///
     ~this() nothrow
     {
diff --git a/source/mir/rc/context.d b/source/mir/rc/context.d
index 69ab8dee..b70abe14 100644
--- a/source/mir/rc/context.d
+++ b/source/mir/rc/context.d
@@ -231,28 +231,4 @@ package mixin template CommonRCImpl()
     {
         return *cast(typeof(return)*)&this;
     }
-
-    ///
-    pragma(inline, true)
-    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
-    {
-        return !this;
-    }
-
-    /// ditto
-    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc @property
-    {
-        return _thisPtr == rhs._thisPtr;
-    }
-
-    ///
-    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc @property
-    {
-        return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
-    }
-
-    size_t toHash() @trusted scope const pure nothrow @nogc @property
-    {
-        return cast(size_t) _thisPtr;
-    }
 }
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index e38ed3cf..676699d8 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -98,6 +98,32 @@ struct mir_rcptr(T)
     ///
     mixin CommonRCImpl;
 
+
+    ///
+    pragma(inline, true)
+    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
+    {
+        return !this;
+    }
+
+    /// ditto
+    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc @property
+    {
+        return _thisPtr == rhs._thisPtr;
+    }
+
+    ///
+    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc @property
+    {
+        return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
+    }
+
+    ///
+    size_t toHash() @trusted scope const pure nothrow @nogc @property
+    {
+        return cast(size_t) _thisPtr;
+    }
+
     ///
     ~this() nothrow
     {
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index 4ee9209e..7064312f 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -92,6 +92,32 @@ struct mir_slim_rcptr(T)
     ///
     mixin CommonRCImpl;
 
+
+    ///
+    pragma(inline, true)
+    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
+    {
+        return !this;
+    }
+
+    /// ditto
+    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc @property
+    {
+        return _thisPtr == rhs._thisPtr;
+    }
+
+    ///
+    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc @property
+    {
+        return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
+    }
+
+    ///
+    size_t toHash() @trusted scope const pure nothrow @nogc @property
+    {
+        return cast(size_t) _thisPtr;
+    }
+
     ///
     ~this() nothrow
     {

From 8fa441d1e25eb35760a448718f2614c5241cd976 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 26 Feb 2021 12:52:58 +0700
Subject: [PATCH 163/573] remove @property attributes

---
 source/mir/rc/array.d    | 8 ++++----
 source/mir/rc/ptr.d      | 8 ++++----
 source/mir/rc/slim_ptr.d | 8 ++++----
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 09f9409c..2f7a4d86 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -57,25 +57,25 @@ struct mir_rcarray(T)
 
     ///
     pragma(inline, true)
-    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
+    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc
     {
         return !this;
     }
 
     /// ditto
-    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc @property
+    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
     {
         return opIndex() == rhs.opIndex();
     }
 
     ///
-    int opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc @property
+    int opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
     {
         return __cmp(opIndex(), rhs.opIndex());
     }
 
     ///
-    size_t toHash() @trusted scope const pure nothrow @nogc @property
+    size_t toHash() @trusted scope const pure nothrow @nogc
     {
         return hashOf(opIndex());
     }
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index 676699d8..f393fedf 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -101,25 +101,25 @@ struct mir_rcptr(T)
 
     ///
     pragma(inline, true)
-    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
+    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc
     {
         return !this;
     }
 
     /// ditto
-    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc @property
+    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
     {
         return _thisPtr == rhs._thisPtr;
     }
 
     ///
-    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc @property
+    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
     {
         return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
     }
 
     ///
-    size_t toHash() @trusted scope const pure nothrow @nogc @property
+    size_t toHash() @trusted scope const pure nothrow @nogc
     {
         return cast(size_t) _thisPtr;
     }
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index 7064312f..ae567038 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -95,25 +95,25 @@ struct mir_slim_rcptr(T)
 
     ///
     pragma(inline, true)
-    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc @property
+    bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc
     {
         return !this;
     }
 
     /// ditto
-    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc @property
+    bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
     {
         return _thisPtr == rhs._thisPtr;
     }
 
     ///
-    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc @property
+    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
     {
         return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
     }
 
     ///
-    size_t toHash() @trusted scope const pure nothrow @nogc @property
+    size_t toHash() @trusted scope const pure nothrow @nogc
     {
         return cast(size_t) _thisPtr;
     }

From 12b05da4c8d73dd53fe52cd626e2651ba6d5f6fb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 4 Mar 2021 20:23:29 +0700
Subject: [PATCH 164/573] add inlining for enforce

---
 source/mir/exception.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/exception.d b/source/mir/exception.d
index 8bed5107..c2f3053b 100644
--- a/source/mir/exception.d
+++ b/source/mir/exception.d
@@ -17,6 +17,7 @@ private enum NOGCEXP = __traits(compiles, (()@nogc {throw new Exception("");})()
 ///
 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))

From 003422562c1be95625b917a81517f4317111c707 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 4 Mar 2021 21:21:30 +0700
Subject: [PATCH 165/573] try fix linking issue with enforce

---
 source/mir/exception.d | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/source/mir/exception.d b/source/mir/exception.d
index c2f3053b..b79d805c 100644
--- a/source/mir/exception.d
+++ b/source/mir/exception.d
@@ -14,6 +14,11 @@ else
 
 private enum NOGCEXP = __traits(compiles, (()@nogc {throw new Exception("");})());
 
+package template staticException(string fmt, string file, int line)
+{
+    static immutable staticException = new Exception(fmt, file, line);
+}
+
 ///
 auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(scope auto return ref Expr arg) @trusted
 {
@@ -30,8 +35,7 @@ auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(
         if (_expect(cast(bool)arg, true))
             return forward!arg;
     }
-    static immutable exception = new Exception(fmt, file, line);
-    throw exception;
+    throw staticException!(fmt, file, line);
 }
 
 ///

From 1361f6feafe0e464113bc3f394bed4d34ca3a8f2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 5 Mar 2021 21:23:21 +0700
Subject: [PATCH 166/573] fix mir_rci

---
 source/mir/rc/array.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 2f7a4d86..da1bc9ba 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -694,8 +694,8 @@ struct mir_rci(T)
     { return this._iterator == right._iterator; }
 
     ///
-    ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const
-    { return this._iterator - right._iterator; }
+    int opCmp()(scope ref const typeof(this) right) scope const
+    { auto d = this - right; return d ? d < 0 ? -1 : 1 : 0; }
 }
 
 /// ditto

From 815b1e604027fe1edb5bbbfc5b4626a02dd36aa0 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Mar 2021 22:58:37 +0700
Subject: [PATCH 167/573] fix decimal

---
 source/mir/bignum/decimal.d        | 27 ++++++++++++++++++---------
 source/mir/bignum/low_level_view.d | 13 ++++++++++++-
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index aa7b8190..7ce72b40 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -142,38 +142,47 @@ struct Decimal(size_t maxSize64)
         DecimalExponentKey key;
 
         assert(decimal.fromStringImpl("1.334", key));
-        assert(cast(double) decimal.view == 1.334);
         assert(key == DecimalExponentKey.dot);
+        assert(cast(double) decimal.view == 1.334);
 
         assert(decimal.fromStringImpl("+0.334e-5"w, key));
-        assert(cast(double) decimal.view == 0.334e-5);
         assert(key == DecimalExponentKey.e);
+        assert(cast(double) decimal.view == 0.334e-5);
 
         assert(decimal.fromStringImpl("-334D-5"d, key));
-        assert(cast(double) decimal.view == -334e-5);
         assert(key == DecimalExponentKey.D);
+        assert(cast(double) decimal.view == -334e-5);
 
         assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
-        assert(cast(double) decimal.view == 2482734692817364218734682973648217364981273648923423.0);
         assert(key == DecimalExponentKey.none);
+        assert(cast(double) decimal.view == 2482734692817364218734682973648217364981273648923423.0);
 
         assert(decimal.fromStringImpl(".023", key));
-        assert(cast(double) decimal.view == .023);
         assert(key == DecimalExponentKey.dot);
+        assert(cast(double) decimal.view == .023);
 
         assert(decimal.fromStringImpl("0E100", key));
-        assert(cast(double) decimal.view == 0);
         assert(key == DecimalExponentKey.E);
+        assert(cast(double) decimal.view == 0);
 
         assert(decimal.fromStringImpl("-nan", key));
-        assert(decimal.coefficient.length == 0);
-        assert(cast(double) decimal.view == 0);
+        assert(decimal.coefficient.length > 0);
+        assert(decimal.exponent == decimal.exponent.max);
         assert(decimal.coefficient.sign);
         assert(key == DecimalExponentKey.nan);
+        assert(cast(double) decimal.view != cast(double) decimal.view);
 
         assert(decimal.fromStringImpl("inf", key));
-        assert(cast(double) decimal.view == 0);
+        assert(decimal.coefficient.length == 0);
+        assert(decimal.exponent == decimal.exponent.max);
+        assert(key == DecimalExponentKey.infinity);
+        assert(cast(double) decimal.view == double.infinity);
+
+        assert(decimal.fromStringImpl("-inf", key));
+        assert(decimal.coefficient.length == 0);
+        assert(decimal.exponent == decimal.exponent.max);
         assert(key == DecimalExponentKey.infinity);
+        assert(cast(double) decimal.view == -double.infinity);
 
         assert(!decimal.fromStringImpl("3.3.4", key));
         assert(!decimal.fromStringImpl("3.4.", key));
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index ddba2f2f..a2984b36 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1989,16 +1989,19 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         }
         else
         {
-            coefficient = coefficient.init;
+            exponent = exponent.max;
             if (str.length == 2)
             {
                 if ((d == 'i' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"nf"))
                 {
+                    coefficient = coefficient.init;
                     key = DecimalExponentKey.infinity;
                     return true;
                 }
                 if ((d == 'n' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"an"))
                 {
+                    coefficient.leastSignificant = 1;
+                    coefficient = coefficient.topLeastSignificantPart(1);
                     key = DecimalExponentKey.nan;
                     return true;
                 }
@@ -2115,12 +2118,20 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 
         auto coeff = coefficient.lightConst;
         T ret = 0;
+
+        if (_expect(exponent == exponent.max, false))
+        {
+            ret = coeff.coefficients.length ? T.nan : T.infinity;
+            goto R;
+        }
+
         static if (!wordNormalized)
             coeff = coeff.normalized;
         static if (!nonZero)
             if (coeff.coefficients.length == 0)
                 goto R;
         enum S = 9;
+
         static if (T.mant_dig < 64)
         {
             Fp!64 load(Exp e)

From 23cdf7dc24b55798e55221355b146907de0cb7e8 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Thu, 11 Mar 2021 17:01:02 +0700
Subject: [PATCH 168/573] improve human readable floating point formatting
 (#331)

* improve human readable floating point formatting

* fixup
---
 source/mir/bignum/decimal.d | 47 +++++++++++++++++++++----------------
 source/mir/format.d         | 17 +++++++++++---
 2 files changed, 41 insertions(+), 23 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 7ce72b40..9a95627f 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -240,7 +240,7 @@ struct Decimal(size_t maxSize64)
         size_t coefficientLength;
         static if (size_t.sizeof == 8)
         {
-            if (__ctfe && size_t.sizeof == 8)
+            if (__ctfe)
             {
                 uint[coefficient.data.length * 2] data;
                 foreach (i; 0 .. coefficient.length)
@@ -280,9 +280,25 @@ struct Decimal(size_t maxSize64)
         {
             // try print decimal form without exponent
             // up to 6 digits exluding leading 0. or final .0
+            sizediff_t s = this.exponent + coefficientLength;
+            if (s <= 0)
+            {
+                //0.001....
+                //0.0001
+                //0.00001
+                //0.000001
+                if (s >= -2 || this.exponent >= -6)
+                {
+                    w.put(zeros[!coefficient.sign .. -s + 2 + 1]);
+                    w.put(buffer[$ - coefficientLength .. $]);
+                    return;
+                }
+            }
+            else
             if (this.exponent >= 0)
             {
-                if (this.exponent + coefficientLength <= 6)
+                ///dddddd.0
+                if (s <= 6)
                 {
                     buffer[$ - coefficientLength - 1] = '-';
                     w.put(buffer[$ - coefficientLength - coefficient.sign .. $]);
@@ -291,25 +307,15 @@ struct Decimal(size_t maxSize64)
                 }
             }
             else
-            if (this.exponent < 0)
             {
-                if (this.exponent >= -6 && coefficientLength <= 6)
+                ///dddddd.d....
+                if (s <= 6 || coefficientLength <= 6)
                 {
-                    sizediff_t zerosLength = -this.exponent - coefficientLength;
-                    if (zerosLength >= 0)
-                    {
-                        w.put(zeros[!coefficient.sign .. zerosLength + 2 + 1]);
-                        w.put(buffer[$ - coefficientLength .. $]);
-                        return;
-                    }
-                    else
-                    {
-                        buffer[$ - coefficientLength - 1] = '-';
-                        w.put(buffer[$ - coefficientLength  - coefficient.sign .. $ - coefficientLength - zerosLength]);
-                        buffer[$ - coefficientLength - zerosLength - 1] = '.';
-                        w.put(buffer[$ - coefficientLength - zerosLength - 1 .. $]);
-                        return;
-                    }
+                    buffer[$ - coefficientLength - 1] = '-';
+                    w.put(buffer[$ - coefficientLength  - coefficient.sign .. $ - coefficientLength + s]);
+                    buffer[$ - coefficientLength + s - 1] = '.';
+                    w.put(buffer[$ - coefficientLength + s - 1 .. $]);
+                    return;
                 }
             }
         }
@@ -340,7 +346,8 @@ struct Decimal(size_t maxSize64)
         else
             enum N = 11;
 
-        auto expLength = printSignedToTail(exponent, buffer[$ - N .. $], '\0');
+        // prints e+/-exponent
+        auto expLength = printSignedToTail(exponent, buffer[$ - N .. $], '+');
         buffer[$ - ++expLength] = 'e';
         w.put(buffer[$ - expLength .. $]);
     }
diff --git a/source/mir/format.d b/source/mir/format.d
index 8583ce25..02dab3e7 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -718,11 +718,22 @@ unittest
     assert(stringBuf() << 0.000003 << getData == "0.000003");
     assert(stringBuf() << -3e-7 << getData == "-3e-7");
     assert(stringBuf() << 123456.0 << getData == "123456.0");
+    assert(stringBuf() << 123456.1 << getData == "123456.1");
     assert(stringBuf() << 12.3456 << getData == "12.3456");
     assert(stringBuf() << -0.123456 << getData == "-0.123456");
-    assert(stringBuf() << 0.1234567 << getData == "1.234567e-1");
-    assert(stringBuf() << -1234567.0 << getData == "-1.234567e6");
-    assert(stringBuf() << 1234567890123.0 << getData == "1.234567890123e12");
+    assert(stringBuf() << 0.1234567 << getData == "0.1234567");
+    assert(stringBuf() << 0.01234567 << getData == "0.01234567");
+    assert(stringBuf() << 0.001234567 << getData == "0.001234567");
+    assert(stringBuf() << 1.234567e-4 << getData == "1.234567e-4");
+    assert(stringBuf() << -1234567.0 << getData == "-1.234567e+6");
+    assert(stringBuf() << 123456.7890123 << getData == "123456.7890123");
+    assert(stringBuf() << 1234567.890123 << getData == "1.234567890123e+6");
+    assert(stringBuf() << 1234567890123.0 << getData == "1.234567890123e+12");
+    assert(stringBuf() << 1234567890123.0 << getData == "1.234567890123e+12");
+    assert(stringBuf() << 0.30000000000000004 << getData == "0.30000000000000004");
+    assert(stringBuf() << 0.030000000000000002 << getData == "0.030000000000000002");
+    assert(stringBuf() << 0.0030000000000000005 << getData == "0.0030000000000000005");
+    assert(stringBuf() << 3.0000000000000003e-4 << getData == "3.0000000000000003e-4");
     assert(stringBuf() << +double.nan << getData == "nan");
     assert(stringBuf() << -double.nan << getData == "nan");
     assert(stringBuf() << +double.infinity << getData == "+inf");

From c4c03a06db23109f7a4f696aa13b8aa59db38dbd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 11 Mar 2021 21:14:38 +0700
Subject: [PATCH 169/573] check test

---
 source/mir/appender.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index 49cbe7ae..9aa35a8b 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -338,7 +338,8 @@ unittest
     }
     Algebraic!(int, string) x;
     x = 42;
-    assert(x.to!string == "42");
+    auto s = x.to!string;
+    assert(s == "42", s);
     x = "abc";
     assert(x.to!string == "abc");
     Algebraic!S y;

From 6f54b2f51e6a087b4ca39910fda62df3c6e75c64 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sat, 13 Mar 2021 12:06:05 +0700
Subject: [PATCH 170/573] human friendly fp print with underscores (#332)

---
 source/mir/bignum/decimal.d |  74 ++++++++++++++++--
 source/mir/format.d         | 147 ++++++++++++++++++++++--------------
 2 files changed, 158 insertions(+), 63 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 9a95627f..ade3dd3b 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -223,7 +223,7 @@ struct Decimal(size_t maxSize64)
     void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
         if(isSomeChar!C && isMutable!C)
     {
-        assert(spec == NumericSpec.exponent || spec == NumericSpec.human);
+        assert(spec == NumericSpec.exponent || spec == NumericSpec.human || spec == NumericSpec.humanWithUnderscores);
         import mir.utility: _expect;
         // handle special values
         if (_expect(exponent == exponent.max, false))
@@ -235,7 +235,8 @@ struct Decimal(size_t maxSize64)
             return;
         }
 
-        C[coefficientBufferLength] buffer = void;
+        C[coefficientBufferLength + 16] buffer0 = void;
+        auto buffer = buffer0[0 .. $ - 16];
 
         size_t coefficientLength;
         static if (size_t.sizeof == 8)
@@ -274,13 +275,13 @@ struct Decimal(size_t maxSize64)
             coefficientLength = work.view.unsigned.toStringImpl(buffer);
         }
 
-        static immutable C[10] zeros = "-0.00000.0";
+        static immutable C[16] zeros = "-0.00000000000.0";
+        sizediff_t s = this.exponent + coefficientLength;
 
         if (spec.format == NumericSpec.Format.human)
         {
             // try print decimal form without exponent
             // up to 6 digits exluding leading 0. or final .0
-            sizediff_t s = this.exponent + coefficientLength;
             if (s <= 0)
             {
                 //0.001....
@@ -289,6 +290,7 @@ struct Decimal(size_t maxSize64)
                 //0.000001
                 if (s >= -2 || this.exponent >= -6)
                 {
+                T0:
                     w.put(zeros[!coefficient.sign .. -s + 2 + 1]);
                     w.put(buffer[$ - coefficientLength .. $]);
                     return;
@@ -313,16 +315,75 @@ struct Decimal(size_t maxSize64)
                 {
                     buffer[$ - coefficientLength - 1] = '-';
                     w.put(buffer[$ - coefficientLength  - coefficient.sign .. $ - coefficientLength + s]);
+                T2:
                     buffer[$ - coefficientLength + s - 1] = '.';
                     w.put(buffer[$ - coefficientLength + s - 1 .. $]);
                     return;
                 }
             }
         }
+        else
+        if (spec.format == NumericSpec.Format.humanWithUnderscores)
+        {
+            void putL(scope const(C)[] b)
+            {
+                assert(b.length);
+
+                if (coefficient.sign)
+                    w.put(zeros[0 .. 1]);
+
+                auto r = b.length % 3;
+                if (r == 0)
+                    r = 3;
+                goto LS;
+                do
+                {
+                    w.put("_");
+                LS:
+                    w.put(b[0 .. r]);
+                    b = b[r .. $];
+                    r = 3;
+                }
+                while(b.length);
+            }
+
+            // try print decimal form without exponent
+            // up to 6 digits exluding leading 0. or final .0
+            if (s <= 0)
+            {
+                //0.00000d....
+                if (s >= -5 || this.exponent >= -6)
+                {
+                    goto T0;
+                }
+            }
+            else
+            if (this.exponent >= 0)
+            {
+                ///dddddd.0
+                ///ddd000000.0
+                if (s <= 12)
+                {
+                    buffer0[$ - 16 .. $] = '0';
+                    putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + exponent]);
+                    w.put(".0");
+                    return;
+                }
+            }
+            else
+            {
+                ///dddddd.d....
+                if (s <= 12 || coefficientLength <= 12)
+                {
+                    putL(buffer[$ - coefficientLength .. $ - coefficientLength + s]);
+                    goto T2;
+                }
+            }
+        }
 
         assert(coefficientLength);
 
-        sizediff_t exponent = this.exponent + coefficientLength - 1;
+        sizediff_t exponent = s - 1;
 
         if (coefficientLength > 1)
         {
@@ -338,7 +399,6 @@ struct Decimal(size_t maxSize64)
 
         w.put(buffer[$ - coefficientLength .. $]);
 
-        C[expBufferLength] expBuffer = void;
         import mir.format_impl: printSignedToTail;
 
         static if (sizediff_t.sizeof == 8)
@@ -347,7 +407,7 @@ struct Decimal(size_t maxSize64)
             enum N = 11;
 
         // prints e+/-exponent
-        auto expLength = printSignedToTail(exponent, buffer[$ - N .. $], '+');
+        auto expLength = printSignedToTail(exponent, buffer0[$ - N - 16 .. $ - 16], '+');
         buffer[$ - ++expLength] = 'e';
         w.put(buffer[$ - expLength .. $]);
     }
diff --git a/source/mir/format.d b/source/mir/format.d
index 02dab3e7..c1aa8b3c 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -194,6 +194,11 @@ struct NumericSpec
         +/
         human,
         /++
+        Human-frindly precise output with underscores.
+        Examples: `345_123_230_000.1234`, but `1e12`
+        +/
+        humanWithUnderscores,
+        /++
         Precise output with explicit exponent.
         Examples: `1e-6`, `6e6`, `1.23456789e-100`.
         +/
@@ -210,10 +215,14 @@ struct NumericSpec
     enum NumericSpec exponent = NumericSpec(Format.exponent);
 
     /++
-    Precise output with explicit exponent.
-    Examples: `1e-6`, `6e6`, `1.23456789e-100`.
+    Human-frindly precise output.
     +/
     enum NumericSpec human = NumericSpec(Format.human);
+
+    /++
+    Human-frindly precise output with underscores.
+    +/
+    enum NumericSpec humanWithUnderscores = NumericSpec(Format.humanWithUnderscores);
 }
 
 // 16-bytes
@@ -279,31 +288,6 @@ FormattedFloating!T withFormat(T)(const T value, FormatSpec spec)
     return typeof(return)(value, spec);
 }
 
-/++
-Wrapper to format floating point numbers using C's library.
-+/
-struct FormattedNumeric(T)
-    if(is(T == float) || is(T == double) || is(T == real))
-{
-    ///
-    T value;
-    ///
-    NumericSpec spec;
-
-    ///
-    void toString(C = char, W)(scope ref W w) scope const
-    {
-        print(w, value, spec);
-    }
-}
-
-/// ditto
-FormattedNumeric!T withFormat(T)(const T value, NumericSpec spec)
-{
-    version(LDC) pragma(inline);
-    return typeof(return)(value, spec);
-}
-
 /++
 +/
 struct HexAddress(T)
@@ -706,38 +690,89 @@ ref W print(C = char, W, T)(scope return ref W w, const T c, NumericSpec spec =
     return w;
 }
 
-///
+/// Human friendly precise output (default)
 version(mir_bignum_test)
-@safe pure nothrow
+@safe pure nothrow @nogc
+unittest
+{
+    stringBuf buffer;
+
+    void check(double num, string value)
+    {
+        assert(buffer.print(num).data == value, buffer.data); buffer.reset;
+    }
+
+    check(-0.0, "-0.0");
+    check(0.0, "0.0");
+    check(-0.01, "-0.01");
+    check(0.0125, "0.0125");
+    check(0.000003, "0.000003");
+    check(-3e-7, "-3e-7");
+    check(123456.0, "123456.0");
+    check(123456.1, "123456.1");
+    check(12.3456, "12.3456");
+    check(-0.123456, "-0.123456");
+    check(0.1234567, "0.1234567");
+    check(0.01234567, "0.01234567");
+    check(0.001234567, "0.001234567");
+    check(1.234567e-4, "1.234567e-4");
+    check(-1234567.0, "-1.234567e+6");
+    check(123456.7890123, "123456.7890123");
+    check(1234567.890123, "1.234567890123e+6");
+    check(1234567890123.0, "1.234567890123e+12");
+    check(0.30000000000000004, "0.30000000000000004");
+    check(0.030000000000000002, "0.030000000000000002");
+    check(0.0030000000000000005, "0.0030000000000000005");
+    check(3.0000000000000003e-4, "3.0000000000000003e-4");
+    check(+double.nan, "nan");
+    check(-double.nan, "nan");
+    check(+double.infinity, "+inf");
+    check(-double.infinity, "-inf");
+}
+
+/// Human friendly precise output with underscores
+version(mir_bignum_test)
+@safe pure nothrow @nogc
 unittest
 {
-    assert(stringBuf() << -0.0 << getData == "-0.0");
-    assert(stringBuf() << 0.0 << getData == "0.0");
-    assert(stringBuf() << -0.01 << getData == "-0.01");
-    assert(stringBuf() << 0.0125 << getData == "0.0125");
-    assert(stringBuf() << 0.000003 << getData == "0.000003");
-    assert(stringBuf() << -3e-7 << getData == "-3e-7");
-    assert(stringBuf() << 123456.0 << getData == "123456.0");
-    assert(stringBuf() << 123456.1 << getData == "123456.1");
-    assert(stringBuf() << 12.3456 << getData == "12.3456");
-    assert(stringBuf() << -0.123456 << getData == "-0.123456");
-    assert(stringBuf() << 0.1234567 << getData == "0.1234567");
-    assert(stringBuf() << 0.01234567 << getData == "0.01234567");
-    assert(stringBuf() << 0.001234567 << getData == "0.001234567");
-    assert(stringBuf() << 1.234567e-4 << getData == "1.234567e-4");
-    assert(stringBuf() << -1234567.0 << getData == "-1.234567e+6");
-    assert(stringBuf() << 123456.7890123 << getData == "123456.7890123");
-    assert(stringBuf() << 1234567.890123 << getData == "1.234567890123e+6");
-    assert(stringBuf() << 1234567890123.0 << getData == "1.234567890123e+12");
-    assert(stringBuf() << 1234567890123.0 << getData == "1.234567890123e+12");
-    assert(stringBuf() << 0.30000000000000004 << getData == "0.30000000000000004");
-    assert(stringBuf() << 0.030000000000000002 << getData == "0.030000000000000002");
-    assert(stringBuf() << 0.0030000000000000005 << getData == "0.0030000000000000005");
-    assert(stringBuf() << 3.0000000000000003e-4 << getData == "3.0000000000000003e-4");
-    assert(stringBuf() << +double.nan << getData == "nan");
-    assert(stringBuf() << -double.nan << getData == "nan");
-    assert(stringBuf() << +double.infinity << getData == "+inf");
-    assert(stringBuf() << -double.infinity << getData == "-inf");
+    auto spec = NumericSpec.humanWithUnderscores;
+    stringBuf buffer;
+
+    void check(double num, string value)
+    {
+        assert(buffer.print(num, spec).data == value, buffer.data); buffer.reset;
+    }
+
+    check(-0.0, "-0.0");
+    check(0.0, "0.0");
+    check(-0.01, "-0.01");
+    check(0.0125, "0.0125");
+    check(0.000003, "0.000003");
+    check(-3e-7, "-3e-7");
+    check(123456.0, "123_456.0");
+    check(123456e5, "12_345_600_000.0");
+    check(123456.1, "123_456.1");
+    check(12.3456, "12.3456");
+    check(-0.123456, "-0.123456");
+    check(0.1234567, "0.1234567");
+    check(0.01234567, "0.01234567");
+    check(0.001234567, "0.001234567");
+    check(1.234567e-4, "0.0001234567");
+    check(-1234567.0, "-1_234_567.0");
+    check(123456.7890123, "123_456.7890123");
+    check(1234567.890123, "1_234_567.890123");
+    check(123456789012.0, "123_456_789_012.0");
+    check(1234567890123.0, "1.234567890123e+12");
+    check(0.30000000000000004, "0.30000000000000004");
+    check(0.030000000000000002, "0.030000000000000002");
+    check(0.0030000000000000005, "0.0030000000000000005");
+    check(3.0000000000000003e-4, "0.00030000000000000003");
+    check(3.0000000000000005e-6, "0.0000030000000000000005");
+    check(3.0000000000000004e-7, "3.0000000000000004e-7");
+    check(+double.nan, "nan");
+    check(-double.nan, "nan");
+    check(+double.infinity, "+inf");
+    check(-double.infinity, "-inf");
 }
 
 /// Prints structs and unions

From 7dc486d288ed65071e41b9b3c0166e06ba432918 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 13 Mar 2021 12:48:50 +0700
Subject: [PATCH 171/573] add NumericSpec.exponentChar

---
 source/mir/bignum/decimal.d | 2 +-
 source/mir/format.d         | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index ade3dd3b..ddc884c7 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -408,7 +408,7 @@ struct Decimal(size_t maxSize64)
 
         // prints e+/-exponent
         auto expLength = printSignedToTail(exponent, buffer0[$ - N - 16 .. $ - 16], '+');
-        buffer[$ - ++expLength] = 'e';
+        buffer[$ - ++expLength] = spec.exponentChar;
         w.put(buffer[$ - expLength .. $]);
     }
 
diff --git a/source/mir/format.d b/source/mir/format.d
index c1aa8b3c..13c5dc9a 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -208,6 +208,9 @@ struct NumericSpec
     ///
     Format format;
 
+    /// Defaults to 'e'
+    char exponentChar = 'e';
+
     /++
     Precise output with explicit exponent.
     Examples: `1e-6`, `6e6`, `1.23456789e-100`.

From ef6a5fe8d53f6a224aef5c1414fc657c08c191ad Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sat, 13 Mar 2021 19:18:09 +0700
Subject: [PATCH 172/573] extend NumericSpec (#333)

---
 source/mir/bignum/decimal.d | 127 ++++++++++++++++--------------------
 source/mir/format.d         |  51 ++++++---------
 2 files changed, 78 insertions(+), 100 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index ddc884c7..0d7947ce 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -223,7 +223,7 @@ struct Decimal(size_t maxSize64)
     void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
         if(isSomeChar!C && isMutable!C)
     {
-        assert(spec == NumericSpec.exponent || spec == NumericSpec.human || spec == NumericSpec.humanWithUnderscores);
+        assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human);
         import mir.utility: _expect;
         // handle special values
         if (_expect(exponent == exponent.max, false))
@@ -275,62 +275,19 @@ struct Decimal(size_t maxSize64)
             coefficientLength = work.view.unsigned.toStringImpl(buffer);
         }
 
-        static immutable C[16] zeros = "-0.00000000000.0";
+        static immutable C[16] zeros = "0.00000000000.0";
+        char sign = coefficient.sign ? '-' : '+';
+        bool addSign = coefficient.sign || spec.plus;
         sizediff_t s = this.exponent + coefficientLength;
 
         if (spec.format == NumericSpec.Format.human)
-        {
-            // try print decimal form without exponent
-            // up to 6 digits exluding leading 0. or final .0
-            if (s <= 0)
-            {
-                //0.001....
-                //0.0001
-                //0.00001
-                //0.000001
-                if (s >= -2 || this.exponent >= -6)
-                {
-                T0:
-                    w.put(zeros[!coefficient.sign .. -s + 2 + 1]);
-                    w.put(buffer[$ - coefficientLength .. $]);
-                    return;
-                }
-            }
-            else
-            if (this.exponent >= 0)
-            {
-                ///dddddd.0
-                if (s <= 6)
-                {
-                    buffer[$ - coefficientLength - 1] = '-';
-                    w.put(buffer[$ - coefficientLength - coefficient.sign .. $]);
-                    w.put(zeros[$ - (exponent + 2) .. $]);
-                    return;
-                }
-            }
-            else
-            {
-                ///dddddd.d....
-                if (s <= 6 || coefficientLength <= 6)
-                {
-                    buffer[$ - coefficientLength - 1] = '-';
-                    w.put(buffer[$ - coefficientLength  - coefficient.sign .. $ - coefficientLength + s]);
-                T2:
-                    buffer[$ - coefficientLength + s - 1] = '.';
-                    w.put(buffer[$ - coefficientLength + s - 1 .. $]);
-                    return;
-                }
-            }
-        }
-        else
-        if (spec.format == NumericSpec.Format.humanWithUnderscores)
         {
             void putL(scope const(C)[] b)
             {
                 assert(b.length);
 
-                if (coefficient.sign)
-                    w.put(zeros[0 .. 1]);
+                if (addSign)
+                    w.put(sign);
 
                 auto r = b.length % 3;
                 if (r == 0)
@@ -338,7 +295,7 @@ struct Decimal(size_t maxSize64)
                 goto LS;
                 do
                 {
-                    w.put("_");
+                    w.put(spec.separatorChar);
                 LS:
                     w.put(b[0 .. r]);
                     b = b[r .. $];
@@ -351,32 +308,68 @@ struct Decimal(size_t maxSize64)
             // up to 6 digits exluding leading 0. or final .0
             if (s <= 0)
             {
-                //0.00000d....
-                if (s >= -5 || this.exponent >= -6)
+                //0.001....
+                //0.0001
+                //0.00001
+                //0.000001
+                //If separatorChar is defined lets be less greed for space.
+                if (this.exponent >= -6 || s >= -2 - (spec.separatorChar != 0) * 3)
                 {
-                    goto T0;
+                    if (addSign)
+                        w.put(sign);
+                    w.put(zeros[0 .. -s + 2 + 1]);
+                    w.put(buffer[$ - coefficientLength .. $]);
+                    return;
                 }
             }
             else
             if (this.exponent >= 0)
             {
                 ///dddddd.0
-                ///ddd000000.0
-                if (s <= 12)
+                if (!spec.separatorChar)
                 {
-                    buffer0[$ - 16 .. $] = '0';
-                    putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + exponent]);
-                    w.put(".0");
-                    return;
+                    if (s <= 6)
+                    {
+                        buffer[$ - coefficientLength - 1] = sign;
+                        w.put(buffer[$ - coefficientLength - addSign .. $]);
+                        w.put(zeros[$ - (exponent + 2) .. $]);
+                        return;
+                    }
+                }
+                else
+                {
+                    if (s <= 12)
+                    {
+                        buffer0[$ - 16 .. $] = '0';
+                        putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + exponent]);
+                        w.put(".0");
+                        return;
+                    }
                 }
             }
             else
             {
-                ///dddddd.d....
-                if (s <= 12 || coefficientLength <= 12)
+                ///dddddd.0
+                if (!spec.separatorChar)
                 {
-                    putL(buffer[$ - coefficientLength .. $ - coefficientLength + s]);
-                    goto T2;
+                    ///dddddd.d....
+                    if (s <= 6 || coefficientLength <= 6)
+                    {
+                        buffer[$ - coefficientLength - 1] = sign;
+                        w.put(buffer[$ - coefficientLength  - addSign .. $ - coefficientLength + s]);
+                    T2:
+                        buffer[$ - coefficientLength + s - 1] = '.';
+                        w.put(buffer[$ - coefficientLength + s - 1 .. $]);
+                        return;
+                    }
+                }
+                else
+                {
+                    if (s <= 12 || coefficientLength <= 12)
+                    {
+                        putL(buffer[$ - coefficientLength .. $ - coefficientLength + s]);
+                        goto T2;
+                    }
                 }
             }
         }
@@ -392,12 +385,8 @@ struct Decimal(size_t maxSize64)
             buffer[$ - ++coefficientLength] = c;
         }
 
-        if (coefficient.sign)
-        {
-            buffer[$ - ++coefficientLength] = '-';
-        }
-
-        w.put(buffer[$ - coefficientLength .. $]);
+        buffer[$ - coefficientLength - 1] = sign;
+        w.put(buffer[$ - coefficientLength - addSign .. $]);
 
         import mir.format_impl: printSignedToTail;
 
diff --git a/source/mir/format.d b/source/mir/format.d
index 13c5dc9a..6fa8034c 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -194,11 +194,6 @@ struct NumericSpec
         +/
         human,
         /++
-        Human-frindly precise output with underscores.
-        Examples: `345_123_230_000.1234`, but `1e12`
-        +/
-        humanWithUnderscores,
-        /++
         Precise output with explicit exponent.
         Examples: `1e-6`, `6e6`, `1.23456789e-100`.
         +/
@@ -208,9 +203,15 @@ struct NumericSpec
     ///
     Format format;
 
+    /// Default valus is '\0' (no separators)
+    char separatorChar = '\0';
+
     /// Defaults to 'e'
     char exponentChar = 'e';
 
+    /// Adds '+' to positive numbers and `+0`.
+    bool plus;
+
     /++
     Precise output with explicit exponent.
     Examples: `1e-6`, `6e6`, `1.23456789e-100`.
@@ -221,11 +222,6 @@ struct NumericSpec
     Human-frindly precise output.
     +/
     enum NumericSpec human = NumericSpec(Format.human);
-
-    /++
-    Human-frindly precise output with underscores.
-    +/
-    enum NumericSpec humanWithUnderscores = NumericSpec(Format.humanWithUnderscores);
 }
 
 // 16-bytes
@@ -698,11 +694,12 @@ version(mir_bignum_test)
 @safe pure nothrow @nogc
 unittest
 {
+    auto spec = NumericSpec.human;
     stringBuf buffer;
 
     void check(double num, string value)
     {
-        assert(buffer.print(num).data == value, buffer.data); buffer.reset;
+        assert(buffer.print(num, spec).data == value, buffer.data); buffer.reset;
     }
 
     check(-0.0, "-0.0");
@@ -731,20 +728,8 @@ unittest
     check(-double.nan, "nan");
     check(+double.infinity, "+inf");
     check(-double.infinity, "-inf");
-}
 
-/// Human friendly precise output with underscores
-version(mir_bignum_test)
-@safe pure nothrow @nogc
-unittest
-{
-    auto spec = NumericSpec.humanWithUnderscores;
-    stringBuf buffer;
-
-    void check(double num, string value)
-    {
-        assert(buffer.print(num, spec).data == value, buffer.data); buffer.reset;
-    }
+    spec.separatorChar = ',';
 
     check(-0.0, "-0.0");
     check(0.0, "0.0");
@@ -752,19 +737,19 @@ unittest
     check(0.0125, "0.0125");
     check(0.000003, "0.000003");
     check(-3e-7, "-3e-7");
-    check(123456.0, "123_456.0");
-    check(123456e5, "12_345_600_000.0");
-    check(123456.1, "123_456.1");
+    check(123456.0, "123,456.0");
+    check(123456e5, "12,345,600,000.0");
+    check(123456.1, "123,456.1");
     check(12.3456, "12.3456");
     check(-0.123456, "-0.123456");
     check(0.1234567, "0.1234567");
     check(0.01234567, "0.01234567");
     check(0.001234567, "0.001234567");
     check(1.234567e-4, "0.0001234567");
-    check(-1234567.0, "-1_234_567.0");
-    check(123456.7890123, "123_456.7890123");
-    check(1234567.890123, "1_234_567.890123");
-    check(123456789012.0, "123_456_789_012.0");
+    check(-1234567.0, "-1,234,567.0");
+    check(123456.7890123, "123,456.7890123");
+    check(1234567.890123, "1,234,567.890123");
+    check(123456789012.0, "123,456,789,012.0");
     check(1234567890123.0, "1.234567890123e+12");
     check(0.30000000000000004, "0.30000000000000004");
     check(0.030000000000000002, "0.030000000000000002");
@@ -776,6 +761,10 @@ unittest
     check(-double.nan, "nan");
     check(+double.infinity, "+inf");
     check(-double.infinity, "-inf");
+
+    spec.plus = true;
+    check(0.0125, "+0.0125");
+    check(-0.0125, "-0.0125");
 }
 
 /// Prints structs and unions

From bf6804a74c4c84e866ea820e84db281e7c50ee40 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 13 Mar 2021 20:44:18 +0700
Subject: [PATCH 173/573] fixup

---
 source/mir/bignum/decimal.d | 16 +++++++++-------
 source/mir/format.d         |  7 +++++++
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 0d7947ce..e71a03c3 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -275,13 +275,15 @@ struct Decimal(size_t maxSize64)
             coefficientLength = work.view.unsigned.toStringImpl(buffer);
         }
 
-        static immutable C[16] zeros = "0.00000000000.0";
+        static immutable C[9] zeros = "0.00000.0";
         char sign = coefficient.sign ? '-' : '+';
         bool addSign = coefficient.sign || spec.plus;
         sizediff_t s = this.exponent + coefficientLength;
 
         if (spec.format == NumericSpec.Format.human)
         {
+            if (!spec.separatorCount)
+                spec.separatorCount = 3;
             void putL(scope const(C)[] b)
             {
                 assert(b.length);
@@ -289,9 +291,9 @@ struct Decimal(size_t maxSize64)
                 if (addSign)
                     w.put(sign);
 
-                auto r = b.length % 3;
+                auto r = b.length % spec.separatorCount;
                 if (r == 0)
-                    r = 3;
+                    r = spec.separatorCount;
                 goto LS;
                 do
                 {
@@ -299,7 +301,7 @@ struct Decimal(size_t maxSize64)
                 LS:
                     w.put(b[0 .. r]);
                     b = b[r .. $];
-                    r = 3;
+                    r = spec.separatorCount;
                 }
                 while(b.length);
             }
@@ -317,7 +319,7 @@ struct Decimal(size_t maxSize64)
                 {
                     if (addSign)
                         w.put(sign);
-                    w.put(zeros[0 .. -s + 2 + 1]);
+                    w.put(zeros[0 .. -s + 2]);
                     w.put(buffer[$ - coefficientLength .. $]);
                     return;
                 }
@@ -332,7 +334,7 @@ struct Decimal(size_t maxSize64)
                     {
                         buffer[$ - coefficientLength - 1] = sign;
                         w.put(buffer[$ - coefficientLength - addSign .. $]);
-                        w.put(zeros[$ - (exponent + 2) .. $]);
+                        w.put(zeros[$ - (this.exponent + 2) .. $]);
                         return;
                     }
                 }
@@ -341,7 +343,7 @@ struct Decimal(size_t maxSize64)
                     if (s <= 12)
                     {
                         buffer0[$ - 16 .. $] = '0';
-                        putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + exponent]);
+                        putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + this.exponent]);
                         w.put(".0");
                         return;
                     }
diff --git a/source/mir/format.d b/source/mir/format.d
index 6fa8034c..c003787c 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -212,6 +212,9 @@ struct NumericSpec
     /// Adds '+' to positive numbers and `+0`.
     bool plus;
 
+    /// Separator count
+    ubyte separatorCount = 3;
+
     /++
     Precise output with explicit exponent.
     Examples: `1e-6`, `6e6`, `1.23456789e-100`.
@@ -762,6 +765,10 @@ unittest
     check(+double.infinity, "+inf");
     check(-double.infinity, "-inf");
 
+    spec.separatorChar = '_';
+    spec.separatorCount = 2;
+    check(123456e5, "1_23_45_60_00_00.0");
+
     spec.plus = true;
     check(0.0125, "+0.0125");
     check(-0.0125, "-0.0125");

From ce39173f002f1dd36241d44e08a1d127c9df193d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 13 Mar 2021 21:45:51 +0700
Subject: [PATCH 174/573] fixup output for json

---
 source/mir/bignum/decimal.d | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index e71a03c3..9cb45350 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -12,6 +12,7 @@ public import mir.bignum.low_level_view: DecimalExponentKey;
 import mir.bignum.low_level_view: ceilLog10Exp2;
 
 private enum expBufferLength = 2 + ceilLog10Exp2(size_t.sizeof * 8);
+private static immutable C[9] zerosImpl(C) = "0.00000.0";
 
 /++
 Stack-allocated decimal type.
@@ -275,11 +276,12 @@ struct Decimal(size_t maxSize64)
             coefficientLength = work.view.unsigned.toStringImpl(buffer);
         }
 
-        static immutable C[9] zeros = "0.00000.0";
-        char sign = coefficient.sign ? '-' : '+';
+        C[1] sign = coefficient.sign ? "-" : "+";
         bool addSign = coefficient.sign || spec.plus;
         sizediff_t s = this.exponent + coefficientLength;
 
+        alias zeros = zerosImpl!C;
+
         if (spec.format == NumericSpec.Format.human)
         {
             if (!spec.separatorCount)
@@ -332,7 +334,7 @@ struct Decimal(size_t maxSize64)
                 {
                     if (s <= 6)
                     {
-                        buffer[$ - coefficientLength - 1] = sign;
+                        buffer[$ - coefficientLength - 1] = sign[0];
                         w.put(buffer[$ - coefficientLength - addSign .. $]);
                         w.put(zeros[$ - (this.exponent + 2) .. $]);
                         return;
@@ -344,7 +346,7 @@ struct Decimal(size_t maxSize64)
                     {
                         buffer0[$ - 16 .. $] = '0';
                         putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + this.exponent]);
-                        w.put(".0");
+                        w.put(zeros[$ - 2 .. $]);
                         return;
                     }
                 }
@@ -357,7 +359,7 @@ struct Decimal(size_t maxSize64)
                     ///dddddd.d....
                     if (s <= 6 || coefficientLength <= 6)
                     {
-                        buffer[$ - coefficientLength - 1] = sign;
+                        buffer[$ - coefficientLength - 1] = sign[0];
                         w.put(buffer[$ - coefficientLength  - addSign .. $ - coefficientLength + s]);
                     T2:
                         buffer[$ - coefficientLength + s - 1] = '.';
@@ -387,7 +389,7 @@ struct Decimal(size_t maxSize64)
             buffer[$ - ++coefficientLength] = c;
         }
 
-        buffer[$ - coefficientLength - 1] = sign;
+        buffer[$ - coefficientLength - 1] = sign[0];
         w.put(buffer[$ - coefficientLength - addSign .. $]);
 
         import mir.format_impl: printSignedToTail;

From 78c4ce5a89df09baba717ae08a345bbc0c05b446 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 13 Mar 2021 21:55:03 +0700
Subject: [PATCH 175/573] fixup

---
 source/mir/bignum/decimal.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 9cb45350..8677f485 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -296,10 +296,11 @@ struct Decimal(size_t maxSize64)
                 auto r = b.length % spec.separatorCount;
                 if (r == 0)
                     r = spec.separatorCount;
+                C[1] sep = spec.separatorChar;
                 goto LS;
                 do
                 {
-                    w.put(spec.separatorChar);
+                    w.put(sep);
                 LS:
                     w.put(b[0 .. r]);
                     b = b[r .. $];

From 40fd333912f9281f71ae6bba7cb89dd6146c2029 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 16 Mar 2021 09:58:29 +0700
Subject: [PATCH 176/573] fixup

---
 source/mir/bignum/decimal.d | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 8677f485..83cb3107 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -291,7 +291,7 @@ struct Decimal(size_t maxSize64)
                 assert(b.length);
 
                 if (addSign)
-                    w.put(sign);
+                    w.put(sign[]);
 
                 auto r = b.length % spec.separatorCount;
                 if (r == 0)
@@ -300,7 +300,7 @@ struct Decimal(size_t maxSize64)
                 goto LS;
                 do
                 {
-                    w.put(sep);
+                    w.put(sep[]);
                 LS:
                     w.put(b[0 .. r]);
                     b = b[r .. $];
@@ -321,7 +321,7 @@ struct Decimal(size_t maxSize64)
                 if (this.exponent >= -6 || s >= -2 - (spec.separatorChar != 0) * 3)
                 {
                     if (addSign)
-                        w.put(sign);
+                        w.put(sign[]);
                     w.put(zeros[0 .. -s + 2]);
                     w.put(buffer[$ - coefficientLength .. $]);
                     return;

From 1b2f3db39db19f22413d0695635625a8cca7b79b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 24 Mar 2021 11:49:29 +0700
Subject: [PATCH 177/573] fixup fp parsing

---
 source/mir/bignum/decimal.d        |  4 ++--
 source/mir/bignum/low_level_view.d | 15 ++++++++-------
 source/mir/parse.d                 |  2 ++
 3 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 83cb3107..f245dd2a 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -187,9 +187,9 @@ struct Decimal(size_t maxSize64)
 
         assert(!decimal.fromStringImpl("3.3.4", key));
         assert(!decimal.fromStringImpl("3.4.", key));
-        assert(!decimal.fromStringImpl("4.", key));
+        assert(decimal.fromStringImpl("4.", key));
         assert(!decimal.fromStringImpl(".", key));
-        assert(!decimal.fromStringImpl("0.", key));
+        assert(decimal.fromStringImpl("0.", key));
         assert(!decimal.fromStringImpl("00", key));
         assert(!decimal.fromStringImpl("0d", key));
     }
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index a2984b36..6dddbb9f 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1985,6 +1985,8 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         else
         if (d == '.' - '0')
         {
+            if (str.length == 0)
+                return false;
             goto D;
         }
         else
@@ -2058,13 +2060,12 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                 D:
                 case DecimalExponentKey.dot:
                     key = cast(DecimalExponentKey)d;
-                    if (_expect(!dot, true))
-                    {
-                        dot = true;
-                        if (str.length)
-                            continue;
-                    }
-                    break;
+                    if (_expect(dot, false))
+                        break;
+                    dot = true;
+                    if (str.length == 0)
+                        goto L;
+                    continue;
                 case DecimalExponentKey.e:
                 case DecimalExponentKey.d:
                 case DecimalExponentKey.E:
diff --git a/source/mir/parse.d b/source/mir/parse.d
index cc03a04c..50cd3f6f 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -21,6 +21,8 @@ unittest
     import mir.small_string;
     alias S = SmallString!32;
     assert(S("123.0").to!double == 123);
+    assert(S("123.").to!double == 123.);
+    assert(S(".123").to!double == .123);
     assert(S("123").to!(immutable int) == 123);
 }
 

From 2fde16c1abe6e2dab11bcce17a20d606264c46e5 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Fri, 26 Mar 2021 23:43:51 +0700
Subject: [PATCH 178/573] add asKindOf (#335)

---
 source/mir/ndslice/package.d  |  1 +
 source/mir/ndslice/topology.d | 64 +++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+)

diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index bd4ab8d5..4fd18b6f 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -128,6 +128,7 @@ $(TR $(TDNW $(SUBMODULE topology) $(BR)
      $(TD
         $(SUBREF topology, alongDim)
         $(SUBREF topology, as)
+        $(SUBREF topology, asKindOf)
         $(SUBREF topology, assumeCanonical)
         $(SUBREF topology, assumeContiguous)
         $(SUBREF topology, assumeHypercube)
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 6d36c3a0..b44c5486 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -43,6 +43,7 @@ $(T2 evertPack, Reverses dimension packs.)
 $(BOOKTABLE $(H2 SliceKind Selectors),
 $(TR $(TH Function Name) $(TH Description))
 
+$(T2 asKindOf, Converts a slice to a user provied kind $(SUBREF slice, SliceKind).)
 $(T2 universal, Converts a slice to universal $(SUBREF slice, SliceKind).)
 $(T2 canonical, Converts a slice to canonical $(SUBREF slice, SliceKind).)
 $(T2 assumeCanonical, Converts a slice to canonical $(SUBREF slice, SliceKind). Does only `assert` checks.)
@@ -129,6 +130,69 @@ version (D_Exceptions) private immutable choppedException = new Exception(choppe
 
 @optmath:
 
+/++
+Converts a slice to user provided kind.
+
+Contiguous slices can be converted to any kind.
+Canonical slices can't be converted to contiguous slices.
+Universal slices can be converted only to the same kind.
+
+See_also:
+    $(LREF canonical),
+    $(LREF universal),
+    $(LREF assumeCanonical),
+    $(LREF assumeContiguous).
++/
+template asKindOf(SliceKind kind)
+{
+    static if (kind == Contiguous)
+    {
+        auto asKindOf(Iterator, size_t N, Labels...)(Slice!(Iterator, N, Contiguous, Labels) slice)
+        {
+            return slice;
+        }
+    }
+    else
+    static if (kind == Canonical)
+    {
+        alias asKindOf = canonical;
+    }
+    else
+    {
+        alias asKindOf = universal;
+    }
+}
+
+/// Universal
+@safe pure nothrow
+version(mir_test) unittest
+{
+    auto slice = iota(2, 3).asKindOf!Universal;
+    assert(slice == [[0, 1, 2], [3, 4, 5]]);
+    assert(slice._lengths == [2, 3]);
+    assert(slice._strides == [3, 1]);
+}
+
+/// Canonical
+@safe pure nothrow
+version(mir_test) unittest
+{
+    auto slice = iota(2, 3).asKindOf!Canonical;
+    assert(slice == [[0, 1, 2], [3, 4, 5]]);
+    assert(slice._lengths == [2, 3]);
+    assert(slice._strides == [3]);
+}
+
+/// Contiguous
+@safe pure nothrow
+version(mir_test) unittest
+{
+    auto slice = iota(2, 3).asKindOf!Contiguous;
+    assert(slice == [[0, 1, 2], [3, 4, 5]]);
+    assert(slice._lengths == [2, 3]);
+    assert(slice._strides == []);
+}
+
 /++
 Converts a slice to universal kind.
 

From 69bac470a53ed573933068e70e3f33a29046c326 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 27 Mar 2021 12:28:10 +0700
Subject: [PATCH 179/573] fixup

---
 source/mir/ndslice/allocation.d | 4 ++--
 source/mir/ndslice/slice.d      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/ndslice/allocation.d b/source/mir/ndslice/allocation.d
index 5158066f..899f68dc 100644
--- a/source/mir/ndslice/allocation.d
+++ b/source/mir/ndslice/allocation.d
@@ -690,12 +690,12 @@ auto ndarray(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice
     import  mir.array.allocation : array;
     static if (slice.N == 1)
     {
-        return array(slice);
+        return slice.array;
     }
     else
     {
         import mir.ndslice.topology: ipack, map;
-        return array(slice.ipack!1.map!(a => .ndarray(a)));
+        return slice.ipack!1.map!(.ndarray).array;
     }
 }
 
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 86cf44a6..83b41719 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -693,7 +693,7 @@ struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_
     alias DeepElement = typeof(Iterator.init[size_t.init]);
 
     ///
-    alias serdeKeysProxy = DeepElementType;
+    alias serdeKeysProxy = DeepElement;
 
     /// Label Iterators types
     alias Labels = Labels_;

From 8035a7dcc0b5e3a5a1ae23fc6425388af242d3cd Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Tue, 30 Mar 2021 14:00:03 +0700
Subject: [PATCH 180/573] fix NaN INF support, add opCast to integral types
 (#337)

---
 source/mir/bignum/decimal.d        |  28 ++++---
 source/mir/bignum/low_level_view.d | 126 ++++++++++++++++++++++++++++-
 2 files changed, 139 insertions(+), 15 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index f245dd2a..aee3ffb0 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -166,18 +166,24 @@ struct Decimal(size_t maxSize64)
         assert(key == DecimalExponentKey.E);
         assert(cast(double) decimal.view == 0);
 
-        assert(decimal.fromStringImpl("-nan", key));
-        assert(decimal.coefficient.length > 0);
-        assert(decimal.exponent == decimal.exponent.max);
-        assert(decimal.coefficient.sign);
-        assert(key == DecimalExponentKey.nan);
-        assert(cast(double) decimal.view != cast(double) decimal.view);
+        foreach (str; ["-nan", "-NaN", "-NAN"])
+        {
+            assert(decimal.fromStringImpl(str, key));
+            assert(decimal.coefficient.length > 0);
+            assert(decimal.exponent == decimal.exponent.max);
+            assert(decimal.coefficient.sign);
+            assert(key == DecimalExponentKey.nan);
+            assert(cast(double) decimal.view != cast(double) decimal.view);
+        }
 
-        assert(decimal.fromStringImpl("inf", key));
-        assert(decimal.coefficient.length == 0);
-        assert(decimal.exponent == decimal.exponent.max);
-        assert(key == DecimalExponentKey.infinity);
-        assert(cast(double) decimal.view == double.infinity);
+        foreach (str; ["inf", "Inf", "INF"])
+        {
+            assert(decimal.fromStringImpl(str, key));
+            assert(decimal.coefficient.length == 0);
+            assert(decimal.exponent == decimal.exponent.max);
+            assert(key == DecimalExponentKey.infinity);
+            assert(cast(double) decimal.view == double.infinity);
+        }
 
         assert(decimal.fromStringImpl("-inf", key));
         assert(decimal.coefficient.length == 0);
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 6dddbb9f..59d0c600 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -341,6 +341,71 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
     }
 
+
+    ///
+    T opCast(T, bool nonZero = false)() const
+        if (isIntegral!T && isUnsigned!T && isMutable!T)
+    {
+        auto work = lightConst;
+        static if (!nonZero)
+        {
+            if (coefficients.length == 0)
+            {
+                return 0;
+            }
+        }
+        static if (T.sizeof <= W.sizeof)
+        {
+            return cast(T) work.leastSignificant;
+        }
+        else
+        {
+            T ret;
+            do
+            {
+                ret <<= W.sizeof * 8;
+                ret |= work.mostSignificant;
+                work.popMostSignificant;
+            }
+            while(work.coefficients.length);
+            return ret;
+        }
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(cast(ulong) view == 0x14a1de7022b0029d);
+        assert(cast(uint) view == 0x22b0029d);
+        assert(cast(ubyte) view == 0x9d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigUIntView!ushort.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(cast(ulong) view == 0x14a1de7022b0029d);
+        assert(cast(uint) view == 0x22b0029d);
+        assert(cast(ubyte) view == 0x9d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigUIntView!uint.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(cast(ulong) view == 0x14a1de7022b0029d);
+        assert(cast(uint) view == 0x22b0029d);
+        assert(cast(ubyte) view == 0x9d);
+    }
+
     static if (endian == TargetEndian)
     ///
     @trusted pure nothrow @nogc
@@ -1346,7 +1411,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     ///
     T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
-        if (isFloatingPoint!T)
+        if (isFloatingPoint!T && isMutable!T)
     {
         auto ret = this.unsigned.opCast!(T, wordNormalized, nonZero);
         if (sign)
@@ -1354,7 +1419,6 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         return ret;
     }
 
-
     static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
     ///
     version(mir_bignum_test)
@@ -1364,6 +1428,48 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
+
+    ///
+    T opCast(T, bool nonZero = false)() const
+        if (is(T == long) || is(T == int))
+    {
+        auto ret = this.unsigned.opCast!(Unsigned!T, nonZero);
+        if (sign)
+            ret = -ret;
+        return ret;
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ushort.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ubyte.fromHexString("-afbbfae3cd0aff2714a1de7022b0021d");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
     /++
     +/
     T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
@@ -1428,6 +1534,17 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         return this.sign == rhs.sign && this.unsigned == rhs.unsigned;
     }
 
+    /++
+    Returns: true if the integer and equals to `rhs`.
+    +/
+    bool opEquals(long rhs)
+        @safe pure nothrow @nogc const
+    {
+        bool sign = rhs < 0;
+        ulong urhs = sign ? -rhs : rhs;
+        return sign == this.sign && unsigned == urhs;
+    }
+
     /++
     +/
     BigIntView topMostSignificantPart(size_t length)
@@ -1994,13 +2111,14 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
             exponent = exponent.max;
             if (str.length == 2)
             {
-                if ((d == 'i' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"nf"))
+                auto stail = cast(C[2])str[0 .. 2];
+                if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
                 {
                     coefficient = coefficient.init;
                     key = DecimalExponentKey.infinity;
                     return true;
                 }
-                if ((d == 'n' - '0') & (cast(C[2])str[0 .. 2] == cast(C[2])"an"))
+                if (d == 'n' - '0' && stail== cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
                 {
                     coefficient.leastSignificant = 1;
                     coefficient = coefficient.topLeastSignificantPart(1);

From 3a7849b344325c6badfb5f159a4cd5368449551c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 30 Mar 2021 14:01:43 +0700
Subject: [PATCH 181/573] fixup docs

---
 source/mir/ndslice/topology.d | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index b44c5486..f00b579d 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -167,6 +167,7 @@ template asKindOf(SliceKind kind)
 @safe pure nothrow
 version(mir_test) unittest
 {
+    import mir.ndslice.slice: Universal;
     auto slice = iota(2, 3).asKindOf!Universal;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
     assert(slice._lengths == [2, 3]);
@@ -177,6 +178,7 @@ version(mir_test) unittest
 @safe pure nothrow
 version(mir_test) unittest
 {
+    import mir.ndslice.slice: Canonical;
     auto slice = iota(2, 3).asKindOf!Canonical;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
     assert(slice._lengths == [2, 3]);
@@ -187,6 +189,7 @@ version(mir_test) unittest
 @safe pure nothrow
 version(mir_test) unittest
 {
+    import mir.ndslice.slice: Contiguous;
     auto slice = iota(2, 3).asKindOf!Contiguous;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
     assert(slice._lengths == [2, 3]);

From 5f89eebc2c14297a920f881802b8ac03e964cbfc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 30 Mar 2021 14:30:56 +0700
Subject: [PATCH 182/573] fixup slice

---
 source/mir/ndslice/slice.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 83b41719..385f5331 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -749,7 +749,7 @@ package(mir):
         ///
         public alias _Structure = AliasSeq!(size_t[N], ptrdiff_t[S]);
         ///
-        _Structure _structure;
+        public _Structure _structure;
         ///
         public alias _lengths = _structure[0];
         ///
@@ -760,7 +760,7 @@ package(mir):
         ///
         public alias _Structure = AliasSeq!(size_t[N]);
         ///
-        _Structure _structure;
+        public _Structure _structure;
         ///
         public alias _lengths = _structure[0];
         ///

From fe4281015675a8899a4b752344814cc2dfd0f87e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 2 Apr 2021 09:48:14 +0700
Subject: [PATCH 183/573] Optimize Decimal and ScopedBuffer usage & fix big
 integer comparison

---
 include/mir/rcptr.h                          |   2 +-
 source/mir/appender.d                        |  17 +-
 source/mir/array/allocation.d                |  52 ++--
 source/mir/bignum/decimal.d                  |  22 +-
 source/mir/bignum/integer.d                  |  21 +-
 source/mir/bignum/internal/ryu/generic_128.d |   2 +-
 source/mir/bignum/low_level_view.d           | 254 +++++++++++--------
 source/mir/format.d                          |  36 ++-
 source/mir/math/sum.d                        |   2 +-
 source/mir/parse.d                           |  17 +-
 source/mir/rc/array.d                        |  49 ++--
 source/mir/rc/context.d                      |   6 +-
 12 files changed, 306 insertions(+), 174 deletions(-)

diff --git a/include/mir/rcptr.h b/include/mir/rcptr.h
index 780fc25e..9959eceb 100644
--- a/include/mir/rcptr.h
+++ b/include/mir/rcptr.h
@@ -33,7 +33,7 @@ extern "C"
         const mir_type_info* typeInfo,
         size_t length,
         const void* payload = nullptr,
-        bool initialise = true,
+        bool initialize = true,
         bool deallocate = true
     );
 }
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 9aa35a8b..01235f0a 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -202,6 +202,13 @@ struct ScopedBuffer(T, size_t bytes = 4096)
         _buffer = null;
     }
 
+    ///
+    void initialize() @system scope nothrow @nogc
+    {
+        _currentLength = 0;
+        _buffer = null;
+    }
+
     ///
     inout(T)[] data() inout @property @safe scope
     {
@@ -223,10 +230,11 @@ struct ScopedBuffer(T, size_t bytes = 4096)
 }
 
 ///
-@safe pure nothrow @nogc
+@trusted pure nothrow @nogc
 version (mir_test) unittest
 {
-    ScopedBuffer!char buf;
+    ScopedBuffer!char buf = void;
+    buf.initialize;
     buf.put('c');
     buf.put("str");
     assert(buf.data == "cstr");
@@ -236,10 +244,11 @@ version (mir_test) unittest
 }
 
 /// immutable
-@safe pure nothrow @nogc
+@trusted pure nothrow @nogc
 version (mir_test) unittest
 {
-    ScopedBuffer!(immutable char) buf;
+    ScopedBuffer!(immutable char) buf = void;
+    buf.initialize;
     buf.put('c');
     buf.put("str");
     assert(buf.data == "cstr");
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index 4c1cb452..4d418fd4 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -115,28 +115,50 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticAr
     else
     {
         import mir.appender: ScopedBuffer;
-        ScopedBuffer!(Unqual!E) a;
-        static if (isInputRange!Range)
-            for (; !r.empty; r.popFront)
-                a.put(r.front);
-        else
-        static if (isPointer!Range)
-        {
-            foreach (e; *r)
-                a.put(forward!e);
-        }
-        else
+
+        if (false)
         {
-            foreach (e; r)
-                a.put(forward!e);
+            ScopedBuffer!(Unqual!E) a;
+            static if (isInputRange!Range)
+                for (; !r.empty; r.popFront)
+                    a.put(r.front);
+            else
+            static if (isPointer!Range)
+            {
+                foreach (e; *r)
+                    a.put(forward!e);
+            }
+            else
+            {
+                foreach (e; r)
+                    a.put(forward!e);
+            }
         }
 
-        return (() @trusted {
+        return () @trusted {
+            ScopedBuffer!(Unqual!E) a = void;
+            a.initialize;
+
+            static if (isInputRange!Range)
+                for (; !r.empty; r.popFront)
+                    a.put(r.front);
+            else
+            static if (isPointer!Range)
+            {
+                foreach (e; *r)
+                    a.put(forward!e);
+            }
+            else
+            {
+                foreach (e; r)
+                    a.put(forward!e);
+            }
+
             import std.array: uninitializedArray;
             auto ret = uninitializedArray!(Unqual!E[])(a.length);
             a.moveDataAndEmplaceTo(ret);
             return ret;
-        })();
+        } ();
     }
 }
 
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index aee3ffb0..34d8b0b0 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -119,13 +119,19 @@ struct Decimal(size_t maxSize64)
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients.
     +/
-    bool fromStringImpl(C)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDExponent = true, bool allowStartingPlus = true)(scope const(C)[] str, out DecimalExponentKey key)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
+        version(LDC)
+        {
+            static if ((allowSpecialValues && allowDExponent && allowStartingPlus) == false)
+                pragma(inline, true);
+        }
+
         import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
         auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
-        auto ret = work.fromStringImpl(str, key);
+        auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus)(str, key);
         coefficient.length = cast(uint) work.coefficient.coefficients.length;
         coefficient.sign = work.sign;
         exponent = work.exponent;
@@ -500,3 +506,15 @@ bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize
 {
     return decimal.fromStringImpl(str, key);
 }
+
+
+version(mir_bignum_test)
+@safe pure @nogc unittest
+{
+    Decimal!4 i = "-0";
+    
+    assert(i.view.coefficient.coefficients.length == 0);
+    assert(i.coefficient.view.unsigned.coefficients.length == 0);
+    assert(i.coefficient.view == 0L);
+    assert(cast(long) i.coefficient == 0);
+}
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index d3247860..c04109a6 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -38,6 +38,7 @@ struct BigInt(size_t maxSize64)
     this(size_t N)(size_t[N] data)
         if (N <= this.data.length)
     {
+        sign = false;
         version(LittleEndian)
             this.data[0 .. N] = data;
         else
@@ -49,6 +50,7 @@ struct BigInt(size_t maxSize64)
     ///
     this(ulong data)
     {
+        sign = false;
         static if (size_t.sizeof == ulong.sizeof)
         {
             length = 1;
@@ -65,8 +67,8 @@ struct BigInt(size_t maxSize64)
     }
 
     ///
-    this(C)(scope const(C)[] str) @safe pure @nogc
-        if (isSomeChar!C)
+    this()(scope const(char)[] str) @safe pure @nogc
+        // if (isSomeChar!C)
     {
         if (fromStringImpl(str))
             return;
@@ -505,6 +507,13 @@ struct BigInt(size_t maxSize64)
         return view.opCast!(T, wordNormalized, nonZero);
     }
 
+    ///
+    T opCast(T, bool nonZero = false)() const
+        if (is(T == long) || is(T == int))
+    {
+        return this.view.opCast!(T, nonZero);
+    }
+
     /++
     Returns: overflow flag
     +/
@@ -647,3 +656,11 @@ unittest
     assert(!c.copyFrom(bView.topLeastSignificantPart(bView.unsigned.coefficients.length - 1)));
     assert(c == b);
 }
+
+version(mir_bignum_test)
+@safe pure @nogc unittest
+{
+    BigInt!4 i = "-0";
+    assert(i.view.coefficients.length == 0);
+    assert(cast(long) i == 0);
+}
diff --git a/source/mir/bignum/internal/ryu/generic_128.d b/source/mir/bignum/internal/ryu/generic_128.d
index 2903e0a3..db870bfd 100644
--- a/source/mir/bignum/internal/ryu/generic_128.d
+++ b/source/mir/bignum/internal/ryu/generic_128.d
@@ -499,7 +499,7 @@ Decimal!(T.mant_dig < 64 ? 1 : 2) genericBinaryToDecimal(T)(const T x)
         }
 
         import mir.bignum.integer: BigInt;
-        fd.coefficient = BigInt!wordCount(output);
+        fd.coefficient.__ctor(output);
         fd.exponent = exp;
     }
     fd.coefficient.sign = x.signbit;
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 59d0c600..bdd1dbd6 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -11,6 +11,9 @@ module mir.bignum.low_level_view;
 import mir.checkedint;
 import std.traits;
 
+version(LDC) import ldc.attributes: optStrategy;
+else struct optStrategy { string opt; }
+
 private alias cop(string op : "-") = subu;
 private alias cop(string op : "+") = addu;
 private enum inverseSign(string op) = op == "+" ? "-" : "+";
@@ -432,7 +435,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         import mir.algorithm.iteration: cmp;
         auto l = this.lightConst.normalized;
         auto r = rhs.lightConst.normalized;
-        if (auto d = l.coefficients.length - r.coefficients.length)
+        if (sizediff_t d = l.coefficients.length - r.coefficients.length)
             return d;
         return cmp(l.mostSignificantFirst, r.mostSignificantFirst);
     }
@@ -658,8 +661,6 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
             return false;
 
         leastSignificant = 0;
-        auto work = topLeastSignificantPart(1);
-
         uint d = str[0] - '0';
         str = str[1 .. $];
 
@@ -669,19 +670,20 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         if (d == 0)
         {
             if (str.length == 0)
+            {
+                coefficients = null;
                 return true;
+            }
             if (str[0] == '0')
                 return false;
-            goto S;
-        }
-        else
-        if (d < 10)
-        {
-            goto S;
         }
         else
+        if (d >= 10)
             return false;
 
+        auto work = topLeastSignificantPart(1);
+        goto S;
+
         for(;;)
         {
             enum mp10 = W(10) ^^ MaxWordPow10!W;
@@ -1521,9 +1523,10 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         import mir.algorithm.iteration: cmp;
         if (auto s = rhs.sign - this.sign)
         {
-            return s;
+            if (this.unsigned.coefficients.length && rhs.unsigned.coefficients.length)
+                return s;
         }
-        sizediff_t d = this.unsigned.opCmp(rhs.unsigned);
+        auto d = this.unsigned.opCmp(rhs.unsigned);
         return sign ? -d : d;
     }
 
@@ -1531,7 +1534,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     bool opEquals(BigIntView!(const W, endian) rhs)
         const @safe pure nothrow @nogc
     {
-        return this.sign == rhs.sign && this.unsigned == rhs.unsigned;
+        return (this.sign == rhs.sign || unsigned.coefficients.length == 0) && this.unsigned == rhs.unsigned;
     }
 
     /++
@@ -1540,6 +1543,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     bool opEquals(long rhs)
         @safe pure nothrow @nogc const
     {
+        if (rhs == 0 && unsigned.coefficients.length == 0)
+            return true;
         bool sign = rhs < 0;
         ulong urhs = sign ? -rhs : rhs;
         return sign == this.sign && unsigned == urhs;
@@ -2011,6 +2016,10 @@ enum DecimalExponentKey
     ///
     none = 0,
     ///
+    infinity = 1,
+    ///
+    nan = 2,
+    ///
     dot = '.' - '0',
     ///
     d = 'd' - '0',
@@ -2020,10 +2029,6 @@ enum DecimalExponentKey
     D = 'D' - '0',
     ///
     E = 'E' - '0',
-    ///
-    infinity,
-    ///
-    nan,
 }
 
 /++
@@ -2044,13 +2049,18 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
     Precondition: non-empty coefficients
     Note: doesn't support signs.
     +/
-    bool fromStringImpl(C)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDExponent = true, bool allowStartingPlus = true)(scope const(C)[] str, out DecimalExponentKey key)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
-        pragma(inline, false);
         import mir.utility: _expect;
 
+        version(LDC)
+        {
+            static if ((allowSpecialValues && allowDExponent && allowStartingPlus) == false)
+                pragma(inline, true);
+        }
+
         assert(coefficient.coefficients.length);
 
         coefficient.leastSignificant = 0;
@@ -2067,11 +2077,14 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                 return false;
         }
         else
-        if (_expect(str[0] == '+', false))
+        static if (allowStartingPlus)
         {
-            str = str[1 .. $];
-            if (_expect(str.length == 0, false))
-                return false;
+            if (_expect(str[0] == '+', false))
+            {
+                str = str[1 .. $];
+                if (_expect(str.length == 0, false))
+                    return false;
+            }
         }
 
         uint d = str[0] - '0';
@@ -2109,21 +2122,24 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         else
         {
             exponent = exponent.max;
-            if (str.length == 2)
+            static if (allowSpecialValues)
             {
-                auto stail = cast(C[2])str[0 .. 2];
-                if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
+                if (str.length == 2)
                 {
-                    coefficient = coefficient.init;
-                    key = DecimalExponentKey.infinity;
-                    return true;
-                }
-                if (d == 'n' - '0' && stail== cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
-                {
-                    coefficient.leastSignificant = 1;
-                    coefficient = coefficient.topLeastSignificantPart(1);
-                    key = DecimalExponentKey.nan;
-                    return true;
+                    auto stail = cast(C[2])str[0 .. 2];
+                    if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
+                    {
+                        coefficient = coefficient.init;
+                        key = DecimalExponentKey.infinity;
+                        return true;
+                    }
+                    if (d == 'n' - '0' && stail== cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
+                    {
+                        coefficient.leastSignificant = 1;
+                        coefficient = coefficient.topLeastSignificantPart(1);
+                        key = DecimalExponentKey.nan;
+                        return true;
+                    }
                 }
             }
             return false;
@@ -2184,10 +2200,14 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     if (str.length == 0)
                         goto L;
                     continue;
+                static if (allowDExponent)
+                {
+                    case DecimalExponentKey.d:
+                    case DecimalExponentKey.D:
+                        goto case DecimalExponentKey.e;
+                }
                 case DecimalExponentKey.e:
-                case DecimalExponentKey.d:
                 case DecimalExponentKey.E:
-                case DecimalExponentKey.D:
                     key = cast(DecimalExponentKey)d;
                     hasExponent = true;
                     import mir.parse: parse;
@@ -2223,16 +2243,21 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 
     /++
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
-    Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
+    Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.
+    +/
     T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-        import mir.bignum.integer: BigInt;
+        version(LDC)
+        {
+            static if (wordNormalized)
+                pragma(inline, true);
+        }
+
         import mir.bignum.fixed: UInt;
         import mir.bignum.fp: Fp, extendedMul;
         import mir.bignum.internal.dec2flt_table;
         import mir.math.common: floor;
-        import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
         import mir.utility: _expect;
 
         auto coeff = coefficient.lightConst;
@@ -2282,7 +2307,8 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                         ret =  c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
                         goto R;
                     }
-                    goto AlgoR;
+                    ret = algoR!T(ret, coeff, cast(int) exponent);
+                    goto R;
                 }
                 ret = expSign ? 0 : T.infinity;
                 goto R;
@@ -2362,85 +2388,97 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                         goto R;
                     }
                 }
+                ret = algoR!T(ret, coeff, cast(int) exponent);
+                goto R;
             }
         }
+    R:
+        if (sign)
+            ret = -ret;
+        return ret;
+    }
+}
+
+@optStrategy("minsize")
+private T algoR(T, W, WordEndian endian)(T ret, BigUIntView!(const W, endian) coeff, int exponent)
+{
+    pragma(inline, false);
 
-    AlgoR:
-        if (exponent >= 0)
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.integer: BigInt;
+    import mir.math.common: floor;
+    import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
+    import mir.utility: _expect;
+
+    BigInt!256 x = void, y = void; // max value is 2^(2^14)-1
+    if (exponent >= 0)
+    {
+        if (!x.copyFrom(coeff) && !x.mulPow5(exponent)) // if no overflow
+            ret = ldexp(cast(T) x, exponent);
+    }
+    else do
+    {
+        if (ret < ret.min_normal)
+            break;
+        int k;
+        auto m0 = frexp(ret, k);
+        k -= T.mant_dig;
+        static if (T.mant_dig <= 64)
         {
-            BigInt!256 x; // max value is 2^(2^14)-1
-            if (x.copyFrom(coeff) || x.mulPow5(exponent)) // if overflow
-                goto R;
-            ret = ldexp(cast(T) x, cast(int) exponent);
+            enum p2 = T(2) ^^ T.mant_dig;
+            auto m = UInt!64(cast(ulong) (m0 * p2));
         }
-        else do
+        else
         {
-            if (ret < ret.min_normal)
-                break;
-            int k;
-            auto m0 = frexp(ret, k);
-            k -= T.mant_dig;
-            static if (T.mant_dig <= 64)
-            {
-                enum p2 = T(2) ^^ T.mant_dig;
-                auto m = UInt!64(cast(ulong) (m0 * p2));
-            }
-            else
-            {
-                enum p2h = T(2) ^^ (T.mant_dig - 64);
-                enum p2l = T(2) ^^ 64;
-                m0 *= p2h;
-                auto mhf = floor(m0);
-                auto mh = cast(ulong) mhf;
-                m0 -= mhf;
-                m0 *= p2l;
-                auto ml = cast(ulong) m0;
-                auto m = UInt!128(mh);
-                m <<= 64;
-                m |= UInt!128(ml);
-            }
-            auto mtz = m.cttz;
-            if (mtz != m.sizeof * 8)
-            {
-                m >>= mtz;
-                k += mtz;
-            }
+            enum p2h = T(2) ^^ (T.mant_dig - 64);
+            enum p2l = T(2) ^^ 64;
+            m0 *= p2h;
+            auto mhf = floor(m0);
+            auto mh = cast(ulong) mhf;
+            m0 -= mhf;
+            m0 *= p2l;
+            auto ml = cast(ulong) m0;
+            auto m = UInt!128(mh);
+            m <<= 64;
+            m |= UInt!128(ml);
+        }
+        auto mtz = m.cttz;
+        if (mtz != m.sizeof * 8)
+        {
+            m >>= mtz;
+            k += mtz;
+        }
 
-            BigInt!256 x; // max value is 2^(2^14)-1
-            if (x.copyFrom(coeff)) // if overflow
-                break;
-            auto y = BigInt!256(m); // max value is 2^(2^14)-1
-            y.mulPow5(-exponent);
-            auto shift = k - cast(int)exponent;
-            (shift >= 0  ? y : x) <<= shift >= 0 ? shift : -shift;
-            x -= y;
-            if (x.length == 0)
+        if (x.copyFrom(coeff)) // if overflow
+            break;
+        y.__ctor(m);
+        y.mulPow5(-exponent);
+        auto shift = k - exponent;
+        (shift >= 0  ? y : x) <<= shift >= 0 ? shift : -shift;
+        x -= y;
+        if (x.length == 0)
+            break;
+        x <<= 1;
+        x *= m;
+        auto cond = mtz == T.mant_dig - 1 && x.sign;
+        auto cmp = x.view.unsigned.opCmp(y.view.unsigned);
+        if (cmp < 0)
+        {
+            if (!cond)
                 break;
             x <<= 1;
-            x *= m;
-            auto cond = mtz == T.mant_dig - 1 && x.sign;
-            auto cmp = x.view.unsigned.opCmp(y.view.unsigned);
-            if (cmp < 0)
-            {
-                if (!cond)
-                    break;
-                x <<= 1;
-                if (x.view.unsigned <= y.view.unsigned)
-                    break;
-            }
-            else
-            if (!cmp && !cond && !mtz)
-                break;
-            ret = x.sign ? nextDown(ret) : nextUp(ret);
-            if (ret == 0)
+            if (x.view.unsigned <= y.view.unsigned)
                 break;
         }
-        while (T.mant_dig >= 64 && exponent < -512);
-    R:
-        if (sign)
-            ret = -ret;
-        return ret;
+        else
+        if (!cmp && !cond && !mtz)
+            break;
+        ret = x.sign ? nextDown(ret) : nextUp(ret);
+        if (ret == 0)
+            break;
     }
+    while (T.mant_dig >= 64 && exponent < -512);
+    return ret;
 }
 
 ///
diff --git a/source/mir/format.d b/source/mir/format.d
index c003787c..1c301966 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -71,16 +71,32 @@ string text(string separator = "", A...)(auto ref A args)
     else
     {
         import mir.appender: ScopedBuffer;
-        ScopedBuffer!char buffer;
-        foreach (i, ref arg; args)
+        if (false)
         {
-            buffer.print(arg);
-            static if (separator.length && i + 1 < args.length)
+            ScopedBuffer!char buffer;
+            foreach (i, ref arg; args)
             {
-                buffer.printStaticString!char(separator);
+                buffer.print(arg);
+                static if (separator.length && i + 1 < args.length)
+                {
+                    buffer.printStaticString!char(separator);
+                }
             }
+            return buffer.data.idup;
         }
-        return buffer.data.idup;
+        return () @trusted  {
+            ScopedBuffer!char buffer = void;
+            buffer.initialize;
+            foreach (i, ref arg; args)
+            {
+                buffer.print(arg);
+                static if (separator.length && i + 1 < args.length)
+                {
+                    buffer.printStaticString!char(separator);
+                }
+            }
+            return buffer.data.idup;
+        } ();
     }
 }
 
@@ -106,7 +122,7 @@ struct _stringBuf(C)
     import mir.appender: ScopedBuffer;
 
     ///
-    ScopedBuffer!C buffer;
+    ScopedBuffer!(C, 256) buffer;
 
     ///
     alias buffer this;
@@ -376,8 +392,8 @@ ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
 version (mir_test) unittest
 {
  
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
+    import mir.format: stringBuf;
+    stringBuf w;
     assert(w.printEscaped("Hi \f\t\b \\\r\n" ~ `"@nogc"`).data == "Hi \f\t\b \\\\\\r\\n\\\"@nogc\\\"", w.data);
     w.reset;
     assert(w.printEscaped("\x03").data == `\x03`, w.data);
@@ -924,8 +940,6 @@ version (mir_test) unittest
     static class F { const(char)[] toString()() const return { return "f"; } }
     static class G { const(char)[] s = "g"; alias s this; }
 
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
     assert(stringBuf() << new A() << new S() << new D() << new F() << new G() << getData == "asdfg");
 }
 
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 3a4c3c61..c903dfd6 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -460,7 +460,7 @@ struct Summator(T, Summation summation)
         import mir.math.ieee: signbit;
     private:
         enum F M = (cast(F)(2)) ^^ (T.max_exp - 1);
-        ScopedBuffer!(F, 16) partials;
+        ScopedBuffer!(F, 8) partials;
         //sum for NaN and infinity.
         F s = summationInitValue!F;
         //Overflow Degree. Count of 2^^F.max_exp minus count of -(2^^F.max_exp)
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 50cd3f6f..71770a73 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -129,7 +129,7 @@ bool fromString(T, C)(scope const(C)[] str, ref T value)
         import mir.bignum.decimal: Decimal, DecimalExponentKey;
         import mir.utility: _expect;
 
-        Decimal!256 decimal;
+        Decimal!256 decimal = void;
         DecimalExponentKey key;
         auto ret = decimal.fromStringImpl(str, key);
         if (_expect(ret, true))
@@ -198,18 +198,18 @@ bool parse(T, Range)(scope ref Range r, scope ref T value)
 }
 
 /// ditto
-pragma(inline, false)
 bool parse(T, Range)(scope ref Range r, scope ref T value)
     if (is(T == int) && isInputRange!Range && !__traits(isUnsigned, T))
 {
+    version(LDC) pragma(inline, true);
     return parseSignedImpl!(int, Range)(r, value);
 }
 
 /// ditto
-pragma(inline, false)
 bool parse(T, Range)(scope ref Range r, scope ref T value)
     if (is(T == long) && isInputRange!Range && !__traits(isUnsigned, T))
 {
+    version(LDC) pragma(inline, true);
     return parseSignedImpl!(long, Range)(r, value);
 }
 
@@ -224,18 +224,18 @@ bool parse(T, Range)(scope ref Range r, scope ref T value)
 }
 
 /// ditto
-pragma(inline, false)
 bool parse(T, Range)(scope ref Range r, scope ref T value)
     if (is(T == uint) && isInputRange!Range && __traits(isUnsigned, T))
 {
+    version(LDC) pragma(inline, true);
     return parseUnsignedImpl!(uint, Range)(r, value);
 }
 
 /// ditto
-pragma(inline, false)
 bool parse(T, Range)(scope ref Range r, scope ref T value)
     if (is(T == ulong) && isInputRange!Range && __traits(isUnsigned, T))
 {
+    version(LDC) pragma(inline, true);
     return parseUnsignedImpl!(ulong, Range)(r, value);
 }
 
@@ -289,14 +289,10 @@ version (mir_test) unittest
     }
 }
 
-alias r1 = parseUnsignedImpl!(uint, string);
-alias r2 = parseUnsignedImpl!(ulong, string);
-alias r3 = parseSignedImpl!(int, string);
-alias r4 = parseSignedImpl!(long, string);
-
 private bool parseUnsignedImpl(T, Range)(scope ref Range r, scope ref T value)
     if(__traits(isUnsigned, T))
 {
+    version(LDC) pragma(inline, true);
     import core.checkedint: addu, mulu;
 
     bool sign;
@@ -342,6 +338,7 @@ R:  return false;
 private bool parseSignedImpl(T, Range)(scope ref Range r, scope ref T value)
     if(!__traits(isUnsigned, T))
 {
+    version(LDC) pragma(inline, true);
     import core.checkedint: negs;
     import std.traits: Unsigned;
 
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index da1bc9ba..8d698c2f 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -399,23 +399,40 @@ template rcarray(T)
         {
             import mir.appender: ScopedBuffer;
             import mir.conv: emplaceRef;
-            ScopedBuffer!T a;
-            static if (isInputRange!Range)
-                for (; !range.empty; range.popFront)
-                    a.put(range.front);
-            else
-            static if (isPointer!Range)
-                foreach (e; *range)
-                    a.put(e);
-            else
-                foreach (e; range)
-                    a.put(e);
-            scope values = a.data;
-            auto ret = RCArray!T(values.length, false);
-            ()@trusted {
+            if (false)
+            {
+                ScopedBuffer!T a;
+                static if (isInputRange!Range)
+                    for (; !range.empty; range.popFront)
+                        a.put(range.front);
+                else
+                static if (isPointer!Range)
+                    foreach (e; *range)
+                        a.put(e);
+                else
+                    foreach (e; range)
+                        a.put(e);
+                scope values = a.data;
+                auto ret = RCArray!T(values.length, false);
+            }
+            return ()@trusted {
+                ScopedBuffer!T a = void;
+                a.initialize;
+                static if (isInputRange!Range)
+                    for (; !range.empty; range.popFront)
+                        a.put(range.front);
+                else
+                static if (isPointer!Range)
+                    foreach (e; *range)
+                        a.put(e);
+                else
+                    foreach (e; range)
+                        a.put(e);
+                scope values = a.data;
+                auto ret = RCArray!T(values.length, false);
                 a.moveDataAndEmplaceTo(ret[]);
-            }();
-            return ret;
+                return ret;
+            } ();
         }
     }
 
diff --git a/source/mir/rc/context.d b/source/mir/rc/context.d
index b70abe14..74f2d7bc 100644
--- a/source/mir/rc/context.d
+++ b/source/mir/rc/context.d
@@ -48,7 +48,7 @@ Params:
 export extern(C)
 void mir_rc_decrease_counter(ref mir_rc_context context) @system nothrow @nogc pure
 {
-    pragma(inline, false);
+    pragma(inline, true);
     import core.atomic: atomicOp;
     with(context)
     {
@@ -107,7 +107,7 @@ mir_rc_context* mir_rc_create(
     ref immutable(mir_type_info) typeInfo,
     size_t length,
     scope const void* payload = null,
-    bool initialise = true,
+    bool initialize = true,
     bool deallocate = true,
     ) @system nothrow @nogc pure
 {
@@ -129,7 +129,7 @@ mir_rc_context* mir_rc_create(
         context.counter = deallocate;
         context.length = length;
 
-        if (initialise)
+        if (initialize)
         {
             auto ptr = cast(void*)(context + 1);
             if (payload)

From 9507776136e3dc9862977aa708b98ec6ac22cb72 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sat, 3 Apr 2021 16:15:44 +0700
Subject: [PATCH 184/573] optimize number parsing Decimal!1 (#338)

---
 source/mir/bignum/decimal.d        | 344 +++++++++++++++++++++++++++--
 source/mir/bignum/low_level_view.d | 109 +++++----
 2 files changed, 388 insertions(+), 65 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 34d8b0b0..83277dbb 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -70,7 +70,7 @@ struct Decimal(size_t maxSize64)
     {
         import mir.math.constant: PI;
         Decimal!2 decimal = "3.141592653589793378e-40"; // constructor
-        assert(cast(double) decimal.view == double(PI) / 1e40);
+        assert(cast(double) decimal == double(PI) / 1e40);
     }
 
     /++
@@ -119,23 +119,180 @@ struct Decimal(size_t maxSize64)
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients.
     +/
-    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDExponent = true, bool allowStartingPlus = true)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true)(scope const(C)[] str, out DecimalExponentKey key)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
+        enum optimize = size_t.sizeof == 8 && maxSize64 == 1;
         version(LDC)
         {
-            static if ((allowSpecialValues && allowDExponent && allowStartingPlus) == false)
+            static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false)
                 pragma(inline, true);
         }
+        static if (optimize)
+        {
+            import mir.utility: _expect;
+            static if (checkEmpty)
+            {
+                if (_expect(str.length == 0, false))
+                    return false;
+            }
+
+            coefficient.sign = str[0] == '-';
+            if (coefficient.sign)
+            {
+                str = str[1 .. $];
+                if (_expect(str.length == 0, false))
+                    return false;
+            }
+            else
+            static if (allowStartingPlus)
+            {
+                if (_expect(str[0] == '+', false))
+                {
+                    str = str[1 .. $];
+                    if (_expect(str.length == 0, false))
+                        return false;
+                }
+            }
+
+            uint d = str[0] - '0';
+            str = str[1 .. $];
+            exponent = 0;
 
-        import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
-        auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
-        auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus)(str, key);
-        coefficient.length = cast(uint) work.coefficient.coefficients.length;
-        coefficient.sign = work.sign;
-        exponent = work.exponent;
-        return ret;
+            ulong v;
+            uint afterDot;
+            bool dot;
+
+            if (d == 0)
+            {
+                if (str.length == 0)
+                    goto R;
+                if (str[0] == '0')
+                    return false;
+                goto S;
+            }
+
+            if (d < 10)
+            {
+                goto S;
+            }
+
+            static if (allowDotOnBounds)
+            {
+                if (d == '.' - '0')
+                {
+                    if (str.length == 0)
+                        return false;
+                    key = DecimalExponentKey.dot;
+                    dot = true;
+                    goto F;
+                }
+            }
+
+            static if (allowSpecialValues)
+            {
+                goto NI;
+            }
+            else
+            {
+                return false;
+            }
+
+            F: for(;;)
+            {
+                d = str[0] - '0';
+                str = str[1 .. $];
+
+                if (_expect(d <= 10, true))
+                {
+                    {
+                        import core.checkedint: addu, mulu;
+                        bool overflow;
+                        v = mulu(v, cast(uint)10, overflow);
+                        if (overflow)
+                            break;
+                    }
+                S:
+                    v += d;
+                    afterDot += dot;
+                    if (str.length)
+                        continue;
+                E:
+                    exponent -= afterDot;
+                R:
+                    coefficient.data[0] = v;
+                    coefficient.length = v != 0;
+                    return true;
+                }
+                switch (d)
+                {
+                    D:
+                    case DecimalExponentKey.dot:
+                        if (dot)
+                            break;
+                        key = cast(DecimalExponentKey)d;
+                        dot = true;
+                        if (str.length)
+                            continue;
+                        static if (allowDotOnBounds)
+                        {
+                            goto R;
+                        }
+                        else
+                        {
+                            break;
+                        }
+                    static if (allowDExponent)
+                    {
+                        case DecimalExponentKey.d:
+                        case DecimalExponentKey.D:
+                            goto case DecimalExponentKey.e;
+                    }
+                    case DecimalExponentKey.e:
+                    case DecimalExponentKey.E:
+                        key = cast(DecimalExponentKey)d;
+                        import mir.parse: parse;
+                        if (parse(str, exponent) && str.length == 0)
+                            goto E;
+                        break;
+                    default:
+                }
+                break;
+            }
+            return false;
+            static if (allowSpecialValues)
+            {
+            NI:
+                exponent = exponent.max;
+                if (str.length == 2)
+                {
+                    auto stail = cast(C[2])str[0 .. 2];
+                    if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
+                    {
+                        key = DecimalExponentKey.infinity;
+                        goto R;
+                    }
+                    if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
+                    {
+                        v = 1;
+                        key = DecimalExponentKey.nan;
+                        goto R;
+                    }
+                }
+                return false;
+            }
+        }
+        else
+        {
+            import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
+            auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
+            auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus)(str, key);
+            coefficient.length = cast(uint) work.coefficient.coefficients.length;
+            coefficient.sign = work.sign;
+            exponent = work.exponent;
+            return ret;
+        }
     }
 
     static if (maxSize64 == 3)
@@ -150,27 +307,27 @@ struct Decimal(size_t maxSize64)
 
         assert(decimal.fromStringImpl("1.334", key));
         assert(key == DecimalExponentKey.dot);
-        assert(cast(double) decimal.view == 1.334);
+        assert(cast(double) decimal == 1.334);
 
         assert(decimal.fromStringImpl("+0.334e-5"w, key));
         assert(key == DecimalExponentKey.e);
-        assert(cast(double) decimal.view == 0.334e-5);
+        assert(cast(double) decimal == 0.334e-5);
 
         assert(decimal.fromStringImpl("-334D-5"d, key));
         assert(key == DecimalExponentKey.D);
-        assert(cast(double) decimal.view == -334e-5);
+        assert(cast(double) decimal == -334e-5);
 
         assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
         assert(key == DecimalExponentKey.none);
-        assert(cast(double) decimal.view == 2482734692817364218734682973648217364981273648923423.0);
+        assert(cast(double) decimal == 2482734692817364218734682973648217364981273648923423.0);
 
         assert(decimal.fromStringImpl(".023", key));
         assert(key == DecimalExponentKey.dot);
-        assert(cast(double) decimal.view == .023);
+        assert(cast(double) decimal == .023);
 
         assert(decimal.fromStringImpl("0E100", key));
         assert(key == DecimalExponentKey.E);
-        assert(cast(double) decimal.view == 0);
+        assert(cast(double) decimal == 0);
 
         foreach (str; ["-nan", "-NaN", "-NAN"])
         {
@@ -179,7 +336,7 @@ struct Decimal(size_t maxSize64)
             assert(decimal.exponent == decimal.exponent.max);
             assert(decimal.coefficient.sign);
             assert(key == DecimalExponentKey.nan);
-            assert(cast(double) decimal.view != cast(double) decimal.view);
+            assert(cast(double) decimal != cast(double) decimal);
         }
 
         foreach (str; ["inf", "Inf", "INF"])
@@ -188,14 +345,79 @@ struct Decimal(size_t maxSize64)
             assert(decimal.coefficient.length == 0);
             assert(decimal.exponent == decimal.exponent.max);
             assert(key == DecimalExponentKey.infinity);
-            assert(cast(double) decimal.view == double.infinity);
+            assert(cast(double) decimal == double.infinity);
         }
 
         assert(decimal.fromStringImpl("-inf", key));
         assert(decimal.coefficient.length == 0);
         assert(decimal.exponent == decimal.exponent.max);
         assert(key == DecimalExponentKey.infinity);
-        assert(cast(double) decimal.view == -double.infinity);
+        assert(cast(double) decimal == -double.infinity);
+
+        assert(!decimal.fromStringImpl("3.3.4", key));
+        assert(!decimal.fromStringImpl("3.4.", key));
+        assert(decimal.fromStringImpl("4.", key));
+        assert(!decimal.fromStringImpl(".", key));
+        assert(decimal.fromStringImpl("0.", key));
+        assert(!decimal.fromStringImpl("00", key));
+        assert(!decimal.fromStringImpl("0d", key));
+    }
+
+    static if (maxSize64 == 3)
+    version(mir_bignum_test)
+    // @safe pure nothrow @nogc
+    unittest
+    {
+        import mir.conv: to;
+        Decimal!1 decimal;
+        DecimalExponentKey key;
+
+        assert(decimal.fromStringImpl("1.334", key));
+        assert(key == DecimalExponentKey.dot);
+        assert(cast(double) decimal == 1.334);
+
+        assert(decimal.fromStringImpl("+0.334e-5"w, key));
+        assert(key == DecimalExponentKey.e);
+        assert(cast(double) decimal == 0.334e-5);
+
+        assert(decimal.fromStringImpl("-334D-5"d, key));
+        assert(key == DecimalExponentKey.D);
+        assert(cast(double) decimal == -334e-5);
+
+        assert(!decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
+
+        assert(decimal.fromStringImpl(".023", key));
+        assert(key == DecimalExponentKey.dot);
+        assert(cast(double) decimal == .023);
+
+        assert(decimal.fromStringImpl("0E100", key));
+        assert(key == DecimalExponentKey.E);
+        assert(cast(double) decimal == 0);
+
+        foreach (str; ["-nan", "-NaN", "-NAN"])
+        {
+            assert(decimal.fromStringImpl(str, key));
+            assert(decimal.coefficient.length > 0);
+            assert(decimal.exponent == decimal.exponent.max);
+            assert(decimal.coefficient.sign);
+            assert(key == DecimalExponentKey.nan);
+            assert(cast(double) decimal != cast(double) decimal);
+        }
+
+        foreach (str; ["inf", "Inf", "INF"])
+        {
+            assert(decimal.fromStringImpl(str, key));
+            assert(decimal.coefficient.length == 0);
+            assert(decimal.exponent == decimal.exponent.max);
+            assert(key == DecimalExponentKey.infinity);
+            assert(cast(double) decimal == double.infinity);
+        }
+
+        assert(decimal.fromStringImpl("-inf", key));
+        assert(decimal.coefficient.length == 0);
+        assert(decimal.exponent == decimal.exponent.max);
+        assert(key == DecimalExponentKey.infinity);
+        assert(cast(double) decimal == -double.infinity);
 
         assert(!decimal.fromStringImpl("3.3.4", key));
         assert(!decimal.fromStringImpl("3.4.", key));
@@ -436,7 +658,87 @@ struct Decimal(size_t maxSize64)
     T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-        return view.opCast!(T, wordNormalized, nonZero);
+
+        enum optimize = maxSize64 == 1 && size_t.sizeof == 8 && T.mant_dig < 64;
+
+        version(LDC)
+        {
+            static if (optimize || wordNormalized)
+                pragma(inline, true);
+        }
+
+        static if (optimize)
+        {
+            import mir.bignum.fixed: UInt;
+            import mir.bignum.fp: Fp, extendedMul;
+            import mir.bignum.internal.dec2flt_table;
+            import mir.bignum.low_level_view: MaxWordPow5, MaxFpPow5;
+            import mir.math.common: floor;
+            import mir.utility: _expect;
+
+            T ret = 0;
+            size_t length = coefficient.length;
+
+
+            static if (!wordNormalized)
+            {
+                if (coefficient.data[0] == 0)
+                    length = 0;
+            }
+
+            if (_expect(exponent == exponent.max, false))
+            {
+                ret = length ? T.nan : T.infinity;
+                goto R;
+            }
+
+            static if (!nonZero)
+                if (length == 0)
+                    goto R;
+            enum S = 9;
+
+            Fp!64 load(typeof(exponent) e)
+            {
+                auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
+                auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
+                return Fp!64(false, p10exp, UInt!64(p10coeff));
+            }
+            {
+                auto expSign = exponent < 0;
+                if (_expect((expSign ? -exponent : exponent) >>> S == 0, true))
+                {
+                    enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
+                    enum ulong half = (1UL << (64 - T.mant_dig - 1));
+                    enum ulong bound = ulong(1) << T.mant_dig;
+
+                    auto c = Fp!64(UInt!64(coefficient.data[0]));
+                    auto z = c.extendedMul(load(exponent));
+                    ret = cast(T) z;
+                    long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
+                    if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > 3 * expSign, true))
+                        goto R;
+                    if (!expSign && exponent <= MaxWordPow5!ulong || exponent == 0)
+                        goto R;
+                    if (expSign && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
+                    {
+                        auto e = load(-exponent);
+                        ret =  c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
+                        goto R;
+                    }
+                    ret = algoR!T(ret, view.coefficient, cast(int) exponent);
+                    goto R;
+                }
+                ret = expSign ? 0 : T.infinity;
+            }
+        R:
+            if (coefficient.sign)
+                ret = -ret;
+            return ret;
+        }
+        else
+        {
+            return view.opCast!(T, wordNormalized, nonZero);
+        }
     }
 
     ///
@@ -463,7 +765,7 @@ unittest
 
     import mir.math.constant: PI;
     assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
-    assert(cast(double) decimal.view == double(PI) / 1e10);
+    assert(cast(double) decimal == double(PI) / 1e10);
     assert(key == DecimalExponentKey.e);
 }
 
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index bdd1dbd6..bf21b487 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -81,7 +81,7 @@ package template MaxWordPow5(T)
         static assert(0);
 }
 
-private template MaxFpPow5(T)
+package template MaxFpPow5(T)
 {
     static if (T.mant_dig == 24)
         enum MaxFpPow5 = 6;
@@ -2049,7 +2049,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
     Precondition: non-empty coefficients
     Note: doesn't support signs.
     +/
-    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDExponent = true, bool allowStartingPlus = true)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true)(scope const(C)[] str, out DecimalExponentKey key)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
@@ -2057,7 +2057,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 
         version(LDC)
         {
-            static if ((allowSpecialValues && allowDExponent && allowStartingPlus) == false)
+            static if ((allowSpecialValues && allowDExponent && allowStartingPlus && allowDotOnBounds && checkEmpty) == false)
                 pragma(inline, true);
         }
 
@@ -2066,8 +2066,11 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         coefficient.leastSignificant = 0;
         auto work = coefficient.topLeastSignificantPart(1);
 
-        if (_expect(str.length == 0, false))
-            return false;
+        static if (checkEmpty)
+        {
+            if (_expect(str.length == 0, false))
+                return false;
+        }
 
         if (str[0] == '-')
         {
@@ -2094,7 +2097,6 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         W t = 1;
         uint afterDot;
         bool dot;
-        bool hasExponent;
 
         if (d == 0)
         {
@@ -2107,45 +2109,34 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                 return false;
             goto S;
         }
-        else
+
         if (d < 10)
         {
             goto S;
         }
-        else
-        if (d == '.' - '0')
+
+        static if (allowDotOnBounds)
         {
-            if (str.length == 0)
-                return false;
-            goto D;
+            if (d == '.' - '0')
+            {
+                if (str.length == 0)
+                    return false;
+                key = DecimalExponentKey.dot;
+                dot = true;
+                goto F;
+            }
+        }
+
+        static if (allowSpecialValues)
+        {
+            goto NI;
         }
         else
         {
-            exponent = exponent.max;
-            static if (allowSpecialValues)
-            {
-                if (str.length == 2)
-                {
-                    auto stail = cast(C[2])str[0 .. 2];
-                    if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
-                    {
-                        coefficient = coefficient.init;
-                        key = DecimalExponentKey.infinity;
-                        return true;
-                    }
-                    if (d == 'n' - '0' && stail== cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
-                    {
-                        coefficient.leastSignificant = 1;
-                        coefficient = coefficient.topLeastSignificantPart(1);
-                        key = DecimalExponentKey.nan;
-                        return true;
-                    }
-                }
-            }
             return false;
         }
 
-        for(;;)
+        F: for(;;)
         {
             enum mp10 = W(10) ^^ MaxWordPow10!W;
             d = str[0] - '0';
@@ -2188,18 +2179,24 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 
                 continue;
             }
-            else
-            if (key != d) switch (d)
+            key = cast(DecimalExponentKey)d;
+            switch (d)
             {
                 D:
                 case DecimalExponentKey.dot:
-                    key = cast(DecimalExponentKey)d;
                     if (_expect(dot, false))
                         break;
                     dot = true;
-                    if (str.length == 0)
+                    if (str.length)
+                        continue;
+                    static if (allowDotOnBounds)
+                    {
                         goto L;
-                    continue;
+                    }
+                    else
+                    {
+                        return false;
+                    }
                 static if (allowDExponent)
                 {
                     case DecimalExponentKey.d:
@@ -2208,8 +2205,6 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                 }
                 case DecimalExponentKey.e:
                 case DecimalExponentKey.E:
-                    key = cast(DecimalExponentKey)d;
-                    hasExponent = true;
                     import mir.parse: parse;
                     if (parse(str, exponent) && str.length == 0)
                     {
@@ -2223,6 +2218,30 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
             break;
         }
         return false;
+
+        static if (allowSpecialValues)
+        {
+        NI:
+            exponent = exponent.max;
+            if (str.length == 2)
+            {
+                auto stail = cast(C[2])str[0 .. 2];
+                if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
+                {
+                    coefficient = coefficient.init;
+                    key = DecimalExponentKey.infinity;
+                    return true;
+                }
+                if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
+                {
+                    coefficient.leastSignificant = 1;
+                    coefficient = coefficient.topLeastSignificantPart(1);
+                    key = DecimalExponentKey.nan;
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 
     ///
@@ -2263,17 +2282,19 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         auto coeff = coefficient.lightConst;
         T ret = 0;
 
+        static if (!wordNormalized)
+            coeff = coeff.normalized;
+
         if (_expect(exponent == exponent.max, false))
         {
             ret = coeff.coefficients.length ? T.nan : T.infinity;
             goto R;
         }
 
-        static if (!wordNormalized)
-            coeff = coeff.normalized;
         static if (!nonZero)
             if (coeff.coefficients.length == 0)
                 goto R;
+
         enum S = 9;
 
         static if (T.mant_dig < 64)
@@ -2400,7 +2421,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 }
 
 @optStrategy("minsize")
-private T algoR(T, W, WordEndian endian)(T ret, BigUIntView!(const W, endian) coeff, int exponent)
+package T algoR(T, W, WordEndian endian)(T ret, BigUIntView!(const W, endian) coeff, int exponent)
 {
     pragma(inline, false);
 

From 367fa74e52b1a49602e38b184d79b77159432a0a Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Fri, 9 Apr 2021 14:49:14 +0700
Subject: [PATCH 185/573] add sum!"decimal", wip: dynamic struct (#339)

* wip: dynamic struct

* wip

* wip

* add decimal sum
---
 meson.build                  |   1 +
 source/mir/bignum/decimal.d  |  53 ++++
 source/mir/bignum/integer.d  |  20 +-
 source/mir/math/sum.d        | 116 +++++++-
 source/mir/ndslice/sorting.d |   4 +-
 source/mir/string_map.d      | 554 +++++++++++++++++++++++++++++++++++
 6 files changed, 731 insertions(+), 17 deletions(-)
 create mode 100644 source/mir/string_map.d

diff --git a/meson.build b/meson.build
index 04dff6c7..a50e5721 100644
--- a/meson.build
+++ b/meson.build
@@ -69,6 +69,7 @@ sources_list = [
     'mir/series',
     'mir/small_array',
     'mir/small_string',
+    'mir/string_map',
     'mir/type_info',
 ]
 
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 83277dbb..3b4271d3 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -752,6 +752,59 @@ struct Decimal(size_t maxSize64)
     {
         return exponent == exponent.max && !coefficient.length;
     }
+
+    ///
+    ref opOpAssign(string op, size_t rhsMaxSize64)(ref const Decimal!rhsMaxSize64 rhs) @safe pure return
+        if (op == "+" || op == "-")
+    {
+        BigInt!rhsMaxSize64 rhsCopy;
+        BigIntView!(const size_t) rhsView;
+        auto expDiff = exponent - rhs.exponent;
+        if (expDiff >= 0)
+        {
+            exponent = rhs.exponent;
+            coefficient.mulPow5(expDiff);
+            coefficient.opOpAssign!"<<"(expDiff);
+            rhsView = rhs.coefficient.view;
+        }
+        else
+        {
+            rhsCopy.copyFrom(rhs.coefficient.view);
+            rhsCopy.mulPow5(-expDiff);
+            rhsCopy.opOpAssign!"<<"(-expDiff);
+            rhsView = rhsCopy.view;
+        }
+        coefficient.opOpAssign!op(rhsView);
+        return this;
+    }
+
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test) @safe pure @nogc unittest
+    {
+        import std.stdio;
+        auto a = Decimal!1("777.7");
+        auto b = Decimal!1("777");
+        import mir.format;
+        assert(stringBuf() << cast(double)a - cast(double)b << getData == "0.7000000000000455");
+        a -= b;
+        assert(stringBuf() << a << getData == "0.7");
+
+        a = Decimal!1("-777.7");
+        b = Decimal!1("777");
+        a += b;
+        assert(stringBuf() << a << getData == "-0.7");
+
+        a = Decimal!1("777.7");
+        b = Decimal!1("-777");
+        a += b;
+        assert(stringBuf() << a << getData == "0.7");
+
+        a = Decimal!1("777");
+        b = Decimal!1("777.7");
+        a -= b;
+        assert(stringBuf() << a << getData == "-0.7");
+    }
 }
 
 ///
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index c04109a6..b809434c 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -324,22 +324,30 @@ struct BigInt(size_t maxSize64)
     Returns:
         overflow
     +/
-    bool opOpAssign(string op)(ref const BigInt rhs)
+    bool opOpAssign(string op, size_t rhsMaxSize64)(ref const BigInt!rhsMaxSize64 rhs)
         @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {
-        int diff = length - rhs.length;
+        return opOpAssign!op(rhs.view);
+    }
+
+    /// ditto
+    bool opOpAssign(string op)(BigIntView!(const size_t) rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        sizediff_t diff = length - rhs.coefficients.length;
         if (diff < 0)
         {
             auto oldLength = length;
-            length = rhs.length;
+            length = cast(int)rhs.coefficients.length;
             view.unsigned.leastSignificantFirst[oldLength .. $] = 0;
         }
         else
-        if (rhs.length == 0)
+        if (rhs.coefficients.length == 0)
             return false;
         auto thisView = view;
-        auto overflow = thisView.opOpAssign!op(rhs.view);
+        auto overflow = thisView.opOpAssign!op(rhs);
         this.sign = thisView.sign;
         if (overflow)
         {
@@ -519,7 +527,7 @@ struct BigInt(size_t maxSize64)
     +/
     bool copyFrom(W, WordEndian endian)(BigIntView!(const W, endian) view)
     {
-        static if (W.sizeof > size_t.sizeof)
+        static if (W.sizeof > size_t.sizeof && endian == TargetEndian)
         {
             return this.copyFrom(cast(BigIntView!(const size_t))view);
         }
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index c903dfd6..aba7b996 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -20,6 +20,23 @@ unittest
     assert(r == ar.sum!"kbn");
     assert(r == ar.sum!"kb2");
     assert(r == ar.sum!"precise");
+    assert(r == ar.sum!"decimal");
+}
+
+/// Decimal precise summation
+version(mir_test)
+unittest
+{
+    auto ar = [777.7, -777];
+    assert(ar.sum!"decimal" == 0.7);
+
+    // The exact binary reuslt is 0.7000000000000455
+    assert(ar[0] + ar[1] == 0.7000000000000455);
+    assert(ar.sum!"fast" == 0.7000000000000455);
+    assert(ar.sum!"kahan" == 0.7000000000000455);
+    assert(ar.sum!"kbn" == 0.7000000000000455);
+    assert(ar.sum!"kb2" == 0.7000000000000455);
+    assert(ar.sum!"precise" == 0.7000000000000455);
 }
 
 ///
@@ -111,6 +128,7 @@ unittest
         assert(r == ar.sum!"kb2");
     }
     assert(r == ar.sum!"precise");
+    assert(r == ar.sum!"decimal");
 }
 
 ///
@@ -317,7 +335,7 @@ enum Summation
     Precise summation algorithm.
     The value of the sum is rounded to the nearest representable
     floating-point number using the $(LUCKY round-half-to-even rule).
-    The result can differ from the exact value on `X86`, `nextDown(proir) <= result &&  result <= nextUp(proir)`.
+    The result can differ from the exact value on 32bit `x86`, `nextDown(proir) <= result &&  result <= nextUp(proir)`.
     The current implementation re-establish special value semantics across iterations (i.e. handling ±inf).
 
     References: $(LINK2 http://www.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps,
@@ -335,6 +353,17 @@ enum Summation
     +/
     precise,
 
+    /++
+    Precise decimal summation algorithm.
+
+    The elements of the sum are converted to a shortest decimal representation that being converted back would result the same floating-point number.
+    The resulting decimal elements are summed without rounding.
+    The decimal sum is converted back to a binary floating point representation using round-half-to-even rule.
+
+    See_also: The $(HTTPS https://github.com/ulfjack/ryu, Ryu algorithm)
+    +/
+    decimal,
+
     /++
     $(WEB en.wikipedia.org/wiki/Kahan_summation, Kahan summation) algorithm.
     +/
@@ -611,6 +640,13 @@ struct Summator(T, Summation summation)
         F s = summationInitValue!F;
     }
     else
+    static if (summation == Summation.decimal)
+    {
+        import mir.bignum.decimal;
+        Decimal!256 s;
+        T ss = 0;
+    }
+    else
     static assert(0, "Unsupported summation type for std.numeric.Summator.");
 
 
@@ -675,6 +711,17 @@ public:
             s = n;
         }
         else
+        static if (summation == Summation.decimal)
+        {
+            ss = 0;
+            if (!(-n.infinity < n && n < n.infinity))
+            {
+                ss = n;
+                n = 0;
+            }
+            s = n;
+        }
+        else
         static assert(0);
     }
 
@@ -895,6 +942,20 @@ public:
             s += n;
         }
         else
+        static if (summation == summation.decimal)
+        {
+            import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
+            if (-n.infinity < n && n < n.infinity)
+            {
+                auto decimal = genericBinaryToDecimal(n);
+                s += decimal;
+            }
+            else
+            {
+                ss += n;
+            }
+        }
+        else
         static assert(0);
     }
 
@@ -1134,7 +1195,12 @@ public:
             return s;
         }
         else
-            static assert(0);
+        static if (summation == Summation.decimal)
+        {
+            return cast(T) s + ss;
+        }
+        else
+        static assert(0);
     }
 
     version(none)
@@ -1311,6 +1377,11 @@ public:
             s = rhs;
         }
         else
+        static if (summation == summation.decimal)
+        {
+            __ctor(rhs);
+        }
+        else
         static assert(0);
     }
 
@@ -1671,13 +1742,21 @@ template sum(F, Summation summation = Summation.appropriate)
         ///
         F sum(Range r)
         {
-            static if (isComplex!F && summation == Summation.precise)
+            static if (isComplex!F && (summation == Summation.precise || summation == Summation.decimal))
             {
                 return sum(r, summationInitValue!F);
             }
             else
             {
-                Summator!(F, ResolveSummationType!(summation, Range, sumType!Range)) sum;
+                static if (summation == Summation.decimal)
+                {
+                    Summator!(F, summation) sum = void;
+                    sum = 0;
+                }
+                else
+                {
+                    Summator!(F, ResolveSummationType!(summation, Range, sumType!Range)) sum;
+                }
                 sum.put(r.move);
                 return sum.sum;
             }
@@ -1686,11 +1765,22 @@ template sum(F, Summation summation = Summation.appropriate)
         ///
         F sum(Range r, F seed)
         {
-            static if (isComplex!F && summation == Summation.precise)
+            static if (isComplex!F && (summation == Summation.precise || summation == Summation.decimal))
             {
                 alias T = typeof(F.init.re);
-                auto sumRe = Summator!(T, Summation.precise)(seed.re);
-                auto sumIm = Summator!(T, Summation.precise)(seed.im);
+                static if (summation == Summation.decimal)
+                {
+                    Summator!(T, summation) sumRe = void;
+                    sumRe = seed.re;
+
+                    Summator!(T, summation) sumIm = void;
+                    sumIm = seed.im;
+                }
+                else
+                {
+                    auto sumRe = Summator!(T, Summation.precise)(seed.re);
+                    auto sumIm = Summator!(T, Summation.precise)(seed.im);
+                }
                 import mir.ndslice.slice: isSlice;
                 static if (isSlice!Range)
                 {
@@ -1713,7 +1803,15 @@ template sum(F, Summation summation = Summation.appropriate)
             }
             else
             {
-                auto sum = Summator!(F, ResolveSummationType!(summation, Range, F))(seed);
+                static if (summation == Summation.decimal)
+                {
+                    Summator!(F, summation) sum = void;
+                    sum = seed;
+                }
+                else
+                {
+                    auto sum = Summator!(F, ResolveSummationType!(summation, Range, F))(seed);
+                }
                 sum.put(r.move);
                 return sum.sum;
             }
@@ -1723,7 +1821,7 @@ template sum(F, Summation summation = Summation.appropriate)
     ///
     F sum(scope const F[] r...)
     {
-        static if (isComplex!F && summation == Summation.precise)
+        static if (isComplex!F && (summation == Summation.precise || summation == Summation.decimal))
         {
             return sum(r, summationInitValue!F);
         }
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 8f0b9847..13f6372e 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -660,8 +660,8 @@ version(mir_test)
     assert(j == 3);
     auto upperBound = a[j .. $];
 
-    assert(a.transitionIndex(a[$-1]) == a.length - 1);
-    assert(a.transitionIndex!"a <= b"(a[$-1]) == a.length);
+    assert(a.transitionIndex(a[$ - 1]) == a.length - 1);
+    assert(a.transitionIndex!"a <= b"(a[$ - 1]) == a.length);
 }
 
 /++
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
new file mode 100644
index 00000000..b91d99e9
--- /dev/null
+++ b/source/mir/string_map.d
@@ -0,0 +1,554 @@
+/++
+$(H1 Ordered string-value associtaive array)
+Macros:
+AlgebraicREF = $(GREF_ALTTEXT mir-core, $(TT $1), $1, mir, algebraic)$(NBSP)
++/
+
+module mir.string_map;
+
+import std.traits;
+
+/++
+Ordered string-value associtaive array with extremely fast lookup.
+
+Params:
+    T = mutable value type, can be instance of $(AlgebraicREF Algebraic) for example.
+    U = an unsigned type that can hold an index of keys. `U.max` must be less then the maximum possible number of struct members.
++/
+struct StringMap(T, U = uint)
+    if (isMutable!T && !__traits(hasMember, T, "opPostMove") && __traits(isUnsigned, U))
+{
+    import mir.utility: _expect;
+    import core.lifetime: move;
+    import mir.conv: emplaceRef;
+
+    private alias Impl = StructImpl!(T, U);
+    private Impl* implementation;
+
+    /++
+    +/
+    this(T[string] aa) @trusted pure nothrow
+    {
+        this(aa.keys, aa.values);
+    }
+
+    /++
+    +/
+    this(string[] keys, T[] values) @trusted pure nothrow
+    {
+        assert(keys.length == values.length);
+        implementation = new Impl(keys, values);
+    }
+
+    /++
+    Returns: number of elements in the table.
+    +/
+    size_t length() @safe pure nothrow @nogc const @property
+    {
+        return implementation ? implementation.length : 0;
+    }
+
+    /++
+    Returns a dynamic array, the elements of which are the keys in the associative array.
+    Doesn't allocate a new copy.
+    +/
+    const(string)[] keys() @safe pure nothrow @nogc const @property
+    {
+        return implementation ? implementation.keys : null;
+    }
+    
+    /++
+    Returns a dynamic array, the elements of which are the values in the associative array.
+    Doesn't allocate a new copy.
+    +/
+    inout(T)[] values() @safe pure nothrow @nogc inout @property
+    {
+        return implementation ? implementation.values : null;
+    }
+
+    /++
+    (Property) Gets the current capacity of an associative array.
+    The capacity is the size that the underlaynig slices can grow to before the underlying arrays may be reallocated or extended.
+    +/
+    size_t capacity() @safe pure nothrow const @property
+    {
+        import mir.utility: min;
+
+        return !implementation ? 0 : min(
+            implementation.keys.capacity,
+            implementation.values.capacity,
+            implementation.indices.capacity,
+        );
+    }
+
+    /++
+    Reserves capacity for an associative array. 
+    The capacity is the size that the underlaying slices can grow to before the underlying arrays may be reallocated or extended.
+    +/
+    size_t reserve(size_t newcapacity) @trusted pure nothrow 
+    {
+        import mir.utility: min;
+
+        if (_expect(!implementation, false))
+        {
+            implementation = new Impl;
+        }
+
+        auto keysV = implementation.keys;
+        auto keysVCaacity = keysV.reserve(newcapacity);
+        implementation._keys = keysV.ptr;
+
+        auto valuesV = implementation.values;
+        auto valuesCapacity = valuesV.reserve(newcapacity);
+        implementation._values = valuesV.ptr;
+
+        auto indicesV = implementation.indices;
+        auto indicesCapacity = indicesV.reserve(newcapacity);
+        implementation._indices = indicesV.ptr;
+
+        return min(
+            keysVCaacity,
+            valuesCapacity,
+            indicesCapacity,
+        );
+    }
+
+    /++
+    Assume that it is safe to append to this associative array.
+    Appends made to this associative array after calling this function may append in place, even if the array was a slice of a larger array to begin with.
+    Use this only when it is certain there are no elements in use beyond the associative array in the memory block. If there are, those elements will be overwritten by appending to this associative array.
+
+    Warning: Calling this function, and then using references to data located after the given associative array results in undefined behavior.
+
+    Returns: The input is returned.
+    +/
+    ref inout(typeof(this)) assumeSafeAppend() @system nothrow inout return
+    {
+        if (implementation)
+        {
+            implementation.keys.assumeSafeAppend;
+            implementation.values.assumeSafeAppend;
+            implementation.indices.assumeSafeAppend;
+        }
+        return this;
+    }
+
+    /++
+    Removes all remaining keys and values from an associative array.
+    +/
+    void clear() @safe pure nothrow @nogc
+    {
+        if (implementation)
+        {
+            implementation._length = 0;
+            implementation._lengthTable = implementation._lengthTable[0 .. 0];
+        }
+
+    }
+
+    /++
+    +/
+    bool remove(scope const(char)[] key) @trusted pure nothrow @nogc
+    {
+        if (!implementation)
+            return false;
+        size_t index;
+        if (!implementation.findIndex(key, index))
+            return false;
+        implementation.removeAt(index);
+        return true;
+    }
+
+    /++
+    +/
+    ptrdiff_t findPos(scope const(char)[] key) @trusted pure nothrow @nogc const
+    {
+        if (!implementation)
+            return -1;
+        size_t index;
+        if (!implementation.findIndex(key, index))
+            return -1;
+        return implementation._indices[index];
+    }
+
+    /++
+    +/
+    inout(T)* opBinaryRight(string op : "in")(scope const(char)[] key) @system pure nothrow @nogc inout
+    {
+        if (!implementation)
+            return null;
+        size_t index;
+        if (!implementation.findIndex(key, index))
+            return null;
+        return implementation._values + index;
+    }
+
+    /++
+    +/
+    ref inout(T) opIndex(scope const(char)[] key) @trusted pure inout //@nogc
+    {
+        size_t index;
+        if (implementation && implementation.findIndex(key, index))
+        {
+            assert (index < length);
+            index = implementation._indices[index];
+            assert (index < length);
+            return implementation._values[index];
+        }
+        import mir.exception: MirException;
+        throw new MirException("No member: ", key);
+    }
+
+    /++
+    +/
+    ref T opIndexAssign(T value, string key) @trusted pure nothrow
+    {
+        size_t index;
+        if (_expect(!implementation, false))
+        {
+            implementation = new Impl;
+        }
+        else
+        {
+            if (key.length + 1 < implementation.lengthTable.length)
+            {
+                if (implementation.findIndex(key, index))
+                {
+                    assert (index < length);
+                    index = implementation._indices[index];
+                    assert (index < length);
+                    implementation._values[index] = move(value);
+                    return implementation._values[index];
+                }
+                assert (index <= length);
+                index = index < length ? implementation.indices[index] : length;
+                assert (index <= length);
+            }
+            else
+            {
+                index = length;
+            }
+        }
+        assert (index <= length);
+        implementation.insertAt(key, move(value), index);
+        return implementation._values[index];
+    }
+
+    /++
+    +/
+    inout(T) get(scope const(char)[] key, lazy inout(T) defaultValue)
+    {
+        size_t index;
+        if (implementation && implementation.findIndex(key, index))
+        {
+            assert (index < length);
+            index = implementation.indices[index];
+            assert (index < length);
+            return implementation.values[index];
+        }
+        return defaultValue;
+    }
+
+    /++
+    +/
+    ref T require(string key, lazy T value = T.init)
+    {
+        import std.stdio;
+        size_t index;
+        if (_expect(!implementation, false))
+        {
+            implementation = new Impl;
+        }
+        else
+        {
+            if (key.length + 1 < implementation.lengthTable.length)
+            {
+                if (implementation.findIndex(key, index))
+                {
+                    assert (index < length);
+                    index = implementation.indices[index];
+                    assert (index < length);
+                    return implementation.values[index];
+                }
+                assert (index <= length);
+                index = index < length ? implementation.indices[index] : length;
+                assert (index <= length);
+            }
+            else
+            {
+                index = length;
+            }
+        }
+        assert (index <= length);
+        implementation.insertAt(key, value, index);
+        return implementation.values[index];
+    }
+
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!int aa = ["aa": 1];
+        assert(aa.require("aa", 0) == 1);
+        assert(aa.require("bb", 0) == 0);
+        assert(aa["bb"] == 0);
+    }
+}
+
+///
+unittest
+{
+    StringMap!int table;
+    table["L"] = 3;
+    table["A"] = 2;
+    table["val"] = 1;
+    assert(table.keys == ["L", "A", "val"]);
+    assert(table.values == [3, 2, 1]);
+    assert(table["A"] == 2);
+    table.values[2] += 10;
+    assert(table["A"] == 2);
+    assert(table["L"] == 3);
+    assert(table["val"] == 11);
+    assert(table.keys == ["L", "A", "val"]);
+    assert(table.values == [3, 2, 11]);
+    table.remove("A");
+    assert(table.keys == ["L", "val"]);
+    assert(table.values == [3, 11]);
+    assert(table["L"] == 3);
+    assert(table["val"] == 11);
+}
+
+private struct StructImpl(T, U = uint)
+    if (!__traits(hasMember, T, "opPostMove") && __traits(isUnsigned, U))
+{
+    import core.lifetime: move;
+    import mir.conv: emplaceRef;
+
+    size_t _length;
+    string* _keys;
+    T* _values;
+    U* _indices;
+    U[] _lengthTable;
+
+    /++
+    +/
+    this(string[] keys, T[] values) @trusted pure nothrow
+    {
+        import mir.array.allocation: array;
+        import mir.ndslice.sorting: sort;
+        import mir.ndslice.topology: iota, indexed;
+        import mir.string_table: smallerStringFirst;
+
+        assert(keys.length == values.length);
+        if (keys.length == 0)
+            return;
+        _length = keys.length;
+        _keys = keys.ptr;
+        _values = values.ptr;
+        _indices = keys.length.iota!U.array.ptr;
+        auto sortedKeys = _keys.indexed(indices);
+        sortedKeys.sort!smallerStringFirst;
+        size_t maxKeyLength;
+        foreach (ref key; keys)
+            if (key.length > maxKeyLength)
+                maxKeyLength = key.length;
+        _lengthTable = new U[maxKeyLength + 2];
+
+        size_t ski;
+        foreach (length; 0 .. maxKeyLength + 1)
+        {
+            while(ski < sortedKeys.length && sortedKeys[ski].length == length)
+                ski++;
+            _lengthTable[length + 1] = cast(U)ski;
+        }
+    }
+
+    // this(size_t length, size_t maxKeyLength)
+    // {
+    //     assert(length);
+    //     _length = length;
+    //     _keys = new string[length].ptr;
+    //     _values = new T[length].ptr;
+    //     _indices = new U[length].assumeSafeAppend;
+    //     _lengthTable = new U[maxKeyLength + 2].assumeSafeAppend;
+    // }
+
+    // void addFirstEntry(string key, T value)
+    // {
+    //     import core.lifetime: move;
+    //     pragma(inline, false);
+    //     __ctor(1, key.length);
+    //     keys[0] = key;
+    //     values[0] = move(value);
+    //     lengthTable[key.length + 1 .. $] = 1;
+    // }
+
+    void insertAt(string key, T value, size_t i) @trusted
+    {
+        pragma(inline, false);
+
+        assert(i <= length);
+        {
+            auto a = keys;
+            a ~= key;
+            _keys = a.ptr;
+        }
+        {
+            auto a = values;
+            a ~= move(value);
+            _values = a.ptr;
+        }
+        {
+            auto a = indices;
+            a ~= 0;
+            _indices = a.ptr;
+
+            if (__ctfe)
+            {
+                foreach_reverse (idx; i .. length)
+                {
+                    _indices[idx + 1] = _indices[idx];
+                }
+            }
+            else
+            {
+                import core.stdc.string: memmove;
+                memmove(_indices + i + 1, _indices + i, (length - i) * U.sizeof);
+            }
+            assert(length <= U.max);
+            _indices[i] = cast(U)length;
+            _length++;
+        }
+        {
+            if (key.length + 2 <= lengthTable.length)
+            {
+                ++lengthTable[key.length + 1 .. $];
+            }
+            else
+            {
+                auto oldLen = _lengthTable.length;
+                _lengthTable.length = key.length + 2;
+                auto oldVal = oldLen ? _lengthTable[oldLen - 1] : 0;
+                _lengthTable[oldLen .. key.length + 1] =  oldVal;
+                _lengthTable[key.length + 1] =  oldVal + 1;
+            }
+        }
+    }
+
+    void removeAt(size_t i)
+    {
+        assert(i < length);
+        auto j = _indices[i];
+        assert(j < length);
+        {
+            --_lengthTable[_keys[j].length + 1 .. $];
+        }
+        {
+            if (__ctfe)
+            {
+                foreach (idx; i .. length)
+                {
+                    _indices[idx] = _indices[idx + 1];
+                    _indices[idx] = _indices[idx + 1];
+                }
+            }
+            else
+            {
+                import core.stdc.string: memmove;
+                memmove(_indices + i, _indices + i + 1, (length - 1 - i) * U.sizeof);
+            }
+            foreach (ref elem; indices[0 .. $ - 1])
+                if (elem > i)
+                    elem--;
+        }
+        {
+            if (__ctfe)
+            {
+                foreach_reverse (idx; j .. length - 1)
+                {
+                    _keys[idx] = _keys[idx + 1];
+                    _values[idx] = move(_values[idx + 1]);
+                }
+            }
+            else
+            {
+                import core.stdc.string: memmove;
+                destroy!false(_values[j]);
+                memmove(_keys + j, _keys + j + 1, (length - 1 - j) * string.sizeof);
+                memmove(_values + j, _values + j + 1, (length - 1 - j) * T.sizeof);
+                emplaceRef(_values[length - 1]);
+            }
+        }
+        _length--;
+        _lengthTable = _lengthTable[0 .. _keys[length - 1].length + 2];
+    }
+
+    size_t length() @safe pure nothrow @nogc const @property
+    {
+        return _length;
+    }
+
+    inout(string)[] keys() @trusted inout @property
+    {
+        return _keys[0 .. _length];
+    }
+    
+    inout(T)[] values() @trusted inout @property
+    {
+        return _values[0 .. _length];
+    }
+    
+    inout(U)[] indices() @trusted inout @property
+    {
+        return _indices[0 .. _length];
+    }
+    
+    inout(U)[] lengthTable() @trusted inout @property
+    {
+        return _lengthTable;
+    }
+
+    auto sortedKeys() @trusted const @property
+    {
+        import mir.ndslice.topology: indexed;
+        return _keys.indexed(indices);
+    }
+
+    bool findIndex(scope const(char)[] key, ref size_t index) @trusted pure nothrow @nogc const
+    {
+        import mir.utility: _expect;
+        if (_expect(key.length + 1 < _lengthTable.length, true))
+        {
+
+            // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+            // 0 1 2 3 4 5 6   8 9 10    12          16
+
+            auto low = _lengthTable[key.length] + 0u;
+            auto high = _lengthTable[key.length + 1] + 0u;
+            while (low < high)
+            {
+                auto mid = (low + high) / 2;
+
+                import core.stdc.string: memcmp;
+                int r = void;
+
+                if (__ctfe)
+                    r = __cmp(key, _keys[_indices[mid]]);
+                else
+                    r = memcmp(key.ptr, _keys[_indices[mid]].ptr, key.length);
+
+                if (r == 0)
+                {
+                    index = mid;
+                    return true;
+                }
+                if (r > 0)
+                    low = mid + 1;
+                else
+                    high = mid;
+            }
+            index = low;
+        }
+        return false;
+    }
+}

From 899d30f6c9bebe1fd425e06c314671c35bc01425 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 9 Apr 2021 15:43:30 +0700
Subject: [PATCH 186/573] fixup docs

---
 source/mir/bignum/decimal.d | 2 +-
 source/mir/math/sum.d       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 3b4271d3..d5e25c07 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -74,7 +74,7 @@ struct Decimal(size_t maxSize64)
     }
 
     /++
-    Constructs Decimal from the floating point number using the $(HTTPS https://github.com/ulfjack/ryu, Ryu algorithm).
+    Constructs Decimal from the floating point number using the $(HTTPS github.com/ulfjack/ryu, Ryu algorithm).
 
     The number is the shortest decimal representation that being converted back would result the same floating-point number.
     +/
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index aba7b996..a4a0acc4 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -360,7 +360,7 @@ enum Summation
     The resulting decimal elements are summed without rounding.
     The decimal sum is converted back to a binary floating point representation using round-half-to-even rule.
 
-    See_also: The $(HTTPS https://github.com/ulfjack/ryu, Ryu algorithm)
+    See_also: The $(HTTPS github.com/ulfjack/ryu, Ryu algorithm)
     +/
     decimal,
 

From bce5f397446b3545cca176c6aef5ec7e2e062e59 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Fri, 9 Apr 2021 21:55:03 +0700
Subject: [PATCH 187/573] Fixes and tests for StringMap (#340)

* update index

* update example

* fix and add test for StringMap
---
 index.d                 |  25 +++++-----
 source/mir/math/sum.d   |   1 +
 source/mir/string_map.d | 108 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 113 insertions(+), 21 deletions(-)

diff --git a/index.d b/index.d
index 0a225c6e..0e8f2dd8 100644
--- a/index.d
+++ b/index.d
@@ -11,40 +11,41 @@ $(BOOKTABLE ,
         $(TH Description)
     )
     $(LEADINGROW Accessories)
+    $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
     $(TR $(TDNW $(MREF mir,exception)) $(TD @nogc MirException with formatting))
     $(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities))
     $(TR $(TDNW $(MREF mir,parse)) $(TD @nogc Parsing Utilities))
-    $(TR $(TDNW $(MREF mir,small_array)) $(TD Generic Small Arrays))
-    $(TR $(TDNW $(MREF mir,small_string)) $(TD Generic Small Strings))
-    $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
     $(TR $(TDNW $(MREF mir,range)) $(TD Ranges))
     $(TR $(TDNW $(MREF mir,serde)) $(TD Utilities for serialization libraries ))
+    $(TR $(TDNW $(MREF mir,small_array)) $(TD Generic Small Arrays))
+    $(TR $(TDNW $(MREF mir,small_string)) $(TD Generic Small Strings))
+    $(TR $(TDNW $(MREF mir,string_map)) $(TD Ordered string-value associative array with fast lookup))
     $(LEADINGROW Date and time)
     $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability))
     $(LEADINGROW NDarrays and Algorithms)
+    $(TR $(TDNW $(MREF mir,algorithm,iteration)) $(TD Mir & BetterC rework of Phobos.))
+    $(TR $(TDNW $(MREF mir,algorithm,setops)) $(TD Mir & BetterC rework of Phobos.))
     $(TR $(TDNW $(MREF mir,ndslice)★) $(TD Package for ndarrays and iteration algorithms.))
     $(TR $(TDNW $(MREF mir,range)) $(TD Additoinal primitive ranges. See also $(MREF mir,ndslice), which contains a lot of range constructos.))
-    $(TR $(TDNW $(MREF mir,algorithm,setops)) $(TD Mir & BetterC rework of Phobos.))
-    $(TR $(TDNW $(MREF mir,algorithm,iteration)) $(TD Mir & BetterC rework of Phobos.))
     $(LEADINGROW Math)
-    $(TR $(TDNW $(MREF mir,numeric)) $(TD Basic numeric optimisations))
+    $(TR $(TDNW $(MREF mir,interpolate)★) $(TD Interpolation algorithms))
     $(TR $(TDNW $(MREF mir,math,numeric)) $(TD Simple numeric algorithms))
-    $(TR $(TDNW $(MREF mir,math,sum)) $(TD Various precise summation algorithms))
     $(TR $(TDNW $(MREF mir,math,stat)) $(TD Basic API for statistics))
+    $(TR $(TDNW $(MREF mir,math,sum)) $(TD Various precise summation algorithms))
+    $(TR $(TDNW $(MREF mir,numeric)) $(TD Basic numeric optimisations))
     $(TR $(TDNW $(MREF mir,polynomial)) $(TD Polynomial ref-counted structure))
-    $(TR $(TDNW $(MREF mir,interpolate)★) $(TD Interpolation algorithms))
     $(LEADINGROW Reference counting)
-    $(TR $(TDNW $(MREF mir,rc)) $(TD Reference counting package and RC conversion utilities.))
     $(TR $(TDNW $(MREF mir,rc,array)) $(TD Thread safe reference count array and the iterator to adopt it to ndslice.))
     $(TR $(TDNW $(MREF mir,rc,ptr)) $(TD Thread safe reference count pointer with polymorphism support for strucs and objects.))
     $(TR $(TDNW $(MREF mir,rc,slim_ptr)) $(TD Thread safe reference count pointer for strucs and objects.))
+    $(TR $(TDNW $(MREF mir,rc)) $(TD Reference counting package and RC conversion utilities.))
     $(LEADINGROW Containers)
-    $(TR $(TDNW $(MREF mir,series)★) $(TD Generic series suitable for time-series or semi-immutable ordered maps with CPU cache friendly binary search.))
-    $(TR $(TDNW $(MREF mir,container,binaryheap)★) $(TD Mir & BetterC rework of Phobos.))
     $(TR $(TDNW $(MREF mir,appender)) $(TD Scoped Buffer.))
+    $(TR $(TDNW $(MREF mir,container,binaryheap)★) $(TD Mir & BetterC rework of Phobos.))
+    $(TR $(TDNW $(MREF mir,series)★) $(TD Generic series suitable for time-series or semi-immutable ordered maps with CPU cache friendly binary search.))
     $(LEADINGROW Graphs)
-    $(TR $(TDNW $(MREF mir,graph)) $(TD Basic routines to work with graphs))
     $(TR $(TDNW $(MREF mir,graph,tarjan)★) $(TD Tarjan's strongly connected components algorithm))
+    $(TR $(TDNW $(MREF mir,graph)) $(TD Basic routines to work with graphs))
     $(LEADINGROW Big Numbers (partial implementation))
     $(TR $(TDNW $(MREF mir,bignum, decimal)) $(TD Stack-allocated decimal type))
     $(TR $(TDNW $(MREF mir,bignum, fixed)) $(TD Stack-allocated fixed length unsigned integer type (like 256bit integers).))
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index a4a0acc4..534aaf0a 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -29,6 +29,7 @@ unittest
 {
     auto ar = [777.7, -777];
     assert(ar.sum!"decimal" == 0.7);
+    assert(sum!"decimal"(777.7, -777) == 0.7);
 
     // The exact binary reuslt is 0.7000000000000455
     assert(ar[0] + ar[1] == 0.7000000000000455);
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index b91d99e9..6dced8f8 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -32,6 +32,14 @@ struct StringMap(T, U = uint)
         this(aa.keys, aa.values);
     }
 
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!int map = ["key" : 1];
+        assert(map.findPos("key") == 0);
+    }
+
     /++
     +/
     this(string[] keys, T[] values) @trusted pure nothrow
@@ -40,6 +48,17 @@ struct StringMap(T, U = uint)
         implementation = new Impl(keys, values);
     }
 
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        auto keys = ["ba", "a"];
+        auto values = [1.0, 3.0];
+        auto map = StringMap!double(keys, values);
+        assert(map.keys is keys);
+        assert(map.values is values);
+    }
+
     /++
     Returns: number of elements in the table.
     +/
@@ -48,6 +67,24 @@ struct StringMap(T, U = uint)
         return implementation ? implementation.length : 0;
     }
 
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!double map;
+        assert(map.length == 0);
+        map["a"] = 3.0;
+        assert(map.length == 1);
+        map["c"] = 4.0;
+        assert(map.length == 2);
+        map.remove("c");
+        assert(map.length == 1);
+        map.remove("c");
+        assert(map.length == 1);
+        map.remove("a");
+        assert(map.length == 0);
+    }
+
     /++
     Returns a dynamic array, the elements of which are the keys in the associative array.
     Doesn't allocate a new copy.
@@ -56,7 +93,25 @@ struct StringMap(T, U = uint)
     {
         return implementation ? implementation.keys : null;
     }
-    
+
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!double map;
+        assert(map.keys == []);
+        map["c"] = 4.0;
+        assert(map.keys == ["c"]);
+        map["a"] = 3.0;
+        assert(map.keys == ["c", "a"]);
+        map.remove("c");
+        assert(map.keys == ["a"]);
+        map.remove("a");
+        assert(map.keys == []);
+        map["c"] = 4.0;
+        assert(map.keys == ["c"]);
+    }
+
     /++
     Returns a dynamic array, the elements of which are the values in the associative array.
     Doesn't allocate a new copy.
@@ -66,6 +121,26 @@ struct StringMap(T, U = uint)
         return implementation ? implementation.values : null;
     }
 
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!double map;
+        assert(map.values == []);
+        map["c"] = 4.0;
+        assert(map.values == [4.0]);
+        map["a"] = 3.0;
+        assert(map.values == [4.0, 3.0]);
+        map.values[0]++;
+        assert(map.values == [5.0, 3.0]);
+        map.remove("c");
+        assert(map.values == [3.0]);
+        map.remove("a");
+        assert(map.values == []);
+        map["c"] = 4.0;
+        assert(map.values == [4.0]);
+    }
+
     /++
     (Property) Gets the current capacity of an associative array.
     The capacity is the size that the underlaynig slices can grow to before the underlying arrays may be reallocated or extended.
@@ -81,6 +156,21 @@ struct StringMap(T, U = uint)
         );
     }
 
+    static if (is(T == int))
+    ///
+    unittest
+    {
+        StringMap!double map;
+        assert(map.capacity == 0);
+        map["c"] = 4.0;
+        assert(map.capacity >= 1);
+        map["a"] = 3.0;
+        assert(map.capacity >= 2);
+        map.remove("c");
+        map.assumeSafeAppend;
+        assert(map.capacity >= 2);
+    }
+
     /++
     Reserves capacity for an associative array. 
     The capacity is the size that the underlaying slices can grow to before the underlying arrays may be reallocated or extended.
@@ -150,13 +240,13 @@ struct StringMap(T, U = uint)
     +/
     bool remove(scope const(char)[] key) @trusted pure nothrow @nogc
     {
-        if (!implementation)
-            return false;
         size_t index;
-        if (!implementation.findIndex(key, index))
-            return false;
-        implementation.removeAt(index);
-        return true;
+        if (implementation && implementation.findIndex(key, index))
+        {
+            implementation.removeAt(index);
+            return true;
+        }
+        return false;
     }
 
     /++
@@ -458,7 +548,7 @@ private struct StructImpl(T, U = uint)
                 memmove(_indices + i, _indices + i + 1, (length - 1 - i) * U.sizeof);
             }
             foreach (ref elem; indices[0 .. $ - 1])
-                if (elem > i)
+                if (elem > j)
                     elem--;
         }
         {
@@ -480,7 +570,7 @@ private struct StructImpl(T, U = uint)
             }
         }
         _length--;
-        _lengthTable = _lengthTable[0 .. _keys[length - 1].length + 2];
+        _lengthTable = _lengthTable[0 .. length ? _keys[length - 1].length + 2 : 0];
     }
 
     size_t length() @safe pure nothrow @nogc const @property

From 5ccdbf61427fa5e2c950e1d6b98296f6f1ef9bb6 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Sat, 10 Apr 2021 22:27:51 +0700
Subject: [PATCH 188/573] add mir.algebraic_alias.json and update sting map
 (#341)

* add tests and fixes for string map

* add mir.algebraic_alias.json and update sting map

* fixup

* fixup
---
 index.d                           |   1 +
 meson.build                       |   1 +
 source/mir/algebraic_alias/json.d | 127 +++++++++++
 source/mir/string_map.d           | 350 +++++++++++++++++++++++++-----
 4 files changed, 426 insertions(+), 53 deletions(-)
 create mode 100644 source/mir/algebraic_alias/json.d

diff --git a/index.d b/index.d
index 0e8f2dd8..721a6046 100644
--- a/index.d
+++ b/index.d
@@ -11,6 +11,7 @@ $(BOOKTABLE ,
         $(TH Description)
     )
     $(LEADINGROW Accessories)
+    $(TR $(TDNW $(MREF mir,algebraic_alias,json)) $(TD Mutable JSON value))
     $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
     $(TR $(TDNW $(MREF mir,exception)) $(TD @nogc MirException with formatting))
     $(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities))
diff --git a/meson.build b/meson.build
index a50e5721..341d8de1 100644
--- a/meson.build
+++ b/meson.build
@@ -8,6 +8,7 @@ subprojects = ['mir-core']
 has_cpp_headers = true
 
 sources_list = [
+    'mir/algebraic_alias/json',
     'mir/algorithm/iteration',
     'mir/algorithm/setops',
     'mir/appender',
diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
new file mode 100644
index 00000000..f8408cd7
--- /dev/null
+++ b/source/mir/algebraic_alias/json.d
@@ -0,0 +1,127 @@
+/++
+$(H1 Mutable JSON value)
+
+This module contains a single alias definition and doesn't provide JSON serialization API.
+
+See_also: Json libraries $(GMREF mir-ion, index) and $(GMREF asdf, index);
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko 
+Macros:
++/
+module mir.algebraic_alias.json;
+
+import mir.algebraic: TaggedVariant, This;
+public import mir.string_map: StringMap;
+
+/++
+Definition union for $(LREF JsonAlgebraic).
++/
+union JsonAlgebraicUnion
+{
+    ///
+    typeof(null) null_;
+    ///
+    bool boolean;
+    ///
+    long integer;
+    ///
+    double float_;
+    ///
+    immutable(char)[] string;
+    /// Self alias in array.
+    This[] array;
+    /// Self alias in $(MREF mir,string_map).
+    StringMap!This object;
+}
+
+/++
+JSON tagged algebraic alias.
+
+The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic).
+See also $(MREF mir,string_map) - ordered string-value associative array.
++/
+alias JsonAlgebraic = TaggedVariant!JsonAlgebraicUnion;
+
+///
+unittest
+{
+    import mir.ndslice.topology: map;
+    import mir.array.allocation: array;
+
+    JsonAlgebraic value;
+
+    StringMap!JsonAlgebraic object;
+
+    // Default
+    assert(value.isNull);
+    assert(value.kind == JsonAlgebraic.Kind.null_);
+
+    // Boolean
+    value = true;
+    object["bool"] = value;
+    assert(!value.isNull);
+    assert(value == true);
+    assert(value.kind == JsonAlgebraic.Kind.boolean);
+    assert(value.get!bool == true);
+    assert(value.get!(JsonAlgebraic.Kind.boolean) == true);
+
+    // Null
+    value = null;
+    object["null"] = value;
+    assert(value.isNull);
+    assert(value == null);
+    assert(value.kind == JsonAlgebraic.Kind.null_);
+    assert(value.get!(typeof(null)) == null);
+    assert(value.get!(JsonAlgebraic.Kind.null_) == null);
+
+    // String
+    value = "s";
+    object["string"] = value;
+    assert(value.kind == JsonAlgebraic.Kind.string);
+    assert(value == "s");
+    assert(value.get!string == "s");
+    assert(value.get!(JsonAlgebraic.Kind.string) == "s");
+
+    // Integer
+    value = 4;
+    object["integer"] = value;
+    assert(value.kind == JsonAlgebraic.Kind.integer);
+    assert(value == 4);
+    assert(value != 4.0);
+    assert(value.get!long == 4);
+    assert(value.get!(JsonAlgebraic.Kind.integer) == 4);
+
+    // Float
+    value = 3.0;
+    object["float"] = value;
+    assert(value.kind == JsonAlgebraic.Kind.float_);
+    assert(value != 3);
+    assert(value == 3.0);
+    assert(value.get!double == 3.0);
+    assert(value.get!(JsonAlgebraic.Kind.float_) == 3.0);
+
+    // Array
+    JsonAlgebraic[] arr = [0, 1, 2, 3, 4].map!JsonAlgebraic.array;
+
+    value = arr;
+    object["array"] = value;
+    assert(value.kind == JsonAlgebraic.Kind.array);
+    assert(value == arr);
+    assert(value.get!(JsonAlgebraic[])[3] == 3);
+
+    // Object
+    assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]);
+    object.values[0] = "false";
+    assert(object["bool"] == "false"); // it is a string now
+    object.remove("bool"); // remove the member
+
+    value = object;
+    object["array"] = value;
+    assert(value.kind == JsonAlgebraic.Kind.object);
+    assert(value.get!(StringMap!JsonAlgebraic).keys is object.keys);
+    assert(value.get!(StringMap!JsonAlgebraic).values is object.values);
+
+    JsonAlgebraic[string] aa = object.toAA;
+    object = StringMap!JsonAlgebraic(aa);
+}
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 6dced8f8..c51cb48c 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -25,9 +25,66 @@ struct StringMap(T, U = uint)
     private alias Impl = StructImpl!(T, U);
     private Impl* implementation;
 
+    // // linking bug
+    // version(none)
+    // {
+    //     /++
+    //     +/
+    //     bool opEquals()(typeof(null)) @safe pure nothrow @nogc const
+    //     {
+    //         return implementation is null;
+    //     }
+
+    //     static if (is(T == int))
+    //     ///
+    //     @safe pure unittest
+    //     {
+    //         StringMap!int map;
+    //         assert(map == null);
+    //         map = StringMap!int(["key" : 1]);
+    //         assert(map != null);
+    //         map.remove("key");
+    //         assert(map != null);
+    //     }
+    // }
+
+    /++
+    Reset the associtave array
+    +/
+    ref opAssign()(typeof(null)) return @safe pure nothrow @nogc
+    {
+        implementation = null;
+        return this;
+    }
+
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!int map = ["key" : 1];
+        map = null;
+    }
+
+    /++
+    Initialize the associtave array with default value.
+    +/
+    this()(typeof(null) aa) @safe pure nothrow @nogc
+    {
+        implementation = null;
+    }
+
+    static if (is(T == int))
+    /// Usefull for default funcion argument.
+    @safe pure unittest
+    {
+        StringMap!int map = null; //
+    }
+
     /++
+    Constructs an associative array using keys and values from the builtin associative array
+    Complexity: `O(n log(n))`
     +/
-    this(T[string] aa) @trusted pure nothrow
+    this()(T[string] aa) @trusted pure nothrow
     {
         this(aa.keys, aa.values);
     }
@@ -41,8 +98,15 @@ struct StringMap(T, U = uint)
     }
 
     /++
+    Constructs an associative array using keys and values.
+    Params:
+        keys = mutable array of keys
+        values = mutable array of values
+    Key and value arrays must have the same length.
+
+    Complexity: `O(n log(n))`
     +/
-    this(string[] keys, T[] values) @trusted pure nothrow
+    this()(string[] keys, T[] values) @trusted pure nothrow
     {
         assert(keys.length == values.length);
         implementation = new Impl(keys, values);
@@ -62,7 +126,7 @@ struct StringMap(T, U = uint)
     /++
     Returns: number of elements in the table.
     +/
-    size_t length() @safe pure nothrow @nogc const @property
+    size_t length()() @safe pure nothrow @nogc const @property
     {
         return implementation ? implementation.length : 0;
     }
@@ -77,19 +141,21 @@ struct StringMap(T, U = uint)
         assert(map.length == 1);
         map["c"] = 4.0;
         assert(map.length == 2);
-        map.remove("c");
+        assert(map.remove("c"));
         assert(map.length == 1);
-        map.remove("c");
+        assert(!map.remove("c"));
         assert(map.length == 1);
-        map.remove("a");
+        assert(map.remove("a"));
         assert(map.length == 0);
     }
 
     /++
     Returns a dynamic array, the elements of which are the keys in the associative array.
     Doesn't allocate a new copy.
+
+    Complexity: `O(1)`
     +/
-    const(string)[] keys() @safe pure nothrow @nogc const @property
+    const(string)[] keys()() @safe pure nothrow @nogc const @property
     {
         return implementation ? implementation.keys : null;
     }
@@ -115,8 +181,10 @@ struct StringMap(T, U = uint)
     /++
     Returns a dynamic array, the elements of which are the values in the associative array.
     Doesn't allocate a new copy.
+
+    Complexity: `O(1)`
     +/
-    inout(T)[] values() @safe pure nothrow @nogc inout @property
+    inout(T)[] values()() @safe pure nothrow @nogc inout @property
     {
         return implementation ? implementation.values : null;
     }
@@ -144,8 +212,10 @@ struct StringMap(T, U = uint)
     /++
     (Property) Gets the current capacity of an associative array.
     The capacity is the size that the underlaynig slices can grow to before the underlying arrays may be reallocated or extended.
+
+    Complexity: `O(1)`
     +/
-    size_t capacity() @safe pure nothrow const @property
+    size_t capacity()() @safe pure nothrow const @property
     {
         import mir.utility: min;
 
@@ -175,7 +245,7 @@ struct StringMap(T, U = uint)
     Reserves capacity for an associative array. 
     The capacity is the size that the underlaying slices can grow to before the underlying arrays may be reallocated or extended.
     +/
-    size_t reserve(size_t newcapacity) @trusted pure nothrow 
+    size_t reserve()(size_t newcapacity) @trusted pure nothrow 
     {
         import mir.utility: min;
 
@@ -203,6 +273,24 @@ struct StringMap(T, U = uint)
         );
     }
 
+    static if (is(T == int))
+    ///
+    unittest
+    {
+        StringMap!double map;
+        auto capacity = map.reserve(10);
+        assert(capacity >= 10);
+        assert(map.capacity == capacity);
+        map["c"] = 4.0;
+        assert(map.capacity == capacity);
+        map["a"] = 3.0;
+        assert(map.capacity >= 2);
+        assert(map.remove("c"));
+        capacity = map.reserve(20);
+        assert(capacity >= 20);
+        assert(map.capacity == capacity);
+    }
+
     /++
     Assume that it is safe to append to this associative array.
     Appends made to this associative array after calling this function may append in place, even if the array was a slice of a larger array to begin with.
@@ -212,7 +300,7 @@ struct StringMap(T, U = uint)
 
     Returns: The input is returned.
     +/
-    ref inout(typeof(this)) assumeSafeAppend() @system nothrow inout return
+    ref inout(typeof(this)) assumeSafeAppend()() @system nothrow inout return
     {
         if (implementation)
         {
@@ -223,10 +311,26 @@ struct StringMap(T, U = uint)
         return this;
     }
 
+    static if (is(T == int))
+    ///
+    unittest
+    {
+        StringMap!double map;
+        map["c"] = 4.0;
+        map["a"] = 3.0;
+        assert(map.capacity >= 2);
+        map.remove("c");
+        assert(map.capacity == 0);
+        map.assumeSafeAppend;
+        assert(map.capacity >= 2);
+    }
+
     /++
     Removes all remaining keys and values from an associative array.
+
+    Complexity: `O(1)`
     +/
-    void clear() @safe pure nothrow @nogc
+    void clear()() @safe pure nothrow @nogc
     {
         if (implementation)
         {
@@ -236,9 +340,26 @@ struct StringMap(T, U = uint)
 
     }
 
+    static if (is(T == int))
+    ///
+    unittest
+    {
+        StringMap!double map;
+        map["c"] = 4.0;
+        map["a"] = 3.0;
+        map.clear;
+        assert(map.length == 0);
+        assert(map.capacity == 0);
+        map.assumeSafeAppend;
+        assert(map.capacity >= 2);
+    }
+
     /++
+    `remove(key)` does nothing if the given key does not exist and returns false. If the given key does exist, it removes it from the AA and returns true.
+
+    Complexity: `O(n)`
     +/
-    bool remove(scope const(char)[] key) @trusted pure nothrow @nogc
+    bool remove()(scope const(char)[] key) @trusted pure nothrow @nogc
     {
         size_t index;
         if (implementation && implementation.findIndex(key, index))
@@ -249,9 +370,29 @@ struct StringMap(T, U = uint)
         return false;
     }
 
+    static if (is(T == int))
+    ///
+    unittest
+    {
+        StringMap!double map;
+        map["a"] = 3.0;
+        map["c"] = 4.0;
+        assert(map.remove("c"));
+        assert(!map.remove("c"));
+        assert(map.remove("a"));
+        assert(map.length == 0);
+        assert(map.capacity == 0);
+        assert(map.assumeSafeAppend.capacity >= 2);
+    }
+
     /++
+    Finds position of the key in the associative array .
+    
+    Return: An index starting from `0` that corresponds to the key or `-1` if the associative array doesn't contain the key.
+
+    Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
     +/
-    ptrdiff_t findPos(scope const(char)[] key) @trusted pure nothrow @nogc const
+    ptrdiff_t findPos()(scope const(char)[] key) @trusted pure nothrow @nogc const
     {
         if (!implementation)
             return -1;
@@ -261,7 +402,29 @@ struct StringMap(T, U = uint)
         return implementation._indices[index];
     }
 
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!double map;
+        map["c"] = 3.0;
+        map["La"] = 4.0;
+        map["a"] = 5.0;
+
+        assert(map.findPos("C") == -1);
+        assert(map.findPos("c") == 0);
+        assert(map.findPos("La") == 1);
+        assert(map.findPos("a") == 2);
+
+        map.remove("c");
+
+        assert(map.findPos("c") == -1);
+        assert(map.findPos("La") == 0);
+        assert(map.findPos("a") == 1);
+    }
+
     /++
+    Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
     +/
     inout(T)* opBinaryRight(string op : "in")(scope const(char)[] key) @system pure nothrow @nogc inout
     {
@@ -273,9 +436,20 @@ struct StringMap(T, U = uint)
         return implementation._values + index;
     }
 
+    static if (is(T == int))
+    ///
+    @system nothrow pure unittest
+    {
+        StringMap!double map;
+        assert(("c" in map) is null);
+        map["c"] = 3.0;
+        assert(*("c" in map) == 3.0);
+    }
+
     /++
+    Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
     +/
-    ref inout(T) opIndex(scope const(char)[] key) @trusted pure inout //@nogc
+    ref inout(T) opIndex()(scope const(char)[] key) @trusted pure inout //@nogc
     {
         size_t index;
         if (implementation && implementation.findIndex(key, index))
@@ -289,9 +463,23 @@ struct StringMap(T, U = uint)
         throw new MirException("No member: ", key);
     }
 
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!double map;
+        map["c"] = 3.0;
+        map["La"] = 4.0;
+        map["a"] = 5.0;
+
+        map["La"] += 10;
+        assert(map["La"] == 14.0);
+    }
+
     /++
+    Complexity: `O(n)`.
     +/
-    ref T opIndexAssign(T value, string key) @trusted pure nothrow
+    ref T opIndexAssign()(T value, string key) @trusted pure nothrow
     {
         size_t index;
         if (_expect(!implementation, false))
@@ -321,12 +509,16 @@ struct StringMap(T, U = uint)
         }
         assert (index <= length);
         implementation.insertAt(key, move(value), index);
+        index = implementation._indices[index];
         return implementation._values[index];
     }
 
     /++
+    Looks up key; if it exists returns corresponding value else evaluates and returns defaultValue.
+
+    Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
     +/
-    inout(T) get(scope const(char)[] key, lazy inout(T) defaultValue)
+    inout(T) get()(scope const(char)[] key, lazy inout(T) defaultValue)
     {
         size_t index;
         if (implementation && implementation.findIndex(key, index))
@@ -339,9 +531,22 @@ struct StringMap(T, U = uint)
         return defaultValue;
     }
 
+    static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!int map;
+        map["c"] = 3;
+        assert(map.get("c", 1) == 3);
+        assert(map.get("C", 1) == 1);
+    }
+
     /++
+    Looks up key; if it exists returns corresponding value else evaluates value, adds it to the associative array and returns it.
+
+    Complexity: `O(n)`
     +/
-    ref T require(string key, lazy T value = T.init)
+    ref T require()(string key, lazy T value = T.init)
     {
         import std.stdio;
         size_t index;
@@ -371,6 +576,7 @@ struct StringMap(T, U = uint)
         }
         assert (index <= length);
         implementation.insertAt(key, value, index);
+        index = implementation.indices[index];
         return implementation.values[index];
     }
 
@@ -378,10 +584,68 @@ struct StringMap(T, U = uint)
     ///
     @safe pure unittest
     {
-        StringMap!int aa = ["aa": 1];
-        assert(aa.require("aa", 0) == 1);
-        assert(aa.require("bb", 0) == 0);
-        assert(aa["bb"] == 0);
+        StringMap!int map = ["aa": 1];
+        int add3(ref int x) { x += 3; return x; }
+        assert(add3(map.require("aa", 10)) == 4);
+        assert(add3(map.require("bb", 10)) == 13);
+        assert(map.require("a", 100));
+        assert(map.require("aa") == 4);
+        assert(map.require("bb") == 13);
+        assert(map.keys == ["aa", "bb", "a"]);
+    }
+
+    /++
+    Converts the associtave array to a common Dlang associative array.
+
+    Complexity: `O(n)`.
+    +/
+    template toAA()
+    {
+        static if (__traits(compiles, (ref const T a) { T b; b = a;}))
+        {
+            ///
+            T[string] toAA()() const
+            {
+                T[string] ret;
+                foreach (i; 0 .. length)
+                {
+                    ret[implementation.keys[i]] = implementation.values[i];
+                }
+                return ret;
+            }
+        }
+        else
+        {
+            ///
+            T[string] toAA()()
+            {
+                T[string] ret;
+                foreach (i; 0 .. length)
+                {
+                    ret[implementation.keys[i]] = implementation.values[i];
+                }
+                return ret;
+            }
+
+            ///
+            const(T)[string] toAA()() const
+            {
+                const(T)[string] ret;
+                foreach (i; 0 .. length)
+                {
+                    ret[implementation.keys[i]] = implementation.values[i];
+                }
+                return ret;
+            }
+        }
+    }
+
+    ///
+    @safe pure nothrow unittest
+    {
+        StringMap!int map = ["k": 1];
+        int[string] aa = map.toAA;
+        assert(aa["k"] == 1);
     }
 }
 
@@ -422,7 +686,7 @@ private struct StructImpl(T, U = uint)
 
     /++
     +/
-    this(string[] keys, T[] values) @trusted pure nothrow
+    this()(string[] keys, T[] values) @trusted pure nothrow
     {
         import mir.array.allocation: array;
         import mir.ndslice.sorting: sort;
@@ -453,27 +717,7 @@ private struct StructImpl(T, U = uint)
         }
     }
 
-    // this(size_t length, size_t maxKeyLength)
-    // {
-    //     assert(length);
-    //     _length = length;
-    //     _keys = new string[length].ptr;
-    //     _values = new T[length].ptr;
-    //     _indices = new U[length].assumeSafeAppend;
-    //     _lengthTable = new U[maxKeyLength + 2].assumeSafeAppend;
-    // }
-
-    // void addFirstEntry(string key, T value)
-    // {
-    //     import core.lifetime: move;
-    //     pragma(inline, false);
-    //     __ctor(1, key.length);
-    //     keys[0] = key;
-    //     values[0] = move(value);
-    //     lengthTable[key.length + 1 .. $] = 1;
-    // }
-
-    void insertAt(string key, T value, size_t i) @trusted
+    void insertAt()(string key, T value, size_t i) @trusted
     {
         pragma(inline, false);
 
@@ -525,7 +769,7 @@ private struct StructImpl(T, U = uint)
         }
     }
 
-    void removeAt(size_t i)
+    void removeAt()(size_t i)
     {
         assert(i < length);
         auto j = _indices[i];
@@ -570,41 +814,41 @@ private struct StructImpl(T, U = uint)
             }
         }
         _length--;
-        _lengthTable = _lengthTable[0 .. length ? _keys[length - 1].length + 2 : 0];
+        _lengthTable = _lengthTable[0 .. length ? _keys[_indices[length - 1]].length + 2 : 0];
     }
 
-    size_t length() @safe pure nothrow @nogc const @property
+    size_t length()() @safe pure nothrow @nogc const @property
     {
         return _length;
     }
 
-    inout(string)[] keys() @trusted inout @property
+    inout(string)[] keys()() @trusted inout @property
     {
         return _keys[0 .. _length];
     }
     
-    inout(T)[] values() @trusted inout @property
+    inout(T)[] values()() @trusted inout @property
     {
         return _values[0 .. _length];
     }
     
-    inout(U)[] indices() @trusted inout @property
+    inout(U)[] indices()() @trusted inout @property
     {
         return _indices[0 .. _length];
     }
     
-    inout(U)[] lengthTable() @trusted inout @property
+    inout(U)[] lengthTable()() @trusted inout @property
     {
         return _lengthTable;
     }
 
-    auto sortedKeys() @trusted const @property
+    auto sortedKeys()() @trusted const @property
     {
         import mir.ndslice.topology: indexed;
         return _keys.indexed(indices);
     }
 
-    bool findIndex(scope const(char)[] key, ref size_t index) @trusted pure nothrow @nogc const
+    bool findIndex()(scope const(char)[] key, ref size_t index) @trusted pure nothrow @nogc const
     {
         import mir.utility: _expect;
         if (_expect(key.length + 1 < _lengthTable.length, true))

From 6f4cb75a7ec133f1c6f3b270e9ee9100a8ce227f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 11 Apr 2021 11:28:21 +0700
Subject: [PATCH 189/573] add isStringMap alias

---
 source/mir/string_map.d | 51 ++++++++++++++++++++++++++---------------
 1 file changed, 33 insertions(+), 18 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index c51cb48c..f942ff6a 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -8,6 +8,20 @@ module mir.string_map;
 
 import std.traits;
 
+/++
+Checks if the type is instance of $(LREF StringMap).
++/
+enum isStringMap(T) = is(Unqual!T == StringMap!V, V);
+
+version(mir_test) 
+///
+unittest
+{
+    static assert(isStringMap!(StringMap!int));
+    static assert(isStringMap!(const StringMap!int));
+    static assert(!isStringMap!int);
+}
+
 /++
 Ordered string-value associtaive array with extremely fast lookup.
 
@@ -35,7 +49,7 @@ struct StringMap(T, U = uint)
     //         return implementation is null;
     //     }
 
-    //     static if (is(T == int))
+    //     version(mir_test) static if (is(T == int))
     //     ///
     //     @safe pure unittest
     //     {
@@ -57,7 +71,7 @@ struct StringMap(T, U = uint)
         return this;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -73,7 +87,7 @@ struct StringMap(T, U = uint)
         implementation = null;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     /// Usefull for default funcion argument.
     @safe pure unittest
     {
@@ -89,7 +103,7 @@ struct StringMap(T, U = uint)
         this(aa.keys, aa.values);
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -112,7 +126,7 @@ struct StringMap(T, U = uint)
         implementation = new Impl(keys, values);
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -131,7 +145,7 @@ struct StringMap(T, U = uint)
         return implementation ? implementation.length : 0;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -160,7 +174,7 @@ struct StringMap(T, U = uint)
         return implementation ? implementation.keys : null;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -189,7 +203,7 @@ struct StringMap(T, U = uint)
         return implementation ? implementation.values : null;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -226,7 +240,7 @@ struct StringMap(T, U = uint)
         );
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     unittest
     {
@@ -273,7 +287,7 @@ struct StringMap(T, U = uint)
         );
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     unittest
     {
@@ -311,7 +325,7 @@ struct StringMap(T, U = uint)
         return this;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     unittest
     {
@@ -340,7 +354,7 @@ struct StringMap(T, U = uint)
 
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     unittest
     {
@@ -370,7 +384,7 @@ struct StringMap(T, U = uint)
         return false;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     unittest
     {
@@ -402,7 +416,7 @@ struct StringMap(T, U = uint)
         return implementation._indices[index];
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -436,7 +450,7 @@ struct StringMap(T, U = uint)
         return implementation._values + index;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @system nothrow pure unittest
     {
@@ -463,7 +477,7 @@ struct StringMap(T, U = uint)
         throw new MirException("No member: ", key);
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -531,7 +545,7 @@ struct StringMap(T, U = uint)
         return defaultValue;
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -580,7 +594,7 @@ struct StringMap(T, U = uint)
         return implementation.values[index];
     }
 
-    static if (is(T == int))
+    version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
     {
@@ -649,6 +663,7 @@ struct StringMap(T, U = uint)
     }
 }
 
+version(mir_test) 
 ///
 unittest
 {

From 8c5dc324b1f9645b01e5bdce57c55c84b663b152 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 11 Apr 2021 18:42:00 +0700
Subject: [PATCH 190/573] fixup links

---
 source/mir/algebraic_alias/json.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index f8408cd7..d9ed72d7 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -3,7 +3,7 @@ $(H1 Mutable JSON value)
 
 This module contains a single alias definition and doesn't provide JSON serialization API.
 
-See_also: Json libraries $(GMREF mir-ion, index) and $(GMREF asdf, index);
+See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf);
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 Authors: Ilya Yaroshenko 

From 24820aed379a3d83d8b37f692351fc1429adcfd3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 11 Apr 2021 21:02:48 +0700
Subject: [PATCH 191/573] fixup docs

---
 source/mir/string_map.d | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index f942ff6a..f294f393 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -371,7 +371,7 @@ struct StringMap(T, U = uint)
     /++
     `remove(key)` does nothing if the given key does not exist and returns false. If the given key does exist, it removes it from the AA and returns true.
 
-    Complexity: `O(n)`
+    Complexity: `O(log(s))` (not exist) or `O(n)` (exist), where `s` is the count of the strings with the same length as they key.
     +/
     bool remove()(scope const(char)[] key) @trusted pure nothrow @nogc
     {
@@ -491,7 +491,7 @@ struct StringMap(T, U = uint)
     }
 
     /++
-    Complexity: `O(n)`.
+    Complexity: `O(log(s))` (exist) or `O(n)` (not exist), where `s` is the count of the strings with the same length as they key.
     +/
     ref T opIndexAssign()(T value, string key) @trusted pure nothrow
     {
@@ -558,7 +558,7 @@ struct StringMap(T, U = uint)
     /++
     Looks up key; if it exists returns corresponding value else evaluates value, adds it to the associative array and returns it.
 
-    Complexity: `O(n)`
+    Complexity: `O(log(s))` (exist) or `O(n)` (not exist), where `s` is the count of the strings with the same length as they key.
     +/
     ref T require()(string key, lazy T value = T.init)
     {

From 2e18a78a908112929400cf5683274bd79ecffde7 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 12 Apr 2021 23:06:47 +0700
Subject: [PATCH 192/573] turn RCArray asSlice and moveToSlice to templates

---
 source/mir/rc/array.d | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 8d698c2f..2589554f 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -130,7 +130,7 @@ struct mir_rcarray(T)
     }
 
     ///
-    auto asSlice() @property
+    auto asSlice()() @property
     {
         import mir.ndslice.slice: mir_slice;
         alias It = mir_rci!T;
@@ -138,7 +138,7 @@ struct mir_rcarray(T)
     }
 
     ///
-    auto asSlice() const @property
+    auto asSlice()() const @property
     {
         import mir.ndslice.slice: mir_slice;
         alias It = mir_rci!(const T);
@@ -146,7 +146,7 @@ struct mir_rcarray(T)
     }
 
     ///
-    auto asSlice() immutable @property
+    auto asSlice()() immutable @property
     {
         import mir.ndslice.slice: mir_slice;
         alias It = mir_rci!(immutable T);
@@ -154,7 +154,7 @@ struct mir_rcarray(T)
     }
 
     ///
-    auto moveToSlice() @property
+    auto moveToSlice()() @property
     {
         import core.lifetime: move;
         import mir.ndslice.slice: mir_slice;

From 8f8edcb02f647981a850045607a5d65ce5e6679c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 16 Apr 2021 18:21:50 +0700
Subject: [PATCH 193/573] fixup

---
 source/mir/serde.d | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 589634b8..aa205680 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -649,7 +649,7 @@ template serdeDeserializationMemberType(T, string member)
 {
     import std.traits: Unqual, Parameters;
     T* aggregate;
-    static if (isField!(T, member))
+    static if (hasField!(T, member))
     {
         alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member));
     }
@@ -706,7 +706,7 @@ template serdeSerializationMemberType(T, string member)
 {
     import std.traits: Unqual, Parameters;
     T* aggregate;
-    static if (isField!(T, member))
+    static if (hasField!(T, member))
     {
         alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member));
     }
@@ -1583,7 +1583,7 @@ struct SerdeOrderedDummy(T, bool __optionalByDefault = false)
     {
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
         {
-            static if (isField!(T, member))
+            static if (hasField!(T, member))
             {
                 static if (__traits(compiles, {__traits(getMember, this, member) = __traits(getMember, value, member);}))
                     __traits(getMember, this, member) = __traits(getMember, value, member);
@@ -1595,7 +1595,7 @@ public:
 
     static foreach (i, member; serdeFinalProxyDeserializableMembers!T)
     {
-        static if (isField!(T, member))
+        static if (hasField!(T, member))
         {
             static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
             {
@@ -1817,7 +1817,7 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
             __traits(getMember, value, member) = to!(serdeDeserializationMemberType!(T, member))(move(temporal));
         }
         else
-        static if (isField!(T, member))
+        static if (hasField!(T, member))
         {
             if (auto exc = impl(data, __traits(getMember, value, member), context))
                 return exc;
@@ -1833,7 +1833,7 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
         static if (hasUDA!(__traits(getMember, value, member), serdeTransformIn))
         {
             alias transform = serdeGetTransformIn!(__traits(getMember, value, member));
-            static if (isField!(T, member))
+            static if (hasField!(T, member))
             {
                 transform(__traits(getMember, value, member));
             }

From 21821b0f9c13b10a102704f374a8b54da7d7f93a Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Tue, 20 Apr 2021 16:47:59 +0700
Subject: [PATCH 194/573] Add timestamp and lob (#342)

---
 index.d                |   2 +
 meson.build            |   2 +
 source/mir/lob.d       |  22 +
 source/mir/timestamp.d | 930 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 956 insertions(+)
 create mode 100644 source/mir/lob.d
 create mode 100644 source/mir/timestamp.d

diff --git a/index.d b/index.d
index 721a6046..32125604 100644
--- a/index.d
+++ b/index.d
@@ -21,8 +21,10 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,small_array)) $(TD Generic Small Arrays))
     $(TR $(TDNW $(MREF mir,small_string)) $(TD Generic Small Strings))
     $(TR $(TDNW $(MREF mir,string_map)) $(TD Ordered string-value associative array with fast lookup))
+    $(TR $(TDNW $(MREF mir,lob)) $(TD Binar and Char Large Objects ))
     $(LEADINGROW Date and time)
     $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability))
+    $(TR $(TDNW $(MREF mir,timestamp)) $(TD Timestamp ))
     $(LEADINGROW NDarrays and Algorithms)
     $(TR $(TDNW $(MREF mir,algorithm,iteration)) $(TD Mir & BetterC rework of Phobos.))
     $(TR $(TDNW $(MREF mir,algorithm,setops)) $(TD Mir & BetterC rework of Phobos.))
diff --git a/meson.build b/meson.build
index 341d8de1..5310695b 100644
--- a/meson.build
+++ b/meson.build
@@ -36,6 +36,7 @@ sources_list = [
     'mir/interpolate/polynomial',
     'mir/interpolate/spline',
     'mir/interpolate/utility',
+    'mir/lob',
     'mir/math/func/expdigamma',
     'mir/math/numeric',
     'mir/math/stat',
@@ -71,6 +72,7 @@ sources_list = [
     'mir/small_array',
     'mir/small_string',
     'mir/string_map',
+    'mir/timestamp',
     'mir/type_info',
 ]
 
diff --git a/source/mir/lob.d b/source/mir/lob.d
new file mode 100644
index 00000000..82a7ebe0
--- /dev/null
+++ b/source/mir/lob.d
@@ -0,0 +1,22 @@
+/++
++/
+module mir.lob;
+
+/++
+Values of type clob are encoded as a sequence of octets that should be interpreted as text
+with an unknown encoding (and thus opaque to the application).
++/
+struct Clob
+{
+    ///
+    const(char)[] data;
+}
+
+/++
+This is a sequence of octets with no interpretation (and thus opaque to the application).
++/
+struct Blob
+{
+    ///
+    const(ubyte)[] data;
+}
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
new file mode 100644
index 00000000..1605b79a
--- /dev/null
+++ b/source/mir/timestamp.d
@@ -0,0 +1,930 @@
+/++
+Timestamp
++/
+module mir.timestamp;
+
+private alias isDigit = (dchar c) => uint(c - '0') < 10;
+
+version(D_Exceptions)
+///
+class DateTimeException : Exception
+{
+    ///
+    @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
+    {
+        super(msg, file, line, nextInChain);
+    }
+
+    /// ditto
+    @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
+    {
+        super(msg, file, line, nextInChain);
+    }
+}
+
+version(D_Exceptions)
+{
+    private static immutable InvalidMonth = new DateTimeException("Invalid Month");
+    private static immutable InvalidDay = new DateTimeException("Invalid Day");
+    private static immutable InvalidISOString = new DateTimeException("Invalid ISO String");
+    private static immutable InvalidISOExtendedString = new DateTimeException("Invalid ISO Extended String");
+    private static immutable InvalidString = new DateTimeException("Invalid String");
+}
+
+/++
+Timestamp
+
+Note: The component values in the binary encoding are always in UTC, while components in the text encoding are in the local time!
+This means that transcoding requires a conversion between UTC and local time.
+
+`Timestamp` precision is up to `10^-12` seconds;
++/
+struct Timestamp
+{
+    import std.traits: isSomeChar;
+
+    ///
+    enum Precision : ubyte
+    {
+        ///
+        year,
+        ///
+        month,
+        ///
+        day,
+        ///
+        minute,
+        ///
+        second,
+        ///
+        fraction,
+    }
+
+    ///
+    this(scope const(char)[] str) @safe pure @nogc
+    {
+        this = fromString(str);
+    }
+
+    ///
+    version (mir_test)
+    @safe pure @nogc unittest
+    {
+        assert(Timestamp("2010-07-04") == Timestamp(2010, 7, 4));
+        assert(Timestamp("20100704") == Timestamp(2010, 7, 4));
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
+        static assert(Timestamp(2021, 01, 29,  4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30"));
+    }
+
+    version(all)
+    {
+        short offset;
+    }
+    else
+    /+
+    If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of “-00:00”.
+    This differs semantically from an offset of “Z” or “+00:00”, which imply that UTC is the preferred reference point for the specified time.
+    RFC2822 describes a similar convention for email.
+    private short _offset;
+    +/
+    {
+
+        /++
+        Timezone offset in minutes
+        +/
+        short offset() const @safe pure nothrow @nogc @property
+        {
+            return _offset >> 1;
+        }
+
+        /++
+        Returns: true if timezone has offset
+        +/
+        bool hasOffset() const @safe pure nothrow @nogc @property
+        {
+            return _offset & 1;
+        }
+    }
+
+    /++
+    Year
+    +/
+    short year;
+    /++
+    +/
+    Precision precision;
+
+    /++
+    Month
+    
+    If the value equals to thero then this and all the following members are undefined.
+    +/
+    ubyte month;
+    /++
+    Day
+    
+    If the value equals to thero then this and all the following members are undefined.
+    +/
+    ubyte day;
+    /++
+    Hour
+    +/
+    ubyte hour;
+
+    version(D_Ddoc)
+    {
+    
+        /++
+        Minute
+
+        Note: the field is implemented as property.
+        +/
+        ubyte minute;
+        /++
+        Second
+
+        Note: the field is implemented as property.
+        +/
+        ubyte second;
+        /++
+        Fraction
+
+        The `fraction_exponent` and `fraction_coefficient` denote the fractional seconds of the timestamp as a decimal value
+        The fractional seconds’ value is `coefficient * 10 ^ exponent`.
+        It must be greater than or equal to zero and less than 1.
+        A missing coefficient defaults to zero.
+        Fractions whose coefficient is zero and exponent is greater than -1 are ignored.
+        
+        'fractionCoefficient' allowed values are [0 ... 10^12-1].
+        'fractionExponent' allowed values are [-12 ... 0].
+
+        Note: the fields are implemented as property.
+        +/
+        byte fractionExponent;
+        /// ditto
+        long fractionCoefficient;
+    }
+    else
+    {
+        import mir.bitmanip: bitfields;
+        version (LittleEndian)
+        {
+
+            mixin(bitfields!(
+                    ubyte, "minute", 8,
+                    ubyte, "second", 8,
+                    byte, "fractionExponent", 8,
+                    long, "fractionCoefficient", 40,
+            ));
+        }
+        else
+        {
+            mixin(bitfields!(
+                    long, "fractionCoefficient", 40,
+                    byte, "fractionExponent", 8,
+                    ubyte, "second", 8,
+                    ubyte, "minute", 8,
+            ));
+        }
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    this(short year)
+    {
+        this.year = year;
+        this.precision = Precision.year;
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    this(short year, ubyte month)
+    {
+        this.year = year;
+        this.month = month;
+        this.precision = Precision.month;
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    this(short year, ubyte month, ubyte day)
+    {
+        this.year = year;
+        this.month = month;
+        this.day = day;
+        this.precision = Precision.day;
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute)
+    {
+        this.year = year;
+        this.month = month;
+        this.day = day;
+        this.hour = hour;
+        this.minute = minute;
+        this.precision = Precision.minute;
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute, ubyte second)
+    {
+        this.year = year;
+        this.month = month;
+        this.day = day;
+        this.hour = hour;
+        this.day = day;
+        this.minute = minute;
+        this.second = second;
+        this.precision = Precision.second;
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute, ubyte second, byte fractionExponent, ulong fractionCoefficient)
+    {
+        this.year = year;
+        this.month = month;
+        this.day = day;
+        this.hour = hour;
+        this.day = day;
+        this.minute = minute;
+        this.second = second;
+        assert(fractionExponent < 0);
+        this.fractionExponent = fractionExponent;
+        this.fractionCoefficient = fractionCoefficient;
+        this.precision = Precision.fraction;
+    }
+
+    /++
+    Attaches local offset, doesn't adjust other fields.
+    Local-time offsets may be represented as either `hour*60+minute` offsets from UTC,
+    or as the zero to denote a local time of UTC. They are required on timestamps with time and are not allowed on date values.
+    +/
+    @safe pure nothrow @nogc const
+    Timestamp withOffset(short minutes)
+    {
+        assert(-24 * 60 <= minutes && minutes <= 24 * 60, "Offset absolute value should be less or equal to 24 * 60");
+        assert(precision >= Precision.minute, "Offsets are not allowed on date values.");
+        Timestamp ret = this;
+        ret.offset = minutes;
+        return ret;
+    }
+
+    version(D_BetterC){} else
+    private string toStringImpl(alias fun)() const @safe pure nothrow
+    {
+        import mir.appender: UnsafeArrayBuffer;
+        char[64] buffer = void;
+        auto w = UnsafeArrayBuffer!char(buffer);
+        fun(w);
+        return w.data.idup;
+    }
+
+    /++
+    Converts this $(LREF Timestamp) to a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`.
+
+    If `w` writer is set, the resulting string will be written directly
+    to it.
+
+    Returns:
+        A `string` when not using an output range; `void` otherwise.
+    +/
+    alias toISOExtString = toISOStringImp!true;
+
+    ///
+    version (mir_test)
+    @safe pure nothrow unittest
+    {
+        assert(Timestamp.init.toString == "0000T");
+        assert(Timestamp(2010, 7, 4).toString == "2010-07-04");
+        assert(Timestamp(1998, 12, 25).toString == "1998-12-25");
+        assert(Timestamp(0, 1, 5).toString == "0000-01-05");
+        assert(Timestamp(-4, 1, 5).toString == "-0004-01-05");
+
+        // YYYY-MM-DDThh:mm:ss±hh:mm
+        assert(Timestamp(2021).toString == "2021T");
+        assert(Timestamp(2021, 01).toString == "2021-01T", Timestamp(2021, 01).toString);
+        assert(Timestamp(2021, 01, 29).toString == "2021-01-29");
+        assert(Timestamp(2021, 01, 29, 19, 42).toString == "2021-01-29T19:42Z");
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString == "2021-01-29T19:42:44+07", Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString);
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toString == "2021-01-29T20:12:44+07:30");
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        // Test A.D.
+        assert(Timestamp(9, 12, 4).toISOExtString == "0009-12-04");
+        assert(Timestamp(99, 12, 4).toISOExtString == "0099-12-04");
+        assert(Timestamp(999, 12, 4).toISOExtString == "0999-12-04");
+        assert(Timestamp(9999, 7, 4).toISOExtString == "9999-07-04");
+        assert(Timestamp(10000, 10, 20).toISOExtString == "+10000-10-20");
+
+        // Test B.C.
+        assert(Timestamp(0, 12, 4).toISOExtString == "0000-12-04");
+        assert(Timestamp(-9, 12, 4).toISOExtString == "-0009-12-04");
+        assert(Timestamp(-99, 12, 4).toISOExtString == "-0099-12-04");
+        assert(Timestamp(-999, 12, 4).toISOExtString == "-0999-12-04");
+        assert(Timestamp(-9999, 7, 4).toISOExtString == "-9999-07-04");
+        assert(Timestamp(-10000, 10, 20).toISOExtString == "-10000-10-20");
+
+        const cdate = Timestamp(1999, 7, 6);
+        immutable idate = Timestamp(1999, 7, 6);
+        assert(cdate.toISOExtString == "1999-07-06");
+        assert(idate.toISOExtString == "1999-07-06");
+    }
+
+    /// ditto
+    alias toString = toISOExtString;
+
+    /++
+    Converts this $(LREF Timestamp) to a string with the format `YYYYMMDDThhmmss±hhmm`.
+
+    If `w` writer is set, the resulting string will be written directly
+    to it.
+
+    Returns:
+        A `string` when not using an output range; `void` otherwise.
+    +/
+    alias toISOString = toISOStringImp!false;
+
+    ///
+    version (mir_test)
+    @safe pure nothrow unittest
+    {
+        assert(Timestamp.init.toISOString == "0000T");
+        assert(Timestamp(2010, 7, 4).toISOString == "20100704");
+        assert(Timestamp(1998, 12, 25).toISOString == "19981225");
+        assert(Timestamp(0, 1, 5).toISOString == "00000105");
+        assert(Timestamp(-4, 1, 5).toISOString == "-00040105");
+
+        // YYYYMMDDThhmmss±hhmm
+        assert(Timestamp(2021).toISOString == "2021T");
+        assert(Timestamp(2021, 01).toISOString == "2021-01T"); // always extended
+        assert(Timestamp(2021, 01, 29).toISOString == "20210129");
+        assert(Timestamp(2021, 01, 29, 19, 42).toISOString == "20210129T1942Z");
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toISOString == "20210129T194244+07");
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
+        static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
+    }
+
+    /// Helpfer for time zone offsets
+    void addMinutes(short minutes) @safe pure nothrow @nogc
+    {
+        int totalMinutes = minutes + (this.minute + this.hour * 60u);
+        auto h = totalMinutes / 60;
+
+        int dayShift;
+
+        while (totalMinutes < 0)
+        {
+            totalMinutes += 24 * 60;
+            dayShift--;
+        }
+
+        while (totalMinutes >= 24 * 60)
+        {
+            totalMinutes -= 24 * 60;
+            dayShift++;
+        }
+
+        if (dayShift)
+        {
+            import mir.date: Date;
+            auto ymd = (Date.trustedCreate(year, month, day) + dayShift).yearMonthDay;
+            year = ymd.year;
+            month = cast(ubyte)ymd.month;
+            day = ymd.day;
+        }
+
+        hour = cast(ubyte) (totalMinutes / 60);
+        minute = cast(ubyte) (totalMinutes % 60);
+    }
+
+    template toISOStringImp(bool ext)
+    {
+        version(D_BetterC){} else
+        string toISOStringImp() const @safe pure nothrow
+        {
+            return toStringImpl!toISOStringImp;
+        }
+
+        /// ditto
+        void toISOStringImp(W)(scope ref W w) const scope
+            // if (isOutputRange!(W, char))
+        {
+            import mir.format: printZeroPad;
+            // YYYY-MM-DDThh:mm:ss±hh:mm
+            Timestamp t = this;
+
+            if (t.offset)
+            {
+                assert(-24 * 60 <= t.offset && t.offset <= 24 * 60, "Offset absolute value should be less or equal to 24 * 60");
+                assert(precision >= Precision.minute, "Offsets are not allowed on date values.");
+                t.addMinutes(t.offset);
+            }
+
+            if (t.year >= 10_000)
+                w.put('+');
+            printZeroPad(w, t.year, t.year >= 0 ? t.year < 10_000 ? 4 : 5 : t.year > -10_000 ? 5 : 6);
+            if (precision == Precision.year)
+            {
+                w.put('T');
+                return;
+            }
+            if (ext || precision == Precision.month) w.put('-');
+
+            printZeroPad(w, cast(uint)t.month, 2);
+            if (precision == Precision.month)
+            {
+                w.put('T');
+                return;
+            }
+            static if (ext) w.put('-');
+
+            printZeroPad(w, t.day, 2);
+            if (precision == Precision.day)
+                return;
+            w.put('T');
+
+            printZeroPad(w, t.hour, 2);
+            static if (ext) w.put(':');
+            printZeroPad(w, t.minute, 2);
+
+            if (precision >= Precision.second)
+            {
+                static if (ext) w.put(':');
+                printZeroPad(w, t.second, 2);
+
+                if (precision > Precision.second && (t.fractionExponent < 0 || t.fractionCoefficient))
+                {
+                    w.put('.');
+                    printZeroPad(w, t.fractionCoefficient, -int(t.fractionExponent));
+                }
+            }
+
+            if (t.offset == 0)
+            {
+                w.put('Z');
+                return;
+            }
+
+            bool sign = t.offset < 0;
+            uint absoluteOffset = !sign ? t.offset : -int(t.offset);
+            uint offsetHour = absoluteOffset / 60u;
+            uint offsetMinute = absoluteOffset % 60u;
+
+            w.put(sign ? '-' : '+');
+            printZeroPad(w, offsetHour, 2);
+            if (offsetMinute)
+            {
+                static if (ext) w.put(':');
+                printZeroPad(w, offsetMinute, 2);
+            }
+        }
+    }
+
+    /++
+    Creates a $(LREF Timestamp) from a string with the format `YYYYMMDDThhmmss±hhmm
+    or its leading part allowed by the standard.
+
+    or its leading part allowed by the standard.
+
+    Params:
+        str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates.
+        value = (optional) result value.
+
+    Throws:
+        $(LREF DateTimeException) if the given string is
+        not in the correct format. Two arguments overload is `nothrow`.
+    Returns:
+        `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload.
+    +/
+    alias fromISOString = fromISOStringImpl!false;
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(Timestamp.fromISOString("20100704") == Timestamp(2010, 7, 4));
+        assert(Timestamp.fromISOString("19981225") == Timestamp(1998, 12, 25));
+        assert(Timestamp.fromISOString("00000105") == Timestamp(0, 1, 5));
+        // assert(Timestamp.fromISOString("-00040105") == Timestamp(-4, 1, 5));
+
+        assert(Timestamp(2021) == Timestamp.fromISOString("2021"));
+        assert(Timestamp(2021) == Timestamp.fromISOString("2021T"));
+        // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01"));
+        // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01T"));
+        assert(Timestamp(2021, 01, 29) == Timestamp.fromISOString("20210129"));
+        assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942"));
+        assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942Z"));
+        assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOString("20210129T194212"));
+        assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOString("20210129T194212.067Z"));
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOString("20210129T194244+07"));
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
+        static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
+        static assert(Timestamp(2021, 01, 29,  4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOString("20210128T211244-0730"));
+    }
+
+    version (mir_test)
+    @safe unittest
+    {
+        import std.exception: assertThrown;
+        assertThrown!DateTimeException(Timestamp.fromISOString(""));
+        assertThrown!DateTimeException(Timestamp.fromISOString("990704"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("0100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010070"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("120100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("-0100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("+0100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010070a"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("20100a04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010a704"));
+
+        assertThrown!DateTimeException(Timestamp.fromISOString("99-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-0"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("12010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("-010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("+010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-0a"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-0a-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-a7-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010/07/04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010/7/04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010/7/4"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010/07/4"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-7-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-7-4"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-4"));
+
+        assertThrown!DateTimeException(Timestamp.fromISOString("99Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010Jul0"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("12010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("-010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("+010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010Jul0a"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010Jua04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010aul04"));
+
+        assertThrown!DateTimeException(Timestamp.fromISOString("99-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-0"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("12010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("-010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("+010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-0a"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jua-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jal-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-aul-04"));
+
+        // assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-04"));
+
+        assert(Timestamp.fromISOString("19990706") == Timestamp(1999, 7, 6));
+        // assert(Timestamp.fromISOString("-19990706") == Timestamp(-1999, 7, 6));
+        // assert(Timestamp.fromISOString("+019990706") == Timestamp(1999, 7, 6));
+        assert(Timestamp.fromISOString("19990706") == Timestamp(1999, 7, 6));
+    }
+
+    // bug# 17801
+    version (mir_test)
+    @safe unittest
+    {
+        import std.conv : to;
+        import std.meta : AliasSeq;
+        static foreach (C; AliasSeq!(char, wchar, dchar))
+        {
+            static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+                assert(Timestamp.fromISOString(to!S("20121221")) == Timestamp(2012, 12, 21));
+        }
+    }
+
+    /++
+    Creates a $(LREF Timestamp) from a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`
+    or its leading part allowed by the standard.
+
+
+    Params:
+        str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates.
+        value = (optional) result value.
+
+    Throws:
+        $(LREF DateTimeException) if the given string is
+        not in the correct format. Two arguments overload is `nothrow`.
+    Returns:
+        `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload.
+    +/
+    alias fromISOExtString = fromISOStringImpl!true;
+
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(Timestamp.fromISOExtString("2010-07-04") == Timestamp(2010, 7, 4));
+        assert(Timestamp.fromISOExtString("1998-12-25") == Timestamp(1998, 12, 25));
+        assert(Timestamp.fromISOExtString("0000-01-05") == Timestamp(0, 1, 5));
+        assert(Timestamp.fromISOExtString("-0004-01-05") == Timestamp(-4, 1, 5));
+
+        assert(Timestamp(2021) == Timestamp.fromISOExtString("2021"));
+        assert(Timestamp(2021) == Timestamp.fromISOExtString("2021T"));
+        assert(Timestamp(2021, 01) == Timestamp.fromISOExtString("2021-01"));
+        assert(Timestamp(2021, 01) == Timestamp.fromISOExtString("2021-01T"));
+        assert(Timestamp(2021, 01, 29) == Timestamp.fromISOExtString("2021-01-29"));
+        assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42"));
+        assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42Z"));
+        assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOExtString("2021-01-29T19:42:12"));
+        assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOExtString("2021-01-29T19:42:12.067Z"));
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOExtString("2021-01-29T19:42:44+07"));
+        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30"));
+        static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30"));
+        static assert(Timestamp(2021, 01, 29,  4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30"));
+    }
+
+    version (mir_test)
+    @safe unittest
+    {
+        import std.exception: assertThrown;
+
+        assertThrown!DateTimeException(Timestamp.fromISOExtString(""));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("990704"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("0100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("120100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("-0100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("+0100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010070a"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("20100a04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010a704"));
+
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("99-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-0"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("12010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("-010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("+010-07-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-0a"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-0a-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-a7-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/07/04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/7/04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/7/4"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/07/4"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-7-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-7-4"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-4"));
+
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("99Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jul0"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("12010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("-010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("+010Jul04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jul0a"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jua04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010aul04"));
+
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("99-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-0"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("12010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("-010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("+010-Jul-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-0a"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jua-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jal-04"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-aul-04"));
+
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("20100704"));
+        assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-04"));
+
+        assert(Timestamp.fromISOExtString("1999-07-06") == Timestamp(1999, 7, 6));
+        assert(Timestamp.fromISOExtString("-1999-07-06") == Timestamp(-1999, 7, 6));
+        assert(Timestamp.fromISOExtString("+01999-07-06") == Timestamp(1999, 7, 6));
+    }
+
+    // bug# 17801
+    version (mir_test)
+    @safe unittest
+    {
+        import std.conv : to;
+        import std.meta : AliasSeq;
+        static foreach (C; AliasSeq!(char, wchar, dchar))
+        {
+            static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+                assert(Timestamp.fromISOExtString(to!S("2012-12-21")) == Timestamp(2012, 12, 21));
+        }
+    }
+
+    /++
+    Creates a $(LREF Timestamp) from a string with the format YYYY-MM-DD, YYYYMMDD, or YYYY-Mon-DD.
+
+    Params:
+        str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) and $(LREF .Timestamp.toISOString) format dates. The function is case sensetive.
+        value = (optional) result value.
+
+    Throws:
+        $(LREF DateTimeException) if the given string is
+        not in the correct format. Two arguments overload is `nothrow`.
+    Returns:
+        `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload.
+    +/
+    static bool fromString(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc
+    {
+        return fromISOExtString(str, value)
+            || fromISOString(str, value);
+    }
+
+    ///
+    version (mir_test)
+    @safe pure @nogc unittest
+    {
+        assert(Timestamp.fromString("2010-07-04") == Timestamp(2010, 7, 4));
+        assert(Timestamp.fromString("20100704") == Timestamp(2010, 7, 4));
+    }
+
+    /// ditto
+    static Timestamp fromString(C)(scope const(C)[] str) @safe pure
+        if (isSomeChar!C)
+    {
+        Timestamp ret;
+        if (fromString(str, ret))
+            return ret;
+        throw InvalidString;
+    }
+
+    template fromISOStringImpl(bool ext)
+    {
+        static Timestamp fromISOStringImpl(C)(scope const(C)[] str) @safe pure
+            if (isSomeChar!C)
+        {
+            Timestamp ret;
+            if (fromISOStringImpl(str, ret))
+                return ret;
+            throw InvalidISOExtendedString;
+        }
+
+        static bool fromISOStringImpl(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc
+            if (isSomeChar!C)
+        {
+            import mir.parse: fromString, parse;
+
+            // YYYY
+            static if (ext)
+            {{
+                auto startIsDigit = str.length && str[0].isDigit;
+                auto strOldLength = str.length;
+                if (!parse(str, value.year))
+                    return false;
+                auto l = strOldLength - str.length;
+                if ((l == 4) != startIsDigit)
+                    return false;
+            }}
+            else
+            {
+                if (str.length < 4 || !str[0].isDigit || !fromString(str[0 .. 4], value.year))
+                    return false;
+                str = str[4 .. $];
+            }
+
+            value.precision = Precision.year;
+            if (str.length == 0 || str == "T")
+                return true;
+            
+            static if (ext)
+            {
+                if (str[0] != '-')
+                    return false;
+                str = str[1 .. $];
+            }
+
+            // MM
+            if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.month))
+                return false;
+            str = str[2 .. $];
+            value.precision = Precision.month;
+            if (str.length == 0 || str == "T")
+                return ext;
+
+            static if (ext)
+            {
+                if (str[0] != '-')
+                    return false;
+                str = str[1 .. $];
+            }
+
+            // DD
+            if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.day))
+                return false;
+            str = str[2 .. $];
+            value.precision = Precision.day;
+            if (str.length == 0)
+                return true;
+
+            // T
+            if (str[0] != 'T')
+                return false;
+            str = str[1 .. $];
+            if (str.length == 0)
+                return true;
+
+            // hh
+            if (str.length < 4 + ext || !str[0].isDigit || !fromString(str[0 .. 2], value.hour))
+                return false;
+            str = str[2 .. $];
+
+            static if (ext)
+            {
+                if (str[0] != ':')
+                    return false;
+                str = str[1 .. $];
+            }
+
+            // mm
+            {
+                uint minute;
+                if (!str[0].isDigit || !fromString(str[0 .. 2], minute))
+                    return false;
+                value.minute = cast(ubyte) minute;
+                str = str[2 .. $];
+                value.precision = Precision.minute;
+                if (str.length == 0)
+                    return true;
+            }
+
+            static if (ext)
+            {
+                if (str[0] != ':')
+                    goto TZ;
+                str = str[1 .. $];
+            }
+
+            // ss
+            {
+                uint second;
+                if (str.length < 2 || !str[0].isDigit)
+                    goto TZ;
+                if (!fromString(str[0 .. 2], second))
+                    return false;
+                value.second = cast(ubyte) second;
+                str = str[2 .. $];
+                value.precision = Precision.second;
+                if (str.length == 0)
+                    return true;
+            }
+
+            // .
+            if (str[0] != '.')
+                goto TZ;
+            str = str[1 .. $];
+            value.precision = Precision.fraction;
+
+            // fraction
+            {
+                const strOldLength = str.length;
+                ulong fractionCoefficient;
+                if (str.length < 1 || !str[0].isDigit || !parse!ulong(str, fractionCoefficient))
+                    return false;
+                sizediff_t fractionExponent = str.length - strOldLength;
+                if (fractionExponent < -12)
+                    return false;
+                value.fractionExponent = cast(byte)fractionExponent;
+                value.fractionCoefficient = fractionCoefficient;
+                if (str.length == 0)
+                    return true;
+            }
+
+        TZ:
+
+            if (str == "Z")
+                return true;
+
+            int hour;
+            int minute;
+            if (str.length < 3 || str[0].isDigit || !fromString(str[0 .. 3], hour))
+                return false;
+            str = str[3 .. $];
+
+            if (str.length)
+            {
+                static if (ext)
+                {
+                    if (str[0] != ':')
+                        return false;
+                    str = str[1 .. $];
+                }
+                if (str.length != 2 || !str[0].isDigit || !fromString(str[0 .. 2], minute))
+                    return false;
+            }
+
+            value.offset = cast(short)(hour * 60 + (hour < 0 ? -minute : minute));
+            value.addMinutes(cast(short)-int(value.offset));
+            return true;
+        }
+    }
+}

From 837ac479459b457c272b512dc939c4a01d1507cb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 20 Apr 2021 16:50:19 +0700
Subject: [PATCH 195/573] add docs

---
 index.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.d b/index.d
index 32125604..e47e4e48 100644
--- a/index.d
+++ b/index.d
@@ -24,7 +24,7 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,lob)) $(TD Binar and Char Large Objects ))
     $(LEADINGROW Date and time)
     $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability))
-    $(TR $(TDNW $(MREF mir,timestamp)) $(TD Timestamp ))
+    $(TR $(TDNW $(MREF mir,timestamp)) $(TD General purpose timestamp implementation with arbitrary precision ))
     $(LEADINGROW NDarrays and Algorithms)
     $(TR $(TDNW $(MREF mir,algorithm,iteration)) $(TD Mir & BetterC rework of Phobos.))
     $(TR $(TDNW $(MREF mir,algorithm,setops)) $(TD Mir & BetterC rework of Phobos.))

From 2eb14c9ddbf6b3508950d0545266ce7aae46312d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Apr 2021 15:27:17 +0700
Subject: [PATCH 196/573] fix decimal parsing

---
 source/mir/bignum/decimal.d        | 2 +-
 source/mir/bignum/low_level_view.d | 5 ++---
 source/mir/math/func/expdigamma.d  | 5 +++--
 source/mir/parse.d                 | 7 +++++++
 4 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index d5e25c07..50197882 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -168,7 +168,7 @@ struct Decimal(size_t maxSize64)
             {
                 if (str.length == 0)
                     goto R;
-                if (str[0] == '0')
+                if (str[0] >= '0' && str[0] <= '9')
                     return false;
                 goto S;
             }
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index bf21b487..f4d6c8e4 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -674,8 +674,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                 coefficients = null;
                 return true;
             }
-            if (str[0] == '0')
-                return false;
+            return false;
         }
         else
         if (d >= 10)
@@ -2105,7 +2104,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                 coefficient = coefficient.init;
                 return true;
             }
-            if (str[0] == '0')
+            if (str[0] >= '0' && str[0] <= '9')
                 return false;
             goto S;
         }
diff --git a/source/mir/math/func/expdigamma.d b/source/mir/math/func/expdigamma.d
index 07a1dc34..d4724b46 100644
--- a/source/mir/math/func/expdigamma.d
+++ b/source/mir/math/func/expdigamma.d
@@ -50,8 +50,9 @@ F expDigamma(F)(in F x)
 version(mir_test)
 unittest
 {
-    import std.meta;
-    import std.mathspecial;
+    import std.meta: AliasSeq;
+    import std.mathspecial: digamma;
+    import mir.math: approxEqual, exp, nextUp, nextDown;
     assert(approxEqual(expDigamma(0.001), exp(digamma(0.001))));
     assert(approxEqual(expDigamma(0.1), exp(digamma(0.1))));
     assert(approxEqual(expDigamma(1.0), exp(digamma(1.0))));
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 71770a73..431823cb 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -116,6 +116,13 @@ version(mir_bignum_test)
 // https://issues.dlang.org/show_bug.cgi?id=20967
 }
 
+version(mir_bignum_test)
+@safe pure unittest
+{
+    import std.exception: assertThrown;
+    assertThrown("010".fromString!float);
+}
+
 /++
 Performs `nothrow` and `@nogc` string to native type conversion.
 

From a6232a630842d5d9327b335deea4f3b6a383d668 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Apr 2021 15:30:08 +0700
Subject: [PATCH 197/573] add serdeAlgebraicAnnotation

---
 source/mir/serde.d | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index aa205680..33a25110 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -92,6 +92,21 @@ struct serdeKeyOut
     this(string key) { this.key = key; }
 }
 
+/++
+An annotation / attribute for algebraic types deserialization.
+
+This feature is used in $(MIR_PACKAGE mir-ion) for $(GMREF mir-core, mir,algebraic).
++/
+struct serdeAlgebraicAnnotation
+{
+    ///
+    string annotation;
+
+@safe pure nothrow @nogc:
+    ///
+    this(string annotation) { this.annotation = annotation; }
+}
+
 /++
 Returns:
     immutable array of the input keys for the symbol or enum value

From e51ce353a9f0589656d19f815f9533172283d8bc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Apr 2021 21:46:08 +0700
Subject: [PATCH 198/573] add leading zeros and underscores support for Decimal
 parsing

---
 source/mir/bignum/decimal.d        | 68 ++++++++++++++++++++++--------
 source/mir/bignum/low_level_view.d | 57 ++++++++++++++++++++-----
 source/mir/parse.d                 | 21 ++++++++-
 3 files changed, 118 insertions(+), 28 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 50197882..debb6deb 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -119,7 +119,7 @@ struct Decimal(size_t maxSize64)
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients.
     +/
-    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true, bool allowUnderscores = true, bool allowLeadingZeros = true)(scope const(C)[] str, out DecimalExponentKey key)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
@@ -163,14 +163,20 @@ struct Decimal(size_t maxSize64)
             ulong v;
             uint afterDot;
             bool dot;
-
-            if (d == 0)
+            static if (allowUnderscores)
             {
-                if (str.length == 0)
-                    goto R;
-                if (str[0] >= '0' && str[0] <= '9')
-                    return false;
-                goto S;
+                bool recentUnderscore;
+            }
+            static if (!allowLeadingZeros)
+            {
+                if (d == 0)
+                {
+                    if (str.length == 0)
+                        goto R;
+                    if (str[0] >= '0' && str[0] <= '9')
+                        return false;
+                    goto S;
+                }
             }
 
             if (d < 10)
@@ -206,6 +212,10 @@ struct Decimal(size_t maxSize64)
 
                 if (_expect(d <= 10, true))
                 {
+                    static if (allowUnderscores)
+                    {
+                        recentUnderscore = false;
+                    }
                     {
                         import core.checkedint: addu, mulu;
                         bool overflow;
@@ -223,18 +233,35 @@ struct Decimal(size_t maxSize64)
                 R:
                     coefficient.data[0] = v;
                     coefficient.length = v != 0;
-                    return true;
+                    static if (allowUnderscores)
+                    {
+                        return !recentUnderscore;
+                    }
+                    else
+                    {
+                        return true;
+                    }
+                }
+                key = cast(DecimalExponentKey)d;
+                static if (allowUnderscores)
+                {
+                    if (recentUnderscore)
+                        return false;
                 }
                 switch (d)
                 {
-                    D:
                     case DecimalExponentKey.dot:
-                        if (dot)
+                        if (_expect(dot, false))
                             break;
-                        key = cast(DecimalExponentKey)d;
                         dot = true;
                         if (str.length)
+                        {
+                            static if (allowUnderscores)
+                            {
+                                recentUnderscore = true;
+                            }
                             continue;
+                        }
                         static if (allowDotOnBounds)
                         {
                             goto R;
@@ -251,11 +278,18 @@ struct Decimal(size_t maxSize64)
                     }
                     case DecimalExponentKey.e:
                     case DecimalExponentKey.E:
-                        key = cast(DecimalExponentKey)d;
                         import mir.parse: parse;
                         if (parse(str, exponent) && str.length == 0)
                             goto E;
                         break;
+                    static if (allowUnderscores)
+                    {
+                        case '_' - '0':
+                            recentUnderscore = true;
+                            if (str.length)
+                                continue;
+                            break;
+                    }
                     default:
                 }
                 break;
@@ -287,7 +321,7 @@ struct Decimal(size_t maxSize64)
         {
             import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
             auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
-            auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus)(str, key);
+            auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus, checkEmpty, allowUnderscores, allowLeadingZeros)(str, key);
             coefficient.length = cast(uint) work.coefficient.coefficients.length;
             coefficient.sign = work.sign;
             exponent = work.exponent;
@@ -359,13 +393,13 @@ struct Decimal(size_t maxSize64)
         assert(decimal.fromStringImpl("4.", key));
         assert(!decimal.fromStringImpl(".", key));
         assert(decimal.fromStringImpl("0.", key));
-        assert(!decimal.fromStringImpl("00", key));
+        assert(decimal.fromStringImpl("00", key));
         assert(!decimal.fromStringImpl("0d", key));
     }
 
     static if (maxSize64 == 3)
     version(mir_bignum_test)
-    // @safe pure nothrow @nogc
+    @safe pure nothrow @nogc
     unittest
     {
         import mir.conv: to;
@@ -424,7 +458,7 @@ struct Decimal(size_t maxSize64)
         assert(decimal.fromStringImpl("4.", key));
         assert(!decimal.fromStringImpl(".", key));
         assert(decimal.fromStringImpl("0.", key));
-        assert(!decimal.fromStringImpl("00", key));
+        assert(decimal.fromStringImpl("00", key));
         assert(!decimal.fromStringImpl("0d", key));
     }
 
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index f4d6c8e4..6faaa258 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2048,7 +2048,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
     Precondition: non-empty coefficients
     Note: doesn't support signs.
     +/
-    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true, bool allowUnderscores = true, bool allowLeadingZeros = true)(scope const(C)[] str, out DecimalExponentKey key)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
@@ -2097,16 +2097,24 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         uint afterDot;
         bool dot;
 
-        if (d == 0)
+        static if (allowUnderscores)
         {
-            if (str.length == 0)
+            bool recentUnderscore;
+        }
+
+        static if (!allowLeadingZeros)
+        {
+            if (d == 0)
             {
-                coefficient = coefficient.init;
-                return true;
+                if (str.length == 0)
+                {
+                    coefficient = coefficient.init;
+                    return true;
+                }
+                if (str[0] >= '0' && str[0] <= '9')
+                    return false;
+                goto S;
             }
-            if (str[0] >= '0' && str[0] <= '9')
-                return false;
-            goto S;
         }
 
         if (d < 10)
@@ -2143,6 +2151,10 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 
             if (_expect(d <= 10, true))
             {
+                static if (allowUnderscores)
+                {
+                    recentUnderscore = false;
+                }
                 v *= 10;
         S:
                 t *= 10;
@@ -2172,22 +2184,39 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     M:
                         exponent -= afterDot;
                         coefficient = work.mostSignificant == 0 ? coefficient.init : work;
-                        return true;
+                        static if (allowUnderscores)
+                        {
+                            return !recentUnderscore;
+                        }
+                        else
+                        {
+                            return true;
+                        }
                     }
                 }
 
                 continue;
             }
             key = cast(DecimalExponentKey)d;
+            static if (allowUnderscores)
+            {
+                if (recentUnderscore)
+                    return false;
+            }
             switch (d)
             {
-                D:
                 case DecimalExponentKey.dot:
                     if (_expect(dot, false))
                         break;
                     dot = true;
                     if (str.length)
+                    {
+                        static if (allowUnderscores)
+                        {
+                            recentUnderscore = true;
+                        }
                         continue;
+                    }
                     static if (allowDotOnBounds)
                     {
                         goto L;
@@ -2212,6 +2241,14 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                         goto M;
                     }
                     break;
+                static if (allowUnderscores)
+                {
+                    case '_' - '0':
+                        recentUnderscore = true;
+                        if (str.length)
+                            continue;
+                        break;
+                }
                 default:
             }
             break;
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 431823cb..731a45ed 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -96,6 +96,16 @@ version(mir_bignum_test)
     assert("-12.3e-30".fromString!double == -12.3e-30);
     assert("2.9802322387695312E-8".fromString!double == 2.9802322387695312E-8);
 
+    // default support of underscores
+    assert("123_456.789_012".fromString!double == 123_456.789_012);
+    assert("12_34_56_78_90_12e-6".fromString!double == 123_456.789_012);
+
+    // default support of leading zeros
+    assert("010".fromString!double == 10.0);
+    assert("000010".fromString!double == 10.0);
+    assert("0000.10".fromString!double == 0.1);
+    assert("0000e10".fromString!double == 0);
+
     /// Test CTFE support  
     static assert("-12.3e-30".fromString!double == -0x1.f2f280b2414d5p-97);
     static assert("+12.3e+30".fromString!double == 0x1.367ee3119d2bap+103);
@@ -120,7 +130,16 @@ version(mir_bignum_test)
 @safe pure unittest
 {
     import std.exception: assertThrown;
-    assertThrown("010".fromString!float);
+    assertThrown("1_".fromString!float);
+    assertThrown("1__2".fromString!float);
+    assertThrown("_1".fromString!float);
+    assertThrown("123_.456".fromString!float);
+    assertThrown("123_e0".fromString!float);
+    assertThrown("123._456".fromString!float);
+    assertThrown("12__34.56".fromString!float);
+    assertThrown("123.456_".fromString!float);
+    assertThrown("-_123.456".fromString!float);
+    assertThrown("_123.456".fromString!float);
 }
 
 /++

From 8858e10e86ff5cc5030a23015a7241c6eb615af7 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Apr 2021 22:17:40 +0700
Subject: [PATCH 199/573] add percentage parsing

---
 source/mir/bignum/decimal.d        | 28 +++++++++++++++++++---------
 source/mir/bignum/low_level_view.d | 17 ++++++++++++-----
 2 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index debb6deb..329ba5dd 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -46,11 +46,11 @@ struct Decimal(size_t maxSize64)
     }
 
     ///
-    this(C)(scope const(C)[] str) @safe pure @nogc
+    this(C)(scope const(C)[] str, int exponentShift = 0) @safe pure @nogc
         if (isSomeChar!C)
     {
         DecimalExponentKey key;
-        if (fromStringImpl(str, key) || key == DecimalExponentKey.nan || key == DecimalExponentKey.infinity)
+        if (fromStringImpl(str, key, exponentShift) || key == DecimalExponentKey.nan || key == DecimalExponentKey.infinity)
             return;
         static if (__traits(compiles, () @nogc { throw new Exception("Can't parse Decimal."); }))
         {
@@ -119,7 +119,16 @@ struct Decimal(size_t maxSize64)
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients.
     +/
-    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true, bool allowUnderscores = true, bool allowLeadingZeros = true)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C,
+        bool allowSpecialValues = true,
+        bool allowDotOnBounds = true,
+        bool allowDExponent = true,
+        bool allowStartingPlus = true,
+        bool allowUnderscores = true,
+        bool allowLeadingZeros = true,
+        bool checkEmpty = true,
+        )
+        (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
@@ -161,7 +170,6 @@ struct Decimal(size_t maxSize64)
             exponent = 0;
 
             ulong v;
-            uint afterDot;
             bool dot;
             static if (allowUnderscores)
             {
@@ -225,11 +233,11 @@ struct Decimal(size_t maxSize64)
                     }
                 S:
                     v += d;
-                    afterDot += dot;
+                    exponentShift -= dot;
                     if (str.length)
                         continue;
                 E:
-                    exponent -= afterDot;
+                    exponent += exponentShift;
                 R:
                     coefficient.data[0] = v;
                     coefficient.length = v != 0;
@@ -321,7 +329,7 @@ struct Decimal(size_t maxSize64)
         {
             import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
             auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
-            auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus, checkEmpty, allowUnderscores, allowLeadingZeros)(str, key);
+            auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus, allowUnderscores, allowLeadingZeros, checkEmpty)(str, key, exponentShift);
             coefficient.length = cast(uint) work.coefficient.coefficients.length;
             coefficient.sign = work.sign;
             exponent = work.exponent;
@@ -339,9 +347,11 @@ struct Decimal(size_t maxSize64)
         Decimal!3 decimal;
         DecimalExponentKey key;
 
-        assert(decimal.fromStringImpl("1.334", key));
+        // Check precise percentate parsing
+        assert(decimal.fromStringImpl("71.7", key, -2));
         assert(key == DecimalExponentKey.dot);
-        assert(cast(double) decimal == 1.334);
+        // The result is exact value instead of 0.7170000000000001 = 71.7 / 100
+        assert(cast(double) decimal == 0.717);
 
         assert(decimal.fromStringImpl("+0.334e-5"w, key));
         assert(key == DecimalExponentKey.e);
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 6faaa258..33a35fce 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2046,9 +2046,17 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
     /++
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients
-    Note: doesn't support signs.
     +/
-    bool fromStringImpl(C, bool allowSpecialValues = true, bool allowDotOnBounds = true, bool allowDExponent = true, bool allowStartingPlus = true, bool checkEmpty = true, bool allowUnderscores = true, bool allowLeadingZeros = true)(scope const(C)[] str, out DecimalExponentKey key)
+    bool fromStringImpl(C,
+        bool allowSpecialValues = true,
+        bool allowDotOnBounds = true,
+        bool allowDExponent = true,
+        bool allowStartingPlus = true,
+        bool allowUnderscores = true,
+        bool allowLeadingZeros = true,
+        bool checkEmpty = true,
+        )
+        (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
@@ -2094,7 +2102,6 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 
         W v;
         W t = 1;
-        uint afterDot;
         bool dot;
 
         static if (allowUnderscores)
@@ -2159,7 +2166,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         S:
                 t *= 10;
                 v += d;
-                afterDot += dot;
+                exponentShift -= dot;
 
                 if (_expect(t == mp10 || str.length == 0, false))
                 {
@@ -2182,7 +2189,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     if (str.length == 0)
                     {
                     M:
-                        exponent -= afterDot;
+                        exponent += exponentShift;
                         coefficient = work.mostSignificant == 0 ? coefficient.init : work;
                         static if (allowUnderscores)
                         {

From bd38d3e951cf771b3000ace96fbfafe515b94945 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 23 Apr 2021 15:18:08 +0700
Subject: [PATCH 200/573] add fromStringWithThousandsSeparatorImpl

---
 source/mir/bignum/decimal.d        | 131 ++++++++++++++++++++++++++---
 source/mir/bignum/low_level_view.d |  30 ++++---
 source/mir/ndslice/slice.d         |   2 +-
 3 files changed, 137 insertions(+), 26 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 329ba5dd..b6fd37b7 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -115,9 +115,103 @@ struct Decimal(size_t maxSize64)
         return this;
     }
 
+    /++
+    Handle thousand separators for non exponential numbers.
+
+    Returns: false in case of overflow or incorrect string.
+    +/
+    bool fromStringWithThousandsSeparatorImpl(C,
+        bool allowSpecialValues = true,
+        bool allowStartingPlus = true,
+        bool allowLeadingZeros = true,
+    )(
+        scope const(C)[] str,
+        const C thousandsSeparator,
+        const C fractionSeparator,
+        int exponentShift = 0,
+    )
+        if (isSomeChar!C)
+    {
+        import mir.algorithm.iteration: find;
+        import mir.format: stringBuf;
+        import mir.ndslice.chunks: chunks;
+        import mir.ndslice.slice: sliced;
+        import mir.ndslice.topology: retro;
+
+        stringBuf buffer;
+        assert(thousandsSeparator != fractionSeparator);
+        if (str.length && (str[0] == '+' || str[0] == '-'))
+        {
+            buffer.put(cast(char)str[0]);
+            str = str[1 .. $];
+        }
+        auto integer = str[0 .. $ - str.find!(a => a == fractionSeparator)];
+        if (integer.length % 4 == 0)
+            return false;
+        foreach_reverse (chunk; integer.sliced.retro.chunks(4))
+        {
+            auto s = chunk.retro.field;
+            if (s.length == 4)
+            {
+                if (s[0] != thousandsSeparator)
+                    return false;
+                s = s[1 .. $];
+            }
+            do
+            {
+                if (s[0] < '0' || s[0] > '9')
+                    return false;
+                buffer.put(cast(char)s[0]);
+                s = s[1 .. $];
+            }
+            while(s.length);
+        }
+        if (str.length > integer.length)
+        {
+            buffer.put('.');
+            str = str[integer.length + 1 .. $];
+            if (str.length == 0)
+                return false;
+            do
+            {
+                buffer.put(cast(char)str[0]);
+                str = str[1 .. $];
+            }
+            while(str.length);
+        }
+        DecimalExponentKey key;
+        return fromStringImpl!(char,
+            allowSpecialValues,
+            false, // allowDotOnBounds
+            false, // allowDExponent
+            allowStartingPlus,
+            false, // allowUnderscores
+            allowLeadingZeros, // allowLeadingZeros
+            false, // allowExponent
+            false, // checkEmpty
+        )(buffer.data, key, exponentShift);
+    }
+
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test) 
+    @safe pure nothrow @nogc
+    unittest
+    {
+        Decimal!3 decimal;
+
+        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345.678", ',', '.'));
+        assert(cast(double) decimal == 12345.678);
+
+        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345,678", ',', '.', -3));
+        assert(cast(double) decimal == 12345.678);
+
+        assert(decimal.fromStringWithThousandsSeparatorImpl("021 345,678", ' ', ','));
+        assert(cast(double) decimal == 21345.678);
+    }
+
     /++
     Returns: false in case of overflow or incorrect string.
-    Precondition: non-empty coefficients.
     +/
     bool fromStringImpl(C,
         bool allowSpecialValues = true,
@@ -126,6 +220,7 @@ struct Decimal(size_t maxSize64)
         bool allowStartingPlus = true,
         bool allowUnderscores = true,
         bool allowLeadingZeros = true,
+        bool allowExponent = true,
         bool checkEmpty = true,
         )
         (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
@@ -278,18 +373,21 @@ struct Decimal(size_t maxSize64)
                         {
                             break;
                         }
-                    static if (allowDExponent)
+                    static if (allowExponent)
                     {
-                        case DecimalExponentKey.d:
-                        case DecimalExponentKey.D:
-                            goto case DecimalExponentKey.e;
+                        static if (allowDExponent)
+                        {
+                            case DecimalExponentKey.d:
+                            case DecimalExponentKey.D:
+                                goto case DecimalExponentKey.e;
+                        }
+                        case DecimalExponentKey.e:
+                        case DecimalExponentKey.E:
+                            import mir.parse: parse;
+                            if (parse(str, exponent) && str.length == 0)
+                                goto E;
+                            break;
                     }
-                    case DecimalExponentKey.e:
-                    case DecimalExponentKey.E:
-                        import mir.parse: parse;
-                        if (parse(str, exponent) && str.length == 0)
-                            goto E;
-                        break;
                     static if (allowUnderscores)
                     {
                         case '_' - '0':
@@ -329,7 +427,16 @@ struct Decimal(size_t maxSize64)
         {
             import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
             auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
-            auto ret = work.fromStringImpl!(C, allowSpecialValues, allowDExponent, allowStartingPlus, allowUnderscores, allowLeadingZeros, checkEmpty)(str, key, exponentShift);
+            auto ret = work.fromStringImpl!(C,
+                allowSpecialValues,
+                allowDotOnBounds,
+                allowDExponent,
+                allowStartingPlus,
+                allowUnderscores,
+                allowLeadingZeros,
+                allowExponent,
+                checkEmpty,
+            )(str, key, exponentShift);
             coefficient.length = cast(uint) work.coefficient.coefficients.length;
             coefficient.sign = work.sign;
             exponent = work.exponent;
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 33a35fce..9b915473 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2054,6 +2054,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         bool allowStartingPlus = true,
         bool allowUnderscores = true,
         bool allowLeadingZeros = true,
+        bool allowExponent = true,
         bool checkEmpty = true,
         )
         (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
@@ -2232,22 +2233,25 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     {
                         return false;
                     }
-                static if (allowDExponent)
+                static if (allowExponent)
                 {
-                    case DecimalExponentKey.d:
-                    case DecimalExponentKey.D:
-                        goto case DecimalExponentKey.e;
-                }
-                case DecimalExponentKey.e:
-                case DecimalExponentKey.E:
-                    import mir.parse: parse;
-                    if (parse(str, exponent) && str.length == 0)
+                    static if (allowDExponent)
                     {
-                        if (t != 1)
-                            goto L;
-                        goto M;
+                        case DecimalExponentKey.d:
+                        case DecimalExponentKey.D:
+                            goto case DecimalExponentKey.e;
                     }
-                    break;
+                    case DecimalExponentKey.e:
+                    case DecimalExponentKey.E:
+                        import mir.parse: parse;
+                        if (parse(str, exponent) && str.length == 0)
+                        {
+                            if (t != 1)
+                                goto L;
+                            goto M;
+                        }
+                        break;
+                }
                 static if (allowUnderscores)
                 {
                     case '_' - '0':
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 385f5331..6f769562 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1935,7 +1935,7 @@ public:
         end = final index of the sub-slice (noninclusive)
     Returns: ndslice with `length!dimension` equal to `end - begin`.
     +/
-    auto select(size_t dimension)(size_t begin, size_t end) scope return
+    auto select(size_t dimension)(size_t begin, size_t end) @trusted
     {
         static if (kind == Contiguous && dimension)
         {

From fcc3cb7f24aa5d699ecd1750391b80a6433de10f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 23 Apr 2021 17:16:07 +0700
Subject: [PATCH 201/573] fix fromStringWithThousandsSeparatorImpl

---
 source/mir/bignum/decimal.d | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index b6fd37b7..cddccbe9 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -128,6 +128,7 @@ struct Decimal(size_t maxSize64)
         scope const(C)[] str,
         const C thousandsSeparator,
         const C fractionSeparator,
+        out DecimalExponentKey key,
         int exponentShift = 0,
     )
         if (isSomeChar!C)
@@ -179,7 +180,6 @@ struct Decimal(size_t maxSize64)
             }
             while(str.length);
         }
-        DecimalExponentKey key;
         return fromStringImpl!(char,
             allowSpecialValues,
             false, // allowDotOnBounds
@@ -199,15 +199,19 @@ struct Decimal(size_t maxSize64)
     unittest
     {
         Decimal!3 decimal;
+        DecimalExponentKey key;
 
-        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345.678", ',', '.'));
+        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345.678", ',', '.', key));
         assert(cast(double) decimal == 12345.678);
+        assert(key == DecimalExponentKey.dot);
 
-        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345,678", ',', '.', -3));
+        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345,678", ',', '.', key, -3));
         assert(cast(double) decimal == 12345.678);
+        assert(key == DecimalExponentKey.none);
 
-        assert(decimal.fromStringWithThousandsSeparatorImpl("021 345,678", ' ', ','));
+        assert(decimal.fromStringWithThousandsSeparatorImpl("021 345,678", ' ', ',', key));
         assert(cast(double) decimal == 21345.678);
+        assert(key == DecimalExponentKey.dot);
     }
 
     /++

From a41193f54bd32fbb2708d4df3e2f6f1d5491cc9f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 23 Apr 2021 22:14:38 +0700
Subject: [PATCH 202/573] fix decimal parsing

---
 source/mir/bignum/decimal.d        | 7 ++++++-
 source/mir/bignum/low_level_view.d | 3 ++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index cddccbe9..7a722efe 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -349,7 +349,6 @@ struct Decimal(size_t maxSize64)
                         return true;
                     }
                 }
-                key = cast(DecimalExponentKey)d;
                 static if (allowUnderscores)
                 {
                     if (recentUnderscore)
@@ -358,6 +357,7 @@ struct Decimal(size_t maxSize64)
                 switch (d)
                 {
                     case DecimalExponentKey.dot:
+                        key = DecimalExponentKey.dot;
                         if (_expect(dot, false))
                             break;
                         dot = true;
@@ -388,6 +388,7 @@ struct Decimal(size_t maxSize64)
                         case DecimalExponentKey.e:
                         case DecimalExponentKey.E:
                             import mir.parse: parse;
+                            key = cast(DecimalExponentKey)d;
                             if (parse(str, exponent) && str.length == 0)
                                 goto E;
                             break;
@@ -468,6 +469,10 @@ struct Decimal(size_t maxSize64)
         assert(key == DecimalExponentKey.e);
         assert(cast(double) decimal == 0.334e-5);
 
+        assert(decimal.fromStringImpl("100_000_000"w, key));
+        assert(key == DecimalExponentKey.none);
+        assert(cast(double) decimal == 1e8);
+
         assert(decimal.fromStringImpl("-334D-5"d, key));
         assert(key == DecimalExponentKey.D);
         assert(cast(double) decimal == -334e-5);
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 9b915473..9596bbd2 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2205,7 +2205,6 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 
                 continue;
             }
-            key = cast(DecimalExponentKey)d;
             static if (allowUnderscores)
             {
                 if (recentUnderscore)
@@ -2214,6 +2213,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
             switch (d)
             {
                 case DecimalExponentKey.dot:
+                    key = DecimalExponentKey.dot;
                     if (_expect(dot, false))
                         break;
                     dot = true;
@@ -2244,6 +2244,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     case DecimalExponentKey.e:
                     case DecimalExponentKey.E:
                         import mir.parse: parse;
+                        key = cast(DecimalExponentKey)d;
                         if (parse(str, exponent) && str.length == 0)
                         {
                             if (t != 1)

From 3b8b2c4f52171ee684f7f2b9f0f603f52fb4e920 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Fri, 30 Apr 2021 17:34:10 +0700
Subject: [PATCH 203/573] prepare for Ion (#343)

* prepare for Ion

* update mir.serde for Ion

* update dub
---
 dub.sdl             |   2 +-
 source/mir/date.d   |  63 ++++++++++-
 source/mir/format.d |  61 ++++++++---
 source/mir/serde.d  | 248 +++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 340 insertions(+), 34 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 6fa64c68..e4e511e4 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.1.54"
+dependency "mir-core" version=">=1.1.67"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
diff --git a/source/mir/date.d b/source/mir/date.d
index eff95488..4ab2048d 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -29,6 +29,7 @@ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like
 +/
 module mir.date;
 
+import mir.timestamp: Timestamp;
 import mir.serde: serdeProxy, serdeScoped;
 import std.range.primitives : isOutputRange;
 import std.traits : isSomeChar, Unqual;
@@ -243,12 +244,55 @@ enum DayOfWeek
 }
 
 ///
+@serdeProxy!Timestamp
 struct YearMonthDay
 {
     short year  = 1;
     Month month = Month.jan;
     ubyte day   = 1;
 
+    ///
+    Timestamp timestamp() @safe pure nothrow @nogc @property
+    {
+        return Timestamp(year, cast(ubyte)month, day);
+    }
+
+    ///
+    alias opCast(T : Timestamp) = timestamp;
+
+    ///
+    version(mir_test)
+    unittest
+    {
+        import mir.timestamp;
+        auto timestamp = cast(Timestamp) YearMonthDay(2020, Month.may, 12);
+    }
+
+    ///
+    this(short year, Month month, ubyte day) @safe pure nothrow @nogc
+    {
+        this.year = year;
+        this.month = month;
+        this.day = day;
+    }
+
+    ///
+    this(Date date) @safe pure nothrow @nogc
+    {
+        this = date.yearMonthDay;
+    }
+
+    version(D_Exceptions)
+    ///
+    this(Timestamp timestamp) @safe pure nothrow @nogc
+    {
+        if (timestamp.precision != Timestamp.Precision.day)
+        {
+            static immutable exc = new Exception("YearMonthDay: invalid timestamp precision");
+        }
+        with(timestamp) this(year, cast(Month)month, day);
+    }
+
     // Shares documentation with "years" version.
     @safe pure nothrow @nogc
     ref YearMonthDay add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
@@ -410,8 +454,7 @@ struct YearMonthDay
  +/
 extern(C++, "boost", "gregorian")
 extern(C++, class)
-@serdeScoped
-@serdeProxy!(const(char)[])
+@serdeProxy!YearMonthDay
 struct date
 {
 extern(D):
@@ -507,6 +550,22 @@ public:
         return ret;
     }
 
+    ///
+    Timestamp timestamp() @safe pure nothrow @nogc @property
+    {
+        return yearMonthDay.timestamp;
+    }
+
+    version(D_Exceptions)
+    ///
+    this(Timestamp timestamp) @safe pure @nogc
+    {
+        if (timestamp.precision != Timestamp.Precision.day)
+        {
+            static immutable exc = new Exception("Date: invalid timestamp precision");
+        }
+    }
+
     version(D_Exceptions)
     ///
     this(scope const(char)[] str) @safe pure @nogc
diff --git a/source/mir/format.d b/source/mir/format.d
index 1c301966..56081cef 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -334,11 +334,17 @@ struct HexAddress(T)
 }
 
 /++
+Escaped string formats
 +/
 enum EscapeFormat
 {
+    /// JSON escaped string format
     json,
+    /// Amzn Ion CLOB format
+    ionClob,
+    /// Amzn Ion symbol format
     ionSymbol,
+    /// Amzn Ion string format
     ion,
 }
 
@@ -355,6 +361,11 @@ ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
             goto E;
         if (_expect(c < ' ', false))
             goto C;
+        static if (escapeFormat == EscapeFormat.ionClob)
+        {
+            if (c >= 127)
+                goto A;
+        }
     P:
         w.put(c);
         continue;
@@ -367,22 +378,42 @@ ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
             continue;
         }
     C:
-        if (c == '\t' || c == '\f' || c == '\b')
-            goto P;
-        if (c == '\n')
-        {
-            c = 'n';
-            goto E;
-        }
-        if (c == '\r')
+        switch (c)
         {
-            c = 'r';
-            goto E;
+            static if (escapeFormat != EscapeFormat.json)
+            {
+                case '\0':
+                    c = '0';
+                    goto E;
+                case '\a':
+                    c = 'a';
+                    goto E;
+                case '\v':
+                    c = 'v';
+                    goto E;
+            }
+            case '\b':
+                c = 'b';
+                goto E;
+            case '\t':
+                c = 't';
+                goto E;
+            case '\n':
+                c = 'n';
+                goto E;
+            case '\f':
+                c = 'f';
+                goto E;
+            case '\r':
+                c = 'r';
+                goto E;
+            default:
+    A:
+                static if (escapeFormat == EscapeFormat.json)
+                    put_uXXXX!C(w, cast(char)c);
+                else
+                    put_xXX!C(w, cast(char)c);
         }
-        static if (escapeFormat == EscapeFormat.json)
-            put_uXXXX!C(w, cast(char)c);
-        else
-            put_xXX!C(w, cast(char)c);
     }
     return w;
 }
@@ -394,7 +425,7 @@ version (mir_test) unittest
  
     import mir.format: stringBuf;
     stringBuf w;
-    assert(w.printEscaped("Hi \f\t\b \\\r\n" ~ `"@nogc"`).data == "Hi \f\t\b \\\\\\r\\n\\\"@nogc\\\"", w.data);
+    assert(w.printEscaped("Hi \a\v\0\f\t\b \\\r\n" ~ `"@nogc"`).data == `Hi \a\v\0\f\t\b \\\r\n\"@nogc\"`);
     w.reset;
     assert(w.printEscaped("\x03").data == `\x03`, w.data);
 }
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 33a25110..108693ff 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -92,6 +92,95 @@ struct serdeKeyOut
     this(string key) { this.key = key; }
 }
 
+/++
+The attribute should be applied to a string-like member that should be de/serialized as an annotation / attribute.
+
+This feature is used in $(MIR_PACKAGE mir-ion).
++/
+enum serdeAnnotation;
+
+
+private template serdeIsAnnotationMemberIn(T)
+{
+    enum bool serdeIsAnnotationMemberIn(string member)
+          = hasUDA!(__traits(getMember, T, member), serdeAnnotation) 
+        && !hasUDA!(__traits(getMember, T, member), serdeIgnore) 
+        && !hasUDA!(__traits(getMember, T, member), serdeIgnoreIn);
+}
+
+/++
++/
+template serdeGetAnnotationMembersIn(T)
+{
+    import std.meta: aliasSeqOf, Filter;
+    static if (isSomeStruct!T)
+        enum string[] serdeGetAnnotationMembersIn = [Filter!(serdeIsAnnotationMemberIn!T, aliasSeqOf!(SerializableMembers!T))];
+    else
+        enum string[] serdeGetAnnotationMembersIn = null;
+}
+
+
+///
+version(mir_test) unittest
+{
+    struct S
+    {
+        double data;
+
+        @serdeAnnotation
+        string a;
+        @serdeAnnotation @serdeIgnoreIn
+        string b;
+        @serdeAnnotation @serdeIgnoreOut
+        string c;
+        @serdeAnnotation @serdeIgnore
+        string d;
+    }
+
+    static assert(serdeGetAnnotationMembersIn!int == []);
+    static assert(serdeGetAnnotationMembersIn!S == ["a", "c"]);
+}
+
+private template serdeIsAnnotationMemberOut(T)
+{
+    enum bool serdeIsAnnotationMemberOut(string member)
+          = hasUDA!(__traits(getMember, T, member), serdeAnnotation) 
+        && !hasUDA!(__traits(getMember, T, member), serdeIgnore) 
+        && !hasUDA!(__traits(getMember, T, member), serdeIgnoreOut);
+}
+
+/++
++/
+template serdeGetAnnotationMembersOut(T)
+{
+    import std.meta: aliasSeqOf, Filter;
+    static if (isSomeStruct!T)
+        enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(DeserializableMembers!T))];
+    else
+        enum string[] serdeGetAnnotationMembersOut = null;
+}
+
+///
+version(mir_test) unittest
+{
+    struct S
+    {
+        double data;
+
+        @serdeAnnotation
+        string a;
+        @serdeAnnotation @serdeIgnoreIn
+        string b;
+        @serdeAnnotation @serdeIgnoreOut
+        string c;
+        @serdeAnnotation @serdeIgnore
+        string d;
+    }
+
+    static assert(serdeGetAnnotationMembersOut!int == []);
+    static assert(serdeGetAnnotationMembersOut!S == ["a", "b"]);
+}
+
 /++
 An annotation / attribute for algebraic types deserialization.
 
@@ -107,13 +196,57 @@ struct serdeAlgebraicAnnotation
     this(string annotation) { this.annotation = annotation; }
 }
 
+/++
++/
+template serdeHasAlgebraicAnnotation(T)
+{
+    static if (isSomeStruct!T || is(T == enum))
+    {
+        static if (hasUDA!(T, serdeAlgebraicAnnotation))
+        {
+            enum serdeHasAlgebraicAnnotation = true;
+        }
+        else
+        static if (__traits(getAliasThis, T).length)
+        {
+            T* aggregate;
+            alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
+            enum serdeHasAlgebraicAnnotation = .serdeHasAlgebraicAnnotation!A;
+        }
+        else
+        {
+            enum serdeHasAlgebraicAnnotation = false;
+        }
+    }
+    else
+    {
+        enum serdeHasAlgebraicAnnotation = false;
+    }
+}
+
+/++
++/
+template serdeGetAlgebraicAnnotation(T)
+{
+    static if (hasUDA!(T, serdeAlgebraicAnnotation))
+    {
+        enum string serdeGetAlgebraicAnnotation = getUDA!(T, serdeAlgebraicAnnotation).annotation;
+    }
+    else
+    {
+        T* aggregate;
+        alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
+        enum serdeGetAlgebraicAnnotation = .serdeGetAlgebraicAnnotation!A;
+    }
+}
+
 /++
 Returns:
     immutable array of the input keys for the symbol or enum value
 +/
 template serdeGetKeysIn(alias symbol)
 {
-    static if (hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn))
+    static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn))
         enum immutable(string)[] serdeGetKeysIn = null;
     else
     static if (hasUDA!(symbol, serdeKeys))
@@ -206,7 +339,7 @@ Returns:
 +/
 template serdeGetKeyOut(alias symbol)
 {
-    static if (hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreOut))
+    static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreOut))
         enum string serdeGetKeyOut = null;
     else
     static if (hasUDA!(symbol, serdeKeyOut))
@@ -883,7 +1016,7 @@ template serdeFinalProxyDeserializableMembers(T)
 {
     import std.meta: Filter, aliasSeqOf;
     alias P = serdeGetFinalProxy!T;
-    static if (is(P == struct) || is(P == class) || is(P == interface) || is(P == union))
+    static if (isSomeStruct!P)
         enum string[] serdeFinalProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
     else
         enum string[] serdeFinalProxyDeserializableMembers = null;
@@ -919,7 +1052,7 @@ template serdeFinalDeepProxySerializableMembers(T)
     import std.traits: isAggregateType;
     import std.meta: Filter, aliasSeqOf;
     alias P = serdeGetFinalDeepProxy!T;
-    static if (isAggregateType!P)
+    static if (isSomeStruct!P)
         enum string[] serdeFinalDeepProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
     else
         enum string[] serdeFinalDeepProxySerializableMembers = null;
@@ -954,7 +1087,7 @@ template serdeFinalProxySerializableMembers(T)
 {
     import std.meta: Filter, aliasSeqOf;
     alias P = serdeGetFinalProxy!T;
-    static if (is(P == struct) || is(P == class) || is(P == interface) || is(P == union))
+    static if (isSomeStruct!P)
         enum string[] serdeFinalProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
     else
         enum string[] serdeFinalProxySerializableMembers = null;
@@ -1195,7 +1328,7 @@ version(mir_test) unittest
     }
 
     import std.meta: AliasSeq;
-    static assert (is(serdeSerializationFinalProxyMemberTypes!D == AliasSeq!A), serdeSerializationFinalProxyMemberTypes!D);
+    static assert (is(serdeSerializationFinalProxyMemberTypes!D == AliasSeq!A));
 }
 
 /++
@@ -1204,7 +1337,18 @@ Deserialization members final deep proxy types
 template serdeDeserializationFinalDeepProxyMemberTypes(T)
 {
     import std.meta: NoDuplicates, staticMap, aliasSeqOf;
-    alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T))));
+    import mir.algebraic: isVariant;
+    static if (isVariant!T)
+        alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes));
+    else
+    static if (isAlgebraicAliasThis!T)
+    {
+        T* aggregate;
+        alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
+        alias serdeDeserializationFinalDeepProxyMemberTypes = .serdeDeserializationFinalDeepProxyMemberTypes!A;
+    }
+    else
+        alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T))));
 }
 
 ///
@@ -1242,7 +1386,18 @@ Serialization members final deep proxy types
 template serdeSerializationFinalDeepProxyMemberTypes(T)
 {
     import std.meta: NoDuplicates, staticMap, aliasSeqOf;
-    alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T))));
+    import mir.algebraic: isVariant;
+    static if (isVariant!T)
+        alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes));
+    else
+    static if (isAlgebraicAliasThis!T)
+    {
+        T* aggregate;
+        alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
+        alias serdeSerializationFinalDeepProxyMemberTypes = .serdeSerializationFinalDeepProxyMemberTypes!A;
+    }
+    else
+        alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T))));
 }
 
 ///
@@ -1442,26 +1597,81 @@ private template serdeFinalDeepProxyDeserializableMemberKeys(T)
 {
     import std.meta: staticMap, aliasSeqOf;
     import std.traits: isAggregateType;
-    import mir.ndslice.fuse: fuseCells;
 
     static if (isAggregateType!T)
-        enum string[] serdeFinalDeepProxyDeserializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeysIn2!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))];
+    {
+        import mir.algebraic: isVariant;
+        static if (isVariant!T)
+            enum string[] serdeFinalDeepProxyDeserializableMemberKeys = getAlgebraicAnnotationsOfVariant!T;
+        else
+            enum string[] serdeFinalDeepProxyDeserializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeysIn2!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))];
+    }
     else
         enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null;
 }
 
+private template getAlgebraicAnnotationsOfVariant(T)
+{
+    import std.meta: staticMap, Filter;
+    enum string[] getAlgebraicAnnotationsOfVariant = [staticMap!(serdeGetAlgebraicAnnotation, Filter!(serdeHasAlgebraicAnnotation, T.AllowedTypes))];
+}
+
 private template serdeFinalDeepProxySerializableMemberKeys(T)
 {
     import std.meta: staticMap, aliasSeqOf;
     import std.traits: isAggregateType;
-    import mir.ndslice.fuse: fuseCells;
 
     static if (isAggregateType!T)
-        enum string[] serdeFinalDeepProxySerializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeyOut2!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))];
+    {
+        import mir.algebraic: isVariant;
+        static if (isVariant!T)
+            enum string[] serdeFinalDeepProxySerializableMemberKeys = getAlgebraicAnnotationsOfVariant!T;
+        else
+            enum string[] serdeFinalDeepProxySerializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeyOut2!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))];
+    }
     else
         enum string[] serdeFinalDeepProxySerializableMemberKeys = null;
 }
 
+private template serdeGetAlgebraicAnnotations(T)
+{
+    static if (isSomeStruct!T || is(T == enum))
+        static if (hasUDA!(T, serdeAlgebraicAnnotation))
+            enum string[] serdeGetAlgebraicAnnotations = [getUDA!(T, serdeAlgebraicAnnotation).annotation];
+        else
+            enum string[] serdeGetAlgebraicAnnotations = null;
+    else
+        enum string[] serdeGetAlgebraicAnnotations = null;
+}
+
+package template serdeIsComplexVariant(T)
+{
+    import mir.algebraic: isVariant, isNullable;
+    static if (isVariant!T)
+    {
+        enum serdeIsComplexVariant = (T.AllowedTypes.length - isNullable!T) > 1;
+    }
+    else
+    {
+        enum bool serdeIsComplexVariant = false;
+    }
+}
+
+private template isAlgebraicAliasThis(T)
+{
+    static if (__traits(getAliasThis, T).length)
+    {
+        import mir.algebraic: isVariant;
+        T* aggregate;
+        alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
+        enum isAlgebraicAliasThis = isVariant!A;
+    }
+    else
+    {
+        enum isAlgebraicAliasThis = false;
+    }
+}
+
 /++
 Serialization members final proxy keys (recursive)
 +/
@@ -1500,10 +1710,14 @@ version(mir_test) unittest
         int d;
     }
 
+    @serdeAlgebraicAnnotation("$F")
     @serdeProxy!D
     static struct F { int f; }
 
     static assert (serdeGetSerializationKeysRecurse!F == ["c", "d", "g"]);
+
+    import mir.algebraic;
+    static assert (serdeGetSerializationKeysRecurse!(Nullable!(F, int)) == ["c", "d", "g", "$F"]);
 }
 
 /++
@@ -1538,10 +1752,14 @@ version(mir_test) unittest
         int d;
     }
 
+    @serdeAlgebraicAnnotation("$F")
     @serdeProxy!D
     static struct F { int f; }
 
     static assert (serdeGetDeserializationKeysRecurse!F == ["c", "d", "g"]);
+
+    import mir.algebraic;
+    static assert (serdeGetDeserializationKeysRecurse!(Nullable!(F, int)) == ["c", "d", "g", "$F"]);
 }
 
 /++
@@ -1576,13 +1794,11 @@ UDA used to force serializer to output members in the alphabetical order of thei
 +/
 enum serdeAlphabetOut;
 
-private enum isCompositeType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface);
-
 /++
 A dummy structure usefull $(LREF serdeOrderedIn) support.
 +/
 struct SerdeOrderedDummy(T, bool __optionalByDefault = false)
-    if (is(serdeGetFinalProxy!T == T) && (is(T == class) || is(T == struct) || is(T == union) || is(T == interface)))
+    if (is(serdeGetFinalProxy!T == T) && isSomeStruct!T)
 {
     import std.traits: hasUDA;
 
@@ -1617,7 +1833,7 @@ public:
                 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
             }
             else
-            static if (isCompositeType!(typeof(__traits(getMember, T, member))))
+            static if (isSomeStruct!(typeof(__traits(getMember, T, member))))
             {
                 static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy))
                 {

From f730393fb0ebf567d5750909100622389fbdaada Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 30 Apr 2021 20:26:37 +0700
Subject: [PATCH 204/573] fix for newer ldc2

---
 source/mir/math/sum.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 534aaf0a..d23c43c3 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -962,7 +962,7 @@ public:
 
     ///ditto
     void put(Range)(Range r)
-        if (isIterable!Range)
+        if (isIterable!Range && !is(Range : __vector(V[N]), V, size_t N))
     {
         static if (summation == Summation.pairwise && fastPairwise && isDynamicArray!Range)
         {

From 4a154dc39800208a7635726aa4afde639aceff7a Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Tue, 4 May 2021 19:08:30 +0700
Subject: [PATCH 205/573] add DIP1000 (#345)

---
 dub.sdl                            |   5 ++
 source/mir/appender.d              |   7 +-
 source/mir/bignum/fixed.d          |  31 ++++----
 source/mir/bignum/low_level_view.d | 113 +++++++++++++++--------------
 source/mir/serde.d                 |   6 +-
 test_travis.sh                     |   1 +
 6 files changed, 86 insertions(+), 77 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index e4e511e4..745dc69a 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -17,6 +17,11 @@ buildType "unittest-dip1008" {
     versions "mir_bignum_test" "mir_test"
     dflags "-lowmem" "-preview=dip1008"
 }
+buildType "unittest-dip1000" {
+    buildOptions "unittests" "debugMode" "debugInfo"
+    versions "mir_bignum_test" // "mir_test"
+    dflags "-lowmem" "-preview=dip1000"
+}
 buildType "unittest-cov" {
     buildOptions "unittests" "coverage" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_test"
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 01235f0a..16d1ebb5 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -332,7 +332,8 @@ version (mir_test) unittest
     assert(buf.data == "cs");
 }
 
-version(mir_test)
+version(mir_bignum_test) // for DIP1000
+@safe pure nothrow
 unittest
 {
     import mir.conv: to;
@@ -345,12 +346,14 @@ unittest
             return "_";
         }
     }
-    Algebraic!(int, string) x;
+    Algebraic!(int, string, double) x;
     x = 42;
     auto s = x.to!string;
     assert(s == "42", s);
     x = "abc";
     assert(x.to!string == "abc");
+    x = 42.0;
+    assert(x.to!string == "42.0");
     Algebraic!S y;
     y = S();
     assert(y.to!string == "_");
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 4a3c4642..ea72fdf1 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -111,7 +111,7 @@ struct UInt(size_t size)
 
     static if (size == 128)
     ///
-    version(mir_test) @safe pure @nogc unittest
+    version(mir_bignum_test) @safe pure @nogc unittest
     {
         import mir.math.constant: PI;
         UInt!256 integer = "34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
@@ -265,14 +265,14 @@ struct UInt(size_t size)
     }
 
     /// ditto
-    auto opCmp(ulong rhs) const
+    auto opCmp(ulong rhs) const scope
     {
         return opCmp(UInt!size(rhs));
     }
 
     /++
     +/
-    ref UInt!size opAssign(ulong rhs) return
+    ref UInt!size opAssign(ulong rhs) scope return
         @safe pure nothrow @nogc
     {
         this.data = UInt!size(rhs).data;
@@ -283,7 +283,7 @@ struct UInt(size_t size)
     `bool overflow = a += b ` and `bool overflow = a -= b` operations.
     +/
     bool opOpAssign(string op)(UInt!size rhs, bool overflow = false)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
         return view.opOpAssign!op(rhs.view, overflow);
@@ -291,7 +291,7 @@ struct UInt(size_t size)
 
     /// ditto
     bool opOpAssign(string op)(size_t rhs)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
         return view.opOpAssign!op(rhs);
@@ -300,7 +300,7 @@ struct UInt(size_t size)
     static if (size_t.sizeof < ulong.sizeof)
     /// ditto
     bool opOpAssign(string op)(ulong rhs)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
         return opOpAssign!op(UInt!size(rhs));
@@ -308,7 +308,7 @@ struct UInt(size_t size)
 
     /// ditto
     bool opOpAssign(string op, size_t rsize)(UInt!rsize rhs, bool overflow = false)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if ((op == "+" || op == "-") && rsize < size)
     {
         return opOpAssign!op(rhs.toSize!size, overflow);
@@ -318,7 +318,7 @@ struct UInt(size_t size)
     Returns: overflow value of multiplication
     +/
     size_t opOpAssign(string op : "*")(size_t rhs, size_t carry = 0)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
     {
         return view.opOpAssign!op(rhs, carry);
     }
@@ -326,7 +326,7 @@ struct UInt(size_t size)
     static if (size_t.sizeof == 4)
     /// ditto
     auto opOpAssign(string op : "*")(ulong rhs)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
     {
         return opOpAssign!op(UInt!64(rhs));
     }
@@ -336,7 +336,7 @@ struct UInt(size_t size)
     Returns: overflow value of multiplication
     +/
     void opOpAssign(string op : "*", size_t rhsSize)(UInt!rhsSize rhs)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if (rhsSize <= size)
     {
         this = extendedMul(this, rhs).toSize!size;
@@ -352,7 +352,7 @@ struct UInt(size_t size)
         unsigned remainder value (evaluated overflow)
     +/
     uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
     {
         assert(overflow < rhs);
         auto work = view.normalized;
@@ -382,7 +382,7 @@ struct UInt(size_t size)
     }
 
     ///
-    ref UInt!size opOpAssign(string op)(size_t rhs) nothrow return
+    ref UInt!size opOpAssign(string op)(size_t rhs) nothrow return scope
         if (op == "^" || op == "|" || op == "&")
     {
         mixin(`view.leastSignificantFirst[0] ` ~ op ~ `= rhs;`);
@@ -392,7 +392,7 @@ struct UInt(size_t size)
     static if (size_t.sizeof < ulong.sizeof)
     /// ditto
     ref opOpAssign(string op)(ulong rhs) return
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if (op == "^" || op == "|" || op == "&")
     {
         return opOpAssign!op(UInt!size(rhs));
@@ -805,7 +805,7 @@ UInt!sizeB extendedMulHigh(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB
 
 /++
 +/
-UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
+UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b) @safe
 {
     UInt!(sizeA + sizeB) ret;
     enum al = a.data.length;
@@ -827,7 +827,7 @@ UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!
 
 /// ditto
 UInt!(size + size_t.sizeof * 8)
-    extendedMul(size_t size)(UInt!size a, size_t b)
+    extendedMul(size_t size)(UInt!size a, size_t b) @safe
 {
     size_t overflow = a.view *= b;
     auto ret = a.toSize!(size + size_t.sizeof * 8);
@@ -873,7 +873,6 @@ UInt!64 extendedMul()(uint a, uint b)
 
 ///
 version(mir_bignum_test)
-version(mir_bignum_test)
 @safe pure @nogc
 unittest
 {
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 9596bbd2..14f69108 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -139,13 +139,13 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Retrurns: signed integer view using the same data payload
     +/
-    BigIntView!(W, endian) signed()() @safe pure nothrow @nogc @property
+    BigIntView!(W, endian) signed()() @safe pure nothrow @nogc scope @property
     {
         return typeof(return)(this);
     }
 
     ///
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
         if (isFloatingPoint!T && isMutable!T)
     {
         import mir.bignum.fp;
@@ -169,7 +169,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     @safe
-    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
+    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const
         if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
     {
         static if (isMutable!W)
@@ -346,7 +346,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
 
     ///
-    T opCast(T, bool nonZero = false)() const
+    T opCast(T, bool nonZero = false)() const scope
         if (isIntegral!T && isUnsigned!T && isMutable!T)
     {
         auto work = lightConst;
@@ -412,7 +412,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     static if (endian == TargetEndian)
     ///
     @trusted pure nothrow @nogc
-    BigUIntView!V opCast(T : BigUIntView!V, V)()
+    BigUIntView!V opCast(T : BigUIntView!V, V)() scope return
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(cast(V[])this.coefficients);
@@ -420,7 +420,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     BigUIntView!(const W, endian) lightConst()()
-        const @safe pure nothrow @nogc @property
+        const @safe pure nothrow @nogc @property scope return
     {
         return typeof(return)(coefficients);
     }
@@ -429,8 +429,8 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    sizediff_t opCmp(BigUIntView!(const W, endian) rhs)
-        const @safe pure nothrow @nogc
+    sizediff_t opCmp(scope BigUIntView!(const W, endian) rhs)
+        const @safe pure nothrow @nogc scope
     {
         import mir.algorithm.iteration: cmp;
         auto l = this.lightConst.normalized;
@@ -441,15 +441,15 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    bool opEquals(BigUIntView!(const W, endian) rhs)
-        const @safe pure nothrow @nogc
+    bool opEquals(scope BigUIntView!(const W, endian) rhs)
+        const @safe pure nothrow @nogc scope
     {
         return this.coefficients == rhs.coefficients;
     }
 
     /++
     +/
-    ref inout(W) mostSignificant() inout @property
+    ref inout(W) mostSignificant() inout @property scope return
     {
         static if (endian == WordEndian.big)
             return coefficients[0];
@@ -459,7 +459,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    ref inout(W) leastSignificant() inout @property
+    ref inout(W) leastSignificant() inout @property scope return
     {
         static if (endian == WordEndian.little)
             return coefficients[0];
@@ -469,7 +469,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    void popMostSignificant()
+    void popMostSignificant() scope
     {
         static if (endian == WordEndian.big)
             coefficients = coefficients[1 .. $];
@@ -479,7 +479,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    void popLeastSignificant()
+    void popLeastSignificant() scope
     {
         static if (endian == WordEndian.little)
             coefficients = coefficients[1 .. $];
@@ -489,7 +489,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigUIntView topMostSignificantPart(size_t length)
+    BigUIntView topMostSignificantPart(size_t length) scope return
     {
         static if (endian == WordEndian.big)
             return BigUIntView(coefficients[0 .. length]);
@@ -499,7 +499,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigUIntView topLeastSignificantPart(size_t length)
+    BigUIntView topLeastSignificantPart(size_t length) scope return
     {
         static if (endian == WordEndian.little)
             return BigUIntView(coefficients[0 .. length]);
@@ -510,7 +510,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Shifts left using at most `size_t.sizeof * 8 - 1` bits
     +/
-    void smallLeftShiftInPlace()(uint shift)
+    void smallLeftShiftInPlace()(uint shift) scope
     {
         assert(shift < W.sizeof * 8);
         if (shift == 0)
@@ -583,7 +583,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     +/
     bool fromHexStringImpl(C)(scope const(C)[] str)
-        @safe pure @nogc nothrow
+        @safe pure @nogc nothrow scope
         if (isSomeChar!C)
     {
         pragma(inline, false);
@@ -732,8 +732,8 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns:
         true in case of unsigned overflow
     +/
-    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
-    @safe pure nothrow @nogc
+    bool opOpAssign(string op)(scope BigUIntView!(const W, endian) rhs, bool overflow = false)
+    @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
         assert(this.coefficients.length > 0);
@@ -756,8 +756,8 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     static if (isMutable!W && W.sizeof >= 4)
     /// ditto
-    bool opOpAssign(string op)(BigIntView!(const W, endian) rhs, bool overflow = false)
-    @safe pure nothrow @nogc
+    bool opOpAssign(string op)(scope BigIntView!(const W, endian) rhs, bool overflow = false)
+    @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
         return rhs.sign == false ?
@@ -775,7 +775,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         true in case of unsigned overflow
     +/
     bool opOpAssign(string op, T)(const T rhs)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if ((op == "+" || op == "-") && is(T == W))
     {
         assert(this.coefficients.length > 0);
@@ -797,7 +797,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     static if (isMutable!W && W.sizeof >= 4)
     /// ditto
     bool opOpAssign(string op, T)(const T rhs)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
         if ((op == "+" || op == "-") && is(T == Signed!W))
     {
         return rhs >= 0 ?
@@ -816,7 +816,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         unsigned overflow value
     +/
     W opOpAssign(string op : "*")(W rhs, W overflow = 0u)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
     {
         assert(coefficients.length);
         auto ns = this.leastSignificantFirst;
@@ -844,7 +844,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         unsigned remainder value (evaluated overflow)
     +/
     uint opOpAssign(string op : "/")(uint rhs, uint overflow = 0)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
     {
         assert(overflow < rhs);
         assert(coefficients.length);
@@ -886,7 +886,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     +/
     UInt!size
     opOpAssign(string op : "*", size_t size)(UInt!size rhs, UInt!size overflow = 0)
-        @safe pure nothrow @nogc
+        @safe pure nothrow @nogc scope
     {
         assert(coefficients.length);
         auto ns = this.leastSignificantFirst;
@@ -909,7 +909,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Returns: the same intger view with inversed sign
     +/
-    BigIntView!(W, endian) opUnary(string op : "-")()
+    BigIntView!(W, endian) opUnary(string op : "-")() scope return
     {
         return typeof(return)(this, true);
     }
@@ -917,7 +917,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     static if (isMutable!W && W.sizeof >= 4)
     /++
     +/
-    void bitwiseNotInPlace()
+    void bitwiseNotInPlace() scope
     {
         foreach (ref coefficient; this.coefficients)
             coefficient = cast(W)~(0 + coefficient);
@@ -930,7 +930,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns:
         true if 'number=-number=0' and false otherwise
     +/
-    bool twoComplementInPlace()
+    bool twoComplementInPlace() scope
     {
         assert(coefficients.length);
         bitwiseNotInPlace();
@@ -941,7 +941,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: a slice of coefficients starting from the least significant.
     +/
     auto leastSignificantFirst()
-        @safe pure nothrow @nogc @property
+        @safe pure nothrow @nogc @property scope return
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.little)
@@ -957,7 +957,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     auto leastSignificantFirst()
-        const @safe pure nothrow @nogc @property
+        const @safe pure nothrow @nogc @property scope return
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.little)
@@ -975,7 +975,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: a slice of coefficients starting from the most significant.
     +/
     auto mostSignificantFirst()
-        @safe pure nothrow @nogc @property
+        @safe pure nothrow @nogc @property scope return
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.big)
@@ -991,7 +991,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     auto mostSignificantFirst()
-        const @safe pure nothrow @nogc @property
+        const @safe pure nothrow @nogc @property scope return
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.big)
@@ -1008,7 +1008,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Strips most significant zero coefficients.
     +/
-    BigUIntView normalized()
+    BigUIntView normalized() scope return
     {
         auto number = this;
         if (number.coefficients.length) do
@@ -1031,14 +1031,14 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///ditto
-    BigUIntView!(const W, endian) normalized() const
+    BigUIntView!(const W, endian) normalized() scope const
     {
         return lightConst.normalized;
     }
 
     /++
     +/
-    bool bt()(size_t position)
+    bool bt()(size_t position) scope
     {
         import mir.ndslice.topology: bitwise;
         assert(position < coefficients.length * W.sizeof * 8);
@@ -1047,7 +1047,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    size_t ctlz()() const @property
+    size_t ctlz()() scope const @property
         @safe pure nothrow @nogc
     {
         import mir.bitop: ctlz;
@@ -1070,7 +1070,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    size_t cttz()() const @property
+    size_t cttz()() scope const @property
         @safe pure nothrow @nogc
     {
         import mir.bitop: cttz;
@@ -1103,7 +1103,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: true on success
     +/
     bool get(U)(scope out U value)
-        @safe pure nothrow @nogc const
+        @safe pure nothrow @nogc scope const
         if (isUnsigned!U)
     {
         auto d = lightConst.mostSignificantFirst;
@@ -1151,7 +1151,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: true if the integer and equals to `rhs`.
     +/
     bool opEquals(ulong rhs)
-        @safe pure nothrow @nogc const
+        @safe pure nothrow @nogc const scope
     {
         foreach_reverse(d; lightConst.leastSignificantFirst)
         {
@@ -1220,7 +1220,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         auto a = BigInt!2("123456789098765432123456789098765432100");
         char[ceilLog10Exp2(a.data.length * (size_t.sizeof * 8))] buffer;
         auto len = a.view.unsigned.toStringImpl(buffer);
-        assert(buffer[$ - len .. $] == "123456789098765432123456789098765432100", buffer[$ - len .. $]);
+        assert(buffer[$ - len .. $] == "123456789098765432123456789098765432100");
     }
 }
 
@@ -1317,7 +1317,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     bool sign;
 
     ///
-    inout(W)[] coefficients() inout @property
+    inout(W)[] coefficients() inout @property scope return
     {
         return unsigned.coefficients;
     }
@@ -1411,7 +1411,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
         if (isFloatingPoint!T && isMutable!T)
     {
         auto ret = this.unsigned.opCast!(T, wordNormalized, nonZero);
@@ -1423,6 +1423,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
     ///
     version(mir_bignum_test)
+    @safe pure
     unittest
     {
         auto a = cast(double) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
@@ -1431,7 +1432,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
 
     ///
-    T opCast(T, bool nonZero = false)() const
+    T opCast(T, bool nonZero = false)() scope const
         if (is(T == long) || is(T == int))
     {
         auto ret = this.unsigned.opCast!(Unsigned!T, nonZero);
@@ -1473,7 +1474,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
+    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const
         if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
     {
         auto ret = unsigned.opCast!(Fp!coefficientSize, internalRoundLastBits, wordNormalized, nonZero);
@@ -1498,14 +1499,14 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     static if (endian == TargetEndian)
     ///
-    BigIntView!V opCast(T : BigIntView!V, V)()
+    BigIntView!V opCast(T : BigIntView!V, V)() scope return
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
     }
 
     ///
-    BigIntView!(const W, endian) lightConst()()
+    BigIntView!(const W, endian) lightConst()() scope return
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(unsigned.lightConst, sign);
@@ -1517,7 +1518,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     /++
     +/
     sizediff_t opCmp(BigIntView!(const W, endian) rhs) 
-        const @safe pure nothrow @nogc
+        const @safe pure nothrow @nogc scope
     {
         import mir.algorithm.iteration: cmp;
         if (auto s = rhs.sign - this.sign)
@@ -1531,7 +1532,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     ///
     bool opEquals(BigIntView!(const W, endian) rhs)
-        const @safe pure nothrow @nogc
+        const @safe pure nothrow @nogc scope
     {
         return (this.sign == rhs.sign || unsigned.coefficients.length == 0) && this.unsigned == rhs.unsigned;
     }
@@ -1540,7 +1541,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     Returns: true if the integer and equals to `rhs`.
     +/
     bool opEquals(long rhs)
-        @safe pure nothrow @nogc const
+        @safe pure nothrow @nogc const scope
     {
         if (rhs == 0 && unsigned.coefficients.length == 0)
             return true;
@@ -1573,7 +1574,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     Returns:
         true in case of unsigned overflow
     +/
-    bool opOpAssign(string op)(BigIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(scope BigIntView!(const W, endian) rhs, bool overflow = false)
     @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {
@@ -1601,7 +1602,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     static if (isMutable!W && W.sizeof >= 4)
     /// ditto
-    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(scope BigUIntView!(const W, endian) rhs, bool overflow = false)
     @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {
@@ -1930,7 +1931,7 @@ struct BigUIntAccumulator(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    void mulPow2(size_t degree)
+    void mulPow2(size_t degree) scope @safe
     {
         import mir.bitop: ctlz;
         assert(canMulPow2(degree));
@@ -2312,7 +2313,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.
     +/
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
         if (isFloatingPoint!T && isMutable!T)
     {
         version(LDC)
@@ -2469,7 +2470,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
 }
 
 @optStrategy("minsize")
-package T algoR(T, W, WordEndian endian)(T ret, BigUIntView!(const W, endian) coeff, int exponent)
+package T algoR(T, W, WordEndian endian)(T ret, scope BigUIntView!(const W, endian) coeff, int exponent)
 {
     pragma(inline, false);
 
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 108693ff..da45d52c 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -114,7 +114,7 @@ template serdeGetAnnotationMembersIn(T)
 {
     import std.meta: aliasSeqOf, Filter;
     static if (isSomeStruct!T)
-        enum string[] serdeGetAnnotationMembersIn = [Filter!(serdeIsAnnotationMemberIn!T, aliasSeqOf!(SerializableMembers!T))];
+        enum string[] serdeGetAnnotationMembersIn = [Filter!(serdeIsAnnotationMemberIn!T, aliasSeqOf!(DeserializableMembers!T))];
     else
         enum string[] serdeGetAnnotationMembersIn = null;
 }
@@ -1610,7 +1610,7 @@ private template serdeFinalDeepProxyDeserializableMemberKeys(T)
         enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null;
 }
 
-private template getAlgebraicAnnotationsOfVariant(T)
+package template getAlgebraicAnnotationsOfVariant(T)
 {
     import std.meta: staticMap, Filter;
     enum string[] getAlgebraicAnnotationsOfVariant = [staticMap!(serdeGetAlgebraicAnnotation, Filter!(serdeHasAlgebraicAnnotation, T.AllowedTypes))];
@@ -1657,7 +1657,7 @@ package template serdeIsComplexVariant(T)
     }
 }
 
-private template isAlgebraicAliasThis(T)
+package template isAlgebraicAliasThis(T)
 {
     static if (__traits(getAliasThis, T).length)
     {
diff --git a/test_travis.sh b/test_travis.sh
index a0085a14..5ec323ae 100644
--- a/test_travis.sh
+++ b/test_travis.sh
@@ -5,6 +5,7 @@ then
   docker build -t ion-arm64 . -f Dockerfile.aarch64
 else
   echo $ARCH
+  dub test --arch=$ARCH --build=unittest-dip1000
   dub test --arch=$ARCH --build=unittest-cov
   # if [ \( "$DC" = "ldc2" \) -o \( "$DC" = "ldmd2" \) ]
   # then

From 333701c038ee40874cc2e2af91724ec444f2df90 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 6 May 2021 22:10:29 +0700
Subject: [PATCH 206/573] fixup series

---
 source/mir/series.d | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index c72c153e..2e717c06 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -219,7 +219,8 @@ alias Observation = mir_observation;
 /// Convenient function for $(LREF Observation) construction.
 auto observation(Index, Data)(Index index, Data data)
 {
-    return mir_observation!(Index, Data)(index, data);
+    alias R = mir_observation!(Index, Data);
+    return R(index, data);
 }
 
 /++
@@ -283,13 +284,13 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     IndexIterator _index;
 
     /// Index / Key / Time type aliases
-    alias Index = typeof(this.front.index);
+    alias Index = typeof(typeof(this).init.index.front);
     /// ditto
     alias Key = Index;
     /// ditto
     alias Time = Index;
     /// Data / Value type aliases
-    alias Data = typeof(this.front.data);
+    alias Data = typeof(typeof(this).init.data.front);
     /// ditto
     alias Value = Data;
 
@@ -1095,7 +1096,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         }
         else
         {
-            return index.front.observation(data.front);
+            return Observation!(Index, Data)(index.front, data.front);
         }
     }
 

From ee774b7648f5bf5762c895cc074beeb6dfdaa708 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 12 May 2021 08:10:13 -0400
Subject: [PATCH 207/573] Address built-in complex deprecation (#344)

* Address built-in complex deprecation

* Update complex transition

* Additional not working sum algorithms

* Add support for std.complex to mir.math.sum

* Fixup

* Adjust statType

* Additional fixups

* Address CircleCI sliced failure

* Fix CircleCI Failure, bignum.fp UInt

* Remove traces of std.complex

* Fixup
---
 dub.sdl                          |   2 +-
 source/mir/algorithm/iteration.d |   7 ++
 source/mir/bignum/fp.d           |   9 ++
 source/mir/math/stat.d           | 148 ++++++++++++++++++++-----
 source/mir/math/sum.d            | 184 ++++++++++++++++++++++++-------
 5 files changed, 280 insertions(+), 70 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 745dc69a..3b642813 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.67"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_test"
+    versions "mir_bignum_test" "mir_builtincomplex_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 64910a70..7b57eebe 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -1445,6 +1445,7 @@ template isSymmetric(alias fun = "a == b")
 version(mir_test)
 unittest
 {
+    import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: iota;
     assert(iota(2, 2).isSymmetric == false);
 
@@ -1580,6 +1581,7 @@ template minmaxPos(alias pred = "a < b")
 version(mir_test)
 unittest
 {
+    import mir.ndslice.slice: sliced;
     auto s = [
         2, 6, 4, -3,
         0, -4, -3, 3,
@@ -1651,6 +1653,7 @@ template minmaxIndex(alias pred = "a < b")
 version(mir_test)
 unittest
 {
+    import mir.ndslice.slice: sliced;
     auto s = [
         2, 6, 4, -3,
         0, -4, -3, 3,
@@ -1720,6 +1723,7 @@ template maxPos(alias pred = "a < b")
 version(mir_test)
 unittest
 {
+    import mir.ndslice.slice: sliced;
     auto s = [
         2, 6, 4, -3,
         0, -4, -3, 3,
@@ -1791,6 +1795,7 @@ template maxIndex(alias pred = "a < b")
 version(mir_test)
 unittest
 {
+    import mir.ndslice.slice: sliced;
     auto s = [
         2, 6, 4, -3,
         0, -4, -3, 3,
@@ -1812,6 +1817,7 @@ unittest
 version(mir_test)
 unittest
 {
+    import mir.ndslice.slice: sliced;
     auto s = [
         -8, 6, 4, -3,
         0, -4, -3, 3,
@@ -1827,6 +1833,7 @@ unittest
 version(mir_test)
 unittest
 {
+    import mir.ndslice.slice: sliced;
     auto s = [
             0, 1, 2, 3,
             4, 5, 6, 7,
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 2a694ee8..49e045f1 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -216,6 +216,8 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     @safe pure @nogc
     unittest
     {
+        import mir.bignum.fixed: UInt;
+
         auto fp = Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
         assert(fp.exponent == 0);
         assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
@@ -280,6 +282,8 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     @safe pure @nogc
     unittest
     {
+        import mir.bignum.fixed: UInt;
+
         auto a = Fp!128(0, -13, UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"));
         auto b = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314"));
         auto fp = a * b;
@@ -354,6 +358,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     @safe pure @nogc
     unittest
     {
+        import mir.bignum.fixed: UInt;
         auto fp = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112cb8b71ad3f85a970a314"));
         assert(cast(double)fp == -0xE3251BACB112C8p+172);
     }
@@ -364,6 +369,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     @safe pure @nogc
     unittest
     {
+        import mir.bignum.fixed: UInt;
         auto fp = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112cb8b71ad3f85a970a314"));
         static if (real.mant_dig == 64)
             assert(cast(real)fp == -0xe3251bacb112cb8bp+164L);
@@ -375,6 +381,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     @safe pure @nogc
     unittest
     {
+        import mir.bignum.fixed: UInt;
         auto fp = Fp!64(1, 100, UInt!64(0xe3251bacb112cb8b));
         version (DigitalMars)
         {
@@ -397,6 +404,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     @safe pure @nogc
     unittest
     {
+        import mir.bignum.fixed: UInt;
         auto fp = Fp!64(1, 100, UInt!64(0xe3251bacb112cb8b));
         static if (real.mant_dig == 64)
             assert(cast(real)fp == -0xe3251bacb112cb8bp+100L);
@@ -417,6 +425,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     @safe pure @nogc
     unittest
     {
+        import mir.bignum.fixed: UInt;
         auto fp = cast(Fp!64) Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2784a1de7022b0029d"));
         assert(fp.exponent == 64);
         assert(fp.coefficient == UInt!64.fromHexString("afbbfae3cd0aff28"));
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 7defaee2..fe09bf2f 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -30,18 +30,26 @@ import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonTyp
 package(mir)
 template statType(T, bool checkComplex = true)
 {
-    import mir.internal.utility: isFloatingPoint, isComplex;
+    import mir.internal.utility: isFloatingPoint;
 
-    static if (isFloatingPoint!T || (checkComplex && isComplex!T)) {
+    static if (isFloatingPoint!T) {
         import std.traits: Unqual;
         alias statType = Unqual!T;
     } else static if (is(T : double)) {
         alias statType = double;
     } else static if (checkComplex) {
-        static if (is(T : cdouble)) {
-            alias statType = cdouble;
+        import mir.internal.utility: isComplex;
+        static if (isComplex!T) {
+            import std.traits: Unqual;
+            static if (is(T : cdouble)) {
+                deprecated("Built-in complex types deprecated in D language version 2.097") alias statType = Unqual!T;
+            } else {
+                alias statType = Unqual!T;
+            }
+        } else static if (is(T : cdouble)) {
+                deprecated("Built-in complex types deprecated in D language version 2.097") alias statType = cdouble;
         } else {
-            static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a floating point (or complex floating point) type");
+            static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a complex floating point type");
         }
     } else {
         static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a floating point type");
@@ -57,9 +65,6 @@ unittest
     static assert(is(statType!double == double));
     static assert(is(statType!float == float));
     static assert(is(statType!real == real));
-    static assert(is(statType!cfloat == cfloat));
-    static assert(is(statType!cdouble == cdouble));
-    static assert(is(statType!creal == creal));
     
     static assert(is(statType!(const(int)) == double));
     static assert(is(statType!(immutable(int)) == double));
@@ -67,6 +72,26 @@ unittest
     static assert(is(statType!(immutable(double)) == double));
 }
 
+version(mir_builtincomplex_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static assert(is(statType!cfloat == cfloat));
+    static assert(is(statType!cdouble == cdouble));
+    static assert(is(statType!creal == creal));
+}
+
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import std.complex: Complex;
+
+    static assert(is(statType!(Complex!float) == Complex!float));
+    static assert(is(statType!(Complex!double) == Complex!double));
+    static assert(is(statType!(Complex!real) == Complex!real));
+}
+
 version(mir_test)
 @safe pure nothrow @nogc
 unittest
@@ -75,14 +100,20 @@ unittest
         float x;
         alias x this;
     }
-    
-    static struct Bar {
+
+    static assert(is(statType!Foo == double)); // note: this is not float
+}
+
+version(mir_builtincomplex_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
         cfloat x;
         alias x this;
     }
 
-    static assert(is(statType!Foo == double)); // note: this is not float
-    static assert(is(statType!Bar == cdouble)); // note: this is not cfloat
+    static assert(is(statType!Foo == cdouble)); // note: this is not Complex!float
 }
 
 version(mir_test)
@@ -93,14 +124,20 @@ unittest
         double x;
         alias x this;
     }
-    
-    static struct Bar {
+
+    static assert(is(statType!Foo == double));
+}
+
+version(mir_builtincomplex_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
         cdouble x;
         alias x this;
     }
 
-    static assert(is(statType!Foo == double));
-    static assert(is(statType!Bar == cdouble));
+    static assert(is(statType!Foo == cdouble));
 }
 
 version(mir_test)
@@ -111,14 +148,20 @@ unittest
         real x;
         alias x this;
     }
-    
-    static struct Bar {
+
+    static assert(is(statType!Foo == double)); // note: this is not real
+}
+
+version(mir_builtincomplex_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
         creal x;
         alias x this;
     }
 
-    static assert(is(statType!Foo == double)); // note: this is not real
-    static assert(is(statType!Bar == cdouble)); // note: this is not creal
+    static assert(is(statType!Foo == cdouble)); // note: this is not Complex!real
 }
 
 version(mir_test)
@@ -160,6 +203,12 @@ unittest
     static assert(is(meanType!(int[]) == double));
     static assert(is(meanType!(double[]) == double));
     static assert(is(meanType!(float[]) == float));
+}
+
+version(mir_builtincomplex_test)
+@safe pure nothrow @nogc
+unittest
+{
     static assert(is(meanType!(cfloat[]) == cfloat));
 }
 
@@ -171,14 +220,20 @@ unittest
         float x;
         alias x this;
     }
-    
-    static struct Bar {
+
+    static assert(is(meanType!(Foo[]) == float));
+}
+
+version(mir_builtincomplex_test)
+@safe pure nothrow @nogc
+unittest
+{
+    static struct Foo {
         cfloat x;
         alias x this;
     }
 
-    static assert(is(meanType!(Foo[]) == float));
-    static assert(is(meanType!(Bar[]) == cfloat));
+    static assert(is(meanType!(Foo[]) == cfloat));
 }
 
 /++
@@ -285,7 +340,7 @@ Computes the mean of the input.
 
 By default, if `F` is not floating point type or complex type, then the result
 will have a `double` type if `F` is implicitly convertible to a floating point 
-type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+type or a type for which `isComplex!F` is true.
 
 Params:
     F = controls type of output
@@ -607,7 +662,7 @@ Computes the harmonic mean of the input.
 
 By default, if `F` is not floating point type or complex type, then the result
 will have a `double` type if `F` is implicitly convertible to a floating point 
-type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+type or a type for which `isComplex!F` is true.
 
 Params:
     F = controls type of output
@@ -1240,7 +1295,7 @@ Computes the median of `slice`.
 
 By default, if `F` is not floating point type or complex type, then the result
 will have a `double` type if `F` is implicitly convertible to a floating point 
-type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+type or a type for which `isComplex!F` is true.
 
 Can also pass a boolean variable, `allowModify`, that allows the input slice to
 be modified. By default, a reference-counted copy is made. 
@@ -2235,7 +2290,7 @@ unittest
     assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
 }
 
-version(mir_test)
+version(mir_builtincomplex_test)
 @safe pure nothrow
 unittest
 {
@@ -2250,6 +2305,22 @@ unittest
     assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
 }
 
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
+    import std.complex: Complex;
+
+    auto x = [Complex!double(1.0, 3), Complex!double(2), Complex!double(3)].sliced;
+
+    VarianceAccumulator!(Complex!double, VarianceAlgo.online, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(true).approxEqual(Complex!double(-4.0, -6) / 3));
+    assert(v.variance(false).approxEqual(Complex!double(-4.0, -6) / 2));
+}
+
 version(mir_test)
 @safe pure nothrow
 unittest
@@ -2582,7 +2653,7 @@ unittest
     assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
 }
 
-version(mir_test)
+version(mir_builtincomplex_test)
 @safe pure nothrow
 unittest
 {
@@ -2598,6 +2669,23 @@ unittest
     assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
 }
 
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.ndslice.slice: sliced;
+    import std.complex: Complex;
+
+    auto a = [Complex!double(1.0, 3), Complex!double(2), Complex!double(3)].sliced;
+    auto x = a.center;
+
+    VarianceAccumulator!(Complex!double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
+    v.put(x);
+    assert(v.variance(true).approxEqual(Complex!double(-4.0, -6) / 3));
+    assert(v.variance(false).approxEqual(Complex!double(-4.0, -6) / 2));
+}
+
 version(mir_test)
 @safe pure nothrow
 unittest
@@ -2645,7 +2733,7 @@ Calculates the variance of the input
 
 By default, if `F` is not floating point type or complex type, then the result
 will have a `double` type if `F` is implicitly convertible to a floating point 
-type or have a `cdouble` type if `F` is implicitly convertible to a complex type.
+type or a type for which `isComplex!F` is true.
 
 Params:
     F = controls type of output
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index d23c43c3..24469f1e 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -114,7 +114,7 @@ unittest
 /++
 All summation algorithms available for complex numbers.
 +/
-version(mir_test)
+version(mir_builtincomplex_test)
 unittest
 {
     cdouble[] ar = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i];
@@ -132,6 +132,27 @@ unittest
     assert(r == ar.sum!"decimal");
 }
 
+///
+version(mir_test)
+unittest
+{
+    import std.complex: Complex;
+
+    auto ar = [Complex!double(1.0, 2), Complex!double(2.0, 3), Complex!double(3.0, 4), Complex!double(4.0, 5)];
+    Complex!double r = Complex!double(10.0, 14);
+    assert(r == ar.sum!"fast");
+    assert(r == ar.sum!"naive");
+    assert(r == ar.sum!"pairwise");
+    assert(r == ar.sum!"kahan");
+    version(LDC) // DMD Internal error: backend/cgxmm.c 628
+    {
+        assert(r == ar.sum!"kbn");
+        assert(r == ar.sum!"kb2");
+    }
+    assert(r == ar.sum!"precise");
+    assert(r == ar.sum!"decimal");
+}
+
 ///
 version(mir_test)
 @safe pure nothrow unittest
@@ -448,7 +469,8 @@ Output range for summation.
 struct Summator(T, Summation summation)
     if (isMutable!T)
 {
-    static if (is(T == class) || is(T == interface) || hasElaborateAssign!T)
+    import mir.internal.utility: isComplex;
+    static if (is(T == class) || is(T == interface) || hasElaborateAssign!T && !isComplex!T)
         static assert (summation == Summation.naive,
             "Classes, interfaces, and structures with "
             ~ "elaborate constructor support only naive summation.");
@@ -472,14 +494,16 @@ struct Summator(T, Summation summation)
 
     @attr:
 
-    static if (summation == Summation.pairwise)
+    static if (summation == Summation.pairwise) {
         private enum bool fastPairwise =
             is(F == float) ||
             is(F == double) ||
             is(F == cfloat) ||
             is(F == cdouble) ||
+            (isComplex!F && F.sizeof <= 16) ||
             is(F : __vector(W[N]), W, size_t N);
             //false;
+    }
 
     alias F = T;
 
@@ -668,8 +692,13 @@ public:
             s = n;
             static if (isComplex!T)
             {
-                cs = 0 + 0fi;
-                ccs = 0 + 0fi;
+                static if (is(T : cfloat)) {
+                    cs = 0 + 0fi;
+                    ccs = 0 + 0fi;
+                } else {
+                    cs = Complex!float(0, 0);
+                    ccs = Complex!float(0, 0);
+                }
             }
             else
             {
@@ -681,18 +710,26 @@ public:
         static if (summation == Summation.kbn)
         {
             s = n;
-            static if (isComplex!T)
-                c = 0 + 0fi;
-            else
+            static if (isComplex!T) {
+                static if (is(T : cfloat)) {
+                    c = 0 + 0fi;
+                } else {
+                    c = Complex!float(0, 0);
+                }
+            } else
                 c = 0.0;
         }
         else
         static if (summation == Summation.kahan)
         {
             s = n;
-            static if (isComplex!T)
-                c = 0 + 0fi;
-            else
+            static if (isComplex!T) {
+                static if (is(T : cfloat)) {
+                    c = 0 + 0fi;
+                } else {
+                    c = Complex!float(0, 0);
+                }
+            } else
                 c = 0.0;
         }
         else
@@ -834,15 +871,25 @@ public:
                 {
                     auto s_re = s.re;
                     auto x_re = x.re;
-                    s = x_re + s.im * 1fi;
-                    x = s_re + x.im * 1fi;
+                    static if (is(F : cfloat)) {
+                        s = x_re + s.im * 1fi;
+                        x = s_re + x.im * 1fi;
+                    } else {
+                        s = F(x_re, s.im);
+                        x = F(s_re, x.im);
+                    }
                 }
                 if (fabs(s.im) < fabs(x.im))
                 {
                     auto s_im = s.im;
                     auto x_im = x.im;
-                    s = s.re + x_im * 1fi;
-                    x = x.re + s_im * 1fi;
+                    static if (is(F : cfloat)) {
+                        s = s.re + x_im * 1fi;
+                        x = x.re + s_im * 1fi;
+                    } else {
+                        s = F(s.re, x_im);
+                        x = F(x.re, s_im);
+                    }
                 }
                 F c = (s-t)+x;
                 s = t;
@@ -850,15 +897,25 @@ public:
                 {
                     auto c_re = c.re;
                     auto cs_re = cs.re;
-                    c = cs_re + c.im * 1fi;
-                    cs = c_re + cs.im * 1fi;
+                    static if (is(F : cfloat)) {
+                        c = cs_re + c.im * 1fi;
+                        cs = c_re + cs.im * 1fi;
+                    } else {
+                        c = F(cs_re, c.im);
+                        cs = F(c_re, cs.im);
+                    }
                 }
                 if (fabs(cs.im) < fabs(c.im))
                 {
                     auto c_im = c.im;
                     auto cs_im = cs.im;
-                    c = c.re + cs_im * 1fi;
-                    cs = cs.re + c_im * 1fi;
+                    static if (is(F : cfloat)) {
+                        c = c.re + cs_im * 1fi;
+                        cs = cs.re + c_im * 1fi;
+                    } else {
+                        c = F(c.re, cs_im);
+                        cs = F(cs.re, c_im);
+                    }
                 }
                 F d = cs - t;
                 d += c;
@@ -893,15 +950,25 @@ public:
                 {
                     auto s_re = s.re;
                     auto x_re = x.re;
-                    s = x_re + s.im * 1fi;
-                    x = s_re + x.im * 1fi;
+                    static if (is(F : cfloat)) {
+                        s = x_re + s.im * 1fi;
+                        x = s_re + x.im * 1fi;
+                    } else {
+                        s = F(x_re, s.im);
+                        x = F(s_re, x.im);
+                    }
                 }
                 if (fabs(s.im) < fabs(x.im))
                 {
                     auto s_im = s.im;
                     auto x_im = x.im;
-                    s = s.re + x_im * 1fi;
-                    x = x.re + s_im * 1fi;
+                    static if (is(F : cfloat)) {
+                        s = s.re + x_im * 1fi;
+                        x = x.re + s_im * 1fi;
+                    } else {
+                        s = F(s.re, x_im);
+                        x = F(x.re, s_im);
+                    }
                 }
                 F d = s - t;
                 d += x;
@@ -993,9 +1060,13 @@ public:
         else
         static if (summation == Summation.fast)
         {
-            static if (isComplex!T)
-                F s0 = 0 + 0fi;
-            else
+            static if (isComplex!F) {
+                static if (is(F : cfloat)) {
+                    F s0 = 0 + 0fi;
+                } else {
+                    F s0 = F(0, 0f);
+                }
+            } else
                 F s0 = 0;
             foreach (ref elem; r)
                 s0 += elem;
@@ -1026,9 +1097,13 @@ public:
         else
         static if (summation == Summation.fast && N == 1)
         {
-            static if (isComplex!T)
-                F s0 = 0 + 0fi;
-            else
+            static if (isComplex!F) {
+                static if (is(F : cfloat)) {
+                    F s0 = 0 + 0fi;
+                } else {
+                    F s0 = F(0, 0f);
+                }
+            } else
                 F s0 = 0;
             import mir.algorithm.iteration: reduce;
             s0 = s0.reduce!"a + b"(r);
@@ -1333,8 +1408,13 @@ public:
             s = rhs;
             static if (isComplex!T)
             {
-                cs = 0 + 0fi;
-                ccs = 0 + 0fi;
+                static if (is(T : cfloat)) {
+                    cs = 0 + 0fi;
+                    ccs = 0 + 0fi;
+                } else {
+                    cs = T(0, 0f);
+                    ccs = T(0.0, 0f);
+                }
             }
             else
             {
@@ -1346,18 +1426,26 @@ public:
         static if (summation == Summation.kbn)
         {
             s = rhs;
-            static if (isComplex!T)
-                c = 0 + 0fi;
-            else
+            static if (isComplex!T) {
+                static if (is(T : cfloat)) {
+                    c = 0 + 0fi;
+                } else {
+                    c = T(0, 0f);
+                }
+            } else
                 c = 0.0;
         }
         else
         static if (summation == Summation.kahan)
         {
             s = rhs;
-            static if (isComplex!T)
-                c = 0 + 0fi;
-            else
+            static if (isComplex!T) {
+                static if (is(T : cfloat)) {
+                    c = 0 + 0fi;
+                } else {
+                    c = T(0, 0f);
+                }
+            } else
                 c = 0.0;
         }
         else
@@ -1800,7 +1888,11 @@ template sum(F, Summation summation = Summation.appropriate)
                         sumIm.put(elem.im);
                     }
                 }
-                return sumRe.sum + sumIm.sum * 1fi;
+                static if (is(F : cfloat)) {
+                    return sumRe.sum + sumIm.sum * 1fi;
+                } else {
+                    return F(sumRe.sum, sumIm.sum);
+                }
             }
             else
             {
@@ -1985,7 +2077,6 @@ unittest
     assert(sum(1) == 1);
     assert(sum(1, 2, 3) == 6);
     assert(sum(1.0, 2.0, 3.0) == 6);
-    assert(sum(1.0 + 1i, 2.0 + 2i, 3.0 + 3i) == (6 + 6i));
 }
 
 version(mir_test)
@@ -1994,9 +2085,24 @@ unittest
     assert(sum!float(1) == 1f);
     assert(sum!float(1, 2, 3) == 6f);
     assert(sum!float(1.0, 2.0, 3.0) == 6f);
+}
+
+version(mir_builtincomplex_test)
+unittest
+{
+    assert(sum(1.0 + 1i, 2.0 + 2i, 3.0 + 3i) == (6 + 6i));
     assert(sum!cfloat(1.0 + 1i, 2.0 + 2i, 3.0 + 3i) == (6f + 6i));
 }
 
+version(mir_test)
+unittest
+{
+    import std.complex: Complex;
+
+    assert(sum(Complex!float(1.0, 1.0), Complex!float(2.0, 2.0), Complex!float(3.0, 3.0)) == Complex!float(6.0, 6.0));
+    assert(sum!(Complex!float)(Complex!float(1.0, 1.0), Complex!float(2.0, 2.0), Complex!float(3.0, 3.0)) == Complex!float(6.0, 6.0));
+}
+
 version(LDC)
 version(X86_Any)
 version(mir_test)

From 33a3bc878138341cfe29584362f2ab1cc3d573e5 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 12 May 2021 13:20:35 -0400
Subject: [PATCH 208/573] Add makeIndex UT, improve docs (#346)

---
 source/mir/ndslice/sorting.d | 41 ++++++++++++++++++++++++++++++------
 1 file changed, 35 insertions(+), 6 deletions(-)

diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 13f6372e..8c3a937a 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -5,8 +5,8 @@ Note:
     The combination of
     $(SUBREF topology, pairwise) with lambda `"a <= b"` (`"a < b"`) and $(SUBREF algorithm, all) can be used
     to check if an ndslice is sorted (strictly monotonic).
-    $(SUBREF topology iota) can be used to make an index.
-    $(SUBREF topology map) and $(SUBREF topology zip) can be used to create Schwartzian transform.
+    $(SUBREF topology, iota) can be used to make an index.
+    $(SUBREF topology, map) and $(SUBREF topology, zip) can be used to create Schwartzian transform.
     See also the examples in the module.
 
 
@@ -667,7 +667,9 @@ version(mir_test)
 /++
 Computes an index for `r` based on the comparison `less`. The
 index is a sorted array of indices into the original
-range. This technique is similar to sorting, but it is more flexible
+range. 
+
+This technique is similar to sorting, but it is more flexible
 because (1) it allows "sorting" of immutable collections, (2) allows
 binary search even if the original collection does not offer random
 access, (3) allows multiple indices, each on a different predicate,
@@ -676,10 +678,19 @@ an index may also be slower under certain circumstances due to the
 extra indirection, and is always larger than a sorting-based solution
 because it needs space for the index in addition to the original
 collection. The complexity is the same as `sort`'s.
+
+Can be combined with $(SUBREF topology, indexed) to create a view that is sorted
+based on the index.
+
 Params:
     less = The comparison to use.
     r = The slice/array to index.
-Returns: Index slice/array.
+
+Returns:
+    Index slice/array.
+    
+See_Also:
+    $(HTTPS numpy.org/doc/stable/reference/generated/numpy.argsort.html, numpy.argsort)
 +/
 Slice!(I*) makeIndex(I = size_t, alias less = "a < b", Iterator, SliceKind kind)(Slice!(Iterator, 1, kind) r)
 {
@@ -701,7 +712,8 @@ I[] makeIndex(I = size_t, alias less = "a < b", T)(scope T[] r)
 
 ///
 version(mir_test)
-@system unittest
+@safe pure nothrow
+unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.topology: indexed, pairwise;
@@ -712,6 +724,23 @@ version(mir_test)
     assert(arr.indexed(index).pairwise!"a < b".all);
 }
 
+/// Sort based on index created from a separate array
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.algorithm.iteration: equal;
+    import mir.ndslice.topology: indexed;
+
+    immutable arr0 = [ 2, 3, 1, 5,  0 ];
+    immutable arr1 = [ 1, 5, 4, 2, -1 ];
+
+    auto index = makeIndex(arr0);
+    assert(index.equal([4, 2, 0, 1, 3]));
+    auto view = arr1.indexed(index);
+    assert(view.equal([-1, 4, 1, 5, 2]));
+}
+
 /++
 Partitions `slice` around `pivot` using comparison function `less`, algorithm 
 akin to $(LINK2 https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme,
@@ -741,7 +770,7 @@ Params:
 
 Returns:
     The new position of the pivot
-    
+
 See_Also:
     $(HTTP jgrcs.info/index.php/jgrcs/article/view/142, Engineering of a Quicksort
     Partitioning Algorithm), D. Abhyankar, Journal of Global Research in Computer

From 33d0eda9e823f9eb7bd0ddbf3de2d7015b84c411 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 13 May 2021 00:21:37 +0700
Subject: [PATCH 209/573] fix docs

---
 source/mir/graph/package.d | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source/mir/graph/package.d b/source/mir/graph/package.d
index 3f6cdd63..37587137 100644
--- a/source/mir/graph/package.d
+++ b/source/mir/graph/package.d
@@ -147,6 +147,8 @@ RCGraph!(I, J) rcgraph(I = uint, J = size_t, KeyIterator, RangeIterator)(Series!
 @safe pure @nogc version(mir_test)
 unittest
 {
+    import mir.series: series;
+
     static immutable keys = ["a", "b", "c"];
     static immutable data = [
         ["b", "c"],

From 0965ada3d13b4be831875b0b754be9e7ae81999b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 13 May 2021 01:05:30 +0700
Subject: [PATCH 210/573] fix docs

---
 source/mir/interpolate/spline.d      |  1 +
 source/mir/math/stat.d               |  1 +
 source/mir/ndslice/allocation.d      |  2 ++
 source/mir/ndslice/connect/cpython.d |  2 +-
 source/mir/ndslice/topology.d        | 42 +++++++++++++++++-----------
 source/mir/numeric.d                 |  4 +--
 6 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index f2191359..75f136ca 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -126,6 +126,7 @@ public import mir.interpolate: atInterval;
 ///
 @safe pure version(mir_test) unittest
 {
+    import mir.rc.array: rcarray;
     import mir.algorithm.iteration: all;
     import mir.functional: aliasCall;
     import mir.math.common: approxEqual;
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index fe09bf2f..d24d0c92 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -25,6 +25,7 @@ import mir.math.sum;
 import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
 import mir.primitives;
 import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType;
+public import mir.math.sum: Summation;
 
 ///
 package(mir)
diff --git a/source/mir/ndslice/allocation.d b/source/mir/ndslice/allocation.d
index 899f68dc..419b0cff 100644
--- a/source/mir/ndslice/allocation.d
+++ b/source/mir/ndslice/allocation.d
@@ -162,6 +162,7 @@ auto rcslice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation)
 version(mir_test)
 @safe pure nothrow @nogc unittest
 {
+    import mir.rc.array: RCI;
     import mir.ndslice.slice: Slice;
     import mir.ndslice.topology : iota;
     import mir.ndslice.concatenation;
@@ -955,6 +956,7 @@ auto stdcUninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @sys
 version(mir_test)
 @system pure nothrow unittest
 {
+    import mir.ndslice.slice: Slice;
     auto tensor = stdcUninitAlignedSlice!double([5, 6, 7], 64);
     assert(tensor.length == 5);
     assert(tensor.elementCount == 5 * 6 * 7);
diff --git a/source/mir/ndslice/connect/cpython.d b/source/mir/ndslice/connect/cpython.d
index 895bb335..07584e6f 100644
--- a/source/mir/ndslice/connect/cpython.d
+++ b/source/mir/ndslice/connect/cpython.d
@@ -215,7 +215,7 @@ PythonBufferErrorCode toPythonBuffer(T, size_t N, SliceKind kind)(Slice!(T*, N,
 ///
 version(mir_test) unittest
 {
-    import mir.ndslice.slice : Slice, Structure, Universal, Contiguous;
+    import mir.ndslice.slice : Slice, Structure, Universal, Contiguous, SliceKind;
     Py_buffer bar(SliceKind kind)(Slice!(double*, 2, kind) slice)
     {
         import core.stdc.stdlib;
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index f00b579d..da4b7273 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -570,7 +570,7 @@ pack(size_t P, Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) sli
 ///
 @safe @nogc pure nothrow version(mir_test) unittest
 {
-    import mir.ndslice.slice : sliced, Slice;
+    import mir.ndslice.slice: sliced, Slice;
 
     auto a = iota(3, 4, 5, 6);
     auto b = a.pack!2;
@@ -624,7 +624,7 @@ ipack(size_t P, Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) sl
 ///
 @safe @nogc pure nothrow version(mir_test) unittest
 {
-    import mir.ndslice.slice : sliced, Slice;
+    import mir.ndslice.slice: sliced, Slice;
 
     auto a = iota(3, 4, 5, 6);
     auto b = a.ipack!2;
@@ -712,7 +712,8 @@ evertPack(Iterator, size_t M, SliceKind innerKind, size_t N, SliceKind outerKind
 ///
 @safe pure nothrow version(mir_test) unittest
 {
-    import mir.ndslice.slice: sliced;
+    import mir.ndslice.iterator: SliceIterator;
+    import mir.ndslice.slice: sliced, Slice, Universal;
     import mir.ndslice.allocation: slice;
     static assert(is(typeof(
         slice!int(6)
@@ -750,7 +751,7 @@ iota
     (I = sizediff_t, size_t N)(size_t[N] lengths...)
     if (__traits(isIntegral, I))
 {
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
     return IotaIterator!I(I.init).sliced(lengths);
 }
 
@@ -759,7 +760,7 @@ Slice!(IotaIterator!sizediff_t, N)
 iota
     (size_t N)(size_t[N] lengths, sizediff_t start)
 {
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
     return IotaIterator!sizediff_t(start).sliced(lengths);
 }
 
@@ -768,7 +769,7 @@ Slice!(StrideIterator!(IotaIterator!sizediff_t), N)
 iota
     (size_t N)(size_t[N] lengths, sizediff_t start, size_t stride)
 {
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
     return StrideIterator!(IotaIterator!sizediff_t)(stride, IotaIterator!sizediff_t(start)).sliced(lengths);
 }
 
@@ -782,7 +783,7 @@ template iota(I)
         (size_t N)(size_t[N] lengths, I start)
         if (__traits(isIntegral, I))
     {
-        import mir.ndslice.slice : sliced;
+        import mir.ndslice.slice: sliced;
         return IotaIterator!I(start).sliced(lengths);
     }
 
@@ -792,7 +793,7 @@ template iota(I)
         (size_t N)(size_t[N] lengths, I start, size_t stride)
         if (__traits(isIntegral, I))
     {
-        import mir.ndslice.slice : sliced;
+        import mir.ndslice.slice: sliced;
         return StrideIterator!(IotaIterator!I)(stride, IotaIterator!I(start)).sliced(lengths);
     }
 }
@@ -803,7 +804,7 @@ iota
     (I, size_t N)(size_t[N] lengths, I start)
     if (is(I P : P*))
 {
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
     return IotaIterator!I(start).sliced(lengths);
 }
 
@@ -813,13 +814,14 @@ iota
     (I, size_t N)(size_t[N] lengths, I start, size_t stride)
     if (is(I P : P*))
 {
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
     return StrideIterator!(IotaIterator!I)(stride, IotaIterator!I(start)).sliced(lengths);
 }
 
 ///
 @safe pure nothrow @nogc version(mir_test) unittest
 {
+    import mir.primitives: DeepElementType;
     auto slice = iota(2, 3);
     static immutable array =
         [[0, 1, 2],
@@ -1746,7 +1748,7 @@ Random access and slicing
 nothrow version(mir_test) unittest
 {
     import mir.ndslice.allocation: slice;
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
 
     auto elems = iota(4, 5).slice.flattened;
 
@@ -2111,6 +2113,8 @@ version(mir_test) unittest
 ///
 @safe pure nothrow version(mir_test) unittest
 {
+    import mir.primitives: DeepElementType;
+
     auto sl = repeat(4.0, 2, 3);
     assert(sl == [[4.0, 4.0, 4.0],
                   [4.0, 4.0, 4.0]]);
@@ -2675,7 +2679,7 @@ auto bytegroup(size_t pack, DestinationType, T)(T withAsSlice)
 @safe pure nothrow @nogc
 version(mir_test) unittest
 {
-    import mir.ndslice.slice : DeepElementType, sliced;
+    import mir.ndslice.slice: DeepElementType, sliced;
 
     ubyte[20] data;
     // creates a packed unsigned integer slice with max allowed value equal to `2^^6 - 1 == 63`.
@@ -2697,7 +2701,7 @@ version(mir_test) unittest
 @safe pure nothrow @nogc
 version(mir_test) unittest
 {
-    import mir.ndslice.slice : DeepElementType, sliced;
+    import mir.ndslice.slice: DeepElementType, sliced;
     ushort[20] data;
     // creates a packed unsigned integer slice with max allowed value equal to `2^^6 - 1 == 63`.
     auto int48ar = data[].sliced.bytegroup!(3, long); // 48 bit integers
@@ -3551,6 +3555,7 @@ template as(T)
 ///
 @safe pure nothrow version(mir_test) unittest
 {
+    import mir.ndslice.slice: Slice;
     import mir.ndslice.allocation : slice;
     import mir.ndslice.topology : diagonal, as;
 
@@ -3573,7 +3578,7 @@ template as(T)
 @safe pure nothrow version(mir_test) unittest
 {
     import mir.ndslice.allocation : slice;
-    import mir.ndslice.slice : Contiguous, Slice;
+    import mir.ndslice.slice: Contiguous, Slice;
 
     Slice!(double*, 2)              matrix = slice!double([2, 2], 0);
     Slice!(const(double)*, 2) const_matrix = matrix.as!(const double);
@@ -3743,7 +3748,7 @@ auto chopped(S, Sliceable)(Sliceable sliceable, S bounds)
 @safe pure version(mir_test) unittest
 {
     import mir.functional: staticArray;
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
     auto pairwiseIndexes = [2, 4, 10].sliced;
     auto sliceable = 10.iota;
 
@@ -4100,6 +4105,7 @@ alias pairwise(alias fun, size_t lag = 1) = slide!(lag + 1, fun);
 ///
 @safe pure nothrow version(mir_test) unittest
 {
+    import mir.ndslice.slice: sliced;
     assert([2, 4, 3, -1].sliced.pairwise!"a + b" == [6, 7, 2]);
 }
 
@@ -4130,6 +4136,7 @@ alias diff(size_t lag = 1) = pairwise!(('a' + lag) ~ " - a", lag);
 ///
 version(mir_test) unittest
 {
+    import mir.ndslice.slice: sliced;
     assert([2, 4, 3, -1].sliced.diff == [2, -1, -4]);
 }
 
@@ -4412,7 +4419,7 @@ template kronecker(alias fun = product)
 version(mir_test) unittest
 {
     import mir.ndslice.allocation: slice;
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
 
     // eye
     auto a = slice!double([4, 4], 0);
@@ -4450,7 +4457,7 @@ version(mir_test) unittest
 version(mir_test) unittest
 {
     import mir.ndslice.allocation: slice;
-    import mir.ndslice.slice : sliced;
+    import mir.ndslice.slice: sliced;
 
     auto a = [ 1,  2,
                3,  4].sliced(2, 2);
@@ -5829,6 +5836,7 @@ auto triplets(string type, S)(S slice, size_t n)
 ///
 version(mir_test) unittest
 {
+    import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: triplets, member, iota;
 
     auto a = [4, 5, 2, 8];
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 3f710f37..5234b4f4 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -199,7 +199,7 @@ FindRootResult!T findRoot(alias f, alias tolerance = null, T)(
 // @nogc
 version(mir_test) @safe unittest
 {
-    import mir.math.common: log, exp;
+    import mir.math.common: log, exp, fabs;
 
     auto logRoot = findRoot!log(0, double.infinity).validate.x;
     assert(logRoot == 1);
@@ -1376,7 +1376,7 @@ version(mir_test) @safe unittest
 ///
 version(mir_test) @safe unittest
 {
-    import mir.math.common: approxEqual;
+    import mir.math.common: approxEqual, log, fabs;
     alias AliasSeq(T...) = T;
     static foreach (T; AliasSeq!(double, float, real))
     {

From 8c0b9918a3a5e1b67db094205e42c38cfdbfeb26 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 21 Jun 2021 15:31:38 +0700
Subject: [PATCH 211/573] move mir.exception to core

---
 index.d                     |   1 -
 meson.build                 |   1 -
 source/mir/bignum/decimal.d |   2 +-
 source/mir/date.d           |   4 +-
 source/mir/exception.d      | 305 ------------------------------------
 source/mir/parse.d          |   2 +-
 6 files changed, 4 insertions(+), 311 deletions(-)
 delete mode 100644 source/mir/exception.d

diff --git a/index.d b/index.d
index e47e4e48..8e2f63f4 100644
--- a/index.d
+++ b/index.d
@@ -13,7 +13,6 @@ $(BOOKTABLE ,
     $(LEADINGROW Accessories)
     $(TR $(TDNW $(MREF mir,algebraic_alias,json)) $(TD Mutable JSON value))
     $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
-    $(TR $(TDNW $(MREF mir,exception)) $(TD @nogc MirException with formatting))
     $(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities))
     $(TR $(TDNW $(MREF mir,parse)) $(TD @nogc Parsing Utilities))
     $(TR $(TDNW $(MREF mir,range)) $(TD Ranges))
diff --git a/meson.build b/meson.build
index 5310695b..5f81d23e 100644
--- a/meson.build
+++ b/meson.build
@@ -24,7 +24,6 @@ sources_list = [
     'mir/container/binaryheap',
     'mir/cpp_export/numeric',
     'mir/date',
-    'mir/exception',
     'mir/format_impl',
     'mir/format',
     'mir/graph/package',
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 7a722efe..2e7f4416 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -324,7 +324,7 @@ struct Decimal(size_t maxSize64)
                         recentUnderscore = false;
                     }
                     {
-                        import core.checkedint: addu, mulu;
+                        import mir.checkedint: mulu;
                         bool overflow;
                         v = mulu(v, cast(uint)10, overflow);
                         if (overflow)
diff --git a/source/mir/date.d b/source/mir/date.d
index 4ab2048d..b1e0dd3f 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -29,8 +29,8 @@ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like
 +/
 module mir.date;
 
-import mir.timestamp: Timestamp;
 import mir.serde: serdeProxy, serdeScoped;
+import mir.timestamp: Timestamp;
 import std.range.primitives : isOutputRange;
 import std.traits : isSomeChar, Unqual;
 
@@ -551,7 +551,7 @@ public:
     }
 
     ///
-    Timestamp timestamp() @safe pure nothrow @nogc @property
+    Timestamp timestamp() @safe pure nothrow @nogc const @property
     {
         return yearMonthDay.timestamp;
     }
diff --git a/source/mir/exception.d b/source/mir/exception.d
deleted file mode 100644
index b79d805c..00000000
--- a/source/mir/exception.d
+++ /dev/null
@@ -1,305 +0,0 @@
-/++
-`@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("");})());
-
-package template staticException(string fmt, string file, int line)
-{
-    static immutable staticException = new Exception(fmt, file, line);
-}
-
-///
-auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(scope auto return ref Expr arg) @trusted
-{
-    version(LDC) pragma(inline, true);
-    import core.lifetime: forward;
-    import mir.utility: _expect;
-    static if (__traits(compiles, arg !is null))
-    {
-        if (_expect(arg !is null, true))
-            return forward!arg;
-    }
-    else
-    {
-        if (_expect(cast(bool)arg, true))
-            return forward!arg;
-    }
-    throw staticException!(fmt, file, line);
-}
-
-///
-@safe pure nothrow @nogc
-version (mir_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)
-@safe pure nothrow @nogc
-unittest
-{
-    import mir.exception;
-    try throw new MirException("Hi D", 2, "!");
-    catch(Exception e) assert(e.msg == "Hi D2!");
-}
-
-/// C++ style
-version (mir_test) static if (NOGCEXP)
-@safe pure nothrow @nogc
-unittest
-{
-    import mir.exception;
-    import mir.format;
-    try throw new MirException(stringBuf() << "Hi D" << 2 << "!" << getData);
-    catch(Exception e) assert(e.msg == "Hi D2!");
-}
-
-/// Low-level style
-version (mir_test) static if (NOGCEXP)
-@safe pure nothrow @nogc
-unittest
-{
-    import mir.exception;
-    import mir.format;
-    stringBuf buf;
-    try throw new MirException(buf.print( "Hi D", 2, "!").data);
-    catch(Exception e) assert(e.msg == "Hi D2!");
-}
-
-///
-version (mir_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_test) unittest static if (NOGCEXP)
-// {
-//     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_test) unittest static if (NOGCEXP)
-// {
-//     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[maxMsgLen] _payload = void;
-
-    /++
-    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 @safe pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
-    {
-        super((() @trusted => cast(immutable) mirExceptionInitilizePayloadImpl(_payload, msg))(), file, line, nextInChain);
-    }
-
-    /// ditto
-    @nogc @safe pure nothrow this(scope const(char)[] msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
-    {
-        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.
-    +/
-    @nogc @safe pure nothrow this(Args...)(scope auto ref Args args, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
-        if (Args.length > 1)
-    {
-        import mir.format;
-        stringBuf buf;
-        this(buf.print(args).data, file, line, nextInChain);
-    }
-}
-
-private enum maxMsgLen = 447;
-
-pragma(inline, false)
-pure nothrow @nogc @safe
-const(char)[] mirExceptionInitilizePayloadImpl(ref return char[maxMsgLen] 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];
-}
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 731a45ed..bfff58e6 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -319,7 +319,7 @@ private bool parseUnsignedImpl(T, Range)(scope ref Range r, scope ref T value)
     if(__traits(isUnsigned, T))
 {
     version(LDC) pragma(inline, true);
-    import core.checkedint: addu, mulu;
+    import mir.checkedint: addu, mulu;
 
     bool sign;
 B:

From 83e09753d2a0a58617401b5cfd0d65659fa7fa7d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 27 Jun 2021 23:26:26 +0700
Subject: [PATCH 212/573] add serdeIgnore * IfAggregate attributes

---
 source/mir/serde.d | 57 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 4 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index da45d52c..dd6161b4 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -439,11 +439,15 @@ version(mir_test) unittest
 
 /++
 Attribute to ignore field.
+
+See_also: $(LREF serdeIgnoreIn) $(LREF serdeIgnoreOut)
 +/
 enum serdeIgnore;
 
 /++
 Attribute to ignore field during deserialization.
+
+See_also: $(LREF serdeIgnoreInIfAggregate)
 +/
 enum serdeIgnoreIn;
 
@@ -510,9 +514,11 @@ Use this attributes only for strings that would not be used after the input data
 deprecated("use @serdeScoped @serdeProxy!(const(char)[]) instead") enum serdeScopeStringProxy;
 
 /++
-Attributes to out conditional ignore field during serialization.
+Attributes to conditional ignore field during serialization.
 
-The predicate should be aplied to the aggregate type itself, not to the member.
+The predicate should be aplied to the member, to the aggregate type.
+
+See_also: $(LREF serdeIgnoreOutIfAggregate)
 +/
 struct serdeIgnoreOutIf(alias pred);
 
@@ -520,6 +526,49 @@ struct serdeIgnoreOutIf(alias pred);
 +/
 alias serdeGetIgnoreOutIf(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIf))[0]);
 
+/++
+Attributes to conditional ignore field during serialization.
+
+The attribute can be combined with $(LREF serdeOrderedIn), $(LREF serdeRealOrderedIn).
+
+The predicate should be aplied to the aggregate value, not to the member.
+
+See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIf), $(LREF serdeIgnoreInIfAggregate)
++/
+struct serdeIgnoreOutIfAggregate(alias pred);
+
+/++
++/
+alias serdeGetIgnoreOutIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIfAggregate))[0]);
+
+/++
+Attributes to conditional ignore field during deserialization.
+
+The predicate should be aplied to the aggregate value, not to the member.
+
+See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreIn)
++/
+struct serdeIgnoreInIfAggregate(alias pred);
+
+/++
++/
+alias serdeGetIgnoreInIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreInIfAggregate))[0]);
+
+/++
+Attributes to conditional ignore field during serialization and deserialization.
+
+The attribute can be combined with $(LREF serdeOrderedIn), $(LREF serdeRealOrderedIn).
+
+The predicate should be aplied to the aggregate value, not to the member.
+
+See_also: $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreInIfAggregate) $ $(LREF serdeIgnore)
++/
+struct serdeIgnoreIfAggregate(alias pred);
+
+/++
++/
+alias serdeGetIgnoreIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreIfAggregate))[0]);
+
 /++
 Allows to use flexible deserialization rules such as conversion from input string to numeric types.
 +/
@@ -1768,7 +1817,7 @@ UDA used to force deserializer to initilize members in the order of their defini
 The attribute force deserializer to create a dummy type (recursively), initializer its fields and then assign them to
 to the object members (fields and setters) in the order of their definition.
 
-See_also: $(LREF SerdeOrderedDummy), $(LREF serdeRealOrderedIn).
+See_also: $(LREF SerdeOrderedDummy), $(LREF serdeRealOrderedIn), $(LREF serdeIgnoreInIfAggregate).
 +/
 enum serdeOrderedIn;
 
@@ -1778,7 +1827,7 @@ UDA used to force deserializer to initilize members in the order of their defini
 Unlike $(LREF serdeOrderedIn) `serdeRealOrderedDummy` force deserialzier to iterate all DOM keys for each object deserialization member.
 It is slower but more universal approach.
 
-See_also: $(LREF serdeOrderedIn).
+See_also: $(LREF serdeOrderedIn), $(LREF serdeIgnoreInIfAggregate)
 +/
 enum serdeRealOrderedIn;
 

From d446eca25cf747a872bf74b31dd000e88ad17df1 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Thu, 1 Jul 2021 14:14:48 +0700
Subject: [PATCH 213/573] add rcmap and rcfilter (#350)

* add rcmap and rcfilter

* more tests
---
 source/mir/algorithm/iteration.d |  99 ++++++++++++++++++
 source/mir/ndslice/topology.d    | 170 ++++++++++++++++++++++++++++++-
 source/mir/rc/array.d            |   1 -
 3 files changed, 265 insertions(+), 5 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 7b57eebe..0dad7bd4 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -4089,6 +4089,105 @@ version(mir_test)
     assert(equal!equal(rc, [ [3, 100], [], [400, 102] ]));
 }
 
+/++
+Implements the higher order filter and map function. The predicate and map functions are passed to
+`mir.functional.naryFun`, and can either accept a string, or any callable
+that can be executed via `pred(element)` and `map(element)`.
+Params:
+    pred = Filter function to apply to each element of range (optional)
+    map = Map function to apply to each  element of range
+Returns:
+    `rcfilter!(pred)(range)` returns a new RCArray containing only elements `map(x)` in `range` for
+    which `pred(x)` returns `true`.
+See_Also:
+    $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
++/
+template rcfilter(alias pred = "a", alias map = "a")
+{
+    static if (__traits(isSame, naryFun!pred, pred) && __traits(isSame, naryFun!map, map))
+    {
+        /++ 
+        Params:
+            r = An input range of elements to filter.
+        Returns:
+            A new range containing only elements `x` in `range` for which `predicate(x)` returns `true`.
+        +/
+        auto rcfilter(Range)(Range r)
+            if (isIterable!Range && (!isSlice!Range || DimensionCount!Range == 1))
+        {
+                import core.lifetime: forward;
+                import std.range.primitives: isInputRange;
+                import mir.rc.array: RCArray;
+
+                if (false)
+                {
+                    auto p = pred(r.front);
+                    auto e = map(r.front);
+                    r.popFront;
+                    auto d = r.empty;
+                }
+                return () @trusted
+                {
+                    import mir.appender: ScopedBuffer;
+                    alias T = typeof(map(r.front));
+                    ScopedBuffer!T buffer = void;
+                    buffer.initialize;
+                    foreach (ref e; r)
+                    {
+                        if (pred(e))
+                        {
+                            static if (__traits(isSame, naryFun!"a", map))
+                                buffer.put(forward!e);
+                            else
+                                buffer.put(map(forward!e));
+                        }
+                    }
+                    auto ret = RCArray!T(buffer.length);
+                    buffer.moveDataAndEmplaceTo(ret[]);
+                    return ret;
+                } ();
+        }
+
+        /// ditto
+        auto rcfilter(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+            if (N > 1)
+        {
+            import mir.ndslice.topology: flattened; 
+            import core.lifetime: move;
+            return rcfilter(slice.move.flattened);
+        }
+    }
+    else
+        alias rcfilter = .rcfilter!(naryFun!pred, naryFun!map);
+}
+
+///
+version(mir_test)
+@safe pure nothrow @nogc unittest
+{
+    import mir.ndslice.topology: iota;
+
+    auto val = 3;
+    auto factor = 5;
+    // Filter iota 2x3 matrix below 3
+    assert(iota(2, 3).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
+    // Filter and map below 3
+    assert(6.iota.rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
+}
+
+version(mir_test)
+@safe pure nothrow @nogc unittest
+{
+    import mir.ndslice.topology: iota, as;
+
+    auto val = 3;
+    auto factor = 5;
+    // Filter iota 2x3 matrix below 3
+    assert(iota(2, 3).as!(const int).rcfilter!(a => a < val).moveToSlice.equal(val.iota));
+    // Filter and map below 3
+    assert(6.iota.as!(immutable int).rcfilter!(a => a < val, a => a * factor).moveToSlice.equal(val.iota * factor));
+}
+
 /++
 Implements the homonym function (also known as `accumulate`, $(D
 compress), `inject`, or `foldl`) present in various programming
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index da4b7273..6c3c9c21 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2731,7 +2731,7 @@ Note:
 Params:
     fun = One or more functions.
 See_Also:
-    $(LREF cached), $(LREF vmap), $(LREF indexed),
+    $(LREF cached), $(LREF vmap),  $(LREF rcmap), $(LREF indexed),
     $(LREF pairwise), $(LREF subSlices), $(LREF slide), $(LREF zip),
     $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
 +/
@@ -3041,7 +3041,7 @@ Params:
     slice = ndslice
     callable = callable object, structure, delegate, or function pointer.
 See_Also:
-    $(LREF cached), $(LREF map), $(LREF indexed),
+    $(LREF cached), $(LREF map),  $(LREF rcmap), $(LREF indexed),
     $(LREF pairwise), $(LREF subSlices), $(LREF slide), $(LREF zip),
     $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
 +/
@@ -3181,7 +3181,7 @@ version(none) version(mir_test) unittest
     }
 }
 
-/// Use map with byDim/alongDim to apply functions to each dimension
+/// Use vmap with byDim/alongDim to apply functions to each dimension
 version(mir_test)
 @safe pure
 unittest
@@ -3214,7 +3214,7 @@ unittest
 }
 
 /++
-Use map with a lambda and with byDim/alongDim, but may need to allocate result. 
+Use vmap with a lambda and with byDim/alongDim, but may need to allocate result. 
 This example uses fuse, which allocates. Note: fuse!1 will transpose the result. 
 +/
 version(mir_test)
@@ -3291,6 +3291,168 @@ private auto unhideStride
         return slice;
 }
 
+/++
+Implements the homonym function (also known as `transform`) present
+in many languages of functional flavor. The call `rmap!(fun)(slice)`
+returns an RC array (1D) or  RC slice (ND) of which elements are obtained by applying `fun`
+for all elements in `slice`. The original slices are
+not changed. Evaluation is done eagerly.
+
+Note:
+    $(SUBREF dynamic, transposed) and
+    $(SUBREF topology, pack) can be used to specify dimensions.
+Params:
+    fun = One or more functions.
+See_Also:
+    $(LREF cached), $(LREF map), $(LREF vmap), $(LREF indexed),
+    $(LREF pairwise), $(LREF subSlices), $(LREF slide), $(LREF zip),
+    $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
++/
+template rcmap(fun...)
+    if (fun.length)
+{
+    import mir.functional: adjoin, naryFun, pipe;
+    static if (fun.length == 1)
+    {
+        static if (__traits(isSame, naryFun!(fun[0]), fun[0]))
+        {
+            alias f = fun[0];
+        @optmath:
+            /++
+            Params:
+                slice = An ndslice, array, or an input range.
+            Returns:
+                ndslice or an input range with each fun applied to all the elements. If there is more than one
+                fun, the element type will be `Tuple` containing one element for each fun.
+            +/
+            auto rcmap(Iterator, size_t N, SliceKind kind)
+                (Slice!(Iterator, N, kind) slice)
+            {
+                import core.lifetime: move;
+                auto shape = slice.shape;
+                auto r = slice.move.flattened;
+                if (false)
+                {
+                    auto e = f(r.front);
+                    r.popFront;
+                    auto d = r.empty;
+                }
+                return () @trusted
+                {
+                    import mir.rc.array: RCArray;
+                    import std.traits: Unqual;
+                    import mir.conv: emplaceRef;
+
+                    alias T = typeof(f(r.front));
+                    auto ret = RCArray!T(r.length);
+                    auto next = ret.ptr;
+                    while (!r.empty)
+                    {
+                        emplaceRef(*cast(Unqual!T*)next++, f(r.front));
+                        r.popFront;
+                    }
+                    static if (N == 1)
+                    {
+                        return ret;
+                    }
+                    else
+                    {
+                        return ret.moveToSlice.sliced(shape);
+                    }
+                } ();
+            }
+
+            /// ditto
+            auto rcmap(T)(T[] array)
+            {
+                return rcmap(array.sliced);
+            }
+
+            /// ditto
+            auto rcmap(T)(T withAsSlice)
+                if (hasAsSlice!T)
+            {
+                static if (__traits(hasMember, T, "moveToSlice"))
+                    return rcmap(withAsSlice.moveToSlice);
+                else
+                    return rcmap(withAsSlice.asSlice);
+            }
+
+            /// ditto
+            auto rcmap(Range)(Range r)
+                if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
+            {
+                import core.lifetime: forward;
+                import std.range.primitives: isInputRange;
+                import mir.rc.array: RCArray;
+
+                if (false)
+                {
+                    auto e = f(r.front);
+                    r.popFront;
+                    auto d = r.empty;
+                }
+                return () @trusted
+                {
+                    import mir.appender: ScopedBuffer;
+                    alias T = typeof(f(r.front));
+                    ScopedBuffer!T buffer = void;
+                    buffer.initialize;
+                    while (!r.empty)
+                    {
+                        buffer.put(f(r.front));
+                        r.popFront;
+                    }
+                    auto ret = RCArray!T(buffer.length, false);
+                    buffer.moveDataAndEmplaceTo(ret[]);
+                    return ret;
+                } ();
+            }
+        }
+        else alias rcmap = .rcmap!(staticMap!(naryFun, fun));
+    }
+    else alias rcmap = .rcmap!(adjoin!fun);
+}
+
+/// Returns RCArray for input ranges and one-dimensional slices.
+@safe pure nothrow @nogc
+version(mir_test) unittest
+{
+    import mir.algorithm.iteration: filter, equal;
+    auto factor = 10;
+    auto step = 20;
+    assert (3.iota.rcmap!(a => a * factor).moveToSlice.equal(3.iota * factor)); 
+    assert (6.iota.filter!"a % 2".rcmap!(a => a * factor).moveToSlice.equal([3].iota(factor, step))); 
+}
+
+/// For multidimensional case returns `Slice!(RCI!T, N)`.
+@safe pure nothrow @nogc
+version(mir_test) unittest
+{
+    import mir.ndslice.topology : iota;
+    auto factor = 3;
+    auto s = iota(2, 3).rcmap!(a => a * factor);
+    assert(s == iota(2, 3) * factor);
+}
+
+/// String lambdas
+@safe pure nothrow
+version(mir_test) unittest
+{
+    import mir.ndslice.topology : iota;
+    assert(iota(2, 3).rcmap!"a * 2" == iota(2, 3) * 2);
+}
+
+@safe pure nothrow @nogc
+version(mir_test) unittest
+{
+    import mir.algorithm.iteration: filter, equal;
+    auto factor = 10;
+    auto step = 20;
+    assert (3.iota.as!(const int).rcmap!(a => a * factor).moveToSlice.equal(3.iota * factor)); 
+    assert (6.iota.filter!"a % 2".as!(immutable int).rcmap!(a => a * factor).moveToSlice.equal([3].iota(factor, step))); 
+}
+
 /++
 Creates a random access cache for lazyly computed elements.
 Params:
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 2589554f..7ec93097 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -887,4 +887,3 @@ unittest
     RCArray!int value = null;
     value = null;
 }
-

From 9e56a8205331e5c72369670d93e8ebc670b0d133 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 2 Jul 2021 19:41:56 +0700
Subject: [PATCH 214/573] fix string map and add toString

---
 source/mir/algebraic_alias/json.d |  4 ++++
 source/mir/serde.d                |  6 ++---
 source/mir/string_map.d           | 38 +++++++++++++++++++++++++------
 3 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index d9ed72d7..8795faed 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -124,4 +124,8 @@ unittest
 
     JsonAlgebraic[string] aa = object.toAA;
     object = StringMap!JsonAlgebraic(aa);
+
+    JsonAlgebraic fromAA = ["a" : JsonAlgebraic(3), "b" : JsonAlgebraic("b")];
+    assert(fromAA.get!(StringMap!JsonAlgebraic)["a"] == 3);
+    assert(fromAA.get!(StringMap!JsonAlgebraic)["b"] == "b");
 }
diff --git a/source/mir/serde.d b/source/mir/serde.d
index dd6161b4..b474dc27 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -529,8 +529,6 @@ alias serdeGetIgnoreOutIf(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbo
 /++
 Attributes to conditional ignore field during serialization.
 
-The attribute can be combined with $(LREF serdeOrderedIn), $(LREF serdeRealOrderedIn).
-
 The predicate should be aplied to the aggregate value, not to the member.
 
 See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIf), $(LREF serdeIgnoreInIfAggregate)
@@ -544,7 +542,7 @@ alias serdeGetIgnoreOutIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getU
 /++
 Attributes to conditional ignore field during deserialization.
 
-The predicate should be aplied to the aggregate value, not to the member.
+The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate.
 
 See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreIn)
 +/
@@ -557,7 +555,7 @@ alias serdeGetIgnoreInIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUD
 /++
 Attributes to conditional ignore field during serialization and deserialization.
 
-The attribute can be combined with $(LREF serdeOrderedIn), $(LREF serdeRealOrderedIn).
+The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate.
 
 The predicate should be aplied to the aggregate value, not to the member.
 
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index f294f393..a474f4fa 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -111,6 +111,34 @@ struct StringMap(T, U = uint)
         assert(map.findPos("key") == 0);
     }
 
+    ///
+    string toString() const
+    {
+        import mir.format: stringBuf;
+        stringBuf buffer;
+        toString(buffer);
+        return buffer.data.idup;
+    }
+
+    ///ditto
+    void toString(W)(scope ref W w) const
+    {
+        bool next;
+        w.put('[');
+        import mir.format: printEscaped, EscapeFormat, print;
+        foreach (i, ref value; values)
+        {
+            if (next)
+                w.put(`, `);
+            next = true;
+            w.put('\"');
+            printEscaped!(char, EscapeFormat.ion)(w, keys[i]);
+            w.put(`": `);
+            print(w, value);
+        }
+        w.put(']');
+    }
+
     /++
     Constructs an associative array using keys and values.
     Params:
@@ -704,7 +732,7 @@ private struct StructImpl(T, U = uint)
     this()(string[] keys, T[] values) @trusted pure nothrow
     {
         import mir.array.allocation: array;
-        import mir.ndslice.sorting: sort;
+        import mir.ndslice.sorting: makeIndex;
         import mir.ndslice.topology: iota, indexed;
         import mir.string_table: smallerStringFirst;
 
@@ -714,13 +742,9 @@ private struct StructImpl(T, U = uint)
         _length = keys.length;
         _keys = keys.ptr;
         _values = values.ptr;
-        _indices = keys.length.iota!U.array.ptr;
+        _indices = keys.makeIndex!(U, smallerStringFirst).ptr;
         auto sortedKeys = _keys.indexed(indices);
-        sortedKeys.sort!smallerStringFirst;
-        size_t maxKeyLength;
-        foreach (ref key; keys)
-            if (key.length > maxKeyLength)
-                maxKeyLength = key.length;
+        size_t maxKeyLength = sortedKeys[$ - 1].length;
         _lengthTable = new U[maxKeyLength + 2];
 
         size_t ski;

From 1dde4580782387eb1dc2e80d10fbb1a3be9d99e0 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 2 Jul 2021 20:50:36 +0700
Subject: [PATCH 215/573] add opequals

---
 source/mir/string_map.d | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index a474f4fa..85b76819 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -39,6 +39,12 @@ struct StringMap(T, U = uint)
     private alias Impl = StructImpl!(T, U);
     private Impl* implementation;
 
+    ///
+    bool opEquals()(const StringMap rhs) const
+    {
+        return keys == rhs.keys && values == rhs.values;
+    }
+
     // // linking bug
     // version(none)
     // {
@@ -713,6 +719,8 @@ unittest
     assert(table.values == [3, 11]);
     assert(table["L"] == 3);
     assert(table["val"] == 11);
+
+    assert(table == table);
 }
 
 private struct StructImpl(T, U = uint)

From c8e0fcbd6f5d13755ac164724b62fa335d196e2f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 3 Jul 2021 21:42:12 +0700
Subject: [PATCH 216/573] use new mir-primitives

---
 source/mir/algorithm/iteration.d  | 8 ++++++--
 source/mir/algorithm/setops.d     | 1 -
 source/mir/array/allocation.d     | 2 --
 source/mir/container/binaryheap.d | 6 ++----
 source/mir/date.d                 | 4 ++--
 source/mir/math/numeric.d         | 2 +-
 source/mir/math/stat.d            | 2 +-
 source/mir/ndslice/topology.d     | 4 ++--
 source/mir/parse.d                | 1 -
 source/mir/rc/array.d             | 2 +-
 source/mir/series.d               | 2 +-
 11 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 0dad7bd4..b4578fa9 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -62,7 +62,6 @@ import mir.ndslice.slice;
 import mir.primitives;
 import mir.qualifier;
 import std.meta;
-import std.range.primitives: isInputRange, isBidirectionalRange, isInfinite, isForwardRange, ElementType;
 import std.traits;
 
 /++
@@ -3857,6 +3856,8 @@ struct Uniq(alias pred, Range)
         return _input.front;
     }
 
+    import std.range.primitives: isBidirectionalRange;
+
     static if (isBidirectionalRange!Range)
     {
         void popBack() scope
@@ -3886,6 +3887,8 @@ struct Uniq(alias pred, Range)
         @property bool empty() const { return _input.empty; }
     }
 
+    import std.range.primitives: isForwardRange;
+
     static if (isForwardRange!Range)
     {
         @property typeof(this) save() scope return
@@ -4022,6 +4025,7 @@ struct Filter(alias pred, Range)
         }
     }
 
+    import std.range.primitives: isForwardRange;
     static if (isForwardRange!Range)
     {
         @property typeof(this) save() scope return
@@ -4116,7 +4120,7 @@ template rcfilter(alias pred = "a", alias map = "a")
             if (isIterable!Range && (!isSlice!Range || DimensionCount!Range == 1))
         {
                 import core.lifetime: forward;
-                import std.range.primitives: isInputRange;
+                import mir.primitives: isInputRange;
                 import mir.rc.array: RCArray;
 
                 if (false)
diff --git a/source/mir/algorithm/setops.d b/source/mir/algorithm/setops.d
index 4ec593c0..3671571a 100644
--- a/source/mir/algorithm/setops.d
+++ b/source/mir/algorithm/setops.d
@@ -52,7 +52,6 @@ struct MultiwayMerge(alias less, RangeOfRanges)
 {
     import mir.primitives;
     import mir.container.binaryheap;
-    import std.range.primitives: ElementType;
 
     ///
     @disable this();
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index 4d418fd4..f40235da 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -24,9 +24,7 @@ module mir.array.allocation;
 
 import mir.functional;
 import mir.primitives;
-
 import std.traits;
-import std.range.primitives: isInfinite, isInputRange, ElementType;
 
 /**
  * Allocates an array and initializes it with copies of the elements
diff --git a/source/mir/container/binaryheap.d b/source/mir/container/binaryheap.d
index 2b79ecce..7b42d2a6 100644
--- a/source/mir/container/binaryheap.d
+++ b/source/mir/container/binaryheap.d
@@ -15,9 +15,7 @@ Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Ya
 module mir.container.binaryheap;
 
 import mir.primitives;
-import mir.primitives;
-
-import std.range.primitives: isRandomAccessRange, hasSwappableElements, ElementType;
+import std.range.primitives: isRandomAccessRange, hasSwappableElements;
 import std.traits;
 
 ///
@@ -384,7 +382,7 @@ BinaryHeap!(less, Store) heapify(alias less = "a < b", Store)(Store s,
 @system nothrow version(mir_test) unittest
 {
     // Test range interface.
-    import std.range.primitives: isInputRange;
+    import mir.primitives: isInputRange;
     import mir.algorithm.iteration : equal;
     int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7];
     auto h = heapify(a);
diff --git a/source/mir/date.d b/source/mir/date.d
index b1e0dd3f..b7354e0c 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -29,10 +29,10 @@ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like
 +/
 module mir.date;
 
+import mir.primitives: isOutputRange;
 import mir.serde: serdeProxy, serdeScoped;
 import mir.timestamp: Timestamp;
-import std.range.primitives : isOutputRange;
-import std.traits : isSomeChar, Unqual;
+import std.traits: isSomeChar, Unqual;
 
 version(mir_test)
 version(D_Exceptions)
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index 8b6001c0..7bfc07e3 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -9,7 +9,7 @@ module mir.math.numeric;
 
 import mir.math.common;
 import mir.primitives;
-import std.range.primitives: isInputRange;
+import mir.primitives: isInputRange;
 import std.traits: CommonType, Unqual, isIterable, ForeachType, isPointer;
 import mir.internal.utility: isFloatingPoint;
 
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index d24d0c92..69e37337 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -3454,7 +3454,7 @@ A linear regression model with a single explanatory variable.
 template simpleLinearRegression(Summation summation = Summation.kbn)
 {
     import mir.ndslice.slice;
-    import std.range.primitives: isInputRange;
+    import mir.primitives: isInputRange;
 
     /++
     Params:
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 6c3c9c21..c70d535f 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2781,7 +2781,7 @@ template map(fun...)
                 if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
             {
                 import core.lifetime: forward;
-                import std.range.primitives: isInputRange;
+                import mir.primitives: isInputRange;
                 static assert (isInputRange!Range, "map can work with ndslice, array, or an input range.");
                 return MapRange!(f, ImplicitlyUnqual!Range)(forward!r);
             }
@@ -3383,7 +3383,7 @@ template rcmap(fun...)
                 if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
             {
                 import core.lifetime: forward;
-                import std.range.primitives: isInputRange;
+                import mir.primitives: isInputRange;
                 import mir.rc.array: RCArray;
 
                 if (false)
diff --git a/source/mir/parse.d b/source/mir/parse.d
index bfff58e6..753b6c60 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -27,7 +27,6 @@ unittest
 }
 
 import mir.primitives;
-import std.range.primitives: isInputRange;
 import std.traits: isMutable, isFloatingPoint, isSomeChar;
 
 /++
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 7ec93097..88518853 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -361,7 +361,7 @@ RCArray!V rcarray(T = void, V)(scope V[] values, bool deallocate)
 template rcarray(T)
     if(!is(T == E[], E) && !is(T == void))
 {
-    import std.range.primitives: isInputRange, isInfinite;
+    import mir.primitives: isInputRange, isInfinite;
 
     ///
     auto rcarray(Range)(ref Range range)
diff --git a/source/mir/series.d b/source/mir/series.d
index 2e717c06..b37b53f9 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -1894,7 +1894,7 @@ Params:
 +/
 template troykaGalop(alias lfun, alias cfun, alias rfun)
 {
-    import std.range.primitives: isInputRange;
+    import mir.primitives: isInputRange;
 
     /++
     Params:

From a60026e379e799bfb2f2415750b45b9c2d27d32d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 3 Jul 2021 21:48:54 +0700
Subject: [PATCH 217/573] add StringMap.opIndexAssign generic overload

---
 source/mir/algebraic_alias/json.d | 21 +++++++--------------
 source/mir/string_map.d           |  9 +++++++++
 2 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index 8795faed..4925f4d4 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -58,8 +58,7 @@ unittest
     assert(value.kind == JsonAlgebraic.Kind.null_);
 
     // Boolean
-    value = true;
-    object["bool"] = value;
+    value = object["bool"] = true;
     assert(!value.isNull);
     assert(value == true);
     assert(value.kind == JsonAlgebraic.Kind.boolean);
@@ -67,8 +66,7 @@ unittest
     assert(value.get!(JsonAlgebraic.Kind.boolean) == true);
 
     // Null
-    value = null;
-    object["null"] = value;
+    value = object["null"] = null;
     assert(value.isNull);
     assert(value == null);
     assert(value.kind == JsonAlgebraic.Kind.null_);
@@ -76,16 +74,14 @@ unittest
     assert(value.get!(JsonAlgebraic.Kind.null_) == null);
 
     // String
-    value = "s";
-    object["string"] = value;
+    value = object["string"] = "s";
     assert(value.kind == JsonAlgebraic.Kind.string);
     assert(value == "s");
     assert(value.get!string == "s");
     assert(value.get!(JsonAlgebraic.Kind.string) == "s");
 
     // Integer
-    value = 4;
-    object["integer"] = value;
+    value = object["integer"] = 4;
     assert(value.kind == JsonAlgebraic.Kind.integer);
     assert(value == 4);
     assert(value != 4.0);
@@ -93,8 +89,7 @@ unittest
     assert(value.get!(JsonAlgebraic.Kind.integer) == 4);
 
     // Float
-    value = 3.0;
-    object["float"] = value;
+    value = object["float"] = 3.0;
     assert(value.kind == JsonAlgebraic.Kind.float_);
     assert(value != 3);
     assert(value == 3.0);
@@ -104,8 +99,7 @@ unittest
     // Array
     JsonAlgebraic[] arr = [0, 1, 2, 3, 4].map!JsonAlgebraic.array;
 
-    value = arr;
-    object["array"] = value;
+    value = object["array"] = arr;
     assert(value.kind == JsonAlgebraic.Kind.array);
     assert(value == arr);
     assert(value.get!(JsonAlgebraic[])[3] == 3);
@@ -116,8 +110,7 @@ unittest
     assert(object["bool"] == "false"); // it is a string now
     object.remove("bool"); // remove the member
 
-    value = object;
-    object["array"] = value;
+    value = object["array"] = object;
     assert(value.kind == JsonAlgebraic.Kind.object);
     assert(value.get!(StringMap!JsonAlgebraic).keys is object.keys);
     assert(value.get!(StringMap!JsonAlgebraic).values is object.values);
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 85b76819..a04ec04b 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -527,6 +527,15 @@ struct StringMap(T, U = uint)
     /++
     Complexity: `O(log(s))` (exist) or `O(n)` (not exist), where `s` is the count of the strings with the same length as they key.
     +/
+    ref T opIndexAssign(R)(auto ref R value, string key) @trusted pure nothrow
+    {
+        import core.lifetime: forward, move;
+        T v;
+        v = forward!value;
+        return opIndexAssign(move(v), key);
+    }
+
+    /// ditto
     ref T opIndexAssign()(T value, string key) @trusted pure nothrow
     {
         size_t index;

From c1a29d2b9783191784725955a6ee2cd85613d595 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 3 Jul 2021 21:49:28 +0700
Subject: [PATCH 218/573] update core

---
 dub.sdl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index 3b642813..1dae75a8 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.1.67"
+dependency "mir-core" version=">=1.1.75"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"

From fe74b87b9be21e21e31a82b3f73d730994518e8e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 4 Jul 2021 09:47:01 +0700
Subject: [PATCH 219/573] add SerdeTarget

---
 source/mir/serde.d | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index b474dc27..a9d0b1c7 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -65,6 +65,28 @@ version (D_Exceptions)
     }
 }
 
+/++
+Helper enumeration for for serializer .
+Use negative `int` values for user defined targets.
++/
+enum SerdeTarget : int
+{
+    ///
+    ion,
+    ///
+    json,
+    ///
+    cbor,
+    ///
+    msgpack,
+    ///
+    yaml,
+    ///
+    csv,
+    ///
+    excel,
+}
+
 /++
 Attribute for key overloading during Serialization and Deserialization.
 The first argument overloads the key value during serialization unless `serdeKeyOut` is given.

From aae7492d0c45dd73c525eedae734e8ffb689536a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 4 Jul 2021 12:07:15 +0700
Subject: [PATCH 220/573] remove deprecated serialization code

---
 source/mir/date.d        | 18 +-----------------
 source/mir/small_array.d | 27 ++++++++-------------------
 2 files changed, 9 insertions(+), 36 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index b7354e0c..72e3578b 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -30,7 +30,7 @@ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like
 module mir.date;
 
 import mir.primitives: isOutputRange;
-import mir.serde: serdeProxy, serdeScoped;
+import mir.serde: serdeProxy;
 import mir.timestamp: Timestamp;
 import std.traits: isSomeChar, Unqual;
 
@@ -2167,22 +2167,6 @@ package:
     }
 
     int _julianDay;
-
-    /// ASDF(JSON) serialization support
-    public void serialize(S)(ref S serializer) pure
-    {
-        import mir.format: print;
-        import mir.small_string : SmallString;
-        SmallString!16 w;
-        print(w, this);
-        serializer.putValue(w[]);
-    }
-
-    /// ASDF(JSON) deserialization support
-    public static Date deserialize(D)(auto ref D deserializer)
-    {
-        return fromString(cast(const(char)[])deserializer);
-    }
 }
 
 /// ditto
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 11e131db..35a2370c 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -20,34 +20,23 @@ version(D_Exceptions)
 template SmallArray(T, uint maxLength)
     if (maxLength)
 {
+    import std.traits: Unqual, isIterable, isImplicitlyConvertible;
+
+    static if (isImplicitlyConvertible!(const T, T))
+        alias V = const T;
+    else
+        alias V = T;
+
     ///
-    @serdeProxy!(T[])
+    @serdeProxy!(V[])
     struct SmallArray
     {
-        import std.traits: Unqual, isIterable, isImplicitlyConvertible;
-
         uint _length;
         T[maxLength] _data;
 
         ///
         alias serdeKeysProxy = T;
 
-        static SmallArray deserialize(S)(S data)
-        {
-            import asdf.serialization: deserialize;
-            return SmallArray(data.deserialize!(T[]));
-        }
-
-        void serialize(S)(ref S serializer)
-        {
-            serializer.putValue(opIndex);
-        }
-
-        static if (isImplicitlyConvertible!(const T, T))
-            alias V = const T;
-        else
-            alias V = T;
-
     @safe pure @nogc:
 
         /// Constructor

From 99ebc0c26afd149108e3194d0d47d994fc3cf8b3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 4 Jul 2021 12:08:23 +0700
Subject: [PATCH 221/573] add to/from timestamp conversions with phobos types

---
 source/mir/timestamp.d | 130 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 1605b79a..64928ebc 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -4,6 +4,7 @@ Timestamp
 module mir.timestamp;
 
 private alias isDigit = (dchar c) => uint(c - '0') < 10;
+import mir.serde: serdeIgnore;
 
 version(D_Exceptions)
 ///
@@ -106,6 +107,8 @@ struct Timestamp
         }
     }
 
+@serdeIgnore:
+
     /++
     Year
     +/
@@ -258,6 +261,133 @@ struct Timestamp
         this.precision = Precision.fraction;
     }
 
+    ///
+    this(Date)(const Date datetime)
+        if (Date.stringof == "Date" || Date.stringof == "date")
+    {
+        static if (__traits(hasMember, Date, "yearMonthDay"))
+            with(datetime.yearMonthDay) this(year, cast(ubyte)month, day);
+        else
+            with(datetime) this(year, month, day);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest {
+        import mir.date : Date;
+        auto dt = Date(1982, 4, 1);
+        Timestamp ts = dt;
+        assert(dt.toISOExtString == ts.toString);
+        assert(dt == cast(Date) ts);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest {
+        import std.datetime.date : Date;
+        auto dt = Date(1982, 4, 1);
+        Timestamp ts = dt;
+        assert(dt.toISOExtString == ts.toString);
+        assert(dt == cast(Date) ts);
+    }
+
+    ///
+    this(DateTime)(const DateTime datetime)
+        if (DateTime.stringof == "DateTime")
+    {
+        with(datetime) this(year, cast(ubyte)month, day, hour, minute, second);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest {
+        import std.datetime.date : DateTime;
+        auto dt = DateTime(1982, 4, 1, 20, 59, 22);
+        Timestamp ts = dt;
+        assert(dt.toISOExtString ~ "Z" == ts.toString);
+        assert(dt == cast(DateTime) ts);
+    }
+
+    ///
+    this(SysTime)(const SysTime systime)
+        if (SysTime.stringof == "SysTime")
+    {
+        with(systime.toUTC) this(year, month, day, hour, minute, second, -7, fracSecs.total!"hnsecs");
+        offset = cast(short) systime.utcOffset.total!"minutes";
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest {
+        import core.time : hnsecs, minutes;
+        import std.datetime.date : DateTime;
+        import std.datetime.timezone : SimpleTimeZone;
+        import std.datetime.systime : SysTime;
+
+        auto dt = DateTime(1982, 4, 1, 20, 59, 22);
+        auto tz = new immutable SimpleTimeZone(-330.minutes);
+        auto st = SysTime(dt, 1234567.hnsecs, tz);
+        Timestamp ts = st;
+
+        assert(st.toISOExtString == ts.toString);
+        assert(st == cast(SysTime) ts);
+    }
+
+    ///
+    T opCast(T)() const
+        if (T.stringof == "YearMonth"
+         || T.stringof == "YearMonthDay"
+         || T.stringof == "Date"
+         || T.stringof == "date"
+         || T.stringof == "DateTime"
+         || T.stringof == "SysTime")
+    {
+        static if (T.stringof == "YearMonth")
+        {
+            return T(year, month, day);
+        }
+        else
+        static if (T.stringof == "Date" || T.stringof == "date" || T.stringof == "YearMonthDay")
+        {
+            return T(year, month, day);
+        }
+        else
+        static if (T.stringof == "DateTime")
+        {
+            return T(year, month, day, hour, minute, second);
+        }
+        else
+        static if (T.stringof == "SysTime")
+        {
+            import core.time : hnsecs, minutes;
+            import std.datetime.date: DateTime;
+            import std.datetime.systime: SysTime;
+            import std.datetime.timezone: UTC, SimpleTimeZone;
+            auto ret = SysTime(DateTime(year, month, day, hour, minute, second), UTC());
+            if (fractionCoefficient)
+            {
+                long coeff = fractionCoefficient;
+                int exp = fractionExponent;
+                while (exp > -7)
+                {
+                    exp--;
+                    coeff *= 10;
+                }
+                while (exp < -7)
+                {
+                    exp++;
+                    coeff /= 10;
+                }
+                ret.fracSecs = coeff.hnsecs;
+            }
+            if (offset)
+            {
+                ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes));
+            }
+            return ret;
+        }
+    }
+
     /++
     Attaches local offset, doesn't adjust other fields.
     Local-time offsets may be represented as either `hour*60+minute` offsets from UTC,

From 979e39fca08615129e55e4ca0aa4b75fabe750a8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 4 Jul 2021 13:45:15 +0700
Subject: [PATCH 222/573] update mir serde for timestamps auto proxy

---
 source/mir/serde.d | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index a9d0b1c7..43c0f857 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -973,6 +973,7 @@ Final proxy type
 +/
 template serdeGetFinalProxy(T)
 {
+    import mir.timestamp: Timestamp;
     import std.traits: hasUDA, isAggregateType;
     static if (isAggregateType!T || is(T == enum))
     {
@@ -981,6 +982,11 @@ template serdeGetFinalProxy(T)
             alias serdeGetFinalProxy = .serdeGetFinalProxy!(serdeGetProxy!T);
         }
         else
+        static if (is(typeof(Timestamp(T.init))))
+        {
+            alias serdeGetFinalProxy = string;
+        }
+        else
         {
             alias serdeGetFinalProxy = T;
         }
@@ -1013,6 +1019,7 @@ Final deep proxy type
 +/
 template serdeGetFinalDeepProxy(T)
 {
+    import mir.timestamp: Timestamp;
     import std.traits: Unqual, hasUDA, isAggregateType, isArray, ForeachType;
     static if (isAggregateType!T || is(T == enum))
     {
@@ -1021,6 +1028,11 @@ template serdeGetFinalDeepProxy(T)
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(serdeGetProxy!T);
         }
         else
+        static if (is(typeof(Timestamp(T.init))))
+        {
+            alias serdeGetFinalDeepProxy = string;
+        }
+        else
         static if (__traits(hasMember, T, "serdeKeysProxy"))
         {
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(T.serdeKeysProxy);

From 17c1e9634b2d7815f73e86f57be586a67404140e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 5 Jul 2021 22:26:39 +0700
Subject: [PATCH 223/573] add Timestamp.opCmp

---
 source/mir/timestamp.d | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 64928ebc..652c63c1 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -271,12 +271,47 @@ struct Timestamp
             with(datetime) this(year, month, day);
     }
 
+    int opCmp(Timestamp rhs) const @safe pure nothrow @nogc
+    {
+        import std.meta: AliasSeq;
+        static foreach (member; [
+            "year",
+            "month",
+            "day",
+            "hour",
+            "minute",
+            "second",
+        ])
+            if (auto d = int(__traits(getMember, this, member)) - int(__traits(getMember, rhs, member)))
+                return d;
+        int frel = this.fractionExponent;
+        int frer = rhs.fractionExponent;
+        ulong frcl = this.fractionCoefficient;
+        ulong frcr = rhs.fractionCoefficient;
+        while(frel > frer)
+        {
+            frel--;
+            frcl *= 10;
+        }
+        while(frer > frel)
+        {
+            frer--;
+            frcr *= 10;
+        }
+        if (frcl < frcr) return -1;
+        if (frcl > frcr) return +1;
+        if (auto d = int(this.fractionExponent) - int(rhs.fractionExponent))
+            return d;
+        return int(this.offset) - int(rhs.offset);
+    }
+
     ///
     version (mir_test)
     @safe unittest {
         import mir.date : Date;
         auto dt = Date(1982, 4, 1);
         Timestamp ts = dt;
+        assert(ts.opCmp(ts) == 0);
         assert(dt.toISOExtString == ts.toString);
         assert(dt == cast(Date) ts);
     }

From d59f9c1de71d436ff6a7787744922d97a1a61f56 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 7 Jul 2021 15:07:09 +0700
Subject: [PATCH 224/573] Timestamp: support bloomberg and YAML time of day

---
 source/mir/serde.d     |   4 +
 source/mir/timestamp.d | 303 +++++++++++++++++++++++++++--------------
 2 files changed, 204 insertions(+), 103 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 43c0f857..0171c299 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -85,6 +85,10 @@ enum SerdeTarget : int
     csv,
     ///
     excel,
+    ///
+    bloomberg,
+    ///
+    typedJson,
 }
 
 /++
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 652c63c1..b143f5af 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -38,7 +38,7 @@ Timestamp
 Note: The component values in the binary encoding are always in UTC, while components in the text encoding are in the local time!
 This means that transcoding requires a conversion between UTC and local time.
 
-`Timestamp` precision is up to `10^-12` seconds;
+`Timestamp` precision is up to picosecond (second/10^12).
 +/
 struct Timestamp
 {
@@ -75,6 +75,14 @@ struct Timestamp
         assert(Timestamp("20100704") == Timestamp(2010, 7, 4));
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
         static assert(Timestamp(2021, 01, 29,  4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30"));
+
+        assert(Timestamp("T0740Z") == Timestamp.onlyTime(7, 40));
+        assert(Timestamp("T074030Z") == Timestamp.onlyTime(7, 40, 30));
+        assert(Timestamp("T074030.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56));
+
+        assert(Timestamp("07:40Z") == Timestamp.onlyTime(7, 40));
+        assert(Timestamp("07:40:30Z") == Timestamp.onlyTime(7, 40, 30));
+        assert(Timestamp("T07:40:30.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56));
     }
 
     version(all)
@@ -261,6 +269,27 @@ struct Timestamp
         this.precision = Precision.fraction;
     }
 
+    ///
+    @safe pure nothrow @nogc
+    static Timestamp onlyTime(ubyte hour, ubyte minute)
+    {
+        return Timestamp(0, 0, 0, hour, minute);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    static Timestamp onlyTime(ubyte hour, ubyte minute, ubyte second)
+    {
+        return Timestamp(0, 0, 0, hour, minute, second);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    static Timestamp onlyTime(ubyte hour, ubyte minute, ubyte second, byte fractionExponent, ulong fractionCoefficient)
+    {
+        return Timestamp(0, 0, 0, hour, minute, second, fractionExponent, fractionCoefficient);
+    }
+
     ///
     this(Date)(const Date datetime)
         if (Date.stringof == "Date" || Date.stringof == "date")
@@ -271,40 +300,6 @@ struct Timestamp
             with(datetime) this(year, month, day);
     }
 
-    int opCmp(Timestamp rhs) const @safe pure nothrow @nogc
-    {
-        import std.meta: AliasSeq;
-        static foreach (member; [
-            "year",
-            "month",
-            "day",
-            "hour",
-            "minute",
-            "second",
-        ])
-            if (auto d = int(__traits(getMember, this, member)) - int(__traits(getMember, rhs, member)))
-                return d;
-        int frel = this.fractionExponent;
-        int frer = rhs.fractionExponent;
-        ulong frcl = this.fractionCoefficient;
-        ulong frcr = rhs.fractionCoefficient;
-        while(frel > frer)
-        {
-            frel--;
-            frcl *= 10;
-        }
-        while(frer > frel)
-        {
-            frer--;
-            frcr *= 10;
-        }
-        if (frcl < frcr) return -1;
-        if (frcl > frcr) return +1;
-        if (auto d = int(this.fractionExponent) - int(rhs.fractionExponent))
-            return d;
-        return int(this.offset) - int(rhs.offset);
-    }
-
     ///
     version (mir_test)
     @safe unittest {
@@ -326,6 +321,23 @@ struct Timestamp
         assert(dt == cast(Date) ts);
     }
 
+    ///
+    this(TimeOfDay)(const TimeOfDay timeOfDay)
+        if (TimeOfDay.stringof == "TimeOfDay")
+    {
+        with(timeOfDay) this = onlyTime(hour, minute, second);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest {
+        import std.datetime.date : TimeOfDay;
+        auto dt = TimeOfDay(7, 14, 30);
+        Timestamp ts = dt;
+        assert(dt.toISOExtString ~ "Z" == ts.toString);
+        assert(dt == cast(TimeOfDay) ts);
+    }
+
     ///
     this(DateTime)(const DateTime datetime)
         if (DateTime.stringof == "DateTime")
@@ -373,6 +385,7 @@ struct Timestamp
         if (T.stringof == "YearMonth"
          || T.stringof == "YearMonthDay"
          || T.stringof == "Date"
+         || T.stringof == "TimeOfDay"
          || T.stringof == "date"
          || T.stringof == "DateTime"
          || T.stringof == "SysTime")
@@ -392,6 +405,11 @@ struct Timestamp
             return T(year, month, day, hour, minute, second);
         }
         else
+        static if (T.stringof == "TimeOfDay")
+        {
+            return T(hour, minute, second);
+        }
+        else
         static if (T.stringof == "SysTime")
         {
             import core.time : hnsecs, minutes;
@@ -423,6 +441,49 @@ struct Timestamp
         }
     }
 
+    /++
+    Returns: true if timestamp represent a time only value.
+    +/
+    bool isOnlyTime() @property const @safe pure nothrow @nogc
+    {
+        return precision > Precision.day && day == 0;
+    }
+
+    ///
+    int opCmp(Timestamp rhs) const @safe pure nothrow @nogc
+    {
+        import std.meta: AliasSeq;
+        static foreach (member; [
+            "year",
+            "month",
+            "day",
+            "hour",
+            "minute",
+            "second",
+        ])
+            if (auto d = int(__traits(getMember, this, member)) - int(__traits(getMember, rhs, member)))
+                return d;
+        int frel = this.fractionExponent;
+        int frer = rhs.fractionExponent;
+        ulong frcl = this.fractionCoefficient;
+        ulong frcr = rhs.fractionCoefficient;
+        while(frel > frer)
+        {
+            frel--;
+            frcl *= 10;
+        }
+        while(frer > frel)
+        {
+            frer--;
+            frcr *= 10;
+        }
+        if (frcl < frcr) return -1;
+        if (frcl > frcr) return +1;
+        if (auto d = int(this.fractionExponent) - int(rhs.fractionExponent))
+            return d;
+        return int(this.offset) - int(rhs.offset);
+    }
+
     /++
     Attaches local offset, doesn't adjust other fields.
     Local-time offsets may be represented as either `hour*60+minute` offsets from UTC,
@@ -457,7 +518,7 @@ struct Timestamp
     Returns:
         A `string` when not using an output range; `void` otherwise.
     +/
-    alias toISOExtString = toISOStringImp!true;
+    alias toString = toISOExtString;
 
     ///
     version (mir_test)
@@ -476,6 +537,10 @@ struct Timestamp
         assert(Timestamp(2021, 01, 29, 19, 42).toString == "2021-01-29T19:42Z");
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString == "2021-01-29T19:42:44+07", Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString);
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toString == "2021-01-29T20:12:44+07:30");
+
+        assert(Timestamp.onlyTime(7, 40).toString == "07:40Z");
+        assert(Timestamp.onlyTime(7, 40, 30).toString == "07:40:30Z");
+        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toString == "07:40:30.056Z");
     }
 
     ///
@@ -497,6 +562,10 @@ struct Timestamp
         assert(Timestamp(-9999, 7, 4).toISOExtString == "-9999-07-04");
         assert(Timestamp(-10000, 10, 20).toISOExtString == "-10000-10-20");
 
+        assert(Timestamp.onlyTime(7, 40).toISOExtString == "07:40Z");
+        assert(Timestamp.onlyTime(7, 40, 30).toISOExtString == "07:40:30Z");
+        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOExtString == "07:40:30.056Z");
+
         const cdate = Timestamp(1999, 7, 6);
         immutable idate = Timestamp(1999, 7, 6);
         assert(cdate.toISOExtString == "1999-07-06");
@@ -504,7 +573,7 @@ struct Timestamp
     }
 
     /// ditto
-    alias toString = toISOExtString;
+    alias toISOExtString = toISOStringImp!true;
 
     /++
     Converts this $(LREF Timestamp) to a string with the format `YYYYMMDDThhmmss±hhmm`.
@@ -535,6 +604,10 @@ struct Timestamp
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toISOString == "20210129T194244+07");
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
         static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
+
+        assert(Timestamp.onlyTime(7, 40).toISOString == "T0740Z");
+        assert(Timestamp.onlyTime(7, 40, 30).toISOString == "T074030Z");
+        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOString == "T074030.056Z");
     }
 
     /// Helpfer for time zone offsets
@@ -593,28 +666,33 @@ struct Timestamp
                 t.addMinutes(t.offset);
             }
 
-            if (t.year >= 10_000)
-                w.put('+');
-            printZeroPad(w, t.year, t.year >= 0 ? t.year < 10_000 ? 4 : 5 : t.year > -10_000 ? 5 : 6);
-            if (precision == Precision.year)
+            if (!t.isOnlyTime)
             {
-                w.put('T');
-                return;
-            }
-            if (ext || precision == Precision.month) w.put('-');
+                if (t.year >= 10_000)
+                    w.put('+');
+                printZeroPad(w, t.year, t.year >= 0 ? t.year < 10_000 ? 4 : 5 : t.year > -10_000 ? 5 : 6);
+                if (precision == Precision.year)
+                {
+                    w.put('T');
+                    return;
+                }
+                if (ext || precision == Precision.month) w.put('-');
 
-            printZeroPad(w, cast(uint)t.month, 2);
-            if (precision == Precision.month)
-            {
-                w.put('T');
-                return;
+                printZeroPad(w, cast(uint)t.month, 2);
+                if (precision == Precision.month)
+                {
+                    w.put('T');
+                    return;
+                }
+                static if (ext) w.put('-');
+
+                printZeroPad(w, t.day, 2);
+                if (precision == Precision.day)
+                    return;
             }
-            static if (ext) w.put('-');
 
-            printZeroPad(w, t.day, 2);
-            if (precision == Precision.day)
-                return;
-            w.put('T');
+            if (!ext || !t.isOnlyTime)
+                w.put('T');
 
             printZeroPad(w, t.hour, 2);
             static if (ext) w.put(':');
@@ -939,69 +1017,89 @@ struct Timestamp
         {
             import mir.parse: fromString, parse;
 
-            // YYYY
             static if (ext)
-            {{
-                auto startIsDigit = str.length && str[0].isDigit;
-                auto strOldLength = str.length;
-                if (!parse(str, value.year))
-                    return false;
-                auto l = strOldLength - str.length;
-                if ((l == 4) != startIsDigit)
-                    return false;
-            }}
+                auto isOnlyTime = str.length >= 3 && (str[0] == 'T' || str[2] == ':');
             else
+                auto isOnlyTime = str.length >= 3 && str[0] == 'T';
+
+            if (!isOnlyTime)
             {
-                if (str.length < 4 || !str[0].isDigit || !fromString(str[0 .. 4], value.year))
+                // YYYY
+                static if (ext)
+                {{
+                    auto startIsDigit = str.length && str[0].isDigit;
+                    auto strOldLength = str.length;
+                    if (!parse(str, value.year))
+                        return false;
+                    auto l = strOldLength - str.length;
+                    if ((l == 4) != startIsDigit)
+                        return false;
+                }}
+                else
+                {
+                    if (str.length < 4 || !str[0].isDigit || !fromString(str[0 .. 4], value.year))
+                        return false;
+                    str = str[4 .. $];
+                }
+
+                value.precision = Precision.year;
+                if (str.length == 0 || str == "T")
+                    return true;
+                
+                static if (ext)
+                {
+                    if (str[0] != '-')
+                        return false;
+                    str = str[1 .. $];
+                }
+
+                // MM
+                if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.month))
+                    return false;
+                str = str[2 .. $];
+                value.precision = Precision.month;
+                if (str.length == 0 || str == "T")
+                    return ext;
+
+                static if (ext)
+                {
+                    if (str[0] != '-')
+                        return false;
+                    str = str[1 .. $];
+                }
+
+                // DD
+                if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.day))
                     return false;
-                str = str[4 .. $];
+                str = str[2 .. $];
+                value.precision = Precision.day;
+                if (str.length == 0)
+                    return true;
             }
 
-            value.precision = Precision.year;
-            if (str.length == 0 || str == "T")
-                return true;
-            
-            static if (ext)
+            // str isn't empty here
+            // T
+            if (str[0] == 'T')
             {
-                if (str[0] != '-')
-                    return false;
                 str = str[1 .. $];
+                // OK, onlyTime requires length >= 3
+                if (str.length == 0)
+                    return true;
             }
-
-            // MM
-            if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.month))
-                return false;
-            str = str[2 .. $];
-            value.precision = Precision.month;
-            if (str.length == 0 || str == "T")
-                return ext;
-
-            static if (ext)
+            else 
             {
-                if (str[0] != '-')
+                if (!(ext && isOnlyTime))
                     return false;
-                str = str[1 .. $];
             }
 
-            // DD
-            if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.day))
-                return false;
-            str = str[2 .. $];
-            value.precision = Precision.day;
-            if (str.length == 0)
-                return true;
-
-            // T
-            if (str[0] != 'T')
-                return false;
-            str = str[1 .. $];
-            if (str.length == 0)
-                return true;
+            value.precision = Precision.minute; // we don't have hour precision
 
             // hh
-            if (str.length < 4 + ext || !str[0].isDigit || !fromString(str[0 .. 2], value.hour))
+            if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.hour))
                 return false;
             str = str[2 .. $];
+            if (str.length == 0)
+                return true;
 
             static if (ext)
             {
@@ -1013,11 +1111,10 @@ struct Timestamp
             // mm
             {
                 uint minute;
-                if (!str[0].isDigit || !fromString(str[0 .. 2], minute))
+                if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], minute))
                     return false;
                 value.minute = cast(ubyte) minute;
                 str = str[2 .. $];
-                value.precision = Precision.minute;
                 if (str.length == 0)
                     return true;
             }

From 01ac21b71dc304797d3b3a9987833438a8537f74 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 9 Jul 2021 23:32:51 +0700
Subject: [PATCH 225/573] update mir.serde for enums

---
 source/mir/serde.d | 95 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 71 insertions(+), 24 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 0171c299..37aea980 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -13,7 +13,7 @@ module mir.serde;
 
 import mir.functional: naryFun;
 import mir.reflection;
-import std.traits: TemplateArgsOf, EnumMembers, hasUDA;
+import std.traits: TemplateArgsOf, EnumMembers, hasUDA, isAggregateType;
 
 version (D_Exceptions)
 {
@@ -139,7 +139,7 @@ private template serdeIsAnnotationMemberIn(T)
 template serdeGetAnnotationMembersIn(T)
 {
     import std.meta: aliasSeqOf, Filter;
-    static if (isSomeStruct!T)
+    static if (isAggregateType!T)
         enum string[] serdeGetAnnotationMembersIn = [Filter!(serdeIsAnnotationMemberIn!T, aliasSeqOf!(DeserializableMembers!T))];
     else
         enum string[] serdeGetAnnotationMembersIn = null;
@@ -180,7 +180,7 @@ private template serdeIsAnnotationMemberOut(T)
 template serdeGetAnnotationMembersOut(T)
 {
     import std.meta: aliasSeqOf, Filter;
-    static if (isSomeStruct!T)
+    static if (isAggregateType!T)
         enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(DeserializableMembers!T))];
     else
         enum string[] serdeGetAnnotationMembersOut = null;
@@ -226,7 +226,7 @@ struct serdeAlgebraicAnnotation
 +/
 template serdeHasAlgebraicAnnotation(T)
 {
-    static if (isSomeStruct!T || is(T == enum))
+    static if (isAggregateType!T || is(T == enum))
     {
         static if (hasUDA!(T, serdeAlgebraicAnnotation))
         {
@@ -986,7 +986,7 @@ template serdeGetFinalProxy(T)
             alias serdeGetFinalProxy = .serdeGetFinalProxy!(serdeGetProxy!T);
         }
         else
-        static if (is(typeof(Timestamp(T.init))))
+        static if (isAggregateType!T && is(typeof(Timestamp(T.init))))
         {
             alias serdeGetFinalProxy = string;
         }
@@ -1032,7 +1032,7 @@ template serdeGetFinalDeepProxy(T)
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(serdeGetProxy!T);
         }
         else
-        static if (is(typeof(Timestamp(T.init))))
+        static if (isAggregateType!T && is(typeof(Timestamp(T.init))))
         {
             alias serdeGetFinalDeepProxy = string;
         }
@@ -1042,11 +1042,11 @@ template serdeGetFinalDeepProxy(T)
             alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(T.serdeKeysProxy);
         }
         else
-        static if (is(T == enum))
-        {
-            alias serdeGetFinalDeepProxy = typeof(null);
-        }
-        else
+        // static if (is(T == enum))
+        // {
+        //     alias serdeGetFinalDeepProxy = typeof(null);
+        // }
+        // else
         {
             alias serdeGetFinalDeepProxy = T;
         }
@@ -1061,7 +1061,7 @@ template serdeGetFinalDeepProxy(T)
             alias serdeGetFinalDeepProxy = T;
     }
     else
-    static if (is(T == V[K], K, V))
+    static if (is(immutable T == immutable V[K], K, V))
     {
         alias E = Unqual!V;
         static if (isAggregateType!E || is(E == enum))
@@ -1101,9 +1101,12 @@ template serdeFinalProxyDeserializableMembers(T)
 {
     import std.meta: Filter, aliasSeqOf;
     alias P = serdeGetFinalProxy!T;
-    static if (isSomeStruct!P)
+    static if (isAggregateType!P || is(P == enum))
         enum string[] serdeFinalProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
     else
+    // static if (is(P == enum))
+    //     enum string[] serdeFinalProxyDeserializableMembers = serdeGetKeysIn!P;
+    // else
         enum string[] serdeFinalProxyDeserializableMembers = null;
 }
 
@@ -1137,9 +1140,12 @@ template serdeFinalDeepProxySerializableMembers(T)
     import std.traits: isAggregateType;
     import std.meta: Filter, aliasSeqOf;
     alias P = serdeGetFinalDeepProxy!T;
-    static if (isSomeStruct!P)
+    static if (isAggregateType!P || is(P == enum))
         enum string[] serdeFinalDeepProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
     else
+    // static if (is(P == enum))
+    //     enum string[] serdeFinalDeepProxySerializableMembers = [serdeGetKeyOut!P];
+    // else
         enum string[] serdeFinalDeepProxySerializableMembers = null;
 }
 
@@ -1172,9 +1178,12 @@ template serdeFinalProxySerializableMembers(T)
 {
     import std.meta: Filter, aliasSeqOf;
     alias P = serdeGetFinalProxy!T;
-    static if (isSomeStruct!P)
+    static if (isAggregateType!P || is(P == enum))
         enum string[] serdeFinalProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))];
     else
+    // static if (is(P == enum))
+    //     enum string[] serdeFinalProxySerializableMembers = [serdeGetKeyOut!P];
+    // else
         enum string[] serdeFinalProxySerializableMembers = null;
 }
 
@@ -1208,16 +1217,18 @@ template serdeFinalDeepProxyDeserializableMembers(T)
     import std.traits: isAggregateType;
     import std.meta: Filter, aliasSeqOf;
     alias P = serdeGetFinalDeepProxy!T;
-    static if (isAggregateType!P)
+    static if (isAggregateType!P || is(P == enum))
         enum string[] serdeFinalDeepProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))];
     else
+    // static if (is(P == enum))
+    //     enum string[] serdeFinalDeepProxyDeserializableMembers = serdeGetKeysIn!P;
+    // else
         enum string[] serdeFinalDeepProxyDeserializableMembers = null;
 }
 
 ///
 version(mir_test) unittest
 {
-
     static struct A
     {
         @serdeIgnore
@@ -1691,6 +1702,11 @@ private template serdeFinalDeepProxyDeserializableMemberKeys(T)
         else
             enum string[] serdeFinalDeepProxyDeserializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeysIn2!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))];
     }
+    else
+    static if (is(T == enum))
+    {
+        enum string[] serdeFinalDeepProxyDeserializableMemberKeys = enumAllKeysIn!T;
+    }
     else
         enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null;
 }
@@ -1714,13 +1730,18 @@ private template serdeFinalDeepProxySerializableMemberKeys(T)
         else
             enum string[] serdeFinalDeepProxySerializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeyOut2!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))];
     }
+    else
+    static if (is(T == enum))
+    {
+        enum string[] serdeFinalDeepProxySerializableMemberKeys = enumAllKeysOut!T;
+    }
     else
         enum string[] serdeFinalDeepProxySerializableMemberKeys = null;
 }
 
 private template serdeGetAlgebraicAnnotations(T)
 {
-    static if (isSomeStruct!T || is(T == enum))
+    static if (isAggregateType!T || is(T == enum))
         static if (hasUDA!(T, serdeAlgebraicAnnotation))
             enum string[] serdeGetAlgebraicAnnotations = [getUDA!(T, serdeAlgebraicAnnotation).annotation];
         else
@@ -1787,22 +1808,26 @@ version(mir_test) unittest
     @serdeProxy!(B[])
     static interface E {  int f() @property; }
 
+    enum N { a, b }
+
     static class D
     {
         C c;
 
         @serdeProxy!(E[])
         int d;
+
+        N e;
     }
 
     @serdeAlgebraicAnnotation("$F")
     @serdeProxy!D
     static struct F { int f; }
 
-    static assert (serdeGetSerializationKeysRecurse!F == ["c", "d", "g"]);
+    static assert (serdeGetSerializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]);
 
     import mir.algebraic;
-    static assert (serdeGetSerializationKeysRecurse!(Nullable!(F, int)) == ["c", "d", "g", "$F"]);
+    static assert (serdeGetSerializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]);
 }
 
 /++
@@ -1829,22 +1854,28 @@ version(mir_test) unittest
     @serdeProxy!(B[])
     static interface E {  int f() @property; }
 
+    enum N { a, b }
+
     static class D
     {
         C c;
 
         @serdeProxy!(E[])
         int d;
+
+        N e;
     }
 
     @serdeAlgebraicAnnotation("$F")
     @serdeProxy!D
     static struct F { int f; }
 
-    static assert (serdeGetDeserializationKeysRecurse!F == ["c", "d", "g"]);
+    static assert (serdeGetDeserializationKeysRecurse!N == ["a", "b"], serdeGetDeserializationKeysRecurse!N);
+
+    static assert (serdeGetDeserializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]);
 
     import mir.algebraic;
-    static assert (serdeGetDeserializationKeysRecurse!(Nullable!(F, int)) == ["c", "d", "g", "$F"]);
+    static assert (serdeGetDeserializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]);
 }
 
 /++
@@ -1883,7 +1914,7 @@ enum serdeAlphabetOut;
 A dummy structure usefull $(LREF serdeOrderedIn) support.
 +/
 struct SerdeOrderedDummy(T, bool __optionalByDefault = false)
-    if (is(serdeGetFinalProxy!T == T) && isSomeStruct!T)
+    if (is(serdeGetFinalProxy!T == T) && isAggregateType!T)
 {
     import std.traits: hasUDA;
 
@@ -1918,7 +1949,7 @@ public:
                 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
             }
             else
-            static if (isSomeStruct!(typeof(__traits(getMember, T, member))))
+            static if (isAggregateType!(typeof(__traits(getMember, T, member))))
             {
                 static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy))
                 {
@@ -2185,3 +2216,19 @@ char[] fastToUpperInPlace()(scope return char[] a)
         e = e.fastToUpper;
     return a;
 }
+
+template enumAllKeysIn(T)
+    if (is(T == enum))
+{
+    import std.traits: EnumMembers;
+    import std.meta: staticMap, aliasSeqOf;
+    enum string[] enumAllKeysIn = [staticMap!(aliasSeqOf, staticMap!(.serdeGetKeysIn, EnumMembers!T))];
+}
+
+template enumAllKeysOut(T)
+    if (is(T == enum))
+{
+    import std.traits: EnumMembers;
+    import std.meta: staticMap, aliasSeqOf;
+    enum string[] enumAllKeysOut = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
+}

From 7b9fdb78afd52eabe218ac3774c308520fc7e3f3 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 23 Jul 2021 14:44:27 +0400
Subject: [PATCH 226/573] add serdeRegisterDeserializer

---
 source/mir/serde.d | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 0171c299..e894c262 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -118,6 +118,13 @@ struct serdeKeyOut
     this(string key) { this.key = key; }
 }
 
+/++
+The attribute should be used as a hind for scripting languages to register type deserializer in the type system.
+
+The attribute should be applied to a type definition.
++/
+enum serdeRegisterDeserializer;
+
 /++
 The attribute should be applied to a string-like member that should be de/serialized as an annotation / attribute.
 

From 6442c20bc5cc337f1c896bc386f7969727be6595 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 27 Jul 2021 16:39:01 +0700
Subject: [PATCH 227/573] rename to serdeRegister

---
 source/mir/serde.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 06db3c83..db4787fc 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -123,7 +123,7 @@ The attribute should be used as a hind for scripting languages to register type
 
 The attribute should be applied to a type definition.
 +/
-enum serdeRegisterDeserializer;
+enum serdeRegister;
 
 /++
 The attribute should be applied to a string-like member that should be de/serialized as an annotation / attribute.

From ee21b95833c4e6707bc1706824636fdff88971d5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 29 Jul 2021 10:48:07 +0700
Subject: [PATCH 228/573] fix string map

---
 source/mir/string_map.d | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index a04ec04b..4363a82b 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -556,8 +556,6 @@ struct StringMap(T, U = uint)
                     return implementation._values[index];
                 }
                 assert (index <= length);
-                index = index < length ? implementation.indices[index] : length;
-                assert (index <= length);
             }
             else
             {
@@ -623,8 +621,6 @@ struct StringMap(T, U = uint)
                     return implementation.values[index];
                 }
                 assert (index <= length);
-                index = index < length ? implementation.indices[index] : length;
-                assert (index <= length);
             }
             else
             {
@@ -942,3 +938,18 @@ private struct StructImpl(T, U = uint)
         return false;
     }
 }
+
+unittest
+{
+    import mir.algebraic_alias.json: JsonAlgebraic;
+    import mir.string_map: StringMap;
+
+    StringMap!JsonAlgebraic token;
+    token[`access_token`] = "secret-data";
+    token[`expires_in`] = 3599;
+    token[`token_type`] = "Bearer";
+
+    assert(token[`access_token`] == "secret-data");
+    assert(token[`expires_in`] == 3599);
+    assert(token[`token_type`] == "Bearer"); // mir/string_map.d(511): No member: token_type
+}

From 6c42e61a3b2a7d3f960186099681b4c6fa44e45a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 29 Jul 2021 10:49:14 +0700
Subject: [PATCH 229/573] fix test version

---
 source/mir/string_map.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 4363a82b..f8e22f35 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -939,6 +939,7 @@ private struct StructImpl(T, U = uint)
     }
 }
 
+version(mir_test)
 unittest
 {
     import mir.algebraic_alias.json: JsonAlgebraic;

From 10a05585eda10eebe8e4e937dfc9678c32948430 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 30 Jul 2021 00:52:56 +0700
Subject: [PATCH 230/573] fix opBinaryRight in StringMap

---
 source/mir/string_map.d | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index f8e22f35..230be810 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -481,6 +481,9 @@ struct StringMap(T, U = uint)
         size_t index;
         if (!implementation.findIndex(key, index))
             return null;
+        assert (index < length);
+        index = implementation.indices[index];
+        assert (index < length);
         return implementation._values + index;
     }
 
@@ -953,4 +956,8 @@ unittest
     assert(token[`access_token`] == "secret-data");
     assert(token[`expires_in`] == 3599);
     assert(token[`token_type`] == "Bearer"); // mir/string_map.d(511): No member: token_type
+
+    const tkType = `token_type` in token;
+
+    assert((*tkType) == "Bearer"); // *tkType contains value 3599
 }

From c0838f2397fb3a0fb0fb955101fde37c2fd9dd4e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 9 Aug 2021 01:37:14 +0700
Subject: [PATCH 231/573] Add SerdeAnnotated

---
 source/mir/serde.d | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index db4787fc..9ff0788a 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -65,6 +65,23 @@ version (D_Exceptions)
     }
 }
 
+/++
+Constructs annotated type.
++/
+template SerdeAnnotated(T, string annotation)
+{
+    ///
+    @serdeAlgebraicAnnotation(annotation)
+    @serdeProxy!T
+    struct SerdeAnnotated
+    {
+        ///
+        T value;
+        ///
+        alias value this;
+    }
+}
+
 /++
 Helper enumeration for for serializer .
 Use negative `int` values for user defined targets.

From 8e227a73d19d059a1ffdd8a26a1d0640fdc5f81d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 11 Aug 2021 14:23:13 +0700
Subject: [PATCH 232/573] fix #353

---
 source/mir/bignum/low_level_view.d | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 14f69108..6e434ab0 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1153,7 +1153,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     bool opEquals(ulong rhs)
         @safe pure nothrow @nogc const scope
     {
-        foreach_reverse(d; lightConst.leastSignificantFirst)
+        foreach (d; lightConst.leastSignificantFirst)
         {
             static if (W.sizeof >= ulong.sizeof)
             {
@@ -1171,6 +1171,20 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return rhs == 0;
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view2 = BigUIntView!(const(ubyte), WordEndian.big)([1, 0]);
+        assert(view2 == 256); // false
+        assert(cast(ulong)view2 == 256); // true
+        auto view = BigUIntView!(const(ubyte), WordEndian.big)([15, 255, 255]);
+        assert(view == 1048575); // false
+        assert(cast(ulong)view == 1048575); // true
+    }
+
     static if (isMutable!W && W.sizeof >= 4)
     /++
     Params:
@@ -1208,7 +1222,6 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return str.length - i;
     }
 
-    ///
     static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
     ///
     version(mir_bignum_test)

From 0f8e5144531d15f0be61d2a75d51d35b1539904a Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Mon, 16 Aug 2021 10:45:09 -0400
Subject: [PATCH 233/573] Add in support for hex literals using underscores
 (#354)

* Add in support for hex literals using underscores

* check for invalid underscores

* add more test cases, unify into one test
---
 source/mir/bignum/fixed.d          |   8 +-
 source/mir/bignum/integer.d        |   8 +-
 source/mir/bignum/low_level_view.d | 183 ++++++++++++++++++++++++++---
 3 files changed, 173 insertions(+), 26 deletions(-)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index ea72fdf1..94d41339 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -197,10 +197,10 @@ struct UInt(size_t size)
     }
 
     ///
-    static UInt!size fromHexString(scope const(char)[] str)
+    static UInt!size fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
     {
         typeof(return) ret;
-        if (ret.fromHexStringImpl(str))
+        if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
             return ret;
         version(D_Exceptions)
         {
@@ -216,11 +216,11 @@ struct UInt(size_t size)
 
     /++
     +/
-    bool fromHexStringImpl(C)(scope const(C)[] str)
+    bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
-        return view.fromHexStringImpl(str);
+        return view.fromHexStringImpl!(C, allowUnderscores)(str);
     }
 
     /++
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index b809434c..bdff3392 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -366,11 +366,11 @@ struct BigInt(size_t maxSize64)
 
     /++
     +/
-    static BigInt fromHexString()(scope const(char)[] str)
+    static BigInt fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
         @trusted pure
     {
         BigInt ret;
-        if (ret.fromHexStringImpl(str))
+        if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
             return ret;
         version(D_Exceptions)
             throw hexStringException;
@@ -380,12 +380,12 @@ struct BigInt(size_t maxSize64)
 
     /++
     +/
-    bool fromHexStringImpl(C)(scope const(C)[] str)
+    bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
         auto work = BigIntView!size_t(data);
-        auto ret = work.fromHexStringImpl(str);
+        auto ret = work.fromHexStringImpl!(C, allowUnderscores)(str);
         if (ret)
         {
             length = cast(uint)work.unsigned.coefficients.length;
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 6e434ab0..c0f524af 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -565,14 +565,15 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    static BigUIntView fromHexString(C)(scope const(C)[] str)
+    static BigUIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str)
         @trusted pure
         if (isSomeChar!C)
     {
         auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
         auto data = new Unqual!W[length];
-        if (BigUIntView!(Unqual!W, endian)(data).fromHexStringImpl(str))
-            return BigUIntView(cast(W[])data);
+        auto view = BigUIntView!(Unqual!W, endian)(data);
+        if (view.fromHexStringImpl!(C, allowUnderscores)(str))
+            return BigUIntView(cast(W[])view.coefficients);
         version(D_Exceptions)
             throw hexStringException;
         else
@@ -582,18 +583,26 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     static if (isMutable!W)
     /++
     +/
-    bool fromHexStringImpl(C)(scope const(C)[] str)
+    bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
         @safe pure @nogc nothrow scope
         if (isSomeChar!C)
     {
         pragma(inline, false);
         import mir.utility: _expect;
-        if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false))
-            return false;
-        coefficients = coefficients[0 .. str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0)];
-        auto rdata = leastSignificantFirst;
+        static if (allowUnderscores) {
+            if (_expect(str.length == 0, false)) // can't tell how big the coeff array needs to be, rely on a runtime check
+                return false;
+        } else {
+            if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 2, false))
+                return false;
+        }
+
+        leastSignificant = 0;
+        auto work = topLeastSignificantPart(1);
         W current;
-        size_t i;
+        size_t i, j;
+        static if (allowUnderscores) bool recentUnderscore;
+
         do
         {
             ubyte c;
@@ -621,28 +630,98 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                 case 'e': c = 0xE; break;
                 case 'F':
                 case 'f': c = 0xF; break;
+                static if (allowUnderscores) 
+                {
+                    case '_': 
+                        if (recentUnderscore) return false;
+                        recentUnderscore = true;
+                        continue;
+                }
                 default: return false;
             }
+            ++j;
+            static if (allowUnderscores) recentUnderscore = false;
+            // how far do we need to shift to get to the top 4 bits
             enum s = W.sizeof * 8 - 4;
+            // shift number to the top most 4 bits
             W cc = cast(W)(W(c) << s);
+            // shift unsigned right 4 bits
             current >>>= 4;
+            // add number to top most 4 bits of current var
             current |= cc;
-            if (i % (W.sizeof * 2) == 0)
+            if (j % (W.sizeof * 2) == 0) // is this packed var full? 
             {
-                rdata.front = current;
-                rdata.popFront;
+                work.mostSignificant = current;
                 current = 0;
+                if (_expect(work.coefficients.length < coefficients.length, true))
+                {
+                    work = topLeastSignificantPart(work.coefficients.length + 1);
+                }
+                else if (i < str.length) // if we've run out of coefficients before reaching the end of the string, error
+                {
+                    return false;
+                }
             }
         }
         while(i < str.length);
+
+        static if (allowUnderscores) 
+        {
+            // check for a underscore at the beginning or the end
+            if (recentUnderscore || str[$ - 1] == '_') return false;
+        }
+
         if (current)
         {
-            current >>>= 4 * (W.sizeof * 2 - i % (W.sizeof * 2));
-            rdata.front = current;
+            current >>>= 4 * (W.sizeof * 2 - j % (W.sizeof * 2));
+            work.mostSignificant = current;
         }
+
+        coefficients = coefficients[0 .. (j / (W.sizeof * 2) + (j % (W.sizeof * 2) != 0))];
+
         return true;
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigUIntView!size_t.fromHexString!(char, true)("abcd_efab_cdef");
+        assert(cast(ulong)view == 0xabcd_efab_cdef);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        // Check that invalid underscores in hex literals throw an error.
+        void expectThrow(const(char)[] input) {
+            bool caught = false;
+            try { 
+                auto view = BigUIntView!size_t.fromHexString!(char, true)(input);
+            } catch (Exception e) {
+                caught = true;
+            }
+
+            assert(caught);
+        }
+
+        expectThrow("abcd_efab_cef_");
+        expectThrow("abcd__efab__cef");
+        expectThrow("_abcd_efab_cdef");
+        expectThrow("_abcd_efab_cdef_");
+        expectThrow("_abcd_efab_cdef__");
+        expectThrow("__abcd_efab_cdef");
+        expectThrow("__abcd_efab_cdef_");
+        expectThrow("__abcd_efab_cdef__");
+        expectThrow("__abcd__efab_cdef__");
+        expectThrow("__abcd__efab__cdef__");
+    }
+
     static if (isMutable!W && W.sizeof >= 4)
     /++
     Returns: false in case of overflow or incorrect string.
@@ -1378,13 +1457,13 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    static BigIntView fromHexString(C)(scope const(C)[] str)
+    static BigIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str)
         @trusted pure
         if (isSomeChar!C)
     {
         auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
         auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]);
-        if (ret.fromHexStringImpl(str))
+        if (ret.fromHexStringImpl!(C, allowUnderscores)(str))
             return  cast(BigIntView) ret;
         version(D_Exceptions)
             throw hexStringException;
@@ -1395,7 +1474,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     static if (isMutable!W)
     /++
     +/
-    bool fromHexStringImpl(C)(scope const(C)[] str)
+    bool fromHexStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
         @safe pure @nogc nothrow
         if (isSomeChar!C)
     {
@@ -1420,7 +1499,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
             str = str[1 .. $];
         }
 
-        return unsigned.fromHexStringImpl(str);
+        return unsigned.fromHexStringImpl!(C, allowUnderscores)(str);
     }
 
     ///
@@ -1443,6 +1522,15 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto a = cast(double) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
+        assert(a == -0xa.fbbfae3cd0bp+124);
+    }
 
     ///
     T opCast(T, bool nonZero = false)() scope const
@@ -1465,6 +1553,17 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
     static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
     version(mir_bignum_test)
     @safe pure
@@ -1474,6 +1573,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(long) view == -0x14a1de7022b0021d);
         assert(cast(int) view == -0x22b0021d);
     }
+    
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ushort.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
 
     static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
     version(mir_bignum_test)
@@ -1485,6 +1594,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ubyte.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_021d");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
     /++
     +/
     T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const
@@ -1510,6 +1629,20 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        import mir.bignum.fixed: UInt;
+        import mir.bignum.fp: Fp;
+
+        auto fp = cast(Fp!128) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
+        assert(fp.sign);
+        assert(fp.exponent == 0);
+        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+    }
+
     static if (endian == TargetEndian)
     ///
     BigIntView!V opCast(T : BigIntView!V, V)() scope return
@@ -1821,6 +1954,20 @@ unittest
 
 }
 
+///
+version(mir_bignum_test)
+unittest
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.low_level_view: BigUIntView;
+    auto bigView2 = BigUIntView!size_t.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
+    auto bigView = BigUIntView!size_t.fromHexString!(char, true)("55a3_25ad_18b2_a771_20d8_70d9_87d5_2374_7379_0532_acab_45da_44bc_07c9_2c92_babf_0b5e_2e2c_7771_cd47_2ae5_d7ac_db15_9a56_fbf7_4f85_1a05_8ae3_41f6_9d1e_b750_d7e3");
+    auto fixed = UInt!256.fromHexString!(true)("55e5_6695_76d3_1726_f4a9_b58a_9015_9de5_923a_dc6c_762e_bd3c_4ba5_18d4_9522_9072");
+    auto overflow = bigView *= fixed;
+    assert(overflow == UInt!256.fromHexString("1cbbe8c42dc21f936e4ce5b2f52ac404439857f174084012fcd1b71fdec2a398"));
+    assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
+}
+
 /++
 An utility type to wrap a local buffer to accumulate unsigned numbers.
 +/

From 9b3e5f078f0e06f0f569ec3ffa00722ee0463b43 Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Fri, 20 Aug 2021 02:24:48 -0400
Subject: [PATCH 234/573] Add GitHub Actions to replace Travis-CI (#356)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add GitHub Actions flow

* Pull out GDC config as it’s not supported
---
 .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++
 test_travis.sh           |  6 ++++-
 2 files changed, 53 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/ci.yml

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..0f436b96
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,48 @@
+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, 'master') || contains(matrix.dc, 'beta') }}
+    env:
+      ARCH: ${{ matrix.arch }}
+    strategy:
+      fail-fast: false
+      matrix:
+        dc: [ldc-latest, ldc-beta, dmd-latest, dmd-master, dmd-beta]
+        os: [ubuntu-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@v2
+      - uses: dlang-community/setup-dlang@v1.1.0
+        with:
+          compiler: ${{ matrix.dc }}
+      - name: Install multi-lib for 32-bit systems
+        if: matrix.arch == 'x86'
+        run: sudo apt-get install gcc-multilib
+      - id: build
+        name: Test building
+        run: bash -e test_travis.sh
+      - id: coverage
+        name: Upload coverage data
+        run: bash <(curl -s https://codecov.io/bash)
+    
+        
diff --git a/test_travis.sh b/test_travis.sh
index 5ec323ae..6279e27e 100644
--- a/test_travis.sh
+++ b/test_travis.sh
@@ -4,8 +4,12 @@ then
   docker run --rm --privileged multiarch/qemu-user-static:register --reset
   docker build -t ion-arm64 . -f Dockerfile.aarch64
 else
+  echo $DC
   echo $ARCH
-  dub test --arch=$ARCH --build=unittest-dip1000
+  if [ "$DC" != "gdc" ] 
+  then
+    dub test --arch=$ARCH --build=unittest-dip1000
+  fi
   dub test --arch=$ARCH --build=unittest-cov
   # if [ \( "$DC" = "ldc2" \) -o \( "$DC" = "ldmd2" \) ]
   # then

From 89241f005074e1a4eb4e14b83f2adc0daf061ab5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 20 Aug 2021 22:31:29 +0700
Subject: [PATCH 235/573] fixup for complex rcarrays

---
 source/mir/rc/array.d | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 88518853..9d582774 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -24,6 +24,7 @@ The implementation never adds roots into the GC.
 +/
 struct mir_rcarray(T)
 {
+    import mir.internal.utility: isComplex, realType;
     ///
     package T* _payload;
     package ref mir_rc_context context() inout scope return pure nothrow @nogc @trusted @property
@@ -65,19 +66,28 @@ struct mir_rcarray(T)
     /// ditto
     bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
     {
-        return opIndex() == rhs.opIndex();
+        static if (isComplex!T)
+            return cast(const realType!T[]) opIndex() == cast(const realType!Y[]) rhs.opIndex();
+        else
+            return opIndex() == rhs.opIndex();
     }
 
     ///
     int opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
     {
-        return __cmp(opIndex(), rhs.opIndex());
+        static if (isComplex!T)
+            return __cmp(cast(const realType!T[])opIndex(), cast(const realType!Y[])rhs.opIndex());
+        else
+            return __cmp(opIndex(), rhs.opIndex());
     }
 
     ///
     size_t toHash() @trusted scope const pure nothrow @nogc
     {
-        return hashOf(opIndex());
+        static if (isComplex!T)
+            return hashOf(cast(const realType!T[])opIndex());
+        else
+            return hashOf(opIndex());
     }
 
     ///
@@ -295,6 +305,14 @@ unittest
     static assert(is(typeof(fs) == Slice!(double*)));
 }
 
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.complex;
+    auto a = rcarray(complex(2.0, 3), complex(4.9, 2));
+}
+
 package template LikeArray(Range)
 {
     static if (__traits(identifier, Range) == "mir_slice")

From 9af158d339a1bd966a6abfea700667a574e54010 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 Aug 2021 14:48:01 +0700
Subject: [PATCH 236/573] fix HeapOps

---
 source/mir/container/binaryheap.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/container/binaryheap.d b/source/mir/container/binaryheap.d
index 7b42d2a6..cef2fd07 100644
--- a/source/mir/container/binaryheap.d
+++ b/source/mir/container/binaryheap.d
@@ -424,6 +424,7 @@ BinaryHeap!(less, Store) heapify(alias less = "a < b", Store)(Store s,
 /// Heap operations for random-access ranges
 template HeapOps(alias less, Range)
 {
+    import std.range.primitives : hasSwappableElements, hasAssignableElements;
     import mir.functional;
     import std.algorithm.mutation : swapAt;
 

From 3c9fe9059bcde35ec070462bb10a625fef2f8aaf Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Sep 2021 13:02:24 +0700
Subject: [PATCH 237/573] improve scopedBuffer

---
 source/mir/algorithm/iteration.d | 50 ++++++++++++----------------
 source/mir/appender.d            | 22 ++++++++-----
 source/mir/array/allocation.d    | 54 ++++++++++--------------------
 source/mir/format.d              | 56 ++++++++++++--------------------
 source/mir/math/sum.d            |  2 +-
 source/mir/ndslice/topology.d    | 18 +++-------
 source/mir/rc/array.d            | 43 ++++++++----------------
 7 files changed, 91 insertions(+), 154 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index b4578fa9..d8159ddf 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -4119,37 +4119,29 @@ template rcfilter(alias pred = "a", alias map = "a")
         auto rcfilter(Range)(Range r)
             if (isIterable!Range && (!isSlice!Range || DimensionCount!Range == 1))
         {
-                import core.lifetime: forward;
-                import mir.primitives: isInputRange;
-                import mir.rc.array: RCArray;
-
-                if (false)
+            import core.lifetime: forward;
+            import mir.appender: scopedBuffer;
+            import mir.primitives: isInputRange;
+            import mir.rc.array: RCArray;
+
+            alias T = typeof(map(r.front));
+            auto buffer = scopedBuffer!T;
+            foreach (ref e; r)
+            {
+                if (pred(e))
                 {
-                    auto p = pred(r.front);
-                    auto e = map(r.front);
-                    r.popFront;
-                    auto d = r.empty;
+                    static if (__traits(isSame, naryFun!"a", map))
+                        buffer.put(forward!e);
+                    else
+                        buffer.put(map(forward!e));
                 }
-                return () @trusted
-                {
-                    import mir.appender: ScopedBuffer;
-                    alias T = typeof(map(r.front));
-                    ScopedBuffer!T buffer = void;
-                    buffer.initialize;
-                    foreach (ref e; r)
-                    {
-                        if (pred(e))
-                        {
-                            static if (__traits(isSame, naryFun!"a", map))
-                                buffer.put(forward!e);
-                            else
-                                buffer.put(map(forward!e));
-                        }
-                    }
-                    auto ret = RCArray!T(buffer.length);
-                    buffer.moveDataAndEmplaceTo(ret[]);
-                    return ret;
-                } ();
+            }
+            return () @trusted
+            {
+                auto ret = RCArray!T(buffer.length);
+                buffer.moveDataAndEmplaceTo(ret[]);
+                return ret;
+            } ();
         }
 
         /// ditto
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 16d1ebb5..3c524357 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -14,7 +14,7 @@ private extern(C) @system nothrow @nogc pure void* memcpy(scope void* s1, scope
 
 ///
 struct ScopedBuffer(T, size_t bytes = 4096)
-    if (bytes)
+    if (bytes && T.sizeof <= bytes)
 {
     import std.traits: Unqual, isMutable, isStaticArray, isIterable, hasElaborateAssign, isAssignable, isArray;
     import mir.primitives: hasLength;
@@ -229,12 +229,19 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     }
 }
 
+/// ditto
+auto scopedBuffer(T, size_t bytes = 4096)() @trusted
+{
+    ScopedBuffer!(T, bytes) buffer = void;
+    buffer.initialize;
+    return buffer;
+}
+
 ///
-@trusted pure nothrow @nogc
+@safe pure nothrow @nogc
 version (mir_test) unittest
 {
-    ScopedBuffer!char buf = void;
-    buf.initialize;
+    auto buf = scopedBuffer!char;
     buf.put('c');
     buf.put("str");
     assert(buf.data == "cstr");
@@ -244,11 +251,10 @@ version (mir_test) unittest
 }
 
 /// immutable
-@trusted pure nothrow @nogc
+@safe pure nothrow @nogc
 version (mir_test) unittest
 {
-    ScopedBuffer!(immutable char) buf = void;
-    buf.initialize;
+    auto buf = scopedBuffer!(immutable char);
     buf.put('c');
     buf.put("str");
     assert(buf.data == "cstr");
@@ -260,7 +266,7 @@ version (mir_test) unittest
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
-    ScopedBuffer!(char, 3) buf;
+    auto buf = scopedBuffer!(char, 3);
     buf.put('c');
     buf.put("str");
     assert(buf.data == "cstr");
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index f40235da..a2488f8e 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -112,47 +112,27 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticAr
     }
     else
     {
-        import mir.appender: ScopedBuffer;
+        import mir.appender: scopedBuffer;
+        import std.array: uninitializedArray;
+
+        auto a = scopedBuffer!(Unqual!E);
 
-        if (false)
+        static if (isInputRange!Range)
+            for (; !r.empty; r.popFront)
+                a.put(r.front);
+        else
+        static if (isPointer!Range)
         {
-            ScopedBuffer!(Unqual!E) a;
-            static if (isInputRange!Range)
-                for (; !r.empty; r.popFront)
-                    a.put(r.front);
-            else
-            static if (isPointer!Range)
-            {
-                foreach (e; *r)
-                    a.put(forward!e);
-            }
-            else
-            {
-                foreach (e; r)
-                    a.put(forward!e);
-            }
+            foreach (e; *r)
+                a.put(forward!e);
+        }
+        else
+        {
+            foreach (e; r)
+                a.put(forward!e);
         }
 
-        return () @trusted {
-            ScopedBuffer!(Unqual!E) a = void;
-            a.initialize;
-
-            static if (isInputRange!Range)
-                for (; !r.empty; r.popFront)
-                    a.put(r.front);
-            else
-            static if (isPointer!Range)
-            {
-                foreach (e; *r)
-                    a.put(forward!e);
-            }
-            else
-            {
-                foreach (e; r)
-                    a.put(forward!e);
-            }
-
-            import std.array: uninitializedArray;
+        return () @trusted { 
             auto ret = uninitializedArray!(Unqual!E[])(a.length);
             a.moveDataAndEmplaceTo(ret);
             return ret;
diff --git a/source/mir/format.d b/source/mir/format.d
index 56081cef..ef27bdd6 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -70,33 +70,17 @@ string text(string separator = "", A...)(auto ref A args)
     }
     else
     {
-        import mir.appender: ScopedBuffer;
-        if (false)
+        import mir.appender: scopedBuffer;
+        auto buffer = scopedBuffer!char;
+        foreach (i, ref arg; args)
         {
-            ScopedBuffer!char buffer;
-            foreach (i, ref arg; args)
+            buffer.print(arg);
+            static if (separator.length && i + 1 < args.length)
             {
-                buffer.print(arg);
-                static if (separator.length && i + 1 < args.length)
-                {
-                    buffer.printStaticString!char(separator);
-                }
+                buffer.printStaticString!char(separator);
             }
-            return buffer.data.idup;
         }
-        return () @trusted  {
-            ScopedBuffer!char buffer = void;
-            buffer.initialize;
-            foreach (i, ref arg; args)
-            {
-                buffer.print(arg);
-                static if (separator.length && i + 1 < args.length)
-                {
-                    buffer.printStaticString!char(separator);
-                }
-            }
-            return buffer.data.idup;
-        } ();
+        return buffer.data.idup;
     }
 }
 
@@ -546,8 +530,8 @@ version (mir_test) unittest
         yes,
     }
 
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
     w.print(Flag.yes);
     assert(w.data == "yes", w.data);
 }
@@ -573,8 +557,8 @@ ref W print(C = char, W)(scope return ref W w, bool c)
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
     assert(w.print(true).data == `true`, w.data);
     w.reset;
     assert(w.print(false).data == `false`, w.data);
@@ -608,8 +592,8 @@ ref W print(C = char, W, V, K)(scope return ref W w, scope const V[K] c)
 @safe pure
 version (mir_test) unittest
 {
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
     w.print(["a": 1, "b": 2]);
     assert(w.data == `["a": 1, "b": 2]` || w.data == `["b": 2, "a": 1]`, w.data);
 }
@@ -639,8 +623,8 @@ ref W print(C = char, W, T)(scope return ref W w, scope const(T)[] c)
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
     string[2] array = ["a\na", "b"];
     assert(w.print(array[]).data == `["a\na", "b"]`, w.data);
 }
@@ -691,8 +675,8 @@ ref W print(C = char, W)(scope return ref W w, char c)
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
     assert(w
         .print('\n')
         .print('\'')
@@ -902,8 +886,8 @@ version (mir_test) unittest
     static struct F { scope const(char)[] toString()() const return { return "f"; } }
     static struct G { const(char)[] s = "g"; alias s this; }
 
-    import mir.appender: ScopedBuffer;
-    ScopedBuffer!char w;
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
     assert(stringBuf() << A() << S() << D() << F() << G() << getData == "asdfg");
 }
 
@@ -1061,7 +1045,7 @@ ref W printZeroPad(C = char, W, I)(scope return ref W w, const I c, size_t minim
 version (mir_test) unittest
 {
     import mir.appender;
-    ScopedBuffer!char w;
+    auto w = scopedBuffer!char;
 
     w.printZeroPad(-123, 5);
     w.put(' ');
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 24469f1e..c6faf41e 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -514,7 +514,7 @@ struct Summator(T, Summation summation)
         import mir.math.ieee: signbit;
     private:
         enum F M = (cast(F)(2)) ^^ (T.max_exp - 1);
-        ScopedBuffer!(F, 8) partials;
+        auto partials = scopedBuffer!(F, 8 * T.sizeof);
         //sum for NaN and infinity.
         F s = summationInitValue!F;
         //Overflow Degree. Count of 2^^F.max_exp minus count of -(2^^F.max_exp)
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index c70d535f..704542c4 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -3383,26 +3383,18 @@ template rcmap(fun...)
                 if (!hasAsSlice!Range && !isSlice!Range && !is(Range : T[], T))
             {
                 import core.lifetime: forward;
+                import mir.appender: scopedBuffer;
                 import mir.primitives: isInputRange;
                 import mir.rc.array: RCArray;
-
-                if (false)
+                alias T = typeof(f(r.front));
+                auto buffer = scopedBuffer!T;
+                while (!r.empty)
                 {
-                    auto e = f(r.front);
+                    buffer.put(f(r.front));
                     r.popFront;
-                    auto d = r.empty;
                 }
                 return () @trusted
                 {
-                    import mir.appender: ScopedBuffer;
-                    alias T = typeof(f(r.front));
-                    ScopedBuffer!T buffer = void;
-                    buffer.initialize;
-                    while (!r.empty)
-                    {
-                        buffer.put(f(r.front));
-                        r.popFront;
-                    }
                     auto ret = RCArray!T(buffer.length, false);
                     buffer.moveDataAndEmplaceTo(ret[]);
                     return ret;
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 9d582774..56ea4e8a 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -415,38 +415,21 @@ template rcarray(T)
         }
         else
         {
-            import mir.appender: ScopedBuffer;
+            import mir.appender: scopedBuffer;
             import mir.conv: emplaceRef;
-            if (false)
-            {
-                ScopedBuffer!T a;
-                static if (isInputRange!Range)
-                    for (; !range.empty; range.popFront)
-                        a.put(range.front);
-                else
-                static if (isPointer!Range)
-                    foreach (e; *range)
-                        a.put(e);
-                else
-                    foreach (e; range)
-                        a.put(e);
-                scope values = a.data;
-                auto ret = RCArray!T(values.length, false);
-            }
+            auto a = scopedBuffer!T;
+            static if (isInputRange!Range)
+                for (; !range.empty; range.popFront)
+                    a.put(range.front);
+            else
+            static if (isPointer!Range)
+                foreach (e; *range)
+                    a.put(e);
+            else
+                foreach (e; range)
+                    a.put(e);
+            scope values = a.data;
             return ()@trusted {
-                ScopedBuffer!T a = void;
-                a.initialize;
-                static if (isInputRange!Range)
-                    for (; !range.empty; range.popFront)
-                        a.put(range.front);
-                else
-                static if (isPointer!Range)
-                    foreach (e; *range)
-                        a.put(e);
-                else
-                    foreach (e; range)
-                        a.put(e);
-                scope values = a.data;
                 auto ret = RCArray!T(values.length, false);
                 a.moveDataAndEmplaceTo(ret[]);
                 return ret;

From f96a97f0fcdae45fb6df464f7e9ac43997c3af79 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 9 Sep 2021 18:07:27 +0700
Subject: [PATCH 238/573] fixup C++ headers

---
 include/mir/interpolate.h  | 1 +
 include/mir/ndslice.h      | 9 ++++-----
 include/mir/small_string.h | 1 +
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/include/mir/interpolate.h b/include/mir/interpolate.h
index 8406c2e1..0c6753e7 100644
--- a/include/mir/interpolate.h
+++ b/include/mir/interpolate.h
@@ -1,3 +1,4 @@
+#pragma once
 
 namespace mir {
     namespace interpolate
diff --git a/include/mir/ndslice.h b/include/mir/ndslice.h
index f071c781..1e2a5d24 100644
--- a/include/mir/ndslice.h
+++ b/include/mir/ndslice.h
@@ -1,3 +1,7 @@
+#ifndef MIR_NDSLICE
+
+#define MIR_NDSLICE
+
 /**
  ************ Mir-Algorithm ************
 
@@ -8,11 +12,6 @@ License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
 Authors: Ilya Yaroshenko
 */
-
-#ifndef MIR_NDSLICE
-
-#define MIR_NDSLICE
-
 #include <cstddef>
 #include <cstdint>
 #include <stdexcept>
diff --git a/include/mir/small_string.h b/include/mir/small_string.h
index 63e3493b..73d677bf 100644
--- a/include/mir/small_string.h
+++ b/include/mir/small_string.h
@@ -1,3 +1,4 @@
+#pragma once
 // Self contained generic small string implementaton.
 #include <cstring>
 #include <cstddef>

From ce2e679a9f001209f61640c47facbf9af0f1f238 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 14 Sep 2021 15:36:57 +0700
Subject: [PATCH 239/573] add derivativeOrder to Lagrange

---
 source/mir/interpolate/polynomial.d | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index a0b31cfb..d05fe13d 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -101,6 +101,9 @@ struct Lagrange(T, uint maxAdditionalFunctions = 0, X = T)
 
 @optmath @safe pure @nogc extern(D):
 
+    ///
+    enum uint derivativeOrder = maxAdditionalFunctions;
+
     /++
     Complexity: `O(N)`
     +/

From cdfe705847d4df4b87f0a115503ca67b5b7a5cb6 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 15 Sep 2021 22:48:08 +0700
Subject: [PATCH 240/573] fix stringmap

---
 source/mir/string_map.d | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 230be810..d47ae13f 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -40,9 +40,15 @@ struct StringMap(T, U = uint)
     private Impl* implementation;
 
     ///
-    bool opEquals()(const StringMap rhs) const
+    bool opEquals(const StringMap rhs) const
     {
-        return keys == rhs.keys && values == rhs.values;
+        if (keys != rhs.keys)
+            return false;
+        if (implementation)
+            foreach (const i; 0 .. implementation._length)
+                if (implementation._values[i] != rhs.implementation._values[i])
+                    return false;
+        return true;
     }
 
     // // linking bug
@@ -118,7 +124,7 @@ struct StringMap(T, U = uint)
     }
 
     ///
-    string toString() const
+    string toString()() const
     {
         import mir.format: stringBuf;
         stringBuf buffer;

From 1b3022e27aecea3ffa8b213805006a3e7d74d7f5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 16 Sep 2021 14:16:34 +0700
Subject: [PATCH 241/573] add comment

---
 source/mir/string_map.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index d47ae13f..b7dbc44b 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -40,6 +40,7 @@ struct StringMap(T, U = uint)
     private Impl* implementation;
 
     ///
+    // current implementation is workaround for linking bugs when used in self referencing algebraic types
     bool opEquals(const StringMap rhs) const
     {
         if (keys != rhs.keys)

From 22918a519767acbe7c9f6f389b57a200cc5f71ea Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 16 Sep 2021 20:31:26 +0700
Subject: [PATCH 242/573] fix fromStringImpl

---
 source/mir/bignum/decimal.d        | 2 +-
 source/mir/bignum/fixed.d          | 2 +-
 source/mir/bignum/integer.d        | 2 +-
 source/mir/bignum/low_level_view.d | 6 +++---
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 2e7f4416..7a897188 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -228,7 +228,7 @@ struct Decimal(size_t maxSize64)
         bool checkEmpty = true,
         )
         (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
-        @safe pure @nogc nothrow
+        scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
         enum optimize = size_t.sizeof == 8 && maxSize64 == 1;
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 94d41339..b016eebb 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -123,7 +123,7 @@ struct UInt(size_t size)
     Precondition: non-empty coefficients.
     +/
     bool fromStringImpl(C)(scope const(C)[] str)
-        @safe pure @nogc nothrow
+        scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
         import mir.bignum.low_level_view: BigUIntView;
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index bdff3392..06b62f13 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -136,7 +136,7 @@ struct BigInt(size_t maxSize64)
     Precondition: non-empty coefficients.
     +/
     bool fromStringImpl(C)(scope const(C)[] str)
-        @safe pure @nogc nothrow
+        scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
         auto work = BigIntView!size_t(data[]); 
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index c0f524af..514c25c8 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -729,7 +729,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Note: doesn't support signs.
     +/
     bool fromStringImpl(C)(scope const(C)[] str)
-        @safe pure @nogc nothrow
+        scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
         import mir.utility: _expect;
@@ -1433,7 +1433,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     Precondition: non-empty coefficients.
     +/
     bool fromStringImpl(C)(scope const(C)[] str)
-        @safe pure @nogc nothrow
+        scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
         import mir.utility: _expect;
@@ -2219,7 +2219,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         bool checkEmpty = true,
         )
         (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
-        @safe pure @nogc nothrow
+        scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
         import mir.utility: _expect;

From 075aea664869c0400e24ae49816a6dba7b73a9d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Fri, 17 Sep 2021 03:41:51 +0200
Subject: [PATCH 243/573] Use __traits(isStaticArray) instead of std.traits
 wrapper (#358)

---
 source/mir/appender.d         | 4 ++--
 source/mir/array/allocation.d | 4 ++--
 source/mir/ndslice/slice.d    | 8 ++++----
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index 3c524357..7e6c0b72 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -16,7 +16,7 @@ private extern(C) @system nothrow @nogc pure void* memcpy(scope void* s1, scope
 struct ScopedBuffer(T, size_t bytes = 4096)
     if (bytes && T.sizeof <= bytes)
 {
-    import std.traits: Unqual, isMutable, isStaticArray, isIterable, hasElaborateAssign, isAssignable, isArray;
+    import std.traits: Unqual, isMutable, isIterable, hasElaborateAssign, isAssignable, isArray;
     import mir.primitives: hasLength;
     import mir.conv: emplaceRef;
 
@@ -177,7 +177,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
 
     ///
     void put(Iterable)(Iterable range) scope
-        if (isIterable!Iterable && !isStaticArray!Iterable && (!isArray!Iterable || hasElaborateAssign!T))
+        if (isIterable!Iterable && !__traits(isStaticArray, Iterable) && (!isArray!Iterable || hasElaborateAssign!T))
     {
         static if (hasLength!Iterable)
         {
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index a2488f8e..40263aed 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -38,7 +38,7 @@ import std.traits;
  *      allocated and initialized array
  */
 auto array(Range)(Range r)
-if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticArray!Range || isPointer!Range && (isInputRange!(PointerTarget!Range) || isIterable!(PointerTarget!Range)))
+if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !__traits(isStaticArray, Range) || isPointer!Range && (isInputRange!(PointerTarget!Range) || isIterable!(PointerTarget!Range)))
 {
     static if (isIterable!Range)
         alias E = ForeachType!Range;
@@ -132,7 +132,7 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isStaticAr
                 a.put(forward!e);
         }
 
-        return () @trusted { 
+        return () @trusted {
             auto ret = uninitializedArray!(Unqual!E[])(a.length);
             a.moveDataAndEmplaceTo(ret);
             return ret;
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 6f769562..f9fb7d03 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -239,7 +239,7 @@ Returns:
     n-dimensional slice
 +/
 auto sliced(size_t N, Iterator)(Iterator iterator, size_t[N] lengths...)
-    if (!isStaticArray!Iterator && N
+    if (!__traits(isStaticArray, Iterator) && N
         && !is(Iterator : Slice!(_Iterator, _N, kind), _Iterator, size_t _N, SliceKind kind))
 {
     alias C = ImplicitlyUnqual!(typeof(iterator));
@@ -2591,11 +2591,11 @@ public:
         assert(imm == x);
         assert(mut == x);
     }
-    
+
     /++
     Provides the index location of a slice, taking into account
     `Slice._strides`. Similar to `Slice.indexStride`, except the slice is
-    indexed by a value. Called by `Slice.accessFlat`. 
+    indexed by a value. Called by `Slice.accessFlat`.
 
     Params:
         n = location in slice
@@ -2632,7 +2632,7 @@ public:
     }
 
     /++
-    Provides access to a slice as if it were `flattened`. 
+    Provides access to a slice as if it were `flattened`.
 
     Params:
         index = location in slice

From bb0942e2fd85de0cbc4ae0f6e94ba18bcdc46b43 Mon Sep 17 00:00:00 2001
From: John Kilpatrick <john.kilpatrick@outlook.com>
Date: Fri, 17 Sep 2021 06:30:57 +0100
Subject: [PATCH 244/573] Update index.d (#352)

Fix typo
---
 index.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.d b/index.d
index 8e2f63f4..53e6ad5c 100644
--- a/index.d
+++ b/index.d
@@ -28,7 +28,7 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,algorithm,iteration)) $(TD Mir & BetterC rework of Phobos.))
     $(TR $(TDNW $(MREF mir,algorithm,setops)) $(TD Mir & BetterC rework of Phobos.))
     $(TR $(TDNW $(MREF mir,ndslice)★) $(TD Package for ndarrays and iteration algorithms.))
-    $(TR $(TDNW $(MREF mir,range)) $(TD Additoinal primitive ranges. See also $(MREF mir,ndslice), which contains a lot of range constructos.))
+    $(TR $(TDNW $(MREF mir,range)) $(TD Additional primitive ranges. See also $(MREF mir,ndslice), which contains a lot of range constructos.))
     $(LEADINGROW Math)
     $(TR $(TDNW $(MREF mir,interpolate)★) $(TD Interpolation algorithms))
     $(TR $(TDNW $(MREF mir,math,numeric)) $(TD Simple numeric algorithms))

From 5b901508276f3a0a82ac664f5046068c7dff72f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Mon, 20 Sep 2021 06:05:08 +0200
Subject: [PATCH 245/573] Rename text() template parameter from A to Args to
 comply with print() (#362)

---
 source/mir/format.d | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index ef27bdd6..bbb613f1 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -59,10 +59,10 @@ unittest
 }
 
 /// Concatenated string results
-string text(string separator = "", A...)(auto ref A args)
-        if (A.length > 0)
+string text(string separator = "", Args...)(auto ref Args args)
+    if (Args.length > 0)
 {
-    static if (A.length == 1)
+    static if (Args.length == 1)
     {
         import mir.functional: forward;
         import mir.conv: to;
@@ -406,7 +406,7 @@ ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
- 
+
     import mir.format: stringBuf;
     stringBuf w;
     assert(w.printEscaped("Hi \a\v\0\f\t\b \\\r\n" ~ `"@nogc"`).data == `Hi \a\v\0\f\t\b \\\r\n\"@nogc\"`);

From 9a6abda64b9c16f920958a1f8c0e6cb7b58b2117 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Mon, 20 Sep 2021 06:12:00 +0200
Subject: [PATCH 246/573] Use const parameters in mir.format to reduce template
 bloat (#359)

* Use const paramater in mir.format.text to reduce template bloat

* Update format.d

Co-authored-by: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
---
 source/mir/format.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index bbb613f1..464995ac 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -59,7 +59,7 @@ unittest
 }
 
 /// Concatenated string results
-string text(string separator = "", Args...)(auto ref Args args)
+string text(string separator = "", Args...)(auto ref const(Args) args)
     if (Args.length > 0)
 {
     static if (Args.length == 1)

From e4d47174dc2bf86f0ca3b0d28eded65d993ffc15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Mon, 20 Sep 2021 06:16:05 +0200
Subject: [PATCH 247/573] Complete use of isSomeChar and isOutputRange in
 template restrictions in mir.format (#361)

Co-authored-by: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
---
 source/mir/format.d | 37 +++++++++++++++++++++++--------------
 1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 464995ac..9de13e29 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -7,6 +7,7 @@ Authors: Ilya Yaroshenko
 module mir.format;
 
 import std.traits;
+import mir.primitives: isOutputRange;
 
 /// `mir.conv: to` extension.
 version(mir_test)
@@ -276,6 +277,7 @@ struct FormattedFloating(T)
 
     ///
     void toString(C = char, W)(scope ref W w) scope const
+    if (isSomeChar!C && isOutputRange!(W, C))
     {
         C[512] buf = void;
         auto n = printFloatingPoint(value, spec, buf);
@@ -302,6 +304,7 @@ struct HexAddress(T)
 
     ///
     void toString(C = char, W)(scope ref W w) scope const
+        if (isSomeChar!C && isOutputRange!(W, C))
     {
         enum N = T.sizeof * 2;
         static if(isFastBuffer!W)
@@ -337,6 +340,7 @@ enum escapeFormatQuote(EscapeFormat escapeFormat) = escapeFormat == EscapeFormat
 /++
 +/
 ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope return ref W w, scope const(C)[] str)
+    if (isOutputRange!(W, C))
 {
     import mir.utility: _expect;
     foreach (C c; str)
@@ -406,7 +410,6 @@ ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
-
     import mir.format: stringBuf;
     stringBuf w;
     assert(w.printEscaped("Hi \a\v\0\f\t\b \\\r\n" ~ `"@nogc"`).data == `Hi \a\v\0\f\t\b \\\r\n\"@nogc\"`);
@@ -418,6 +421,7 @@ version (mir_test) unittest
 Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
 +/
 ref W put_xXX(C = char, W)(scope return ref W w, char c)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     ubyte[2] spl;
     spl[0] = c >> 4;
@@ -434,6 +438,7 @@ ref W put_xXX(C = char, W)(scope return ref W w, char c)
 Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
 +/
 ref W put_uXXXX(C = char, W)(scope return ref W w, char c)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     ubyte[2] spl;
     spl[0] = c >> 4;
@@ -452,6 +457,7 @@ ref W put_uXXXX(C = char, W)(scope return ref W w, char c)
 Decodes ushort `c` to the form `uXXXX`, where `XXXX` is 2 hexadecimal characters.
 +/
 ref W put_uXXXX(C = char, W)(scope return ref W w, ushort c)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     ubyte[4] spl;
     spl[0] = (c >> 12) & 0xF;
@@ -470,7 +476,7 @@ ref W put_uXXXX(C = char, W)(scope return ref W w, ushort c)
 
 ///
 ref W printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope return ref W w, scope const(C)[] c)
-    if (isSomeChar!C)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     static immutable C[1] quote = '\"';
     return w
@@ -481,7 +487,7 @@ ref W printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
 
 ///
 ref W printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)(scope return ref W w, scope auto ref const T c)
-    if (!isSomeString!T)
+    if (!isSomeString!T && isOutputRange!(W, C))
 {
     return w.print!C(c);
 }
@@ -490,7 +496,7 @@ ref W printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)
 Multiargument overload.
 +/
 ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Args args)
-    if (Args.length > 1)
+    if (isSomeChar!C && isOutputRange!(W, C) && Args.length > 1)
 {
     foreach(i, ref c; args)
         static if (i < Args.length - 1)
@@ -501,7 +507,7 @@ ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Arg
 
 /// Prints enums
 ref W print(C = char, W, T)(scope return ref W w, const T c)
-    if (is(T == enum))
+    if (isSomeChar!C && isOutputRange!(W, C) && is(T == enum))
 {
     import mir.enums: getEnumIndex, enumStrings;
     import mir.utility: _expect;
@@ -538,6 +544,7 @@ version (mir_test) unittest
 
 /// Prints boolean
 ref W print(C = char, W)(scope return ref W w, bool c)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     enum N = 5;
     static if(isFastBuffer!W)
@@ -567,6 +574,7 @@ version (mir_test) unittest
 /// Prints associative array
 pragma(inline, false)
 ref W print(C = char, W, V, K)(scope return ref W w, scope const V[K] c)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     enum C left = '[';
     enum C right = ']';
@@ -601,7 +609,7 @@ version (mir_test) unittest
 /// Prints array
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const(T)[] c)
-    if (!isSomeChar!T)
+    if (isSomeChar!C && isOutputRange!(W, C) && !isSomeChar!T)
 {
     enum C left = '[';
     enum C right = ']';
@@ -632,6 +640,7 @@ version (mir_test) unittest
 /// Prints escaped character in the form `'c'`.
 pragma(inline, false)
 ref W print(C = char, W)(scope return ref W w, char c)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     w.put('\'');
     if (c >= 0x20)
@@ -687,7 +696,7 @@ version (mir_test) unittest
 
 /// Prints some string
 ref W print(C = char, W)(scope return ref W w, scope const(C)[] c)
-    if (isSomeChar!C)
+    if (isSomeChar!C && isOutputRange!(W, C))
 {
     w.put(c);
     return w;
@@ -695,7 +704,7 @@ ref W print(C = char, W)(scope return ref W w, scope const(C)[] c)
 
 /// Prints integers
 ref W print(C = char, W, I)(scope return ref W w, const I c)
-    if (isIntegral!I && !is(I == enum))
+    if (isSomeChar!C && isOutputRange!(W, C) && isIntegral!I && !is(I == enum))
 {
     static if (I.sizeof == 16)
         enum N = 39;
@@ -715,7 +724,7 @@ ref W print(C = char, W, I)(scope return ref W w, const I c)
 
 /// Prints floating point numbers
 ref W print(C = char, W, T)(scope return ref W w, const T c, NumericSpec spec = NumericSpec.init)
-    if(is(T == float) || is(T == double) || is(T == real))
+    if(isSomeChar!C && isOutputRange!(W, C) && is(T == float) || is(T == double) || is(T == real))
 {
     import mir.bignum.decimal;
     auto decimal = Decimal!(T.mant_dig < 64 ? 1 : 2)(c);
@@ -808,7 +817,7 @@ unittest
 /// Prints structs and unions
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, ref const T c)
-    if (is(T == struct) || is(T == union) && !is(T : NumericSpec))
+    if (isSomeChar!C && isOutputRange!(W, C) && is(T == struct) || is(T == union) && !is(T : NumericSpec))
 {
     static if (__traits(hasMember, T, "toString"))
     {
@@ -871,7 +880,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
 // FUTURE: remove it
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
-    if (is(T == struct) || is(T == union))
+    if (isSomeChar!C && isOutputRange!(W, C) && is(T == struct) || is(T == union))
 {
     return print!(C, W, T)(w, c);
 }
@@ -894,7 +903,7 @@ version (mir_test) unittest
 /// Prints classes and interfaces
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
-    if (is(T == class) || is(T == interface))
+    if (isSomeChar!C && isOutputRange!(W, C) && is(T == class) || is(T == interface))
 {
     enum C[4] Null = "null";
     static if (__traits(hasMember, T, "toString") || __traits(compiles, { scope const(C)[] string_of_c = c; }))
@@ -960,7 +969,7 @@ version (mir_test) unittest
 
 ///
 ref W printStaticString(C, size_t N, W)(scope return ref W w, ref scope const C[N] c)
-    if (is(C == char) || is(C == wchar) || is(C == dchar))
+    if (isSomeChar!C && isOutputRange!(W, C) && is(C == char) || is(C == wchar) || is(C == dchar))
 {
     static if (isFastBuffer!W)
     {
@@ -1008,7 +1017,7 @@ template isFastBuffer(W)
 
 ///
 ref W printZeroPad(C = char, W, I)(scope return ref W w, const I c, size_t minimalLength)
-    if (isIntegral!I && !is(I == enum))
+    if (isSomeChar!C && isOutputRange!(W, C) && isIntegral!I && !is(I == enum))
 {
     static if (I.sizeof == 16)
         enum N = 39;

From 64cfcbf0269a87f6734dca2cdf4113528eae29b9 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 21 Sep 2021 12:17:41 +0700
Subject: [PATCH 248/573] Fix #363

---
 source/mir/ndslice/slice.d | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index f9fb7d03..ade4950d 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -2929,10 +2929,11 @@ public:
             sl.opIndexOpAssignImplConcatenation!""(concatenation);
         }
 
+        static if (!isNumeric!DeepElement)
         /++
         Assignment of a value (e.g. a number) to a $(B fully defined slice).
         +/
-        void opIndexAssign(T, Slices...)(T value, Slices slices) scope return
+        void opIndexAssign(T, Slices...)(T value, Slices slices) scope
             if ((isFullPureSlice!Slices || isIndexedSlice!Slices)
                 && (!isDynamicArray!T || isDynamicArray!DeepElement)
                 && DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement
@@ -2943,6 +2944,14 @@ public:
             if(!sl.anyRUEmpty)
                 sl.opIndexOpAssignImplValue!""(value);
         }
+        else
+        void opIndexAssign(Slices...)(DeepElement value, Slices slices) scope
+            if (isFullPureSlice!Slices || isIndexedSlice!Slices)
+        {
+            auto sl = this.lightScope.opIndex(slices);
+            if(!sl.anyRUEmpty)
+                sl.opIndexOpAssignImplValue!""(value);
+        }
 
         static if (doUnittest)
         ///
@@ -2996,6 +3005,17 @@ public:
             }
             return _iterator[indexStride(_indices)] = value;
         }
+        ///ditto
+        auto ref opIndexAssign()(DeepElement value, size_t[N] _indices...) scope return @trusted
+        {
+            import mir.functional: forward;
+            // check assign safety
+            static auto ref fun(ref DeepElement t, ref DeepElement v) @safe
+            {
+                return t = v;
+            }
+            return _iterator[indexStride(_indices)] = forward!value;
+        }
 
         static if (doUnittest)
         ///
@@ -4080,3 +4100,17 @@ unittest // check it can be compiled
     alias S = Slice!(double*, 2);
     alias D = Variant!S;
 }
+
+version(mir_test)
+unittest
+{
+    import mir.ndslice;
+
+    auto matrix = slice!short(3, 4);
+    matrix[] = 0;
+    matrix.diagonal[] = 1;
+
+    auto row = matrix[2];
+    row[3] = 6;
+    assert(matrix[2, 3] == 6); // D & C index order
+}

From 1115b6d89660bac6c438c0e46e9a21bed5b16105 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 21 Sep 2021 16:00:43 +0700
Subject: [PATCH 249/573] Add mir.numeric.diff

---
 source/mir/numeric.d | 139 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 133 insertions(+), 6 deletions(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 5234b4f4..cad10f86 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -4,7 +4,7 @@ Base numeric algorithms.
 Reworked part of `std.numeric`.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot)
+Authors: Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot), Lars Tandle Kyllingstad (diff)
 +/
 module mir.numeric;
 
@@ -502,8 +502,8 @@ private @fmamath FindRootResult!T findRootImplGen(T)(
     {
         // Find the coefficients of the quadratic polynomial.
         const T a0 = fa;
-        const T a1 = (fb - fa)/(b - a);
-        const T a2 = ((fd - fb)/(d - b) - a1)/(d - a);
+        const T a1 = (fb - fa) / (b - a);
+        const T a2 = ((fd - fb) / (d - b) - a1) / (d - a);
 
         // Determine the starting point of newton steps.
         T c = a2.signbit != fa.signbit ? a : b;
@@ -950,7 +950,7 @@ version(mir_test) @safe unittest
         return pow(x, n) + double.min_normal;
     }
     int [] power_nvals = [3, 5, 7, 9, 19, 25];
-    // Alefeld paper states that pow(x,n) is a very poor case, where bisection
+    // Alefeld paper states that pow(x, n) is a very poor case, where bisection
     // outperforms his method, and gives total numcalls =
     // 921 for bisection (2.4 calls per bit), 1830 for Alefeld (4.76/bit),
     // 0.5f624 for brent (6.8/bit)
@@ -977,7 +977,7 @@ version(mir_test) @safe unittest
         ++numCalls;
         real q =  sin(x) - x/2;
         for (int i=1; i<20; ++i)
-            q+=(2*i-5.0)*(2*i-5.0)/((x-i*i)*(x-i*i)*(x-i*i));
+            q+=(2*i-5.0)*(2*i-5.0) / ((x-i*i)*(x-i*i)*(x-i*i));
         return q;
     }
     real alefeld1(real x)
@@ -1020,7 +1020,7 @@ version(mir_test) @safe unittest
     {
         ++numCalls;
         ++alefeldSums[7];
-        return (n*x-1)/((n-1)*x);
+        return (n*x-1) / ((n-1)*x);
     }
 
     numProblems=0;
@@ -1432,3 +1432,130 @@ private auto trustedAllAttr(T)(T t) @trusted
         | FunctionAttribute.nothrow_;
     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
 }
+
+/++
+Calculate the derivative of a function.
+This function uses Ridders' method of extrapolating the results
+of finite difference formulas for consecutively smaller step sizes,
+with an improved stopping criterion described in the Numerical Recipes
+books by Press et al.
+
+This method gives a much higher degree of accuracy in the answer
+compared to a single finite difference calculation, but requires
+more function evaluations; typically 6 to 12. The maximum number
+of function evaluations is $(D 24).
+
+Params:
+    f = The function of which to take the derivative.
+    x = The point at which to take the derivative.
+    h = A "characteristic scale" over which the function changes.
+    factor = Stepsize is decreased by factor at each iteration.
+    safe = Return when error is SAFE worse than the best so far.
+
+References:
+$(UL
+    $(LI
+        C. J. F. Ridders,
+        $(I Accurate computation of F'(x) and F'(x)F''(x)).
+        Advances in Engineering Software, vol. 4 (1982), issue 2, p. 75.)
+    $(LI
+        W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery,
+        $(I Numerical Recipes in C++) (2nd ed.).
+        Cambridge University Press, 2003.)
+)
++/
+@fmamath
+DiffResult!T diff(alias f, T)(const T x, const T h, const T factor = T(2).sqrt, const T safe = 2)
+{
+    if (false) // break attributes
+        T y = f(T(1));
+    scope funInst = delegate(T x) {
+        return T(f(x));
+    };
+    scope fun = funInst.trustedAllAttr;
+    return diffImpl(fun, x, h, factor, safe);
+}
+
+///ditto
+DiffResult!T diffImpl(T)
+    (scope const T delegate(T) @safe pure nothrow @nogc f, const T x, const T h, const T factor = T(2).sqrt, const T safe = 2)
+    @safe pure nothrow @nogc
+in {
+    assert(h < T.max);
+    assert(h > T.min_normal);
+}
+do {
+    // Set up Romberg tableau.
+    import mir.ndslice.slice: sliced;
+    pragma(inline, false);
+
+    enum tableauSize = 16;
+    T[tableauSize ^^ 2] workspace = void;
+    auto tab = workspace[].sliced(tableauSize, tableauSize);
+
+    // From the NR book: Stop when the difference between consecutive
+    // approximations is bigger than SAFE*error, where error is an
+    // estimate of the absolute error in the current (best) approximation.
+
+    // First approximation: A_0
+    T result = void;
+    T error = T.max;
+    T hh = h;
+
+    tab[0,0] = (f(x + h) - f(x - h))  / (2 * h);
+    foreach (n; 1 .. tableauSize)
+    {
+        // Decrease h.
+        hh /= factor;
+
+        // Compute A_n
+        tab[0, n] = (f(x + hh) - f(x - hh)) / (2 * hh);
+
+        T facm = 1;
+        foreach (m; 1 .. n + 1)
+        {
+            facm *= factor ^^ 2;
+
+            // Compute B_(n-1), C_(n-2), ...
+            T upLeft  = tab[m - 1, n - 1];
+            T up      = tab[m - 1, n];
+            T current = (facm * up - upLeft) / (facm - 1);
+            tab[m, n] = current;
+
+            // Calculate and check error.
+            T currentError = fmax(fabs(current - upLeft), fabs(current - up));
+            if (currentError <= error)
+            {
+                result = current;
+                error = currentError;
+            }
+        }
+
+        if (fabs(tab[n, n] - tab[n - 1, n - 1]) >= safe * error)
+            break;
+    }
+
+    return typeof(return)(result, error);
+}
+
+///
+unittest
+{
+    import mir.math.common;
+
+    auto f(double x) { return exp(x) / (sin(x) - x ^^ 2); }
+    auto d(double x) { return -(exp(x) * ((x - 2) * x - sin(x) + cos(x)))/(x^^2 - sin(x))^^2; }
+    auto r = diff!f(1.0, 0.01);
+    assert (approxEqual(r.value, d(1)));
+}
+
+/++
++/
+struct DiffResult(T)
+    if (__traits(isFloating, T))
+{
+    ///
+    T value = 0;
+    ///
+    T error = 0;
+}

From 1300aad0b22c2029e9b14764b5824d9f34131096 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 23 Sep 2021 09:51:13 +0700
Subject: [PATCH 250/573] revert some changes in format: for some API the
 appender can accept only arrays, not chars.

---
 source/mir/format.d | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 9de13e29..0e31448f 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -277,7 +277,7 @@ struct FormattedFloating(T)
 
     ///
     void toString(C = char, W)(scope ref W w) scope const
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
     {
         C[512] buf = void;
         auto n = printFloatingPoint(value, spec, buf);
@@ -304,7 +304,7 @@ struct HexAddress(T)
 
     ///
     void toString(C = char, W)(scope ref W w) scope const
-        if (isSomeChar!C && isOutputRange!(W, C))
+        if (isSomeChar!C)
     {
         enum N = T.sizeof * 2;
         static if(isFastBuffer!W)
@@ -421,7 +421,7 @@ version (mir_test) unittest
 Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
 +/
 ref W put_xXX(C = char, W)(scope return ref W w, char c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     ubyte[2] spl;
     spl[0] = c >> 4;
@@ -438,7 +438,7 @@ ref W put_xXX(C = char, W)(scope return ref W w, char c)
 Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
 +/
 ref W put_uXXXX(C = char, W)(scope return ref W w, char c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     ubyte[2] spl;
     spl[0] = c >> 4;
@@ -457,7 +457,7 @@ ref W put_uXXXX(C = char, W)(scope return ref W w, char c)
 Decodes ushort `c` to the form `uXXXX`, where `XXXX` is 2 hexadecimal characters.
 +/
 ref W put_uXXXX(C = char, W)(scope return ref W w, ushort c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     ubyte[4] spl;
     spl[0] = (c >> 12) & 0xF;
@@ -476,7 +476,7 @@ ref W put_uXXXX(C = char, W)(scope return ref W w, ushort c)
 
 ///
 ref W printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope return ref W w, scope const(C)[] c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     static immutable C[1] quote = '\"';
     return w
@@ -487,7 +487,7 @@ ref W printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
 
 ///
 ref W printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)(scope return ref W w, scope auto ref const T c)
-    if (!isSomeString!T && isOutputRange!(W, C))
+    if (!isSomeString!T)
 {
     return w.print!C(c);
 }
@@ -496,7 +496,7 @@ ref W printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)
 Multiargument overload.
 +/
 ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Args args)
-    if (isSomeChar!C && isOutputRange!(W, C) && Args.length > 1)
+    if (isSomeChar!C && Args.length > 1)
 {
     foreach(i, ref c; args)
         static if (i < Args.length - 1)
@@ -507,7 +507,7 @@ ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Arg
 
 /// Prints enums
 ref W print(C = char, W, T)(scope return ref W w, const T c)
-    if (isSomeChar!C && isOutputRange!(W, C) && is(T == enum))
+    if (isSomeChar!C && is(T == enum))
 {
     import mir.enums: getEnumIndex, enumStrings;
     import mir.utility: _expect;
@@ -544,7 +544,7 @@ version (mir_test) unittest
 
 /// Prints boolean
 ref W print(C = char, W)(scope return ref W w, bool c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     enum N = 5;
     static if(isFastBuffer!W)
@@ -574,7 +574,7 @@ version (mir_test) unittest
 /// Prints associative array
 pragma(inline, false)
 ref W print(C = char, W, V, K)(scope return ref W w, scope const V[K] c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     enum C left = '[';
     enum C right = ']';
@@ -609,7 +609,7 @@ version (mir_test) unittest
 /// Prints array
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const(T)[] c)
-    if (isSomeChar!C && isOutputRange!(W, C) && !isSomeChar!T)
+    if (isSomeChar!C && !isSomeChar!T)
 {
     enum C left = '[';
     enum C right = ']';
@@ -640,7 +640,7 @@ version (mir_test) unittest
 /// Prints escaped character in the form `'c'`.
 pragma(inline, false)
 ref W print(C = char, W)(scope return ref W w, char c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     w.put('\'');
     if (c >= 0x20)
@@ -696,7 +696,7 @@ version (mir_test) unittest
 
 /// Prints some string
 ref W print(C = char, W)(scope return ref W w, scope const(C)[] c)
-    if (isSomeChar!C && isOutputRange!(W, C))
+    if (isSomeChar!C)
 {
     w.put(c);
     return w;
@@ -704,7 +704,7 @@ ref W print(C = char, W)(scope return ref W w, scope const(C)[] c)
 
 /// Prints integers
 ref W print(C = char, W, I)(scope return ref W w, const I c)
-    if (isSomeChar!C && isOutputRange!(W, C) && isIntegral!I && !is(I == enum))
+    if (isSomeChar!C && isIntegral!I && !is(I == enum))
 {
     static if (I.sizeof == 16)
         enum N = 39;
@@ -724,7 +724,7 @@ ref W print(C = char, W, I)(scope return ref W w, const I c)
 
 /// Prints floating point numbers
 ref W print(C = char, W, T)(scope return ref W w, const T c, NumericSpec spec = NumericSpec.init)
-    if(isSomeChar!C && isOutputRange!(W, C) && is(T == float) || is(T == double) || is(T == real))
+    if(isSomeChar!C && is(T == float) || is(T == double) || is(T == real))
 {
     import mir.bignum.decimal;
     auto decimal = Decimal!(T.mant_dig < 64 ? 1 : 2)(c);
@@ -817,7 +817,7 @@ unittest
 /// Prints structs and unions
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, ref const T c)
-    if (isSomeChar!C && isOutputRange!(W, C) && is(T == struct) || is(T == union) && !is(T : NumericSpec))
+    if (isSomeChar!C && is(T == struct) || is(T == union) && !is(T : NumericSpec))
 {
     static if (__traits(hasMember, T, "toString"))
     {
@@ -880,7 +880,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
 // FUTURE: remove it
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
-    if (isSomeChar!C && isOutputRange!(W, C) && is(T == struct) || is(T == union))
+    if (isSomeChar!C && is(T == struct) || is(T == union))
 {
     return print!(C, W, T)(w, c);
 }
@@ -903,7 +903,7 @@ version (mir_test) unittest
 /// Prints classes and interfaces
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
-    if (isSomeChar!C && isOutputRange!(W, C) && is(T == class) || is(T == interface))
+    if (isSomeChar!C && is(T == class) || is(T == interface))
 {
     enum C[4] Null = "null";
     static if (__traits(hasMember, T, "toString") || __traits(compiles, { scope const(C)[] string_of_c = c; }))
@@ -969,7 +969,7 @@ version (mir_test) unittest
 
 ///
 ref W printStaticString(C, size_t N, W)(scope return ref W w, ref scope const C[N] c)
-    if (isSomeChar!C && isOutputRange!(W, C) && is(C == char) || is(C == wchar) || is(C == dchar))
+    if (isSomeChar!C && is(C == char) || is(C == wchar) || is(C == dchar))
 {
     static if (isFastBuffer!W)
     {
@@ -1017,7 +1017,7 @@ template isFastBuffer(W)
 
 ///
 ref W printZeroPad(C = char, W, I)(scope return ref W w, const I c, size_t minimalLength)
-    if (isSomeChar!C && isOutputRange!(W, C) && isIntegral!I && !is(I == enum))
+    if (isSomeChar!C && isIntegral!I && !is(I == enum))
 {
     static if (I.sizeof == 16)
         enum N = 39;

From 407757b82072f4d5791b61e7a6052dee2aa9e4ef Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 24 Sep 2021 20:09:10 +0700
Subject: [PATCH 251/573] fix serdeGetFinalDeepProxy

---
 source/mir/serde.d | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 9ff0788a..09409268 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1087,9 +1087,9 @@ template serdeGetFinalDeepProxy(T)
     else
     static if (is(immutable T == immutable V[K], K, V))
     {
-        alias E = Unqual!V;
+        alias E = serdeGetFinalDeepProxy!(Unqual!V);
         static if (isAggregateType!E || is(E == enum))
-            alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E;
+            alias serdeGetFinalDeepProxy = E;
         else
             alias serdeGetFinalDeepProxy = T;
     }
@@ -1114,7 +1114,7 @@ version(mir_test) unittest
     @serdeProxy!(B[])
     static struct C {}
 
-    static assert (is(serdeGetFinalDeepProxy!C == string), serdeGetFinalDeepProxy!C.stringof);
+    static assert (is(serdeGetFinalDeepProxy!C == A[E]));
     static assert (is(serdeGetFinalDeepProxy!string == string));
 }
 

From f2b9a9907e045928688ce4553ce0c9ab7dfde412 Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Fri, 24 Sep 2021 20:11:04 +0700
Subject: [PATCH 252/573] Revert "Use const parameters in mir.format to reduce
 template bloat" (#364)

* Revert "Use const parameters in mir.format to reduce template bloat (#359)"

This reverts commit 9a6abda64b9c16f920958a1f8c0e6cb7b58b2117.

* add test
---
 source/mir/format.d | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 0e31448f..b8f02524 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -60,7 +60,7 @@ unittest
 }
 
 /// Concatenated string results
-string text(string separator = "", Args...)(auto ref const(Args) args)
+string text(string separator = "", Args...)(auto ref Args args)
     if (Args.length > 0)
 {
     static if (Args.length == 1)
@@ -88,7 +88,8 @@ string text(string separator = "", Args...)(auto ref const(Args) args)
 ///
 @safe pure nothrow unittest
 {
-    assert(text("str ", true, " ", 100, " ", 124.1) == "str true 100 124.1", text("str ", true, " ", 100, " ", 124.1));
+    const i = 100;
+    assert(text("str ", true, " ", i, " ", 124.1) == "str true 100 124.1", text("str ", true, " ", 100, " ", 124.1));
     assert(text!" "("str", true, 100, 124.1) == "str true 100 124.1");
 }
 

From 74931bff75ce3bc2709ad94b01fe2620a8374125 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 1 Nov 2021 22:56:33 +0700
Subject: [PATCH 253/573] fix Timestamp

---
 source/mir/serde.d     | 2 +-
 source/mir/timestamp.d | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 09409268..08bdc219 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1010,7 +1010,7 @@ template serdeGetFinalProxy(T)
             alias serdeGetFinalProxy = .serdeGetFinalProxy!(serdeGetProxy!T);
         }
         else
-        static if (isAggregateType!T && is(typeof(Timestamp(T.init))))
+        static if (isAggregateType!T && is(typeof(Timestamp(T.init))) && __traits(getAliasThis, T).length == 0)
         {
             alias serdeGetFinalProxy = string;
         }
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index b143f5af..73994ab5 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -173,7 +173,7 @@ struct Timestamp
         +/
         byte fractionExponent;
         /// ditto
-        long fractionCoefficient;
+        ulong fractionCoefficient;
     }
     else
     {
@@ -185,13 +185,13 @@ struct Timestamp
                     ubyte, "minute", 8,
                     ubyte, "second", 8,
                     byte, "fractionExponent", 8,
-                    long, "fractionCoefficient", 40,
+                    ulong, "fractionCoefficient", 40,
             ));
         }
         else
         {
             mixin(bitfields!(
-                    long, "fractionCoefficient", 40,
+                    ulong, "fractionCoefficient", 40,
                     byte, "fractionExponent", 8,
                     ubyte, "second", 8,
                     ubyte, "minute", 8,

From 6617d18e1e76d9aa0a18ae5b998fd8d0bca7034e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 6 Nov 2021 12:13:50 +0700
Subject: [PATCH 254/573] Add algebraic decomposition for timestamp

---
 source/mir/timestamp.d | 109 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 106 insertions(+), 3 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 73994ab5..781d6590 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -380,19 +380,122 @@ struct Timestamp
         assert(st == cast(SysTime) ts);
     }
 
+    /++
+    Decomposes Timestamp to an algebraic type.
+    Supported types up to T.stringof equivalence:
+
+    $(UL
+    $(LI `Year`)
+    $(LI `YearMonth`)
+    $(LI `YearMonthDay`)
+    $(LI `Date`)
+    $(LI `date`)
+    $(LI `TimeOfDay`)
+    $(LI `DateTime`)
+    $(LI `SysTime`)
+    $(LI `Timestamp` as fallback type)
+
+    Throws: an exception if timestamp cannot be converted to an algebraic type and there is no `Timestamp` type in the Algebraic set.
+    )
+    +/
+    T opCast(T)() const
+        if (__traits(hasMember, T, "AllowedTypes"))
+    {
+        foreach (AT; T.AllowedTypes)
+            static if (AT.stringof == "Year")
+                if (precision == precision.year)
+                    return T(opCast!AT);
+
+        foreach (AT; T.AllowedTypes)
+            static if (AT.stringof == "YearMonth")
+                if (precision == precision.month)
+                    return T(opCast!AT);
+
+        foreach (AT; T.AllowedTypes)
+            static if (AT.stringof == "YearMonthDay" || AT.stringof == "Date" ||  AT.stringof == "date")
+                if (precision == precision.day)
+                    return T(opCast!AT);
+
+        foreach (AT; T.AllowedTypes)
+            static if (AT.stringof == "TimeOfDay")
+                if (isOnlyTime)
+                    return T(opCast!AT);
+
+        if (!isOnlyTime && precision >= precision.day)
+        {
+            foreach (AT; T.AllowedTypes)
+                static if (AT.stringof == "DateTime")
+                    if (offset == 0 && precision <= precision.second)
+                        return T(opCast!AT);
+
+            foreach (AT; T.AllowedTypes)
+                static if (AT.stringof == "SysTime")
+                    return T(opCast!AT);
+        }
+
+        import std.meta: staticIndexOf;
+        static if (staticIndexOf!(Timestamp, T.AllowedTypes) < 0)
+        {
+            static immutable exc = Exception("Cannot cast Timestamp to " ~ T.stringof);
+            throw exc;
+        }
+        else
+        {
+            return T(this);
+        }
+    }
+
     ///
+    unittest
+    {
+        import core.time : hnsecs, minutes;
+        import mir.algebraic;
+        import mir.date: Date; // Can be other Date type as well
+        import std.datetime.date : TimeOfDay, DateTime;
+        import std.datetime.systime : SysTime;
+        import std.datetime.timezone: UTC, SimpleTimeZone;
+
+        alias A = Variant!(Date, TimeOfDay, DateTime, SysTime, Timestamp, string); // non-date-time types is OK
+        assert(cast(A) Timestamp(1023) == Timestamp(1023)); // Year isn't represented in the algebraic, use fallback type
+        assert(cast(A) Timestamp.onlyTime(7, 40, 30) == TimeOfDay(7, 40, 30));
+        assert(cast(A) Timestamp(1982, 4, 1, 20, 59, 22) == DateTime(1982, 4, 1, 20, 59, 22));
+
+        auto dt = DateTime(1982, 4, 1, 20, 59, 22);
+        auto tz = new immutable SimpleTimeZone(-330.minutes);
+        auto st = SysTime(dt, 1234567.hnsecs, tz);
+        assert(cast(A) Timestamp(st) == st);
+    }
+
+    /++
+    Casts timestamp to a date-time type.
+
+    Supported types up to T.stringof equivalence:
+
+    $(UL
+    $(LI `Year`)
+    $(LI `YearMonth`)
+    $(LI `YearMonthDay`)
+    $(LI `Date`)
+    $(LI `date`)
+    $(LI `TimeOfDay`)
+    $(LI `DateTime`)
+    $(LI `SysTime`)
+    )
+    +/
     T opCast(T)() const
-        if (T.stringof == "YearMonth"
+        if (
+            T.stringof == "Year"
+         || T.stringof == "YearMonth"
          || T.stringof == "YearMonthDay"
          || T.stringof == "Date"
-         || T.stringof == "TimeOfDay"
          || T.stringof == "date"
+         || T.stringof == "TimeOfDay"
          || T.stringof == "DateTime"
          || T.stringof == "SysTime")
     {
         static if (T.stringof == "YearMonth")
         {
-            return T(year, month, day);
+            return T(year, month);
         }
         else
         static if (T.stringof == "Date" || T.stringof == "date" || T.stringof == "YearMonthDay")

From ebb721b00165a3da2e9f9cf646831f99ee26bd7a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 6 Nov 2021 12:21:34 +0700
Subject: [PATCH 255/573] mark test safe

---
 source/mir/timestamp.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 781d6590..039d5d3b 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -446,7 +446,7 @@ struct Timestamp
     }
 
     ///
-    unittest
+    @safe unittest
     {
         import core.time : hnsecs, minutes;
         import mir.algebraic;

From 6f5566e2f18ed1d118ed20453ec7a3830549297f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 6 Nov 2021 12:54:20 +0700
Subject: [PATCH 256/573] fixup

---
 source/mir/bignum/decimal.d | 2 +-
 source/mir/numeric.d        | 2 +-
 source/mir/timestamp.d      | 3 ++-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 7a897188..fcb7bcec 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -130,7 +130,7 @@ struct Decimal(size_t maxSize64)
         const C fractionSeparator,
         out DecimalExponentKey key,
         int exponentShift = 0,
-    )
+    ) @trusted
         if (isSomeChar!C)
     {
         import mir.algorithm.iteration: find;
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index cad10f86..b4a9655c 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1479,7 +1479,7 @@ DiffResult!T diff(alias f, T)(const T x, const T h, const T factor = T(2).sqrt,
 ///ditto
 DiffResult!T diffImpl(T)
     (scope const T delegate(T) @safe pure nothrow @nogc f, const T x, const T h, const T factor = T(2).sqrt, const T safe = 2)
-    @safe pure nothrow @nogc
+    @trusted pure nothrow @nogc
 in {
     assert(h < T.max);
     assert(h > T.min_normal);
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 039d5d3b..0f1000b3 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -394,9 +394,10 @@ struct Timestamp
     $(LI `DateTime`)
     $(LI `SysTime`)
     $(LI `Timestamp` as fallback type)
+    )
+
 
     Throws: an exception if timestamp cannot be converted to an algebraic type and there is no `Timestamp` type in the Algebraic set.
-    )
     +/
     T opCast(T)() const
         if (__traits(hasMember, T, "AllowedTypes"))

From e967733bb34d2c6feb3e7038898a5d172db3d7c8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 13:19:49 +0700
Subject: [PATCH 257/573] fixup Timestamp exception

---
 source/mir/timestamp.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 0f1000b3..311e2433 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -437,7 +437,7 @@ struct Timestamp
         import std.meta: staticIndexOf;
         static if (staticIndexOf!(Timestamp, T.AllowedTypes) < 0)
         {
-            static immutable exc = Exception("Cannot cast Timestamp to " ~ T.stringof);
+            static immutable exc = new Exception("Cannot cast Timestamp to " ~ T.stringof);
             throw exc;
         }
         else

From c6eb8e33e52c622c22331ee4961fd7c9c188c18e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 13:49:57 +0700
Subject: [PATCH 258/573] update lob

---
 source/mir/lob.d | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source/mir/lob.d b/source/mir/lob.d
index 82a7ebe0..8c4036e7 100644
--- a/source/mir/lob.d
+++ b/source/mir/lob.d
@@ -2,10 +2,13 @@
 +/
 module mir.lob;
 
+import mir.serde: serdeRegister;
+
 /++
 Values of type clob are encoded as a sequence of octets that should be interpreted as text
 with an unknown encoding (and thus opaque to the application).
 +/
+@serdeRegister
 struct Clob
 {
     ///
@@ -15,6 +18,7 @@ struct Clob
 /++
 This is a sequence of octets with no interpretation (and thus opaque to the application).
 +/
+@serdeRegister
 struct Blob
 {
     ///

From 51b4da6f9c5ed2872b48c2e0be5b1835c87109a5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 19:58:36 +0700
Subject: [PATCH 259/573] add mir.annotated

---
 source/mir/annotated.d | 82 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 source/mir/annotated.d

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
new file mode 100644
index 00000000..98e11241
--- /dev/null
+++ b/source/mir/annotated.d
@@ -0,0 +1,82 @@
+/++
+$(H1 Annotated value)
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko 
+Macros:
++/
+module mir.annotated;
+
+import mir.serde: serdeRegister, serdeAnnotation;
+
+static immutable excMsg = "At least one annotation is required to create an annotated value.";
+version (D_Exceptions)
+    static immutable exc = new Exception(excMsg);
+
+/++
+A convenience difintion of an annotated value. 
++/
+@serdeRegister
+@serdeAnnotation
+struct Annotated(T) {
+    ///
+    @serdeAnnotation
+    string[] annotations;
+    static if (is(T == union) || is(T == struct))
+    {
+        ///
+        private T* _value;
+        ///
+        ref inout(T) value() inout @property
+        {
+            return *_value;
+        }
+    }
+    else
+    {
+        ///
+        T value;
+    }
+
+    ///
+    alias value this;
+
+    /++
+    Params:
+        annotations = non-empty array of annotations
+        value = actual value
+    +/
+    this(Args...)(string[] annotations, Args args) @safe pure {
+        if (annotations.length == 0)
+        {
+            version (D_Exceptions)
+                throw exc;
+            else
+                assert(0, excMsg);
+        }
+        import core.lifetime: forward;
+        this.annotations = annotations;
+        static if (is(T == union) || is(T == struct))
+            this._value = new T(forward!args);
+        else
+        static if (__traits(compiles, value = args))
+            this.value = args;
+        else
+            this.value = new T(forward!args);
+    }
+}
+
+///
+unittest
+{
+    auto annotations = ["annotation"];
+    static struct S {double x;}
+    auto as = Annotated!S(annotations, 5);
+    assert(as.annotations == annotations);
+    assert(as.x == 5);
+
+    static struct C {double x;}
+    auto ac = Annotated!S(annotations, 5);
+    assert(ac.annotations == annotations);
+    assert(ac.x == 5);
+}

From a7e0ee2fa5cc45de3ccf598611ee6becd27aa6f6 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 19:58:48 +0700
Subject: [PATCH 260/573] add mir.algebraic_alias.ion

---
 source/mir/algebraic_alias/ion.d | 136 +++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)
 create mode 100644 source/mir/algebraic_alias/ion.d

diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
new file mode 100644
index 00000000..76b63bea
--- /dev/null
+++ b/source/mir/algebraic_alias/ion.d
@@ -0,0 +1,136 @@
+/++
+$(H1 Mutable Ion value)
+
+This module contains a single alias definition and doesn't provide Ion serialization API.
+
+See_also: Ion library $(MIR_PACKAGE mir-ion)
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko 
+Macros:
++/
+module mir.algebraic_alias.ion;
+
+import mir.algebraic: TaggedVariant, This;
+public import mir.annotated: Annotated;
+public import mir.lob: Clob, Blob;
+public import mir.string_map: StringMap;
+public import mir.timestamp: Timestamp;
+
+
+/++
+Definition union for $(LREF IonAlgebraic).
++/
+union IonAlgebraicUnion
+{
+    ///
+    typeof(null) null_;
+    ///
+    bool boolean;
+    ///
+    long integer;
+    ///
+    double float_;
+    ///
+    immutable(char)[] string;
+    ///
+    Blob blob;
+    ///
+    Clob clob;
+    ///
+    Timestamp timestamp;
+    /// Self alias in array.
+    This[] array;
+    /// Self alias in $(MREF mir,string_map).
+    StringMap!This object;
+    /// Self alias in $(MREF mir,annotated).
+    Annotated!This annotations;
+}
+
+/++
+Ion tagged algebraic alias.
+
+The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic).
+See also $(MREF mir,string_map) - ordered string-value associative array.
++/
+alias IonAlgebraic = TaggedVariant!IonAlgebraicUnion;
+
+///
+unittest
+{
+    import mir.ndslice.topology: map;
+    import mir.array.allocation: array;
+
+    IonAlgebraic value;
+
+    StringMap!IonAlgebraic object;
+
+    // Default
+    assert(value.isNull);
+    assert(value.kind == IonAlgebraic.Kind.null_);
+
+    // Boolean
+    value = object["bool"] = true;
+    assert(!value.isNull);
+    assert(value == true);
+    assert(value.kind == IonAlgebraic.Kind.boolean);
+    assert(value.get!bool == true);
+    assert(value.get!(IonAlgebraic.Kind.boolean) == true);
+
+    // Null
+    value = object["null"] = null;
+    assert(value.isNull);
+    assert(value == null);
+    assert(value.kind == IonAlgebraic.Kind.null_);
+    assert(value.get!(typeof(null)) == null);
+    assert(value.get!(IonAlgebraic.Kind.null_) == null);
+
+    // String
+    value = object["string"] = "s";
+    assert(value.kind == IonAlgebraic.Kind.string);
+    assert(value == "s");
+    assert(value.get!string == "s");
+    assert(value.get!(IonAlgebraic.Kind.string) == "s");
+
+    // Integer
+    value = object["integer"] = 4;
+    assert(value.kind == IonAlgebraic.Kind.integer);
+    assert(value == 4);
+    assert(value != 4.0);
+    assert(value.get!long == 4);
+    assert(value.get!(IonAlgebraic.Kind.integer) == 4);
+
+    // Float
+    value = object["float"] = 3.0;
+    assert(value.kind == IonAlgebraic.Kind.float_);
+    assert(value != 3);
+    assert(value == 3.0);
+    assert(value.get!double == 3.0);
+    assert(value.get!(IonAlgebraic.Kind.float_) == 3.0);
+
+    // Array
+    IonAlgebraic[] arr = [0, 1, 2, 3, 4].map!IonAlgebraic.array;
+
+    value = object["array"] = arr;
+    assert(value.kind == IonAlgebraic.Kind.array);
+    assert(value == arr);
+    assert(value.get!(IonAlgebraic[])[3] == 3);
+
+    // Object
+    assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]);
+    object.values[0] = "false";
+    assert(object["bool"] == "false"); // it is a string now
+    object.remove("bool"); // remove the member
+
+    value = object["array"] = object;
+    assert(value.kind == IonAlgebraic.Kind.object);
+    assert(value.get!(StringMap!IonAlgebraic).keys is object.keys);
+    assert(value.get!(StringMap!IonAlgebraic).values is object.values);
+
+    IonAlgebraic[string] aa = object.toAA;
+    object = StringMap!IonAlgebraic(aa);
+
+    IonAlgebraic fromAA = ["a" : IonAlgebraic(3), "b" : IonAlgebraic("b")];
+    assert(fromAA.get!(StringMap!IonAlgebraic)["a"] == 3);
+    assert(fromAA.get!(StringMap!IonAlgebraic)["b"] == "b");
+}

From ea897487c6b4c8552b5887f1aaebeaa5ec32a978 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 19:59:07 +0700
Subject: [PATCH 261/573] update docs

---
 index.d            | 4 +++-
 source/mir/serde.d | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/index.d b/index.d
index 53e6ad5c..7cdf53f8 100644
--- a/index.d
+++ b/index.d
@@ -11,16 +11,18 @@ $(BOOKTABLE ,
         $(TH Description)
     )
     $(LEADINGROW Accessories)
+    $(TR $(TDNW $(MREF mir,algebraic_alias,ion)) $(TD Mutable Amazon's Ion value))
     $(TR $(TDNW $(MREF mir,algebraic_alias,json)) $(TD Mutable JSON value))
+    $(TR $(TDNW $(MREF mir,annotated)) $(TD Mutable generic Annotated value))
     $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
     $(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities))
+    $(TR $(TDNW $(MREF mir,lob)) $(TD Binar and Char Large Objects ))
     $(TR $(TDNW $(MREF mir,parse)) $(TD @nogc Parsing Utilities))
     $(TR $(TDNW $(MREF mir,range)) $(TD Ranges))
     $(TR $(TDNW $(MREF mir,serde)) $(TD Utilities for serialization libraries ))
     $(TR $(TDNW $(MREF mir,small_array)) $(TD Generic Small Arrays))
     $(TR $(TDNW $(MREF mir,small_string)) $(TD Generic Small Strings))
     $(TR $(TDNW $(MREF mir,string_map)) $(TD Ordered string-value associative array with fast lookup))
-    $(TR $(TDNW $(MREF mir,lob)) $(TD Binar and Char Large Objects ))
     $(LEADINGROW Date and time)
     $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability))
     $(TR $(TDNW $(MREF mir,timestamp)) $(TD General purpose timestamp implementation with arbitrary precision ))
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 08bdc219..82e5a772 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -143,7 +143,9 @@ The attribute should be applied to a type definition.
 enum serdeRegister;
 
 /++
-The attribute should be applied to a string-like member that should be de/serialized as an annotation / attribute.
+The attribute can be applied to a string-like member that should be de/serialized as an annotation / attribute.
+
+Also, the attribute can be applied to on a type used in an algebraic type to denote that the type should be used to deserialize annotated value.
 
 This feature is used in $(MIR_PACKAGE mir-ion).
 +/

From 9f0bd15ab9d1315a770f602165b4e1abaec23e8d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 21:01:28 +0700
Subject: [PATCH 262/573] add serdeDynamicAlgebraic

---
 source/mir/serde.d | 71 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 82e5a772..eb372329 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -292,6 +292,77 @@ template serdeGetAlgebraicAnnotation(T)
     }
 }
 
+/++
+User defined attribute used to attach a function that returns a deserialization delegate.
+
+The attribute is usefull for scripting languages and dynamic algebraic types.
++/
+template serdeDynamicAlgebraic(alias getAlgebraicDeserializerByAnnotation)
+{
+    enum serdeDynamicAlgebraic;
+}
+
+///
+unittest
+{
+    static struct _global
+    {
+        alias Deserializer = S delegate(string s, ubyte[] data) @safe pure;
+        Deserializer getDeserializer(string name) { return map[name]; }
+        Deserializer[string] map;
+
+        @serdeDynamicAlgebraic!getDeserializer
+        struct S {}
+
+        static assert(serdeIsDynamicAlgebraic!S);
+        static assert(__traits(isSame, serdeGetAlgebraicDeserializer!S, getDeserializer));
+    }
+}
+
+/++
++/
+template serdeIsDynamicAlgebraic(T)
+{
+    static if (isAggregateType!T)
+    {
+        static if (hasUDA!(T, serdeDynamicAlgebraic))
+        {
+            enum serdeIsDynamicAlgebraic = true;
+        }
+        else
+        static if (__traits(getAliasThis, T).length)
+        {
+            T* aggregate;
+            alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
+            enum serdeIsDynamicAlgebraic = .serdeIsDynamicAlgebraic!A;
+        }
+        else
+        {
+            enum serdeIsDynamicAlgebraic = false;
+        }
+    }
+    else
+    {
+        enum serdeIsDynamicAlgebraic = false;
+    }
+}
+
+/++
++/
+template serdeGetAlgebraicDeserializer(T)
+{
+    static if (hasUDA!(T, serdeDynamicAlgebraic))
+    {
+        alias serdeGetAlgebraicDeserializer = TemplateArgsOf!(getUDA!(T, serdeDynamicAlgebraic))[0];
+    }
+    else
+    {
+        T* aggregate;
+        alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
+        alias serdeGetAlgebraicDeserializer = .serdeGetAlgebraicDeserializer!A;
+    }
+}
+
 /++
 Returns:
     immutable array of the input keys for the symbol or enum value

From 8effd5b5d093d0352be67d274188b9501b05d05b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 21:21:53 +0700
Subject: [PATCH 263/573] update meson

---
 meson.build | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/meson.build b/meson.build
index 5f81d23e..dc9a0b2c 100644
--- a/meson.build
+++ b/meson.build
@@ -8,9 +8,11 @@ subprojects = ['mir-core']
 has_cpp_headers = true
 
 sources_list = [
+    'mir/algebraic_alias/ion',
     'mir/algebraic_alias/json',
     'mir/algorithm/iteration',
     'mir/algorithm/setops',
+    'mir/annotated',
     'mir/appender',
     'mir/array/allocation',
     'mir/bignum/decimal',

From 1870c7567c7157006e0cb950c5059ec7c1a22c37 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 22:58:13 +0700
Subject: [PATCH 264/573] fixup annotated

---
 source/mir/algebraic_alias/ion.d |  6 +++
 source/mir/annotated.d           | 68 ++++++++++++++++++++++++++------
 source/mir/serde.d               |  5 ++-
 3 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
index 76b63bea..b66b6f42 100644
--- a/source/mir/algebraic_alias/ion.d
+++ b/source/mir/algebraic_alias/ion.d
@@ -133,4 +133,10 @@ unittest
     IonAlgebraic fromAA = ["a" : IonAlgebraic(3), "b" : IonAlgebraic("b")];
     assert(fromAA.get!(StringMap!IonAlgebraic)["a"] == 3);
     assert(fromAA.get!(StringMap!IonAlgebraic)["b"] == "b");
+
+    auto annotated = Annotated!IonAlgebraic(["birthday"], Timestamp("2001-01-01"));
+    value = annotated;
+    assert(value == annotated);
+    value = annotated.IonAlgebraic;
+    assert(value == annotated);
 }
diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 98e11241..a649e049 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -7,7 +7,7 @@ Macros:
 +/
 module mir.annotated;
 
-import mir.serde: serdeRegister, serdeAnnotation;
+import mir.serde: serdeRegister, serdeAnnotation, serdeIsDynamicAlgebraic;
 
 static immutable excMsg = "At least one annotation is required to create an annotated value.";
 version (D_Exceptions)
@@ -22,24 +22,51 @@ struct Annotated(T) {
     ///
     @serdeAnnotation
     string[] annotations;
-    static if (is(T == union) || is(T == struct))
+
+    static if (!(is(T == union) || is(T == struct)))
+        private enum _alloc_ = false;
+    else
+    static if (serdeIsDynamicAlgebraic!T)
+        private enum _alloc_ = true;
+    else
+    {
+        import mir.algebraic: isVariant;
+        static if (isVariant!T)
+            private enum _alloc_ = true;
+        else
+            private enum _alloc_ = false;
+    }
+
+    static if (_alloc_)
     {
         ///
-        private T* _value;
+        private T* _value_;
         ///
-        ref inout(T) value() inout @property
+        ref inout(T) _value() inout @property
         {
-            return *_value;
+            return *_value_;
+        }
+
+        ///
+        ref T _value(T value) @property
+        {
+            if (_value_ is null)
+            {
+                _value_ = new T;
+                import core.lifetime: move;
+                *_value_ = move(value);
+            }
+            return *_value_;
         }
     }
     else
     {
         ///
-        T value;
+        T _value;
     }
 
     ///
-    alias value this;
+    alias _value this;
 
     /++
     Params:
@@ -56,13 +83,16 @@ struct Annotated(T) {
         }
         import core.lifetime: forward;
         this.annotations = annotations;
-        static if (is(T == union) || is(T == struct))
-            this._value = new T(forward!args);
+        static if (_alloc_)
+            this._value_ = new T(forward!args);
         else
         static if (__traits(compiles, value = args))
-            this.value = args;
+            this._value = args;
+        else
+        static if (is(T == class))
+            this._value = new T(forward!args);
         else
-            this.value = new T(forward!args);
+            this._value = T(forward!args);
     }
 }
 
@@ -80,3 +110,19 @@ unittest
     assert(ac.annotations == annotations);
     assert(ac.x == 5);
 }
+
+///
+unittest
+{
+    import mir.algebraic;
+    auto annotations = ["annotation"];
+    static struct S {double x;}
+    auto as = Annotated!(Variant!S)(annotations, 5);
+    assert(as.annotations == annotations);
+    assert(as.x == 5);
+
+    static struct C {double x;}
+    auto ac = Annotated!(Variant!S)(annotations, 5);
+    assert(ac.annotations == annotations);
+    assert(ac.x == 5);
+}
diff --git a/source/mir/serde.d b/source/mir/serde.d
index eb372329..570dd385 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1867,7 +1867,10 @@ package template isAlgebraicAliasThis(T)
         import mir.algebraic: isVariant;
         T* aggregate;
         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
-        enum isAlgebraicAliasThis = isVariant!A;
+        static if (isVariant!A)
+            enum isAlgebraicAliasThis = true;
+        else
+            enum isAlgebraicAliasThis = serdeIsDynamicAlgebraic!A;
     }
     else
     {

From e58919e057c67e414debf423a1ea8456e18262ad Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 23:08:02 +0700
Subject: [PATCH 265/573] add Annotated.opEquals

---
 source/mir/annotated.d | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index a649e049..00a510f4 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -68,6 +68,12 @@ struct Annotated(T) {
     ///
     alias _value this;
 
+    ///
+    bool opEquals(const Annotated rhs) const
+    {
+        return annotations == rhs.annotations && _value == rhs._value;
+    }
+
     /++
     Params:
         annotations = non-empty array of annotations

From 9981ab5ce3f47d4e60ae55f898d303afb96ad64c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Nov 2021 23:12:21 +0700
Subject: [PATCH 266/573] fixup

---
 source/mir/annotated.d | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 00a510f4..87c2ce7d 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -58,6 +58,12 @@ struct Annotated(T) {
             }
             return *_value_;
         }
+
+        ///
+        bool opEquals(const Annotated rhs) const
+        {
+            return annotations == rhs.annotations && _value == rhs._value;
+        }
     }
     else
     {
@@ -68,12 +74,6 @@ struct Annotated(T) {
     ///
     alias _value this;
 
-    ///
-    bool opEquals(const Annotated rhs) const
-    {
-        return annotations == rhs.annotations && _value == rhs._value;
-    }
-
     /++
     Params:
         annotations = non-empty array of annotations

From ee3f80349b350fa62e220c0af33c128a822f77bc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 8 Nov 2021 21:12:58 +0700
Subject: [PATCH 267/573] fix type

---
 source/mir/annotated.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 87c2ce7d..29a30107 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -14,7 +14,7 @@ version (D_Exceptions)
     static immutable exc = new Exception(excMsg);
 
 /++
-A convenience difintion of an annotated value. 
+A convenience difinition of an annotated value. 
 +/
 @serdeRegister
 @serdeAnnotation

From e3d640af74b0e7495e60401d0fc06e4c0a3172bc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 9 Nov 2021 09:57:39 +0700
Subject: [PATCH 268/573] RM alias this  for Annotated

---
 source/mir/annotated.d | 40 +++++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 29a30107..df9ebbe0 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -40,44 +40,42 @@ struct Annotated(T) {
     static if (_alloc_)
     {
         ///
-        private T* _value_;
+        private T* _value;
         ///
-        ref inout(T) _value() inout @property
+        ref inout(T) value() inout @property
         {
-            return *_value_;
+            return *_value;
         }
 
         ///
-        ref T _value(T value) @property
+        ref T value(T value) @property
         {
-            if (_value_ is null)
+            if (_value is null)
             {
-                _value_ = new T;
+                _value = new T;
                 import core.lifetime: move;
-                *_value_ = move(value);
+                *_value = move(value);
             }
-            return *_value_;
+            return *_value;
         }
 
         ///
         bool opEquals(const Annotated rhs) const
         {
-            return annotations == rhs.annotations && _value == rhs._value;
+            return annotations == rhs.annotations && value == rhs.value;
         }
     }
     else
     {
         ///
-        T _value;
+        T value;
     }
 
-    ///
-    alias _value this;
 
     /++
     Params:
         annotations = non-empty array of annotations
-        value = actual value
+        args = arguments to construct value with
     +/
     this(Args...)(string[] annotations, Args args) @safe pure {
         if (annotations.length == 0)
@@ -90,15 +88,15 @@ struct Annotated(T) {
         import core.lifetime: forward;
         this.annotations = annotations;
         static if (_alloc_)
-            this._value_ = new T(forward!args);
+            this._value = new T(forward!args);
         else
         static if (__traits(compiles, value = args))
-            this._value = args;
+            this.value = args;
         else
         static if (is(T == class))
-            this._value = new T(forward!args);
+            this.value = new T(forward!args);
         else
-            this._value = T(forward!args);
+            this.value = T(forward!args);
     }
 }
 
@@ -109,12 +107,12 @@ unittest
     static struct S {double x;}
     auto as = Annotated!S(annotations, 5);
     assert(as.annotations == annotations);
-    assert(as.x == 5);
+    assert(as.value.x == 5);
 
     static struct C {double x;}
     auto ac = Annotated!S(annotations, 5);
     assert(ac.annotations == annotations);
-    assert(ac.x == 5);
+    assert(ac.value.x == 5);
 }
 
 ///
@@ -125,10 +123,10 @@ unittest
     static struct S {double x;}
     auto as = Annotated!(Variant!S)(annotations, 5);
     assert(as.annotations == annotations);
-    assert(as.x == 5);
+    assert(as.value.x == 5);
 
     static struct C {double x;}
     auto ac = Annotated!(Variant!S)(annotations, 5);
     assert(ac.annotations == annotations);
-    assert(ac.x == 5);
+    assert(ac.value.x == 5);
 }

From 473f20e0c53e688208a50d26ce330df246e47735 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 9 Nov 2021 11:33:48 +0700
Subject: [PATCH 269/573] extend timestamp example

---
 source/mir/timestamp.d | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 311e2433..0ebd9183 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -447,16 +447,17 @@ struct Timestamp
     }
 
     ///
+    version (mir_test)
     @safe unittest
     {
-        import core.time : hnsecs, minutes;
+        import core.time : hnsecs, minutes, Duration;
         import mir.algebraic;
         import mir.date: Date; // Can be other Date type as well
         import std.datetime.date : TimeOfDay, DateTime;
         import std.datetime.systime : SysTime;
         import std.datetime.timezone: UTC, SimpleTimeZone;
 
-        alias A = Variant!(Date, TimeOfDay, DateTime, SysTime, Timestamp, string); // non-date-time types is OK
+        alias A = Variant!(Date, TimeOfDay, DateTime, Duration, SysTime, Timestamp, string); // non-date-time types is OK
         assert(cast(A) Timestamp(1023) == Timestamp(1023)); // Year isn't represented in the algebraic, use fallback type
         assert(cast(A) Timestamp.onlyTime(7, 40, 30) == TimeOfDay(7, 40, 30));
         assert(cast(A) Timestamp(1982, 4, 1, 20, 59, 22) == DateTime(1982, 4, 1, 20, 59, 22));

From 8e8c8837134ffaf198fa8c76feb83bb62337fac0 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 9 Nov 2021 11:33:58 +0700
Subject: [PATCH 270/573] rework Annotated

---
 source/mir/annotated.d | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index df9ebbe0..6d582750 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -14,7 +14,9 @@ version (D_Exceptions)
     static immutable exc = new Exception(excMsg);
 
 /++
-A convenience difinition of an annotated value. 
+A convenience difinition of an annotated value.
+
+A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member.
 +/
 @serdeRegister
 @serdeAnnotation
@@ -26,7 +28,7 @@ struct Annotated(T) {
     static if (!(is(T == union) || is(T == struct)))
         private enum _alloc_ = false;
     else
-    static if (serdeIsDynamicAlgebraic!T)
+    static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic"))
         private enum _alloc_ = true;
     else
     {

From 53ed807ba141399fb029b0d71e994ac852055e68 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 9 Nov 2021 12:00:32 +0700
Subject: [PATCH 271/573] fix typo

---
 source/mir/annotated.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 6d582750..ff4e18bb 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -14,7 +14,7 @@ version (D_Exceptions)
     static immutable exc = new Exception(excMsg);
 
 /++
-A convenience difinition of an annotated value.
+A convenience definition of an annotated value.
 
 A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member.
 +/

From 13cd1823b6cfee947d58f202dbceb4e2bf773aec Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 9 Nov 2021 14:05:12 +0700
Subject: [PATCH 272/573] add serde.isAnnotated

---
 source/mir/serde.d | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 570dd385..0df2801e 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -145,12 +145,26 @@ enum serdeRegister;
 /++
 The attribute can be applied to a string-like member that should be de/serialized as an annotation / attribute.
 
-Also, the attribute can be applied to on a type used in an algebraic type to denote that the type should be used to deserialize annotated value.
+Also, the attribute can be applied on a type to denote that the type should be used to de/serialize annotated value.
 
 This feature is used in $(MIR_PACKAGE mir-ion).
 +/
 enum serdeAnnotation;
 
+/++
+Checks if the type marked with $(LREF serdeAnnotation).
++/
+template isAnnotated(T)
+{
+    import mir.serde: serdeAnnotation;
+    static if (is(T == enum) || isAggregateType!T) {
+        enum isAnnotated = hasUDA!(T, serdeAnnotation);
+        static if (isAnnotated)
+            static assert(__traits(getAliasThis, T).length == 1 || __traits(hasMember, T, "value"), "@serdeAnnotation " ~ T.stringof ~" requires alias this member or `value` member.");
+    }
+    else
+        enum isAnnotated = false;
+}
 
 private template serdeIsAnnotationMemberIn(T)
 {

From 65d9a02db3ddc76764bb4894ca4220121845e72b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 10 Nov 2021 13:43:55 +0700
Subject: [PATCH 273/573] add serdeRegister for timestamp

---
 source/mir/timestamp.d | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 0ebd9183..6e1e9078 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -4,7 +4,7 @@ Timestamp
 module mir.timestamp;
 
 private alias isDigit = (dchar c) => uint(c - '0') < 10;
-import mir.serde: serdeIgnore;
+import mir.serde: serdeIgnore, serdeRegister;
 
 version(D_Exceptions)
 ///
@@ -30,6 +30,7 @@ version(D_Exceptions)
     private static immutable InvalidISOString = new DateTimeException("Invalid ISO String");
     private static immutable InvalidISOExtendedString = new DateTimeException("Invalid ISO Extended String");
     private static immutable InvalidString = new DateTimeException("Invalid String");
+    private static immutable ExpectedDuration = new DateTimeException("Expected Duration");
 }
 
 /++
@@ -40,6 +41,7 @@ This means that transcoding requires a conversion between UTC and local time.
 
 `Timestamp` precision is up to picosecond (second/10^12).
 +/
+@serdeRegister
 struct Timestamp
 {
     import std.traits: isSomeChar;

From 88b905a5530ab741fea9abc47cf0c70734b5bada Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 11 Nov 2021 00:22:08 +0700
Subject: [PATCH 274/573] add fake Timestamps to encode Duration

---
 source/mir/timestamp.d | 128 +++++++++++++++++++++++++++++++++--------
 1 file changed, 105 insertions(+), 23 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 6e1e9078..406d5ca4 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -162,7 +162,7 @@ struct Timestamp
         /++
         Fraction
 
-        The `fraction_exponent` and `fraction_coefficient` denote the fractional seconds of the timestamp as a decimal value
+        The `fractionExponent` and `fractionCoefficient` denote the fractional seconds of the timestamp as a decimal value
         The fractional seconds’ value is `coefficient * 10 ^ exponent`.
         It must be greater than or equal to zero and less than 1.
         A missing coefficient defaults to zero.
@@ -382,6 +382,53 @@ struct Timestamp
         assert(st == cast(SysTime) ts);
     }
 
+    /++
+    Creates a fake timestamp from a Duration using `total!"hnsecs"` method.
+    For positive and zero timestamps the format is
+        `wwww-dd-88Thh:mm:ss.nnnnnnn`
+        and for negative timestamps
+        `wwww-dd-99Thh:mm:ss.nnnnnnn`.
+    +/
+    this(Duration)(const Duration duration)
+        if (Duration.stringof == "Duration")
+    {
+        auto hnsecs = duration.total!"hnsecs";
+        ulong abs = hnsecs < 0 ? -hnsecs : hnsecs;
+        precision = Precision.fraction;
+        day = hnsecs >= 0 ? 88 : 99;
+        fractionExponent = -7;
+        fractionCoefficient = abs % 10_000_000U;
+        abs /= 10_000_000U;
+        second = abs % 60;
+        abs /= 60;
+        minute = abs % 60;
+        abs /= 60;
+        hour = abs % 24;
+        abs /= 24;
+        month = abs % 7;
+        abs /= 7;
+        year = cast(typeof(year)) abs;
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest {
+        import core.time : Duration, weeks, days, hours, minutes, seconds, hnsecs;
+
+        auto duration = 5.weeks + 2.days + 7.hours + 40.minutes + 4.seconds + 9876543.hnsecs;
+        Timestamp ts = duration;
+
+        assert(ts.toISOExtString == `0005-02-88T07:40:04.9876543Z`);
+        assert(duration == cast(Duration) ts);
+
+        duration = -duration;
+        ts = Timestamp(duration);
+        assert(ts.toISOExtString == `0005-02-99T07:40:04.9876543Z`);
+        assert(duration == cast(Duration) ts);
+
+        assert(Timestamp(Duration.zero).toISOExtString == `0000-00-88T00:00:00.0000000Z`);
+    }
+
     /++
     Decomposes Timestamp to an algebraic type.
     Supported types up to T.stringof equivalence:
@@ -414,6 +461,11 @@ struct Timestamp
                 if (precision == precision.month)
                     return T(opCast!AT);
 
+        foreach (AT; T.AllowedTypes)
+            static if (AT.stringof == "Duration")
+                if (isDuration)
+                    return T(opCast!AT);
+
         foreach (AT; T.AllowedTypes)
             static if (AT.stringof == "YearMonthDay" || AT.stringof == "Date" ||  AT.stringof == "date")
                 if (precision == precision.day)
@@ -494,6 +546,7 @@ struct Timestamp
          || T.stringof == "Date"
          || T.stringof == "date"
          || T.stringof == "TimeOfDay"
+         || T.stringof == "Duration"
          || T.stringof == "DateTime"
          || T.stringof == "SysTime")
     {
@@ -523,29 +576,47 @@ struct Timestamp
             import std.datetime.date: DateTime;
             import std.datetime.systime: SysTime;
             import std.datetime.timezone: UTC, SimpleTimeZone;
-            auto ret = SysTime(DateTime(year, month, day, hour, minute, second), UTC());
-            if (fractionCoefficient)
-            {
-                long coeff = fractionCoefficient;
-                int exp = fractionExponent;
-                while (exp > -7)
-                {
-                    exp--;
-                    coeff *= 10;
-                }
-                while (exp < -7)
-                {
-                    exp++;
-                    coeff /= 10;
-                }
-                ret.fracSecs = coeff.hnsecs;
-            }
+            auto ret = SysTime(DateTime(year, month, day, hour, minute, second), getPhobosFraction.hnsecs, UTC());
             if (offset)
             {
                 ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes));
             }
             return ret;
         }
+        else
+        static if (T.stringof == "Duration")
+        {
+            if (!isDuration)
+                throw ExpectedDuration;
+            auto coeff = ((((long(year) * 7 + month) * 24 + hour) * 60 + minute) * 60 + second) * 10_000_000 + getPhobosFraction;
+            if (isNegativeDuration)
+                coeff = -coeff;
+
+            import mir.conv: to;
+            import core.time: hnsecs;
+            return coeff.hnsecs.to!T;
+        }
+    }
+
+    private long getPhobosFraction() @property const @safe pure nothrow @nogc
+    {
+        long coeff;
+        if (fractionCoefficient)
+        {
+            coeff = fractionCoefficient;
+            int exp = fractionExponent;
+            while (exp > -7)
+            {
+                exp--;
+                coeff *= 10;
+            }
+            while (exp < -7)
+            {
+                exp++;
+                coeff /= 10;
+            }
+        }
+        return coeff;
     }
 
     /++
@@ -617,7 +688,7 @@ struct Timestamp
     }
 
     /++
-    Converts this $(LREF Timestamp) to a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`.
+    Converts this $(LREF Timestamp) to a string with the format `yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm`.
 
     If `w` writer is set, the resulting string will be written directly
     to it.
@@ -637,7 +708,7 @@ struct Timestamp
         assert(Timestamp(0, 1, 5).toString == "0000-01-05");
         assert(Timestamp(-4, 1, 5).toString == "-0004-01-05");
 
-        // YYYY-MM-DDThh:mm:ss±hh:mm
+        // yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm
         assert(Timestamp(2021).toString == "2021T");
         assert(Timestamp(2021, 01).toString == "2021-01T", Timestamp(2021, 01).toString);
         assert(Timestamp(2021, 01, 29).toString == "2021-01-29");
@@ -763,7 +834,7 @@ struct Timestamp
             // if (isOutputRange!(W, char))
         {
             import mir.format: printZeroPad;
-            // YYYY-MM-DDThh:mm:ss±hh:mm
+            // yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm
             Timestamp t = this;
 
             if (t.offset)
@@ -956,10 +1027,9 @@ struct Timestamp
     }
 
     /++
-    Creates a $(LREF Timestamp) from a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`
+    Creates a $(LREF Timestamp) from a string with the format `yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm`
     or its leading part allowed by the standard.
 
-
     Params:
         str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates.
         value = (optional) result value.
@@ -1296,4 +1366,16 @@ struct Timestamp
             return true;
         }
     }
+
+    ///
+    bool isDuration() const @safe pure nothrow @nogc @property
+    {
+        return day == 88 || day == 99;
+    }
+
+    ///
+    bool isNegativeDuration() const @safe pure nothrow @nogc @property
+    {
+        return day == 99;
+    }
 }

From 1e3f35938a91ec047bff1400d1d57e74b5cf5487 Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Thu, 11 Nov 2021 02:39:08 -0500
Subject: [PATCH 275/573] Implement support for binary literals (#368)

* Implement support for binary literals

* Add basic test for BigInt fromBinaryString
---
 source/mir/bignum/fixed.d          |  27 +++
 source/mir/bignum/integer.d        |  38 ++++
 source/mir/bignum/low_level_view.d | 267 +++++++++++++++++++++++++++++
 3 files changed, 332 insertions(+)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index b016eebb..2561dc01 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -223,6 +223,33 @@ struct UInt(size_t size)
         return view.fromHexStringImpl!(C, allowUnderscores)(str);
     }
 
+    ///
+    static UInt!size fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str)
+    {
+        typeof(return) ret;
+        if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
+            return ret;
+        version(D_Exceptions)
+        {
+            import mir.bignum.low_level_view: binaryStringException;
+            throw binaryStringException;
+        }
+        else
+        {
+            import mir.bignum.low_level_view: binaryStringErrorMsg;
+            assert(0, binaryStringErrorMsg);
+        }
+    }
+
+    /++
+    +/
+    bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        return view.fromBinaryStringImpl!(C, allowUnderscores)(str);
+    }
+
     /++
     +/
     auto opEquals(size_t rhsSize)(auto ref const UInt!rhsSize rhs) const
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 06b62f13..f01d32a6 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -394,6 +394,44 @@ struct BigInt(size_t maxSize64)
         return ret;
     }
 
+    /++
+    +/
+    static BigInt fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str)
+        @trusted pure
+    {
+        BigInt ret;
+        if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
+            return ret;
+        version(D_Exceptions)
+            throw binaryStringException;
+        else
+            assert(0, binaryStringErrorMsg);
+    }
+
+    static if (maxSize64 == 3)
+    ///
+    version(mir_bignum_test) @safe pure @nogc unittest
+    {
+        BigInt!4 integer = "-34010447314490204552169750449563978034784726557588085989975288830070948234680"; // constructor
+        assert(integer == BigInt!4.fromBinaryString("-100101100110001001110110010001110101010010101100000111000011011000010011000010111111000100111001011111001101101111101010100011000001000011000001110001110011010011001001011101010010010101101001010101111011101001111101110011101111110010011100000010110111000"));
+    }
+
+    /++
+    +/
+    bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        auto work = BigIntView!size_t(data);
+        auto ret = work.fromBinaryStringImpl!(C, allowUnderscores)(str);
+        if (ret)
+        {
+            length = cast(uint)work.unsigned.coefficients.length;
+            sign = work.sign;
+        }
+        return ret;
+    }
+
     ///
     bool mulPow5(size_t degree)
     {
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 514c25c8..c2ab6a7b 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -19,9 +19,11 @@ private alias cop(string op : "+") = addu;
 private enum inverseSign(string op) = op == "+" ? "-" : "+";
 
 package immutable hexStringErrorMsg = "Incorrect hex string for BigUIntView.fromHexString";
+package immutable binaryStringErrorMsg = "Incorrect binary string for BigUIntView.fromBinaryString";
 version (D_Exceptions)
 {
     package immutable hexStringException = new Exception(hexStringErrorMsg);
+    package immutable binaryStringException = new Exception(binaryStringErrorMsg);
 }
 
 /++
@@ -721,6 +723,144 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         expectThrow("__abcd__efab_cdef__");
         expectThrow("__abcd__efab__cdef__");
     }
+ 
+    /++
+    +/
+    static BigUIntView fromBinaryString(C, bool allowUnderscores = false)(scope const(C)[] str)
+        @trusted pure
+        if (isSomeChar!C)
+    {
+        auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
+        auto data = new Unqual!W[length];
+        auto view = BigUIntView!(Unqual!W, endian)(data);
+        if (view.fromBinaryStringImpl!(C, allowUnderscores)(str))
+            return BigUIntView(cast(W[])view.coefficients);
+        version(D_Exceptions)
+            throw binaryStringException;
+        else
+            assert(0, binaryStringErrorMsg);
+    }
+
+    static if (isMutable!W)
+    /++
+    +/
+    bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
+        @safe pure @nogc nothrow scope
+        if (isSomeChar!C)
+    {
+        pragma(inline, false);
+        import mir.utility: _expect;
+        static if (allowUnderscores) {
+            if (_expect(str.length == 0, false)) // can't tell how big the coeff array needs to be, rely on a runtime check
+                return false;
+        } else {
+            if (_expect(str.length == 0 || str.length > coefficients.length * W.sizeof * 8, false))
+                return false;
+        }
+
+        leastSignificant = 0;
+        auto work = topLeastSignificantPart(1);
+        W current;
+        size_t i, j;
+        static if (allowUnderscores) bool recentUnderscore;
+
+        do
+        {
+            ubyte c;
+            switch(str[$ - ++i])
+            {
+                case '0': c = 0x0; break;
+                case '1': c = 0x1; break;
+                static if (allowUnderscores) 
+                {
+                    case '_': 
+                        if (recentUnderscore) return false;
+                        recentUnderscore = true;
+                        continue;
+                }
+                default: return false;
+            }
+            ++j;
+            static if (allowUnderscores) recentUnderscore = false;
+            // how far do we need to shift to get to the top bit?
+            enum s = W.sizeof * 8 - 1;
+            // shift number to the top most bit
+            W cc = cast(W)(W(c) << s);
+            // shift unsigned right 1 bit
+            current >>>= 1;
+            // add number to top most bit of current var
+            current |= cc;
+            if (j % (W.sizeof * 8) == 0) // is this packed var full? 
+            {
+                work.mostSignificant = current;
+                current = 0;
+                if (_expect(work.coefficients.length < coefficients.length, true))
+                {
+                    work = topLeastSignificantPart(work.coefficients.length + 1);
+                }
+                else if (i < str.length) // if we've run out of coefficients before reaching the end of the string, error
+                {
+                    return false;
+                }
+            }
+        }
+        while(i < str.length);
+
+        static if (allowUnderscores) 
+        {
+            // check for a underscore at the beginning or the end
+            if (recentUnderscore || str[$ - 1] == '_') return false;
+        }
+
+        if (current)
+        {
+            current >>>= (W.sizeof * 8 - j % (W.sizeof * 8));
+            work.mostSignificant = current;
+        }
+
+        coefficients = coefficients[0 .. (j / (W.sizeof * 8) + (j % (W.sizeof * 8) != 0))];
+
+        return true;
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigUIntView!size_t.fromBinaryString!(char, true)("1111_0000_0101");
+        assert(cast(ulong)view == 0b1111_0000_0101);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        // Check that invalid underscores in hex literals throw an error.
+        void expectThrow(const(char)[] input) {
+            bool caught = false;
+            try { 
+                auto view = BigUIntView!size_t.fromBinaryString!(char, true)(input);
+            } catch (Exception e) {
+                caught = true;
+            }
+
+            assert(caught);
+        }
+
+        expectThrow("abcd");
+        expectThrow("0101__1011__0111");
+        expectThrow("_0101_1011_0111");
+        expectThrow("_0101_1011_0111_");
+        expectThrow("_0101_1011_0111__");
+        expectThrow("__0101_1011_0111_");
+        expectThrow("__0101_1011_0111__");
+        expectThrow("__0101__1011_0111__");
+        expectThrow("__1011__0111__1011__");
+    }
 
     static if (isMutable!W && W.sizeof >= 4)
     /++
@@ -1502,6 +1642,53 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         return unsigned.fromHexStringImpl!(C, allowUnderscores)(str);
     }
 
+    /++
+    +/
+    static BigIntView fromBinaryString(C, bool allowUnderscores = false)(scope const(C)[] str)
+        @trusted pure
+        if (isSomeChar!C)
+    {
+        auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
+        auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]);
+        if (ret.fromBinaryStringImpl!(C, allowUnderscores)(str))
+            return cast(BigIntView) ret;
+        version(D_Exceptions)
+            throw binaryStringException;
+        else
+            assert(0, binaryStringErrorMsg);
+    }
+
+    static if (isMutable!W)
+    /++
+    +/
+    bool fromBinaryStringImpl(C, bool allowUnderscores = false)(scope const(C)[] str)
+        @safe pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        pragma(inline, false);
+        import mir.utility: _expect;
+
+        assert(unsigned.coefficients.length);
+
+        if (_expect(str.length == 0, false))
+            return false;
+
+        sign = false;
+
+        if (str[0] == '-')
+        {
+            sign = true;
+            str = str[1 .. $];
+        }
+        else
+        if (_expect(str[0] == '+', false))
+        {
+            str = str[1 .. $];
+        }
+
+        return unsigned.fromBinaryStringImpl!(C, allowUnderscores)(str);
+    }
+
     ///
     T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
         if (isFloatingPoint!T && isMutable!T)
@@ -1522,6 +1709,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto a = cast(double) BigIntView!size_t.fromBinaryString("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001010011101");
+        assert(a == -0xa.fbbfae3cd0bp+124);
+    }
+
     static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
     ///
     version(mir_bignum_test)
@@ -1532,6 +1729,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    ///
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto a = cast(double) BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_1001_1101");
+        assert(a == -0xa.fbbfae3cd0bp+124);
+    }
+
     ///
     T opCast(T, bool nonZero = false)() scope const
         if (is(T == long) || is(T == int))
@@ -1604,6 +1811,66 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!size_t.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ushort.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ushort.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ubyte.fromBinaryString!(char, true)("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001000011101");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
+    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    version(mir_bignum_test)
+    @safe pure
+    unittest
+    {
+        auto view = BigIntView!ubyte.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_0001_1101");
+        assert(cast(long) view == -0x14a1de7022b0021d);
+        assert(cast(int) view == -0x22b0021d);
+    }
+
     /++
     +/
     T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const

From 9df51fba1a485425bcc9d3c1cd7e6ddc65f7d71c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 12 Nov 2021 15:57:34 +0700
Subject: [PATCH 276/573] fix serdeGetAnnotationMembersOut

---
 source/mir/serde.d | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 0df2801e..7bcf45ac 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -221,7 +221,7 @@ template serdeGetAnnotationMembersOut(T)
 {
     import std.meta: aliasSeqOf, Filter;
     static if (isAggregateType!T)
-        enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(DeserializableMembers!T))];
+        enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(SerializableMembers!T))];
     else
         enum string[] serdeGetAnnotationMembersOut = null;
 }
@@ -241,10 +241,14 @@ version(mir_test) unittest
         string c;
         @serdeAnnotation @serdeIgnore
         string d;
+        @serdeAnnotation enum string e = "e";
+        static @serdeAnnotation string f() @safe pure nothrow @nogc @property {
+            return "f";
+        }
     }
 
     static assert(serdeGetAnnotationMembersOut!int == []);
-    static assert(serdeGetAnnotationMembersOut!S == ["a", "b"]);
+    static assert(serdeGetAnnotationMembersOut!S == ["a", "b", "f"]);
 }
 
 /++

From 08600232668e1fe3a166540ec9b689f4867de420 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sat, 13 Nov 2021 16:05:25 +0700
Subject: [PATCH 277/573] Fix sorting

---
 source/mir/ndslice/sorting.d | 45 ++++++++++++++++++++++++++++++++----
 1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 8c3a937a..da0103bf 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -293,6 +293,9 @@ void quickSortImpl(alias less, Iterator)(Slice!Iterator slice) @trusted
         auto r = l;
         r += slice.length;
 
+        if(slice.length <= 1)
+            return;
+
         static if (naive > 1)
         {
             if (slice.length <= naive || __ctfe)
@@ -341,11 +344,6 @@ void quickSortImpl(alias less, Iterator)(Slice!Iterator slice) @trusted
                 return;
             }
         }
-        else
-        {
-            if(slice.length <= 1)
-                return;
-        }
 
         // partition
         auto lessI = l;
@@ -1822,3 +1820,40 @@ unittest
     auto hiI = frontI + 6;
     assert(expandPartition!((a, b) => a < b)(frontI, lastI, loI, pivotI, hiI) == (frontI + 9));
 }
+
+version(mir_test)
+unittest
+{
+    import std.random;
+    import mir.ndslice.sorting: sort;
+
+    static struct StructA
+    {
+        double val0;
+        double val1;
+        double val2;
+    }
+
+    static struct StructB
+    {
+        ulong   productId; 
+        StructA strA;       
+    }
+
+    auto createStructBArray(uint nbTrades)
+    {
+        auto rnd = Random(42);
+
+        auto p = StructA(0,0,0);
+
+        StructB[] ret;    
+        foreach(i;0..nbTrades)
+        {
+            ret ~= StructB(uniform(0, nbTrades, rnd), p);
+        }
+
+        return ret;
+    }
+
+    auto arrayB = createStructBArray(10000).sort!((a,b) => a.productId<b.productId);
+}

From 70b80dd64c2ea6130b3d0ebc3f9dae59a1df4e57 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 11:02:48 +0700
Subject: [PATCH 278/573] Rework series API a bit

---
 source/mir/series.d | 171 ++++++++++++++++++++------------------------
 1 file changed, 77 insertions(+), 94 deletions(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index b37b53f9..b6765871 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -15,12 +15,13 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 +/
 module mir.series;
 
-public import mir.ndslice.slice;
-public import mir.ndslice.sorting: sort;
 import mir.ndslice.iterator: IotaIterator;
 import mir.ndslice.sorting: transitionIndex;
 import mir.qualifier;
+import mir.serde: serdeIgnore;
 import std.traits;
+public import mir.ndslice.slice;
+public import mir.ndslice.sorting: sort;
 
 /++
 See_also: $(LREF unionSeries), $(LREF troykaSeries), $(LREF troykaGalop).
@@ -204,13 +205,13 @@ struct mir_observation(Index, Data)
     /// Date, date-time, time, or index.
     Index index;
     /// An alias for time-series index.
-    alias time = index;
+    deprecated ("use `index` instead") alias time = index;
     /// An alias for key-value representation.
-    alias key = index;
+    deprecated ("use `index` instead") alias key = index;
     /// Value or ndslice.
     Data data;
     /// An alias for key-value representation.
-    alias value = data;
+    deprecated ("use `data` instead") alias value = data;
 }
 
 /// ditto
@@ -277,8 +278,11 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     ///
     enum SliceKind kind = kind_;
 
-    ///
-    Slice!(Iterator, N, kind) _data;
+    /++
+    Data is any ndslice with only one constraints,
+    `data` and `index` lengths should be equal.
+    +/
+    Slice!(Iterator, N, kind) data;
 
     ///
     IndexIterator _index;
@@ -286,20 +290,20 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     /// Index / Key / Time type aliases
     alias Index = typeof(typeof(this).init.index.front);
     /// ditto
-    alias Key = Index;
+    deprecated ("use `Index` instead") alias Key = Index;
     /// ditto
-    alias Time = Index;
+    deprecated ("use `Index` instead") alias Time = Index;
     /// Data / Value type aliases
     alias Data = typeof(typeof(this).init.data.front);
     /// ditto
-    alias Value = Data;
+    deprecated ("use `Data` instead") alias Value = Data;
 
     /// An alias for time-series index.
-    alias time = index;
+    deprecated ("use `index` instead") alias time = index;
     /// An alias for key-value representation.
-    alias key = index;
+    deprecated ("use `index` instead") alias key = index;
     /// An alias for key-value representation.
-    alias value = data;
+    deprecated ("use `data` instead") alias value = data;
 
     private enum defaultMsg() = "Series " ~ Unqual!(this.Data).stringof ~ "[" ~ Unqual!(this.Index).stringof ~ "]: Missing";
     private static immutable defaultExc() = new Exception(defaultMsg!() ~ " required key");
@@ -310,7 +314,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     this()(Slice!IndexIterator index, Slice!(Iterator, N, kind) data)
     {
         assert(index.length == data.length, "Series constructor: index and data lengths must be equal.");
-        _data = data;
+        this.data = data;
         _index = index._iterator;
     }
 
@@ -318,14 +322,14 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     /// Construct from null
     this(typeof(null))
     {
-        _data = _data.init;
+        this.data = this.data.init;
         _index = _index.init;
     }
 
     ///
     bool opEquals(RIndexIterator, RIterator, size_t RN, SliceKind rkind, )(Series!(RIndexIterator, RIterator, RN, rkind) rhs) const
     {
-        return this.lightScopeIndex == rhs.lightScopeIndex && this._data.lightScope == rhs._data.lightScope;
+        return this.lightScopeIndex == rhs.lightScopeIndex && this.data.lightScope == rhs.data.lightScope;
     }
 
     /++
@@ -336,55 +340,34 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     +/
     auto index()() @property @trusted
     {
-        return _index.sliced(_data._lengths[0]);
+        return _index.sliced(this.data._lengths[0]);
     }
 
     /// ditto
     auto index()() @property @trusted const
     {
-        return _index.lightConst.sliced(_data._lengths[0]);
+        return _index.lightConst.sliced(this.data._lengths[0]);
     }
 
     /// ditto
     auto index()() @property @trusted immutable
     {
-        return _index.lightImmutable.sliced(_data._lengths[0]);
+        return _index.lightImmutable.sliced(this.data._lengths[0]);
     }
 
     private auto lightScopeIndex()() @property @trusted
     {
-        return .lightScope(_index).sliced(_data._lengths[0]);
+        return .lightScope(_index).sliced(this.data._lengths[0]);
     }
 
     private auto lightScopeIndex()() @property @trusted const
     {
-        return .lightScope(_index).sliced(_data._lengths[0]);
+        return .lightScope(_index).sliced(this.data._lengths[0]);
     }
 
     private auto lightScopeIndex()() @property @trusted immutable
     {
-        return .lightScope(_index).sliced(_data._lengths[0]);
-    }
-
-    /++
-    Data is any ndslice with only one constraints,
-    `data` and `index` lengths should be equal.
-    +/
-    auto data()() @property @trusted
-    {
-        return _data;
-    }
-
-    /// ditto
-    auto data()() @property @trusted const
-    {
-        return _data[];
-    }
-
-    /// ditto
-    auto data()() @property @trusted immutable
-    {
-        return _data[];
+        return .lightScope(_index).sliced(this.data._lengths[0]);
     }
 
     ///
@@ -606,7 +589,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         if (!is(Value : const(Exception)))
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        return idx < _data._lengths[0] && _index[idx] == key ? _data[idx] : _default;
+        return idx < this.data._lengths[0] && _index[idx] == key ? this.data[idx] : _default;
     }
 
     /// ditto
@@ -627,7 +610,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         if (!is(Value : const(Exception)))
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        return idx < _data._lengths[0] && _index[idx] == key ? _data[idx] : _default;
+        return idx < this.data._lengths[0] && _index[idx] == key ? this.data[idx] : _default;
     }
 
     /// ditto
@@ -659,9 +642,9 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     auto ref get(Index)(auto ref scope const Index key) @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        if (idx < _data._lengths[0] && _index[idx] == key)
+        if (idx < this.data._lengths[0] && _index[idx] == key)
         {
-            return _data[idx];
+            return this.data[idx];
         }
         throw defaultExc!();
     }
@@ -670,9 +653,9 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     auto ref get(Index)(auto ref scope const Index key, lazy const Exception exc) @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        if (idx < _data._lengths[0] && _index[idx] == key)
+        if (idx < this.data._lengths[0] && _index[idx] == key)
         {
-            return _data[idx];
+            return this.data[idx];
         }
         throw exc;
     }
@@ -760,18 +743,18 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     bool contains(Index)(auto ref scope const Index key) const @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        return idx < _data._lengths[0] && _index[idx] == key;
+        return idx < this.data._lengths[0] && _index[idx] == key;
     }
 
     ///
     auto opBinaryRight(string op : "in", Index)(auto ref scope const Index key) @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        bool cond = idx < _data._lengths[0] && _index[idx] == key;
-        static if (__traits(compiles, &_data[size_t.init]))
+        bool cond = idx < this.data._lengths[0] && _index[idx] == key;
+        static if (__traits(compiles, &this.data[size_t.init]))
         {
             if (cond)
-                return &_data[idx];
+                return &this.data[idx];
             return null;
         }
         else
@@ -802,9 +785,9 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     bool tryGet(Index, Value)(Index key, scope ref Value val) @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        auto cond = idx < _data._lengths[0] && _index[idx] == key;
+        auto cond = idx < this.data._lengths[0] && _index[idx] == key;
         if (cond)
-            val = _data[idx];
+            val = this.data[idx];
         return cond;
     }
 
@@ -828,9 +811,9 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     bool tryGetNext(Index, Value)(auto ref scope const Index key, scope ref Value val)
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        auto cond = idx < _data._lengths[0];
+        auto cond = idx < this.data._lengths[0];
         if (cond)
-            val = _data[idx];
+            val = this.data[idx];
         return cond;
     }
 
@@ -855,11 +838,11 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     bool tryGetNextUpdateKey(Index, Value)(scope ref Index key, scope ref Value val) @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(key);
-        auto cond = idx < _data._lengths[0];
+        auto cond = idx < this.data._lengths[0];
         if (cond)
         {
             key = _index[idx];
-            val = _data[idx];
+            val = this.data[idx];
         }
         return cond;
     }
@@ -886,7 +869,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         size_t idx = lightScopeIndex.transitionIndex!"a <= b"(key) - 1;
         auto cond = 0 <= sizediff_t(idx);
         if (cond)
-            val = _data[idx];
+            val = this.data[idx];
         return cond;
     }
 
@@ -915,7 +898,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         if (cond)
         {
             key = _index[idx];
-            val = _data[idx];
+            val = this.data[idx];
         }
         return cond;
     }
@@ -940,9 +923,9 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     bool tryGetFirst(Index, Value)(auto ref scope const Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(lowerBound);
-        auto cond = idx < _data._lengths[0] && _index[idx] <= upperBound;
+        auto cond = idx < this.data._lengths[0] && _index[idx] <= upperBound;
         if (cond)
-            val = _data[idx];
+            val = this.data[idx];
         return cond;
     }
 
@@ -967,11 +950,11 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     bool tryGetFirstUpdateLower(Index, Value)(ref Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) @trusted
     {
         size_t idx = lightScopeIndex.transitionIndex(lowerBound);
-        auto cond = idx < _data._lengths[0] && _index[idx] <= upperBound;
+        auto cond = idx < this.data._lengths[0] && _index[idx] <= upperBound;
         if (cond)
         {
             lowerBound = _index[idx];
-            val = _data[idx];
+            val = this.data[idx];
         }
         return cond;
     }
@@ -998,7 +981,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         size_t idx = lightScopeIndex.transitionIndex!"a <= b"(upperBound) - 1;
         auto cond = 0 <= sizediff_t(idx) && _index[idx] >= lowerBound;
         if (cond)
-            val = _data[idx];
+            val = this.data[idx];
         return cond;
     }
 
@@ -1027,7 +1010,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         if (cond)
         {
             upperBound = _index[idx];
-            val = _data[idx];
+            val = this.data[idx];
         }
         return cond;
     }
@@ -1082,7 +1065,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     size_t length(size_t dimension = 0)() const @property
         if (dimension < N)
     {
-        return _data.length!dimension;
+        return this.data.length!dimension;
     }
 
     /// ditto
@@ -1107,11 +1090,11 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         assert(!empty!dimension);
         static if (dimension)
         {
-            return index.series(_data.back!dimension);
+            return index.series(this.data.back!dimension);
         }
         else
         {
-            return index.back.observation(_data.back);
+            return index.back.observation(this.data.back);
         }
     }
 
@@ -1122,7 +1105,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         assert(!empty!dimension);
         static if (dimension == 0)
             _index++;
-        _data.popFront!dimension;
+        this.data.popFront!dimension;
     }
 
     /// ditto
@@ -1130,7 +1113,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         if (dimension < N)
     {
         assert(!empty!dimension);
-        _data.popBack!dimension;
+        this.data.popBack!dimension;
     }
 
     /// ditto
@@ -1140,7 +1123,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         assert(length!dimension >= n);
         static if (dimension == 0)
             _index += n;
-        _data.popFrontExactly!dimension(n);
+        this.data.popFrontExactly!dimension(n);
     }
 
     /// ditto
@@ -1148,7 +1131,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         if (dimension < N)
     {
         assert(length!dimension >= n);
-        _data.popBackExactly!dimension(n);
+        this.data.popBackExactly!dimension(n);
     }
 
     /// ditto
@@ -1178,7 +1161,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
             "Series.opSlice!" ~ dimension.stringof ~ ": the left opSlice boundary must be less than or equal to the right bound.");
         enum errorMsg = ": difference between the right and the left bounds"
                         ~ " must be less than or equal to the length of the given dimension.";
-        assert(j - i <= _data._lengths[dimension],
+        assert(j - i <= this.data._lengths[dimension],
               "Series.opSlice!" ~ dimension.stringof ~ errorMsg);
     }
     do
@@ -1189,7 +1172,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     /// ditto
     size_t opDollar(size_t dimension = 0)() const
     {
-        return _data.opDollar!dimension;
+        return this.data.opDollar!dimension;
     }
 
     /// ditto
@@ -1229,8 +1212,8 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     ref opAssign(typeof(this) rvalue) return @trusted
     {
         import mir.utility: swap;
-        this._data._structure = rvalue._data._structure;
-        swap(this._data._iterator, rvalue._data._iterator);
+        this.data._structure = rvalue.data._structure;
+        swap(this.data._iterator, rvalue.data._iterator);
         swap(this._index, rvalue._index);
         return this;
     }
@@ -1240,8 +1223,8 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         if (isAssignable!(IndexIterator, RIndexIterator) && isAssignable!(Iterator, RIterator))
     {
         import core.lifetime: move;
-        this._data._structure = rvalue._data._structure;
-        this._data._iterator = rvalue._data._iterator.move;
+        this.data._structure = rvalue.data._structure;
+        this.data._iterator = rvalue.data._iterator.move;
         this._index = rvalue._index.move;
         return this;
     }
@@ -1275,31 +1258,31 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     ///
     Series!(LightScopeOf!IndexIterator, LightScopeOf!Iterator, N, kind) lightScope()() @trusted @property
     {
-        return typeof(return)(lightScopeIndex, _data.lightScope);
+        return typeof(return)(lightScopeIndex, this.data.lightScope);
     }
 
     /// ditto
     Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() @trusted const @property
     {
-        return typeof(return)(lightScopeIndex, _data.lightScope);
+        return typeof(return)(lightScopeIndex, this.data.lightScope);
     }
 
     /// ditto
     Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() @trusted immutable @property
     {
-        return typeof(return)(lightScopeIndex, _data.lightScope);
+        return typeof(return)(lightScopeIndex, this.data.lightScope);
     }
 
     ///
     Series!(LightConstOf!IndexIterator, LightConstOf!Iterator, N, kind) lightConst()() const @property @trusted
     {
-        return index.series(data);
+        return index[].series(data[]);
     }
 
     ///
     Series!(LightImmutableOf!IndexIterator, LightImmutableOf!Iterator, N, kind) lightImmutable()() immutable @property @trusted
     {
-        return index.series(data);
+        return index[].series(data[]);
     }
 
     ///
@@ -1562,7 +1545,7 @@ Series!(K*, V*) series(RK, RV, K = RK, V = RV)(RV[RK] aa)
     {
         import mir.conv: emplaceRef;
         emplaceRef!K(it.index.front, kv.key.to!K);
-        emplaceRef!V(it._data.front, kv.value.to!V);
+        emplaceRef!V(it.data.front, kv.value.to!V);
         it.popFront;
     }
     .sort(ret);
@@ -1639,7 +1622,7 @@ auto rcseries(RK, RV, K = RK, V = RV)(RV[RK] aa)
     {
         import mir.conv: emplaceRef;
         emplaceRef!K(it.lightScopeIndex.front, kv.key.to!K);
-        emplaceRef!V(it._data.front, kv.value.to!V);
+        emplaceRef!V(it.data.front, kv.value.to!V);
         it.popFront;
     }
     import core.lifetime: move;
@@ -1828,7 +1811,7 @@ Returns:
 size_t findIndex(IndexIterator, Iterator, size_t N, SliceKind kind, Index)(Series!(IndexIterator, Iterator, N, kind) series, auto ref scope const Index key)
 {
     auto idx = series.lightScopeIndex.transitionIndex(key);
-    if (idx < series._data._lengths[0] && series.index[idx] == key)
+    if (idx < series.data._lengths[0] && series.index[idx] == key)
     {
         return idx;
     }
@@ -1858,7 +1841,7 @@ Returns:
 size_t find(IndexIterator, Iterator, size_t N, SliceKind kind, Index)(Series!(IndexIterator, Iterator, N, kind) series, auto ref scope const Index key)
 {
     auto idx = series.lightScopeIndex.transitionIndex(key);
-    auto bidx = series._data._lengths[0] - idx;
+    auto bidx = series.data._lengths[0] - idx;
     if (bidx && series.index[idx] == key)
     {
         return bidx;
@@ -2426,7 +2409,7 @@ auto unionSeriesImpl(I, E,
 
     enum N = N;
     alias I = DeepElementType!(typeof(seriesTuple[0].index));
-    alias E = DeepElementType!(typeof(seriesTuple[0]._data));
+    alias E = DeepElementType!(typeof(seriesTuple[0].data));
 
     if(uninitSeries.length)
     {
@@ -2436,9 +2419,9 @@ auto unionSeriesImpl(I, E,
             auto obs = u.front;
             emplaceRef!I(uninitSeries.index.front, obs.index);
             static if (N == 1)
-                emplaceRef!E(uninitSeries._data.front, obs.data);
+                emplaceRef!E(uninitSeries.data.front, obs.data);
             else
-                each!(emplaceRef!E)(uninitSeries._data.front, obs.data);
+                each!(emplaceRef!E)(uninitSeries.data.front, obs.data);
             u.popFront;
             uninitSeries.popFront;
         }
@@ -2473,12 +2456,12 @@ private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N,
 
     static if (N > 1)
     {
-        auto shape = seriesTuple[0]._data._lengths;
+        auto shape = seriesTuple[0].data._lengths;
         shape[0] = len;
 
         foreach (ref sl; seriesTuple[1 .. $])
             foreach (i; Iota!(1, N))
-                if (seriesTuple._data[0]._lengths[i] != sl._data._lengths[i])
+                if (seriesTuple.data[0]._lengths[i] != sl.data._lengths[i])
                     assert(0, "shapes mismatch");
     }
     else

From f517d96fe5c874e4efd3e8f7f8746f1d64f9fa6c Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 12:06:53 +0700
Subject: [PATCH 279/573] get rid of builtin complex numbers

---
 dub.sdl                       |   2 +-
 source/mir/math/stat.d        | 119 ++++++++++--------------
 source/mir/math/sum.d         | 168 ++++++++--------------------------
 source/mir/ndslice/topology.d |   6 +-
 4 files changed, 90 insertions(+), 205 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 1dae75a8..8678cb23 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.75"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_builtincomplex_test" "mir_test"
+    versions "mir_bignum_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 69e37337..4b1ac73c 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -41,14 +41,14 @@ template statType(T, bool checkComplex = true)
     } else static if (checkComplex) {
         import mir.internal.utility: isComplex;
         static if (isComplex!T) {
-            import std.traits: Unqual;
-            static if (is(T : cdouble)) {
-                deprecated("Built-in complex types deprecated in D language version 2.097") alias statType = Unqual!T;
-            } else {
+            static if (__traits(getAliasThis, T).length == 1)
+            {
+                alias statType = .statType!(typeof(__traits(getMember, T, __traits(getAliasThis, T)[0]))); 
+            }
+            else
+            {
                 alias statType = Unqual!T;
             }
-        } else static if (is(T : cdouble)) {
-                deprecated("Built-in complex types deprecated in D language version 2.097") alias statType = cdouble;
         } else {
             static assert(0, "statType: type " ~ T.stringof ~ " must be convertible to a complex floating point type");
         }
@@ -73,15 +73,6 @@ unittest
     static assert(is(statType!(immutable(double)) == double));
 }
 
-version(mir_builtincomplex_test)
-@safe pure nothrow @nogc
-unittest
-{
-    static assert(is(statType!cfloat == cfloat));
-    static assert(is(statType!cdouble == cdouble));
-    static assert(is(statType!creal == creal));
-}
-
 version(mir_test)
 @safe pure nothrow @nogc
 unittest
@@ -105,16 +96,17 @@ unittest
     static assert(is(statType!Foo == double)); // note: this is not float
 }
 
-version(mir_builtincomplex_test)
+version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.complex;
     static struct Foo {
-        cfloat x;
+        Complex!float x;
         alias x this;
     }
 
-    static assert(is(statType!Foo == cdouble)); // note: this is not Complex!float
+    static assert(is(statType!Foo == Complex!float));
 }
 
 version(mir_test)
@@ -129,16 +121,17 @@ unittest
     static assert(is(statType!Foo == double));
 }
 
-version(mir_builtincomplex_test)
+version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.complex;
     static struct Foo {
-        cdouble x;
+        Complex!double x;
         alias x this;
     }
 
-    static assert(is(statType!Foo == cdouble));
+    static assert(is(statType!Foo == Complex!double));
 }
 
 version(mir_test)
@@ -153,16 +146,17 @@ unittest
     static assert(is(statType!Foo == double)); // note: this is not real
 }
 
-version(mir_builtincomplex_test)
+version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.complex;
     static struct Foo {
-        creal x;
+        Complex!real x;
         alias x this;
     }
 
-    static assert(is(statType!Foo == cdouble)); // note: this is not Complex!real
+    static assert(is(statType!Foo == Complex!real));
 }
 
 version(mir_test)
@@ -206,11 +200,12 @@ unittest
     static assert(is(meanType!(float[]) == float));
 }
 
-version(mir_builtincomplex_test)
+version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
-    static assert(is(meanType!(cfloat[]) == cfloat));
+    import mir.complex;
+    static assert(is(meanType!(Complex!float[]) == Complex!float));
 }
 
 version(mir_test)
@@ -225,16 +220,17 @@ unittest
     static assert(is(meanType!(Foo[]) == float));
 }
 
-version(mir_builtincomplex_test)
+version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.complex;
     static struct Foo {
-        cfloat x;
+        Complex!float x;
         alias x this;
     }
 
-    static assert(is(meanType!(Foo[]) == cfloat));
+    static assert(is(meanType!(Foo[]) == Complex!float));
 }
 
 /++
@@ -423,9 +419,11 @@ version(mir_test)
 unittest
 {
     import mir.ndslice.slice: sliced;
+    import mir.complex;
+    alias C = Complex!double;
 
     assert(mean([1.0, 2, 3]) == 2);
-    assert(mean([1.0 + 3i, 2, 3]) == 2 + 1i);
+    assert(mean([C(1, 3), C(2), C(3)]) == C(2, 1));
     
     assert(mean!float([0, 1, 2, 3, 4, 5].sliced(3, 2)) == 2.5);
     
@@ -539,9 +537,11 @@ unittest
 {
     import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
+    import mir.complex;
+    alias C = Complex!double;
 
-    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
-    assert(x.mean.approxEqual(2.5 + 3.5i));
+    auto x = [C(1.0, 2), C(2, 3), C(3, 4), C(4, 5)].sliced;
+    assert(x.mean.approxEqual(C(2.5, 3.5)));
 }
 
 /// Compute mean tensors along specified dimention of tensors
@@ -634,28 +634,30 @@ version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.complex;
     static assert(is(hmeanType!(int[]) == double));
     static assert(is(hmeanType!(double[]) == double));
     static assert(is(hmeanType!(float[]) == float)); 
-    static assert(is(hmeanType!(cfloat[]) == cfloat));    
+    static assert(is(hmeanType!(Complex!float[]) == Complex!float));    
 }
 
 version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.complex;
     static struct Foo {
         float x;
         alias x this;
     }
     
     static struct Bar {
-        cfloat x;
+        Complex!float x;
         alias x this;
     }
 
     static assert(is(hmeanType!(Foo[]) == float));
-    static assert(is(hmeanType!(Bar[]) == cfloat));
+    static assert(is(hmeanType!(Bar[]) == Complex!float));
 }
 
 /++
@@ -864,9 +866,11 @@ unittest
 {
     import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
+    import mir.complex;
+    alias C = Complex!double;
 
-    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
-    assert(x.hmean.approxEqual(1.97110904 + 3.14849332i));
+    auto x = [C(1, 2), C(2, 3), C(3, 4), C(4, 5)].sliced;
+    assert(x.hmean.approxEqual(C(1.97110904, 3.14849332)));
 }
 
 /// Arbitrary harmonic mean
@@ -2291,21 +2295,6 @@ unittest
     assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
 }
 
-version(mir_builtincomplex_test)
-@safe pure nothrow
-unittest
-{
-    import mir.math.common: approxEqual;
-    import mir.ndslice.slice: sliced;
-
-    auto x = [1.0 + 3i, 2, 3].sliced;
-
-    VarianceAccumulator!(cdouble, VarianceAlgo.online, Summation.naive) v;
-    v.put(x);
-    assert(v.variance(true).approxEqual((-4.0 - 6i) / 3));
-    assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
-}
-
 version(mir_test)
 @safe pure nothrow
 unittest
@@ -2654,22 +2643,6 @@ unittest
     assert(v.variance(PopulationFalseCT).approxEqual(54.76562 / 11));
 }
 
-version(mir_builtincomplex_test)
-@safe pure nothrow
-unittest
-{
-    import mir.math.common: approxEqual;
-    import mir.ndslice.slice: sliced;
-
-    auto a = [1.0 + 3i, 2, 3].sliced;
-    auto x = a.center;
-
-    VarianceAccumulator!(cdouble, VarianceAlgo.assumeZeroMean, Summation.naive) v;
-    v.put(x);
-    assert(v.variance(true).approxEqual((-4.0 - 6i) / 3));
-    assert(v.variance(false).approxEqual((-4.0 - 6i) / 2));
-}
-
 version(mir_test)
 @safe pure nothrow
 unittest
@@ -2824,11 +2797,13 @@ unittest
 {
     import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
+    import mir.complex;
+    alias C = Complex!double;
 
     assert(variance([1.0, 2, 3]).approxEqual(2.0 / 2));
     assert(variance([1.0, 2, 3], true).approxEqual(2.0 / 3));
 
-    assert(variance([1.0 + 3i, 2, 3]).approxEqual((-4.0 - 6i) / 2));
+    assert(variance([C(1, 3), C(2), C(3)]).approxEqual(C(-4, -6) / 2));
     
     assert(variance!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 5));
     
@@ -2993,9 +2968,11 @@ unittest
 {
     import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
+    import mir.complex;
+    alias C = Complex!double;
 
-    auto x = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i].sliced;
-    assert(x.variance.approxEqual((0.0+10.0i)/ 3));
+    auto x = [C(1, 2), C(2, 3), C(3, 4), C(4, 5)].sliced;
+    assert(x.variance.approxEqual((C(0, 10)) / 3));
 }
 
 /// Compute variance along specified dimention of tensors
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index c6faf41e..2edd6905 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -114,25 +114,6 @@ unittest
 /++
 All summation algorithms available for complex numbers.
 +/
-version(mir_builtincomplex_test)
-unittest
-{
-    cdouble[] ar = [1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i];
-    cdouble r = 10 + 14i;
-    assert(r == ar.sum!"fast");
-    assert(r == ar.sum!"naive");
-    assert(r == ar.sum!"pairwise");
-    assert(r == ar.sum!"kahan");
-    version(LDC) // DMD Internal error: backend/cgxmm.c 628
-    {
-        assert(r == ar.sum!"kbn");
-        assert(r == ar.sum!"kb2");
-    }
-    assert(r == ar.sum!"precise");
-    assert(r == ar.sum!"decimal");
-}
-
-///
 version(mir_test)
 unittest
 {
@@ -287,10 +268,12 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
+    import mir.complex;
+    alias C = Complex!double;
     assert(sum(1, 2, 3, 4) == 10);
     assert(sum!float(1, 2, 3, 4) == 10f);
     assert(sum(1f, 2, 3, 4) == 10f);
-    assert(sum(1.0 + 2i, 2 + 3i, 3 + 4i, 4 + 5i) == (10 + 14i));
+    assert(sum(C(1.0, 2), C(2, 3), C(3, 4), C(4, 5)) == C(10, 14));
 }
 
 version(X86)
@@ -498,8 +481,6 @@ struct Summator(T, Summation summation)
         private enum bool fastPairwise =
             is(F == float) ||
             is(F == double) ||
-            is(F == cfloat) ||
-            is(F == cdouble) ||
             (isComplex!F && F.sizeof <= 16) ||
             is(F : __vector(W[N]), W, size_t N);
             //false;
@@ -692,13 +673,8 @@ public:
             s = n;
             static if (isComplex!T)
             {
-                static if (is(T : cfloat)) {
-                    cs = 0 + 0fi;
-                    ccs = 0 + 0fi;
-                } else {
-                    cs = Complex!float(0, 0);
-                    ccs = Complex!float(0, 0);
-                }
+                cs = Complex!float(0, 0);
+                ccs = Complex!float(0, 0);
             }
             else
             {
@@ -710,26 +686,18 @@ public:
         static if (summation == Summation.kbn)
         {
             s = n;
-            static if (isComplex!T) {
-                static if (is(T : cfloat)) {
-                    c = 0 + 0fi;
-                } else {
-                    c = Complex!float(0, 0);
-                }
-            } else
+            static if (isComplex!T)
+                c = Complex!float(0, 0);
+            else
                 c = 0.0;
         }
         else
         static if (summation == Summation.kahan)
         {
             s = n;
-            static if (isComplex!T) {
-                static if (is(T : cfloat)) {
-                    c = 0 + 0fi;
-                } else {
+            static if (isComplex!T)
                     c = Complex!float(0, 0);
-                }
-            } else
+            else
                 c = 0.0;
         }
         else
@@ -871,25 +839,15 @@ public:
                 {
                     auto s_re = s.re;
                     auto x_re = x.re;
-                    static if (is(F : cfloat)) {
-                        s = x_re + s.im * 1fi;
-                        x = s_re + x.im * 1fi;
-                    } else {
-                        s = F(x_re, s.im);
-                        x = F(s_re, x.im);
-                    }
+                    s = F(x_re, s.im);
+                    x = F(s_re, x.im);
                 }
                 if (fabs(s.im) < fabs(x.im))
                 {
                     auto s_im = s.im;
                     auto x_im = x.im;
-                    static if (is(F : cfloat)) {
-                        s = s.re + x_im * 1fi;
-                        x = x.re + s_im * 1fi;
-                    } else {
-                        s = F(s.re, x_im);
-                        x = F(x.re, s_im);
-                    }
+                    s = F(s.re, x_im);
+                    x = F(x.re, s_im);
                 }
                 F c = (s-t)+x;
                 s = t;
@@ -897,25 +855,15 @@ public:
                 {
                     auto c_re = c.re;
                     auto cs_re = cs.re;
-                    static if (is(F : cfloat)) {
-                        c = cs_re + c.im * 1fi;
-                        cs = c_re + cs.im * 1fi;
-                    } else {
-                        c = F(cs_re, c.im);
-                        cs = F(c_re, cs.im);
-                    }
+                    c = F(cs_re, c.im);
+                    cs = F(c_re, cs.im);
                 }
                 if (fabs(cs.im) < fabs(c.im))
                 {
                     auto c_im = c.im;
                     auto cs_im = cs.im;
-                    static if (is(F : cfloat)) {
-                        c = c.re + cs_im * 1fi;
-                        cs = cs.re + c_im * 1fi;
-                    } else {
-                        c = F(c.re, cs_im);
-                        cs = F(cs.re, c_im);
-                    }
+                    c = F(c.re, cs_im);
+                    cs = F(cs.re, c_im);
                 }
                 F d = cs - t;
                 d += c;
@@ -950,25 +898,15 @@ public:
                 {
                     auto s_re = s.re;
                     auto x_re = x.re;
-                    static if (is(F : cfloat)) {
-                        s = x_re + s.im * 1fi;
-                        x = s_re + x.im * 1fi;
-                    } else {
-                        s = F(x_re, s.im);
-                        x = F(s_re, x.im);
-                    }
+                    s = F(x_re, s.im);
+                    x = F(s_re, x.im);
                 }
                 if (fabs(s.im) < fabs(x.im))
                 {
                     auto s_im = s.im;
                     auto x_im = x.im;
-                    static if (is(F : cfloat)) {
-                        s = s.re + x_im * 1fi;
-                        x = x.re + s_im * 1fi;
-                    } else {
-                        s = F(s.re, x_im);
-                        x = F(x.re, s_im);
-                    }
+                    s = F(s.re, x_im);
+                    x = F(x.re, s_im);
                 }
                 F d = s - t;
                 d += x;
@@ -1060,13 +998,9 @@ public:
         else
         static if (summation == Summation.fast)
         {
-            static if (isComplex!F) {
-                static if (is(F : cfloat)) {
-                    F s0 = 0 + 0fi;
-                } else {
-                    F s0 = F(0, 0f);
-                }
-            } else
+            static if (isComplex!F)
+                F s0 = F(0, 0f);
+            else
                 F s0 = 0;
             foreach (ref elem; r)
                 s0 += elem;
@@ -1097,13 +1031,9 @@ public:
         else
         static if (summation == Summation.fast && N == 1)
         {
-            static if (isComplex!F) {
-                static if (is(F : cfloat)) {
-                    F s0 = 0 + 0fi;
-                } else {
-                    F s0 = F(0, 0f);
-                }
-            } else
+            static if (isComplex!F)
+                F s0 = F(0, 0f);
+            else
                 F s0 = 0;
             import mir.algorithm.iteration: reduce;
             s0 = s0.reduce!"a + b"(r);
@@ -1408,13 +1338,8 @@ public:
             s = rhs;
             static if (isComplex!T)
             {
-                static if (is(T : cfloat)) {
-                    cs = 0 + 0fi;
-                    ccs = 0 + 0fi;
-                } else {
-                    cs = T(0, 0f);
-                    ccs = T(0.0, 0f);
-                }
+                cs = T(0, 0f);
+                ccs = T(0.0, 0f);
             }
             else
             {
@@ -1426,26 +1351,18 @@ public:
         static if (summation == Summation.kbn)
         {
             s = rhs;
-            static if (isComplex!T) {
-                static if (is(T : cfloat)) {
-                    c = 0 + 0fi;
-                } else {
-                    c = T(0, 0f);
-                }
-            } else
+            static if (isComplex!T)
+                c = T(0, 0f);
+            else
                 c = 0.0;
         }
         else
         static if (summation == Summation.kahan)
         {
             s = rhs;
-            static if (isComplex!T) {
-                static if (is(T : cfloat)) {
-                    c = 0 + 0fi;
-                } else {
-                    c = T(0, 0f);
-                }
-            } else
+            static if (isComplex!T)
+                c = T(0, 0f);
+            else
                 c = 0.0;
         }
         else
@@ -1888,11 +1805,7 @@ template sum(F, Summation summation = Summation.appropriate)
                         sumIm.put(elem.im);
                     }
                 }
-                static if (is(F : cfloat)) {
-                    return sumRe.sum + sumIm.sum * 1fi;
-                } else {
-                    return F(sumRe.sum, sumIm.sum);
-                }
+                return F(sumRe.sum, sumIm.sum);
             }
             else
             {
@@ -2087,13 +2000,6 @@ unittest
     assert(sum!float(1.0, 2.0, 3.0) == 6f);
 }
 
-version(mir_builtincomplex_test)
-unittest
-{
-    assert(sum(1.0 + 1i, 2.0 + 2i, 3.0 + 3i) == (6 + 6i));
-    assert(sum!cfloat(1.0 + 1i, 2.0 + 2i, 3.0 + 3i) == (6f + 6i));
-}
-
 version(mir_test)
 unittest
 {
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 704542c4..9aeb0c0c 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2027,8 +2027,10 @@ version(mir_test) unittest
 @safe pure nothrow
 version(mir_test) unittest
 {
-    auto s = linspace!cdouble([3], [1.0 + 0i, 2.0 + 4i]);
-    assert(s == [1.0 + 0i, 1.5 + 2i, 2.0 + 4i]);
+    import mir.complex;
+    alias C = Complex!double;
+    auto s = linspace!C([3], [C(1.0, 0), C(2.0, 4)]);
+    assert(s == [C(1.0, 0), C(1.5, 2), C(2.0, 4)]);
 }
 
 /++

From 519f846cf3144dcf2c1b34e4da92257732553a46 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 12:07:38 +0700
Subject: [PATCH 280/573] replace Phobos complex numbers with Mir

---
 source/mir/math/stat.d | 6 +++---
 source/mir/math/sum.d  | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 4b1ac73c..a326d91a 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -77,7 +77,7 @@ version(mir_test)
 @safe pure nothrow @nogc
 unittest
 {
-    import std.complex: Complex;
+    import mir.complex: Complex;
 
     static assert(is(statType!(Complex!float) == Complex!float));
     static assert(is(statType!(Complex!double) == Complex!double));
@@ -2301,7 +2301,7 @@ unittest
 {
     import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
-    import std.complex: Complex;
+    import mir.complex: Complex;
 
     auto x = [Complex!double(1.0, 3), Complex!double(2), Complex!double(3)].sliced;
 
@@ -2649,7 +2649,7 @@ unittest
 {
     import mir.math.common: approxEqual;
     import mir.ndslice.slice: sliced;
-    import std.complex: Complex;
+    import mir.complex: Complex;
 
     auto a = [Complex!double(1.0, 3), Complex!double(2), Complex!double(3)].sliced;
     auto x = a.center;
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 2edd6905..03537167 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -117,7 +117,7 @@ All summation algorithms available for complex numbers.
 version(mir_test)
 unittest
 {
-    import std.complex: Complex;
+    import mir.complex: Complex;
 
     auto ar = [Complex!double(1.0, 2), Complex!double(2.0, 3), Complex!double(3.0, 4), Complex!double(4.0, 5)];
     Complex!double r = Complex!double(10.0, 14);
@@ -2003,7 +2003,7 @@ unittest
 version(mir_test)
 unittest
 {
-    import std.complex: Complex;
+    import mir.complex: Complex;
 
     assert(sum(Complex!float(1.0, 1.0), Complex!float(2.0, 2.0), Complex!float(3.0, 3.0)) == Complex!float(6.0, 6.0));
     assert(sum!(Complex!float)(Complex!float(1.0, 1.0), Complex!float(2.0, 2.0), Complex!float(3.0, 3.0)) == Complex!float(6.0, 6.0));

From 1851c4d5baf7c3c8a167068f4ba151f9cd759ce1 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 12:21:48 +0700
Subject: [PATCH 281/573] simplify Series.toString

---
 source/mir/series.d | 59 +++++----------------------------------------
 1 file changed, 6 insertions(+), 53 deletions(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index b6765871..2816ad35 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -1292,53 +1292,11 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     }
 
     ///
-    void toString(Writer, Spec)(auto ref Writer w, const ref Spec f) const
+    void toString(Writer)(scope ref Writer w) const
     {
-        import std.format: formatValue, formatElement;
-        import std.range: put;
-
-        if (f.spec != 's' && f.spec != '(')
-            throw new Exception("incompatible format character for Mir Series argument: %" ~ f.spec);
-
-        enum defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator;
-        auto fmtSpec = f.spec == '(' ? f.nested : defSpec;
-
-        if (f.spec == 's')
-            put(w, f.seqBefore);
-        if (length) for (size_t i = 0;;)
-        {
-            auto fmt = Spec(fmtSpec);
-            fmt.writeUpToNextSpec(w);
-            if (f.flDash)
-            {
-                formatValue(w, index[i], fmt);
-                fmt.writeUpToNextSpec(w);
-                formatValue(w, data[i], fmt);
-            }
-            else
-            {
-                formatElement(w, index[i], fmt);
-                fmt.writeUpToNextSpec(w);
-                formatElement(w, data[i], fmt);
-            }
-            if (f.sep !is null)
-            {
-                fmt.writeUpToNextSpec(w);
-                if (++i != length)
-                    put(w, f.sep);
-                else
-                    break;
-            }
-            else
-            {
-                if (++i != length)
-                    fmt.writeUpToNextSpec(w);
-                else
-                    break;
-            }
-        }
-        if (f.spec == 's')
-            put(w, f.seqAfter);
+        import mir.format: print;
+        auto ls = lightScope;
+        print(w, "{ index: ", ls.index, ", data: ", ls.data, " }");
     }
 
     version(mir_test)
@@ -1348,13 +1306,8 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         import mir.series: series, sort;
         auto s = ["b", "a"].series([9, 8]).sort;
 
-        import std.conv : to;
-        assert(s.to!string == `["a":8, "b":9]`);
-
-        import std.format : format;
-        assert("%s".format(s) == `["a":8, "b":9]`);
-        assert("%(%s %s | %)".format(s) == `"a" 8 | "b" 9`);
-        assert("%-(%s,%s\n%)\n".format(s) == "a,8\nb,9\n");
+        import mir.format : text;
+        assert(s.text == `{ index: [a, b], data: [8, 9] }`, s.text);
     }
 }
 

From 8c29aada0a32c0cad4c2329071d953aec5427d46 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 12:22:06 +0700
Subject: [PATCH 282/573] fixup

---
 source/mir/series.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index 2816ad35..e2e45e93 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -1307,7 +1307,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         auto s = ["b", "a"].series([9, 8]).sort;
 
         import mir.format : text;
-        assert(s.text == `{ index: [a, b], data: [8, 9] }`, s.text);
+        assert(s.text == `{ index: [a, b], data: [8, 9] }`);
     }
 }
 

From 8298baa5fc39a97afe06f6fa06e219d06c5d140c Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 12:39:39 +0700
Subject: [PATCH 283/573] prepeare Series for serialisation

---
 source/mir/series.d | 94 ++++++++++++++++++++++++++-------------------
 1 file changed, 54 insertions(+), 40 deletions(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index e2e45e93..3cc26b7c 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -204,13 +204,11 @@ struct mir_observation(Index, Data)
 {
     /// Date, date-time, time, or index.
     Index index;
-    /// An alias for time-series index.
-    deprecated ("use `index` instead") alias time = index;
-    /// An alias for key-value representation.
-    deprecated ("use `index` instead") alias key = index;
     /// Value or ndslice.
     Data data;
-    /// An alias for key-value representation.
+@serdeIgnore:
+    deprecated ("use `index` instead") alias time = index;
+    deprecated ("use `index` instead") alias key = index;
     deprecated ("use `data` instead") alias value = data;
 }
 
@@ -264,7 +262,7 @@ $(LREF sort) can be used to normalise a series.
 +/
 struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous)
 {
-    private enum doUnittest = is(typeof(this) == Series!(int*, double*));
+    private enum doUnittest = is(typeof(this) == mir_series!(int*, double*));
 
     ///
     alias IndexIterator = IndexIterator_;
@@ -273,10 +271,10 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     alias Iterator = Iterator_;
 
     ///
-    enum size_t N = N_;
+    @serdeIgnore enum size_t N = N_;
 
     ///
-    enum SliceKind kind = kind_;
+    @serdeIgnore enum SliceKind kind = kind_;
 
     /++
     Data is any ndslice with only one constraints,
@@ -285,24 +283,63 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     Slice!(Iterator, N, kind) data;
 
     ///
-    IndexIterator _index;
+    @serdeIgnore IndexIterator _index;
+
+    /++
+    Index series is assumed to be sorted.
+
+    `IndexIterator` is an iterator on top of date, date-time, time, or numbers or user defined types with defined `opCmp`.
+    For example, `Date*`, `DateTime*`, `immutable(long)*`, `mir.ndslice.iterator.IotaIterator`.
+    +/
+    auto index()() @property @trusted
+    {
+        return _index.sliced(this.data._lengths[0]);
+    }
 
-    /// Index / Key / Time type aliases
-    alias Index = typeof(typeof(this).init.index.front);
     /// ditto
-    deprecated ("use `Index` instead") alias Key = Index;
+    auto index()() @property @trusted const
+    {
+        return _index.lightConst.sliced(this.data._lengths[0]);
+    }
+
     /// ditto
-    deprecated ("use `Index` instead") alias Time = Index;
+    auto index()() @property @trusted immutable
+    {
+        return _index.lightImmutable.sliced(this.data._lengths[0]);
+    }
+
+    /// ditto
+    void index()(Slice!IndexIterator index) @property @trusted
+    {
+        import core.lifetime: move;
+        assert(index._lengths[0] == data._lengths[0]);
+        this._index = move(index._iterator);
+    }
+
+    ///
+    static if (doUnittest)
+    unittest
+    {
+        import mir.ndslice.slice: sliced;
+        auto s = ["a", "b"].series([5, 6]);
+        assert(s.index == ["a", "b"]);
+        s.index = ["c", "d"].sliced;
+        assert(s.index == ["c", "d"]);
+    }
+
+@serdeIgnore:
+
+    /// Index / Key / Time type aliases
+    alias Index = typeof(typeof(this).init.index.front);
     /// Data / Value type aliases
     alias Data = typeof(typeof(this).init.data.front);
-    /// ditto
+
+    deprecated ("use `Index` instead") alias Key = Index;
+    deprecated ("use `Index` instead") alias Time = Index;
     deprecated ("use `Data` instead") alias Value = Data;
 
-    /// An alias for time-series index.
     deprecated ("use `index` instead") alias time = index;
-    /// An alias for key-value representation.
     deprecated ("use `index` instead") alias key = index;
-    /// An alias for key-value representation.
     deprecated ("use `data` instead") alias value = data;
 
     private enum defaultMsg() = "Series " ~ Unqual!(this.Data).stringof ~ "[" ~ Unqual!(this.Index).stringof ~ "]: Missing";
@@ -332,29 +369,6 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         return this.lightScopeIndex == rhs.lightScopeIndex && this.data.lightScope == rhs.data.lightScope;
     }
 
-    /++
-    Index series is assumed to be sorted.
-
-    `IndexIterator` is an iterator on top of date, date-time, time, or numbers or user defined types with defined `opCmp`.
-    For example, `Date*`, `DateTime*`, `immutable(long)*`, `mir.ndslice.iterator.IotaIterator`.
-    +/
-    auto index()() @property @trusted
-    {
-        return _index.sliced(this.data._lengths[0]);
-    }
-
-    /// ditto
-    auto index()() @property @trusted const
-    {
-        return _index.lightConst.sliced(this.data._lengths[0]);
-    }
-
-    /// ditto
-    auto index()() @property @trusted immutable
-    {
-        return _index.lightImmutable.sliced(this.data._lengths[0]);
-    }
-
     private auto lightScopeIndex()() @property @trusted
     {
         return .lightScope(_index).sliced(this.data._lengths[0]);

From 792a4e9661b7a00adcf918743a16594cf4f1b9e5 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 12:48:24 +0700
Subject: [PATCH 284/573] fix unittest

---
 source/mir/series.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index 3cc26b7c..70d7404e 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -318,7 +318,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
 
     ///
     static if (doUnittest)
-    unittest
+    @safe version(mir_test) unittest
     {
         import mir.ndslice.slice: sliced;
         auto s = ["a", "b"].series([5, 6]);

From 6d91eca08c1b85a3df8648f0b53d08297a4d2db1 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 12:50:38 +0700
Subject: [PATCH 285/573] add serdeFields

---
 source/mir/serde.d | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 7bcf45ac..e9245375 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -754,6 +754,11 @@ See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn)
 +/
 enum serdeLikeStruct;
 
+/++
+Force serialize / deserialize on fields instead of Range API.
++/
+enum serdeFields;
+
 /++
 Ignore keys for object and enum members.
 Should be applied to members or enum type itself.

From 47c16e219c502405a9c4274447b31b1428def109 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 16 Nov 2021 13:33:03 +0700
Subject: [PATCH 286/573] adopt series for serialization

---
 source/mir/series.d | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index 70d7404e..e94d2129 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -18,7 +18,7 @@ module mir.series;
 import mir.ndslice.iterator: IotaIterator;
 import mir.ndslice.sorting: transitionIndex;
 import mir.qualifier;
-import mir.serde: serdeIgnore;
+import mir.serde: serdeIgnore, serdeFields, serdeKeys, serdeKeyOut;
 import std.traits;
 public import mir.ndslice.slice;
 public import mir.ndslice.sorting: sort;
@@ -260,6 +260,7 @@ Plain index series data structure.
 Index is assumed to be sorted.
 $(LREF sort) can be used to normalise a series.
 +/
+@serdeFields
 struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous)
 {
     private enum doUnittest = is(typeof(this) == mir_series!(int*, double*));
@@ -276,34 +277,25 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     ///
     @serdeIgnore enum SliceKind kind = kind_;
 
-    /++
-    Data is any ndslice with only one constraints,
-    `data` and `index` lengths should be equal.
-    +/
-    Slice!(Iterator, N, kind) data;
-
-    ///
-    @serdeIgnore IndexIterator _index;
-
     /++
     Index series is assumed to be sorted.
 
     `IndexIterator` is an iterator on top of date, date-time, time, or numbers or user defined types with defined `opCmp`.
     For example, `Date*`, `DateTime*`, `immutable(long)*`, `mir.ndslice.iterator.IotaIterator`.
     +/
-    auto index()() @property @trusted
+    auto index() @property @trusted
     {
         return _index.sliced(this.data._lengths[0]);
     }
 
     /// ditto
-    auto index()() @property @trusted const
+    auto index() @property @trusted const
     {
         return _index.lightConst.sliced(this.data._lengths[0]);
     }
 
     /// ditto
-    auto index()() @property @trusted immutable
+    auto index() @property @trusted immutable
     {
         return _index.lightImmutable.sliced(this.data._lengths[0]);
     }
@@ -312,11 +304,10 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     void index()(Slice!IndexIterator index) @property @trusted
     {
         import core.lifetime: move;
-        assert(index._lengths[0] == data._lengths[0]);
         this._index = move(index._iterator);
     }
 
-    ///
+    /// ditto
     static if (doUnittest)
     @safe version(mir_test) unittest
     {
@@ -327,8 +318,17 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         assert(s.index == ["c", "d"]);
     }
 
+    /++
+    Data is any ndslice with only one constraints,
+    `data` and `index` lengths should be equal.
+    +/
+    Slice!(Iterator, N, kind) data;
+
 @serdeIgnore:
 
+    ///
+    IndexIterator _index;
+
     /// Index / Key / Time type aliases
     alias Index = typeof(typeof(this).init.index.front);
     /// Data / Value type aliases

From 415f5ccc01912dc1817dfad4923723c71f8ef26a Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Thu, 18 Nov 2021 11:15:40 +0700
Subject: [PATCH 287/573] add commented test

---
 source/mir/parse.d | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/source/mir/parse.d b/source/mir/parse.d
index 753b6c60..f5b03588 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -116,7 +116,7 @@ version(mir_bignum_test)
     static assert("3.518437208883201171875".fromString!float == 0x1.c25c26p+1);
     static assert("3.518437208883201171875".fromString!double == 0x1.c25c268497684p+1);
     static if (real.mant_dig >= 64)
-        static assert("3.518437208883201171875".fromString!real == 0xe.12e13424bb4232fp-2L);
+        static assert("3.518437208883201171875".fromString!real == 0xe.12e13424bb4232fp-2L);    
 
 //  Related DMD Issues:
 // https://issues.dlang.org/show_bug.cgi?id=20951
@@ -125,6 +125,18 @@ version(mir_bignum_test)
 // https://issues.dlang.org/show_bug.cgi?id=20967
 }
 
+// unittest
+// {
+//     import std.stdio;
+//     import mir.conv;
+//     import mir.format;
+//     text(double.min_normal).writeln;
+//     text(double.min_normal * double.epsilon).writeln;
+//     assert("2.2250738585072014e-308".fromString!double == double.min_normal);
+//     assert("5e-324".fromString!double == double.min_normal * double.epsilon);
+//     assert("2.5e-324".fromString!double == double.min_normal * double.epsilon);
+// }
+
 version(mir_bignum_test)
 @safe pure unittest
 {

From 3f14886f2fa5b330ae5f5ce27f73d197acd90570 Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Sat, 20 Nov 2021 20:09:00 -0500
Subject: [PATCH 288/573] Add simple base64 de/encoding (#372)

* Add simple base64 de/encoding support

* Add fixes, improve coverage

* Fix build

* Remove Kaledic from copyright, add fixes requested
---
 source/mir/base64.d | 454 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 454 insertions(+)
 create mode 100644 source/mir/base64.d

diff --git a/source/mir/base64.d b/source/mir/base64.d
new file mode 100644
index 00000000..9b754f93
--- /dev/null
+++ b/source/mir/base64.d
@@ -0,0 +1,454 @@
+/++
+$(H1 @nogc Simple Base64 parsing)
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Harrison Ford
+Copyright: 2021 Harrison Ford, Symmetry Investments
++/
+module mir.base64;
+import mir.ndslice.topology;
+
+package static immutable base64DecodeInvalidCharMsg = "Invalid character encountered.";
+package static immutable base64DecodeInvalidLenMsg = "Cannot decode a buffer with given length (not a multiple of 4, missing padding?)";
+version(D_Exceptions) {
+    package static immutable base64DecodeInvalidCharException = new Exception(base64DecodeInvalidCharMsg);
+    package static immutable base64DecodeInvalidLenException = new Exception(base64DecodeInvalidLenMsg);
+}
+
+// NOTE: I do not know if this would work on big-endian systems.
+// Needs further testing to figure out if it *does* work on them.
+
+// Technique borrowed from http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html#branchless-code-for-lookup-table
+private char lookup_encoding(ubyte i, char plusChar = '+', char slashChar = '/') @safe @nogc pure {
+    assert(i < 64);
+
+    ubyte shift;
+
+    if (i < 26)
+    {
+        // range A-Z
+        shift = 'A';
+    }
+    else if (i >= 26 && i < 52)
+    {
+        // range a-z
+        shift = 'a' - 26;
+    }
+    else if (i >= 52 && i < 62)
+    {
+        // range 0-9
+        shift = cast(ubyte)('0' - 52);
+    }
+    else if (i == 62)
+    {
+        // character plus
+        shift = cast(ubyte)(plusChar - 62);
+    }
+    else if (i == 63)
+    {
+        // character slash
+        shift = cast(ubyte)(slashChar - 63);
+    }
+
+    return cast(char)(i + shift);
+}
+
+// Do the inverse of above (convert an ASCII value into the Base64 character set)
+private ubyte lookup_decoding(char i, char plusChar = '+', char slashChar = '/') @safe @nogc pure
+{
+    // Branching bad, but this isn't performance sensitive
+    if (i <= 'Z' && i >= 'A') {
+        return cast(ubyte)(i - 'A');
+    }
+    else if (i <= 'z' && i >= 'a') {
+        return cast(ubyte)(i - 'a' + 26); 
+    }
+    else if (i <= '9' && i >= '0') {
+        return cast(ubyte)(i - '0' + 52);
+    }
+    else if (i == plusChar) {
+        return 62;
+    }
+    else if (i == slashChar) {
+        return 63;
+    }
+    // Just return 0 for padding,
+    // as it typically means nothing.
+    else if (i == '=') {
+        return 0;
+    }
+    else {
+        version(D_Exceptions) {
+            throw base64DecodeInvalidCharException;
+        } else {
+            assert(0, base64DecodeInvalidCharMsg);
+        }
+    }
+
+}
+
+/++
+Decode a Base64 encoded value, returning the buffer.
++/
+ubyte[] decodeBase64(scope const(char)[] data, char plusChar = '+', char slashChar = '/') @safe pure
+{
+    import mir.appender : scopedBuffer;
+    auto app = scopedBuffer!ubyte;
+    decodeBase64(data, app, plusChar, slashChar);
+    return app.data.dup;
+}
+
+/++
+Decode a Base64 encoded value, placing the result onto an Appender.
++/
+void decodeBase64(Appender)(scope const(char)[] data,
+                            scope return ref Appender appender,
+                            char plusChar = '+',
+                            char slashChar = '/') @safe pure
+{
+    // We expect data should be well-formed (with padding),
+    // so we should throw if it is not well-formed.
+    if (data.length % 4 != 0)
+    {
+        version(D_Exceptions) {
+            throw base64DecodeInvalidLenException;
+        } else {
+            assert(0, base64DecodeInvalidLenMsg);
+        }
+    }
+    
+    ubyte[3] decodedByteGroup;
+    ubyte sz = 0;
+
+    // We can't use mir.ndslice.chunk.chunks here, as it violates
+    // the scope requirements.
+    for (size_t i = 0; i < data.length; i += 4)
+    {
+        auto group = data[i .. (i + 4)];
+
+        ubyte[4] decodedBytes;
+        decodedBytes[0] = lookup_decoding(group[0], plusChar, slashChar);
+        decodedBytes[1] = lookup_decoding(group[1], plusChar, slashChar);
+
+        uint transformed_group = (decodedBytes[0] << 26) | (decodedBytes[1] << 20);
+
+        // According to RFC4648 Section 3.3, we don't have to accept extra padding characters,
+        // and we can safely throw (and stay within spec).
+        // x=== is also invalid, so we can just throw on that here.
+        if (group[0] == '=' || group[1] == '=')
+        {
+            version(D_Exceptions)
+                throw base64DecodeInvalidCharException;
+            else
+                assert(0, base64DecodeInvalidCharMsg);
+        }
+
+        // xx=(=)?
+        if (group[2] == '=')
+        {
+            // If we are not at the end of a string, according to RFC4648,
+            // we can safely treat a padding character as "non-alphabet data",
+            // and as such, we should throw. See RFC4648 Section 3.3 for more information
+            if ((i / 4) != ((data.length / 4) - 1))
+            {
+                version(D_Exceptions)
+                    throw base64DecodeInvalidCharException;
+                else
+                    assert(0, base64DecodeInvalidCharMsg);
+            }
+
+            if (group[3] == '=')
+            {
+                // xx==
+                sz = 1;
+            }
+            // xx=x (invalid)
+            // Padding should not be in the middle of a chunk
+            else
+            {
+                version(D_Exceptions)
+                    throw base64DecodeInvalidCharException;
+                else
+                    assert(0, base64DecodeInvalidCharMsg);
+            }
+        }
+        // xxx=
+        else if (group[3] == '=')
+        {
+            // If we are not at the end of a string, according to RFC4648,
+            // we can safely treat a padding character as "non-alphabet data",
+            // and as such, we should throw. See RFC4648 Section 3.3 for more information
+            if ((i / 4) != ((data.length / 4) - 1))
+            {
+                version(D_Exceptions)
+                    throw base64DecodeInvalidCharException;
+                else
+                    assert(0, base64DecodeInvalidCharMsg);
+            }
+
+            decodedBytes[2] = lookup_decoding(group[2], plusChar, slashChar);
+            transformed_group |= (decodedBytes[2] << 14);
+            sz = 2;
+        }
+        // xxxx
+        else 
+        {
+            decodedBytes[2] = lookup_decoding(group[2], plusChar, slashChar);
+            decodedBytes[3] = lookup_decoding(group[3], plusChar, slashChar);
+            transformed_group |= ((decodedBytes[2] << 14) | (decodedBytes[3] << 8)); 
+            sz = 3;
+        }
+
+        decodedByteGroup[0] = (transformed_group >> 24) & 0xff;
+        decodedByteGroup[1] = (transformed_group >> 16) & 0xff;
+        decodedByteGroup[2] = (transformed_group >> 8) & 0xff;
+
+        // Only emit the transformed bytes that we got data for. 
+        appender.put(decodedByteGroup[0 .. sz]);
+    }
+}
+
+/// Test decoding of data which has a length which can be
+/// cleanly decoded.
+@safe pure unittest
+{
+    {
+        enum data = "QUJD";
+        assert(data.decodeBase64 == "ABC");
+    }
+
+    {
+        enum data = "QQ==";
+        assert(data.decodeBase64 == "A");
+    }
+
+    {
+        enum data = "YSBiIGMgZCBlIGYgZyBoIGkgaiBrIGwgbSBuIG8gcCBxIHIgcyB0IHUgdiB3IHggeSB6";
+        assert(data.decodeBase64 == "a b c d e f g h i j k l m n o p q r s t u v w x y z");
+    }
+
+    {
+        enum data = "LCAuIDsgLyBbICcgXSBcID0gLSAwIDkgOCA3IDYgNSA0IDMgMiAxIGAgfiAhIEAgIyAkICUgXiAmICogKCApIF8gKyB8IDogPCA+ID8=";
+        assert(data.decodeBase64 == ", . ; / [ ' ] \\ = - 0 9 8 7 6 5 4 3 2 1 ` ~ ! @ # $ % ^ & * ( ) _ + | : < > ?");
+    }
+
+    {
+        enum data = "AAA=";
+        assert(data.decodeBase64 == "\x00\x00");
+    }
+
+    {
+        enum data = "AAAABBCC";
+        assert(data.decodeBase64 == "\x00\x00\x00\x04\x10\x82");
+    }
+
+    {
+        enum data = "AA==";
+        assert(data.decodeBase64 == "\x00");
+    }
+    
+    {
+        enum data = "AA/=";
+        assert(data.decodeBase64 == "\x00\x0f");
+    }
+}
+
+/// Test decoding invalid data
+@safe pure unittest
+{
+    void testFail(const(char)[] input) @safe pure
+    {
+        bool thrown = false;
+        try {
+            ubyte[] decoded = input.decodeBase64;
+        } catch (Exception t) {
+            thrown = true;
+        }
+
+        assert(thrown);
+    }
+
+    testFail("===A");
+    testFail("A=");
+    testFail("AA=");
+    testFail("A=AA");
+    testFail("AA=A");
+    testFail("AA=A====");
+    testFail("=AAA");
+    testFail("AAA=QUJD");
+    // This fails because we don't allow extra padding (than what is necessary)
+    testFail("AA======");
+    // This fails because we don't allow padding before the end of the string (otherwise we'd have a side-channel)
+    testFail("QU==QUJD");
+    testFail("QU======QUJD");
+    // Invalid data that's out of the alphabet
+    testFail("!@##@@!@");
+}
+
+/++
+Encode a ubyte array as Base64, returning the encoded value.
++/
+const(char)[] encodeBase64(scope const(ubyte)[] buf, char plusChar = '+', char slashChar = '/') @safe pure
+{
+    import mir.appender : scopedBuffer;
+    auto app = scopedBuffer!char;
+    encodeBase64(buf, app, plusChar, slashChar);
+    return app.data.dup;
+}
+
+/++
+Encode a ubyte array as Base64, placing the result onto an Appender.
++/
+void encodeBase64(Appender)(scope const(ubyte)[] input,
+                            scope return ref Appender appender,
+                            char plusChar = '+',
+                            char slashChar = '/') @safe pure
+{
+    import core.bitop : bswap;
+    import mir.ndslice.topology : bytegroup, map;
+    // Slice our input array so that n % 3 == 0 (we have a multiple of 3) 
+    // If we have less then 3, then this is effectively a no-op (will result in a 0-length slice)
+    char[4] encodedByteGroup;
+    const(ubyte)[] window = input[0 .. input.length - (input.length % 3)];
+    foreach(group; window.bytegroup!(3, uint).map!bswap) {
+        const(ubyte) a = (group >> 26) & 0x3f;
+        const(ubyte) b = (group >> 20) & 0x3f;
+        const(ubyte) c = (group >> 14) & 0x3f;
+        const(ubyte) d = (group >> 8) & 0x3f;
+
+        encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
+        encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
+        encodedByteGroup[2] = c.lookup_encoding(plusChar, slashChar);
+        encodedByteGroup[3] = d.lookup_encoding(plusChar, slashChar);
+        appender.put(encodedByteGroup[]);
+    }
+
+    // If it's a clean multiple of 3, then it requires no padding.
+    // If not, then we need to add padding.
+    if (input.length % 3 != 0)
+    {
+        window = input[window.length .. input.length];
+
+        uint group = (window[0] << 24);
+
+        if (window.length == 1) {
+            const(ubyte) a = (group >> 26) & 0x3f;
+            const(ubyte) b = (group >> 20) & 0x3f;
+            encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
+            encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
+            encodedByteGroup[2] = '=';
+            encodedByteGroup[3] = '=';
+        }
+        else {
+            // Just in case 
+            assert(window.length == 2);
+
+            group |= (window[1] << 16);
+            const(ubyte) a = (group >> 26) & 0x3f;
+            const(ubyte) b = (group >> 20) & 0x3f;
+            const(ubyte) c = (group >> 14) & 0x3f;
+            encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
+            encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
+            encodedByteGroup[2] = c.lookup_encoding(plusChar, slashChar);
+            encodedByteGroup[3] = '=';
+        }
+
+        appender.put(encodedByteGroup[]);
+    }
+}
+
+/// Test encoding of data which has a length that can be cleanly
+/// encoded.
+@safe pure unittest
+{
+    // 3 bytes
+    {
+        enum data = cast(immutable(ubyte)[])"ABC";
+        assert(data.encodeBase64 == "QUJD");
+    }
+
+    // 6 bytes
+    {
+        enum data = cast(immutable(ubyte)[])"ABCDEF";
+        assert(data.encodeBase64 == "QUJDREVG");
+    }
+
+    // 9 bytes
+    {
+        enum data = cast(immutable(ubyte)[])"ABCDEFGHI";
+        assert(data.encodeBase64 == "QUJDREVGR0hJ");
+    }
+
+    // 12 bytes
+    {
+        enum data = cast(immutable(ubyte)[])"ABCDEFGHIJKL";
+        assert(data.encodeBase64 == "QUJDREVGR0hJSktM");
+    }
+}
+
+/// Test encoding of data which has a length which CANNOT be cleanly encoded.
+/// This typically means that there's padding.
+@safe pure unittest
+{
+    // 1 byte 
+    {
+        enum data = cast(immutable(ubyte)[])"A";
+        assert(data.encodeBase64 == "QQ==");
+    }
+    // 2 bytes
+    {
+        enum data = cast(immutable(ubyte)[])"AB";
+        assert(data.encodeBase64 == "QUI=");
+    }
+    // 2 bytes
+    {
+        enum data = [0xFF, 0xFF];
+        assert(data.encodeBase64 == "//8=");
+    }
+    // 4 bytes
+    {
+        enum data = [0xDE, 0xAD, 0xBA, 0xBE];
+        assert(data.encodeBase64 == "3q26vg==");
+    }
+    // 37 bytes
+    {
+        enum data = cast(immutable(ubyte)[])"A Very Very Very Very Large Test Blob";
+        assert(data.encodeBase64 == "QSBWZXJ5IFZlcnkgVmVyeSBWZXJ5IExhcmdlIFRlc3QgQmxvYg==");
+    }
+}
+
+/// Test nogc encoding
+@safe pure @nogc unittest
+{
+    import mir.appender : scopedBuffer;
+
+    {
+        enum data = cast(immutable(ubyte)[])"A Very Very Very Very Large Test Blob";
+        auto appender = scopedBuffer!char();
+        data.encodeBase64(appender); 
+        assert(appender.data == "QSBWZXJ5IFZlcnkgVmVyeSBWZXJ5IExhcmdlIFRlc3QgQmxvYg==");     
+    }
+
+    {
+        enum data = cast(immutable(ubyte)[])"abc123!?$*&()'-=@~";
+        auto appender = scopedBuffer!char();
+        data.encodeBase64(appender);
+        assert(appender.data == "YWJjMTIzIT8kKiYoKSctPUB+");
+    }
+}
+
+/// Make sure we can decode what we encode.
+@safe pure unittest
+{
+    // Test an example string
+    {
+        enum data = cast(immutable(ubyte)[])"abc123!?$*&()'-=@~";
+        assert(data.encodeBase64.decodeBase64 == data);
+    }
+    // Test an example from Ion data
+    {
+        enum data = cast(immutable(ubyte)[])"a b c d e f g h i j k l m n o p q r s t u v w x y z";
+        assert(data.encodeBase64.decodeBase64 == data);
+    }
+}
+

From 348dd6a36b982b6629c3c0e87e5b245f1b880258 Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Mon, 22 Nov 2021 09:56:29 -0500
Subject: [PATCH 289/573] Add fixes to both the Decimal and DecimalView impls
 to allow exponents to follow dots (#374)

* Add fixes to DecimalView to allow exponents to follow dots

* Finalize fixes, add tests
---
 source/mir/bignum/decimal.d        | 88 ++++++++++++++++++++++++++----
 source/mir/bignum/low_level_view.d | 47 ++++++++++++----
 2 files changed, 112 insertions(+), 23 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index fcb7bcec..a25c51d8 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -274,6 +274,8 @@ struct Decimal(size_t maxSize64)
             {
                 bool recentUnderscore;
             }
+            // Was there a recent character within the set: ['.', 'e', 'E', 'd', 'D']?
+            bool recentModifier;
             static if (!allowLeadingZeros)
             {
                 if (d == 0)
@@ -299,6 +301,7 @@ struct Decimal(size_t maxSize64)
                         return false;
                     key = DecimalExponentKey.dot;
                     dot = true;
+                    recentModifier = true;
                     goto F;
                 }
             }
@@ -323,6 +326,7 @@ struct Decimal(size_t maxSize64)
                     {
                         recentUnderscore = false;
                     }
+                    recentModifier = false;
                     {
                         import mir.checkedint: mulu;
                         bool overflow;
@@ -342,31 +346,35 @@ struct Decimal(size_t maxSize64)
                     coefficient.length = v != 0;
                     static if (allowUnderscores)
                     {
-                        return !recentUnderscore;
+                        return !recentUnderscore && !recentModifier;
                     }
                     else
                     {
-                        return true;
+                        return !recentModifier;
                     }
                 }
-                static if (allowUnderscores)
-                {
-                    if (recentUnderscore)
-                        return false;
-                }
+
                 switch (d)
                 {
                     case DecimalExponentKey.dot:
+                        // The dot modifier CANNOT be preceded by any modifiers. 
+                        if (recentModifier || key != DecimalExponentKey.none)
+                            break;
+
+                        static if (allowUnderscores)
+                        {
+                            // If we allow underscores, the dot also CANNOT be preceded by any underscores.
+                            // It must be preceded by a number.
+                            if (recentUnderscore)
+                                break;
+                        }
                         key = DecimalExponentKey.dot;
                         if (_expect(dot, false))
                             break;
                         dot = true;
                         if (str.length)
                         {
-                            static if (allowUnderscores)
-                            {
-                                recentUnderscore = true;
-                            }
+                            recentModifier = true;
                             continue;
                         }
                         static if (allowDotOnBounds)
@@ -388,14 +396,24 @@ struct Decimal(size_t maxSize64)
                         case DecimalExponentKey.e:
                         case DecimalExponentKey.E:
                             import mir.parse: parse;
+                            // We don't really care if the e/E/d/D modifiers are preceded by a modifier,
+                            // so as long as they are a dot or a regular number.
+                            if (key != DecimalExponentKey.dot && key != DecimalExponentKey.none)
+                                break; 
                             key = cast(DecimalExponentKey)d;
-                            if (parse(str, exponent) && str.length == 0)
+                            if (parse(str, exponent) && str.length == 0) {
+                                recentModifier = false;
                                 goto E;
+                            }
                             break;
                     }
                     static if (allowUnderscores)
                     {
                         case '_' - '0':
+                            // A underscore cannot be preceded by an underscore or a modifier.
+                            if (recentUnderscore || recentModifier)
+                                break;
+
                             recentUnderscore = true;
                             if (str.length)
                                 continue;
@@ -554,6 +572,52 @@ struct Decimal(size_t maxSize64)
         assert(key == DecimalExponentKey.E);
         assert(cast(double) decimal == 0);
 
+        /++ Test that Issue #365 is handled properly +/
+        assert(decimal.fromStringImpl("123456.e0", key));
+        assert(key == DecimalExponentKey.e);
+        assert(cast(double) decimal == 123_456.0);
+
+        assert(decimal.fromStringImpl("123_456.e0", key));
+        assert(key == DecimalExponentKey.e);
+        assert(cast(double) decimal == 123_456.0);
+
+        assert(decimal.fromStringImpl("123456.E0", key));
+        assert(key == DecimalExponentKey.E);
+        assert(cast(double) decimal == 123_456.0);
+
+        assert(decimal.fromStringImpl("123_456.E0", key));
+        assert(key == DecimalExponentKey.E);
+        assert(cast(double) decimal == 123_456.0);
+
+        assert(decimal.fromStringImpl("123456.d0", key));
+        assert(key == DecimalExponentKey.d);
+        assert(cast(double) decimal == 123_456.0);
+
+        assert(decimal.fromStringImpl("123_456.d0", key));
+        assert(key == DecimalExponentKey.d);
+        assert(cast(double) decimal == 123_456.0);
+
+        assert(decimal.fromStringImpl("123456.D0", key));
+        assert(key == DecimalExponentKey.D);
+        assert(cast(double) decimal == 123_456.0);
+
+        assert(decimal.fromStringImpl("123_456.D0", key));
+        assert(key == DecimalExponentKey.D);
+        assert(cast(double) decimal == 123_456.0);
+
+        /++ Test invalid examples with the fix introduced for Issue #365 +/
+        assert(!decimal.fromStringImpl("123_456_.D0", key));
+        assert(!decimal.fromStringImpl("123_456.DD0", key));
+        assert(!decimal.fromStringImpl("123_456_.E0", key));
+        assert(!decimal.fromStringImpl("123_456.EE0", key));
+        assert(!decimal.fromStringImpl("123456.ED0", key));
+        assert(!decimal.fromStringImpl("123456E0D0", key));
+        assert(!decimal.fromStringImpl("123456._D0", key));
+        assert(!decimal.fromStringImpl("123456_.D0", key));
+        assert(!decimal.fromStringImpl("123456.E0D0", key));
+        assert(!decimal.fromStringImpl("123456.D0_", key));
+        assert(!decimal.fromStringImpl("123456_", key));
+
         foreach (str; ["-nan", "-NaN", "-NAN"])
         {
             assert(decimal.fromStringImpl(str, key));
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index c2ab6a7b..ac413e45 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2489,6 +2489,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
+        debug import std.stdio;
         import mir.utility: _expect;
 
         version(LDC)
@@ -2538,6 +2539,9 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
             bool recentUnderscore;
         }
 
+        // Was there a recent character within the set: ['.', 'e', 'E', 'd', 'D']?
+        bool recentModifier;
+
         static if (!allowLeadingZeros)
         {
             if (d == 0)
@@ -2566,6 +2570,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     return false;
                 key = DecimalExponentKey.dot;
                 dot = true;
+                recentModifier = true;
                 goto F;
             }
         }
@@ -2591,6 +2596,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                 {
                     recentUnderscore = false;
                 }
+                recentModifier = false;
                 v *= 10;
         S:
                 t *= 10;
@@ -2622,35 +2628,44 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                         coefficient = work.mostSignificant == 0 ? coefficient.init : work;
                         static if (allowUnderscores)
                         {
-                            return !recentUnderscore;
+                            // If we have no characters, then we should return true IF
+                            // the last character was NOT a underscore OR a modifier
+                            return !recentUnderscore && !recentModifier;
                         }
                         else
                         {
-                            return true;
+                            // If we don't allow underscores, and we have no characters,
+                            // then we should return true IF the character was NOT a modifier.
+                            return !recentModifier;
                         }
                     }
                 }
 
                 continue;
             }
-            static if (allowUnderscores)
-            {
-                if (recentUnderscore)
-                    return false;
-            }
+
             switch (d)
             {
                 case DecimalExponentKey.dot:
+                    // The dot modifier CANNOT be preceded by any modifiers. 
+                    if (recentModifier || key != DecimalExponentKey.none)
+                        return false;
+
+                    static if (allowUnderscores)
+                    {
+                        // If we allow underscores, the dot also CANNOT be preceded by any underscores.
+                        // It must be preceded by a number.
+                        if (recentUnderscore)
+                            return false;
+                    }
+
                     key = DecimalExponentKey.dot;
                     if (_expect(dot, false))
                         break;
                     dot = true;
                     if (str.length)
                     {
-                        static if (allowUnderscores)
-                        {
-                            recentUnderscore = true;
-                        }
+                        recentModifier = true;
                         continue;
                     }
                     static if (allowDotOnBounds)
@@ -2672,9 +2687,14 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                     case DecimalExponentKey.e:
                     case DecimalExponentKey.E:
                         import mir.parse: parse;
+                        // We don't really care if the e/E/d/D modifiers are preceded by a modifier,
+                        // so as long as they are a dot or a regular number.
+                        if (key != DecimalExponentKey.dot && key != DecimalExponentKey.none)
+                            return false;
                         key = cast(DecimalExponentKey)d;
                         if (parse(str, exponent) && str.length == 0)
                         {
+                            recentModifier = false;
                             if (t != 1)
                                 goto L;
                             goto M;
@@ -2684,6 +2704,10 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
                 static if (allowUnderscores)
                 {
                     case '_' - '0':
+                        // A underscore cannot be preceded by an underscore or a modifier.
+                        if (recentUnderscore || recentModifier)
+                            return false;
+                        
                         recentUnderscore = true;
                         if (str.length)
                             continue;
@@ -2693,6 +2717,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
             }
             break;
         }
+
         return false;
 
         static if (allowSpecialValues)

From 2c3747080c10d05f2c179c658d6158c6774865b4 Mon Sep 17 00:00:00 2001
From: hatf0 <harrison@0xcc.pw>
Date: Mon, 22 Nov 2021 11:23:33 -0500
Subject: [PATCH 290/573] Remove unused imports from recent features

---
 source/mir/base64.d                | 1 -
 source/mir/bignum/low_level_view.d | 1 -
 2 files changed, 2 deletions(-)

diff --git a/source/mir/base64.d b/source/mir/base64.d
index 9b754f93..d7f971ee 100644
--- a/source/mir/base64.d
+++ b/source/mir/base64.d
@@ -6,7 +6,6 @@ Authors: Harrison Ford
 Copyright: 2021 Harrison Ford, Symmetry Investments
 +/
 module mir.base64;
-import mir.ndslice.topology;
 
 package static immutable base64DecodeInvalidCharMsg = "Invalid character encountered.";
 package static immutable base64DecodeInvalidLenMsg = "Cannot decode a buffer with given length (not a multiple of 4, missing padding?)";
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index ac413e45..ef838f69 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2489,7 +2489,6 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
         scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
-        debug import std.stdio;
         import mir.utility: _expect;
 
         version(LDC)

From 78322f7b9dbf491c066254fd5446627a5ce9a2f7 Mon Sep 17 00:00:00 2001
From: hatf0 <harrison@0xcc.pw>
Date: Mon, 22 Nov 2021 19:04:58 -0500
Subject: [PATCH 291/573] Fix meson build

---
 meson.build | 1 +
 1 file changed, 1 insertion(+)

diff --git a/meson.build b/meson.build
index dc9a0b2c..cff5d488 100644
--- a/meson.build
+++ b/meson.build
@@ -15,6 +15,7 @@ sources_list = [
     'mir/annotated',
     'mir/appender',
     'mir/array/allocation',
+    'mir/base64',
     'mir/bignum/decimal',
     'mir/bignum/fixed',
     'mir/bignum/fp',

From e49ec3282441b7f26d49007b6a067337c4c4399f Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Sun, 5 Dec 2021 12:27:54 +0700
Subject: [PATCH 292/573] Fix  mir.array.allocation.array

---
 source/mir/appender.d         |  6 +++++-
 source/mir/array/allocation.d | 12 +++---------
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index 7e6c0b72..d50caa53 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -12,7 +12,11 @@ import mir.conv: _mir_destroy = xdestroy;
 private extern(C) @system nothrow @nogc pure void* memcpy(scope void* s1, scope const void* s2, size_t n);
 
 
-///
+/++
+The buffer uses stack memory and C Runtime to allocate temporal memory.
+
+Shouldn't store references to GC allocated data.
++/
 struct ScopedBuffer(T, size_t bytes = 4096)
     if (bytes && T.sizeof <= bytes)
 {
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index 40263aed..6fc6e330 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -112,10 +112,9 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !__traits(i
     }
     else
     {
-        import mir.appender: scopedBuffer;
-        import std.array: uninitializedArray;
+        import std.array: std_appender = appender;
 
-        auto a = scopedBuffer!(Unqual!E);
+        auto a = std_appender!(E[]);
 
         static if (isInputRange!Range)
             for (; !r.empty; r.popFront)
@@ -131,12 +130,7 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !__traits(i
             foreach (e; r)
                 a.put(forward!e);
         }
-
-        return () @trusted {
-            auto ret = uninitializedArray!(Unqual!E[])(a.length);
-            a.moveDataAndEmplaceTo(ret);
-            return ret;
-        } ();
+        return a.data;
     }
 }
 

From 73cbb22ed14313c6c22b180f47e64307a7945c9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Mon, 6 Dec 2021 15:02:37 +0100
Subject: [PATCH 293/573] Make String.opBinaryRight @safe when possible and
 qualify test as @safe (#377)

* Make String.opBinaryRight @safe when possible and qualify test as @safe

* Remove two more @system qualifiers
---
 source/mir/string_map.d | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index b7dbc44b..711561b2 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -13,7 +13,7 @@ Checks if the type is instance of $(LREF StringMap).
 +/
 enum isStringMap(T) = is(Unqual!T == StringMap!V, V);
 
-version(mir_test) 
+version(mir_test)
 ///
 unittest
 {
@@ -297,10 +297,10 @@ struct StringMap(T, U = uint)
     }
 
     /++
-    Reserves capacity for an associative array. 
+    Reserves capacity for an associative array.
     The capacity is the size that the underlaying slices can grow to before the underlying arrays may be reallocated or extended.
     +/
-    size_t reserve()(size_t newcapacity) @trusted pure nothrow 
+    size_t reserve()(size_t newcapacity) @trusted pure nothrow
     {
         import mir.utility: min;
 
@@ -355,7 +355,7 @@ struct StringMap(T, U = uint)
 
     Returns: The input is returned.
     +/
-    ref inout(typeof(this)) assumeSafeAppend()() @system nothrow inout return
+    ref inout(typeof(this)) assumeSafeAppend()() nothrow inout return
     {
         if (implementation)
         {
@@ -442,7 +442,7 @@ struct StringMap(T, U = uint)
 
     /++
     Finds position of the key in the associative array .
-    
+
     Return: An index starting from `0` that corresponds to the key or `-1` if the associative array doesn't contain the key.
 
     Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
@@ -481,7 +481,7 @@ struct StringMap(T, U = uint)
     /++
     Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
     +/
-    inout(T)* opBinaryRight(string op : "in")(scope const(char)[] key) @system pure nothrow @nogc inout
+    inout(T)* opBinaryRight(string op : "in")(scope const(char)[] key) pure nothrow @nogc inout
     {
         if (!implementation)
             return null;
@@ -491,12 +491,12 @@ struct StringMap(T, U = uint)
         assert (index < length);
         index = implementation.indices[index];
         assert (index < length);
-        return implementation._values + index;
+        return &implementation.values[index];
     }
 
     version(mir_test) static if (is(T == int))
     ///
-    @system nothrow pure unittest
+    @safe nothrow pure unittest
     {
         StringMap!double map;
         assert(("c" in map) is null);
@@ -712,7 +712,7 @@ struct StringMap(T, U = uint)
     }
 }
 
-version(mir_test) 
+version(mir_test)
 ///
 unittest
 {
@@ -888,17 +888,17 @@ private struct StructImpl(T, U = uint)
     {
         return _keys[0 .. _length];
     }
-    
+
     inout(T)[] values()() @trusted inout @property
     {
         return _values[0 .. _length];
     }
-    
+
     inout(U)[] indices()() @trusted inout @property
     {
         return _indices[0 .. _length];
     }
-    
+
     inout(U)[] lengthTable()() @trusted inout @property
     {
         return _lengthTable;
@@ -923,7 +923,7 @@ private struct StructImpl(T, U = uint)
             auto high = _lengthTable[key.length + 1] + 0u;
             while (low < high)
             {
-                auto mid = (low + high) / 2;
+                const mid = (low + high) / 2;
 
                 import core.stdc.string: memcmp;
                 int r = void;
@@ -950,7 +950,7 @@ private struct StructImpl(T, U = uint)
 }
 
 version(mir_test)
-unittest
+@safe unittest
 {
     import mir.algebraic_alias.json: JsonAlgebraic;
     import mir.string_map: StringMap;

From 7cef4f7ddf1f8a957c9a62e2c71b7a27fcd7eb36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Mon, 6 Dec 2021 16:08:46 +0100
Subject: [PATCH 294/573] Generalize StringMap.opEquals to other values (#378)

* Generalize StringMap.opEquals to other values

* Make StringMap.opEquals @safe along with test

* Use scalar comparison instead of array comparison in static assert
---
 source/mir/string_map.d | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 711561b2..7be67e44 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -41,13 +41,16 @@ struct StringMap(T, U = uint)
 
     ///
     // current implementation is workaround for linking bugs when used in self referencing algebraic types
-    bool opEquals(const StringMap rhs) const
+    bool opEquals(V)(scope const StringMap!(V, U) rhs) const
     {
+        // NOTE: moving this to template restriction fails with recursive template instanation
+        static assert(is(typeof(T.init == V.init) : bool),
+                      "Unsupported rhs of type " ~ typeof(rhs).stringof); // TODO:
         if (keys != rhs.keys)
             return false;
         if (implementation)
             foreach (const i; 0 .. implementation._length)
-                if (implementation._values[i] != rhs.implementation._values[i])
+                if (implementation.values[i] != rhs.implementation.values[i]) // needs `values` instead of `_values` to be @safe
                     return false;
         return true;
     }
@@ -738,6 +741,23 @@ unittest
     assert(table == table);
 }
 
+version(mir_test)
+///
+@safe unittest
+{
+    StringMap!int x;
+    x["L"] = 3;
+    x["A"] = 2;
+    x["val"] = 1;
+
+    StringMap!uint y;
+    y["L"] = 3;
+    y["A"] = 2;
+    y["val"] = 1;
+
+    assert(x == y);
+}
+
 private struct StructImpl(T, U = uint)
     if (!__traits(hasMember, T, "opPostMove") && __traits(isUnsigned, U))
 {

From c72c2d6ce568c819f3493bd3ab10d7eeadc8eee0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Tue, 7 Dec 2021 04:20:02 +0100
Subject: [PATCH 295/573] Move StructImpl into non-template StringMap.Impl
 reducing template-bloat (#380)

---
 source/mir/string_map.d | 422 ++++++++++++++++++++--------------------
 1 file changed, 210 insertions(+), 212 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 7be67e44..1af8ca9f 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -36,9 +36,6 @@ struct StringMap(T, U = uint)
     import core.lifetime: move;
     import mir.conv: emplaceRef;
 
-    private alias Impl = StructImpl!(T, U);
-    private Impl* implementation;
-
     ///
     // current implementation is workaround for linking bugs when used in self referencing algebraic types
     bool opEquals(V)(scope const StringMap!(V, U) rhs) const
@@ -713,260 +710,261 @@ struct StringMap(T, U = uint)
         int[string] aa = map.toAA;
         assert(aa["k"] == 1);
     }
-}
-
-version(mir_test)
-///
-unittest
-{
-    StringMap!int table;
-    table["L"] = 3;
-    table["A"] = 2;
-    table["val"] = 1;
-    assert(table.keys == ["L", "A", "val"]);
-    assert(table.values == [3, 2, 1]);
-    assert(table["A"] == 2);
-    table.values[2] += 10;
-    assert(table["A"] == 2);
-    assert(table["L"] == 3);
-    assert(table["val"] == 11);
-    assert(table.keys == ["L", "A", "val"]);
-    assert(table.values == [3, 2, 11]);
-    table.remove("A");
-    assert(table.keys == ["L", "val"]);
-    assert(table.values == [3, 11]);
-    assert(table["L"] == 3);
-    assert(table["val"] == 11);
-
-    assert(table == table);
-}
-
-version(mir_test)
-///
-@safe unittest
-{
-    StringMap!int x;
-    x["L"] = 3;
-    x["A"] = 2;
-    x["val"] = 1;
-
-    StringMap!uint y;
-    y["L"] = 3;
-    y["A"] = 2;
-    y["val"] = 1;
-
-    assert(x == y);
-}
 
-private struct StructImpl(T, U = uint)
-    if (!__traits(hasMember, T, "opPostMove") && __traits(isUnsigned, U))
-{
-    import core.lifetime: move;
-    import mir.conv: emplaceRef;
 
-    size_t _length;
-    string* _keys;
-    T* _values;
-    U* _indices;
-    U[] _lengthTable;
-
-    /++
-    +/
-    this()(string[] keys, T[] values) @trusted pure nothrow
+    private static struct Impl
     {
-        import mir.array.allocation: array;
-        import mir.ndslice.sorting: makeIndex;
-        import mir.ndslice.topology: iota, indexed;
-        import mir.string_table: smallerStringFirst;
+        import core.lifetime: move;
+        import mir.conv: emplaceRef;
 
-        assert(keys.length == values.length);
-        if (keys.length == 0)
-            return;
-        _length = keys.length;
-        _keys = keys.ptr;
-        _values = values.ptr;
-        _indices = keys.makeIndex!(U, smallerStringFirst).ptr;
-        auto sortedKeys = _keys.indexed(indices);
-        size_t maxKeyLength = sortedKeys[$ - 1].length;
-        _lengthTable = new U[maxKeyLength + 2];
-
-        size_t ski;
-        foreach (length; 0 .. maxKeyLength + 1)
-        {
-            while(ski < sortedKeys.length && sortedKeys[ski].length == length)
-                ski++;
-            _lengthTable[length + 1] = cast(U)ski;
-        }
-    }
+        size_t _length;
+        string* _keys;
+        T* _values;
+        U* _indices;
+        U[] _lengthTable;
 
-    void insertAt()(string key, T value, size_t i) @trusted
-    {
-        pragma(inline, false);
-
-        assert(i <= length);
-        {
-            auto a = keys;
-            a ~= key;
-            _keys = a.ptr;
-        }
+        /++
+         +/
+        this()(string[] keys, T[] values) @trusted pure nothrow
         {
-            auto a = values;
-            a ~= move(value);
-            _values = a.ptr;
-        }
-        {
-            auto a = indices;
-            a ~= 0;
-            _indices = a.ptr;
-
-            if (__ctfe)
-            {
-                foreach_reverse (idx; i .. length)
-                {
-                    _indices[idx + 1] = _indices[idx];
-                }
-            }
-            else
+            import mir.array.allocation: array;
+            import mir.ndslice.sorting: makeIndex;
+            import mir.ndslice.topology: iota, indexed;
+            import mir.string_table: smallerStringFirst;
+
+            assert(keys.length == values.length);
+            if (keys.length == 0)
+                return;
+            _length = keys.length;
+            _keys = keys.ptr;
+            _values = values.ptr;
+            _indices = keys.makeIndex!(U, smallerStringFirst).ptr;
+            auto sortedKeys = _keys.indexed(indices);
+            size_t maxKeyLength = sortedKeys[$ - 1].length;
+            _lengthTable = new U[maxKeyLength + 2];
+
+            size_t ski;
+            foreach (length; 0 .. maxKeyLength + 1)
             {
-                import core.stdc.string: memmove;
-                memmove(_indices + i + 1, _indices + i, (length - i) * U.sizeof);
+                while(ski < sortedKeys.length && sortedKeys[ski].length == length)
+                    ski++;
+                _lengthTable[length + 1] = cast(U)ski;
             }
-            assert(length <= U.max);
-            _indices[i] = cast(U)length;
-            _length++;
         }
+
+        void insertAt()(string key, T value, size_t i) @trusted
         {
-            if (key.length + 2 <= lengthTable.length)
+            pragma(inline, false);
+
+            assert(i <= length);
             {
-                ++lengthTable[key.length + 1 .. $];
+                auto a = keys;
+                a ~= key;
+                _keys = a.ptr;
             }
-            else
             {
-                auto oldLen = _lengthTable.length;
-                _lengthTable.length = key.length + 2;
-                auto oldVal = oldLen ? _lengthTable[oldLen - 1] : 0;
-                _lengthTable[oldLen .. key.length + 1] =  oldVal;
-                _lengthTable[key.length + 1] =  oldVal + 1;
+                auto a = values;
+                a ~= move(value);
+                _values = a.ptr;
             }
-        }
-    }
-
-    void removeAt()(size_t i)
-    {
-        assert(i < length);
-        auto j = _indices[i];
-        assert(j < length);
-        {
-            --_lengthTable[_keys[j].length + 1 .. $];
-        }
-        {
-            if (__ctfe)
             {
-                foreach (idx; i .. length)
+                auto a = indices;
+                a ~= 0;
+                _indices = a.ptr;
+
+                if (__ctfe)
                 {
-                    _indices[idx] = _indices[idx + 1];
-                    _indices[idx] = _indices[idx + 1];
+                    foreach_reverse (idx; i .. length)
+                    {
+                        _indices[idx + 1] = _indices[idx];
+                    }
                 }
+                else
+                {
+                    import core.stdc.string: memmove;
+                    memmove(_indices + i + 1, _indices + i, (length - i) * U.sizeof);
+                }
+                assert(length <= U.max);
+                _indices[i] = cast(U)length;
+                _length++;
             }
-            else
             {
-                import core.stdc.string: memmove;
-                memmove(_indices + i, _indices + i + 1, (length - 1 - i) * U.sizeof);
+                if (key.length + 2 <= lengthTable.length)
+                {
+                    ++lengthTable[key.length + 1 .. $];
+                }
+                else
+                {
+                    auto oldLen = _lengthTable.length;
+                    _lengthTable.length = key.length + 2;
+                    auto oldVal = oldLen ? _lengthTable[oldLen - 1] : 0;
+                    _lengthTable[oldLen .. key.length + 1] =  oldVal;
+                    _lengthTable[key.length + 1] =  oldVal + 1;
+                }
             }
-            foreach (ref elem; indices[0 .. $ - 1])
-                if (elem > j)
-                    elem--;
         }
+
+        void removeAt()(size_t i)
         {
-            if (__ctfe)
+            assert(i < length);
+            auto j = _indices[i];
+            assert(j < length);
             {
-                foreach_reverse (idx; j .. length - 1)
+                --_lengthTable[_keys[j].length + 1 .. $];
+            }
+            {
+                if (__ctfe)
+                {
+                    foreach (idx; i .. length)
+                    {
+                        _indices[idx] = _indices[idx + 1];
+                        _indices[idx] = _indices[idx + 1];
+                    }
+                }
+                else
                 {
-                    _keys[idx] = _keys[idx + 1];
-                    _values[idx] = move(_values[idx + 1]);
+                    import core.stdc.string: memmove;
+                    memmove(_indices + i, _indices + i + 1, (length - 1 - i) * U.sizeof);
                 }
+                foreach (ref elem; indices[0 .. $ - 1])
+                    if (elem > j)
+                        elem--;
             }
-            else
             {
-                import core.stdc.string: memmove;
-                destroy!false(_values[j]);
-                memmove(_keys + j, _keys + j + 1, (length - 1 - j) * string.sizeof);
-                memmove(_values + j, _values + j + 1, (length - 1 - j) * T.sizeof);
-                emplaceRef(_values[length - 1]);
+                if (__ctfe)
+                {
+                    foreach_reverse (idx; j .. length - 1)
+                    {
+                        _keys[idx] = _keys[idx + 1];
+                        _values[idx] = move(_values[idx + 1]);
+                    }
+                }
+                else
+                {
+                    import core.stdc.string: memmove;
+                    destroy!false(_values[j]);
+                    memmove(_keys + j, _keys + j + 1, (length - 1 - j) * string.sizeof);
+                    memmove(_values + j, _values + j + 1, (length - 1 - j) * T.sizeof);
+                    emplaceRef(_values[length - 1]);
+                }
             }
+            _length--;
+            _lengthTable = _lengthTable[0 .. length ? _keys[_indices[length - 1]].length + 2 : 0];
         }
-        _length--;
-        _lengthTable = _lengthTable[0 .. length ? _keys[_indices[length - 1]].length + 2 : 0];
-    }
-
-    size_t length()() @safe pure nothrow @nogc const @property
-    {
-        return _length;
-    }
 
-    inout(string)[] keys()() @trusted inout @property
-    {
-        return _keys[0 .. _length];
-    }
-
-    inout(T)[] values()() @trusted inout @property
-    {
-        return _values[0 .. _length];
-    }
+        size_t length()() @safe pure nothrow @nogc const @property
+        {
+            return _length;
+        }
 
-    inout(U)[] indices()() @trusted inout @property
-    {
-        return _indices[0 .. _length];
-    }
+        inout(string)[] keys()() @trusted inout @property
+        {
+            return _keys[0 .. _length];
+        }
 
-    inout(U)[] lengthTable()() @trusted inout @property
-    {
-        return _lengthTable;
-    }
+        inout(T)[] values()() @trusted inout @property
+        {
+            return _values[0 .. _length];
+        }
 
-    auto sortedKeys()() @trusted const @property
-    {
-        import mir.ndslice.topology: indexed;
-        return _keys.indexed(indices);
-    }
+        inout(U)[] indices()() @trusted inout @property
+        {
+            return _indices[0 .. _length];
+        }
 
-    bool findIndex()(scope const(char)[] key, ref size_t index) @trusted pure nothrow @nogc const
-    {
-        import mir.utility: _expect;
-        if (_expect(key.length + 1 < _lengthTable.length, true))
+        inout(U)[] lengthTable()() @trusted inout @property
         {
+            return _lengthTable;
+        }
 
-            // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-            // 0 1 2 3 4 5 6   8 9 10    12          16
+        auto sortedKeys()() @trusted const @property
+        {
+            import mir.ndslice.topology: indexed;
+            return _keys.indexed(indices);
+        }
 
-            auto low = _lengthTable[key.length] + 0u;
-            auto high = _lengthTable[key.length + 1] + 0u;
-            while (low < high)
+        bool findIndex()(scope const(char)[] key, ref size_t index) @trusted pure nothrow @nogc const
+        {
+            import mir.utility: _expect;
+            if (_expect(key.length + 1 < _lengthTable.length, true))
             {
-                const mid = (low + high) / 2;
 
-                import core.stdc.string: memcmp;
-                int r = void;
+                // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+                // 0 1 2 3 4 5 6   8 9 10    12          16
 
-                if (__ctfe)
-                    r = __cmp(key, _keys[_indices[mid]]);
-                else
-                    r = memcmp(key.ptr, _keys[_indices[mid]].ptr, key.length);
-
-                if (r == 0)
+                auto low = _lengthTable[key.length] + 0u;
+                auto high = _lengthTable[key.length + 1] + 0u;
+                while (low < high)
                 {
-                    index = mid;
-                    return true;
+                    const mid = (low + high) / 2;
+
+                    import core.stdc.string: memcmp;
+                    int r = void;
+
+                    if (__ctfe)
+                        r = __cmp(key, _keys[_indices[mid]]);
+                    else
+                        r = memcmp(key.ptr, _keys[_indices[mid]].ptr, key.length);
+
+                    if (r == 0)
+                    {
+                        index = mid;
+                        return true;
+                    }
+                    if (r > 0)
+                        low = mid + 1;
+                    else
+                        high = mid;
                 }
-                if (r > 0)
-                    low = mid + 1;
-                else
-                    high = mid;
+                index = low;
             }
-            index = low;
+            return false;
         }
-        return false;
     }
+    private Impl* implementation;
+}
+
+version(mir_test)
+///
+unittest
+{
+    StringMap!int table;
+    table["L"] = 3;
+    table["A"] = 2;
+    table["val"] = 1;
+    assert(table.keys == ["L", "A", "val"]);
+    assert(table.values == [3, 2, 1]);
+    assert(table["A"] == 2);
+    table.values[2] += 10;
+    assert(table["A"] == 2);
+    assert(table["L"] == 3);
+    assert(table["val"] == 11);
+    assert(table.keys == ["L", "A", "val"]);
+    assert(table.values == [3, 2, 11]);
+    table.remove("A");
+    assert(table.keys == ["L", "val"]);
+    assert(table.values == [3, 11]);
+    assert(table["L"] == 3);
+    assert(table["val"] == 11);
+
+    assert(table == table);
+}
+
+version(mir_test)
+///
+@safe unittest
+{
+    StringMap!int x;
+    x["L"] = 3;
+    x["A"] = 2;
+    x["val"] = 1;
+
+    StringMap!uint y;
+    y["L"] = 3;
+    y["A"] = 2;
+    y["val"] = 1;
+
+    assert(x == y);
 }
 
 version(mir_test)

From f4d0a9464149c6f1881eeddafc726126631014f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Tue, 7 Dec 2021 11:22:53 +0100
Subject: [PATCH 296/573] Add String.opEquals(AA) (#379)

* Limit imports of emplaceRef

* Add String.opEquals(AA)

* Make opEquals tests more extensive

* Fix syntax error

* Adjust logic for computing index in StringMap.opEquals

* Improve opEquals test to increase coverage

* Add more asserts

* Correct comment

* Simplify opEquals

* Qualify K and V part of opEquals(AA) as const

* Simplify StringMap.opEquals to use array comparison

* Simplify StringMap.opEquals further
---
 source/mir/string_map.d | 65 ++++++++++++++++++++++++++++++++---------
 1 file changed, 52 insertions(+), 13 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 1af8ca9f..b09ec483 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -42,7 +42,7 @@ struct StringMap(T, U = uint)
     {
         // NOTE: moving this to template restriction fails with recursive template instanation
         static assert(is(typeof(T.init == V.init) : bool),
-                      "Unsupported rhs of type " ~ typeof(rhs).stringof); // TODO:
+                      "Unsupported rhs of type " ~ typeof(rhs).stringof);
         if (keys != rhs.keys)
             return false;
         if (implementation)
@@ -51,6 +51,27 @@ struct StringMap(T, U = uint)
                     return false;
         return true;
     }
+    /// ditto
+    bool opEquals(K, V)(scope const const(V)[const(K)] rhs) const
+    if (is(typeof(K.init == string.init) : bool) &&
+        is(typeof(V.init == T.init) : bool))
+    {
+        if (implementation is null)
+            return rhs.length == 0;
+        if (implementation._length != rhs.length)
+            return false;
+        foreach (const i; 0 .. implementation._length)
+        {
+            if (const valuePtr = implementation.keys[i] in rhs)
+            {
+                if (*valuePtr != implementation.values[i])
+                    return false;
+            }
+            else
+                return false;
+        }
+        return true;
+    }
 
     // // linking bug
     // version(none)
@@ -658,7 +679,7 @@ struct StringMap(T, U = uint)
     }
 
     /++
-    Converts the associtave array to a common Dlang associative array.
+    Converts to a builtin associative array.
 
     Complexity: `O(n)`.
     +/
@@ -711,7 +732,6 @@ struct StringMap(T, U = uint)
         assert(aa["k"] == 1);
     }
 
-
     private static struct Impl
     {
         import core.lifetime: move;
@@ -954,17 +974,36 @@ version(mir_test)
 ///
 @safe unittest
 {
-    StringMap!int x;
-    x["L"] = 3;
-    x["A"] = 2;
-    x["val"] = 1;
-
-    StringMap!uint y;
-    y["L"] = 3;
-    y["A"] = 2;
-    y["val"] = 1;
+    static void testEquals(X, Y)()
+    {
+        X x;
+        Y y;
+        assert(x == y);
+
+        x["L"] = 3;
+        assert(x != y);
+        x["A"] = 2;
+        assert(x != y);
+        x["val"] = 1;
+        assert(x != y);
+
+        y["L"] = 3;
+        assert(x != y);
+        y["A"] = 2;
+        assert(x != y);
+        y["val"] = 1;
+        assert(x == y);
+
+        x = X.init;
+        assert(x != y);
+
+        y = Y.init;
+        assert(x == y);
+    }
 
-    assert(x == y);
+    testEquals!(StringMap!int, StringMap!uint)();
+    testEquals!(StringMap!int, uint[string])();
+    testEquals!(uint[string], StringMap!int)();
 }
 
 version(mir_test)

From b21ff3232c963d62d97fc09207d45707ea93f70f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Tue, 7 Dec 2021 12:46:19 +0100
Subject: [PATCH 297/573] Make StringMap.opEquals independent of insert order
 of elements in this and rhs (#384)

---
 source/mir/string_map.d | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index b09ec483..4331f4fd 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -38,17 +38,21 @@ struct StringMap(T, U = uint)
 
     ///
     // current implementation is workaround for linking bugs when used in self referencing algebraic types
-    bool opEquals(V)(scope const StringMap!(V, U) rhs) const
+    bool opEquals(V)(scope const StringMap!(V, U) rhs) const @trusted
     {
         // NOTE: moving this to template restriction fails with recursive template instanation
         static assert(is(typeof(T.init == V.init) : bool),
                       "Unsupported rhs of type " ~ typeof(rhs).stringof);
-        if (keys != rhs.keys)
+        if (implementation is null)
+            return rhs.length == 0;
+        if (rhs.implementation is null)
+            return length == 0;
+        if (implementation._length != rhs.implementation._length)
             return false;
-        if (implementation)
-            foreach (const i; 0 .. implementation._length)
-                if (implementation.values[i] != rhs.implementation.values[i]) // needs `values` instead of `_values` to be @safe
-                    return false;
+        foreach (const i, const index; implementation.indices)
+            if (implementation._keys[index] != rhs.implementation._keys[rhs.implementation._indices[i]] ||
+                implementation._values[index] != rhs.implementation._values[rhs.implementation._indices[i]])
+                return false;
         return true;
     }
     /// ditto
@@ -987,11 +991,11 @@ version(mir_test)
         x["val"] = 1;
         assert(x != y);
 
+        y["val"] = 1;
+        assert(x != y);
         y["L"] = 3;
         assert(x != y);
         y["A"] = 2;
-        assert(x != y);
-        y["val"] = 1;
         assert(x == y);
 
         x = X.init;

From e669f3161cd59918f913477f8332cc7f385f3ccf Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Tue, 7 Dec 2021 20:01:44 +0700
Subject: [PATCH 298/573] Rework comparison for (Slim)RCPtr and interpolators

---
 source/mir/interpolate/constant.d | 10 +++++++
 source/mir/interpolate/generic.d  | 10 +++++++
 source/mir/interpolate/linear.d   | 10 +++++++
 source/mir/interpolate/spline.d   | 10 +++++++
 source/mir/rc/ptr.d               | 45 ++++++++++++++++++++++++++-----
 source/mir/rc/slim_ptr.d          | 28 ++++++++++++++++---
 6 files changed, 102 insertions(+), 11 deletions(-)

diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index a452e50d..56180653 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -84,6 +84,16 @@ struct Constant(F, size_t N = 1, X = F)
 
 extern(D):
 
+    bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
+    {
+        if (rhs._data != this._data)
+            return false;
+        static foreach (d; 0 .. N)
+            if (gridScopeView!d != rhs.gridScopeView!d)
+                return false;
+        return true;
+    }
+
     /++
     +/
     this(Repeat!(N, Slice!(RCI!(immutable X))) grid, Slice!(RCI!(const F), N) data) @safe @nogc
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index 63f0a11b..3fc2f503 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -103,6 +103,16 @@ struct Generic(X, F)
     /// Grid iterators. $(RED For internal use.)
     RCI!(immutable X) _grid;
 
+    bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
+    {
+        if (rhs._data != this._data)
+            return false;
+        static foreach (d; 0 .. 1)
+            if (gridScopeView!d != rhs.gridScopeView!d)
+                return false;
+        return true;
+    }
+
 extern(D):
 
     /++
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index e1725d8f..8701fb8c 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -184,6 +184,16 @@ struct Linear(F, size_t N = 1, X = F)
 
 @optmath extern(D):
 
+    bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
+    {
+        if (rhs._data != this._data)
+            return false;
+        static foreach (d; 0 .. N)
+            if (gridScopeView!d != rhs.gridScopeView!d)
+                return false;
+        return true;
+    }
+
     /++
     +/
     this(Repeat!(N, Slice!(RCI!(immutable X))) grid, Slice!(RCI!(const F), N) data) @safe @nogc
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 75f136ca..6e124816 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -619,6 +619,16 @@ struct Spline(F, size_t N = 1, X = F)
 
 @fmamath extern(D):
 
+    bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
+    {
+        if (rhs._data != this._data)
+            return false;
+        static foreach (d; 0 .. N)
+            if (gridScopeView!d != rhs.gridScopeView!d)
+                return false;
+        return true;
+    }
+
     /++
     +/
     this(Repeat!(N, Slice!(RCI!(immutable X))) grid) @safe @nogc
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index f393fedf..22f8d7b9 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -109,19 +109,29 @@ struct mir_rcptr(T)
     /// ditto
     bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
     {
-        return _thisPtr == rhs._thisPtr;
+        if (_thisPtr is null)
+            return rhs._thisPtr is null;
+        if (rhs._thisPtr is null)
+            return false;
+        return _get_value == rhs._get_value;
     }
 
     ///
-    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
+    auto opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
     {
-        return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
+        if (_thisPtr is null)
+            return (rhs._thisPtr is null) - 1;
+        if (rhs._thisPtr is null)
+            return 1;
+        return _get_value.opCmp(rhs._get_value);
     }
 
     ///
     size_t toHash() @trusted scope const pure nothrow @nogc
     {
-        return cast(size_t) _thisPtr;
+        if (_thisPtr is null)
+            return 0;
+        return hashOf(_get_value);
     }
 
     ///
@@ -331,6 +341,11 @@ unittest
     static class C
     {
         int index = 34;
+
+        override size_t toHash() const scope @safe pure nothrow @nogc
+        {
+            return index;
+        }
     }
     assert(createRC!C.index == 34);
 }
@@ -340,13 +355,29 @@ version(mir_test)
 @safe pure @nogc nothrow
 unittest
 {
-    static interface I { ref double bar() @safe pure nothrow @nogc; }
-    static abstract class D { int index; }
+    static interface I {
+        ref double bar() @safe pure nothrow @nogc;
+        size_t toHash() @trusted scope const pure nothrow @nogc;
+    }
+    static abstract class D
+    {
+        int index;
+
+        override size_t toHash() const scope @safe pure nothrow @nogc
+        {
+            return index;
+        }
+    }
     static class C : D, I
     {
         double value;
         ref double bar() @safe pure nothrow @nogc { return value; }
-        this(double d) { value = d; }
+        this(double d){ value = d; }
+
+        override size_t toHash() const scope @safe pure nothrow @nogc
+        {
+            return hashOf(value, super.toHash);
+        }
     }
     auto a = createRC!C(10);
     assert(a._counter == 1);
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index ae567038..a1717ce9 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -103,19 +103,29 @@ struct mir_slim_rcptr(T)
     /// ditto
     bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc
     {
-        return _thisPtr == rhs._thisPtr;
+        if (_thisPtr is null)
+            return rhs._thisPtr is null;
+        if (rhs._thisPtr is null)
+            return false;
+        return _get_value == rhs._get_value;
     }
 
     ///
-    sizediff_t opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
+    auto opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc
     {
-        return cast(void*)_thisPtr - cast(void*)rhs._thisPtr;
+        if (_thisPtr is null)
+            return (rhs._thisPtr is null) - 1;
+        if (rhs._thisPtr is null)
+            return 1;
+        return _get_value.opCmp(rhs._get_value);
     }
 
     ///
     size_t toHash() @trusted scope const pure nothrow @nogc
     {
-        return cast(size_t) _thisPtr;
+        if (_thisPtr is null)
+            return 0;
+        return hashOf(_get_value);
     }
 
     ///
@@ -249,6 +259,11 @@ unittest
         double value;
         ref double bar() @safe pure nothrow @nogc { return value; }
         this(double d) { value = d; }
+
+        override size_t toHash() const scope @safe pure nothrow @nogc
+        {
+            return index;
+        }
     }
     auto a = createSlimRC!C(10);
     assert(a._counter == 1);
@@ -292,6 +307,11 @@ unittest
     static class C
     {
         int index = 34;
+
+        override size_t toHash() const scope @safe pure nothrow @nogc
+        {
+            return index;
+        }
     }
     assert(createSlimRC!C.index == 34);
 }

From ed7a3003a315b6f3a9c2cc45ddb1279e17d556f5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 14 Dec 2021 14:12:23 +0700
Subject: [PATCH 299/573] Add toUnixTime, fromUnixTime for timestamp

---
 source/mir/timestamp.d | 91 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 77 insertions(+), 14 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 406d5ca4..23b094c3 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -108,13 +108,13 @@ struct Timestamp
             return _offset >> 1;
         }
 
-        /++
-        Returns: true if timezone has offset
-        +/
-        bool hasOffset() const @safe pure nothrow @nogc @property
-        {
-            return _offset & 1;
-        }
+        // /++
+        // Returns: true if timezone has offset
+        // +/
+        // bool hasOffset() const @safe pure nothrow @nogc @property
+        // {
+        //     return _offset & 1;
+        // }
     }
 
 @serdeIgnore:
@@ -361,8 +361,12 @@ struct Timestamp
     this(SysTime)(const SysTime systime)
         if (SysTime.stringof == "SysTime")
     {
-        with(systime.toUTC) this(year, month, day, hour, minute, second, -7, fracSecs.total!"hnsecs");
-        offset = cast(short) systime.utcOffset.total!"minutes";
+        auto utcTime = systime.toUTC;
+        this = fromUnixTime(utcTime.toUnixTime);
+        this.fractionExponent = -7;
+        this.fractionCoefficient = utcTime.fracSecs.total!"hnsecs";
+        this.precision = Precision.fraction;
+        this.offset = cast(short) systime.utcOffset.total!"minutes";
     }
 
     ///
@@ -576,7 +580,7 @@ struct Timestamp
             import std.datetime.date: DateTime;
             import std.datetime.systime: SysTime;
             import std.datetime.timezone: UTC, SimpleTimeZone;
-            auto ret = SysTime(DateTime(year, month, day, hour, minute, second), getPhobosFraction.hnsecs, UTC());
+            auto ret = SysTime.fromUnixTime(toUnixTime, UTC()) + getFraction!7.hnsecs;
             if (offset)
             {
                 ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes));
@@ -588,7 +592,7 @@ struct Timestamp
         {
             if (!isDuration)
                 throw ExpectedDuration;
-            auto coeff = ((((long(year) * 7 + month) * 24 + hour) * 60 + minute) * 60 + second) * 10_000_000 + getPhobosFraction;
+            auto coeff = ((((long(year) * 7 + month) * 24 + hour) * 60 + minute) * 60 + second) * 10_000_000 + getFraction!7;
             if (isNegativeDuration)
                 coeff = -coeff;
 
@@ -598,19 +602,22 @@ struct Timestamp
         }
     }
 
-    private long getPhobosFraction() @property const @safe pure nothrow @nogc
+    /++
+    +/
+    long getFraction(int digits)() @property const @safe pure nothrow @nogc
+        if (digits >= 1 && digits <= 12)
     {
         long coeff;
         if (fractionCoefficient)
         {
             coeff = fractionCoefficient;
             int exp = fractionExponent;
-            while (exp > -7)
+            while (exp > -digits)
             {
                 exp--;
                 coeff *= 10;
             }
-            while (exp < -7)
+            while (exp < -digits)
             {
                 exp++;
                 coeff /= 10;
@@ -788,6 +795,62 @@ struct Timestamp
         assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOString == "T074030.056Z");
     }
 
+    ///
+    long toUnixTime() const @safe pure nothrow @nogc
+    {
+        import mir.date: Date;
+        long result;
+        if (!isDuration && !isOnlyTime)
+        {
+            enum fistDay = Date.trustedCreate(1970, 1, 1);
+            result = Date.trustedCreate(year, month ? month : 1, day ? day : 1) - fistDay;
+            result *= 24;
+        }
+        result += hour;
+        result *= 60;
+        result += minute;
+        result *= 60;
+        result += second;
+        return result;
+    }
+
+    ///
+    @safe pure nothrow @nogc unittest
+    {
+        assert(Timestamp(1970, 1, 1).toUnixTime == 0);
+        assert(Timestamp(2007, 12, 22, 8, 14, 45).toUnixTime == 1_198_311_285);
+        assert(Timestamp(2007, 12, 22, 8, 14, 45).withOffset(90).toUnixTime == 1_198_311_285);
+        assert(Timestamp(1969, 12, 31, 23, 59, 59).toUnixTime == -1);
+    }
+
+    ///
+    static Timestamp fromUnixTime(long time) @safe pure nothrow @nogc
+    {
+        import mir.date: Date;
+        auto days = time / (24 * 60 * 60); 
+        if (time < 0)
+            days--;
+        time -= days * (24 * 60 * 60);
+        assert(time >= 0);
+        auto second = time % 60;
+        time /= 60;
+        auto minute = time % 60;
+        time /= 60;
+        auto hour = cast(ubyte) time;
+        enum fistDay = Date.trustedCreate(1970, 1, 1);
+        with((fistDay + cast(int)days).yearMonthDay)
+            return Timestamp(year, cast(ubyte)month, day, hour, cast(ubyte)minute, cast(ubyte)second);
+    }
+
+    ///
+    @safe pure nothrow @nogc unittest
+    {
+        import mir.format;
+        assert(Timestamp.fromUnixTime(0) == Timestamp(1970, 1, 1, 0, 0, 0));
+        assert(Timestamp.fromUnixTime(1_198_311_285) == Timestamp(2007, 12, 22, 8, 14, 45));
+        assert(Timestamp.fromUnixTime(-1) == Timestamp(1969, 12, 31, 23, 59, 59));
+    }
+
     /// Helpfer for time zone offsets
     void addMinutes(short minutes) @safe pure nothrow @nogc
     {

From f365e153f517fae3e74f2b8650634d589ecedb90 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 15 Dec 2021 12:10:11 +0700
Subject: [PATCH 300/573] make _currentLength in the ScopedBuffer public

---
 source/mir/appender.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index d50caa53..7f8299d8 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -26,7 +26,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
 
     private enum size_t _bufferLength =  bytes / T.sizeof + (bytes % T.sizeof != 0);
     private T[] _buffer;
-    private size_t _currentLength;
+    size_t _currentLength;
 
     version (mir_secure_memory)
         private align(T.alignof) ubyte[_bufferLength * T.sizeof] _scopeBufferPayload;

From 05abcc59582328328bd8dd95cfbfa8758b316d93 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 17 Dec 2021 00:45:53 +0700
Subject: [PATCH 301/573] fix timestamp bug

---
 source/mir/timestamp.d | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 23b094c3..b8046cff 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -63,6 +63,8 @@ struct Timestamp
         fraction,
     }
 
+@serdeIgnore:
+
     ///
     this(scope const(char)[] str) @safe pure @nogc
     {
@@ -117,8 +119,6 @@ struct Timestamp
         // }
     }
 
-@serdeIgnore:
-
     /++
     Year
     +/
@@ -828,9 +828,12 @@ struct Timestamp
     {
         import mir.date: Date;
         auto days = time / (24 * 60 * 60); 
+        time -= days * (24 * 60 * 60);
         if (time < 0)
+        {
             days--;
-        time -= days * (24 * 60 * 60);
+            time += 24 * 60 * 60;
+        }
         assert(time >= 0);
         auto second = time % 60;
         time /= 60;
@@ -1442,3 +1445,19 @@ struct Timestamp
         return day == 99;
     }
 }
+
+version(mir_test)
+unittest
+{
+    long sec = -2208988800;
+    uint nanosec = 0;
+    auto ts = Timestamp.fromUnixTime(sec);
+    if (nanosec >= 0)
+    {
+        ts.precision = Timestamp.Precision.fraction;
+        ts.fractionCoefficient = nanosec;
+        ts.fractionExponent = -9;
+    }
+    auto ts2 = "1900-01-01T00:00:00.000000000Z".Timestamp;
+    assert(ts == ts2);
+}

From dcbcf703b50d381099407b7bdb73ce51ecef33a1 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 30 Dec 2021 13:44:02 +0700
Subject: [PATCH 302/573] add mir.numeric.integrate

---
 source/mir/numeric.d | 209 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 208 insertions(+), 1 deletion(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index b4a9655c..241758d7 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1280,7 +1280,7 @@ do
         i++;
         T m = (a + b) * 0.5f;
         // This fix is not part of the original algorithm
-        if (!((m.fabs < T.infinity))) // fix infinity loop. Issue can be reproduced in R.
+        if (!(m.fabs < T.infinity)) // fix infinity loop. Issue can be reproduced in R.
         {
             m = a.half + b.half;
         }
@@ -1559,3 +1559,210 @@ struct DiffResult(T)
     ///
     T error = 0;
 }
+
+/++
+Integrates function on the interval `[a, b]` using adaptive Gauss-Lobatto algorithm.
+
+References:
+    W. Gander and W. Gautschi. Adaptive Quadrature — Revisited
+
+Params:
+    f = function to integrate. `f` should be valid on interval `[a, b]` including the bounds.
+    a = finite left interval bound
+    b = finite right interval bound
+    tolerance = (optional) relative tolerance should be greater or equal to `T.epsilon`
+
+Returns:
+    Integral value
++/
+T integrate(alias f, T)(const T a, const T b, const T tolerance = T.epsilon)
+    if (__traits(isFloating, T))
+{
+    if (false) // break attributes
+        T y = f(T(1));
+    scope funInst = delegate(T x) {
+        return T(f(x));
+    };
+    scope fun = funInst.trustedAllAttr;
+    return integrateImpl(fun, a, b, tolerance);
+}
+
+/// ditto
+@optmath
+T integrateImpl(T)(
+    scope const T delegate(T) @safe pure nothrow @nogc f,
+    const T a,
+    const T b,
+    const T tolerance = T.epsilon,
+) @safe pure nothrow @nogc
+    if (__traits(isFloating, T))
+in {
+    assert(-T.infinity < a);
+    assert(b < +T.infinity);
+    assert(a < b);
+    assert(tolerance >= T.epsilon);
+}
+do {
+    pragma(inline, false);
+    enum T alpha = 0.816496580927726032732428024901963797322;
+    enum T beta = 0.447213595499957939281834733746255247088;
+    enum T x1 = 0.94288241569947971905635175843185720232;
+    enum T x2 = 0.64185334234978130978123554132903188394;
+    enum T x3 = 0.23638319966214988028222377349205292599;
+    enum T A = 0.015827191973480183087169986733305510591;
+    enum T B = 0.094273840218850045531282505077108171960;
+    enum T C = 0.19507198733658539625363597980210298680;
+    enum T D = 0.18882157396018245442000533937297167125;
+    enum T E = 0.19977340522685852679206802206648840246;
+    enum T F = 0.22492646533333952701601768799639508076;
+    enum T G = 0.24261107190140773379964095790325635233;
+    enum T A1 = 77 / 1470.0L;
+    enum T B1 = 432 / 1470.0L;
+    enum T C1 = 625 / 1470.0L;
+    enum T D1 = 672 / 1470.0L;
+    enum T A2 = 1 / 6.0L;
+    enum T B2 = 5 / 6.0L;
+
+    T m = (a + b) * 0.5f;
+    // This fix is not part of the original algorithm
+    if (!(m.fabs < T.infinity))
+    {
+        m = a.half + b.half;
+    }
+    T h = (b - a) * 0.5f;
+    // This fix is not part of the original algorithm
+    if (!(h.fabs < T.infinity))
+    {
+        h = b.half - a.half;
+    }
+    T fa = f(a);
+    T fb = f(b);
+    T[7] y = [
+        fa + fb,
+        f(m - x1 * h) + f(m + x1 * h),
+        f(m - alpha * h) + f(m + alpha * h),
+        f(m - x2 * h) + f(m + x2 * h),
+        f(m - beta * h) + f(m + beta * h),
+        f(m - x3 * h) + f(m + x3 * h),
+        f(m),
+    ];
+    T i2 = h * (
+        + A2 * y[0]
+        + B2 * y[4]
+    );
+    T i1 = h * (
+        + A1 * y[0]
+        + B1 * y[2]
+        + C1 * y[4]
+        + D1 * y[6]
+    );
+    T si = h * (
+        + A * y[0]
+        + B * y[1]
+        + C * y[2]
+        + D * y[3]
+        + E * y[4]
+        + F * y[5]
+        + G * y[6]
+    );
+    T erri1 = fabs(i1 - si);
+    T erri2 = fabs(i2 - si);
+    T R = erri1 / erri2;
+    T tol = tolerance;
+    if (tol < T.epsilon)
+        tol = T.epsilon;
+    if (0 < R && R < 1)
+        tol /= R;
+    si *= tol / T.epsilon;
+    if (si == 0)
+        si = b - a;
+    
+    if (!(si.fabs < T.infinity))
+        return T.nan;
+
+    T step(const T a, const T b, const T fa, const T fb)
+    {
+        T m = (a + b) * 0.5f;
+        // This fix is not part of the original algorithm
+        if (!(m.fabs < T.infinity))
+        {
+            m = a.half + b.half;
+        }
+        T h = (b - a) * 0.5f;
+        if (!(h.fabs < T.infinity))
+        {
+            h = b.half - a.half;
+        }
+        T[5] x = [
+            m - alpha * h,
+            m - beta * h,
+            m,
+            m + beta * h,
+            m + alpha * h,
+        ];
+        T[5] y = [
+            f(x[0]),
+            f(x[1]),
+            f(x[2]),
+            f(x[3]),
+            f(x[4]),
+        ];
+        T i2 = h * (
+            + A2 * (fa + fb)
+            + B2 * (y[1] + y[3])
+        );
+        T i1 = h * (
+            + A1 * (fa + fb)
+            + B1 * (y[0] + y[4])
+            + C1 * (y[1] + y[3])
+            + D1 * y[2]
+        );
+        if (!(i1.fabs < T.infinity) || (si + (i1 - i2) == si) || !(a < x[0]) || !(x[4] < b))
+        {
+            return i1;
+        }
+        return
+             +(step(   a, x[0],   fa, y[0])
+             + step(x[0], x[1], y[0], y[1]))
+             +(step(x[1], x[2], y[1], y[2])
+             + step(x[2], x[3], y[2], y[3]))
+             +(step(x[3], x[4], y[3], y[4])
+             + step(x[4],    b, y[4],   fb));
+    }
+
+    return step(a, b, fa, fb);
+}
+
+///
+unittest
+{
+    import mir.math.common;
+    import mir.math.constant;
+
+    alias cosh = x => 0.5 * (exp(x) + exp(-x));
+    enum Pi = double(PI);
+
+    assert(integrate!exp(0.0, 1.0).approxEqual(double(E - 1)));
+    assert(integrate!(x => x >= 3)(0.0, 10.0).approxEqual(7.0));
+    assert(integrate!sqrt(0.0, 1.0).approxEqual(2.0 / 3));
+    assert(integrate!(x => 23.0 / 25 * cosh(x) - cos(x))(-1.0, 1.0).approxEqual(0.479428226688801667));
+    assert(integrate!(x => 1 / (x ^^ 4 + x ^^ 2 + 0.9))(-1.0, 1.0).approxEqual(1.5822329637294));
+    assert(integrate!(x => sqrt(x ^^ 3))(0.0, 1.0).approxEqual(0.4));
+    assert(integrate!(x => x ? 1 / sqrt(x) : 0)(0.0, 1.0).approxEqual(2));
+    assert(integrate!(x => 1 / (1 + x ^^ 4))(0.0, 1.0).approxEqual(0.866972987339911));
+    assert(integrate!(x => 2 / (2 + sin(10 * Pi * x)))(0.0, 1.0).approxEqual(1.1547005383793));
+    assert(integrate!(x => 1 / (1 + x))(0.0, 1.0).approxEqual(0.6931471805599));
+    assert(integrate!(x => 1 / (1 + exp(x)))(0.0, 1.0).approxEqual(0.3798854930417));
+    assert(integrate!(x => exp(x) - 1 ? x / (exp(x) - 1) : 0)(0.0, 1.0).approxEqual(0.777504634112248));
+    assert(integrate!(x => sin(100 * Pi * x) / (Pi * x))(0.1, 1.0).approxEqual(0.0090986375391668));
+    assert(integrate!(x => sqrt(50.0) * exp(-50 * Pi * x ^^ 2))(0.0, 10.0).approxEqual(0.5));
+    assert(integrate!(x => 25 * exp(-25 * x))(0.0, 10.0).approxEqual(1.0));
+    assert(integrate!(x => 50 / Pi * (2500 * x ^^ 2 + 1))(0.0, 10.0).approxEqual(1.3263071079268e+7));
+    assert(integrate!(x => 50 * (sin(50 * Pi * x) / (50 * Pi * x)) ^^ 2)(0.01, 1.0).approxEqual(0.11213930374164));
+    assert(integrate!(x => cos(cos(x) + 3 * sin(x) + 2 * cos(2 * x) + 3 * sin(2 * x) + 3 * cos(3 * x)))(0.0, Pi).approxEqual(0.83867634269443));
+    assert(integrate!(x => x > 1e-15 ? log(x) : 0)(0.0, 1.0).approxEqual(-1));
+    assert(integrate!(x => 1 / (x ^^ 2 + 1.005))(-1.0, 1.0).approxEqual(1.5643964440690));
+    assert(integrate!(x => 1 / cosh(20 * (x - 0.2)) + 1 / cosh(400 * (x - 0.04)) + 1 / cosh(8000 * (x - 0.008)))(0.0, 1.0).approxEqual(0.16349495585710));
+    assert(integrate!(x => 4 * Pi ^^ 2 * x * sin(20 * Pi * x) * cos(2 * Pi * x))(0.0, 1.0).approxEqual(-0.6346651825434));
+    assert(integrate!(x => 1 / (1 + (230 * x - 30) ^^ 2))(0.0, 1.0).approxEqual(0.013492485649468));
+}

From 3fad2ffad953b5e43cdff373376132cfdf06a4e2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 30 Dec 2021 17:37:09 +0700
Subject: [PATCH 303/573] improve integration

---
 source/mir/numeric.d | 91 +++++++++++++++++++++++++++++---------------
 1 file changed, 61 insertions(+), 30 deletions(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 241758d7..f012c84b 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1635,35 +1635,54 @@ do {
     {
         h = b.half - a.half;
     }
-    T fa = f(a);
-    T fb = f(b);
-    T[7] y = [
-        fa + fb,
-        f(m - x1 * h) + f(m + x1 * h),
-        f(m - alpha * h) + f(m + alpha * h),
-        f(m - x2 * h) + f(m + x2 * h),
-        f(m - beta * h) + f(m + beta * h),
-        f(m - x3 * h) + f(m + x3 * h),
-        f(m),
+    T[13] x = [
+        a,
+        m - x1 * h,
+        m - alpha * h,
+        m - x2 * h,
+        m - beta * h,
+        m - x3 * h,
+        m,
+        m + x3 * h,
+        m + beta * h,
+        m + x2 * h,
+        m + alpha * h,
+        m + x1 * h,
+        b,
+    ];
+    T[13] y = [
+        f(x[ 0]),
+        f(x[ 1]),
+        f(x[ 2]),
+        f(x[ 3]),
+        f(x[ 4]),
+        f(x[ 5]),
+        f(x[ 6]),
+        f(x[ 7]),
+        f(x[ 8]),
+        f(x[ 9]),
+        f(x[10]),
+        f(x[11]),
+        f(x[12]),
     ];
     T i2 = h * (
-        + A2 * y[0]
-        + B2 * y[4]
+        + A2 * (y[0] + y[12])
+        + B2 * (y[4] + y[ 8])
     );
     T i1 = h * (
-        + A1 * y[0]
-        + B1 * y[2]
-        + C1 * y[4]
-        + D1 * y[6]
+        + A1 * (y[0] + y[12])
+        + B1 * (y[2] + y[10])
+        + C1 * (y[4] + y[ 8])
+        + D1 * (y[6]        )
     );
     T si = h * (
-        + A * y[0]
-        + B * y[1]
-        + C * y[2]
-        + D * y[3]
-        + E * y[4]
-        + F * y[5]
-        + G * y[6]
+        + A * (y[0] + y[12])
+        + B * (y[1] + y[11])
+        + C * (y[2] + y[10])
+        + D * (y[3] + y[ 9])
+        + E * (y[4] + y[ 8])
+        + F * (y[5] + y[ 7])
+        + G * (y[6]        )
     );
     T erri1 = fabs(i1 - si);
     T erri2 = fabs(i2 - si);
@@ -1722,15 +1741,27 @@ do {
             return i1;
         }
         return
-             +(step(   a, x[0],   fa, y[0])
-             + step(x[0], x[1], y[0], y[1]))
-             +(step(x[1], x[2], y[1], y[2])
-             + step(x[2], x[3], y[2], y[3]))
-             +(step(x[3], x[4], y[3], y[4])
-             + step(x[4],    b, y[4],   fb));
+            + (step(   a, x[0],   fa, y[0])
+            +  step(x[0], x[1], y[0], y[1]))
+            + (step(x[1], x[2], y[1], y[2])
+            +  step(x[2], x[3], y[2], y[3]))
+            + (step(x[3], x[4], y[3], y[4])
+            +  step(x[4],    b, y[4],   fb));
     }
 
-    return step(a, b, fa, fb);
+    return
+        + ((step(x[ 0], x[ 1], y[ 0], y[ 1])
+        +   step(x[ 1], x[ 2], y[ 1], y[ 2]))
+        +  (step(x[ 2], x[ 3], y[ 2], y[ 3])
+        +   step(x[ 3], x[ 4], y[ 3], y[ 4])))
+        + ((step(x[ 4], x[ 5], y[ 4], y[ 5])
+        +   step(x[ 5], x[ 6], y[ 5], y[ 6]))
+        +  (step(x[ 6], x[ 7], y[ 6], y[ 7])
+        +   step(x[ 7], x[ 8], y[ 7], y[ 8])))
+        + ((step(x[ 8], x[ 9], y[ 8], y[ 9])
+        +   step(x[ 9], x[10], y[ 9], y[10]))
+        +  (step(x[10], x[11], y[10], y[11])
+        +   step(x[11], x[12], y[11], y[12])));
 }
 
 ///

From c5f602c4e0a613e9a9b1fe7d98a07b7a724a09f2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 30 Dec 2021 17:46:21 +0700
Subject: [PATCH 304/573] ditto

---
 source/mir/numeric.d | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index f012c84b..f92a8146 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1749,19 +1749,22 @@ do {
             +  step(x[4],    b, y[4],   fb));
     }
 
+    foreach (i; 0 .. 12)
+        x[i] = step(x[i], x[i + 1], y[i], y[i + 1]);
+
     return
-        + ((step(x[ 0], x[ 1], y[ 0], y[ 1])
-        +   step(x[ 1], x[ 2], y[ 1], y[ 2]))
-        +  (step(x[ 2], x[ 3], y[ 2], y[ 3])
-        +   step(x[ 3], x[ 4], y[ 3], y[ 4])))
-        + ((step(x[ 4], x[ 5], y[ 4], y[ 5])
-        +   step(x[ 5], x[ 6], y[ 5], y[ 6]))
-        +  (step(x[ 6], x[ 7], y[ 6], y[ 7])
-        +   step(x[ 7], x[ 8], y[ 7], y[ 8])))
-        + ((step(x[ 8], x[ 9], y[ 8], y[ 9])
-        +   step(x[ 9], x[10], y[ 9], y[10]))
-        +  (step(x[10], x[11], y[10], y[11])
-        +   step(x[11], x[12], y[11], y[12])));
+        + ((x[ 0]
+        +   x[ 1])
+        +  (x[ 2]
+        +   x[ 3]))
+        + ((x[ 4]
+        +   x[ 5])
+        +  (x[ 6]
+        +   x[ 7]))
+        + ((x[ 8]
+        +   x[ 9])
+        +  (x[10]
+        +   x[11]));
 }
 
 ///

From b831df612111a2ef0b3abf772cd8929929477aed Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 2 Jan 2022 13:15:40 +0700
Subject: [PATCH 305/573] fixup

---
 source/mir/numeric.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index f92a8146..6bb474ec 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1736,7 +1736,8 @@ do {
             + C1 * (y[1] + y[3])
             + D1 * y[2]
         );
-        if (!(i1.fabs < T.infinity) || (si + (i1 - i2) == si) || !(a < x[0]) || !(x[4] < b))
+        auto sic = si + (i1 - i2);
+        if (!(i1.fabs < T.infinity) || sic == si || !(a < x[0]) || !(x[4] < b))
         {
             return i1;
         }

From 541cacca81ade5f07180ad088b38cc1c57a70abc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 9 Jan 2022 15:30:21 +0700
Subject: [PATCH 306/573] add StringMap.sort

---
 source/mir/string_map.d | 48 +++++++++++++++++++++++++++++++++++------
 1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 4331f4fd..08087f16 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -751,21 +751,32 @@ struct StringMap(T, U = uint)
          +/
         this()(string[] keys, T[] values) @trusted pure nothrow
         {
-            import mir.array.allocation: array;
-            import mir.ndslice.sorting: makeIndex;
-            import mir.ndslice.topology: iota, indexed;
-            import mir.string_table: smallerStringFirst;
-
             assert(keys.length == values.length);
             if (keys.length == 0)
                 return;
             _length = keys.length;
             _keys = keys.ptr;
             _values = values.ptr;
-            _indices = keys.makeIndex!(U, smallerStringFirst).ptr;
+            _indices = new U[keys.length].ptr;
+            size_t maxKeyLength;
+            foreach(ref key; keys)
+                if (key.length > maxKeyLength)
+                    maxKeyLength = key.length;
+            _lengthTable = new U[maxKeyLength + 2];
+            sortIndices();
+        }
+
+        private void sortIndices() pure nothrow
+        {
+            import mir.ndslice.sorting: sort;
+            import mir.ndslice.topology: indexed;
+            import mir.string_table: smallerStringFirst;
+            foreach (i, ref index; indices)
+                index = cast(U)i;
+
+            indices.sort!((a, b) => smallerStringFirst(keys[a], keys[b]));
             auto sortedKeys = _keys.indexed(indices);
             size_t maxKeyLength = sortedKeys[$ - 1].length;
-            _lengthTable = new U[maxKeyLength + 2];
 
             size_t ski;
             foreach (length; 0 .. maxKeyLength + 1)
@@ -945,6 +956,20 @@ struct StringMap(T, U = uint)
             return false;
         }
     }
+
+    /// Sorts table according to the keys
+    ref sort(alias less = "a < b")() return
+    {
+        import mir.functional: naryFun;
+        import mir.ndslice.sorting: sort;
+        import mir.ndslice.topology: zip;
+        if (length) {
+            zip(implementation.keys, implementation.values).sort!((l, r) => naryFun!less(l.a, r.a));
+            implementation.sortIndices;
+        }
+        return this;
+    }
+
     private Impl* implementation;
 }
 
@@ -972,6 +997,15 @@ unittest
     assert(table["val"] == 11);
 
     assert(table == table);
+
+    // sorting
+    table["A"] = 2;
+    table.sort;
+    assert(table.keys == ["A", "L", "val"]);
+    assert(table.values == [2, 3, 11]);
+    assert(table["A"] == 2);
+    assert(table["L"] == 3);
+    assert(table["val"] == 11);
 }
 
 version(mir_test)

From 581076ff7117f0fe1baf945d31d27d34e92eb82a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 9 Jan 2022 17:11:21 +0700
Subject: [PATCH 307/573] add

---
 source/mir/serde.d | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index e9245375..dd8595bb 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -754,6 +754,12 @@ See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn)
 +/
 enum serdeLikeStruct;
 
+/++
+The attribute is used for algebraic deserialization for types like `Variant!(string, S)`
+`@serdeFallbackStruct struct S {}`
++/
+enum serdeFallbackStruct;
+
 /++
 Force serialize / deserialize on fields instead of Range API.
 +/

From 70701e9c6024b7e5b76e326520a62438dd84a027 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 9 Jan 2022 21:08:21 +0700
Subject: [PATCH 308/573] Add keys proxy for StringMap

---
 source/mir/string_map.d | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 08087f16..ea9802e3 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -36,6 +36,9 @@ struct StringMap(T, U = uint)
     import core.lifetime: move;
     import mir.conv: emplaceRef;
 
+    ///
+    alias serdeKeysProxy = T;
+
     ///
     // current implementation is workaround for linking bugs when used in self referencing algebraic types
     bool opEquals(V)(scope const StringMap!(V, U) rhs) const @trusted

From 58667cf11459f54c8bb4f1c899cde9de674162ae Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 11 Jan 2022 13:27:12 +0700
Subject: [PATCH 309/573] add better null formatting

---
 source/mir/format.d | 52 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 45 insertions(+), 7 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index b8f02524..28841cc5 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -65,9 +65,16 @@ string text(string separator = "", Args...)(auto ref Args args)
 {
     static if (Args.length == 1)
     {
-        import mir.functional: forward;
-        import mir.conv: to;
-        return to!string(forward!args);
+        static if (is(immutable Args[0] == immutable typeof(null)))
+        {
+            return "null";
+        }
+        else
+        {
+            import mir.functional: forward;
+            import mir.conv: to;
+            return to!string(forward!args);
+        }
     }
     else
     {
@@ -90,7 +97,8 @@ string text(string separator = "", Args...)(auto ref Args args)
 {
     const i = 100;
     assert(text("str ", true, " ", i, " ", 124.1) == "str true 100 124.1", text("str ", true, " ", 100, " ", 124.1));
-    assert(text!" "("str", true, 100, 124.1) == "str true 100 124.1");
+    assert(text!" "("str", true, 100, 124.1, null) == "str true 100 124.1 null");
+    assert(text(null) == "null", text(null));
 }
 
 import mir.format_impl;
@@ -607,6 +615,24 @@ version (mir_test) unittest
     assert(w.data == `["a": 1, "b": 2]` || w.data == `["b": 2, "a": 1]`, w.data);
 }
 
+/// Prints null
+ref W print(C = char, W, V)(scope return ref W w, const V c)
+    if (is(V == typeof(null)))
+{
+    enum C[4] Null = "null";
+    return w.printStaticString!C(Null);
+}
+
+///
+@safe pure @nogc
+version (mir_test) unittest
+{
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
+    w.print(null);
+    assert(w.data == `null`);
+}
+
 /// Prints array
 pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const(T)[] c)
@@ -906,23 +932,34 @@ pragma(inline, false)
 ref W print(C = char, W, T)(scope return ref W w, scope const T c)
     if (isSomeChar!C && is(T == class) || is(T == interface))
 {
-    enum C[4] Null = "null";
     static if (__traits(hasMember, T, "toString") || __traits(compiles, { scope const(C)[] string_of_c = c; }))
     {
         if (c is null)
-            w.printStaticString!C(Null);
+            return w.print(null);
         else
         static if (is(typeof(c.toString!C(w))))
+        {
             c.toString!C(w);
+            return w;
+        }
         else
         static if (is(typeof(c.toString(w))))
+        {
             c.toString(w);
+            return w;
+        }
         else
         static if (is(typeof(c.toString((scope const(C)[] s) { w.put(s); }))))
+        {
             c.toString((scope const(C)[] s) { w.put(s); });
+            return w;
+        }
         else
         static if (is(typeof(w.put(c.toString))))
+        {
             w.put(c.toString);
+            return w;
+        }
         else
         static if (__traits(compiles, { scope const(C)[] string_of_c = c; }))
         {
@@ -947,12 +984,13 @@ ref W print(C = char, W, T)(scope return ref W w, scope const T c)
             print!C(w, e);
         }
         w.put(right);
+        return w;
     }
     else
     {
         w.put(T.stringof);
+        return w;
     }
-    return w;
 }
 
 ///

From 3f32207019a24b171bd4df1692210d8386c10af8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 11 Jan 2022 13:29:01 +0700
Subject: [PATCH 310/573] add algebraic_alias.transform

---
 source/mir/algebraic_alias/transform.d | 153 +++++++++++++++++++++++++
 1 file changed, 153 insertions(+)
 create mode 100644 source/mir/algebraic_alias/transform.d

diff --git a/source/mir/algebraic_alias/transform.d b/source/mir/algebraic_alias/transform.d
new file mode 100644
index 00000000..fcd92765
--- /dev/null
+++ b/source/mir/algebraic_alias/transform.d
@@ -0,0 +1,153 @@
+/++
+$(H1 Transformation utilities for JSON-like values)
+
+See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf);
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: Ilya Yaroshenko 
+Macros:
++/
+module mir.algebraic_alias.transform;
+
+import mir.algebraic: Algebraic, tryVisit, visit, optionalVisit;
+import mir.functional: naryFun;
+private alias AliasSeq(T...) = T;
+
+/++
+Transforms algebraics leafs recursively in place,
+ensuring that all leaf types are handled by the visiting functions.
+
+Recursion is done for `This[]`, `StringMap!This`, `This[string]`, and `Annotated!This` types.
++/
+alias transformLeafs(visitors...) = transformLeafsImpl!(visit, naryFun!visitors);
+
+///
+unittest
+{
+    import mir.format: text;
+    import mir.algebraic_alias.json;
+    JsonAlgebraic value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, null.JsonAlgebraic].JsonAlgebraic];
+ 
+    // converts all leavs to a text form
+    value.transformLeafs!text;
+    assert(value == ["key" : ["str".JsonAlgebraic, "2.32".JsonAlgebraic, "null".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
+    
+    value = ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, true.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic;
+
+    /// converts only bool values
+    value.transformLeafs!(
+        (bool b) => b.text,
+        v => v, // other values are copied as is
+    );
+
+    assert(value == ["key" : ["str".JsonAlgebraic, 2.32.JsonAlgebraic, "true".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
+}
+
+/++
+Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions.
+
+Throws: Exception if `naryFun!visitors` can't be called with provided arguments
++/
+alias tryTransformLeafs(visitors...) = transformLeafsImpl!(tryVisit, naryFun!visitors);
+
+///
+unittest
+{
+    import mir.format: text;
+    import mir.algebraic_alias.json;
+    JsonAlgebraic value = ["key" : [true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic];
+ 
+    // converts long and double numbers to a text form, bool values is converted to `long`
+    value.tryTransformLeafs!((double v) => v.text, (long v) => v.text);
+    assert(value == ["key" : ["1".JsonAlgebraic, "100".JsonAlgebraic, "2.32".JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
+}
+
+/++
+Behaves as $(LREF transformLeafs) but doesn't enforce at compile time that all types can be handled by the visiting functions.
+
+The function ignores leafs that can't be handled by the visiting functions.
++/
+alias optionalTransformLeafs(visitors...) = transformLeafsImpl!(optionalVisit, naryFun!visitors);
+
+///
+unittest
+{
+    import mir.format: text;
+    import mir.algebraic_alias.json;
+    JsonAlgebraic value = ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, 100.JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic];
+ 
+    // converts long numbers to a text form, ignores other types
+    value.optionalTransformLeafs!(
+        (long v) => v.text,
+        (bool b) => b, // needs special overload for bool to get rid of implicit converion to long/double
+    );
+    assert(value == ["key" : [null.JsonAlgebraic, true.JsonAlgebraic, "100".JsonAlgebraic, 2.32.JsonAlgebraic].JsonAlgebraic].JsonAlgebraic);
+}
+
+///
+template transformLeafsImpl(alias handler, alias visitor)
+{
+    ///
+    ref Algebraic!Types transformLeafsImpl(Types...)(ref return Algebraic!Types value)
+    {
+        import core.lifetime: move;
+        import mir.algebraic: visit;
+        import mir.annotated: Annotated;
+        import mir.string_map: StringMap;
+        alias T = Algebraic!Types;
+        static if (is(T.AllowedTypes[0] == typeof(null)))
+        {
+            enum nullCompiles = __traits(compiles, value = visitor(null));
+            static if (nullCompiles || __traits(isSame, handler, visit))
+            {
+                alias nullHandler = (typeof(null)) {
+                    static if (nullCompiles)
+                        value = visitor(null);
+                    else
+                        assert(0, "Null " ~ T.stringof);
+                };
+            }
+            else
+            {
+                alias nullHandler = AliasSeq!();
+            }
+        }
+        else
+        {
+            alias nullHandler = AliasSeq!();
+        }
+        handler!(
+            (T[] v) {
+                foreach (ref e; v)
+                   transformLeafsImpl(e);
+            },
+            (StringMap!T v) {
+                foreach (ref e; v.values)
+                   transformLeafsImpl(e);
+            },
+            (T[string] v) {
+                foreach (key, ref e; v)
+                   transformLeafsImpl(e);
+            },
+            (Annotated!T v) {
+                transformLeafsImpl(v.value);
+            },
+            nullHandler,
+            (ref v) { // auto for typeof(null) support
+                static if (__traits(compiles, value = visitor(move(v))))
+                    value = visitor(move(v));
+                else
+                    value = visitor(v);
+            }
+        )(value);
+        return value;
+    }
+
+    /// ditto
+    Algebraic!Types transformLeafsImpl(Types...)(Algebraic!Types value)
+    {
+        import core.lifetime: move;
+        transformLeafsImpl(value);
+        return move(value);
+    }
+}

From 4290142368b604d92abb0f124d0763ee246cdb12 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 11 Jan 2022 13:35:31 +0700
Subject: [PATCH 311/573] add algebraic_alias/transform to meson build

---
 index.d     | 1 +
 meson.build | 1 +
 2 files changed, 2 insertions(+)

diff --git a/index.d b/index.d
index 7cdf53f8..daddc51f 100644
--- a/index.d
+++ b/index.d
@@ -13,6 +13,7 @@ $(BOOKTABLE ,
     $(LEADINGROW Accessories)
     $(TR $(TDNW $(MREF mir,algebraic_alias,ion)) $(TD Mutable Amazon's Ion value))
     $(TR $(TDNW $(MREF mir,algebraic_alias,json)) $(TD Mutable JSON value))
+    $(TR $(TDNW $(MREF mir,algebraic_alias,transform)) $(TD Mutation algorithms for Ion/JSON-like values))
     $(TR $(TDNW $(MREF mir,annotated)) $(TD Mutable generic Annotated value))
     $(TR $(TDNW $(MREF mir,array,allocation)) $(TD `std.array` reworked for Mir))
     $(TR $(TDNW $(MREF mir,format)) $(TD @nogc Formatting Utilities))
diff --git a/meson.build b/meson.build
index cff5d488..5b2ac982 100644
--- a/meson.build
+++ b/meson.build
@@ -10,6 +10,7 @@ has_cpp_headers = true
 sources_list = [
     'mir/algebraic_alias/ion',
     'mir/algebraic_alias/json',
+    'mir/algebraic_alias/transform',
     'mir/algorithm/iteration',
     'mir/algorithm/setops',
     'mir/annotated',

From f4d9f18e160d4bd0cb1788786beab085fede64e8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 11 Jan 2022 13:35:44 +0700
Subject: [PATCH 312/573] fixup deprecated warnings

---
 dub.sdl                |  2 +-
 source/mir/math/stat.d | 13 +++++++------
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 8678cb23..fc71c020 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.1.75"
+dependency "mir-core" version=">=1.1.88"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index a326d91a..f5e7f1bf 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -535,7 +535,7 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.math.common: approxEqual;
+    import mir.complex.math: approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.complex;
     alias C = Complex!double;
@@ -864,7 +864,7 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.math.common: approxEqual;
+    import mir.complex.math: approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.complex;
     alias C = Complex!double;
@@ -2299,7 +2299,7 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.math.common: approxEqual;
+    import mir.complex.math: approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.complex: Complex;
 
@@ -2647,7 +2647,7 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.math.common: approxEqual;
+    import mir.complex.math: approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.complex: Complex;
 
@@ -2796,6 +2796,7 @@ version(mir_test)
 unittest
 {
     import mir.math.common: approxEqual;
+    import mir.complex.math: capproxEqual = approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.complex;
     alias C = Complex!double;
@@ -2803,7 +2804,7 @@ unittest
     assert(variance([1.0, 2, 3]).approxEqual(2.0 / 2));
     assert(variance([1.0, 2, 3], true).approxEqual(2.0 / 3));
 
-    assert(variance([C(1, 3), C(2), C(3)]).approxEqual(C(-4, -6) / 2));
+    assert(variance([C(1, 3), C(2), C(3)]).capproxEqual(C(-4, -6) / 2));
     
     assert(variance!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 5));
     
@@ -2966,7 +2967,7 @@ version(mir_test)
 @safe pure nothrow
 unittest
 {
-    import mir.math.common: approxEqual;
+    import mir.complex.math: approxEqual;
     import mir.ndslice.slice: sliced;
     import mir.complex;
     alias C = Complex!double;

From 7db102175b9d0fc93b909766e0d62e706ba0fe7b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 15 Jan 2022 17:04:09 +0700
Subject: [PATCH 313/573] add serdeIgnoreUnexpectedKeys

---
 source/mir/serde.d | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index dd8595bb..9d53ae87 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -578,6 +578,11 @@ version(mir_test) unittest
     static assert (serdeGetKeyOut(E.c) == "c");
 }
 
+/++
+Attribute used to ignore unexpected keys during an aggregate type deserialization.
++/
+enum serdeIgnoreUnexpectedKeys;
+
 /++
 Attribute to ignore field.
 

From 878d38ac1764e4cff6658804a851723d21f3f252 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Fri, 21 Jan 2022 13:45:40 +0700
Subject: [PATCH 314/573] sort series after deserialization

---
 source/mir/series.d | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index e94d2129..236b8652 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -18,7 +18,7 @@ module mir.series;
 import mir.ndslice.iterator: IotaIterator;
 import mir.ndslice.sorting: transitionIndex;
 import mir.qualifier;
-import mir.serde: serdeIgnore, serdeFields, serdeKeys, serdeKeyOut;
+import mir.serde: serdeIgnore, serdeFields;
 import std.traits;
 public import mir.ndslice.slice;
 public import mir.ndslice.sorting: sort;
@@ -345,6 +345,19 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     private enum defaultMsg() = "Series " ~ Unqual!(this.Data).stringof ~ "[" ~ Unqual!(this.Index).stringof ~ "]: Missing";
     private static immutable defaultExc() = new Exception(defaultMsg!() ~ " required key");
 
+    ///
+    void serdeFinalize()() @trusted
+    {
+        import mir.algorithm.iteration: any;
+        import mir.ndslice.topology: pairwise;
+        import std.traits: Unqual;
+        if (length <= 1)
+            return;
+        auto mutableOf = cast(Series!(Unqual!Index*, Unqual!Data*)) this.lightScope();
+        if (any(pairwise!"a > b"(mutableOf.index)))
+            sort(mutableOf);
+    }
+
 @optmath:
 
     ///

From 60899a51f46ead92cdc8e2b5642439ac4d60ddd1 Mon Sep 17 00:00:00 2001
From: 9il <ilyayaroshenko@gmail.com>
Date: Wed, 26 Jan 2022 14:52:34 +0700
Subject: [PATCH 315/573] fix ndslice.fuse safety

---
 source/mir/ndslice/fuse.d | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index 7d03ba82..0407199b 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -180,7 +180,7 @@ template fuseImpl(bool RC, T_, Dimensions...)
     Params:
         r = parallelotope (ndrange) with length/shape and input range primitives.
     +/
-    auto fuseImpl(NDRange)(NDRange r)
+    auto fuseImpl(NDRange)(NDRange r) @safe
         if (hasShape!NDRange)
     {
         import mir.conv: emplaceRef;
@@ -231,11 +231,11 @@ template fuseImpl(bool RC, T_, Dimensions...)
                     ret = shapep.slice!UT;
                     ret.transposed!InverseDimensions.each!"a = b"(r);
                 }
-                else
+                else () @trusted
                 {
                     ret = shapep.uninitSlice!UT;
                     ret.transposed!InverseDimensions.each!(emplaceRef!T)(r);
-                }
+                } ();
 
             }
         }
@@ -253,11 +253,11 @@ template fuseImpl(bool RC, T_, Dimensions...)
                     ret = shape.slice!UT;
                     ret.each!"a = b"(r);
                 }
-                else
+                else () @trusted
                 {
                     ret = shape.uninitSlice!UT;
                     ret.each!(emplaceRef!T)(r);
-                }
+                } ();
             }
         }
         static if (RC)
@@ -356,15 +356,15 @@ auto fuseCells(S)(S cells)
         else
             return ret;
     }
-    else
+    else return () @trusted
     {
         import mir.ndslice.allocation: uninitSlice;
         import mir.conv;
         auto ret = cells.fuseCellsShape.uninitSlice!UT;
         ret.fuseCellsAssign!(emplaceRef!T) = cells;
         alias R = Slice!(T*, ret.N);
-        return R(ret._structure, (() @trusted => cast(T*)ret._iterator)());
-    }
+        return R(ret._structure, cast(T*)ret._iterator);
+    } ();
 }
 
 /// 1D

From f85f2fc7a201a2ea7d57dcac75c06331ac13a501 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 9 Feb 2022 15:10:14 +0700
Subject: [PATCH 316/573] extend SmallString opIndex API

---
 source/mir/bignum/decimal.d |  3 +--
 source/mir/small_array.d    |  6 +++---
 source/mir/small_string.d   | 27 ++++++++++++++++++++++++---
 3 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index a25c51d8..bb81fa35 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -1040,9 +1040,8 @@ unittest
     Decimal!3 decimal;
     DecimalExponentKey key;
 
-    import mir.math.constant: PI;
     assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
-    assert(cast(double) decimal == double(PI) / 1e10);
+    assert(cast(double) decimal == 0x1.596bf8ce7631ep-32);
     assert(key == DecimalExponentKey.e);
 }
 
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 35a2370c..0431f22b 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -10,8 +10,6 @@ Authors: Ilya Yaroshenko
 +/
 module mir.small_array;
 
-import mir.serde: serdeProxy;
-
 private static immutable errorMsg = "Cannot create SmallArray: input range exceeds maximum allowed length.";
 version(D_Exceptions)
     private static immutable exception = new Exception(errorMsg);
@@ -20,6 +18,7 @@ version(D_Exceptions)
 template SmallArray(T, uint maxLength)
     if (maxLength)
 {
+    import mir.serde: serdeProxy, serdeScoped;
     import std.traits: Unqual, isIterable, isImplicitlyConvertible;
 
     static if (isImplicitlyConvertible!(const T, T))
@@ -28,6 +27,7 @@ template SmallArray(T, uint maxLength)
         alias V = T;
 
     ///
+    @serdeScoped
     @serdeProxy!(V[])
     struct SmallArray
     {
@@ -45,7 +45,7 @@ template SmallArray(T, uint maxLength)
         }
 
         /// ditto
-        this(V[] array)
+        this(scope V[] array)
         {
             this.opAssign(array);
         }
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index ab8bd920..bb5eaa7f 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -205,9 +205,7 @@ scope nothrow:
     /++
     Returns an scope common string.
 
-    The property is used as common string representation self alias.
-
-    The alias helps with `[]`, `[i]`, `[i .. j]`, `==`, and `!=` operations and implicit conversion to strings.
+    The method implement with `[]` operation.
     +/
     inout(char)[] opIndex() inout @trusted scope return
     {
@@ -225,8 +223,25 @@ scope nothrow:
         return opIndex[index];
     }
 
+    /++
+    Returns a common scope string.
+
+    The method implement with `[i .. j]` operation.
+    +/
+    inout(char)[] opIndex(size_t[2] range) inout @trusted scope return
+    in (range[0] <= range[1])
+    in (range[1] <= this.length)
+    {
+        return opIndex()[range[0] .. range[1]];
+    }
+
 const:
 
+    /// ditto
+    size_t[2] opSlice(size_t pos : 0)(size_t i, size_t j) scope {
+        return [i, j];
+    }
+
     ///
     bool empty() @property
     {
@@ -239,6 +254,9 @@ const:
         return opIndex.length;
     }
 
+    /// ditto
+    alias opDollar(size_t pos : 0) = length;
+
     ///
     alias toString = opIndex;
 
@@ -294,6 +312,9 @@ const:
 
     auto s8 = SmallString!8("Hellow!!");
     assert(s8 == "Hellow!!", s8[]);
+    assert(s8[] == "Hellow!!");
+    assert(s8[0 .. $] == "Hellow!!");
+    assert(s8[1 .. 4] == "ell");
 
     s16 = s8;
     assert(s16 == "Hellow!!", s16[]);

From 6b1df969c001f9d88199175824104f8e2ca0a841 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 9 Feb 2022 20:09:05 +0700
Subject: [PATCH 317/573] combined test with mir-core

---
 dub.sdl        | 2 +-
 test_travis.sh | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index fc71c020..0489caae 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.88"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_test"
+    versions "mir_bignum_test" "mir_test" "mir_core_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/test_travis.sh b/test_travis.sh
index 6279e27e..e8236cd1 100644
--- a/test_travis.sh
+++ b/test_travis.sh
@@ -11,6 +11,7 @@ else
     dub test --arch=$ARCH --build=unittest-dip1000
   fi
   dub test --arch=$ARCH --build=unittest-cov
+  dub test --arch=$ARCH --combined
   # if [ \( "$DC" = "ldc2" \) -o \( "$DC" = "ldmd2" \) ]
   # then
   #     cd benchmarks/sajson ; dub --build=release-nobounds --compiler=ldmd2 ; cd ../..

From 3a5510bb8d39824c8a6fd58dbcf0f8d2fce8993a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 15 Feb 2022 13:06:24 +0700
Subject: [PATCH 318/573] update orb

---
 .circleci/config.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 6785e151..19a372fd 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,7 +1,7 @@
 version: 2.1
 
 orbs:
-  mirci: libmir/upload_docs@0.1.4
+  mirci: libmir/upload_docs@0.3.0
 
 workflows:
   version: 2

From b6a5b6bd5269526477b9cbb6f827f94b2b6fea0e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 15 Feb 2022 14:11:48 +0700
Subject: [PATCH 319/573] simplify CI configs

---
 .github/workflows/ci.yml |   3 +-
 .travis.yml              |  34 -------------
 Dockerfile.aarch64       |  12 -----
 appveyor.yml             | 105 ---------------------------------------
 test_travis.sh           |  19 -------
 5 files changed, 2 insertions(+), 171 deletions(-)
 delete mode 100644 .travis.yml
 delete mode 100644 Dockerfile.aarch64
 delete mode 100644 appveyor.yml
 delete mode 100644 test_travis.sh

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0f436b96..779a7a9a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -40,7 +40,8 @@ jobs:
         run: sudo apt-get install gcc-multilib
       - id: build
         name: Test building
-        run: bash -e test_travis.sh
+        run: dub test --arch=$ARCH --build=unittest-cov
+        run: dub test --arch=$ARCH --combined
       - id: coverage
         name: Upload coverage data
         run: bash <(curl -s https://codecov.io/bash)
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 133b88ad..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-sudo: false
-os:
- - linux
-language: d
-d:
- - ldc
- - ldc-beta
- - dmd-nightly
- - dmd-beta
- - dmd
-branches:
-  only:
-    - master
-env:
- - ARCH="x86"
- - ARCH="x86_64"
-addons:
-  apt:
-    packages:
-      - gcc-multilib
-matrix:
-  allow_failures:
-    - {d: dmd-nightly}
-    - {d: ldc-beta}
-    - {d: gdc}
-  include:
-    - {os: linux, env: ARCH="AARCH64", services: docker}
-    - {os: osx, env: ARCH="x86_64", d: ldc}
-    - {os: osx, env: ARCH="x86_64", d: dmd}
-script:
- - travis_wait 100 bash -e test_travis.sh
-
-after_success:
- - bash <(curl -s https://codecov.io/bash)
diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64
deleted file mode 100644
index 462ecdc4..00000000
--- a/Dockerfile.aarch64
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM resin/aarch64-ubuntu
-RUN apt-get update && apt-get -y install xz-utils libxml2 gcc git pkg-config ninja-build python3-pip
-RUN pip3 install meson
-RUN curl -fsSLO https://github.com/ldc-developers/ldc/releases/download/v1.24.0/ldc2-1.24.0-linux-aarch64.tar.xz \
- && tar xf ldc2-1.24.0-linux-aarch64.tar.xz \
- && sudo cp -rf ldc2-1.24.0-linux-aarch64/* /usr/local \
- && rm -rf ldc*
-COPY source source
-COPY include include
-COPY meson.build meson.build
-COPY subprojects subprojects
-RUN ls subprojects && meson build -D with_test_explicit=true --default-library=static && ninja test -C build
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index a91b548c..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,105 +0,0 @@
-platform: x64
-environment:
- matrix:
-  # - DC: dmd
-  #   DVersion: 2.080.0
-  #   arch: x64
-  # - DC: dmd
-  #   DVersion: 2.080.0
-  #   arch: x86
-  - DC: ldc
-    DVersion: '1.24.0'
-    arch: x64
-
-matrix:
-  allow_failures:
-    - {DC: dmd, arch: x86}
-
-skip_tags: true
-branches:
-  only:
-    - master
-    - stable
-
-install:
-  - ps: function SetUpDCompiler
-        {
-            if($env:DC -eq "dmd"){
-              if($env:arch -eq "x86"){
-                $env:DConf = "m32";
-              }
-              elseif($env:arch -eq "x64"){
-                $env:DConf = "m64";
-              }
-              echo "downloading ...";
-              $env:toolchain = "msvc";
-              $version = $env:DVersion;
-              Invoke-WebRequest "http://downloads.dlang.org/releases/2.x/$($version)/dmd.$($version).windows.7z" -OutFile "c:\dmd.7z";
-              echo "finished.";
-              pushd c:\\;
-              7z x dmd.7z > $null;
-              popd;
-            }
-            elseif($env:DC -eq "ldc"){
-              echo "downloading ...";
-              if($env:arch -eq "x86"){
-                $env:DConf = "m32";
-              }
-              elseif($env:arch -eq "x64"){
-                $env:DConf = "m64";
-              }
-              $env:toolchain = "msvc";
-              $version = $env:DVersion;
-              Invoke-WebRequest "https://github.com/ldc-developers/ldc/releases/download/v$($version)/ldc2-$($version)-windows-x64.7z" -OutFile "c:\ldc.7z";
-              echo "finished.";
-              pushd c:\\;
-              7z x ldc.7z > $null;
-              popd;
-            }
-        }
-  - ps: SetUpDCompiler
-  - powershell -Command Invoke-WebRequest https://code.dlang.org/files/dub-1.9.0-windows-x86.zip -OutFile dub.zip
-  - 7z x dub.zip -odub > nul
-  - set PATH=%CD%\%binpath%;%CD%\dub;%PATH%
-  - dub --version
-
-before_build:
-  - ps: if($env:arch -eq "x86"){
-            $env:compilersetupargs = "x86";
-            $env:Darch = "x86";
-          }
-        elseif($env:arch -eq "x64"){
-            $env:compilersetupargs = "amd64";
-            $env:Darch = "x86_64";
-        }
-  - ps : if($env:DC -eq "dmd"){
-           $env:PATH += ";C:\dmd2\windows\bin;";
-         }
-         elseif($env:DC -eq "ldc"){
-           $version = $env:DVersion;
-           $env:PATH += ";C:\ldc2-$($version)-windows-x64\bin";
-           $env:DC = "ldc2";
-         }
-  - ps: $env:compilersetup = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall";
-  - '"%compilersetup%" %compilersetupargs%'
-
-build_script:
- - echo dummy build script - dont remove me
-
-test_script:
- - echo %PLATFORM%
- - echo %Darch%
- - echo %DC%
- - echo %PATH%
- - '%DC% --version'
- - dub test --arch=%Darch% --compiler=%DC%
-
-notifications:
- - provider: Webhook
-   url: https://webhooks.gitter.im/e/56aae174f8cc81f4eda4
- - provider: Email
-   to:
-    - devteam@mir.rocks
-   on_build_success: false
-   on_build_failure: true
-   on_build_status_changed: true
diff --git a/test_travis.sh b/test_travis.sh
deleted file mode 100644
index e8236cd1..00000000
--- a/test_travis.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-echo $ARCH
-if [ "$ARCH" = "AARCH64" ]
-then
-  docker run --rm --privileged multiarch/qemu-user-static:register --reset
-  docker build -t ion-arm64 . -f Dockerfile.aarch64
-else
-  echo $DC
-  echo $ARCH
-  if [ "$DC" != "gdc" ] 
-  then
-    dub test --arch=$ARCH --build=unittest-dip1000
-  fi
-  dub test --arch=$ARCH --build=unittest-cov
-  dub test --arch=$ARCH --combined
-  # if [ \( "$DC" = "ldc2" \) -o \( "$DC" = "ldmd2" \) ]
-  # then
-  #     cd benchmarks/sajson ; dub --build=release-nobounds --compiler=ldmd2 ; cd ../..
-  # fi
-fi

From a73dcd0759a489cad885205f956f85460253db63 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 17 Feb 2022 18:40:23 +0700
Subject: [PATCH 320/573] Workaround opDispatch compiler bug

---
 source/mir/string_map.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index ea9802e3..dd6ab993 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -30,7 +30,7 @@ Params:
     U = an unsigned type that can hold an index of keys. `U.max` must be less then the maximum possible number of struct members.
 +/
 struct StringMap(T, U = uint)
-    if (isMutable!T && !__traits(hasMember, T, "opPostMove") && __traits(isUnsigned, U))
+    if (isMutable!T && !is(typeof(T.opPostMove)) && __traits(isUnsigned, U))
 {
     import mir.utility: _expect;
     import core.lifetime: move;

From 27bbd1730c82f6f0ce444dcd31f938cd5cdfee84 Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Tue, 22 Feb 2022 00:02:48 -0500
Subject: [PATCH 321/573] Add Windows CI flow, remove dmd-master testing (#390)

---
 .github/workflows/ci.yml | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 779a7a9a..d9f4fef4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,14 +14,14 @@ jobs:
   builder:
     name: 'Build and test on ${{ matrix.arch }}-${{ matrix.os }}/${{ matrix.dc }}'
     runs-on: ${{ matrix.os }}
-    continue-on-error: ${{ contains(matrix.dc, 'master') || contains(matrix.dc, 'beta') }}
+    continue-on-error: ${{ contains(matrix.dc, 'beta') }}
     env:
       ARCH: ${{ matrix.arch }}
     strategy:
       fail-fast: false
       matrix:
-        dc: [ldc-latest, ldc-beta, dmd-latest, dmd-master, dmd-beta]
-        os: [ubuntu-latest]
+        dc: [ldc-latest, ldc-beta, dmd-latest, dmd-beta]
+        os: [ubuntu-latest, windows-latest]
         arch: [x86, x86_64]
         include:
           - dc: ldc-latest
@@ -36,14 +36,15 @@ jobs:
         with:
           compiler: ${{ matrix.dc }}
       - name: Install multi-lib for 32-bit systems
-        if: matrix.arch == 'x86'
+        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
-        run: dub test --arch=$ARCH --combined
+        run: |
+          dub test --arch=$ARCH --build=unittest-cov
+          dub test --arch=$ARCH --combined
+        shell: bash
       - id: coverage
-        name: Upload coverage data
-        run: bash <(curl -s https://codecov.io/bash)
+        uses: codecov/codecov-action@v2
     
         

From d88779a32dcd64cb5b179dcc121ef4873ed2fd60 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 4 Mar 2022 16:47:31 +0700
Subject: [PATCH 322/573] Add serdeDiscriminatedField

---
 source/mir/serde.d | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 9d53ae87..5330da5a 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -2227,6 +2227,20 @@ struct SerdeFlags(T)
         static foreach(member; serdeFinalProxyDeserializableMembers!T)
             mixin("bool " ~ member ~ ";");
 }
+
+/++
+The UDA used on struct or class definitions to denote an discriminated field and its tag
+for algebraic deserialization.
+
+Discriminated field is ignored during the annotated structure/class deseriliazation.
++/
+struct serdeDiscriminatedField
+{
+    /// Name of the field in the JSON like data object.
+    string field;
+    /// Value of the field for the current type.
+    string tag;
+}
         
 template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped)
 {

From 8da40523bcdc21fa455be301576303cf5809c3e1 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 8 Mar 2022 16:54:26 +0700
Subject: [PATCH 323/573] add binary heap tests

---
 source/mir/container/binaryheap.d | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/source/mir/container/binaryheap.d b/source/mir/container/binaryheap.d
index cef2fd07..880d4fb7 100644
--- a/source/mir/container/binaryheap.d
+++ b/source/mir/container/binaryheap.d
@@ -397,6 +397,20 @@ BinaryHeap!(less, Store) heapify(alias less = "a < b", Store)(Store s,
     Array!int elements = [1, 2, 10, 12];
     auto heap = heapify(elements);
     assert(heap.front == 12);
+    heap.insert(100);
+    assert(heap.front == 100);
+    heap.insert(1);
+    assert(heap.front == 100);
+}
+
+@system version(mir_test) unittest
+{
+    import std.container.array : Array;
+
+    Array!Object elements;
+    auto heap = heapify(elements);
+    Object obj;
+    heap.insert(obj);
 }
 
 @system nothrow version(mir_test) unittest

From 1da452eaf413b85131f1478d94f6520ffb81e06c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 8 Mar 2022 16:55:34 +0700
Subject: [PATCH 324/573] fix serdeKeysProxy definitions

---
 source/mir/ndslice/slice.d | 2 +-
 source/mir/rc/array.d      | 2 +-
 source/mir/string_map.d    | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index ade4950d..e592369d 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -693,7 +693,7 @@ struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_
     alias DeepElement = typeof(Iterator.init[size_t.init]);
 
     ///
-    alias serdeKeysProxy = DeepElement;
+    alias serdeKeysProxy = Unqual!DeepElement;
 
     /// Label Iterators types
     alias Labels = Labels_;
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 56ea4e8a..bff23275 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -38,7 +38,7 @@ struct mir_rcarray(T)
     package alias _thisPtr = _payload;
 
     ///
-    alias serdeKeysProxy = T;
+    alias serdeKeysProxy = Unqual!T;
 
     ///
     void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index dd6ab993..5f594403 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -37,7 +37,7 @@ struct StringMap(T, U = uint)
     import mir.conv: emplaceRef;
 
     ///
-    alias serdeKeysProxy = T;
+    alias serdeKeysProxy = Unqual!T;
 
     ///
     // current implementation is workaround for linking bugs when used in self referencing algebraic types

From 84b3c247930c62e5af3e020afe5e581b3dd489ad Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Wed, 16 Mar 2022 09:51:18 -0400
Subject: [PATCH 325/573] Update CI files to include requested changes (#394)

* Update CI files to include requested changes

* Split out tests into individual configurations

* allow for selecting individual CI platforms on commit, add skip-ci

* Split out ndslice tests into ndslice-specific version

* [windows-only] fix windows build

* fix badge, change CI to check if tag is at the beginning

* remove fetch depth, comment some bits

* add ndslice cfg to meson
---
 .github/workflows/ci.yml             | 179 ++++++++++++++--
 .github/workflows/compilers.json     |  10 +
 README.md                            |   2 +-
 dub.sdl                              |  28 ++-
 meson.build                          |   2 +-
 source/mir/ndslice/allocation.d      |  52 ++---
 source/mir/ndslice/chunks.d          |  12 +-
 source/mir/ndslice/concatenation.d   |  38 ++--
 source/mir/ndslice/connect/cpython.d |   4 +-
 source/mir/ndslice/dynamic.d         |  40 ++--
 source/mir/ndslice/field.d           |   2 +-
 source/mir/ndslice/filling.d         |   2 +-
 source/mir/ndslice/fuse.d            |  14 +-
 source/mir/ndslice/internal.d        |   4 +-
 source/mir/ndslice/iterator.d        |  32 +--
 source/mir/ndslice/mutation.d        |   4 +-
 source/mir/ndslice/package.d         |  18 +-
 source/mir/ndslice/slice.d           | 228 ++++++++++----------
 source/mir/ndslice/sorting.d         |  80 +++----
 source/mir/ndslice/topology.d        | 302 +++++++++++++--------------
 source/mir/ndslice/traits.d          |  16 +-
 21 files changed, 617 insertions(+), 452 deletions(-)
 create mode 100644 .github/workflows/compilers.json

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d9f4fef4..80bc988c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,41 +10,176 @@ on:
   workflow_dispatch:
     # allow this workflow to be triggered manually
 
+# Only allow for one job to run at a time, and cancel any jobs currently in progress.
+concurrency:
+  group: gh-actions-${{ github.actor }}-${{ github.head_ref }}
+  cancel-in-progress: true
+
 jobs:
-  builder:
-    name: 'Build and test on ${{ matrix.arch }}-${{ matrix.os }}/${{ matrix.dc }}'
-    runs-on: ${{ matrix.os }}
-    continue-on-error: ${{ contains(matrix.dc, 'beta') }}
+  setup:
+    name: 'Load job configuration'
+    runs-on: ubuntu-20.04
+    outputs:
+      compilers: ${{ steps.load-config.outputs.compilers }}
+    steps:
+    - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+    # This step checks if we want to only run tests on a specific platform or
+    # if we want to skip CI entirely, then outputs the compilers to be used for
+    # each job.
+    - id: load-config
+      uses: actions/github-script@9ac08808f993958e9de277fe43a64532a609130e
+      with:
+        script: |
+          const base_compiler_config = require("./.github/workflows/compilers.json");
+          const compilers = {"windows": [], "macos": [], "ubuntu": []};
+          const {owner, repo} = context.repo;
+          let commit_sha = context.sha;
+          if (context.eventName == "pull_request") 
+          {
+            commit_sha = context.payload.pull_request.head.sha;
+          }
+
+          const commit = await github.rest.git.getCommit({
+            owner,
+            repo,
+            commit_sha
+          });
+          const head_commit_message = commit.data.message;
+
+          if (head_commit_message.startsWith("[windows-only]"))
+          {
+            compilers.windows = base_compiler_config;
+          }
+          else if (head_commit_message.startsWith("[macos-only]"))
+          {
+            compilers.macos = base_compiler_config;
+          }
+          else if (head_commit_message.startsWith("[ubuntu-only]"))
+          {
+            compilers.ubuntu = base_compiler_config;
+          }
+          else if (!head_commit_message.startsWith("[skip-ci]"))
+          {
+            compilers.windows = base_compiler_config;
+            compilers.macos = base_compiler_config;
+            compilers.ubuntu = base_compiler_config;
+          }
+          core.setOutput("compilers", JSON.stringify(compilers));
+
+  macos:
+    name: '[macos] x86_64/${{ matrix.dc }}'
+    runs-on: macos-11
+    needs: setup
+    # Only run if the setup phase explicitly defined compilers to be used
+    if: ${{ fromJSON(needs.setup.outputs.compilers).macos != '' && fromJSON(needs.setup.outputs.compilers).macos != '[]' }}
+    # Beta / master versions of any compiler are allowed to fail
+    continue-on-error: ${{ contains(matrix.dc, 'beta') || contains(matrix.dc, 'master') }}
+    env:
+      ARCH: x86_64
+    strategy:
+      fail-fast: false
+      matrix:
+        dc: ${{ fromJSON(needs.setup.outputs.compilers).macos }}
+    steps:
+      - name: Checkout repo 
+        uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - name: Setup D compiler 
+        uses: dlang-community/setup-dlang@763d869b4d67e50c3ccd142108c8bca2da9df166
+        with:
+          compiler: ${{ matrix.dc }}
+      - name: Cache dub dependencies
+        uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
+        with:
+          path: ~/.dub/packages
+          key: macos-latest-build-${{ hashFiles('**/dub.sdl', '**/dub.json') }}
+          restore-keys: |
+            macos-latest-build-
+      - name: Build / test
+        run: |
+          dub test --arch=$ARCH --build=unittest-cov
+          dub test --arch=$ARCH --combined
+        shell: bash
+      - name: Upload coverage data
+        uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b
+
+  ubuntu:
+    name: '[ubuntu] ${{ matrix.arch }}/${{ matrix.dc }}'
+    runs-on: ubuntu-20.04
+    needs: setup
+    # Only run if the setup phase explicitly defined compilers to be used
+    if: ${{ fromJSON(needs.setup.outputs.compilers).ubuntu != '' && fromJSON(needs.setup.outputs.compilers).ubuntu != '[]' }}
+    # Beta / master versions of any compiler are allowed to fail
+    continue-on-error: ${{ contains(matrix.dc, 'beta') || contains(matrix.dc, 'master') }}
     env:
       ARCH: ${{ matrix.arch }}
     strategy:
       fail-fast: false
       matrix:
-        dc: [ldc-latest, ldc-beta, dmd-latest, dmd-beta]
-        os: [ubuntu-latest, windows-latest]
+        dc: ${{ fromJSON(needs.setup.outputs.compilers).ubuntu }}
         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@v2
-      - uses: dlang-community/setup-dlang@v1.1.0
+      - name: Checkout repo 
+        uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - name: Setup D compiler 
+        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
+        if: matrix.arch == 'x86'
+        run: sudo apt-get update && sudo apt-get install gcc-multilib
+      - name: Cache dub dependencies
+        uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
+        with:
+          path: ~/.dub/packages
+          key: ubuntu-latest-build-${{ hashFiles('**/dub.sdl', '**/dub.json') }}
+          restore-keys: |
+            ubuntu-latest-build-
+      - name: Build / test 
         run: |
           dub test --arch=$ARCH --build=unittest-cov
-          dub test --arch=$ARCH --combined
+          dub test --arch=$ARCH --combined 
+        shell: bash
+      - name: Upload coverage data
+        uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b
+  
+  windows:
+    name: '[windows] x86_64/${{ matrix.dc }}'
+    runs-on: windows-2022
+    needs: setup
+    # Only run if the setup phase explicitly defined compilers to be used
+    if: ${{ fromJSON(needs.setup.outputs.compilers).windows != '' && fromJSON(needs.setup.outputs.compilers).windows != '[]' }}
+    # Beta / master versions of any compiler are allowed to fail
+    continue-on-error: ${{ contains(matrix.dc, 'beta') || contains(matrix.dc, 'master') }}
+    env:
+      ARCH: x86_64
+    strategy:
+      fail-fast: false
+      matrix:
+        dc: ${{ fromJSON(needs.setup.outputs.compilers).windows }}
+    steps:
+      - name: Checkout repo 
+        uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
+      - name: Setup D compiler 
+        uses: dlang-community/setup-dlang@763d869b4d67e50c3ccd142108c8bca2da9df166
+        with:
+          compiler: ${{ matrix.dc }}
+      - name: Cache dub dependencies
+        uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
+        with:
+          path: ~\AppData\Local\dub
+          key: windows-latest-build-${{ hashFiles('**/dub.sdl', '**/dub.json') }}
+          restore-keys: |
+            windows-latest-build-
+      # Tests are split up to work around OOM errors -- no combined testing is done
+      # as it's simply too big for the compiler to handle on Windows.
+      - name: Build / test
+        run: |
+          dub test --arch=$ARCH --build=unittest-ci -c ci-bignum-test
+          dub test --arch=$ARCH --build=unittest-ci -c ci-core-test
+          dub test --arch=$ARCH --build=unittest-ci -c ci-ndslice-test
+          dub test --arch=$ARCH --build=unittest-ci -c ci-test
         shell: bash
-      - id: coverage
-        uses: codecov/codecov-action@v2
+      - name: Upload coverage data
+        uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b
     
         
diff --git a/.github/workflows/compilers.json b/.github/workflows/compilers.json
new file mode 100644
index 00000000..dc811c76
--- /dev/null
+++ b/.github/workflows/compilers.json
@@ -0,0 +1,10 @@
+[
+    "dmd-master",
+    "dmd-latest",
+    "dmd-beta",
+    "dmd-2.098.1",
+    "ldc-master",
+    "ldc-latest",
+    "ldc-beta",
+    "ldc-1.28.1"
+]
\ No newline at end of file
diff --git a/README.md b/README.md
index 4cabc6fe..948d924f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 [![codecov.io](https://codecov.io/github/libmir/mir-algorithm/coverage.svg?branch=master)](https://codecov.io/github/libmir/mir-algorithm?branch=master)
-[![Build Status](https://travis-ci.org/libmir/mir-algorithm.svg?branch=master)](https://travis-ci.org/libmir/mir-algorithm)
+[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/libmir/mir-algorithm/CI/master)](https://github.com/libmir/mir-algorithm/actions)
 [![Circle CI](https://circleci.com/gh/libmir/mir-algorithm.svg?style=svg)](https://circleci.com/gh/libmir/mir-algorithm)
 
 [![Dub downloads](https://img.shields.io/dub/dt/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm)
diff --git a/dub.sdl b/dub.sdl
index 0489caae..8366872f 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,12 +9,12 @@ dependency "mir-core" version=">=1.1.88"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_test" "mir_core_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test" "mir_core_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
     dflags "-lowmem" "-preview=dip1008"
 }
 buildType "unittest-dip1000" {
@@ -24,12 +24,16 @@ buildType "unittest-dip1000" {
 }
 buildType "unittest-cov" {
     buildOptions "unittests" "coverage" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
+    dflags "-lowmem"
+}
+buildType "unittest-ci" {
+    buildOptions "unittests" "coverage" "debugMode" "debugInfo"
     dflags "-lowmem"
 }
 buildType "unittest-release" {
     buildOptions "unittests" "releaseMode" "optimize" "inline" "noBoundsCheck"
-    versions "mir_bignum_test" "mir_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 
@@ -47,3 +51,19 @@ configuration "dip1008" {
 configuration "dips" {
     dflags "-preview=dip1000" "-preview=dip1008"
 }
+
+configuration "ci-bignum-test" {
+    versions "mir_bignum_test"
+}
+
+configuration "ci-core-test" {
+    versions "mir_core_test"
+}
+
+configuration "ci-ndslice-test" {
+    versions "mir_ndslice_test"
+}
+
+configuration "ci-test" {
+    versions "mir_test"
+}
diff --git a/meson.build b/meson.build
index 5b2ac982..04028a55 100644
--- a/meson.build
+++ b/meson.build
@@ -117,7 +117,7 @@ this_dep = declare_dependency(
     dependencies: required_deps,
 )
 
-test_versions = ['mir_test', 'mir_bignum_test', 'mir_secure_memory']
+test_versions = ['mir_test', 'mir_bignum_test', 'mir_ndslice_test', 'mir_secure_memory']
 
 if has_cpp_headers
     install_subdir('include/',
diff --git a/source/mir/ndslice/allocation.d b/source/mir/ndslice/allocation.d
index 419b0cff..70fce888 100644
--- a/source/mir/ndslice/allocation.d
+++ b/source/mir/ndslice/allocation.d
@@ -120,7 +120,7 @@ auto rcslice(T, I)(I[] array)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow @nogc unittest
 {
     import mir.ndslice.slice: Slice;
@@ -136,7 +136,7 @@ version(mir_test)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow @nogc unittest
 {
     import mir.ndslice.slice: Slice;
@@ -159,7 +159,7 @@ auto rcslice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow @nogc unittest
 {
     import mir.rc.array: RCI;
@@ -188,7 +188,7 @@ Slice!(RCI!T, N)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow @nogc unittest
 {
     import mir.ndslice.slice: Slice;
@@ -219,7 +219,7 @@ Slice!(FieldIterator!(BitField!(RCI!size_t)), N) bitRcslice(size_t N)(size_t[N]
 
 /// 1D
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto bitarray = 100.bitRcslice; // allocates 16 bytes total (plus RC context)
     assert(bitarray.shape == cast(size_t[1])[100]);
@@ -230,7 +230,7 @@ version(mir_test) unittest
 
 /// 2D
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto bitmatrix = bitRcslice(20, 6); // allocates 16 bytes total (plus RC context)
     assert(bitmatrix.shape == cast(size_t[2])[20, 6]);
@@ -254,7 +254,7 @@ Slice!(RCI!T, N) mininitRcslice(T, size_t N)(size_t[N] lengths...)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 pure nothrow @nogc unittest
 {
     import mir.ndslice.slice: Slice;
@@ -317,7 +317,7 @@ template slice(Args...)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow unittest
 {
     import mir.ndslice.slice: Slice;
@@ -329,7 +329,7 @@ version(mir_test)
 }
 
 /// 2D DataFrame example
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure unittest
 {
     import mir.ndslice.slice;
@@ -410,7 +410,7 @@ auto slice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow unittest
 {
     auto tensor = slice([2, 3], 5);
@@ -436,7 +436,7 @@ auto slice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow unittest
 {
     import mir.ndslice.slice: Slice;
@@ -466,7 +466,7 @@ Slice!(FieldIterator!(BitField!(size_t*)), N) bitSlice(size_t N)(size_t[N] lengt
 }
 
 /// 1D
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     auto bitarray = bitSlice(100); // allocates 16 bytes total
     assert(bitarray.shape == [100]);
@@ -476,7 +476,7 @@ Slice!(FieldIterator!(BitField!(size_t*)), N) bitSlice(size_t N)(size_t[N] lengt
 }
 
 /// 2D
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     auto bitmatrix = bitSlice(20, 6); // allocates 16 bytes total
     assert(bitmatrix.shape == [20, 6]);
@@ -505,7 +505,7 @@ auto uninitSlice(T, size_t N)(size_t[N] lengths...)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow unittest
 {
     import mir.ndslice.slice: Slice;
@@ -535,7 +535,7 @@ auto uninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @system
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @system pure nothrow unittest
 {
     import mir.ndslice.slice: Slice;
@@ -600,7 +600,7 @@ auto makeSlice(Allocator, Iterator, size_t N, SliceKind kind)
 }
 
 /// Initialization with default value
-version(mir_test)
+version(mir_ndslice_test)
 @nogc unittest
 {
     import std.experimental.allocator;
@@ -620,7 +620,7 @@ version(mir_test)
     Mallocator.instance.dispose(ar2);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @nogc unittest
 {
     import std.experimental.allocator;
@@ -664,7 +664,7 @@ makeUninitSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] leng
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @system @nogc unittest
 {
     import std.experimental.allocator;
@@ -701,7 +701,7 @@ auto ndarray(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow unittest
 {
     import mir.ndslice.topology : iota;
@@ -737,7 +737,7 @@ auto makeNdarray(T, Allocator, Iterator, size_t N, SliceKind kind)(auto ref Allo
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @nogc unittest
 {
     import std.experimental.allocator;
@@ -797,7 +797,7 @@ L:
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure unittest
 {
     int err;
@@ -810,7 +810,7 @@ version(mir_test)
 }
 
 /// Slice from ndarray
-version(mir_test)
+version(mir_ndslice_test)
 unittest
 {
     import mir.ndslice.allocation: slice, shape;
@@ -821,7 +821,7 @@ unittest
     assert(s == array);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure unittest
 {
     int err;
@@ -830,7 +830,7 @@ version(mir_test)
     assert(shape[1] == 0);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 nothrow unittest
 {
     import mir.ndslice.allocation;
@@ -915,7 +915,7 @@ void stdcFreeSlice(T, size_t N)(Slice!(T*, N) slice)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 unittest
 {
     import mir.ndslice.topology: iota;
@@ -953,7 +953,7 @@ auto stdcUninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @sys
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @system pure nothrow unittest
 {
     import mir.ndslice.slice: Slice;
diff --git a/source/mir/ndslice/chunks.d b/source/mir/ndslice/chunks.d
index 97dff53c..16acc47e 100644
--- a/source/mir/ndslice/chunks.d
+++ b/source/mir/ndslice/chunks.d
@@ -72,7 +72,7 @@ Chunks!([0], Iterator, N, kind) chunks(Iterator, size_t N, SliceKind kind)(Slice
 
 
 /// 1Dx1D
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     import mir.ndslice.chunks: chunks, isChunks;
     import mir.ndslice.topology: iota;
@@ -112,7 +112,7 @@ Chunks!([0], Iterator, N, kind) chunks(Iterator, size_t N, SliceKind kind)(Slice
 
 /// 2Dx2D
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.chunks: chunks, isChunks;
     import mir.ndslice.topology: iota;
@@ -179,7 +179,7 @@ version(mir_test) unittest
 }
 
 /// 1Dx2D
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.chunks: chunks, isChunks;
     import mir.ndslice.topology: iota;
@@ -209,7 +209,7 @@ version(mir_test) unittest
 }
 
 // conversion to ndslice
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice : slicedField;
     import mir.ndslice.chunks: chunks;
@@ -565,7 +565,7 @@ template isChunks(T)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.chunks: chunks, isChunks;
     import mir.ndslice.topology: iota;
@@ -613,7 +613,7 @@ do
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.chunks: chunks;
     import mir.ndslice.topology: iota;
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index c6672cef..043302fd 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -121,7 +121,7 @@ auto concatenation(size_t dim = 0, Slices...)(Slices slices)
 }
 
 /// Concatenation of slices with different dimmensions. 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: repeat, iota;
@@ -147,7 +147,7 @@ version(mir_test) unittest
 }
 
 /// Multidimensional
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -195,7 +195,7 @@ version(mir_test) unittest
 }
 
 /// 1D
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -416,7 +416,7 @@ auto pad(string direction = "both", S, T, size_t N)(S s, T value, size_t[N] leng
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -429,7 +429,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -510,7 +510,7 @@ template pad(size_t[] dimensions, string[] directions)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -525,7 +525,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -562,7 +562,7 @@ auto padWrap(string direction = "both", Iterator, size_t N, SliceKind kind)(Slic
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -575,7 +575,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -678,7 +678,7 @@ template padWrap(size_t[] dimensions, string[] directions)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -693,7 +693,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -730,7 +730,7 @@ auto padSymmetric(string direction = "both", Iterator, size_t N, SliceKind kind)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -743,7 +743,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -864,7 +864,7 @@ template padSymmetric(size_t[] dimensions, string[] directions)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -879,7 +879,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -916,7 +916,7 @@ auto padEdge(string direction = "both", Iterator, size_t N, SliceKind kind)(Slic
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -929,7 +929,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -1040,7 +1040,7 @@ template padEdge(size_t[] dimensions, string[] directions)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
@@ -1058,7 +1058,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: iota;
diff --git a/source/mir/ndslice/connect/cpython.d b/source/mir/ndslice/connect/cpython.d
index 07584e6f..a3b8a9cd 100644
--- a/source/mir/ndslice/connect/cpython.d
+++ b/source/mir/ndslice/connect/cpython.d
@@ -75,7 +75,7 @@ PythonBufferErrorCode fromPythonBuffer(T, size_t N, SliceKind kind)(ref Slice!(T
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: Slice;
     auto bar(ref const Py_buffer view)
@@ -213,7 +213,7 @@ PythonBufferErrorCode toPythonBuffer(T, size_t N, SliceKind kind)(Slice!(T*, N,
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice : Slice, Structure, Universal, Contiguous, SliceKind;
     Py_buffer bar(SliceKind kind)(Slice!(double*, 2, kind) slice)
diff --git a/source/mir/ndslice/dynamic.d b/source/mir/ndslice/dynamic.d
index 47e625af..9cbcebf3 100644
--- a/source/mir/ndslice/dynamic.d
+++ b/source/mir/ndslice/dynamic.d
@@ -131,7 +131,7 @@ bool normalizeStructure(Iterator, size_t N, SliceKind kind)(ref Slice!(Iterator,
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
 
@@ -233,7 +233,7 @@ Slice!(Iterator, 2, Universal) swapped(Iterator, SliceKind kind)(Slice!(Iterator
 }
 
 /// Template
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -248,7 +248,7 @@ Slice!(Iterator, 2, Universal) swapped(Iterator, SliceKind kind)(Slice!(Iterator
 }
 
 /// Function
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -263,7 +263,7 @@ Slice!(Iterator, 2, Universal) swapped(Iterator, SliceKind kind)(Slice!(Iterator
 }
 
 /// 2D
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -365,7 +365,7 @@ Slice!(Iterator, 2, Universal) rotated(Iterator, SliceKind kind)(Slice!(Iterator
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -431,7 +431,7 @@ auto everted(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slic
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -558,7 +558,7 @@ Slice!(Iterator, 2, Universal) transposed(Iterator, SliceKind kind)(Slice!(Itera
 }
 
 /// Template
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -573,7 +573,7 @@ Slice!(Iterator, 2, Universal) transposed(Iterator, SliceKind kind)(Slice!(Itera
 }
 
 /// Function
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -588,7 +588,7 @@ Slice!(Iterator, 2, Universal) transposed(Iterator, SliceKind kind)(Slice!(Itera
 }
 
 /// Single-argument function
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -603,7 +603,7 @@ Slice!(Iterator, 2, Universal) transposed(Iterator, SliceKind kind)(Slice!(Itera
 }
 
 /// _2-dimensional transpose
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota;
@@ -642,7 +642,7 @@ Slice!(Iterator, N, Universal) allReversed(Iterator, size_t N, SliceKind kind)(S
 
 ///
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.topology: iota, retro;
@@ -719,7 +719,7 @@ auto reversed(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slic
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
 
@@ -747,7 +747,7 @@ auto reversed(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slic
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota, canonical;
     auto slice = iota([2, 2], 1).canonical;
@@ -762,7 +762,7 @@ auto reversed(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slic
     assert(slice.reversed (0, 0, 0) == [[3, 4], [1, 2]]);
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration : equal;
     import mir.ndslice.concatenation : concatenation;
@@ -818,7 +818,7 @@ auto strided(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
     // 0  1   2  3
@@ -880,7 +880,7 @@ Slice!(Iterator, N, Universal) strided(Iterator, size_t N, SliceKind kind)(Slice
 }
 
 ///
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
     auto slice = iota(3, 4);
@@ -910,7 +910,7 @@ pure nothrow version(mir_test) unittest
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota, universal;
     static assert(iota(13, 40).universal.strided!(0, 1)(2, 5).shape == [7, 8]);
@@ -918,7 +918,7 @@ pure nothrow version(mir_test) unittest
 }
 
 ///
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota, canonical;
     auto slice = iota(3, 4).canonical;
@@ -935,7 +935,7 @@ pure nothrow version(mir_test) unittest
         == [[0,1,2,3],            [8,9,10,11]]);
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice;
     import mir.algorithm.iteration : equal;
@@ -986,7 +986,7 @@ Slice!(Iterator, N, Canonical) dropToHypercube(Iterator, size_t N)(Slice!(Iterat
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota, canonical, universal;
 
diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d
index bc79c9ea..b9b6db1a 100644
--- a/source/mir/ndslice/field.d
+++ b/source/mir/ndslice/field.d
@@ -392,7 +392,7 @@ struct BitField(Field, I = typeof(cast()Field.init[size_t.init]))
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.iterator: FieldIterator;
     ushort[10] data;
diff --git a/source/mir/ndslice/filling.d b/source/mir/ndslice/filling.d
index d151a9a7..554f3073 100644
--- a/source/mir/ndslice/filling.d
+++ b/source/mir/ndslice/filling.d
@@ -43,7 +43,7 @@ do {
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: sliced;
     import mir.ndslice.allocation: uninitSlice;
diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index 0407199b..cc488f5b 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -37,7 +37,7 @@ alias fuse(Dimensions...) = fuseImpl!(false, void, Dimensions);
 alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions);
 
 ///
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.slice : Contiguous, Slice;
@@ -66,7 +66,7 @@ alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions);
 }
 
 /// Transposed
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.topology: iota;
@@ -94,7 +94,7 @@ alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions);
 }
 
 /// 3D
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.topology: iota;
@@ -115,7 +115,7 @@ alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions);
 }
 
 /// Work with RC Arrays of RC Arrays
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.slice;
@@ -143,7 +143,7 @@ alias fuseAs(T, Dimensions...) = fuseImpl!(false, T, Dimensions);
 alias rcfuseAs(T, Dimensions...) = fuseImpl!(true, T, Dimensions);
 
 ///
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.slice : Contiguous, Slice;
@@ -368,7 +368,7 @@ auto fuseCells(S)(S cells)
 }
 
 /// 1D
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
     enum ar = [[0, 1], [], [2, 3, 4, 5], [6], [7, 8, 9]];
@@ -377,7 +377,7 @@ auto fuseCells(S)(S cells)
 }
 
 /// 2D
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
     import mir.ndslice.chunks;
diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index b0207e78..93d001fa 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -265,7 +265,7 @@ bool isPermutation(size_t N)(auto ref const scope size_t[N] perm)
     return isValidPartialPermutationImpl(perm, mask);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     assert(isPermutation([0, 1]));
     // all numbers 0..N-1 need to be part of the permutation
@@ -336,7 +336,7 @@ size_t lengthsProduct(size_t N)(auto ref const scope size_t[N] lengths)
     return length;
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     const size_t[3] lengths = [3, 4, 5];
     assert(lengthsProduct(lengths) == 60);
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 6fe737e7..57457d09 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -151,7 +151,7 @@ pure:
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     IotaIterator!int iota;
     assert(*iota == 0);
@@ -177,7 +177,7 @@ pure:
 }
 
 ///
-pure nothrow @nogc version(mir_test) unittest
+pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     int[32] data;
     auto iota = IotaIterator!(int*)(data.ptr);
@@ -209,7 +209,7 @@ auto RetroIterator__map(Iterator, alias fun)(ref RetroIterator!Iterator it)
     return RetroIterator!(typeof(iterator))(iterator);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology;
     import mir.ndslice.allocation;
@@ -286,7 +286,7 @@ struct RetroIterator(Iterator)
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     IotaIterator!int iota;
     RetroIterator!(IotaIterator!int) retro;
@@ -326,7 +326,7 @@ auto StrideIterator__map(Iterator, alias fun)(StrideIterator!Iterator it)
     return StrideIterator!(typeof(iterator))(it._stride, iterator);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology;
     import mir.ndslice.allocation;
@@ -402,7 +402,7 @@ struct StrideIterator(Iterator)
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     IotaIterator!int iota;
     StrideIterator!(IotaIterator!int) stride;
@@ -511,7 +511,7 @@ struct StrideIterator(Iterator, ptrdiff_t factor)
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     IotaIterator!int iota;
     StrideIterator!(IotaIterator!int, -3) stride;
@@ -697,7 +697,7 @@ struct ZipIterator(Iterators...)
 }
 
 ///
-pure nothrow @nogc version(mir_test) unittest
+pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     import mir.ndslice.traits: isIterator;
 
@@ -1149,7 +1149,7 @@ auto _vmapIterator(Iterator, Fun)(Iterator iterator, Fun fun)
        return MapIterator!(Iterator, fun)(iterator);
 }
 
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     // https://github.com/libmir/mir-algorithm/issues/111
     import mir.ndslice.topology : iota, map;
@@ -1482,7 +1482,7 @@ struct SlideIterator(Iterator, size_t params, alias fun)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.functional: naryFun;
     auto data = [1, 3, 8, 18];
@@ -1498,7 +1498,7 @@ auto IndexIterator__map(Iterator, Field, alias fun)(ref IndexIterator!(Iterator,
     return IndexIterator!(Iterator, typeof(field))(it._iterator, field);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology;
     import mir.ndslice.allocation;
@@ -1733,7 +1733,7 @@ public auto FieldIterator__map(Field, alias fun)(FieldIterator!(Field) it)
     return FieldIterator!(typeof(field))(it._index, field);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology;
     import mir.ndslice.allocation;
@@ -1854,7 +1854,7 @@ auto FlattenedIterator__map(Iterator, size_t N, SliceKind kind, alias fun)(Flatt
     return FlattenedIterator!(TemplateArgsOf!(typeof(slice)))(it._indices, slice);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology;
     import mir.ndslice.allocation;
@@ -2029,7 +2029,7 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
     }
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology;
     import mir.ndslice.slice;
@@ -2208,7 +2208,7 @@ struct StairsIterator(Iterator, string direction)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     // 0
     // 1 2
@@ -2233,7 +2233,7 @@ version(mir_test) unittest
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     // [0, 1, 2, 3, 4],
     //    [5, 6, 7, 8],
diff --git a/source/mir/ndslice/mutation.d b/source/mir/ndslice/mutation.d
index d1d81b54..6d0becd4 100644
--- a/source/mir/ndslice/mutation.d
+++ b/source/mir/ndslice/mutation.d
@@ -43,7 +43,7 @@ do {
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest
 {
@@ -68,7 +68,7 @@ void reverseInPlace(Iterator)(Slice!Iterator slice)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest
 {
diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index 4fd18b6f..9bf7fd63 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -38,7 +38,7 @@
 
 8. Memory allocation and algorithm logic should be separated whenever possible.
 
-9. CTFE version(mir_test) unittests should be added to new functions.
+9. CTFE version(mir_ndslice_test) unittests should be added to new functions.
 +/
 
 /**
@@ -511,7 +511,7 @@ public import mir.ndslice.topology;
 public import mir.ndslice.traits;
 
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto matrix = new double[12].sliced(3, 4);
     matrix[] = 0;
@@ -524,7 +524,7 @@ version(mir_test) unittest
 }
 
 // relaxed example
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.qualifier;
 
@@ -585,7 +585,7 @@ version(mir_test) unittest
     }
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     immutable r = 1000.iota;
 
@@ -612,7 +612,7 @@ version(mir_test) unittest
     assert(t1 == iota([6], 12));
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     import mir.array.allocation : array;
@@ -703,7 +703,7 @@ pure nothrow version(mir_test) unittest
     assert(&t1[$ - 1] is &(t1.back()));
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import std.range : iota;
     auto r = (10_000L * 2 * 3 * 4).iota;
@@ -716,14 +716,14 @@ pure nothrow version(mir_test) unittest
     assert(t0.length!3 == 40);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto tensor = new int[3 * 4 * 8].sliced(3, 4, 8);
     assert(&(tensor.back.back.back()) is &tensor[2, 3, 7]);
     assert(&(tensor.front.front.front()) is &tensor[0, 0, 0]);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto slice = new int[24].sliced(2, 3, 4);
     auto r0 = slice.pack!1[1, 2];
@@ -732,7 +732,7 @@ pure nothrow version(mir_test) unittest
     assert(slice[1, 2, 3] == 4);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto ar = new int[3 * 8 * 9];
 
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index e592369d..adf6436a 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -60,7 +60,7 @@ template hasAsSlice(T)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.series;
     static assert(!hasAsSlice!(int[]));
@@ -73,7 +73,7 @@ Check if $(LREF toConst) function can be called with type T.
 enum isConvertibleToSlice(T) = isSlice!T || isDynamicArray!T || hasAsSlice!T;
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.series: SeriesMap;
     static assert(isConvertibleToSlice!(immutable int[]));
@@ -155,7 +155,7 @@ enum isSlice(T) = is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKin
 
 ///
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias A = uint[];
     alias S = Slice!(int*);
@@ -211,7 +211,7 @@ enum kindOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind) =
 
 ///
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     static assert(kindOf!(Slice!(int*, 1, Universal)) == Universal);
 }
@@ -270,7 +270,7 @@ auto sliced(size_t N, Iterator)(Iterator iterator, size_t[N] lengths...)
 }
 
 /// Random access range primitives for slices over user defined types
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     struct MyIota
     {
@@ -312,7 +312,7 @@ Slice!(T*) sliced(T)(T[] array) @trusted
 }
 
 /// Creates a slice from an array.
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     auto slice = new int[10].sliced;
     assert(slice.length == 10);
@@ -345,7 +345,7 @@ Slice!(Iterator, N, kind)
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     auto data = new int[24];
@@ -388,7 +388,7 @@ auto slicedField(Field)(Field field)
 }
 
 /// Creates an 1-dimensional slice over a field, array, or random access range.
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     auto slice = 10.iota.slicedField;
@@ -904,7 +904,7 @@ public:
 
     // static if (doUnittest)
     // ///
-    // @safe pure version(mir_test) unittest
+    // @safe pure version(mir_ndslice_test) unittest
     // {
     //     import mir.ndslice.slice;
     //     alias Array = Slice!(double*);
@@ -922,7 +922,7 @@ public:
     static if (doUnittest)
     /// Creates a 2-dimentional slice with custom strides.
     nothrow pure
-    version(mir_test) unittest
+    version(mir_ndslice_test) unittest
     {
         uint[8] array = [1, 2, 3, 4, 5, 6, 7, 8];
         auto slice = Slice!(uint*, 2, Universal)([2, 2], [4, 1], array.ptr);
@@ -1089,7 +1089,7 @@ public:
 
         static if (doUnittest)
         ///
-        version(mir_test) unittest
+        version(mir_ndslice_test) unittest
         {
             static struct Foo
             {
@@ -1114,7 +1114,7 @@ public:
 
         static if (doUnittest)
         ///
-        version(mir_test) unittest
+        version(mir_ndslice_test) unittest
         {
             Slice!(double*, 2, Universal) nn;
             Slice!(immutable(double)*, 2, Universal) ni;
@@ -1216,7 +1216,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe version(mir_test) unittest
+    @safe version(mir_ndslice_test) unittest
     {
         auto arr = [1, 2, 3, 4];
         auto sl0 = arr.sliced;
@@ -1246,7 +1246,7 @@ public:
 
     static if (doUnittest)
     /// Regular slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         assert(iota(3, 4, 5).shape == cast(size_t[3])[3, 4, 5]);
@@ -1255,7 +1255,7 @@ public:
     static if (doUnittest)
     /// Packed slice
     @safe @nogc pure nothrow
-    version(mir_test) unittest
+    version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : pack, iota;
         size_t[3] s = [3, 4, 5];
@@ -1292,7 +1292,7 @@ public:
     static if (doUnittest)
     /// Regular slice
     @safe @nogc pure nothrow
-    version(mir_test) unittest
+    version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         size_t[3] s = [20, 5, 1];
@@ -1301,7 +1301,7 @@ public:
 
     static if (doUnittest)
     /// Modified regular slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : pack, iota, universal;
         import mir.ndslice.dynamic : reversed, strided, transposed;
@@ -1315,7 +1315,7 @@ public:
 
     static if (doUnittest)
     /// Packed slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : pack, iota;
         size_t[3] s = [20 * 42, 5 * 42, 1 * 42];
@@ -1335,7 +1335,7 @@ public:
 
     static if (doUnittest)
     /// Regular slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         assert(iota(3, 4, 5)
@@ -1344,7 +1344,7 @@ public:
 
     static if (doUnittest)
     /// Modified regular slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : pack, iota, universal;
         import mir.ndslice.dynamic : reversed, strided, transposed;
@@ -1358,7 +1358,7 @@ public:
 
     static if (doUnittest)
     /// Packed slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : pack, iota;
         assert(iota(3, 4, 5, 6, 7)
@@ -1376,7 +1376,7 @@ public:
 
     static if (doUnittest)
     /// Save range
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         auto slice = iota(2, 3).save;
@@ -1384,7 +1384,7 @@ public:
 
     static if (doUnittest)
     /// Pointer type.
-    @safe pure nothrow version(mir_test) unittest
+    @safe pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.allocation;
         //sl type is `Slice!(2, int*)`
@@ -1404,7 +1404,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         auto slice = iota(3, 4, 5);
@@ -1445,7 +1445,7 @@ public:
 
     static if (doUnittest)
     /// Regular slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         auto slice = iota(3, 4, 5);
@@ -1457,7 +1457,7 @@ public:
 
     static if (doUnittest)
     /// Modified regular slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.dynamic : reversed, strided, swapped;
         import mir.ndslice.topology : universal, iota;
@@ -1679,7 +1679,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import std.range.primitives;
         import mir.ndslice.topology : iota, canonical;
@@ -1752,7 +1752,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow @nogc version(mir_test) unittest
+        @safe pure nothrow @nogc version(mir_ndslice_test) unittest
         {
             import mir.ndslice.topology: iota, universal, canonical;
             auto f = 5;
@@ -1776,7 +1776,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow @nogc version(mir_test) unittest
+        @safe pure nothrow @nogc version(mir_ndslice_test) unittest
         {
             import mir.ndslice.topology: iota;
             auto f = 5;
@@ -1794,7 +1794,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.topology: iota, canonical;
             auto v = [2, 3].iota.canonical;
@@ -1813,7 +1813,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.topology: iota, canonical;
             auto v = [2, 3].iota.canonical;
@@ -1854,7 +1854,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure nothrow @nogc version(mir_test) unittest
+    @safe pure nothrow @nogc version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota, canonical;
         auto s = iota(2, 3).canonical;
@@ -1889,7 +1889,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         auto s = iota(2, 3);
@@ -1909,7 +1909,7 @@ public:
 
     static if (doUnittest)
     /// Regular slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         assert(iota(3, 4, 5).elementCount == 60);
@@ -1918,7 +1918,7 @@ public:
 
     static if (doUnittest)
     /// Packed slice
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : pack, evertPack, iota;
         auto slice = iota(3, 4, 5, 6, 7, 8);
@@ -1955,7 +1955,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         auto sl = iota(3, 4);
@@ -1987,7 +1987,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         auto sl = iota(3, 4);
@@ -2020,7 +2020,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe @nogc pure nothrow version(mir_test) unittest
+    @safe @nogc pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology : iota;
         auto sl = iota(3, 4);
@@ -2085,7 +2085,7 @@ public:
     static if (doUnittest)
     ///
     @safe pure nothrow
-    version(mir_test) unittest
+    version(mir_ndslice_test) unittest
     {
         auto a = [1, 2, 3, 4].sliced(2, 2);
 
@@ -2100,7 +2100,7 @@ public:
     }
 
     static if (doUnittest)
-    @safe pure nothrow version(mir_test) unittest
+    @safe pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.allocation: slice;
         import mir.ndslice.topology : iota;
@@ -2277,7 +2277,7 @@ public:
 
     static if (doUnittest)
     ///
-    pure nothrow version(mir_test) unittest
+    pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.allocation;
         auto slice = slice!int(5, 3);
@@ -2307,7 +2307,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure nothrow version(mir_test) unittest
+    @safe pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.allocation: slice;
         auto sli = slice!int(4, 3);
@@ -2342,7 +2342,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure nothrow version(mir_test) unittest
+    @safe pure nothrow version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology: iota;
         import mir.ndslice.allocation: slice;
@@ -2387,7 +2387,7 @@ public:
 
     static if (doUnittest)
     ///
-    version(mir_test) unittest
+    version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology;
 
@@ -2428,7 +2428,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure nothrow @nogc version(mir_test) unittest
+    @safe pure nothrow @nogc version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology;
 
@@ -2442,7 +2442,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure nothrow @nogc version(mir_test) unittest
+    @safe pure nothrow @nogc version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology;
 
@@ -2473,7 +2473,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure nothrow @nogc version(mir_test) unittest
+    @safe pure nothrow @nogc version(mir_ndslice_test) unittest
     {
         import mir.ndslice.topology: iota, map, zip;
 
@@ -2527,7 +2527,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure version(mir_test) unittest
+    @safe pure version(mir_ndslice_test) unittest
     {
         import mir.ndslice;
         auto x = 3.iota!int;
@@ -2582,7 +2582,7 @@ public:
 
     static if (doUnittest)
     ///
-    @safe pure version(mir_test) unittest
+    @safe pure version(mir_ndslice_test) unittest
     {
         import mir.ndslice;
         auto x = 3.iota!int;
@@ -2646,7 +2646,7 @@ public:
     }
 
     ///
-    version(mir_test)
+    version(mir_ndslice_test)
     @safe pure @nogc nothrow
     unittest
     {
@@ -2726,7 +2726,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -2748,7 +2748,7 @@ public:
 
         static if (doUnittest)
         /// Left slice is packed
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.topology : blocks, iota;
             import mir.ndslice.allocation : slice;
@@ -2764,7 +2764,7 @@ public:
 
         static if (doUnittest)
         /// Both slices are packed
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.topology : blocks, iota, pack;
             import mir.ndslice.allocation : slice;
@@ -2850,7 +2850,7 @@ public:
 
         static if (doUnittest)
         ///
-        pure nothrow version(mir_test) unittest
+        pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -2874,7 +2874,7 @@ public:
 
         static if (doUnittest)
         /// Packed slices
-        pure nothrow version(mir_test) unittest
+        pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation : slice;
             import mir.ndslice.topology : blocks;
@@ -2956,7 +2956,7 @@ public:
         static if (doUnittest)
         ///
         @safe pure nothrow
-        version(mir_test) unittest
+        version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -2983,7 +2983,7 @@ public:
 
         static if (doUnittest)
         /// Packed slices have the same behavior.
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             import mir.ndslice.topology : pack;
@@ -3019,7 +3019,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -3029,7 +3029,7 @@ public:
         }
 
         static if (doUnittest)
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             auto a = new int[6].sliced(2, 3);
 
@@ -3059,7 +3059,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -3069,7 +3069,7 @@ public:
         }
 
         static if (doUnittest)
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             auto a = new int[6].sliced(2, 3);
 
@@ -3092,7 +3092,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -3113,7 +3113,7 @@ public:
 
         static if (doUnittest)
         /// Left slice is packed
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation : slice;
             import mir.ndslice.topology : blocks, iota;
@@ -3129,7 +3129,7 @@ public:
 
         static if (doUnittest)
         /// Both slices are packed
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation : slice;
             import mir.ndslice.topology : blocks, iota, pack;
@@ -3157,7 +3157,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation : slice;
             auto a = slice!int(2, 3);
@@ -3178,7 +3178,7 @@ public:
         static if (doUnittest)
         /// Packed slices
         @safe pure nothrow
-        version(mir_test) unittest
+        version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation : slice;
             import mir.ndslice.topology : blocks;
@@ -3236,7 +3236,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -3262,7 +3262,7 @@ public:
 
         static if (doUnittest)
         /// Packed slices have the same behavior.
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             import mir.ndslice.topology : pack;
@@ -3291,7 +3291,7 @@ public:
 
         static if (doUnittest)
         ///
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -3302,7 +3302,7 @@ public:
 
         // Issue 16473
         static if (doUnittest)
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto sl = slice!double(2, 5);
@@ -3310,7 +3310,7 @@ public:
         }
 
         static if (doUnittest)
-        @safe pure nothrow version(mir_test) unittest
+        @safe pure nothrow version(mir_ndslice_test) unittest
         {
             auto a = new int[6].sliced(2, 3);
 
@@ -3351,7 +3351,7 @@ public:
         static if (doUnittest)
         ///
         @safe pure nothrow
-        version(mir_test) unittest
+        version(mir_ndslice_test) unittest
         {
             import mir.ndslice.allocation;
             auto a = slice!int(2, 3);
@@ -3372,7 +3372,7 @@ alias Slice = mir_slice;
 /++
 Slicing, indexing, and arithmetic operations.
 +/
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.dynamic : transposed;
@@ -3406,7 +3406,7 @@ pure nothrow version(mir_test) unittest
 /++
 Operations with rvalue slices.
 +/
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.topology: universal;
@@ -3440,7 +3440,7 @@ pure nothrow version(mir_test) unittest
 Creating a slice from text.
 See also $(MREF std, format).
 +/
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: filter, all;
     import mir.array.allocation;
@@ -3482,7 +3482,7 @@ version(mir_test) unittest
 }
 
 // Slicing
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     auto a = iota(10, 20, 30, 40);
@@ -3495,7 +3495,7 @@ version(mir_test) unittest
 }
 
 // Operator overloading. # 1
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.topology : iota;
@@ -3515,7 +3515,7 @@ pure nothrow version(mir_test) unittest
 }
 
 // Operator overloading. # 2
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: map, iota;
     import mir.array.allocation : array;
@@ -3537,7 +3537,7 @@ pure nothrow version(mir_test) unittest
 }
 
 // Operator overloading. # 3
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.topology : iota;
@@ -3559,7 +3559,7 @@ pure nothrow version(mir_test) unittest
 }
 
 // Type deduction
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     // Arrays
     foreach (T; AliasSeq!(int, const int, immutable int))
@@ -3573,7 +3573,7 @@ version(mir_test) unittest
 }
 
 // Test for map #1
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: map, byDim;
     auto slice = [1, 2, 3, 4].sliced(2, 2);
@@ -3595,7 +3595,7 @@ version(mir_test) unittest
 }
 
 // Test for map #2
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: map, byDim;
     import std.range.primitives;
@@ -3656,7 +3656,7 @@ private bool _checkAssignLengths(
     }
 }
 
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
 
@@ -3666,7 +3666,7 @@ private bool _checkAssignLengths(
     assert(!_checkAssignLengths(iota(2, 2), iota(3, 3)));
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto slice = new int[15].slicedField(5, 3);
 
@@ -3679,7 +3679,7 @@ pure nothrow version(mir_test) unittest
     auto col = slice[0..$, 1];
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
     auto b = [1, 2, 3, 4].sliced(2, 2);
@@ -3697,7 +3697,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1] == [1, 2, 0]);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
     auto b = [[1, 2], [3, 4]];
@@ -3718,7 +3718,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1] == [3, 4, 6]);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3741,7 +3741,7 @@ pure nothrow version(mir_test) unittest
     //assert(a[1] == [5, 5, 9]);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3749,7 +3749,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1, 2] == 3);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3757,7 +3757,7 @@ pure nothrow version(mir_test) unittest
     assert(a[[1, 2]] == 3);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3765,7 +3765,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1, 2] == 3);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3773,7 +3773,7 @@ pure nothrow version(mir_test) unittest
     assert(a[[1, 2]] == 3);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
     auto b = [1, 2, 3, 4].sliced(2, 2);
@@ -3791,7 +3791,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1] == [8, 12, 0]);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3808,7 +3808,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1] == [8, 12, 0]);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3822,7 +3822,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1] == [6, 6, 1]);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3830,7 +3830,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1, 2] == 1);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3838,7 +3838,7 @@ pure nothrow version(mir_test) unittest
     assert(a[[1, 2]] == 1);
 }
 
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = new int[6].slicedField(2, 3);
 
@@ -3849,7 +3849,7 @@ pure nothrow version(mir_test) unittest
     assert(a[1] == [0, 0, 1]);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota, universal;
 
@@ -3857,13 +3857,13 @@ version(mir_test) unittest
     assert(sl[0 .. $] == sl);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: canonical, iota;
     static assert(kindOf!(typeof(iota([1, 2]).canonical[1])) == Contiguous);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
     auto s = iota(2, 3);
@@ -3897,7 +3897,7 @@ auto ndassign(string op = "", L, R)(L lside, auto ref R rside) @property
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota;
     import mir.ndslice.allocation: slice;
@@ -3917,7 +3917,7 @@ version(mir_test) unittest
     assert(vector == [5, 7, 9]);
 }
 
-version(mir_test) pure nothrow unittest
+version(mir_ndslice_test) pure nothrow unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: universal;
@@ -3946,7 +3946,7 @@ version(mir_test) pure nothrow unittest
     assert(immdf2.label!1[1] == 2);
 }
 
-version(mir_test) pure nothrow unittest
+version(mir_ndslice_test) pure nothrow unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.topology: universal;
@@ -3962,7 +3962,7 @@ version(mir_test) pure nothrow unittest
     assert(immvalues[0][0] == 5);
 }
 
-version(mir_test) @safe unittest
+version(mir_ndslice_test) @safe unittest
 {
     import mir.ndslice.allocation;
     auto a = rcslice!double([2, 3], 0);
@@ -3972,7 +3972,7 @@ version(mir_test) @safe unittest
     assert(a == b);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -3986,7 +3986,7 @@ unittest
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -4000,7 +4000,7 @@ unittest
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -4016,7 +4016,7 @@ unittest
 }
 
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -4032,7 +4032,7 @@ unittest
 }
 
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -4048,7 +4048,7 @@ unittest
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -4064,7 +4064,7 @@ unittest
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -4078,7 +4078,7 @@ unittest
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest
 {
@@ -4092,7 +4092,7 @@ unittest
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure @nogc nothrow
 unittest // check it can be compiled
 {
@@ -4101,7 +4101,7 @@ unittest // check it can be compiled
     alias D = Variant!S;
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 unittest
 {
     import mir.ndslice;
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index da0103bf..1c869b81 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -24,7 +24,7 @@ Macros:
 module mir.ndslice.sorting;
 
 /// Check if ndslice is sorted, or strictly monotonic.
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.slice: sliced;
@@ -48,7 +48,7 @@ module mir.ndslice.sorting;
 }
 
 /// Create index
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.allocation: slice;
@@ -65,7 +65,7 @@ version(mir_test) unittest
 }
 
 /// Schwartzian transform
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.allocation: slice;
@@ -87,7 +87,7 @@ import mir.math.common: optmath;
 
 @optmath:
 
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.topology: pairwise;
@@ -104,7 +104,7 @@ import mir.math.common: optmath;
     assert(c.pairwise!"a <= b".all);
 }
 
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.topology: pairwise;
@@ -212,7 +212,7 @@ template sort(alias less = "a < b")
 }
 
 ///
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: all;
     import mir.ndslice.slice;
@@ -227,7 +227,7 @@ template sort(alias less = "a < b")
 }
 
 /// one-dimensional series
-pure version(mir_test) unittest
+pure version(mir_ndslice_test) unittest
 {
     import mir.series;
 
@@ -248,7 +248,7 @@ pure version(mir_test) unittest
 }
 
 /// two-dimensional series
-pure version(mir_test) unittest
+pure version(mir_ndslice_test) unittest
 {
     import mir.series;
     import mir.ndslice.allocation: uninitSlice;
@@ -579,7 +579,7 @@ template assumeSortedEqualIndex(alias test = "a < b")
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure unittest
 {
     // sorted: a < b
@@ -644,7 +644,7 @@ template transitionIndex(alias test = "a < b")
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure unittest
 {
     // sorted: a < b
@@ -709,7 +709,7 @@ I[] makeIndex(I = size_t, alias less = "a < b", T)(scope T[] r)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest
 {
@@ -723,7 +723,7 @@ unittest
 }
 
 /// Sort based on index created from a separate array
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest
 {
@@ -811,7 +811,7 @@ template pivotPartition(alias less = "a < b")
 }
 
 /// pivotPartition with 1-dimensional Slice
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest
 {
@@ -826,7 +826,7 @@ unittest
 }
 
 /// pivotPartition with 2-dimensional Slice
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest
 {
@@ -846,7 +846,7 @@ unittest
     assert(xFlattened[pivot .. $].all!(a => a >= xFlattened[pivot]));
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe
 unittest
 {
@@ -943,7 +943,7 @@ template pivotPartitionImpl(alias less)
             for (;;)
             {
                 // Loop invariant
-                version(mir_test)
+                version(mir_ndslice_test)
                 {
                     // this used to import std.algorithm.all, but we want to
                     // save imports when unittests are enabled if possible.
@@ -1004,7 +1004,7 @@ template pivotPartitionImpl(alias less)
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest {
     import mir.ndslice.sorting: partitionAt;
@@ -1018,7 +1018,7 @@ unittest {
 }
 
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest
 {
@@ -1091,7 +1091,7 @@ template partitionAt(alias less = "a < b")
 }
 
 /// Partition 1-dimensional slice at nth
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1103,7 +1103,7 @@ unittest {
 }
 
 /// Partition 2-dimensional slice
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1115,7 +1115,7 @@ unittest {
 }
 
 /// Can supply alternate ordering function
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1127,7 +1127,7 @@ unittest {
 }
 
 // Check issue #328 fixed
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1172,7 +1172,7 @@ version(unittest) {
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1306,7 +1306,7 @@ void partitionAtImpl(alias less, Iterator)(
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1319,7 +1319,7 @@ unittest {
     assert(x[nth] == 2);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1332,7 +1332,7 @@ unittest {
     assert(x[2, 0] == 5);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1345,7 +1345,7 @@ unittest {
     assert(x[nth] == 0);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1358,7 +1358,7 @@ unittest {
     assert(x[nth] == 3);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1371,7 +1371,7 @@ unittest {
     assert(x[nth] == 3);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1384,7 +1384,7 @@ unittest {
     assert(x[nth] == 7);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1397,7 +1397,7 @@ unittest {
     assert(x[nth] == 8);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1411,7 +1411,7 @@ unittest {
 }
 
 // Check all partitionAt
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1491,7 +1491,7 @@ Iterator partitionAtPartition(alias less, Iterator)(
     return expandPartition!less(frontI, lastI, loI, pivotI, hiI);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1549,7 +1549,7 @@ Iterator partitionAtPartitionOffMedian(alias less, bool leanRight, Iterator)(
     return expandPartition!less(frontI, lastI, loI, pivotI, hiI);
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1562,7 +1562,7 @@ unittest {
     assert(x.equal([6, 7, 8, 9, 5, 0, 2, 7, 9, 15, 10, 25, 11, 10, 13, 18, 17, 13, 25, 22]));
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1598,7 +1598,7 @@ void p3(alias less, Iterator)(
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1656,7 +1656,7 @@ template p4(alias less, bool leanRight)
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1671,7 +1671,7 @@ unittest {
     assert(x.equal([3, 1, 0, 4, 2, 6, 4, 7, 5]));
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest {
     import mir.ndslice.slice: sliced;
@@ -1806,7 +1806,7 @@ template expandPartition(alias less)
     }
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 @trusted pure nothrow
 unittest
 {
@@ -1821,7 +1821,7 @@ unittest
     assert(expandPartition!((a, b) => a < b)(frontI, lastI, loI, pivotI, hiI) == (frontI + 9));
 }
 
-version(mir_test)
+version(mir_ndslice_test)
 unittest
 {
     import std.random;
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 9aeb0c0c..05ddde30 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -165,7 +165,7 @@ template asKindOf(SliceKind kind)
 
 /// Universal
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: Universal;
     auto slice = iota(2, 3).asKindOf!Universal;
@@ -176,7 +176,7 @@ version(mir_test) unittest
 
 /// Canonical
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: Canonical;
     auto slice = iota(2, 3).asKindOf!Canonical;
@@ -187,7 +187,7 @@ version(mir_test) unittest
 
 /// Contiguous
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: Contiguous;
     auto slice = iota(2, 3).asKindOf!Contiguous;
@@ -250,7 +250,7 @@ auto universal(Iterator, size_t N, SliceKind kind, Labels...)(Slice!(Iterator, N
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto slice = iota(2, 3).universal;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
@@ -259,7 +259,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto slice = iota(2, 3).canonical.universal;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
@@ -269,7 +269,7 @@ version(mir_test) unittest
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.allocation: slice;
@@ -329,7 +329,7 @@ Slice!(Iterator, N, N == 1 ? Contiguous : Canonical, Labels)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto slice = iota(2, 3).canonical;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
@@ -339,7 +339,7 @@ version(mir_test) unittest
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.allocation: slice;
@@ -396,7 +396,7 @@ Slice!(Iterator, N, Canonical, Labels)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto slice = iota(2, 3).universal.assumeCanonical;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
@@ -406,7 +406,7 @@ version(mir_test) unittest
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.allocation: slice;
@@ -458,7 +458,7 @@ Slice!(Iterator, N, Contiguous, Labels)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto slice = iota(2, 3).universal.assumeContiguous;
     assert(slice == [[0, 1, 2], [3, 4, 5]]);
@@ -468,7 +468,7 @@ version(mir_test) unittest
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.allocation: slice;
@@ -502,7 +502,7 @@ void assumeHypercube
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     auto b = iota(5, 5);
     
@@ -526,7 +526,7 @@ void assumeSameShape(T...)
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     auto a = iota(5, 5);
     auto b = iota(5, 5);
@@ -568,7 +568,7 @@ pack(size_t P, Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) sli
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: sliced, Slice;
 
@@ -622,7 +622,7 @@ ipack(size_t P, Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) sl
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: sliced, Slice;
 
@@ -696,7 +696,7 @@ evertPack(Iterator, size_t M, SliceKind innerKind, size_t N, SliceKind outerKind
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.dynamic : transposed;
     auto slice = iota(3, 4, 5, 6, 7, 8, 9, 10, 11).universal;
@@ -710,7 +710,7 @@ evertPack(Iterator, size_t M, SliceKind innerKind, size_t N, SliceKind outerKind
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.iterator: SliceIterator;
     import mir.ndslice.slice: sliced, Slice, Universal;
@@ -726,7 +726,7 @@ evertPack(Iterator, size_t M, SliceKind innerKind, size_t N, SliceKind outerKind
 
 ///
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto a = iota(3, 4, 5, 6, 7, 8, 9, 10, 11);
     auto b = a.pack!2.unpack;
@@ -819,7 +819,7 @@ iota
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     import mir.primitives: DeepElementType;
     auto slice = iota(2, 3);
@@ -834,7 +834,7 @@ iota
 
 ///
 pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     int[6] data;
     auto slice = iota([2, 3], data.ptr);
@@ -845,7 +845,7 @@ version(mir_test) unittest
 
 ///
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto im = iota([10, 5], 100);
     assert(im[2, 1] == 111); // 100 + 2 * 5 + 1
@@ -856,7 +856,7 @@ version(mir_test) unittest
 }
 
 /// `iota` with step
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     auto sl = iota([2, 3], 10, 10);
 
@@ -906,7 +906,7 @@ Slice!(Iterator, 1, N == 1 ? kind : Universal)
 }
 
 /// Matrix, main diagonal
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     //  -------
     // | 0 1 2 |
@@ -919,7 +919,7 @@ Slice!(Iterator, 1, N == 1 ? kind : Universal)
 }
 
 /// Non-square matrix
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     //  -------
     // | 0 1 |
@@ -933,7 +933,7 @@ Slice!(Iterator, 1, N == 1 ? kind : Universal)
 }
 
 /// Loop through diagonal
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.allocation;
@@ -950,7 +950,7 @@ Slice!(Iterator, 1, N == 1 ? kind : Universal)
 
 /// Matrix, subdiagonal
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     //  -------
     // | 0 1 2 |
@@ -965,7 +965,7 @@ version(mir_test) unittest
 }
 
 /// 3D, main diagonal
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     //  -----------
     // |  0   1  2 |
@@ -981,7 +981,7 @@ version(mir_test) unittest
 }
 
 /// 3D, subdiagonal
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     //  -----------
     // |  0   1  2 |
@@ -1000,7 +1000,7 @@ version(mir_test) unittest
 
 /// 3D, diagonal plain
 @nogc @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     //  -----------
     // |  0   1  2 |
@@ -1065,7 +1065,7 @@ Slice!(Iterator, 1, Universal)
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     //  -----
     // | 0 1 |
@@ -1079,7 +1079,7 @@ Slice!(Iterator, 1, Universal)
 }
 
 ///
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     //  -------
     // | 0 1 2 |
@@ -1140,7 +1140,7 @@ do
 }
 
 ///
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.allocation;
@@ -1168,7 +1168,7 @@ pure nothrow version(mir_test) unittest
 }
 
 /// Diagonal blocks
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.allocation;
@@ -1200,7 +1200,7 @@ pure nothrow version(mir_test) unittest
 }
 
 /// Matrix divided into vertical blocks
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.slice;
@@ -1272,7 +1272,7 @@ do
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.slice;
@@ -1295,7 +1295,7 @@ version(mir_test) unittest
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.slice;
@@ -1316,7 +1316,7 @@ version(mir_test) unittest
 }
 
 /// Multi-diagonal matrix
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.slice;
@@ -1341,7 +1341,7 @@ version(mir_test) unittest
 }
 
 /// Sliding window over matrix columns
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
     import mir.ndslice.slice;
@@ -1364,7 +1364,7 @@ version(mir_test) unittest
 }
 
 /// Overlapping blocks using windows
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     //  ----------------
     // |  0  1  2  3  4 |
@@ -1399,7 +1399,7 @@ version(mir_test) unittest
               [[12, 13, 14], [17, 18, 19], [22, 23, 24]]]]);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto w = iota(9, 9).windows(3, 3);
     assert(w.front == w[0]);
@@ -1521,7 +1521,7 @@ Slice!(Iterator, M, kind) reshape
 
 ///
 @safe nothrow pure
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.dynamic : allReversed;
     int err;
@@ -1538,7 +1538,7 @@ version(mir_test) unittest
 }
 
 /// Reshaping with memory allocation
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: sliced;
     import mir.ndslice.allocation: slice;
@@ -1569,7 +1569,7 @@ version(mir_test) unittest
          [ 1, 2,  3]]);
 }
 
-nothrow @safe pure version(mir_test) unittest
+nothrow @safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.dynamic : allReversed;
     auto slice = iota(1, 1, 3, 2, 1, 2, 1).universal.allReversed;
@@ -1584,14 +1584,14 @@ nothrow @safe pure version(mir_test) unittest
 
 // Issue 15919
 nothrow @nogc @safe pure
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     int err;
     assert(iota(3, 4, 5, 6, 7).pack!2.reshape([4, 3, 5], err)[0, 0, 0].shape == cast(size_t[2])[6, 7]);
     assert(err == 0);
 }
 
-nothrow @nogc @safe pure version(mir_test) unittest
+nothrow @nogc @safe pure version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
 
@@ -1607,7 +1607,7 @@ nothrow @nogc @safe pure version(mir_test) unittest
 }
 
 nothrow @nogc @safe pure
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto pElements = iota(3, 4, 5, 6, 7)
         .pack!2
@@ -1670,7 +1670,7 @@ Slice!(StrideIterator!Iterator)
     return slice.move.hideStride;
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     auto sl1 = iota(2, 3).slice.universal.pack!1.flattened;
@@ -1679,14 +1679,14 @@ version(mir_test) unittest
 }
 
 /// Regular slice
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     assert(iota(4, 5).flattened == iota(20));
     assert(iota(4, 5).canonical.flattened == iota(20));
     assert(iota(4, 5).universal.flattened == iota(20));
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     assert(iota(4).flattened == iota(4));
     assert(iota(4).canonical.flattened == iota(4));
@@ -1694,7 +1694,7 @@ version(mir_test) unittest
 }
 
 /// Packed slice
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.dynamic;
@@ -1702,7 +1702,7 @@ version(mir_test) unittest
 }
 
 /// Properties
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     auto elems = iota(3, 4).universal.flattened;
 
@@ -1717,7 +1717,7 @@ version(mir_test) unittest
 }
 
 /// Index property
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     auto slice = new long[20].sliced(5, 4);
@@ -1735,7 +1735,7 @@ version(mir_test) unittest
          [40, 43, 46, 49]]);
 }
 
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     auto elems = iota(3, 4).universal.flattened;
     assert(elems.front == 0);
@@ -1745,7 +1745,7 @@ version(mir_test) unittest
 /++
 Random access and slicing
 +/
-nothrow version(mir_test) unittest
+nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.slice: sliced;
@@ -1777,7 +1777,7 @@ nothrow version(mir_test) unittest
     assert(elems[2 .. 6] == sl);
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.dynamic : allReversed;
 
@@ -1799,7 +1799,7 @@ nothrow version(mir_test) unittest
     }
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import std.range.primitives : isRandomAccessRange, hasSlicing;
     auto elems = iota(4, 5).flattened;
@@ -1808,7 +1808,7 @@ nothrow version(mir_test) unittest
 }
 
 // Checks strides
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.dynamic;
     import std.range.primitives : isRandomAccessRange;
@@ -1824,7 +1824,7 @@ nothrow version(mir_test) unittest
     }
 }
 
-@safe @nogc pure nothrow version(mir_test) unittest
+@safe @nogc pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice;
     import mir.ndslice.dynamic;
@@ -1865,7 +1865,7 @@ nothrow version(mir_test) unittest
 }
 
 // Issue 15549
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import std.range.primitives;
     import mir.ndslice.allocation;
@@ -1880,7 +1880,7 @@ version(mir_test) unittest
 }
 
 // Issue 16010
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto s = iota(3, 4).flattened;
     foreach (_; 0 .. s.length)
@@ -1908,7 +1908,7 @@ Slice!(FieldIterator!(ndIotaField!N), N)
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     auto slice = ndiota(2, 3);
     static immutable array =
@@ -1919,7 +1919,7 @@ Slice!(FieldIterator!(ndIotaField!N), N)
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     auto im = ndiota(7, 9);
 
@@ -1930,7 +1930,7 @@ Slice!(FieldIterator!(ndIotaField!N), N)
     assert(cm[2, 1] == [3, 5]);
 }
 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto r = ndiota(1);
     auto d = r.front;
@@ -1966,7 +1966,7 @@ auto linspace(T, size_t N)(size_t[N] lengths, T[2][N] intervals...)
 }
 
 // example from readme
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice;
     // import std.stdio: writefln;
@@ -1985,7 +1985,7 @@ version(mir_test) unittest
 
 /// 1D
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto s = linspace!double([5], [1.0, 2.0]);
     assert(s == [1.0, 1.25, 1.5, 1.75, 2.0]);
@@ -2000,7 +2000,7 @@ version(mir_test) unittest
 
 /// 2D
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.functional: refTuple;
 
@@ -2025,7 +2025,7 @@ version(mir_test) unittest
 
 /// Complex numbers
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.complex;
     alias C = Complex!double;
@@ -2071,7 +2071,7 @@ Slice!(SliceIterator!(Iterator, N, kind), M, Universal)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto sl = iota(3).repeat(4);
     assert(sl == [[0, 1, 2],
@@ -2081,7 +2081,7 @@ version(mir_test) unittest
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.dynamic : transposed;
 
@@ -2097,7 +2097,7 @@ version(mir_test) unittest
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation;
 
@@ -2113,7 +2113,7 @@ version(mir_test) unittest
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.primitives: DeepElementType;
 
@@ -2186,7 +2186,7 @@ auto cycle(size_t loopLength, T)(T withAsSlice, size_t length)
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     auto slice = iota(3);
     assert(slice.cycle(7) == [0, 1, 2, 0, 1, 2, 0]);
@@ -2279,7 +2279,7 @@ auto stride(T)(T withAsSlice, ptrdiff_t factor)
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     auto slice = iota(6);
     static immutable str = [0, 2, 4];
@@ -2290,7 +2290,7 @@ auto stride(T)(T withAsSlice, ptrdiff_t factor)
 }
 
 /// ND-compile time
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     auto slice = iota(4, 6);
     static immutable str = [[0, 2, 4], [12, 14, 16]];
@@ -2435,7 +2435,7 @@ struct RetroRange(Range)
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     auto slice = iota(2, 3);
     static immutable reversed = [[5, 4, 3], [2, 1, 0]];
@@ -2449,7 +2449,7 @@ struct RetroRange(Range)
 }
 
 /// Ranges
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: equal;
     import std.range: std_iota = iota;
@@ -2515,7 +2515,7 @@ auto bitwise(T)(T withAsSlice)
 
 ///
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     size_t[10] data;
     auto bits = data[].bitwise;
@@ -2532,7 +2532,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     size_t[10] data;
     auto slice = FieldIterator!(size_t[])(0, data[]).sliced(10);
@@ -2626,7 +2626,7 @@ auto bitpack(size_t pack, T)(T withAsSlice)
 
 ///
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     size_t[10] data;
     // creates a packed unsigned integer slice with max allowed value equal to `2^^6 - 1 == 63`.
@@ -2679,7 +2679,7 @@ auto bytegroup(size_t pack, DestinationType, T)(T withAsSlice)
 
 /// 24 bit integers
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: DeepElementType, sliced;
 
@@ -2701,7 +2701,7 @@ version(mir_test) unittest
 
 /// 48 bit integers
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: DeepElementType, sliced;
     ushort[20] data;
@@ -2853,7 +2853,7 @@ struct MapRange(alias fun, Range)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     auto s = iota(2, 3).map!(a => a * 3);
@@ -2863,7 +2863,7 @@ version(mir_test) unittest
 
 /// String lambdas
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     assert(iota(2, 3).map!"a * 2" == [[0, 2, 4], [6, 8, 10]]);
@@ -2871,7 +2871,7 @@ version(mir_test) unittest
 
 /// Input ranges
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: filter, equal;
     assert (6.iota.filter!"a % 2".map!"a * 10".equal([10, 30, 50])); 
@@ -2879,7 +2879,7 @@ version(mir_test) unittest
 
 /// Packed tensors
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota, windows;
     import mir.math.sum: sum;
@@ -2900,7 +2900,7 @@ version(mir_test) unittest
 
 /// Zipped tensors
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota, zip;
 
@@ -2923,7 +2923,7 @@ In that case, the element type of `map` is a refTuple containing
 one element for each function.
 +/
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
 
@@ -2940,7 +2940,7 @@ version(mir_test) unittest
 /++
 `map` can be aliased to a symbol and be used separately:
 +/
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
 
@@ -2951,7 +2951,7 @@ pure nothrow version(mir_test) unittest
 /++
 Type normalization
 +/
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.functional : pipe;
     import mir.ndslice.topology : iota;
@@ -2962,7 +2962,7 @@ version(mir_test) unittest
 }
 
 /// Use map with byDim/alongDim to apply functions to each dimension
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest
 {
@@ -2988,7 +2988,7 @@ unittest
 Use map with a lambda and with byDim/alongDim, but may need to allocate result. 
 This example uses fuse, which allocates. Note: fuse!1 will transpose the result. 
 +/
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest {
     import mir.ndslice.topology: iota, byDim, alongDim, map;
@@ -3013,7 +3013,7 @@ unittest {
 }
 
 ///
-pure version(mir_test) unittest
+pure version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: reduce;
     import mir.math.common: fmax;
@@ -3075,7 +3075,7 @@ auto vmap(T, Callable)(T withAsSlice, Callable callable)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
 
@@ -3094,7 +3094,7 @@ version(mir_test) unittest
 
 /// Packed tensors.
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.math.sum: sum;
     import mir.ndslice.topology : iota, windows;
@@ -3128,7 +3128,7 @@ version(mir_test) unittest
 
 /// Zipped tensors
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota, zip;
 
@@ -3165,7 +3165,7 @@ In that case, the element type of `vmap` is a refTuple containing
 one element for each function.
 +/
 @safe pure nothrow
-version(none) version(mir_test) unittest
+version(none) version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
 
@@ -3184,7 +3184,7 @@ version(none) version(mir_test) unittest
 }
 
 /// Use vmap with byDim/alongDim to apply functions to each dimension
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest
 {
@@ -3219,7 +3219,7 @@ unittest
 Use vmap with a lambda and with byDim/alongDim, but may need to allocate result. 
 This example uses fuse, which allocates. Note: fuse!1 will transpose the result. 
 +/
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest {
     import mir.ndslice.topology: iota, alongDim, map;
@@ -3410,7 +3410,7 @@ template rcmap(fun...)
 
 /// Returns RCArray for input ranges and one-dimensional slices.
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: filter, equal;
     auto factor = 10;
@@ -3421,7 +3421,7 @@ version(mir_test) unittest
 
 /// For multidimensional case returns `Slice!(RCI!T, N)`.
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     auto factor = 3;
@@ -3431,14 +3431,14 @@ version(mir_test) unittest
 
 /// String lambdas
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     assert(iota(2, 3).rcmap!"a * 2" == iota(2, 3) * 2);
 }
 
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: filter, equal;
     auto factor = 10;
@@ -3477,7 +3477,7 @@ Slice!(CachedIterator!(Iterator, CacheIterator, FlagIterator), N, kind)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: cached, iota, map;
     import mir.ndslice.allocation: bitSlice, uninitSlice;
@@ -3514,7 +3514,7 @@ version(mir_test) unittest
 
 /// Cache of immutable elements
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: DeepElementType;
     import mir.ndslice.topology: cached, iota, map, as;
@@ -3589,7 +3589,7 @@ auto cachedGC(T)(T withAsSlice)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: cachedGC, iota, map;
 
@@ -3623,7 +3623,7 @@ version(mir_test) unittest
 
 /// Cache of immutable elements
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: DeepElementType;
     import mir.ndslice.topology: cachedGC, iota, map, as;
@@ -3709,7 +3709,7 @@ template as(T)
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: Slice;
     import mir.ndslice.allocation : slice;
@@ -3731,7 +3731,7 @@ template as(T)
 }
 
 /// Special behavior for pointers to a constant data.
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation : slice;
     import mir.ndslice.slice: Contiguous, Slice;
@@ -3741,7 +3741,7 @@ template as(T)
 }
 
 /// Ranges
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.algorithm.iteration: filter, equal;
     assert(5.iota.filter!"a % 2".as!double.map!"a / 2".equal([0.5, 1.5]));
@@ -3785,7 +3785,7 @@ auto indexed(Field, S)(Field source, S indices)
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     auto source = [1, 2, 3, 4, 5];
     auto indices = [4, 3, 1, 2, 0, 4];
@@ -3835,7 +3835,7 @@ auto subSlices(S, Sliceable)(Sliceable sliceable, S slices)
 }
 
 ///
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.functional: staticArray;
     auto subs =[
@@ -3901,7 +3901,7 @@ auto chopped(S, Sliceable)(Sliceable sliceable, S bounds)
 }
 
 ///
-@safe pure version(mir_test) unittest
+@safe pure version(mir_ndslice_test) unittest
 {
     import mir.functional: staticArray;
     import mir.ndslice.slice: sliced;
@@ -3973,7 +3973,7 @@ template zip(bool sameStrides = false)
 }
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation : slice;
     import mir.ndslice.topology : flattened, iota;
@@ -3993,7 +3993,7 @@ template zip(bool sameStrides = false)
     assert(alpha == beta);
 }
 
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation : slice;
     import mir.ndslice.topology : flattened, iota;
@@ -4036,7 +4036,7 @@ auto unzip
 }
 
 ///
-pure nothrow version(mir_test) unittest
+pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation : slice;
     import mir.ndslice.topology : iota;
@@ -4156,7 +4156,7 @@ template slideAlong(size_t params, alias fun, SDimensions...)
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     auto data = [4, 5].iota;
 
@@ -4218,7 +4218,7 @@ template slide(size_t params, alias fun)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto data = 10.iota;
     auto sw = data.slide!(3, "a + 2 * b + c");
@@ -4232,7 +4232,7 @@ version(mir_test) unittest
 /++
 ND-use case
 +/
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     auto data = [4, 5].iota;
 
@@ -4259,7 +4259,7 @@ See_also: $(LREF slide), $(LREF slideAlong), $(LREF subSlices).
 alias pairwise(alias fun, size_t lag = 1) = slide!(lag + 1, fun);
 
 ///
-@safe pure nothrow version(mir_test) unittest
+@safe pure nothrow version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: sliced;
     assert([2, 4, 3, -1].sliced.pairwise!"a + b" == [6, 7, 2]);
@@ -4267,7 +4267,7 @@ alias pairwise(alias fun, size_t lag = 1) = slide!(lag + 1, fun);
 
 /// N-dimensional
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     // performs pairwise along each dimension
     // 0 1 2 3
@@ -4290,7 +4290,7 @@ See_also: $(LREF slide), $(LREF slide).
 alias diff(size_t lag = 1) = pairwise!(('a' + lag) ~ " - a", lag);
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: sliced;
     assert([2, 4, 3, -1].sliced.diff == [2, -1, -4]);
@@ -4298,7 +4298,7 @@ version(mir_test) unittest
 
 /// N-dimensional
 @safe pure nothrow @nogc
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     // 0 1 2 3
     // 4 5 6 7     =>
@@ -4315,7 +4315,7 @@ version(mir_test) unittest
 }
 
 /// packed slices
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     // 0 1  2  3
     // 4 5  6  7
@@ -4363,7 +4363,7 @@ Slice!(Iterator, N, N > 1 && kind == Contiguous ? Canonical : kind, Labels)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     assert([4, 5].iota.dropBorders == [[6, 7, 8], [11, 12, 13]]);
 }
@@ -4444,7 +4444,7 @@ template withNeighboursSum(alias fun = "a + b")
 }
 
 ///
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.algorithm.iteration: all;
@@ -4454,7 +4454,7 @@ template withNeighboursSum(alias fun = "a + b")
     assert(wn.map!"a" == wn.map!"b * 0.25");
 }
 
-@safe pure nothrow @nogc version(mir_test) unittest
+@safe pure nothrow @nogc version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.algorithm.iteration: all;
@@ -4480,7 +4480,7 @@ auto cartesian(NdFields...)(NdFields fields)
 }
 
 /// 1D x 1D
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto a = [10, 20, 30];
     auto b = [ 1,  2,  3];
@@ -4495,7 +4495,7 @@ version(mir_test) unittest
 }
 
 /// 1D x 2D
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto a = [10, 20, 30];
     auto b = iota([2, 3], 1);
@@ -4521,7 +4521,7 @@ version(mir_test) unittest
 }
 
 /// 1D x 1D x 1D
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto u = [100, 200];
     auto v = [10, 20, 30];
@@ -4572,7 +4572,7 @@ template kronecker(alias fun = product)
 }
 
 /// 2D
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.slice: sliced;
@@ -4598,7 +4598,7 @@ version(mir_test) unittest
 }
 
 /// 1D
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     auto a = iota([3], 1);
 
@@ -4610,7 +4610,7 @@ version(mir_test) unittest
 }
 
 /// 2D with 3 arguments
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.allocation: slice;
     import mir.ndslice.slice: sliced;
@@ -4657,7 +4657,7 @@ auto magic(size_t length)
 
 ///
 @safe pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.math.sum;
     import mir.ndslice: slice, magic, byDim, map, as, repeat, diagonal, antidiagonal;
@@ -4731,7 +4731,7 @@ auto stairs(string type, S)(S slice, size_t n)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota, stairs;
 
@@ -4801,7 +4801,7 @@ auto stairs(string type, Iterator, SliceKind kind)(Slice!(Iterator, 2, kind) sli
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology: iota, as, stairs;
 
@@ -4888,7 +4888,7 @@ template alongDim(SDimensions...)
 
 /// 2-dimensional slice support
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice;
 
@@ -4937,7 +4937,7 @@ version(mir_test) unittest
 
 /// 3-dimensional slice support, N-dimensional also supported
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice;
 
@@ -5086,7 +5086,7 @@ version(mir_test) unittest
 }
 
 /// Use alongDim to calculate column mean/row mean of 2-dimensional slice
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest
 {
@@ -5115,7 +5115,7 @@ unittest
 Use alongDim and map with a lambda, but may need to allocate result. This example
 uses fuse, which allocates. Note: fuse!1 will transpose the result. 
 +/
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest {
     import mir.ndslice.topology: iota, alongDim, map;
@@ -5231,7 +5231,7 @@ template byDim(SDimensions...)
 
 /// 2-dimensional slice support
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice;
 
@@ -5280,7 +5280,7 @@ version(mir_test) unittest
 
 /// 3-dimensional slice support, N-dimensional also supported
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice;
 
@@ -5429,7 +5429,7 @@ version(mir_test) unittest
 }
 
 /// Use byDim to calculate column mean/row mean of 2-dimensional slice
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest
 {
@@ -5458,7 +5458,7 @@ unittest
 Use byDim and map with a lambda, but may need to allocate result. This example
 uses fuse, which allocates. Note: fuse!1 will transpose the result. 
 +/
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure
 unittest {
     import mir.ndslice.topology: iota, byDim, map;
@@ -5478,7 +5478,7 @@ unittest {
 
 // Ensure works on canonical
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota, canonical;
     //  ------------
@@ -5526,7 +5526,7 @@ version(mir_test) unittest
 
 // Ensure works on universal
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota, universal;
     //  ------------
@@ -5574,7 +5574,7 @@ version(mir_test) unittest
 
 // 1-dimensional slice support
 @safe @nogc pure nothrow
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.topology : iota;
     //  -------
@@ -5776,7 +5776,7 @@ template unsqueeze(sizediff_t axis = 0)
 }
 
 ///
-version (mir_test) 
+version (mir_ndslice_test) 
 @safe pure nothrow @nogc
 unittest
 {
@@ -5842,7 +5842,7 @@ template member(string name)
 }
 
 ///
-version(mir_test)
+version(mir_ndslice_test)
 @safe pure unittest
 {
     // struct, union or class
@@ -5916,7 +5916,7 @@ template orthogonalReduceField(alias fun)
 }
 
 /// bit array operations
-version(mir_test)
+version(mir_ndslice_test)
 unittest
 {
     import mir.ndslice.slice: slicedField;
@@ -5990,7 +5990,7 @@ auto triplets(string type, S)(S slice, size_t n)
 }
 
 ///
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice: sliced;
     import mir.ndslice.topology: triplets, member, iota;
diff --git a/source/mir/ndslice/traits.d b/source/mir/ndslice/traits.d
index 85e18018..61d20b50 100644
--- a/source/mir/ndslice/traits.d
+++ b/source/mir/ndslice/traits.d
@@ -65,7 +65,7 @@ enum bool isUniversalMatrix(T) = is(T : Slice!(Iterator,  2, Universal), Iterato
 
 ///
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     import mir.ndslice.slice : Slice;
 
@@ -86,7 +86,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias S2 = Slice!(float*, 1, Universal);
     static assert(!isContiguousVector!S2);
@@ -105,7 +105,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias S3 = Slice!(byte*, 2);
     static assert(!isContiguousVector!S3);
@@ -124,7 +124,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias S4 = Slice!(int*, 2, Canonical);
     static assert(!isContiguousVector!S4);
@@ -143,7 +143,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias S5 = Slice!(int*, 2, Universal);
     static assert(!isContiguousVector!S5);
@@ -162,7 +162,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias S6 = Slice!(int*, 3);
     
@@ -182,7 +182,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias S7 = Slice!(int*, 3, Canonical);
 
@@ -202,7 +202,7 @@ version(mir_test) unittest
 }
 
 @safe pure nothrow @nogc 
-version(mir_test) unittest
+version(mir_ndslice_test) unittest
 {
     alias S8 = Slice!(int*, 3, Universal);
     

From a26a388e8825b140c4a20ac2ebcc04e092224e4d Mon Sep 17 00:00:00 2001
From: Ilya Yaroshenko <ilyayaroshenko@gmail.com>
Date: Thu, 17 Mar 2022 16:32:30 +0700
Subject: [PATCH 326/573] meta linear and spline interpolations (#396)

---
 source/mir/interpolate/linear.d  | 222 +++++++++++++++-
 source/mir/interpolate/spline.d  | 417 +++++++++++++++++++++++++++----
 source/mir/interpolate/utility.d |  12 +-
 3 files changed, 585 insertions(+), 66 deletions(-)

diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 8701fb8c..6581c8b2 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -26,6 +26,14 @@ import std.meta: AliasSeq, staticMap;
 import std.traits;
 public import mir.interpolate: atInterval;
 
+enum  msg_min =  "linear interpolant: minimal allowed length for the grid equals 2.";
+enum  msg_eq =  "linear interpolant: X and Y values length should be equal.";
+version(D_Exceptions)
+{
+    static immutable exc_min = new Exception(msg_min);
+    static immutable exc_eq = new Exception(msg_eq);
+}
+
 @optmath:
 
 /++
@@ -198,13 +206,6 @@ struct Linear(F, size_t N = 1, X = F)
     +/
     this(Repeat!(N, Slice!(RCI!(immutable X))) grid, Slice!(RCI!(const F), N) data) @safe @nogc
     {
-        enum  msg_min =  "linear interpolant: minimal allowed length for the grid equals 2.";
-        enum  msg_eq =  "linear interpolant: X and Y values length should be equal.";
-        version(D_Exceptions)
-        {
-            static immutable exc_min = new Exception(msg_min);
-            static immutable exc_eq = new Exception(msg_eq);
-        }
         foreach(i, ref x; grid)
         {
             if (x.length < 2)
@@ -259,6 +260,9 @@ struct Linear(F, size_t N = 1, X = F)
     ///
     enum uint derivativeOrder = 1;
 
+    ///
+    enum dimensionCount = N;
+
     ///
     template opCall(uint derivative = 0)
         if (derivative <= derivativeOrder)
@@ -268,7 +272,7 @@ struct Linear(F, size_t N = 1, X = F)
         Complexity:
             `O(log(grid.length))`
         +/
-        auto opCall(X...)(in X xs) scope const @trusted
+        auto opCall(X...)(const X xs) scope const @trusted
             if (X.length == N)
         {
             import mir.functional: AliasCall;
@@ -342,6 +346,8 @@ struct LinearKernel(X)
     X w0 = 0;
     X w1 = 0;
 
+@optmath:
+
     ///
     auto lightConst()() const @property
     {
@@ -369,7 +375,8 @@ struct LinearKernel(X)
         if (derivative <= 1)
     {
         ///
-        auto opCall(Y)(in Y y0, in Y y1)
+        auto opCall(Y)(const Y y0, const Y y1)
+            if (__traits(isFloating, Y))
         {
             auto r0 = y0 * w1;
             auto r1 = y1 * w0;
@@ -387,8 +394,205 @@ struct LinearKernel(X)
                 return y;
             }
         }
+
+        static if (derivative)
+        auto opCall(Y, size_t N)(scope ref const Y[N] y0, scope ref const Y[N] y1)
+        {
+            Y[N][derivative + 1] ret = void;
+            Y[derivative + 1][N] temp = void;
+
+            static foreach(i; 0 .. N)
+                temp[i] = this.opCall!derivative(y0[i], y1[i]);
+            static foreach(i; 0 .. N)
+            static foreach(d; 0 .. derivative + 1)
+                ret[d][i] = temp[i][d];
+            return ret;
+        }
     }
 
     ///
     alias withDerivative = opCall!1;
 }
+
+/++
+Interpolator used for non-rectiliner trapezoid-like greeds.
+Params:
+    grid = rc-array of interpolation grid
+    data = rc-array of interpolator-like structures
++/
+auto metaLinear(X, T)(RCArray!(immutable X) grid, RCArray!(const T) data)
+{
+    import core.lifetime: move;
+    return MetaLinear!(T, X)(grid.move, data.move);
+}
+
+/// ditto
+struct MetaLinear(T, X)
+    if (T.derivativeOrder >= 1)
+{
+    import mir.interpolate.utility: DeepType;
+    // alias ElementInterpolator = Linear!(F, N, X);
+
+    ///
+    RCArray!(immutable X) grid;
+    ///
+    RCArray!(const T) data;
+
+    ///
+    this(RCArray!(immutable X) grid, RCArray!(const T) data)
+    {
+        import core.lifetime: move;
+
+        if (grid.length < 2)
+        {
+            version(D_Exceptions) throw exc_min;
+            else assert(0, msg_min);
+        }
+        if (grid.length != data.length)
+        {
+            version(D_Exceptions) throw exc_eq;
+            else assert(0, msg_eq);
+        }
+
+        this.grid = grid.move;
+        this.data = data.move;
+    }
+
+    ///
+    MetaLinear lightConst()() const @property { return *cast(MetaLinear*)&this; }
+
+    ///
+    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+        if (dimension == 0)
+    {
+        return grid[];
+    }
+
+    /++
+    Returns: intervals count.
+    +/
+    size_t intervalCount(size_t dimension = 0)() scope const @property
+        if (dimension == 0)
+    {
+        assert(data.length > 1);
+        return data.length - 1;
+    }
+
+    ///
+    enum uint derivativeOrder = 1;
+
+    ///
+    enum dimensionCount = T.dimensionCount + 1;
+
+    ///
+    template opCall(uint derivative = 0)
+        if (derivative <= derivativeOrder)
+    {
+        /++
+        `(x)` operator.
+        Complexity:
+            `O(log(grid.length))`
+        +/
+        auto opCall(X...)(const X xs) scope const @trusted
+            if (X.length == dimensionCount)
+        {
+            static if (isInterval!(typeof(xs[0])))
+            {
+                size_t index = xs[0][1];
+                auto x = xs[0][0];
+            }
+            else
+            { 
+                alias x = xs[0];
+                size_t index = this.findInterval(x);
+            }
+            auto lhs = data[index + 0].opCall!derivative(xs[1 .. $]);
+            auto rhs = data[index + 1].opCall!derivative(xs[1 .. $]);
+            alias E = typeof(lhs);
+            alias F = DeepType!E;
+            auto kernel = LinearKernel!F(grid[index], grid[index + 1], x);
+            return kernel.opCall!derivative(lhs, rhs);
+        }
+    }
+
+    ///
+    alias withDerivative = opCall!1;
+}
+
+/// 2D trapezoid-like (not rectilinear) linear interpolation
+unittest
+{
+    auto x = [
+        [0.0, 1, 2, 3, 5],
+        [-4.0, 3, 4],
+        [0.0, 10],
+    ];
+    auto y = [
+        [4.0, 0, 9, 23, 40],
+        [9.0, 0, 3],
+        [4.0, 40],
+    ];
+
+    auto g = [7.0, 10, 15];
+
+    import mir.rc.array: RCArray;
+    import mir.ndslice.allocation: rcslice;
+
+    auto d = RCArray!(Linear!double)(3);
+
+    foreach (i; 0 .. x.length)
+        d[i] = linear!double(x[i].rcslice!(immutable double), y[i].rcslice!(const double));
+
+    auto trapezoidInterpolator = metaLinear(g.rcarray!(immutable double), d.lightConst);
+
+    auto val = trapezoidInterpolator(9.0, 1.8);
+    auto valWithDerivative = trapezoidInterpolator.withDerivative(9.0, 1.8);
+}
+
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.ndslice;
+    alias appreq = (a, b) => approxEqual(a, b, 10e-10, 10e-10);
+
+    //// set test function ////
+    enum y_x0 = 2;
+    enum y_x1 = -7;
+    enum y_x0x1 = 3;
+
+    // this function should be approximated very well
+    alias f = (x0, x1) => y_x0 * x0 + y_x1 * x1 + y_x0x1 * x0 * x1 - 11;
+
+    ///// set interpolant ////
+    static immutable x0 = [-1.0, 2, 8, 15];
+    static immutable x1 = [-4.0, 2, 5, 10, 13];
+
+    auto grid = cartesian(x0, x1)
+        .map!f
+        .rcslice
+        .lightConst;
+    
+    auto int0 = linear!double(x1.rcslice!(immutable double), grid[0]);
+    auto int1 = linear!double(x1.rcslice!(immutable double), grid[1]);
+    auto int2 = linear!double(x1.rcslice!(immutable double), grid[2]);
+    auto int3 = linear!double(x1.rcslice!(immutable double), grid[3]);
+
+    auto interpolant = metaLinear(x0.rcarray!(immutable double), rcarray(int0, int1, int2, int3).lightConst);
+
+    ///// compute test data ////
+    auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23);
+    auto real_data = test_grid.map!f;
+    auto interp_data = test_grid.vmap(interpolant);
+    ///// verify result ////
+    assert(all!appreq(interp_data, real_data));
+
+    //// check derivatives ////
+    auto z0 = 1.23;
+    auto z1 = 3.21;
+    auto d = interpolant.withDerivative(z0, z1);
+    assert(appreq(interpolant(z0, z1), f(z0, z1)));
+    assert(appreq(d[0][0], f(z0, z1)));
+    assert(appreq(d[1][0], y_x0 + y_x0x1 * z1));
+    assert(appreq(d[0][1], y_x1 + y_x0x1 * z0));
+    assert(appreq(d[1][1], y_x0x1));
+}
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 6e124816..c1b93ac1 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -29,6 +29,16 @@ import std.meta: AliasSeq, staticMap;
 import std.traits: Unqual;
 public import mir.interpolate: atInterval;
 
+enum msg_min =  "spline interpolant: minimal allowed length for the grid equals 2.";
+enum msg_eq =  "spline interpolant: X and Y values length should be equal.";
+version(D_Exceptions)
+{
+    static immutable exc_min = new Exception(msg_min);
+    static immutable exc_eq = new Exception(msg_eq);
+}
+
+private alias ValueType(T, X) = typeof(T.init.opCall(Repeat!(T.dimensionCount, X.init)));
+
 @fmamath:
 
 ///
@@ -524,8 +534,8 @@ template spline(T, size_t N = 1, X = T)
     Params:
         grid = immutable `x` values for interpolant
         values = `f(x)` values for interpolant
-        rBoundary = $(LREF SplineBoundaryCondition) for left tail.
-        lBoundary = $(LREF SplineBoundaryCondition) for right tail.
+        lBoundary = $(LREF SplineBoundaryCondition) for left tail.
+        rBoundary = $(LREF SplineBoundaryCondition) for right tail.
         kind = $(LREF SplineType) type of cubic spline.
         param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
             Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
@@ -536,15 +546,15 @@ template spline(T, size_t N = 1, X = T)
     Spline!(T, N, X) spline(yIterator, SliceKind ykind)(
         Repeat!(N, Slice!(RCI!(immutable X))) grid,
         Slice!(yIterator, N, ykind) values,
-        SplineBoundaryCondition!T rBoundary,
         SplineBoundaryCondition!T lBoundary,
+        SplineBoundaryCondition!T rBoundary,
         SplineType kind = SplineType.c2,
         in T param = 0,
         )
     {
         auto ret = typeof(return)(forward!grid);
         ret._values = values;
-        ret._computeDerivatives(kind, param, rBoundary, lBoundary);
+        ret._computeDerivatives(kind, param, lBoundary, rBoundary);
         return ret;
     }
 }
@@ -597,6 +607,7 @@ See_also: $(LREF SplineBoundaryType)
 +/
 extern(C++, "mir", "interpolate")
 struct SplineBoundaryCondition(T)
+    if (__traits(isFloating, T))
 {
     /// type (default is $(LREF SplineBoundaryType.notAKnot))
     SplineBoundaryType type = SplineBoundaryType.notAKnot;
@@ -617,6 +628,8 @@ struct Spline(F, size_t N = 1, X = F)
     /// Grid iterators. $(RED For internal use.)
     Repeat!(N, RCI!(immutable X)) _grid;
 
+    enum dimensionCount = N;
+
 @fmamath extern(D):
 
     bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
@@ -635,15 +648,12 @@ struct Spline(F, size_t N = 1, X = F)
     {
         size_t length = 1;
         size_t[N] shape;
-        enum  msg =  "spline interpolant: minimal allowed length for the grid equals 2.";
-        version(D_Exceptions)
-            static immutable exc = new Exception(msg);
         foreach(i, ref x; grid)
         {
             if (x.length < 2)
             {
-                version(D_Exceptions) throw exc;
-                else assert(0, msg);
+                version(D_Exceptions) throw exc_min;
+                else assert(0, msg_min);
             }
             length *= shape[i] = x.length;
             this._grid[i] = x._iterator.move;
@@ -674,24 +684,24 @@ struct Spline(F, size_t N = 1, X = F)
     Computes derivatives and stores them in `_data`.
     `_data` is assumed to be preinitialized with function values filled in `F[2 ^^ N][0]`.
     Params:
-        lbc = left boundary condition
-        rbc = right boundary condition
+        lBoundary = left boundary condition
+        rBoundary = right boundary condition
         temp = temporal buffer length points count (optional)
 
     $(RED For internal use.)
     +/
-    void _computeDerivatives(SplineType kind, F param, SplineBoundaryCondition!F lbc, SplineBoundaryCondition!F rbc) scope @trusted nothrow @nogc
+    void _computeDerivatives(SplineType kind, F param, SplineBoundaryCondition!F lBoundary, SplineBoundaryCondition!F rBoundary) scope @trusted nothrow @nogc
     {
         import mir.algorithm.iteration: maxLength;
         auto ml = this._data.maxLength;
         auto temp = RCArray!F(ml);
         auto tempSlice = temp[].sliced;
-        _computeDerivativesTemp(kind, param, lbc, rbc, tempSlice);
+        _computeDerivativesTemp(kind, param, lBoundary, rBoundary, tempSlice);
     }
 
     /// ditto
     pragma(inline, false)
-    void _computeDerivativesTemp(SplineType kind, F param, SplineBoundaryCondition!F lbc, SplineBoundaryCondition!F rbc, Slice!(F*) temp) scope @system nothrow @nogc
+    void _computeDerivativesTemp(SplineType kind, F param, SplineBoundaryCondition!F lBoundary, SplineBoundaryCondition!F rBoundary, Slice!(F*) temp) scope @system nothrow @nogc
     {
         import mir.algorithm.iteration: maxLength, each;
         import mir.ndslice.topology: map, byDim, evertPack;
@@ -700,7 +710,7 @@ struct Spline(F, size_t N = 1, X = F)
 
         static if (N == 1)
         {
-            splineSlopes!(F, F)(_grid[0].lightConst.sliced(_data._lengths[0]), pickDataSubslice(_data.lightScope, 0), pickDataSubslice(_data.lightScope, 1), temp[0 .. _data._lengths[0]], kind, param, lbc, rbc);
+            splineSlopes!(F, F)(_grid[0].lightConst.sliced(_data._lengths[0]), pickDataSubslice(_data.lightScope, 0), pickDataSubslice(_data.lightScope, 1), temp[0 .. _data._lengths[0]], kind, param, lBoundary, rBoundary);
         }
         else
         foreach_reverse(i; Iota!N)
@@ -717,7 +727,7 @@ struct Spline(F, size_t N = 1, X = F)
                         auto y = pickDataSubslice(d, l);
                         auto s = pickDataSubslice(d, L + l);
                         // debug printf("ptr = %ld, stride = %ld, stride = %ld, d = %ld i = %ld l = %ld\n", d.iterator, d._stride!0, y._stride!0, d.length, i, l);
-                        splineSlopes!(F, F)(_grid[i].sliced(_data._lengths[i]), y, s, temp[0 .. _data._lengths[i]], kind, param, lbc, rbc);
+                        splineSlopes!(F, F)(_grid[i].sliced(_data._lengths[i]), y, s, temp[0 .. _data._lengths[i]], kind, param, lBoundary, rBoundary);
                         // debug{
                         //     (cast(void delegate() @nogc)(){
                         //     writeln("y = ", y);
@@ -941,8 +951,8 @@ Params:
     kind = $(LREF SplineType) type of cubic spline.
     param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
         Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
-    lbc = left boundary condition
-    rbc = right boundary condition
+    lBoundary = left boundary condition
+    rBoundary = right boundary condition
 Constraints:
     `points`, `values`, and `slopes`, must have the same length > 3;
     `temp` must have length greater or equal to points less minus one.
@@ -954,8 +964,8 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
     Slice!(T*) temp,
     SplineType kind,
     F param,
-    SplineBoundaryCondition!F lbc,
-    SplineBoundaryCondition!F rbc,
+    SplineBoundaryCondition!F lBoundary,
+    SplineBoundaryCondition!F rBoundary,
     ) @trusted
 {
     import mir.ndslice.topology: diff, zip, slide;
@@ -979,50 +989,50 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
         case c2:
             break;
         case cardinal:
-            if (lbc.type == SplineBoundaryType.notAKnot)
-                lbc.type = SplineBoundaryType.parabolic;
-            if (rbc.type == SplineBoundaryType.notAKnot)
-                rbc.type = SplineBoundaryType.parabolic;
+            if (lBoundary.type == SplineBoundaryType.notAKnot)
+                lBoundary.type = SplineBoundaryType.parabolic;
+            if (rBoundary.type == SplineBoundaryType.notAKnot)
+                rBoundary.type = SplineBoundaryType.parabolic;
             break;
         case monotone:
-            if (lbc.type == SplineBoundaryType.notAKnot)
-                lbc.type = SplineBoundaryType.monotone;
-            if (rbc.type == SplineBoundaryType.notAKnot)
-                rbc.type = SplineBoundaryType.monotone;
+            if (lBoundary.type == SplineBoundaryType.notAKnot)
+                lBoundary.type = SplineBoundaryType.monotone;
+            if (rBoundary.type == SplineBoundaryType.notAKnot)
+                rBoundary.type = SplineBoundaryType.monotone;
             break;
         case doubleQuadratic:
-            if (lbc.type == SplineBoundaryType.notAKnot)
-                lbc.type = SplineBoundaryType.parabolic;
-            if (rbc.type == SplineBoundaryType.notAKnot)
-                rbc.type = SplineBoundaryType.parabolic;
+            if (lBoundary.type == SplineBoundaryType.notAKnot)
+                lBoundary.type = SplineBoundaryType.parabolic;
+            if (rBoundary.type == SplineBoundaryType.notAKnot)
+                rBoundary.type = SplineBoundaryType.parabolic;
             break;
         case akima:
-            if (lbc.type == SplineBoundaryType.notAKnot)
-                lbc.type = SplineBoundaryType.akima;
-            if (rbc.type == SplineBoundaryType.notAKnot)
-                rbc.type = SplineBoundaryType.akima;
+            if (lBoundary.type == SplineBoundaryType.notAKnot)
+                lBoundary.type = SplineBoundaryType.akima;
+            if (rBoundary.type == SplineBoundaryType.notAKnot)
+                rBoundary.type = SplineBoundaryType.akima;
             break;
     }
 
     if (n <= 3)
     {
-        if (lbc.type == SplineBoundaryType.notAKnot)
-            lbc.type = SplineBoundaryType.parabolic;
-        if (rbc.type == SplineBoundaryType.notAKnot)
-            rbc.type = SplineBoundaryType.parabolic;
+        if (lBoundary.type == SplineBoundaryType.notAKnot)
+            lBoundary.type = SplineBoundaryType.parabolic;
+        if (rBoundary.type == SplineBoundaryType.notAKnot)
+            rBoundary.type = SplineBoundaryType.parabolic;
 
         if (n == 2)
         {
-            if (lbc.type == SplineBoundaryType.monotone
-             || lbc.type == SplineBoundaryType.akima)
-                lbc.type = SplineBoundaryType.parabolic;
-            if (rbc.type == SplineBoundaryType.monotone
-             || rbc.type == SplineBoundaryType.akima)
-                rbc.type = SplineBoundaryType.parabolic;
+            if (lBoundary.type == SplineBoundaryType.monotone
+             || lBoundary.type == SplineBoundaryType.akima)
+                lBoundary.type = SplineBoundaryType.parabolic;
+            if (rBoundary.type == SplineBoundaryType.monotone
+             || rBoundary.type == SplineBoundaryType.akima)
+                rBoundary.type = SplineBoundaryType.parabolic;
         }
         /// special case
-        if (rbc.type == SplineBoundaryType.parabolic
-         && lbc.type == SplineBoundaryType.parabolic)
+        if (rBoundary.type == SplineBoundaryType.parabolic
+         && lBoundary.type == SplineBoundaryType.parabolic)
         {
             import mir.interpolate.utility;
             if (n == 3)
@@ -1041,7 +1051,7 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
         }
     }
 
-    with(SplineBoundaryType) final switch(lbc.type)
+    with(SplineBoundaryType) final switch(lBoundary.type)
     {
     case periodic:
 
@@ -1065,14 +1075,14 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
 
         slopes.front = 1;
         first = 0;
-        temp.front = lbc.value;
+        temp.front = lBoundary.value;
         break;
 
     case secondDerivative:
 
         slopes.front = 2;
         first = 1;
-        temp.front = 3 * dd.front - 0.5 * lbc.value * xd.front;
+        temp.front = 3 * dd.front - 0.5 * lBoundary.value * xd.front;
         break;
 
     case parabolic:
@@ -1098,7 +1108,7 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
 
     }
 
-    with(SplineBoundaryType) final switch(rbc.type)
+    with(SplineBoundaryType) final switch(rBoundary.type)
     {
     case periodic:
         assert(0);
@@ -1120,14 +1130,14 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
 
         slopes.back = 1;
         last = 0;
-        temp.back = rbc.value;
+        temp.back = rBoundary.value;
         break;
 
     case secondDerivative:
 
         slopes.back = 2;
         last = 1;
-        temp.back = 3 * dd.back + 0.5 * rbc.value * xd.back;
+        temp.back = 3 * dd.back + 0.5 * rBoundary.value * xd.back;
         break;
 
     case parabolic:
@@ -1292,6 +1302,8 @@ struct SplineKernel(X)
     X w1 = 0;
     X wq = 0;
 
+@fmamath:
+
     ///
     this(X x0, X x1, X x)
     {
@@ -1308,7 +1320,7 @@ struct SplineKernel(X)
         if (derivative <= 3)
     {
         ///
-        auto opCall(Y)(in Y y0, in Y y1, in Y s0, in Y s1) const
+        auto opCall(Y)(const Y y0, const Y y1, const Y s0, const Y s1) const
         {
             auto diff = y1 - y0;
             auto z0 = s0 * step - diff;
@@ -1341,6 +1353,20 @@ struct SplineKernel(X)
                 return y;
             }
         }
+
+        static if (derivative)
+        auto opCall(Y, size_t N)(scope ref const Y[N] y0, scope ref const Y[N] y1, scope ref const Y[N] s0, scope ref const Y[N] s1)
+        {
+            Y[N][derivative + 1] ret = void;
+            Y[derivative + 1][N] temp = void;
+
+            static foreach(i; 0 .. N)
+                temp[i] = this.opCall!derivative(y0[i], y1[i], s0[i], s1[i]);
+            static foreach(i; 0 .. N)
+            static foreach(d; 0 .. derivative + 1)
+                ret[d][i] = temp[i][d];
+            return ret;
+        }
     }
 
     ///
@@ -1367,3 +1393,284 @@ package T pchipTail(T)(in T step0, in T step1, in T diff0, in T diff1)
     }
     return slope;
 }
+
+/++
+Spline interpolator used for non-rectiliner trapezoid-like greeds.
+Params:
+    grid = rc-array of interpolation grid
+    data = rc-array of interpolator-like structures
+    typeOfBoundaries = $(LREF SplineBoundaryType) for both tails (optional).
+    valueOfBoundaryConditions = value of the boundary type (optional). 
+Constraints:
+    `grid` and `values` must have the same length >= 3
+Returns: $(LREF Spline)
++/
+MetaSpline!(T, X) metaSpline(F, X, T)(
+    RCArray!(immutable X) grid,
+    RCArray!(const T) data,
+    SplineBoundaryType typeOfBoundaries = SplineBoundaryType.notAKnot,
+    const F valueOfBoundaryConditions = 0,
+    )
+{
+    return metaSpline!(F, X, T)(grid, data, SplineType.c2, 0, typeOfBoundaries, valueOfBoundaryConditions);
+}
+
+/++
+Spline interpolator used for non-rectiliner trapezoid-like greeds.
+Params:
+    grid = rc-array of interpolation grid
+    data = rc-array of interpolator-like structures
+    kind = $(LREF SplineType) type of cubic spline.
+    param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
+        Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
+    typeOfBoundaries = $(LREF SplineBoundaryType) for both tails (optional).
+    valueOfBoundaryConditions = value of the boundary type (optional). 
+Constraints:
+    `grid` and `values` must have the same length >= 3
+Returns: $(LREF Spline)
++/
+MetaSpline!(T, X) metaSpline(F, X, T)(
+    RCArray!(immutable X) grid,
+    RCArray!(const T) data,
+    SplineType kind,
+    const F param = 0,
+    SplineBoundaryType typeOfBoundaries = SplineBoundaryType.notAKnot,
+    const F valueOfBoundaryConditions = 0,
+    )
+{
+    return metaSpline!(F, X, T)(grid, data, SplineBoundaryCondition!F(typeOfBoundaries, valueOfBoundaryConditions), kind, param);
+}
+
+/++
+Spline interpolator used for non-rectiliner trapezoid-like greeds.
+Params:
+    grid = rc-array of interpolation grid
+    data = rc-array of interpolator-like structures
+    boundaries = $(LREF SplineBoundaryCondition) for both tails.
+    kind = $(LREF SplineType) type of cubic spline.
+    param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
+        Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
+Constraints:
+    `grid` and `values` must have the same length >= 3
+Returns: $(LREF Spline)
++/
+MetaSpline!(T, X) metaSpline(F, X, T)(
+    RCArray!(immutable X) grid,
+    RCArray!(const T) data,
+    SplineBoundaryCondition!F boundaries,
+    SplineType kind = SplineType.c2,
+    const F param = 0,
+    )
+{
+    return metaSpline!(F, X, T)(grid, data, boundaries, boundaries, kind, param);
+}
+
+/++
+Spline interpolator used for non-rectiliner trapezoid-like greeds.
+Params:
+    grid = rc-array of interpolation grid
+    data = rc-array of interpolator-like structures
+    lBoundary = $(LREF SplineBoundaryCondition) for left tail.
+    rBoundary = $(LREF SplineBoundaryCondition) for right tail.
+    kind = $(LREF SplineType) type of cubic spline.
+    param = tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
+        Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
+Constraints:
+    `grid` and `values` must have the same length >= 3
+Returns: $(LREF Spline)
++/
+MetaSpline!(T, X) metaSpline(F, X, T)(
+    RCArray!(immutable X) grid,
+    RCArray!(const T) data,
+    SplineBoundaryCondition!F lBoundary,
+    SplineBoundaryCondition!F rBoundary,
+    SplineType kind = SplineType.c2,
+    in F param = 0,
+    )
+{
+    import core.lifetime: move;
+    return MetaSpline!(T, X)(grid.move, data.move, kind, param, lBoundary, rBoundary);
+}
+
+/// ditto
+struct MetaSpline(T, X)
+    if (T.derivativeOrder >= 3)
+{
+    import mir.interpolate.utility: DeepType;
+    // alias ElementInterpolator = Linear!(F, N, X);
+    alias F = ValueType!(T, X);
+    ///
+    Spline!F spline;
+    ///
+    RCArray!(const T) data;
+    //
+    private RCArray!F _temp;
+    ///
+    SplineType splineType;
+    ///
+    F splineParam;
+    ///
+    SplineBoundaryCondition!F lBoundary;
+    ///
+    SplineBoundaryCondition!F rBoundary;
+
+
+    ///
+    this(
+        RCArray!(immutable X) grid,
+        RCArray!(const T) data,
+        SplineType splineType,
+        F splineParam,
+        SplineBoundaryCondition!F lBoundary,
+        SplineBoundaryCondition!F rBoundary,
+    )
+    {
+        import core.lifetime: move;
+
+        if (grid.length < 2)
+        {
+            version(D_Exceptions) throw exc_min;
+            else assert(0, msg_min);
+        }
+
+        if (grid.length != data.length)
+        {
+            version(D_Exceptions) throw exc_eq;
+            else assert(0, msg_eq);
+        }
+
+        this.data = data.move;
+        this._temp = grid.length;
+        this.spline = grid.moveToSlice;
+        this.splineType = splineType;
+        this.splineParam = splineParam;
+        this.lBoundary = lBoundary;
+        this.rBoundary = rBoundary;
+    }
+
+    ///
+    MetaLinear lightConst()() const @property { return *cast(MetaLinear*)&this; }
+
+    ///
+    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+        if (dimension == 0)
+    {
+        return spline.gridScopeView;
+    }
+
+    /++
+    Returns: intervals count.
+    +/
+    size_t intervalCount(size_t dimension = 0)() scope const @property
+        if (dimension == 0)
+    {
+        assert(data.length > 1);
+        return data.length - 1;
+    }
+
+    ///
+    enum uint derivativeOrder = 3;
+
+    ///
+    enum dimensionCount = T.dimensionCount + 1;
+
+    ///
+    template opCall(uint derivative = 0)
+        // if (derivative <= derivativeOrder)
+        if (derivative <= 0) // doesn't support derivatives for now
+    {
+        /++
+        `(x)` operator.
+        Complexity:
+            `O(log(grid.length))`
+        +/
+        auto opCall(X...)(const X xs) scope const @trusted
+            if (X.length == dimensionCount)
+        {
+            auto mutable = cast(F[2][]) spline._data.lightScope.field;
+            assert(mutable.length == data.length);
+            foreach (i, ref d; data)
+                mutable[i][0] = d(xs[1 .. $]);
+            (*cast(Spline!F*)&spline)._computeDerivativesTemp(
+                splineType,
+                splineParam,
+                lBoundary,
+                rBoundary,
+                (cast(F[])_temp[]).sliced);
+            return spline(xs[0]);
+        }
+    }
+
+    // ///
+    // alias withDerivative = opCall!1;
+}
+
+/// 2D trapezoid-like (not rectilinear) linear interpolation
+unittest
+{
+    auto x = [
+        [0.0, 1, 2, 3, 5],
+        [-4.0, 3, 4],
+        [0.0, 10],
+    ];
+    auto y = [
+        [4.0, 0, 9, 23, 40],
+        [9.0, 0, 3],
+        [4.0, 40],
+    ];
+
+    auto g = [7.0, 10, 15];
+
+    import mir.rc.array: RCArray;
+    import mir.ndslice.allocation: rcslice;
+
+    auto d = RCArray!(Spline!double)(3);
+
+    foreach (i; 0 .. x.length)
+        d[i] = spline!double(x[i].rcslice!(immutable double), y[i].rcslice!(const double));
+
+    auto trapezoidInterpolator = metaSpline!double(g.rcarray!(immutable double), d.lightConst);
+
+    auto val = trapezoidInterpolator(9.0, 1.8);
+
+}
+
+unittest
+{
+    import mir.math.common: approxEqual;
+    import mir.ndslice;
+    alias appreq = (a, b) => approxEqual(a, b, 10e-10, 10e-10);
+
+    //// set test function ////
+    enum y_x0 = 2;
+    enum y_x1 = -7;
+    enum y_x0x1 = 3;
+
+    // this function should be approximated very well
+    alias f = (x0, x1) => y_x0 * x0 + y_x1 * x1 + y_x0x1 * x0 * x1 - 11;
+
+    ///// set interpolant ////
+    static immutable x0 = [-1.0, 2, 8, 15];
+    static immutable x1 = [-4.0, 2, 5, 10, 13];
+
+    auto grid = cartesian(x0, x1)
+        .map!f
+        .rcslice
+        .lightConst;
+    
+    auto int0 = spline!double(x1.rcslice!(immutable double), grid[0]);
+    auto int1 = spline!double(x1.rcslice!(immutable double), grid[1]);
+    auto int2 = spline!double(x1.rcslice!(immutable double), grid[2]);
+    auto int3 = spline!double(x1.rcslice!(immutable double), grid[3]);
+
+    auto interpolant = metaSpline!double(x0.rcarray!(immutable double), rcarray(int0, int1, int2, int3).lightConst);
+
+    ///// compute test data ////
+    auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23);
+    // auto test_grid = cartesian(x0 + 0, x1 + 0);
+    auto real_data = test_grid.map!f;
+    auto interp_data = test_grid.vmap(interpolant);
+
+    ///// verify result ////
+    assert(all!appreq(interp_data, real_data));
+}
diff --git a/source/mir/interpolate/utility.d b/source/mir/interpolate/utility.d
index e0c87b82..945ae297 100644
--- a/source/mir/interpolate/utility.d
+++ b/source/mir/interpolate/utility.d
@@ -4,6 +4,14 @@ import mir.ndslice.slice;
 import std.traits;
 import mir.math.common: optmath;
 
+package template DeepType(T)
+{
+    static if (is(T : E[N], E, size_t N))
+        alias DeepType = DeepType!E;
+    else
+        alias DeepType = T;
+}
+
 /++
 Quadratic function structure
 +/
@@ -86,7 +94,7 @@ struct ParabolaKernel(T)
 }
 
 /// ditto
-ParabolaKernel!(Unqual!(typeof(X.init - Y.init))) parabolaKernel(X, Y)(in X x0, in X x1, in X x2, in Y y0, in Y y1, in Y y2)
+ParabolaKernel!(Unqual!(typeof(X.init - Y.init))) parabolaKernel(X, Y)(in X x0, in X x1, in X x2, const Y y0, const Y y1, const Y y2)
 {
     return typeof(return)(x0, x1, x2, y0, y1, y2);
 }
@@ -94,7 +102,7 @@ ParabolaKernel!(Unqual!(typeof(X.init - Y.init))) parabolaKernel(X, Y)(in X x0,
 /++
 Returns: `[f'(x0), f'(x1), f'(x2)]`
 +/
-Unqual!(typeof(X.init - Y.init))[3] parabolaDerivatives(X, Y)(in X x0, in X x1, in X x2, in Y y0, in Y y1, in Y y2)
+Unqual!(typeof(X.init - Y.init))[3] parabolaDerivatives(X, Y)(in X x0, in X x1, in X x2, const Y y0, const Y y1, const Y y2)
 {
     auto d0 = (y2 - y1) / (x2 - x1);
     auto d1 = (y0 - y2) / (x0 - x2);

From 2989760940186bfb6d0a66c96b4c1adb0042bc8a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 24 Mar 2022 11:22:30 +0700
Subject: [PATCH 327/573] add mir.test

---
 index.d           |   1 +
 meson.build       |   1 +
 source/mir/test.d | 104 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 106 insertions(+)
 create mode 100644 source/mir/test.d

diff --git a/index.d b/index.d
index daddc51f..0cf158e2 100644
--- a/index.d
+++ b/index.d
@@ -24,6 +24,7 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,small_array)) $(TD Generic Small Arrays))
     $(TR $(TDNW $(MREF mir,small_string)) $(TD Generic Small Strings))
     $(TR $(TDNW $(MREF mir,string_map)) $(TD Ordered string-value associative array with fast lookup))
+    $(TR $(TDNW $(MREF mir,test)) $(TD Testing utilities))
     $(LEADINGROW Date and time)
     $(TR $(TDNW $(MREF mir,date)) $(TD Fast BetterC Date type with Boost ABI and mangling compatability))
     $(TR $(TDNW $(MREF mir,timestamp)) $(TD General purpose timestamp implementation with arbitrary precision ))
diff --git a/meson.build b/meson.build
index 04028a55..8e222198 100644
--- a/meson.build
+++ b/meson.build
@@ -75,6 +75,7 @@ sources_list = [
     'mir/small_array',
     'mir/small_string',
     'mir/string_map',
+    'mir/test',
     'mir/timestamp',
     'mir/type_info',
 ]
diff --git a/source/mir/test.d b/source/mir/test.d
new file mode 100644
index 00000000..d5e32ea0
--- /dev/null
+++ b/source/mir/test.d
@@ -0,0 +1,104 @@
+/++
+Testing utilities
+
+Authors: Ilya Yaroshenko
++/
+module mir.test;
+
+import mir.exception: MirException;
+
+///
+struct ShouldApprox(T)
+    if (__traits(isFloating, T))
+{
+    ///
+    T value;
+    ///
+    T maxRelDiff = 0x1p-20f;
+    ///
+    T maxAbsDiff = 0x1p-20f;
+
+    ///
+    bool opEquals(T expected, string file = __FILE__, int line = __LINE__) const
+    {
+        import mir.format: stringBuf, getData;
+        import mir.math.common: approxEqual;
+        if (value.approxEqual(expected, maxRelDiff, maxAbsDiff))
+            return true;
+        stringBuf buf;
+        throw new MirException(buf
+            << "expected approximately " << expected
+            << ", got " << value
+            << ", maxRelDiff = " << maxRelDiff
+            << ", maxAbsDiff = " << maxAbsDiff
+            << getData, file, line);
+    }
+}
+
+/// ditto
+ShouldApprox!T shouldApprox(T)(const T value, const T maxRelDiff = T(0x1p-20f), const T maxAbsDiff = T(0x1p-20f))
+    if (__traits(isFloating, T))
+{
+    return typeof(return)(value, maxRelDiff, maxAbsDiff);
+}
+
+///
+unittest
+{
+    1.0.shouldApprox == 1 + 9e-7;
+    shouldApprox(1 + 9e-7, 1e-6, 1e-6) == 1;
+}
+
+///
+struct Should(T)
+{
+    ///
+    T value;
+
+    ///
+    bool opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__) const
+    {
+        import mir.format: stringBuf, getData;
+        if (value == expected)
+            return true;
+        stringBuf buf;
+        throw new MirException(buf
+            << "expected " << expected
+            << ", got " << value
+            << getData, file, line);
+    }
+}
+
+/// ditto
+Should!T should(T)(T value)
+{
+    return typeof(return)(value);
+}
+
+///
+unittest
+{
+    1.0.should == 1;
+    should(1) == 1;
+}
+
+///
+bool should(alias fun, T, R)(const T value, const R expected, string file = __FILE__, int line = __LINE__)
+{
+    import mir.functional;
+    import mir.format: stringBuf, getData;
+    if (naryFun!fun(value, expected))
+        return true;
+    stringBuf buf;
+    buf << fun.stringof
+        << " returns false"
+        << " for a = " << value
+        << ", b = " << expected;
+    throw new MirException(buf << getData, file, line);
+}
+
+///
+unittest
+{
+    1.0.should!"a < b"(1.3);
+}

From f6590421992a121ae3fe272ce8407734fa013a8a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 24 Mar 2022 13:09:34 +0700
Subject: [PATCH 328/573] add findSmileRoots

---
 source/mir/numeric.d | 145 ++++++++++++++++++++++++++++++++++++++++++-
 source/mir/test.d    |   3 +
 2 files changed, 147 insertions(+), 1 deletion(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 6bb474ec..ff2c8625 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -14,7 +14,7 @@ import mir.math.ieee;
 
 version(D_Exceptions)
 {
-    private static immutable findRoot_badBounds = new Exception("findRoot/findLocalMin: f(ax) and f(bx) must have opposite signs to bracket the root.");
+    private static immutable findRoot_badBounds = new Exception("findRoot: f(ax) and f(bx) must have opposite signs to bracket the root.");
     private static immutable findRoot_nanX = new Exception("findRoot/findLocalMin: ax or bx is NaN.");
     private static immutable findRoot_nanY = new Exception("findRoot/findLocalMin: f(x) returned NaN.");
 }
@@ -1415,6 +1415,149 @@ version(mir_test) @safe unittest
     }
 }
 
+/++
++/
+struct FindSmileRootsResult(T)
+    if (__traits(isFloating, T))
+{
+    import mir.algebraic: Nullable;
+
+    /++
+    Left result if any
+    +/
+    Nullable!(FindRootResult!T) leftResult;
+    /++
+    Right result if any
+    +/
+    Nullable!(FindRootResult!T) rightResult;
+
+@safe pure @nogc scope const @property:
+
+    /++
+    Returns: $(LREF mir_find_root_status)
+    +/
+    FindRootStatus status()
+    {
+        if (leftResult && leftResult.get.status)
+            return leftResult.get.status;
+        if (rightResult && rightResult.get.status)
+            return rightResult.get.status;
+        if (!leftResult && !rightResult)
+            return FindRootStatus.badBounds;
+        return FindRootStatus.success;
+    }
+
+    ///
+    version(D_Exceptions)
+    ref validate() inout return
+    {
+        with(FindRootStatus) final switch(status)
+        {
+            case success: return this;
+            case badBounds: throw findRoot_badBounds;
+            case nanX: throw findRoot_nanX;
+            case nanY: throw findRoot_nanY;
+        }
+    }
+
+    /++
+    Returns: `SmallArray!(T, 2)` of roots if any.
+    +/
+    auto roots()()
+    {
+        import mir.small_array;
+        SmallArray!(T, 2) ret;
+        if (leftResult)
+            ret.append(leftResult.get.x);
+        if (rightResult)
+            ret.append(rightResult.get.x);
+        return ret;
+    }
+}
+
+/++
+Find one or two roots of a real function f(x) using combination of $(LREF FindRoot) and $(LREF FindLocalMin).
+
+Params:
+    f = Function to be analyzed. If `f(ax)` and `f(bx)` have opposite signs one root is returned,
+        otherwise the implementation tries to find a local minimum and returns two roots.
+        At least one of `f(ax)` and `f(bx)` should be greater or equal to zero.
+    tolerance = Defines an early termination condition. Receives the
+        current upper and lower bounds on the root. The delegate must return `true` when these bounds are
+        acceptable. If this function always returns `false` or it is null, full machine precision will be achieved.
+    ax = Left inner bound of initial range of `f` known to contain the roots.
+    bx = Right inner bound of initial range of `f` known to contain the roots. Can be equal to `ax`.
+    fax = Value of `f(ax)` (optional).
+    fbx = Value of `f(bx)` (optional).
+    relTolerance = Relative tolerance used by $(LREF findLocalMin).
+    absTolerance = Absolute tolerance used by $(LREF findLocalMin).
+    maxIterations = Appr. maximum allowed number of function calls for each $(LREF findRoot) call.
+
+Returns: $(LREF FindSmileRootsResult)
++/
+FindSmileRootsResult!T findSmileRoots(alias f, alias tolerance = null, T)(
+    const T ax,
+    const T bx,
+    const T fax = T.nan,
+    const T fbx = T.nan,
+    const T relTolerance = sqrt(T.epsilon),
+    const T absTolerance = sqrt(T.epsilon),
+    const T lowerBound = T.nan,
+    const T upperBound = T.nan,
+    uint maxIterations = T.sizeof * 16,
+    uint steps = 0,
+    )
+if (__traits(isFloating, T))
+{
+    FindSmileRootsResult!T ret;
+    auto res = findRoot!(f, tolerance)(ax, bx, fax, fbx, T.nan, T.nan, maxIterations);
+    if (res.status == FindRootStatus.success)
+    {
+        (res.bx.signbit ? ret.leftResult : ret.rightResult) = res;
+    }
+    else
+    if (res.status == FindRootStatus.badBounds)
+    {
+        auto min = findLocalMin!f(ax, bx, relTolerance, absTolerance);
+        ret.leftResult = findRoot!(f, tolerance)(ax, min.x, T.nan, min.y, T.nan, T.nan, maxIterations);
+        ret.rightResult = findRoot!(f, tolerance)(min.x, bx, min.y, T.nan, T.nan, T.nan, maxIterations);
+    }
+    return ret;
+}
+
+/// Smile
+version(mir_test)
+unittest
+{
+    auto result = findSmileRoots!(x => x ^^ 2 - 1)(-10.0, 10.0);
+    assert(result.roots.length == 2);
+    assert(result.roots[0].approxEqual(-1));
+    assert(result.roots[1].approxEqual(+1));
+    assert(result.leftResult);
+    assert(result.rightResult);
+}
+
+/// Skew
+version(mir_test)
+unittest
+{
+    auto result = findSmileRoots!(x => x ^^ 2 - 1)(0.5, 10.0);
+    assert(result.roots.length == 1);
+    assert(result.roots[0].approxEqual(+1));
+    assert(!result.leftResult);
+    assert(result.rightResult);
+}
+
+version(mir_test)
+unittest
+{
+    auto result = findSmileRoots!(x => x ^^ 2 - 1)(-10.0, -0.5);
+    assert(result.roots.length == 1);
+    assert(result.roots[0].approxEqual(-1));
+    assert(result.leftResult);
+    assert(!result.rightResult);
+}
+
 // force disabled FMA math
 private static auto half(T)(const T x)
 {
diff --git a/source/mir/test.d b/source/mir/test.d
index d5e32ea0..69958a53 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -43,6 +43,7 @@ ShouldApprox!T shouldApprox(T)(const T value, const T maxRelDiff = T(0x1p-20f),
 }
 
 ///
+version(mir_test)
 unittest
 {
     1.0.shouldApprox == 1 + 9e-7;
@@ -76,6 +77,7 @@ Should!T should(T)(T value)
 }
 
 ///
+version(mir_test)
 unittest
 {
     1.0.should == 1;
@@ -98,6 +100,7 @@ bool should(alias fun, T, R)(const T value, const R expected, string file = __FI
 }
 
 ///
+version(mir_test)
 unittest
 {
     1.0.should!"a < b"(1.3);

From d3b6a512441319899747972f2308f98076ca844b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 24 Mar 2022 13:39:05 +0700
Subject: [PATCH 329/573] update findSmileRoots

---
 source/mir/numeric.d | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index ff2c8625..77e63624 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1430,6 +1430,9 @@ struct FindSmileRootsResult(T)
     Right result if any
     +/
     Nullable!(FindRootResult!T) rightResult;
+    /++
+    +/
+    Nullable!(FindLocalMinResult!T) localMinResult;
 
 @safe pure @nogc scope const @property:
 
@@ -1518,9 +1521,9 @@ if (__traits(isFloating, T))
     else
     if (res.status == FindRootStatus.badBounds)
     {
-        auto min = findLocalMin!f(ax, bx, relTolerance, absTolerance);
-        ret.leftResult = findRoot!(f, tolerance)(ax, min.x, T.nan, min.y, T.nan, T.nan, maxIterations);
-        ret.rightResult = findRoot!(f, tolerance)(min.x, bx, min.y, T.nan, T.nan, T.nan, maxIterations);
+        ret.localMinResult = findLocalMin!f(ax, bx, relTolerance, absTolerance);
+        ret.leftResult = findRoot!(f, tolerance)(ax, ret.localMinResult.x, T.nan, ret.localMinResult.y, T.nan, T.nan, maxIterations);
+        ret.rightResult = findRoot!(f, tolerance)(ret.localMinResult.x, bx, ret.localMinResult.y, T.nan, T.nan, T.nan, maxIterations);
     }
     return ret;
 }
@@ -1529,18 +1532,23 @@ if (__traits(isFloating, T))
 version(mir_test)
 unittest
 {
+    import mir.math.common: approxEqual;
     auto result = findSmileRoots!(x => x ^^ 2 - 1)(-10.0, 10.0);
     assert(result.roots.length == 2);
     assert(result.roots[0].approxEqual(-1));
     assert(result.roots[1].approxEqual(+1));
     assert(result.leftResult);
     assert(result.rightResult);
+    assert(result.localMinResult);
+    assert(result.localMinResult.get.x.approxEqual(0));
+    assert(result.localMinResult.get.y.approxEqual(-1));
 }
 
 /// Skew
 version(mir_test)
 unittest
 {
+    import mir.math.common: approxEqual;
     auto result = findSmileRoots!(x => x ^^ 2 - 1)(0.5, 10.0);
     assert(result.roots.length == 1);
     assert(result.roots[0].approxEqual(+1));

From eb81f4d586952fede5ea16a95fc0e25f39950e99 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 25 Mar 2022 17:32:57 +0700
Subject: [PATCH 330/573] add sumSlices

---
 dub.sdl               |  2 +-
 source/mir/math/sum.d | 47 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index 8366872f..15ca28d6 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.88"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_ndslice_test" "mir_test" "mir_core_test"
+    versions "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 03537167..0d1b4b6d 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1881,7 +1881,54 @@ template sum(string summation)
     mixin("alias sum = .sum!(Summation." ~ summation ~ ");");
 }
 
+private static immutable jaggedMsg = "sum: each slice should have the same length";
+version(D_Exceptions)
+    static immutable jaggedException = new Exception(jaggedMsg);
 
+/++
+Sum slices with a naive algorithm.
++/
+template sumSlices()
+{
+    import mir.primitives: DeepElementType;
+    import mir.ndslice.slice: Slice, SliceKind, isSlice;
+    ///
+    auto sumSlices(Iterator, SliceKind kind)(Slice!(Iterator, 1, kind) sliceOfSlices)
+        if (isSlice!(DeepElementType!(Slice!(Iterator, 1, kind))))
+    {
+        import mir.ndslice.topology: as;
+        import mir.ndslice.allocation: slice;
+        alias T = Unqual!(DeepElementType!(DeepElementType!(Slice!(Iterator, 1, kind))));
+        import mir.ndslice: slice;
+        if (sliceOfSlices.length == 0)
+            return Slice!(T*).init;
+        auto ret = slice(as!T(sliceOfSlices.front));
+        sliceOfSlices.popFront;
+        foreach (sl; sliceOfSlices)
+        {
+            if (sl.length != ret.length)
+            {
+                version (D_Exceptions)
+                    throw jaggedException;
+                else
+                    assert(0);
+            }
+            ret[] += sl[];
+        }
+        return ret;
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    import mir.ndslice.topology: map;
+    import mir.ndslice.slice: sliced;
+
+    auto ar = [[1, 2, 3], [10, 20, 30]];
+    assert(ar.map!sliced.sumSlices == [11, 22, 33]);
+}
 
 version(mir_test)
 @safe pure nothrow unittest

From 4e8aa3329998d0872d147f6edf9cc5dc6a4d8da7 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 25 Mar 2022 17:51:05 +0700
Subject: [PATCH 331/573] add KeyValue to StringMap

---
 source/mir/string_map.d | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 5f594403..7d701d50 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -36,6 +36,15 @@ struct StringMap(T, U = uint)
     import core.lifetime: move;
     import mir.conv: emplaceRef;
 
+    ///
+    static struct KeyValue
+    {
+        ///
+        string key;
+        ///
+        T value;
+    }
+
     ///
     alias serdeKeysProxy = Unqual!T;
 
@@ -242,6 +251,8 @@ struct StringMap(T, U = uint)
     {
         return implementation ? implementation.keys : null;
     }
+    ///
+    alias byKey = keys;
 
     version(mir_test) static if (is(T == int))
     ///
@@ -272,6 +283,27 @@ struct StringMap(T, U = uint)
         return implementation ? implementation.values : null;
     }
 
+    /// ditto
+    alias byValue = values;
+
+    version(mir_test) static if (is(T == int))
+    ///
+    @safe pure unittest
+    {
+        StringMap!double map;
+        assert(map.byKeyValue == StringMap!double.KeyValue[].init);
+        map["c"] = 4.0;
+        map["a"] = 3.0;
+        assert(map.byKeyValue == [StringMap!double.KeyValue("c", 4.0), StringMap!double.KeyValue("a", 3.0)]);
+    }
+
+    ///
+    auto byKeyValue(this This)() @trusted pure nothrow @nogc
+    {
+        import mir.ndslice.topology: zip, map;
+        return keys.zip(values).map!KeyValue;
+    }
+
     version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest

From b8148f3ba58dcc6c0490119af074f88dbd8e2977 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 29 Mar 2022 20:41:19 +0700
Subject: [PATCH 332/573] add SmileRoots

---
 source/mir/numeric.d | 71 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 77e63624..8c21defe 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1415,6 +1415,63 @@ version(mir_test) @safe unittest
     }
 }
 
+/++
+A set of one or two smile roots.
+
+Because we assume that volatility smile is convex the equantion above can have no more then two roots.
+
+The `left` and `right` members are equal if the smile has only one root.
++/
+struct SmileRoots(T)
+    if (__traits(isFloating, T))
+{
+    import mir.small_array: SmallArray;
+
+    ///
+    T left;
+    ///
+    T right;
+
+@safe pure nothrow @nogc:
+
+    ///
+    this(T value)
+    {
+        left = right = value;
+    }
+
+    ///
+    this(T left, T right)
+    {
+        this.left = left;
+        this.right = right;
+    }
+
+    ///
+    this(SmallArray!(T, 2) array)
+    {
+        if (array.length == 2)
+            this(array[0], array[1]);
+        else
+        if (array.length == 1)
+            this(array[0]);
+        else
+            this(T.nan, T.nan);
+    }
+
+    ///
+    inout(T)[] opIndex() @trusted inout
+    {
+        return (&left)[0 .. 2];
+    }
+
+    ///
+    size_t count() const
+    {
+        return 1 + (left != right);
+    }
+}
+
 /++
 +/
 struct FindSmileRootsResult(T)
@@ -1476,6 +1533,14 @@ struct FindSmileRootsResult(T)
             ret.append(rightResult.get.x);
         return ret;
     }
+
+    /++
+    Returns: $(LREF SmileRoots).
+    +/
+    SmileRoots!double smileRoots()()
+    {
+        return typeof(return)(roots);
+    }
 }
 
 /++
@@ -1537,6 +1602,8 @@ unittest
     assert(result.roots.length == 2);
     assert(result.roots[0].approxEqual(-1));
     assert(result.roots[1].approxEqual(+1));
+    assert(result.smileRoots.left.approxEqual(-1));
+    assert(result.smileRoots.right.approxEqual(+1));
     assert(result.leftResult);
     assert(result.rightResult);
     assert(result.localMinResult);
@@ -1552,6 +1619,8 @@ unittest
     auto result = findSmileRoots!(x => x ^^ 2 - 1)(0.5, 10.0);
     assert(result.roots.length == 1);
     assert(result.roots[0].approxEqual(+1));
+    assert(result.smileRoots.left.approxEqual(+1));
+    assert(result.smileRoots.right.approxEqual(+1));
     assert(!result.leftResult);
     assert(result.rightResult);
 }
@@ -1562,6 +1631,8 @@ unittest
     auto result = findSmileRoots!(x => x ^^ 2 - 1)(-10.0, -0.5);
     assert(result.roots.length == 1);
     assert(result.roots[0].approxEqual(-1));
+    assert(result.smileRoots.left.approxEqual(-1));
+    assert(result.smileRoots.right.approxEqual(-1));
     assert(result.leftResult);
     assert(!result.rightResult);
 }

From e33ca497794680999a509a7ab27f9ed35f9812e2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 30 Mar 2022 14:35:56 +0700
Subject: [PATCH 333/573] extend serdeParseEnum API

---
 source/mir/serde.d | 66 +++++++++++++---------------------------------
 1 file changed, 18 insertions(+), 48 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 5330da5a..afa0b3e2 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -882,51 +882,10 @@ alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value
 
 /++
 +/
-bool serdeParseEnum(E)(const char[] str, ref E res)
+bool serdeParseEnum(E)(scope const char[] str, ref E res)
     @safe pure nothrow @nogc
+    if (is(E == enum))
 {
-    static if (__VERSION__ < 2093)
-    {
-        static if (hasUDA!(E, serdeIgnoreCase))
-        {
-            import mir.format: stringBuf;
-            stringBuf buf;
-            buf << str;
-            auto ustr = buf.data.fastToUpperInPlace;
-        }
-        else
-        {
-            alias ustr = str;
-        }
-        switch(ustr)
-        {
-            foreach(i, member; EnumMembers!E)
-            {{
-                enum initKeys = serdeGetKeysIn(EnumMembers!E[i]);
-                static if (hasUDA!(E, serdeIgnoreCase))
-                {
-                    import mir.ndslice.topology: map;
-                    import mir.array.allocation: array;
-                    enum keys = initKeys.map!fastLazyToUpper.map!array.array;
-                }
-                else
-                {
-                    enum keys = initKeys;
-                }
-                static assert (keys.length, "At least one input enum key is required");
-                static foreach (key; keys)
-                {
-                    case key:
-                }
-                res = member;
-                return true;
-            }}
-            default:
-                return false;
-        }
-    }
-    else
-    {
     import mir.enums: getEnumIndexFromKey, unsafeEnumFromIndex;
     import mir.utility: _expect;
 
@@ -937,7 +896,20 @@ bool serdeParseEnum(E)(const char[] str, ref E res)
         return true;
     }
     return false;
-    }
+}
+
+version(D_Exceptions)
+/// ditto
+auto serdeParseEnum(E)(scope const char[] str)
+    @safe pure
+    if (is(E == enum))
+{
+    import mir.utility: max;
+    E ret;
+    if (.serdeParseEnum(str, ret))
+        return ret;
+    import mir.exception: MirException;
+    throw new MirException("Can't deserialzie ", E.stringof, " from string", str[0 .. max($, 128u)]);
 }
 
 ///
@@ -959,10 +931,8 @@ version(mir_test) unittest
     assert(e == E.a);
     assert(serdeParseEnum("beta", e));
     assert(e == E.b);
-    assert(serdeParseEnum("B", e));
-    assert(e == E.b);
-    assert(serdeParseEnum("c", e));
-    assert(e == E.c);
+    assert("B".serdeParseEnum!E == E.b);
+    assert("c".serdeParseEnum!E == E.c);
 
     assert(!serdeParseEnum("C", e));
     assert(!serdeParseEnum("Alpha", e));

From a828eb1da6832fd4d57794141663deb09d58085d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 31 Mar 2022 20:42:05 +0700
Subject: [PATCH 334/573] add ediff and normal

---
 index.d                       |   3 +
 meson.build                   |   2 +
 source/mir/ediff.d            | 938 ++++++++++++++++++++++++++++++++++
 source/mir/math/func/normal.d | 444 ++++++++++++++++
 4 files changed, 1387 insertions(+)
 create mode 100644 source/mir/ediff.d
 create mode 100644 source/mir/math/func/normal.d

diff --git a/index.d b/index.d
index 0cf158e2..fa1c7123 100644
--- a/index.d
+++ b/index.d
@@ -40,6 +40,9 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,math,sum)) $(TD Various precise summation algorithms))
     $(TR $(TDNW $(MREF mir,numeric)) $(TD Basic numeric optimisations))
     $(TR $(TDNW $(MREF mir,polynomial)) $(TD Polynomial ref-counted structure))
+    $(TR $(TDNW $(MREF mir,ediff)) $(TD Expression differentiation))
+    $(TR $(TDNW $(MREF mir,math,func,expdigamma)) $(TD `exp(digamma(x))`))
+    $(TR $(TDNW $(MREF mir,math,func,normal)) $(TD Normal Distribution API))
     $(LEADINGROW Reference counting)
     $(TR $(TDNW $(MREF mir,rc,array)) $(TD Thread safe reference count array and the iterator to adopt it to ndslice.))
     $(TR $(TDNW $(MREF mir,rc,ptr)) $(TD Thread safe reference count pointer with polymorphism support for strucs and objects.))
diff --git a/meson.build b/meson.build
index 8e222198..22ba9604 100644
--- a/meson.build
+++ b/meson.build
@@ -28,6 +28,7 @@ sources_list = [
     'mir/container/binaryheap',
     'mir/cpp_export/numeric',
     'mir/date',
+    'mir/ediff',
     'mir/format_impl',
     'mir/format',
     'mir/graph/package',
@@ -41,6 +42,7 @@ sources_list = [
     'mir/interpolate/utility',
     'mir/lob',
     'mir/math/func/expdigamma',
+    'mir/math/func/normal',
     'mir/math/numeric',
     'mir/math/stat',
     'mir/math/sum',
diff --git a/source/mir/ediff.d b/source/mir/ediff.d
new file mode 100644
index 00000000..1ad75ce3
--- /dev/null
+++ b/source/mir/ediff.d
@@ -0,0 +1,938 @@
+/++
+$(H1 Expression differentiation)
+
+The module provides API for rapid evaluation of a user-requested set of partial derivatives of any order.
+
+The implementation operates with double precision numbers.
+
+$(BOOKTABLE $(H2 Expression construction),
+$(TR $(TH Name) $(TH Description))
+$(T2 $(LREF Const), A double precision constant with an immidiate value)
+$(T2 $(LREF Var), A double precision named variable with an immidiate value)
+$(T2 `+`, Constructs the sum of two expressions)
+$(T2 `-`, Constructs the difference of two expressions)
+$(T2 `*`, Constructs the product of two expressions)
+$(T2 `/`, Constructs the quotient of two expressions)
+$(T2 $(LREF derivativeOf), Constructs a partial derivative of one order.
+    The function can be applied in any stage any number of times.
+    The function does NOT evaluate the expression.)
+$(T2 $(LREF powi), Constructs of the expression raised to the specified (positive or negative) compile-time integral power.)
+$(T2 $(LREF log), Constructs the natural logarithm of the expression.)
+$(T2 $(LREF exp), Constructs the natural exponent of the expression.)
+$(T2 $(LREF sqrt), Constructs the square root of the expression.)
+$(T2 $(LREF normalDistribution), Constructs the cumulative normal distribution function of the expression.)
+)
+
+$(BOOKTABLE $(H2 Expression composition),
+$(TR $(TH Name) $(TH Description))
+$(T2 $(LREF composeAt), Given a compiletime variable name `v` and two expressions `F(v, U)` and `G(Y)`,
+    constructs a composition `F ∘ G (U, Y)` as `v = G`)
+)
+
+$(BOOKTABLE $(H2 Derivative set definition),
+$(TR $(TH Name) $(TH Description))
+$(T2 $(LREF Derivative), User defined attribute that should applied on a struct member definition of a user-defined set of derivatives.
+    The attribute holds an unordered set with duplicates of variables names to reflect which partial derivative this member contains.)
+$(T2 $(LREF Minus), User-defined attribute that can be applied on struct member definition along with $(LREF Derivative) to denote
+    that the member holds a negative value of the partial derivative.
+    This attribute is useful for financial code where verbal definitions may denote negative partial derivatives.)
+)
+
+$(BOOKTABLE $(H2 Function definition),
+$(TR $(TH Name) $(TH Description))
+$(T2 $(LREF DependsOn), User defined attribute that should applied on struct definition of a user-defined expression function)
+$(T2 $(LREF Dependencies), Fetchs a set of variables the expression is depends on.
+    First, it checks if the expression has $(LREF DependsOn) UDA
+    ; otherwise, iterates over members with $(LREF Derivative) UDA and collects variable names. )
+$(T2 .getDerivative, The generic method, which a user expression function should define.
+    The method should accept a compile-time array of variable names and evaluate the corresponding partial derivative.)
+$(T2 $(LREF ediffOperators, Mixin template that propagates `+`, `-`, `*`, and `/` binary operators for user defined expression functions.))
+)
+
+$(BOOKTABLE $(H2 Expression evaluation),
+$(TR $(TH Name) $(TH Description))
+$(T2 $(LREF getFunctionValue), Evaluates function value. It is shortcut for $(LREF getDerivative) of zero order.)
+$(T2 $(LREF getDerivative), Evaluates partial derivative for user-defined compiletime set of variables.
+    First, it checks if the expression has `.getDerivative` methods and uses it,
+    otherwise iterates over members with $(LREF Derivative) UDA and tries to find a member that holds the required partial derivative.)
+$(T2 $(LREF setDerivatives), Evaluates partial derivatives and function value, if any, for a user-provided set of partial derivatives.
+    The derivative set can be defined with $(LREF Derivative) and $(LREF Minus) UDAs.)
+)
+
+$(H2 Optimization note)
+
+During function differentiation, the resulting set of expressions likely contains a lot of
+identical calls of elementary functions. LLVM  efficiently eliminates equivalent calls of intrinsic
+functions such as `powi`, `log`, `exp`, and `sqrt`.
+On the other hand, it can't eliminate identical calls of complex functions.
+It is highly recommended to evaluate a set of partial derivatives immediately after constructing
+a complex expression such as $(LREF normalDistribution).
+
+Authors: Ilya Yaroshenko
++/
+module mir.ediff;
+
+///
+version(mir_test)
+unittest
+{
+    ///
+    static struct D
+    {
+        @Derivative()
+        double _;
+        @Derivative("a")
+        double a;
+        @Derivative("b")
+        double b;
+        @Minus @Derivative("a", "b") 
+        double mab;
+    }
+
+    auto d = D(3, 4, 5, -6);
+    assert(d.powi!2.setDerivatives!D == D(9, 24, 30, -76));
+}
+///
+version(mir_test)
+unittest
+{
+    ///
+    static struct Greeks
+    {
+        @Derivative("spot")
+        double delta;
+        @Derivative("spot", "spot")
+        double gamma;
+        @Minus @Derivative("time", "spot") 
+        double charm;
+    }
+
+    auto greeks = Greeks(2, 3, 4);
+
+    auto dspot = derivativeOf!"spot"(&greeks);
+
+    assert(greeks.delta is dspot.getFunctionValue);
+    assert(greeks.gamma is dspot.getDerivative!(["spot"]));
+    assert(greeks.charm is -dspot.getDerivative!(["time"]));
+}
+
+///
+version(mir_test)
+unittest
+{
+    import mir.math;
+
+    // Test Const
+    static assert(Dependencies!Const == [].DependsOn);
+    auto c = 5.Const;
+    static assert(c.getDerivative!(["any"]) == 0);
+    assert(c.getFunctionValue == 5);
+
+    // Test Var
+    auto spot = 7.Var!"spot";
+    static assert(Dependencies!(Var!"spot") == ["spot"].DependsOn);
+    static assert(spot.getDerivative!(["spot"]) == 1);
+    static assert(spot.getDerivative!(["other"]) == 0);
+    assert(spot.getFunctionValue == 7);
+
+    // Test integer power and exponent
+    auto f1 = exp(3.Const * spot.powi!(-2));
+    static assert(Dependencies!(typeof(f1)) == ["spot"].DependsOn);
+    assert(f1.getFunctionValue == mir.math.exp(3 * 7.0 ^^ -2));
+    assert(f1.getDerivative!(["spot"]) == 3 * -2 * 7.0 ^^ -3 * mir.math.exp(3 * 7.0 ^^ -2));
+    // Test DerivativeOf
+    assert(f1.derivativeOf!"spot".getFunctionValue == f1.getDerivative!(["spot"]));
+    // Test product and sum
+    assert(f1.derivativeOf!"spot".derivativeOf!"spot".getFunctionValue.approxEqual((3 * (-2 * 7.0 ^^ -3)) ^^ 2 * mir.math.exp(3 * 7.0 ^^ -2) + 3 * (6 * 7.0 ^^ -4)* mir.math.exp(3 * 7.0 ^^ -2)));
+
+    auto strike = 9.Var!"strike";
+
+    auto f2 = strike * f1 + strike;
+    assert(f2.getDerivative!(["strike"]).approxEqual(1 + f1.getFunctionValue));
+    // Test log
+    assert(f2.log.getFunctionValue == mir.math.log(f2.getFunctionValue));
+    assert(f2.log.getDerivative!(["strike"]) == getFunctionValue(f2.powi!(-1) * (1.Const + f1)));
+    assert(f2.sqrt.getFunctionValue == mir.math.sqrt(f2.getFunctionValue));
+    assert(f2.sqrt.getDerivative!(["strike"]) == getFunctionValue(f2.sqrt.powi!(-1)  * 0.5.Const * (1.Const + f1)));
+
+    // Compose
+    auto barrier = 13.Var!"barrier";
+    auto fc = barrier.powi!2 / strike;
+    auto f3 = f2.composeAt!"strike"(fc);
+    assert(f3.getFunctionValue == f2.getFunctionValue);
+    assert(f3.getDerivative!(["vol"]) == f2.getDerivative!(["vol"]));
+    assert(f3.getDerivative!(["strike"]) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike"]));
+    assert(f3.getDerivative!(["barrier"]) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]));
+    assert(getDerivative!(["barrier"])(f3 + barrier) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) + 1);
+    assert(f3.getDerivative!(["strike", "barrier"]) == 
+        f2.getDerivative!(["strike", "strike"]) * fc.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) +
+        f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike", "barrier"]));
+
+    /// normalDistribution
+    import mir.math.func.normal: constantNormalDistribution = normalDistribution, normalProbabilityDensity;
+    assert(barrier.powi!2.normalDistribution.getFunctionValue == constantNormalDistribution(13.0 ^^ 2));
+    assert(barrier.powi!2.normalDistribution.getDerivative!(["barrier"]) == normalProbabilityDensity(13.0 ^^ 2) * (2 * 13));
+}
+
+import mir.algorithm.iteration: uniq;
+import mir.array.allocation;
+import mir.math.common;
+import mir.ndslice.sorting: sort;
+import std.traits: Unqual, getUDAs, hasUDA, isPointer, PointerTarget;
+
+@optmath:
+
+/++
++/
+mixin template ediffOperators()
+{
+const:
+    auto opBinary(string op, R)(const R rhs)
+        if ((op == "+" || op == "-") && is(R == struct))
+    {
+        return Sum!(Unqual!(typeof(this)), R, op == "-")(this, rhs);
+    }
+
+    auto opBinary(string op, R)(const R rhs)
+        if (op == "*" && is(R == struct))
+    {
+        alias L = Unqual!(typeof(this));
+        static if  (is(R == Const) && is(L == Const))
+        {
+            return Const(this.value * rhs.value);
+        }
+        else
+        static if (is(R == Const) && is(L == Powi!(_power, _LT), size_t _power, _LT))
+        {
+            return L(this.value, this.coefficient * rhs.value);
+        }
+        else
+        static if (is(L == Const) && is(R == Powi!(_power, _RT), size_t _power, _RT))
+        {
+            return R(rhs.value, rhs.coefficient * this.value);
+        }
+        else
+        {
+            return Product!(L, R)(this, rhs);
+        }
+    }
+
+    auto opBinaryRight(string op, L)(const L lhs)
+        if ((op == "+" || op == "*") && is(L == struct))
+    {
+        return this.opBinary!op(lhs);
+    }
+
+    auto opBinaryRight(string op, L)(const L lhs)
+        if ((op == "-") && is(L == struct))
+    {
+        return Sum!(L, Unqual!(typeof(this)), true)(lhs, this);
+    }
+
+    auto opBinary(string op, R)(const R rhs)
+        if (op == "/" && is(R == struct))
+    {
+        alias L = Unqual!(typeof(this));
+        alias A = L;
+        alias B = R;
+        alias a = this;
+        alias b = rhs;
+        mixin _div;
+        return result;
+    }
+
+    auto opBinaryRight(string op, L)(const L lhs)
+        if ((op == "/") && is(L == struct))
+    {
+        alias R = Unqual!(typeof(this));
+        alias A = L;
+        alias B = R;
+        alias a = lhs;
+        alias b = this;
+        mixin _div;
+        return result;
+    }
+}
+
+private mixin template _div()
+{
+    // A / B
+    static if (is(A == Const) && is(B == Const))
+        auto result = Const(a.value / b.value);
+    else
+    static if (is(A == Const))
+        auto result = Powi!(-1, B)(b, a.value);
+    else
+    static if (is(B == Const))
+        auto result = Const(1.0 / b.value) * a;
+    else
+        auto result = b.powi!(-1) * a;
+}
+
+private template Sum(A, B, bool diff = false)
+{
+
+    @(Dependencies!A ~ Dependencies!B)
+    struct Sum
+    {
+        A a;
+        B b;
+
+    @optmath:
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                auto getDerivative() const @property
+                {
+                    double ret = 0;
+                    static if (Dependencies!A.containsAll(variables))
+                        ret = a.getDerivative!(variables, strict);
+                    static if (Dependencies!B.containsAll(variables))
+                    {
+                        static if (diff)
+                            ret -= b.getDerivative!(variables, strict);
+                        else
+                            ret += b.getDerivative!(variables, strict);
+                    }
+                    return ret;
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+private template Product(A, B)
+{
+    @(Dependencies!A ~ Dependencies!B)
+    struct Product
+    {
+        A a;
+        B b;
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+        @optmath:
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                auto getDerivative() const @property
+                {
+                    static if (variables.length == 0)
+                    {
+                        return a.getFunctionValue!strict * b.getFunctionValue!strict;
+                    }
+                    else
+                    {
+                        enum variable = variables[0];
+                        static if (!Dependencies!A.contains(variable))
+                            return (a * b.derivativeOf!variable).getDerivative!(variables[1 .. $], strict);
+                        else
+                        static if (!Dependencies!B.contains(variable))
+                            return (a.derivativeOf!variable * b).getDerivative!(variables[1 .. $], strict);
+                        else
+                            return (a * b.derivativeOf!variable + a.derivativeOf!variable * b).getDerivative!(variables[1 .. $], strict);
+                    }
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+User defined attribute that should applied on struct definition of a user-defined expression function.
++/
+struct DependsOn
+{
+    ///
+    string[] variables;
+
+@safe pure nothrow:
+    ///
+    auto contains(string variable) const @nogc
+    {
+        foreach (v; variables)
+            if (v == variable)
+                return true;
+        return false;
+    }
+
+    ///
+    auto containsAll(string[] variables) const @nogc
+    {
+        foreach (v; variables)
+            if (!contains(v))
+                return false;
+        return true;
+    }
+
+    ///
+    auto containsAny(string[] variables) const @nogc
+    {
+        foreach (v; variables)
+            if (contains(v))
+                return true;
+        return false;
+    }
+
+    /// Set union
+    DependsOn opBinary(string op : "~")(DependsOn rhs)
+    {
+        return (this.variables ~ rhs.variables).sort.uniq.array.DependsOn;
+    }
+}
+
+/++
+Fetchs a set of variables the expression is depends on.
+    First, it checks if the expression has $(LREF DependsOn) UDA
+    ; otherwise, iterates over members with $(LREF Derivative) UDA and collects variable names.
++/
+template Dependencies(T)
+{
+    static if (isPointer!T)
+        enum Dependencies = Dependencies!(PointerTarget!T);
+    else
+    static if (hasUDA!(T, DependsOn))
+        enum DependsOn Dependencies = getUDAs!(T, DependsOn)[0];
+    else
+    {
+        enum DependsOn Dependencies = () {
+            string[] variables;
+            static foreach (member; __traits(allMembers, T))
+            {
+                static if (hasUDA!(__traits(getMember, T, member), Derivative))
+                {
+                    variables ~= getUDAs!(__traits(getMember, T, member), Derivative)[0].variables;
+                }
+            }
+            return variables.sort.uniq.array.DependsOn;
+        } ();
+    }
+}
+
+/++
+User defined attribute that should applied on a struct member definition of a user-defined set of derivatives.
+    The attribute holds an unordered set with duplicates of variables names to reflect which partial derivative this member contains.
++/
+struct Derivative
+{
+    ///
+    string[] variables;
+
+@trusted pure nothrow @nogc:
+
+    ///
+    this(string[] variables...)
+    {
+        this.variables = variables.sort;
+    }
+}
+
+/++
+User-defined attribute that can be applied on struct member definition along with $(LREF Derivative) to denote
+    that the member holds a negative value of the partial derivative.
+    This attribute is useful for financial code where verbal definitions may denote negative partial derivatives.
++/
+enum Minus;
+
+/++
+Evaluates function value. It is shortcut for $(LREF getDerivative) of zero order.
+Params:
+    strict = The parameter is used when the expression can't evaluate the function. If true, prints error at compile-time; otherwise, `getFunctionValues` returns NaN.
++/
+alias getFunctionValue(bool strict = true) = getDerivative!(string[].init, strict);
+
+/++
+Evaluates partial derivative for user-defined compiletime set of variables.
+    First, it checks if the expression has `.getDerivative` methods and uses it,
+    otherwise iterates over members with $(LREF Derivative) UDA and tries to find a member that holds the required partial derivative.
+Params:
+    variables = array that denotes partial derivative
+    strict = The parameter is used when the expression can't evaluate the derivative. If true, prints error at compile-time; otherwise, `getDerivative` returns NaN.
++/
+template getDerivative(string[] variables, bool strict = true)
+{
+    import mir.ndslice.topology: pairwise;
+    import mir.algorithm.iteration: all;
+
+@optmath:
+
+    static if (variables.length == 0 || variables.pairwise!"a <= b".all)
+    {
+        auto ref getDerivative(T)(auto ref T value)
+        {
+            static if (__traits(hasMember, T, "getDerivative"))
+                return value.getDerivative!(variables, strict);
+            else
+            {
+                import std.meta: anySatisfy;
+
+                static if (isPointer!T)
+                    alias V = PointerTarget!T;
+                else
+                    alias V = T;
+                template hasDerivative(string member)
+                {
+                    static if (hasUDA!(__traits(getMember, V, member), Derivative))
+                        enum hasDerivative = variables == getUDAs!(__traits(getMember, V, member), Derivative)[0].variables;
+                    else
+                        enum hasDerivative = false;
+                }
+                static if (anySatisfy!(hasDerivative, __traits(allMembers, V)))
+                {
+                    static foreach (member; __traits(allMembers, V))
+                    {
+                        static if (hasDerivative!member)
+                        {
+                            static if (hasUDA!(__traits(getMember, V, member), Minus))
+                                return -__traits(getMember, value, member);
+                            else
+                                return __traits(getMember, value, member);
+                        }
+                    }
+                }
+                else
+                static if (strict)
+                {
+                    static assert(0, Unqual!V.stringof ~ "'_" ~ variables.stringof ~ " not found");
+                }
+                else
+                {
+                    return double.nan;
+                }
+            }
+        }
+    }
+    else
+    {
+        import mir.ndslice.sorting: sort;
+        alias getDerivative = .getDerivative!(variables.sort, strict);
+    }
+}
+
+/++
+Evaluates partial derivatives and function value, if any, for a user-provided set of partial derivatives.
+    The derivative set can be defined with $(LREF Derivative) and $(LREF Minus) UDAs.
+Params:
+    D = type of the requested set of partial derivatives
+    strict = The parameter is used when the expression can't evaluate the derivative. If true, prints error at compile-time; otherwise, the corresponding member is set to NaN.
+    derivatives = a structure that holds set of partial derivatives (optional)
+    expression = expression
++/
+void setDerivatives(bool strict = true, D, E)(scope ref D derivatives, E expression)
+{
+    static if (__traits(hasMember, D, "setDerivatives"))
+        return derivatives.setDerivatives!strict(expression);
+    else
+    {
+        import std.traits: getUDAs, hasUDA, isPointer, PointerTarget;
+
+        static foreach (member; __traits(allMembers, D))
+        {
+            static if (hasUDA!(__traits(getMember, D, member), Derivative))
+            {
+                static if (hasUDA!(__traits(getMember, derivatives, member), Minus))
+                    __traits(getMember, derivatives, member) = -expression.getDerivative!(getUDAs!(__traits(getMember, derivatives, member), Derivative)[0].variables, strict);
+                else
+                    __traits(getMember, derivatives, member) = expression.getDerivative!(getUDAs!(__traits(getMember, derivatives, member), Derivative)[0].variables, strict);
+            }
+        }
+    }
+}
+
+/// ditto
+template setDerivatives(D, bool strict = true)
+{
+    ///
+    D setDerivatives(E)(E expression)
+    {
+        D ret;
+        .setDerivatives!strict(ret, expression);
+        return ret;
+    }
+}
+
+private auto removeVariable(DependsOn variables, string variable)
+{
+    string[] ret;
+    foreach (v; variables.variables)
+        if (v != variable)
+            ret ~= v;
+    return ret.DependsOn;
+}
+
+private template ComposeAt(F, G, string position)
+    if (Dependencies!F.contains(position))
+{
+    @(Dependencies!F.removeVariable(position) ~ Dependencies!G)
+    struct ComposeAt
+    {
+        ///
+        F f;
+        ///
+        G g;
+
+        ///
+        template getDerivative(string[] variables, bool strict = true)
+        {
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                ///
+                auto ref getDerivative() const @property
+                {
+                    static if (!Dependencies!G.containsAny(variables))
+                        return f.getDerivative!(variables, strict);
+                    else
+                    {
+                        static if (Dependencies!F.contains(variables[0]) && variables[0] != position)
+                            auto a = f.derivativeOf!(variables[0]).composeAt!position(g).getDerivative!(variables[1 .. $], strict);
+                        else
+                            enum a = 0;
+                        static if (Dependencies!G.contains(variables[0]))
+                            auto b = (f.derivativeOf!position.composeAt!position(g) * g.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
+                        else
+                            enum b = 0;
+                        return a + b;
+                    }
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+Given a compiletime variable name `v` and two expressions `F(v, U)` and `G(Y)`,
+    constructs a composition `F ∘ G (U, Y)` as `v = G`.
+
+Params:
+    position = name of the variable to compose functions at.
++/
+template composeAt(string position)
+{
+    /++
+    Params:
+        f = F expression
+        g = G expression
+    +/
+    auto composeAt(F, G)(const F f, const G g)
+    {
+        return ComposeAt!(F, G, position)(f, g);
+    }
+}
+
+private template DerivativeOf(T, string variable)
+{
+    @Dependencies!T
+    struct DerivativeOf
+    {
+        // Underlying expression
+        T underlying;
+
+    @optmath:
+
+        ///
+        auto ref getDerivative(string[] variables, bool strict = true)() const @property
+        {
+            return underlying.getDerivative!(variable ~ variables, strict);
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+Constructs a partial derivative of one order.
+    The function can be applied in any stage any number of times.
+    The function does NOT evaluate the expression.
++/
+template derivativeOf(string variable)
+{
+@optmath:
+
+    /++
+    Params:
+        value = expression
+    +/
+    auto derivativeOf(T)(const T value) @property
+    {
+        static if (isPointer!T)
+            return DerivativeOf!(const(PointerTarget!T)*, variable)(value);
+        else
+            return DerivativeOf!(T, variable)(value);
+    }
+}
+
+/++
+A double precision constant with an immidiate value
++/
+@DependsOn()
+struct Const
+{
+    /// Immidiate value
+    double value;
+
+    template getDerivative(string[] variables, bool strict = true)
+    {
+        static if (variables.length == 0)
+            alias getDerivative = value;
+        else
+            enum double getDerivative = 0;
+    }
+
+    mixin ediffOperators;
+}
+
+/++
+A double precision named variable with an immidiate value
++/
+template Var(string name)
+{
+    ///
+    @DependsOn([name])
+    struct Var
+    {
+        /// Immidiate value
+        double value;
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+            static if (variables.length == 0)
+                alias getDerivative = value;
+            else
+                enum double getDerivative = variables == [name];
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+private template Powi(int power, T)
+    if (power)
+{
+    @Dependencies!T
+    struct Powi
+    {
+        T value;
+        double coefficient = 1;
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+        @optmath:
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                auto getDerivative() const @property
+                {
+                    static if (variables.length == 0)
+                    {
+                        static if (power == 0)
+                            return coefficient;
+                        else
+                        static if (power == 1)
+                            return coefficient * value.getFunctionValue!strict;
+                        else
+                        static if (power == 2)
+                            return coefficient * value.getFunctionValue!strict ^^ 2;
+                        else
+                        static if (power == -1)
+                            return coefficient / value.getFunctionValue!strict;
+                        else
+                        static if (power == -2)
+                            return coefficient / value.getFunctionValue!strict ^^ 2;
+                        else
+                            return coefficient * mir.math.common.powi(value.getFunctionValue!strict, power);
+                    }
+                    else
+                    {
+                        static if (power == 1)
+                            auto v = coefficient.Const;
+                        else
+                            auto v = Powi!(power - 1, T)(value, coefficient * power);
+                        static if (is(T == Var!(variables[0])))
+                            return v.getDerivative!(variables[1 .. $], strict);
+                        else
+                            return (v * value.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
+                    }
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+Constructs of the expression raised to the specified (positive or negative) compile-time integral power.
+
+Params:
+    power = integral power (compile-time)
+    value = expression
++/
+auto powi(int power, T)(const T value)
+    if (is(T == struct))
+{
+    static if (power == 0)
+        return 1.Const;
+    else
+        return Powi!(power, T)(value);
+}
+
+private template Exp(T)
+{
+    @Dependencies!T
+    struct Exp
+    {
+        /// Power function
+        T power;
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+        @optmath:
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                auto getDerivative() const @property
+                {
+                    static if (variables.length == 0)
+                        return mir.math.common.exp(power.getFunctionValue!strict);
+                    else
+                        return (this * power.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+Constructs the natural exponent of the expression.
+
+Params:
+    power = expression
++/
+auto exp(T)(const T power)
+    if (is(T == struct))
+{
+    return Exp!T(power);
+}
+
+private template Log(T)
+{
+    @Dependencies!T
+    struct Log
+    {
+        T value;
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+        @optmath:
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                auto getDerivative() const @property
+                {
+                    static if (variables.length == 0)
+                        return mir.math.common.log(value.getFunctionValue!strict);
+                    else
+                        return (value.derivativeOf!(variables[0]) / value).getDerivative!(variables[1 .. $], strict);
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+Constructs the natural logarithm of the expression.
+
+Params:
+    value = expression
++/
+auto log(T)(const T value)
+    if (is(T == struct))
+{
+    return Log!T(value);
+}
+
+private template Sqrt(T)
+{
+    @Dependencies!T
+    struct Sqrt
+    {
+        T value;
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+        @optmath:
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                auto getDerivative() const @property
+                {
+                    static if (variables.length == 0)
+                        return mir.math.common.sqrt(value.getFunctionValue!strict);
+                    else
+                        return (Powi!(-1, Sqrt!T)(this, 0.5) * value.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+Constructs the square root of the expression.
+
+Params:
+    value = expression
++/
+auto sqrt(T)(const T value)
+    if (is(T == struct))
+{
+    return Sqrt!T(value);
+}
+
+private template NormalDistribution(T)
+{
+    @Dependencies!T
+    struct NormalDistribution
+    {
+        /// Square root argument function
+        T value;
+
+        template getDerivative(string[] variables, bool strict = true)
+        {
+        @optmath:
+            static if (Dependencies!(typeof(this)).containsAll(variables))
+                auto getDerivative() const @property
+                {
+                    import mir.math.func.normal: normalDistribution, SQRT2PIINV;
+                    static if (variables.length == 0)
+                        return normalDistribution(value.getFunctionValue!strict);
+                    else
+                        return (SQRT2PIINV.Const * Powi!(2, T)(value, -0.5).exp * value.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
+                }
+            else
+                enum double getDerivative = 0;
+        }
+
+        mixin ediffOperators;
+    }
+}
+
+/++
+Constructs the cumulative normal distribution function of the expression
+
+Params:
+    value = expression
++/
+auto normalDistribution(T)(const T value)
+    if (is(T == struct))
+{
+    return NormalDistribution!T(value);
+}
diff --git a/source/mir/math/func/normal.d b/source/mir/math/func/normal.d
new file mode 100644
index 00000000..5d3ddef6
--- /dev/null
+++ b/source/mir/math/func/normal.d
@@ -0,0 +1,444 @@
+/**
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+*/
+/**
+ * Error Functions and Normal Distribution.
+ *
+ * Copyright: Based on the CEPHES math library, which is
+ *            Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
+ * Authors:   Stephen L. Moshier, ported to D by Don Clugston and David Nadlinger. Adopted to Mir by Ilya Yaroshenko.
+ */
+/**
+ * Macros:
+ *  NAN = $(RED NAN)
+ *  INTEGRAL = &#8747;
+ *  POWER = $1<sup>$2</sup>
+ *      <caption>Special Values</caption>
+ *      $0</table>
+ */
+
+module mir.math.func.normal;
+
+import std.traits: isFloatingPoint;
+import mir.math.common;
+
+@safe pure nothrow @nogc:
+
+///
+T normalProbabilityDensity(T)(const T z)
+ if (isFloatingPoint!T)
+{
+    import mir.math.common: sqrt, exp;
+    return T(SQRT2PIINV) * exp(z * z * -0.5);
+}
+
+/// ditto
+T normalProbabilityDensity(T)(const T x, const T mean, const T stdDev)
+if (isFloatingPoint!T)
+{
+    return normalProbabilityDensity((x - mean) / stdDev) / stdDev;
+}
+
+/++
+Computes the normal distribution function.
+The normal (or Gaussian, or bell-shaped) distribution is
+defined as:
+normalDist(x) = 1/$(SQRT) &pi; $(INTEGRAL -$(INFINITY), x) exp( - $(POWER t, 2)/2) dt
+    = 0.5 + 0.5 * erf(x/sqrt(2))
+    = 0.5 * erfc(- x/sqrt(2))
+To maintain accuracy at high values of x, use
+normalDistribution(x) = 1 - normalDistribution(-x).
+Accuracy:
+Within a few bits of machine resolution over the entire
+range.
+References:
+$(LINK http://www.netlib.org/cephes/ldoubdoc.html),
+G. Marsaglia, "Evaluating the Normal Distribution",
+Journal of Statistical Software <b>11</b>, (July 2004).
++/
+T normalDistribution(T)(const T a)
+    if (isFloatingPoint!T)
+{
+    pragma(inline, false);
+    import mir.math.constant: SQRT1_2;
+
+    T x = a * T(SQRT1_2);
+    T z = fabs(x);
+
+    if (z < 1)
+    {
+        return 0.5f + 0.5f * erf(x);
+    }
+    else
+    {
+        T y = 0.5f * erfce(z);
+        /* Multiply by exp(-x^2 / 2)  */
+        z = expx2(a, -1);
+        y = y * sqrt(z);
+        if (x > 0)
+            y = 1 - y;
+        return y;
+    }
+}
+
+/// ditto
+T normalDistribution(T)(const T x, const T mean, const T stdDev)
+    if (isFloatingPoint!T)
+{
+    return normalDistribution((x - mean) / stdDev);
+}
+
+version(mir_test)
+@safe unittest
+{
+    assert(fabs(normalDistribution(1.0L) - (0.841344746068543))< 0.0000000000000005);
+}
+
+///
+T normalDistributionInverse(T)(const T p)
+in {
+  assert(p >= 0 && p <= 1, "Domain error");
+}
+do
+{
+    pragma(inline, false);
+    if (p <= 0 || p >= 1)
+    {
+        if (p == 0)
+            return -T.infinity;
+        if ( p == 1 )
+            return T.infinity;
+        return T.nan; // domain error
+    }
+    int code = 1;
+    T y = p;
+    if ( y > (1 - T(EXP_2)) )
+    {
+        y = 1 - y;
+        code = 0;
+    }
+
+    T x, z, y2, x0, x1;
+
+    if ( y > T(EXP_2) )
+    {
+        y = y - 0.5L;
+        y2 = y * y;
+        x = y + y * (y2 * rationalPoly!(P0, Q0)(y2));
+        return x * double(SQRT2PI);
+    }
+
+    x = sqrt( -2 * log(y) );
+    x0 = x - log(x)/x;
+    z = 1/x;
+    if ( x < 8 )
+    {
+        x1 = z * rationalPoly!(P1, Q1)(z);
+    }
+    else if ( x < 32 )
+    {
+        x1 = z * rationalPoly!(P2, Q2)(z);
+    }
+    else
+    {
+        x1 = z * rationalPoly!(P3, Q3)(z);
+    }
+    x = x0 - x1;
+    if ( code != 0 )
+    {
+        x = -x;
+    }
+    return x;
+}
+
+/// ditto
+T normalDistributionInverse(T)(const T p, const T mean, const T stdDev)
+    if (isFloatingPoint!T)
+{
+    return normalDistributionInverse(p) * stdDev + mean;
+}
+
+///
+version(mir_test)
+@safe unittest
+{
+    import std.math: feqrel;
+    // TODO: Use verified test points.
+    // The values below are from Excel 2003.
+    assert(fabs(normalDistributionInverse(0.001) - (-3.09023230616779))< 0.00000000000005);
+    assert(fabs(normalDistributionInverse(1e-50) - (-14.9333375347885))< 0.00000000000005);
+    assert(feqrel(normalDistributionInverse(0.999L), -normalDistributionInverse(0.001L)) > real.mant_dig-6);
+
+    // Excel 2003 gets all the following values wrong!
+    assert(normalDistributionInverse(0.0) == -real.infinity);
+    assert(normalDistributionInverse(1.0) == real.infinity);
+    assert(normalDistributionInverse(0.5) == 0);
+    // (Excel 2003 returns norminv(p) = -30 for all p < 1e-200).
+    // The value tested here is the one the function returned in Jan 2006.
+    real unknown1 = normalDistributionInverse(1e-250L);
+    assert( fabs(unknown1 -(-33.79958617269L) ) < 0.00000005);
+}
+
+///
+enum real SQRT2PI = 2.50662827463100050241576528481104525L; // sqrt(2pi)
+///
+enum real SQRT2PIINV = 1 / SQRT2PI; // 1 / sqrt(2pi)
+
+private:
+
+enum real EXP_2  = 0.135335283236612691893999494972484403L; /* exp(-2) */
+
+///
+enum T MAXLOG(T) = log(T.max);
+///
+enum T MINLOG(T) = log(T.min_normal * T.epsilon); // log(smallest denormal);
+
+/**
+ *  Exponential of squared argument
+ *
+ * Computes y = exp(x*x) while suppressing error amplification
+ * that would ordinarily arise from the inexactness of the
+ * exponential argument x*x.
+ *
+ * If sign < 0, the result is inverted; i.e., y = exp(-x*x) .
+ *
+ * ACCURACY:
+ *                      Relative error:
+ * arithmetic      domain        # trials      peak         rms
+ *   IEEE     -106.566, 106.566    10^5       1.6e-19     4.4e-20
+ */
+T expx2(T)(const T x_, int sign)
+{
+    /*
+    Cephes Math Library Release 2.9:  June, 2000
+    Copyright 2000 by Stephen L. Moshier
+    */
+    const T M = 32_768.0;
+    const T MINV = 3.0517578125e-5L;
+
+    T x = fabs(x_);
+    if (sign < 0)
+        x = -x;
+
+  /* Represent x as an exact multiple of M plus a residual.
+     M is a power of 2 chosen so that exp(m * m) does not overflow
+     or underflow and so that |x - m| is small.  */
+    T m = MINV * floor(M * x + 0.5f);
+    T f = x - m;
+
+    /* x^2 = m^2 + 2mf + f^2 */
+    T u = m * m;
+    T u1 = 2 * m * f  +  f * f;
+
+    if (sign < 0)
+    {
+        u = -u;
+        u1 = -u1;
+    }
+
+    if (u + u1 > MAXLOG!T)
+        return T.infinity;
+
+    /* u is exact, u1 is small.  */
+    return exp(u) * exp(u1);
+}
+
+/**
+   Exponentially scaled erfc function
+   exp(x^2) erfc(x)
+   valid for x > 1.
+   Use with ndtrl and expx2l.  */
+
+T erfce(T)(const T x)
+{
+    T y = 1 / x;
+
+    if (x < 8)
+    {
+        return rationalPoly!(P, Q)(y);
+    }
+    else
+    {
+        return y * rationalPoly!(R, S)(y * y);
+    }
+}
+
+private T rationalPoly(alias numerator, alias denominator, T)(const T x) pure nothrow
+{
+    return x.poly!numerator / x.poly!denominator;
+}
+
+private T poly(alias vec, T)(const T x)
+{
+    import mir.internal.utility: Iota;
+    T y = T(vec[$ - 1]);
+    foreach_reverse(i; Iota!(vec.length - 1))
+    {
+        y *= x;
+        y += T(vec[i]);
+    }
+    return y;
+}
+
+/* erfc(x) = exp(-x^2) P(1/x)/Q(1/x)
+   1/8 <= 1/x <= 1
+   Peak relative error 5.8e-21  */
+immutable real[10] P = [ -0x1.30dfa809b3cc6676p-17, 0x1.38637cd0913c0288p+18,
+   0x1.2f015e047b4476bp+22, 0x1.24726f46aa9ab08p+25, 0x1.64b13c6395dc9c26p+27,
+   0x1.294c93046ad55b5p+29, 0x1.5962a82f92576dap+30, 0x1.11a709299faba04ap+31,
+   0x1.11028065b087be46p+31, 0x1.0d8ef40735b097ep+30
+];
+
+immutable real[11] Q = [ 0x1.14d8e2a72dec49f4p+19, 0x1.0c880ff467626e1p+23,
+   0x1.04417ef060b58996p+26, 0x1.404e61ba86df4ebap+28, 0x1.0f81887bc82b873ap+30,
+   0x1.4552a5e39fb49322p+31, 0x1.11779a0ceb2a01cep+32, 0x1.3544dd691b5b1d5cp+32,
+   0x1.a91781f12251f02ep+31, 0x1.0d8ef3da605a1c86p+30, 1.0
+];
+
+/* erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+    1/128 <= 1/x < 1/8
+    Peak relative error 1.9e-21  */
+immutable real[5] R = [ 0x1.b9f6d8b78e22459ep-6, 0x1.1b84686b0a4ea43ap-1,
+    0x1.b8f6aebe96000c2ap+1, 0x1.cb1dbedac27c8ec2p+2, 0x1.cf885f8f572a4c14p+1
+];
+
+immutable real[6] S = [
+    0x1.87ae3cae5f65eb5ep-5, 0x1.01616f266f306d08p+0, 0x1.a4abe0411eed6c22p+2,
+    0x1.eac9ce3da600abaap+3, 0x1.5752a9ac2faebbccp+3, 1.0
+];
+
+/* erf(x)  = x P(x^2)/Q(x^2)
+    0 <= x <= 1
+    Peak relative error 7.6e-23  */
+immutable real[7] T = [ 0x1.0da01654d757888cp+20, 0x1.2eb7497bc8b4f4acp+17,
+    0x1.79078c19530f72a8p+15, 0x1.4eaf2126c0b2c23p+11, 0x1.1f2ea81c9d272a2ep+8,
+    0x1.59ca6e2d866e625p+2, 0x1.c188e0b67435faf4p-4
+];
+
+immutable real[7] U = [ 0x1.dde6025c395ae34ep+19, 0x1.c4bc8b6235df35aap+18,
+    0x1.8465900e88b6903ap+16, 0x1.855877093959ffdp+13, 0x1.e5c44395625ee358p+9,
+    0x1.6a0fed103f1c68a6p+5, 1.0
+];
+
+F erf(F)(const F x)
+ if(isFloatingPoint!F)
+{
+    if (x == 0)
+        return x; // deal with negative zero
+    if (x == -F.infinity)
+        return -1;
+    if (x == F.infinity)
+        return 1;
+    immutable ax = fabs(x);
+    if (ax > 1)
+        return 1 - erfc(x);
+
+    return x * rationalPoly!(T, U)(x * x);
+}
+
+/**
+ *  Complementary error function
+ *
+ * erfc(x) = 1 - erf(x), and has high relative accuracy for
+ * values of x far from zero. (For values near zero, use erf(x)).
+ *
+ *  1 - erf(x) =  2/ $(SQRT)(&pi;)
+ *     $(INTEGRAL x, $(INFINITY)) exp( - $(POWER t, 2)) dt
+ *
+ *
+ * For small x, erfc(x) = 1 - erf(x); otherwise rational
+ * approximations are computed.
+ *
+ * A special function expx2(x) is used to suppress error amplification
+ * in computing exp(-x^2).
+ */
+T erfc(T)(const T a)
+{
+    if (a == T.infinity)
+        return 0;
+    if (a == -T.infinity)
+        return 2;
+
+    immutable x = (a < 0) ? -a : a;
+
+    if (x < 1)
+        return 1 - erf(a);
+
+    if (-a * a < -MAXLOG!T)
+    {
+        // underflow
+        if (a < 0) return 2;
+        else return 0;
+    }
+
+    T y;
+    immutable z = expx2(a, -1);
+
+    y = 1 / x;
+    if (x < 8)
+        y = z * rationalPoly!(P, Q)(y);
+    else
+        y = z * y * rationalPoly!(R, S)(y * y);
+
+    if (a < 0)
+        y = 2 - y;
+
+    if (y == 0)
+    {
+        // underflow
+        if (a < 0) return 2;
+        else return 0;
+    }
+
+    return y;
+}
+
+static immutable real[8] P0 =
+[ -0x1.758f4d969484bfdcp-7, 0x1.53cee17a59259dd2p-3,
+-0x1.ea01e4400a9427a2p-1,  0x1.61f7504a0105341ap+1, -0x1.09475a594d0399f6p+2,
+0x1.7c59e7a0df99e3e2p+1, -0x1.87a81da52edcdf14p-1,  0x1.1fb149fd3f83600cp-7
+];
+
+static immutable real[8] Q0 =
+[ -0x1.64b92ae791e64bb2p-7, 0x1.7585c7d597298286p-3,
+-0x1.40011be4f7591ce6p+0, 0x1.1fc067d8430a425ep+2, -0x1.21008ffb1e7ccdf2p+3,
+0x1.3d1581cf9bc12fccp+3, -0x1.53723a89fd8f083cp+2, 1.0
+];
+
+static immutable real[10] P1 =
+[ 0x1.20ceea49ea142f12p-13, 0x1.cbe8a7267aea80bp-7,
+0x1.79fea765aa787c48p-2, 0x1.d1f59faa1f4c4864p+1, 0x1.1c22e426a013bb96p+4,
+0x1.a8675a0c51ef3202p+5, 0x1.75782c4f83614164p+6, 0x1.7a2f3d90948f1666p+6,
+0x1.5cd116ee4c088c3ap+5, 0x1.1361e3eb6e3cc20ap+2
+];
+
+static immutable real[10] Q1 =
+[ 0x1.3a4ce1406cea98fap-13, 0x1.f45332623335cda2p-7,
+0x1.98f28bbd4b98db1p-2, 0x1.ec3b24f9c698091cp+1, 0x1.1cc56ecda7cf58e4p+4,
+0x1.92c6f7376bf8c058p+5, 0x1.4154c25aa47519b4p+6, 0x1.1b321d3b927849eap+6,
+0x1.403a5f5a4ce7b202p+4, 1.0
+];
+
+static immutable real[8] P2 =
+[ 0x1.8c124a850116a6d8p-21, 0x1.534abda3c2fb90bap-13,
+0x1.29a055ec93a4718cp-7, 0x1.6468e98aad6dd474p-3, 0x1.3dab2ef4c67a601cp+0,
+0x1.e1fb3a1e70c67464p+1, 0x1.b6cce8035ff57b02p+2, 0x1.9f4c9e749ff35f62p+1
+];
+
+static immutable real[8] Q2 =
+[ 0x1.af03f4fc0655e006p-21, 0x1.713192048d11fb2p-13,
+0x1.4357e5bbf5fef536p-7, 0x1.7fdac8749985d43cp-3, 0x1.4a080c813a2d8e84p+0,
+0x1.c3a4b423cdb41bdap+1, 0x1.8160694e24b5557ap+2, 1.0
+];
+
+static immutable real[8] P3 =
+[ -0x1.55da447ae3806168p-34, -0x1.145635641f8778a6p-24,
+-0x1.abf46d6b48040128p-17, -0x1.7da550945da790fcp-11, -0x1.aa0b2a31157775fap-8,
+0x1.b11d97522eed26bcp-3, 0x1.1106d22f9ae89238p+1, 0x1.029a358e1e630f64p+1
+];
+
+static immutable real[8] Q3 =
+[ -0x1.74022dd5523e6f84p-34, -0x1.2cb60d61e29ee836p-24,
+-0x1.d19e6ec03a85e556p-17, -0x1.9ea2a7b4422f6502p-11, -0x1.c54b1e852f107162p-8,
+0x1.e05268dd3c07989ep-3, 0x1.239c6aff14afbf82p+1, 1.0
+];

From 1bcb9a54beab9de2c9360140616a6452c5504bcb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 6 Apr 2022 16:07:45 +0700
Subject: [PATCH 335/573] fix bigint overflow bug

---
 source/mir/bignum/decimal.d | 3 ++-
 source/mir/math/sum.d       | 2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index bb81fa35..84ec95ad 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -981,7 +981,8 @@ struct Decimal(size_t maxSize64)
     ref opOpAssign(string op, size_t rhsMaxSize64)(ref const Decimal!rhsMaxSize64 rhs) @safe pure return
         if (op == "+" || op == "-")
     {
-        BigInt!rhsMaxSize64 rhsCopy;
+        import mir.utility: max;
+        BigInt!(max(rhsMaxSize64, maxSize64, 256u)) rhsCopy = void;
         BigIntView!(const size_t) rhsView;
         auto expDiff = exponent - rhs.exponent;
         if (expDiff >= 0)
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 0d1b4b6d..5121b622 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -38,6 +38,8 @@ unittest
     assert(ar.sum!"kbn" == 0.7000000000000455);
     assert(ar.sum!"kb2" == 0.7000000000000455);
     assert(ar.sum!"precise" == 0.7000000000000455);
+
+    assert([1e-20, 1].sum!"decimal" == 1);
 }
 
 ///

From d1cfde72c500adc2aca33b134bac466b42c8269c Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 12 Apr 2022 00:54:07 -0400
Subject: [PATCH 336/573] Provide alternate API for normal distribution
 functions, deprecate existing API (#397)

* Provide alternate API for normal distribution functions, deprecate existing API

* Address review on deprecated, remove UT, change to alias
---
 source/mir/ediff.d            | 27 ++++++++++-----
 source/mir/math/func/normal.d | 62 +++++++++++++++++++++++++----------
 2 files changed, 63 insertions(+), 26 deletions(-)

diff --git a/source/mir/ediff.d b/source/mir/ediff.d
index 1ad75ce3..bd0397d3 100644
--- a/source/mir/ediff.d
+++ b/source/mir/ediff.d
@@ -169,9 +169,9 @@ unittest
         f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike", "barrier"]));
 
     /// normalDistribution
-    import mir.math.func.normal: constantNormalDistribution = normalDistribution, normalProbabilityDensity;
-    assert(barrier.powi!2.normalDistribution.getFunctionValue == constantNormalDistribution(13.0 ^^ 2));
-    assert(barrier.powi!2.normalDistribution.getDerivative!(["barrier"]) == normalProbabilityDensity(13.0 ^^ 2) * (2 * 13));
+    import mir.math.func.normal: constantNormalCDF = normalCDF, normalPDF;
+    assert(barrier.powi!2.normalCDF.getFunctionValue == constantNormalCDF(13.0 ^^ 2));
+    assert(barrier.powi!2.normalCDF.getDerivative!(["barrier"]) == normalPDF(13.0 ^^ 2) * (2 * 13));
 }
 
 import mir.algorithm.iteration: uniq;
@@ -897,10 +897,10 @@ auto sqrt(T)(const T value)
     return Sqrt!T(value);
 }
 
-private template NormalDistribution(T)
+private template NormalCDF(T)
 {
     @Dependencies!T
-    struct NormalDistribution
+    struct NormalCDF
     {
         /// Square root argument function
         T value;
@@ -911,9 +911,9 @@ private template NormalDistribution(T)
             static if (Dependencies!(typeof(this)).containsAll(variables))
                 auto getDerivative() const @property
                 {
-                    import mir.math.func.normal: normalDistribution, SQRT2PIINV;
+                    import mir.math.func.normal: normalCDF, SQRT2PIINV;
                     static if (variables.length == 0)
-                        return normalDistribution(value.getFunctionValue!strict);
+                        return normalCDF(value.getFunctionValue!strict);
                     else
                         return (SQRT2PIINV.Const * Powi!(2, T)(value, -0.5).exp * value.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
                 }
@@ -931,8 +931,17 @@ Constructs the cumulative normal distribution function of the expression
 Params:
     value = expression
 +/
-auto normalDistribution(T)(const T value)
+deprecated("normalDistribution renamed, use normalCDF instead")
+alias normalDistribution = normalCDF;
+
+/++
+Constructs the cumulative normal distribution function of the expression
+
+Params:
+    value = expression
++/
+auto normalCDF(T)(const T value)
     if (is(T == struct))
 {
-    return NormalDistribution!T(value);
+    return NormalCDF!T(value);
 }
diff --git a/source/mir/math/func/normal.d b/source/mir/math/func/normal.d
index 5d3ddef6..bc9e566e 100644
--- a/source/mir/math/func/normal.d
+++ b/source/mir/math/func/normal.d
@@ -25,7 +25,11 @@ import mir.math.common;
 @safe pure nothrow @nogc:
 
 ///
-T normalProbabilityDensity(T)(const T z)
+deprecated("normalProbabilityDensity renamed, use normalPDF instead")
+alias normalProbabilityDensity = normalPDF;
+
+///
+T normalPDF(T)(const T z)
  if (isFloatingPoint!T)
 {
     import mir.math.common: sqrt, exp;
@@ -33,10 +37,10 @@ T normalProbabilityDensity(T)(const T z)
 }
 
 /// ditto
-T normalProbabilityDensity(T)(const T x, const T mean, const T stdDev)
+T normalPDF(T)(const T x, const T mean, const T stdDev)
 if (isFloatingPoint!T)
 {
-    return normalProbabilityDensity((x - mean) / stdDev) / stdDev;
+    return normalPDF((x - mean) / stdDev) / stdDev;
 }
 
 /++
@@ -56,7 +60,27 @@ $(LINK http://www.netlib.org/cephes/ldoubdoc.html),
 G. Marsaglia, "Evaluating the Normal Distribution",
 Journal of Statistical Software <b>11</b>, (July 2004).
 +/
-T normalDistribution(T)(const T a)
+deprecated("normalDistribution renamed, use normalCDF instead")
+alias normalDistribution = normalCDF;
+
+/++
+Computes the normal distribution cumulative distribution function (CDF).
+The normal (or Gaussian, or bell-shaped) distribution is
+defined as:
+normalDist(x) = 1/$(SQRT) &pi; $(INTEGRAL -$(INFINITY), x) exp( - $(POWER t, 2)/2) dt
+    = 0.5 + 0.5 * erf(x/sqrt(2))
+    = 0.5 * erfc(- x/sqrt(2))
+To maintain accuracy at high values of x, use
+normalCDF(x) = 1 - normalCDF(-x).
+Accuracy:
+Within a few bits of machine resolution over the entire
+range.
+References:
+$(LINK http://www.netlib.org/cephes/ldoubdoc.html),
+G. Marsaglia, "Evaluating the Normal Distribution",
+Journal of Statistical Software <b>11</b>, (July 2004).
++/
+T normalCDF(T)(const T a)
     if (isFloatingPoint!T)
 {
     pragma(inline, false);
@@ -82,20 +106,24 @@ T normalDistribution(T)(const T a)
 }
 
 /// ditto
-T normalDistribution(T)(const T x, const T mean, const T stdDev)
+T normalCDF(T)(const T x, const T mean, const T stdDev)
     if (isFloatingPoint!T)
 {
-    return normalDistribution((x - mean) / stdDev);
+    return normalCDF((x - mean) / stdDev);
 }
 
 version(mir_test)
 @safe unittest
 {
-    assert(fabs(normalDistribution(1.0L) - (0.841344746068543))< 0.0000000000000005);
+    assert(fabs(normalCDF(1.0L) - (0.841344746068543))< 0.0000000000000005);
 }
 
 ///
-T normalDistributionInverse(T)(const T p)
+deprecated("normalDistributionInverse renamed, use normalInvCDF instead")
+alias normalDistributionInverse = normalInvCDF;
+
+///
+T normalInvCDF(T)(const T p)
 in {
   assert(p >= 0 && p <= 1, "Domain error");
 }
@@ -152,10 +180,10 @@ do
 }
 
 /// ditto
-T normalDistributionInverse(T)(const T p, const T mean, const T stdDev)
+T normalInvCDF(T)(const T p, const T mean, const T stdDev)
     if (isFloatingPoint!T)
 {
-    return normalDistributionInverse(p) * stdDev + mean;
+    return normalInvCDF(p) * stdDev + mean;
 }
 
 ///
@@ -165,17 +193,17 @@ version(mir_test)
     import std.math: feqrel;
     // TODO: Use verified test points.
     // The values below are from Excel 2003.
-    assert(fabs(normalDistributionInverse(0.001) - (-3.09023230616779))< 0.00000000000005);
-    assert(fabs(normalDistributionInverse(1e-50) - (-14.9333375347885))< 0.00000000000005);
-    assert(feqrel(normalDistributionInverse(0.999L), -normalDistributionInverse(0.001L)) > real.mant_dig-6);
+    assert(fabs(normalInvCDF(0.001) - (-3.09023230616779))< 0.00000000000005);
+    assert(fabs(normalInvCDF(1e-50) - (-14.9333375347885))< 0.00000000000005);
+    assert(feqrel(normalInvCDF(0.999L), -normalInvCDF(0.001L)) > real.mant_dig-6);
 
     // Excel 2003 gets all the following values wrong!
-    assert(normalDistributionInverse(0.0) == -real.infinity);
-    assert(normalDistributionInverse(1.0) == real.infinity);
-    assert(normalDistributionInverse(0.5) == 0);
+    assert(normalInvCDF(0.0) == -real.infinity);
+    assert(normalInvCDF(1.0) == real.infinity);
+    assert(normalInvCDF(0.5) == 0);
     // (Excel 2003 returns norminv(p) = -30 for all p < 1e-200).
     // The value tested here is the one the function returned in Jan 2006.
-    real unknown1 = normalDistributionInverse(1e-250L);
+    real unknown1 = normalInvCDF(1e-250L);
     assert( fabs(unknown1 -(-33.79958617269L) ) < 0.00000005);
 }
 

From 2db113952a59becb6b768325c89a225f20097518 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 13 Apr 2022 14:02:46 +0700
Subject: [PATCH 337/573] add SplineConfiguration

---
 source/mir/interpolate/spline.d | 139 +++++++++++++++++++++++++++++---
 1 file changed, 129 insertions(+), 10 deletions(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index c1b93ac1..1e353deb 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -29,12 +29,15 @@ import std.meta: AliasSeq, staticMap;
 import std.traits: Unqual;
 public import mir.interpolate: atInterval;
 
-enum msg_min =  "spline interpolant: minimal allowed length for the grid equals 2.";
-enum msg_eq =  "spline interpolant: X and Y values length should be equal.";
+static immutable msg_min =  "spline interpolant: minimal allowed length for the grid equals 2.";
+static immutable msg_eq =  "spline interpolant: X and Y values length should be equal.";
+static immutable splineConfigurationMsg = "spline configuration: .boundary method requires equal left and right boundaries";
+
 version(D_Exceptions)
 {
     static immutable exc_min = new Exception(msg_min);
     static immutable exc_eq = new Exception(msg_eq);
+    static immutable splineConfigurationException = new Exception(splineConfigurationMsg);
 }
 
 private alias ValueType(T, X) = typeof(T.init.opCall(Repeat!(T.dimensionCount, X.init)));
@@ -55,7 +58,7 @@ private alias ValueType(T, X) = typeof(T.init.opCall(Repeat!(T.dimensionCount, X
     static immutable ydata = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
     auto y = ydata.sliced;
 
-    auto interpolant = spline!double(x, y); // constructs Spline
+    auto interpolant = spline!double(x, y, SplineConfiguration!double()); // constructs Spline
     auto xs = x + 0.5;                     // input X values for cubic spline
 
     static immutable test_data0 = [
@@ -557,6 +560,28 @@ template spline(T, size_t N = 1, X = T)
         ret._computeDerivatives(kind, param, lBoundary, rBoundary);
         return ret;
     }
+
+    /++
+    Params:
+        grid = immutable `x` values for interpolant
+        values = `f(x)` values for interpolant
+        configuration = $(LREF SplineConfiguration)
+    Constraints:
+        `grid` and `values` must have the same length >= 3
+    Returns: $(LREF Spline)
+    +/
+    Spline!(T, N, X) spline(yIterator, SliceKind ykind)(
+        Repeat!(N, Slice!(RCI!(immutable X))) grid,
+        Slice!(yIterator, N, ykind) values,
+        SplineConfiguration!T configuration,
+        )
+    {
+        auto ret = typeof(return)(forward!grid);
+        ret._values = values;
+        with(configuration)
+            ret._computeDerivatives(kind, param, leftBoundary, rightBoundary);
+        return ret;
+    }
 }
 
 /++
@@ -567,10 +592,6 @@ See_also: $(LREF SplineBoundaryCondition) $(LREF SplineType)
 extern(C++, "mir", "interpolate")
 enum SplineBoundaryType
 {
-    /++
-    Not implemented.
-    +/
-    periodic = -1,
     /++
     Not-a-knot (or cubic) boundary condition.
     It is an aggresive boundary condition that is used only for C2 splines and is default for all API calls.
@@ -598,6 +619,10 @@ enum SplineBoundaryType
     Default for Akima splines.
     +/
     akima,
+    /++
+    Not implemented.
+    +/
+    periodic = -1,
 }
 
 /++
@@ -609,12 +634,85 @@ extern(C++, "mir", "interpolate")
 struct SplineBoundaryCondition(T)
     if (__traits(isFloating, T))
 {
+    import mir.serde: serdeOptional, serdeIgnoreDefault;
+
     /// type (default is $(LREF SplineBoundaryType.notAKnot))
-    SplineBoundaryType type = SplineBoundaryType.notAKnot;
+    SplineBoundaryType type;
+
+@serdeOptional @serdeIgnoreDefault:
+
     /// value (default is 0)
     T value = 0;
 }
 
+/// Spline configuration
+struct SplineConfiguration(T)
+    if (__traits(isFloating, T))
+{
+    import mir.serde: serdeOptional, serdeIgnoreDefault, serdeIgnoreOutIfAggregate, serdeIgnore;
+
+    ///
+    @serdeOptional @serdeIgnoreDefault
+    SplineType kind;
+    ///
+    @serdeOptional @serdeIgnoreOutIfAggregate!"a.symmetric"
+    SplineBoundaryCondition!T leftBoundary;
+    ///
+    @serdeOptional @serdeIgnoreOutIfAggregate!"a.symmetric"
+    SplineBoundaryCondition!T rightBoundary;
+
+    /++
+    Returns:
+        true of `leftBoundary` equals `rightBoundary`.
+    +/
+    @serdeIgnore
+    bool symmetric() const @property
+    {
+        return leftBoundary == rightBoundary;
+    }
+
+    ///
+    @serdeOptional
+    void boundary(SplineBoundaryCondition!T boundary) @property
+    {
+        leftBoundary = rightBoundary = boundary;
+    }
+
+    ///
+    @serdeIgnoreOutIfAggregate!"!a.symmetric"
+    SplineBoundaryCondition!T boundary() const @property
+    {
+        assert(!symmetric, splineConfigurationMsg);
+        return leftBoundary;
+    }
+
+    /++
+    Tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
+    Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
+    +/
+    @serdeOptional @serdeIgnoreDefault
+    T param = 0;
+}
+
+/// Spline configuration with two boundaries
+struct SplineSymmetricConfiguration(T)
+    if (__traits(isFloating, T))
+{
+    import mir.serde: serdeOptional, serdeIgnoreDefault;
+
+@serdeOptional @serdeIgnoreDefault:
+
+    ///
+    SplineType type;
+    ///
+    SplineBoundaryCondition!T boundary;
+    /++
+    Tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
+    Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
+    +/
+    T param = 0;
+}
+
 /++
 Multivariate cubic spline with nodes on rectilinear grid.
 +/
@@ -1485,13 +1583,34 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
     SplineBoundaryCondition!F lBoundary,
     SplineBoundaryCondition!F rBoundary,
     SplineType kind = SplineType.c2,
-    in F param = 0,
+    const F param = 0,
     )
 {
     import core.lifetime: move;
     return MetaSpline!(T, X)(grid.move, data.move, kind, param, lBoundary, rBoundary);
 }
 
+/++
+Spline interpolator used for non-rectiliner trapezoid-like greeds.
+Params:
+    grid = rc-array of interpolation grid
+    data = rc-array of interpolator-like structures
+    configuration = $(LREF SplineConfiguration)
+Constraints:
+    `grid` and `values` must have the same length >= 3
+Returns: $(LREF Spline)
++/
+MetaSpline!(T, X) metaSpline(F, X, T)(
+    RCArray!(immutable X) grid,
+    RCArray!(const T) data,
+    SplineConfiguration!F configuration,
+    )
+{
+    import core.lifetime: move;
+    with(configuration)
+        return metaSpline!(F, X, T)(grid.move, data.move, leftBoundary, rightBoundary, kind, param);
+}
+
 /// ditto
 struct MetaSpline(T, X)
     if (T.derivativeOrder >= 3)
@@ -1663,7 +1782,7 @@ unittest
     auto int2 = spline!double(x1.rcslice!(immutable double), grid[2]);
     auto int3 = spline!double(x1.rcslice!(immutable double), grid[3]);
 
-    auto interpolant = metaSpline!double(x0.rcarray!(immutable double), rcarray(int0, int1, int2, int3).lightConst);
+    auto interpolant = metaSpline(x0.rcarray!(immutable double), rcarray(int0, int1, int2, int3).lightConst, SplineConfiguration!double.init);
 
     ///// compute test data ////
     auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23);

From 1a5dbca2498a646ce52382d0084f8e5384cd1ef6 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 16 Apr 2022 13:43:45 +0700
Subject: [PATCH 338/573] rework MetaSpline

---
 source/mir/interpolate/spline.d | 44 ++++++++++++++-------------------
 1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 1e353deb..1cbe05cd 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -1560,7 +1560,7 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
     const F param = 0,
     )
 {
-    return metaSpline!(F, X, T)(grid, data, boundaries, boundaries, kind, param);
+    return metaSpline!(F, X, T)(grid, data, SplineConfiguration!F(kind, boundaries, boundaries, param));
 }
 
 /++
@@ -1587,7 +1587,7 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
     )
 {
     import core.lifetime: move;
-    return MetaSpline!(T, X)(grid.move, data.move, kind, param, lBoundary, rBoundary);
+    return metaSpline(grid.move, data.move, SplineConfiguration!F(kind, lBoundary, rBoundary, param));
 }
 
 /++
@@ -1607,8 +1607,7 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
     )
 {
     import core.lifetime: move;
-    with(configuration)
-        return metaSpline!(F, X, T)(grid.move, data.move, leftBoundary, rightBoundary, kind, param);
+    return MetaSpline!(T, X)(grid.move, data.move, configuration);
 }
 
 /// ditto
@@ -1619,29 +1618,19 @@ struct MetaSpline(T, X)
     // alias ElementInterpolator = Linear!(F, N, X);
     alias F = ValueType!(T, X);
     ///
-    Spline!F spline;
+    private Spline!F spline;
     ///
     RCArray!(const T) data;
     //
     private RCArray!F _temp;
     ///
-    SplineType splineType;
-    ///
-    F splineParam;
-    ///
-    SplineBoundaryCondition!F lBoundary;
-    ///
-    SplineBoundaryCondition!F rBoundary;
-
+    SplineConfiguration!F configuration;
 
     ///
     this(
         RCArray!(immutable X) grid,
         RCArray!(const T) data,
-        SplineType splineType,
-        F splineParam,
-        SplineBoundaryCondition!F lBoundary,
-        SplineBoundaryCondition!F rBoundary,
+        SplineConfiguration!F configuration,
     )
     {
         import core.lifetime: move;
@@ -1661,10 +1650,15 @@ struct MetaSpline(T, X)
         this.data = data.move;
         this._temp = grid.length;
         this.spline = grid.moveToSlice;
-        this.splineType = splineType;
-        this.splineParam = splineParam;
-        this.lBoundary = lBoundary;
-        this.rBoundary = rBoundary;
+        this.configuration = configuration;
+    }
+
+    ///
+    bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
+    {
+        return this.gridScopeView == rhs.gridScopeView
+            && this.data == rhsthis.data
+            && this.configuration == rhs.configuration;
     }
 
     ///
@@ -1711,10 +1705,10 @@ struct MetaSpline(T, X)
             foreach (i, ref d; data)
                 mutable[i][0] = d(xs[1 .. $]);
             (*cast(Spline!F*)&spline)._computeDerivativesTemp(
-                splineType,
-                splineParam,
-                lBoundary,
-                rBoundary,
+                configuration.kind,
+                configuration.param,
+                configuration.leftBoundary,
+                configuration.rightBoundary,
                 (cast(F[])_temp[]).sliced);
             return spline(xs[0]);
         }

From 88a37907368ab1c2572f49056f540f179fa402f3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 16 Apr 2022 14:22:53 +0700
Subject: [PATCH 339/573] fixup

---
 source/mir/interpolate/spline.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 1cbe05cd..2b6e71c6 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -1657,7 +1657,7 @@ struct MetaSpline(T, X)
     bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
     {
         return this.gridScopeView == rhs.gridScopeView
-            && this.data == rhsthis.data
+            && this.data == rhs.data
             && this.configuration == rhs.configuration;
     }
 

From 93ead59be7d8ac527c260cacfa689f73834c3e47 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 16 Apr 2022 14:24:45 +0700
Subject: [PATCH 340/573] add  opEquals test for MetaSpline

---
 source/mir/interpolate/spline.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 2b6e71c6..f2885a40 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -1777,6 +1777,7 @@ unittest
     auto int3 = spline!double(x1.rcslice!(immutable double), grid[3]);
 
     auto interpolant = metaSpline(x0.rcarray!(immutable double), rcarray(int0, int1, int2, int3).lightConst, SplineConfiguration!double.init);
+    assert(interpolant == interpolant);
 
     ///// compute test data ////
     auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23);

From d10bcb3ab7d0223be4702cc4f0bf4410490123f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Wed, 20 Apr 2022 15:09:33 +0200
Subject: [PATCH 341/573] Document orderness of the return values of the
 StringMap members keys(), values() and byKeyValue() (#398)

* Correct mis-spelling of associative

* Document  orderness of the return values of the StringMap members keys(), values() and byKeyValue()
---
 source/mir/string_map.d | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 7d701d50..42576bce 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -1,5 +1,5 @@
 /++
-$(H1 Ordered string-value associtaive array)
+$(H1 Ordered string-value associative array)
 Macros:
 AlgebraicREF = $(GREF_ALTTEXT mir-core, $(TT $1), $1, mir, algebraic)$(NBSP)
 +/
@@ -23,7 +23,7 @@ unittest
 }
 
 /++
-Ordered string-value associtaive array with extremely fast lookup.
+Ordered string-value associative array with extremely fast lookup.
 
 Params:
     T = mutable value type, can be instance of $(AlgebraicREF Algebraic) for example.
@@ -113,7 +113,7 @@ struct StringMap(T, U = uint)
     // }
 
     /++
-    Reset the associtave array
+    Reset the associative array
     +/
     ref opAssign()(typeof(null)) return @safe pure nothrow @nogc
     {
@@ -130,7 +130,7 @@ struct StringMap(T, U = uint)
     }
 
     /++
-    Initialize the associtave array with default value.
+    Initialize the associative array with default value.
     +/
     this()(typeof(null) aa) @safe pure nothrow @nogc
     {
@@ -245,6 +245,9 @@ struct StringMap(T, U = uint)
     Returns a dynamic array, the elements of which are the keys in the associative array.
     Doesn't allocate a new copy.
 
+    The keys returned are guaranteed to be in the ordered inserted as long as no
+    key removals followed by at least one key insertion has been performed.
+
     Complexity: `O(1)`
     +/
     const(string)[] keys()() @safe pure nothrow @nogc const @property
@@ -276,6 +279,9 @@ struct StringMap(T, U = uint)
     Returns a dynamic array, the elements of which are the values in the associative array.
     Doesn't allocate a new copy.
 
+    The values returned are guaranteed to be in the ordered inserted as long as no
+    key removals followed by at least one key insertion has been performed.
+
     Complexity: `O(1)`
     +/
     inout(T)[] values()() @safe pure nothrow @nogc inout @property
@@ -297,7 +303,11 @@ struct StringMap(T, U = uint)
         assert(map.byKeyValue == [StringMap!double.KeyValue("c", 4.0), StringMap!double.KeyValue("a", 3.0)]);
     }
 
-    ///
+    /** Return a range over all elements (key-values pairs) currently stored in the associative array.
+
+        The elements returned are guaranteed to be in the ordered inserted as
+        long as no key removals nor no value mutations has been performed.
+    */
     auto byKeyValue(this This)() @trusted pure nothrow @nogc
     {
         import mir.ndslice.topology: zip, map;

From fdc2db6d14bb3a4add24f122c7570b894c369135 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 21 Apr 2022 16:53:52 +0700
Subject: [PATCH 342/573] Fix LikeArray

---
 source/mir/rc/array.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index bff23275..000963cd 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -318,7 +318,7 @@ package template LikeArray(Range)
     static if (__traits(identifier, Range) == "mir_slice")
     {
         import mir.ndslice.slice;
-        enum LikeArray = is(Range : Slice!(T*, N, kind), T, size_t N, SliceKind kind);
+        enum LikeArray = is(Range : Slice!(T*, N), T, size_t N);
     }
     else
     {

From b9b343d497a82bf42e35a618ee95b795c8e16e9c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 21 Apr 2022 17:42:27 +0700
Subject: [PATCH 343/573] fix LikeArray

---
 source/mir/rc/array.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 000963cd..7cee7e79 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -317,8 +317,8 @@ package template LikeArray(Range)
 {
     static if (__traits(identifier, Range) == "mir_slice")
     {
-        import mir.ndslice.slice;
-        enum LikeArray = is(Range : Slice!(T*, N), T, size_t N);
+        import mir.ndslice.slice: Slice, SliceKind;
+        enum LikeArray = is(Range : Slice!(T*, N, SliceKind.contiguous), T, size_t N);
     }
     else
     {

From c778cd3f8bff0a4a13cba18fd3fb0a682efd4a41 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 27 Apr 2022 12:30:48 +0700
Subject: [PATCH 344/573] relax linear interpolant contraints

---
 source/mir/interpolate/linear.d | 143 +++++++++++++++++++-------------
 1 file changed, 84 insertions(+), 59 deletions(-)

diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 6581c8b2..e205f12f 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -71,6 +71,11 @@ version(mir_test)
     static immutable data = [0.0011, 0.0030, 0.0064, 0.0104, 0.0144, 0.0176, 0.0207, 0.0225, 0.0243, 0.0261, 0.0268, 0.0274, 0.0281, 0.0288, 0.0295, 0.0302, 0.0309, 0.0316, 0.0322, 0.0329, 0.0332, 0.0335, 0.0337, 0.0340, 0.0342, 0.0345, 0.0348, 0.0350, 0.0353, 0.0356];
 
     assert(xs.sliced.vmap(interpolant).all!((a, b) => approxEqual(a, b, 1e-4, 1e-4))(data));
+
+    auto d = interpolant.withDerivative(9.0);
+    auto de = interpolant.opCall!2(9.0);
+    assert(de[0 .. 2] == d);
+    assert(de[2] == 0);
 }
 
 /// R^2 -> R: Bilinear interpolation
@@ -265,7 +270,6 @@ struct Linear(F, size_t N = 1, X = F)
 
     ///
     template opCall(uint derivative = 0)
-        if (derivative <= derivativeOrder)
     {
         /++
         `(x)` operator.
@@ -275,62 +279,72 @@ struct Linear(F, size_t N = 1, X = F)
         auto opCall(X...)(const X xs) scope const @trusted
             if (X.length == N)
         {
-            import mir.functional: AliasCall;
-            import mir.ndslice.topology: iota;
-            alias Kernel = AliasCall!(LinearKernel!F, "opCall", derivative);
+            static if (derivative > derivativeOrder)
+            {
+                auto res = this.opCall!derivativeOrder(xs);
+                typeof(res[0])[derivative + 1] ret = 0;
+                ret[0 .. derivativeOrder + 1] = res;
+                return ret;
+            }
+            else
+            {
+                import mir.functional: AliasCall;
+                import mir.ndslice.topology: iota;
+                alias Kernel = AliasCall!(LinearKernel!F, "opCall", derivative);
 
-            size_t[N] indices;
-            Kernel[N] kernels;
+                size_t[N] indices;
+                Kernel[N] kernels;
 
-            enum rp2d = derivative;
+                enum rp2d = derivative;
 
-            foreach(i; Iota!N)
-            {
-                static if (isInterval!(typeof(xs[i])))
+                foreach(i; Iota!N)
                 {
-                    indices[i] = xs[i][1];
-                    auto x = xs[i][0];
+                    static if (isInterval!(typeof(xs[i])))
+                    {
+                        indices[i] = xs[i][1];
+                        auto x = xs[i][0];
+                    }
+                    else
+                    { 
+                        alias x = xs[i];
+                        indices[i] = this.findInterval!i(x);
+                    }
+                    kernels[i] = LinearKernel!F(_grid[i][indices[i]], _grid[i][indices[i] + 1], x);
                 }
-                else
-                { 
-                    alias x = xs[i];
-                    indices[i] = this.findInterval!i(x);
-                }
-                kernels[i] = LinearKernel!F(_grid[i][indices[i]], _grid[i][indices[i] + 1], x);
-            }
 
-            align(64) F[2 ^^ N][derivative + 1] local;
-            immutable strides = _data._lengths.iota.strides;
+                align(64) F[2 ^^ N][derivative + 1] local;
+                immutable strides = _data._lengths.iota.strides;
 
-            void load(sizediff_t i)(F* from, F* to)
-            {
-                version(LDC) pragma(inline, true);
-                static if (i == -1)
-                {
-                    *to = *from;
-                }
-                else
+                void load(sizediff_t i)(F* from, F* to)
                 {
-                    from += strides[i] * indices[i];
-                    load!(i - 1)(from, to);
-                    from += strides[i];
-                    enum s = 2 ^^ (N - 1 - i);
-                    to += s;
-                    load!(i - 1)(from, to);
+                    version(LDC) pragma(inline, true);
+                    static if (i == -1)
+                    {
+                        *to = *from;
+                    }
+                    else
+                    {
+                        from += strides[i] * indices[i];
+                        load!(i - 1)(from, to);
+                        from += strides[i];
+                        enum s = 2 ^^ (N - 1 - i);
+                        to += s;
+                        load!(i - 1)(from, to);
+                    }
                 }
-            }
 
-            load!(N - 1)(cast(F*) _data.ptr, cast(F*)local[0].ptr);
+                load!(N - 1)(cast(F*) _data.ptr, cast(F*)local[0].ptr);
 
-            foreach(i; Iota!N)
-            {
-                enum P = 2 ^^ (N - 1 - i);
-                enum L = 2 ^^ (N - i * (1 - rp2d)) / 2;
-                vectorize(kernels[i], local[0][0 * L .. 1 * L], local[0][1 * L .. 2 * L], *cast(F[L][2 ^^ rp2d]*)local[rp2d].ptr);
-                static if (rp2d == 1)
-                    shuffle3!1(local[1][0 .. L], local[1][L .. 2 * L], local[0][0 .. L], local[0][L .. 2 * L]);
-                static if (i + 1 == N)
-                    return *cast(SplineReturnType!(F, N, 2 ^^ rp2d)*) local[0].ptr;
+                foreach(i; Iota!N)
+                {
+                    enum P = 2 ^^ (N - 1 - i);
+                    enum L = 2 ^^ (N - i * (1 - rp2d)) / 2;
+                    vectorize(kernels[i], local[0][0 * L .. 1 * L], local[0][1 * L .. 2 * L], *cast(F[L][2 ^^ rp2d]*)local[rp2d].ptr);
+                    static if (rp2d == 1)
+                        shuffle3!1(local[1][0 .. L], local[1][L .. 2 * L], local[0][0 .. L], local[0][L .. 2 * L]);
+                    static if (i + 1 == N)
+                        return *cast(SplineReturnType!(F, N, 2 ^^ rp2d)*) local[0].ptr;
+                }
             }
         }
     }
@@ -486,7 +500,6 @@ struct MetaLinear(T, X)
 
     ///
     template opCall(uint derivative = 0)
-        if (derivative <= derivativeOrder)
     {
         /++
         `(x)` operator.
@@ -496,22 +509,32 @@ struct MetaLinear(T, X)
         auto opCall(X...)(const X xs) scope const @trusted
             if (X.length == dimensionCount)
         {
-            static if (isInterval!(typeof(xs[0])))
+            static if (derivative > derivativeOrder)
             {
-                size_t index = xs[0][1];
-                auto x = xs[0][0];
+                auto res = this.opCall!derivativeOrder(xs);
+                typeof(res[0])[derivative + 1] ret = 0;
+                ret[0 .. derivativeOrder + 1] = res;
+                return ret;
             }
             else
-            { 
-                alias x = xs[0];
-                size_t index = this.findInterval(x);
+            {
+                static if (isInterval!(typeof(xs[0])))
+                {
+                    size_t index = xs[0][1];
+                    auto x = xs[0][0];
+                }
+                else
+                { 
+                    alias x = xs[0];
+                    size_t index = this.findInterval(x);
+                }
+                auto lhs = data[index + 0].opCall!derivative(xs[1 .. $]);
+                auto rhs = data[index + 1].opCall!derivative(xs[1 .. $]);
+                alias E = typeof(lhs);
+                alias F = DeepType!E;
+                auto kernel = LinearKernel!F(grid[index], grid[index + 1], x);
+                return kernel.opCall!derivative(lhs, rhs);
             }
-            auto lhs = data[index + 0].opCall!derivative(xs[1 .. $]);
-            auto rhs = data[index + 1].opCall!derivative(xs[1 .. $]);
-            alias E = typeof(lhs);
-            alias F = DeepType!E;
-            auto kernel = LinearKernel!F(grid[index], grid[index + 1], x);
-            return kernel.opCall!derivative(lhs, rhs);
         }
     }
 
@@ -520,6 +543,7 @@ struct MetaLinear(T, X)
 }
 
 /// 2D trapezoid-like (not rectilinear) linear interpolation
+version(mir_test)
 unittest
 {
     auto x = [
@@ -549,6 +573,7 @@ unittest
     auto valWithDerivative = trapezoidInterpolator.withDerivative(9.0, 1.8);
 }
 
+version(mir_test)
 unittest
 {
     import mir.math.common: approxEqual;

From 388d4f90c17e676cda0f768693fcd775c76321e4 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 27 Apr 2022 16:06:35 +0700
Subject: [PATCH 345/573] extend interpolation derivatives api

---
 source/mir/interpolate/linear.d | 39 +++++++---------
 source/mir/interpolate/spline.d | 82 +++++++++++++++++++++++++--------
 2 files changed, 78 insertions(+), 43 deletions(-)

diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index e205f12f..cda6cff2 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -386,7 +386,7 @@ struct LinearKernel(X)
 
     ///
     template opCall(uint derivative = 0)
-        if (derivative <= 1)
+        // if (derivative <= 1)
     {
         ///
         auto opCall(Y)(const Y y0, const Y y1)
@@ -509,37 +509,30 @@ struct MetaLinear(T, X)
         auto opCall(X...)(const X xs) scope const @trusted
             if (X.length == dimensionCount)
         {
-            static if (derivative > derivativeOrder)
+            static if (isInterval!(typeof(xs[0])))
             {
-                auto res = this.opCall!derivativeOrder(xs);
-                typeof(res[0])[derivative + 1] ret = 0;
-                ret[0 .. derivativeOrder + 1] = res;
-                return ret;
+                size_t index = xs[0][1];
+                auto x = xs[0][0];
             }
             else
-            {
-                static if (isInterval!(typeof(xs[0])))
-                {
-                    size_t index = xs[0][1];
-                    auto x = xs[0][0];
-                }
-                else
-                { 
-                    alias x = xs[0];
-                    size_t index = this.findInterval(x);
-                }
-                auto lhs = data[index + 0].opCall!derivative(xs[1 .. $]);
-                auto rhs = data[index + 1].opCall!derivative(xs[1 .. $]);
-                alias E = typeof(lhs);
-                alias F = DeepType!E;
-                auto kernel = LinearKernel!F(grid[index], grid[index + 1], x);
-                return kernel.opCall!derivative(lhs, rhs);
+            { 
+                alias x = xs[0];
+                size_t index = this.findInterval(x);
             }
+            auto lhs = data[index + 0].opCall!derivative(xs[1 .. $]);
+            auto rhs = data[index + 1].opCall!derivative(xs[1 .. $]);
+            alias E = typeof(lhs);
+            alias F = DeepType!E;
+            auto kernel = LinearKernel!F(grid[index], grid[index + 1], x);
+            return kernel.opCall!derivative(lhs, rhs);
         }
     }
 
     ///
     alias withDerivative = opCall!1;
+
+    ///
+    alias withTwoDerivatives = opCall!2;
 }
 
 /// 2D trapezoid-like (not rectilinear) linear interpolation
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index f2885a40..328f2541 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -19,7 +19,6 @@ import core.lifetime: move;
 import mir.functional;
 import mir.internal.utility;
 import mir.interpolate;
-import mir.interpolate: Repeat;
 import mir.math.common;
 import mir.ndslice.slice;
 import mir.primitives;
@@ -1618,7 +1617,7 @@ struct MetaSpline(T, X)
     // alias ElementInterpolator = Linear!(F, N, X);
     alias F = ValueType!(T, X);
     ///
-    private Spline!F spline;
+    private Repeat!(3, Spline!F) splines;
     ///
     RCArray!(const T) data;
     //
@@ -1649,7 +1648,9 @@ struct MetaSpline(T, X)
 
         this.data = data.move;
         this._temp = grid.length;
-        this.spline = grid.moveToSlice;
+        this.splines[0] = grid.asSlice;
+        this.splines[1] = grid.asSlice;
+        this.splines[2] = grid.moveToSlice;
         this.configuration = configuration;
     }
 
@@ -1668,7 +1669,7 @@ struct MetaSpline(T, X)
     immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
         if (dimension == 0)
     {
-        return spline.gridScopeView;
+        return splines[0].gridScopeView;
     }
 
     /++
@@ -1689,8 +1690,7 @@ struct MetaSpline(T, X)
 
     ///
     template opCall(uint derivative = 0)
-        // if (derivative <= derivativeOrder)
-        if (derivative <= 0) // doesn't support derivatives for now
+        if (derivative <= derivativeOrder)
     {
         /++
         `(x)` operator.
@@ -1700,25 +1700,61 @@ struct MetaSpline(T, X)
         auto opCall(X...)(const X xs) scope const @trusted
             if (X.length == dimensionCount)
         {
-            auto mutable = cast(F[2][]) spline._data.lightScope.field;
-            assert(mutable.length == data.length);
-            foreach (i, ref d; data)
-                mutable[i][0] = d(xs[1 .. $]);
-            (*cast(Spline!F*)&spline)._computeDerivativesTemp(
-                configuration.kind,
-                configuration.param,
-                configuration.leftBoundary,
-                configuration.rightBoundary,
-                (cast(F[])_temp[]).sliced);
-            return spline(xs[0]);
+            F[2][][derivative + 1] mutable;
+
+            static foreach (o; 0 .. derivative + 1)
+            {
+                mutable[o] = cast(F[2][]) splines[o]._data.lightScope.field;
+                assert(mutable[o].length == data.length);
+            }
+
+            static if (!derivative)
+            {
+                foreach (i, ref d; data)
+                    mutable[0][i][0] = d(xs[1 .. $]);
+            }
+            else
+            {
+                foreach (i, ref d; data)
+                {
+                    auto node = d.opCall!derivative(xs[1 .. $]);
+                    static foreach (o; 0 .. derivative + 1)
+                        mutable[o][i][0] = node[o];
+                }
+            }
+
+            static foreach (o; 0 .. derivative + 1)
+            {
+                (*cast(Spline!F*)&splines[o])._computeDerivativesTemp(
+                    configuration.kind,
+                    configuration.param,
+                    configuration.leftBoundary,
+                    configuration.rightBoundary,
+                    (cast(F[])_temp[]).sliced);
+            }
+
+            static if (!derivative)
+            {
+                return splines[0](xs[0]);
+            }
+            else
+            {
+                typeof(splines[0].opCall!derivative(xs[0]))[derivative + 1] ret = void;
+                static foreach (o; 0 .. derivative + 1)
+                    ret[o] = splines[o].opCall!derivative(xs[0]);
+                return ret;
+            }
         }
     }
 
-    // ///
-    // alias withDerivative = opCall!1;
+    ///
+    alias withDerivative = opCall!1;
+    ///
+    alias withTwoDerivatives = opCall!2;
 }
 
 /// 2D trapezoid-like (not rectilinear) linear interpolation
+version(mir_test)
 unittest
 {
     auto x = [
@@ -1745,9 +1781,15 @@ unittest
     auto trapezoidInterpolator = metaSpline!double(g.rcarray!(immutable double), d.lightConst);
 
     auto val = trapezoidInterpolator(9.0, 1.8);
-
+    auto ext = trapezoidInterpolator.opCall!2(9.0, 1.8);
+    assert(ext[0][0] == val);
+    assert(ext == [
+        [-0.6323361344537806, -2.2344649859943977, 1.362173669467787],
+        [3.6676610644257703, -2.984652194211018, 0.9911251167133525],
+        [2.4365546218487393, -1.556932773109244, 0.3836134453781514]]);
 }
 
+version(mir_test)
 unittest
 {
     import mir.math.common: approxEqual;

From 217b93d4a931f01db114ddb835894878f47ab20f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 27 Apr 2022 16:54:19 +0700
Subject: [PATCH 346/573] fix metaspline with derivatives

---
 source/mir/interpolate/spline.d | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 328f2541..2cfd4807 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -1741,7 +1741,12 @@ struct MetaSpline(T, X)
             {
                 typeof(splines[0].opCall!derivative(xs[0]))[derivative + 1] ret = void;
                 static foreach (o; 0 .. derivative + 1)
-                    ret[o] = splines[o].opCall!derivative(xs[0]);
+                {{
+                    auto s = splines[o].opCall!derivative(xs[0]);
+                    static foreach (r; 0 .. derivative + 1)
+                        ret[r][o] = s[r];
+
+                }}
                 return ret;
             }
         }
@@ -1781,12 +1786,6 @@ unittest
     auto trapezoidInterpolator = metaSpline!double(g.rcarray!(immutable double), d.lightConst);
 
     auto val = trapezoidInterpolator(9.0, 1.8);
-    auto ext = trapezoidInterpolator.opCall!2(9.0, 1.8);
-    assert(ext[0][0] == val);
-    assert(ext == [
-        [-0.6323361344537806, -2.2344649859943977, 1.362173669467787],
-        [3.6676610644257703, -2.984652194211018, 0.9911251167133525],
-        [2.4365546218487393, -1.556932773109244, 0.3836134453781514]]);
 }
 
 version(mir_test)
@@ -1829,4 +1828,12 @@ unittest
 
     ///// verify result ////
     assert(all!appreq(interp_data, real_data));
+
+    auto z0 = 1.23;
+    auto z1 = 3.21;
+    auto d = interpolant.withDerivative(z0, z1);
+    assert(appreq(d[0][0], f(z0, z1)));
+    assert(appreq(d[1][0], y_x0 + y_x0x1 * z1));
+    assert(appreq(d[0][1], y_x1 + y_x0x1 * z0));
+    assert(appreq(d[1][1], y_x0x1));
 }

From 315d5ab62a3d2e6ee4ef1f0c18c9a39008320739 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 3 May 2022 14:39:47 +0700
Subject: [PATCH 347/573] fix dub test configs

---
 dub.sdl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 15ca28d6..a21101a2 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.88"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
@@ -19,7 +19,7 @@ buildType "unittest-dip1008" {
 }
 buildType "unittest-dip1000" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" // "mir_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
     dflags "-lowmem" "-preview=dip1000"
 }
 buildType "unittest-cov" {

From b0285a7bd6a3480fd3d0461bd74979b950b30200 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 3 May 2022 16:11:27 +0700
Subject: [PATCH 348/573] rework map-like iterators

---
 dub.sdl                       |  2 +-
 source/mir/ndslice/iterator.d | 50 +++++++++++++++++------------------
 source/mir/ndslice/package.d  | 14 ++++++++++
 3 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index a21101a2..5d93f070 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzba
 copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.1.88"
+dependency "mir-core" version=">=1.1.106"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 57457d09..794b8c6c 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -860,14 +860,14 @@ struct CachedIterator(Iterator, CacheIterator, FlagIterator)
 
 private enum map_primitives = q{
 
-    import mir.functional: RefTuple, unref;
+    import mir.functional: RefTuple, autoExpandAndForward;
 
     auto ref opUnary(string op : "*")()
     {
         static if (is(typeof(*_iterator) : RefTuple!T, T...))
         {
             auto t = *_iterator;
-            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            return _fun(autoExpandAndForward!t);
         }
         else
             return _fun(*_iterator);
@@ -878,7 +878,7 @@ private enum map_primitives = q{
         static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
         {
             auto t = _iterator[index];
-            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            return _fun(autoExpandAndForward!t);
         }
         else
             return _fun(_iterator[index]);
@@ -891,7 +891,7 @@ private enum map_primitives = q{
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value");
+                return _fun(autoExpandAndForward!t) = value;
             }
             else
                 return _fun(_iterator[index]) = value;
@@ -902,7 +902,7 @@ private enum map_primitives = q{
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+                return _fun(autoExpandAndForward!t);
             }
             else
                 return mixin(op ~ "_fun(_iterator[index])");
@@ -913,7 +913,7 @@ private enum map_primitives = q{
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value");
+                return mixin("_fun(autoExpandAndForward!t)" ~ op ~ "= value");
             }
             else
                 return mixin("_fun(_iterator[index])" ~ op ~ "= value");
@@ -945,14 +945,14 @@ struct VmapIterator(Iterator, Fun)
         return VmapIterator!(LightImmutableOf!Iterator, LightImmutableOf!Fun)(.lightImmutable(_iterator), .lightImmutable(_fun));
     }
 
-    import mir.functional: RefTuple, unref;
+    import mir.functional: RefTuple, autoExpandAndForward;
 
     auto ref opUnary(string op : "*")()
     {
         static if (is(typeof(*_iterator) : RefTuple!T, T...))
         {
             auto t = *_iterator;
-            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            return _fun(autoExpandAndForward!t);
         }
         else
             return _fun(*_iterator);
@@ -963,7 +963,7 @@ struct VmapIterator(Iterator, Fun)
         static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
         {
             auto t = _iterator[index];
-            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            return _fun(autoExpandAndForward!t);
         }
         else
             return _fun(_iterator[index]);
@@ -976,7 +976,7 @@ struct VmapIterator(Iterator, Fun)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value");
+                return _fun(autoExpandAndForward!t) = value;
             }
             else
                 return _fun(_iterator[index]) = value;
@@ -987,7 +987,7 @@ struct VmapIterator(Iterator, Fun)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+                return mixin(op ~ "_fun(autoExpandAndForward!t)");
             }
             else
                 return mixin(op ~ "_fun(_iterator[index])");
@@ -998,7 +998,7 @@ struct VmapIterator(Iterator, Fun)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value");
+                return mixin("_fun(autoExpandAndForward!t)" ~ op ~ "= value");
             }
             else
                 return mixin("_fun(_iterator[index])" ~ op ~ "= value");
@@ -1041,18 +1041,18 @@ struct MapIterator(Iterator, alias _fun)
         return MapIterator!(LightImmutableOf!Iterator, _fun)(.lightImmutable(_iterator));
     }
 
-    import mir.functional: pipe;
+    import mir.functional: pipe, autoExpandAndForward;
     ///
     static alias __map(alias fun1) = MapIterator__map!(Iterator, _fun, pipe!(_fun, fun1));
 
-    import mir.functional: RefTuple, unref;
+    import mir.functional: RefTuple, autoExpandAndForward;
 
     auto ref opUnary(string op : "*")()
     {
         static if (is(typeof(*_iterator) : RefTuple!T, T...))
         {
             auto t = *_iterator;
-            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            return _fun(autoExpandAndForward!t);
         }
         else
             return _fun(*_iterator);
@@ -1063,7 +1063,7 @@ struct MapIterator(Iterator, alias _fun)
         static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
         {
             auto t = _iterator[index];
-            return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+            return _fun(autoExpandAndForward!t);
         }
         else
             return _fun(_iterator[index]);
@@ -1076,7 +1076,7 @@ struct MapIterator(Iterator, alias _fun)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value");
+                return _fun(autoExpandAndForward!t) = value;
             }
             else
                 return _fun(_iterator[index]) = value;
@@ -1087,7 +1087,7 @@ struct MapIterator(Iterator, alias _fun)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")");
+                return mixin(op ~ "_fun(autoExpandAndForward!t)");
             }
             else
                 return mixin(op ~ "_fun(_iterator[index])");
@@ -1098,7 +1098,7 @@ struct MapIterator(Iterator, alias _fun)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value");
+                return mixin("_fun(autoExpandAndForward!t)" ~ op ~ "= value");
             }
             else
                 return mixin("_fun(_iterator[index])" ~ op ~ "= value");
@@ -1516,7 +1516,7 @@ Iterates a field using an iterator.
 +/
 struct IndexIterator(Iterator, Field)
 {
-    import mir.functional: RefTuple, unref;
+    import mir.functional: RefTuple, autoExpandAndForward;
 
 @optmath:
     ///
@@ -1544,7 +1544,7 @@ struct IndexIterator(Iterator, Field)
         static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
         {
             auto t = *_iterator;
-            return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]");
+            return _field[autoExpandAndForward!t];
         }
         else
             return _field[*_iterator];
@@ -1555,7 +1555,7 @@ struct IndexIterator(Iterator, Field)
         static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
         {
             auto t = _iterator[index];
-            return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]");
+            return _field[autoExpandAndForward!t];
         }
         else
             return _field[_iterator[index]];
@@ -1568,7 +1568,7 @@ struct IndexIterator(Iterator, Field)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "] = value");
+                return _field[autoExpandAndForward!t] = value;
             }
             else
                 return _field[_iterator[index]] = value;
@@ -1579,7 +1579,7 @@ struct IndexIterator(Iterator, Field)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin(op ~ "_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]");
+                return mixin(op ~ "_field[autoExpandAndForward!t]");
             }
             else
                 return mixin(op ~ "_field[_iterator[index]]");
@@ -1590,7 +1590,7 @@ struct IndexIterator(Iterator, Field)
             static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
             {
                 auto t = _iterator[index];
-                return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]" ~ op ~ "= value");
+                return mixin("_field[autoExpandAndForward!t]" ~ op ~ "= value");
             }
             else
                 return mixin("_field[_iterator[index]]" ~ op ~ "= value");
diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index 9bf7fd63..01854cca 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -751,3 +751,17 @@ pure nothrow version(mir_ndslice_test) unittest
     assert(matrix[0, 2] == tensor[0, 1, 2]);
     assert(&matrix[0, 2] is &tensor[0, 1, 2]);
 }
+
+version(mir_ndslice_test)
+pure nothrow
+unittest
+{
+    auto x = iota(2, 3);
+    auto y = iota([2, 3], 1);
+    auto combine1 = x.zip(y).map!"b";
+    auto combine2 = x.zip(y).map!("b", "a * b").map!(a => a[0]);
+
+    assert(combine1[0, 0] == 1);
+    assert(combine2[0, 0] == 1);
+    static assert(is(typeof(combine2[0, 0]) == long));
+}

From 514dd5909c8a9c3df3508ee62b921d5c1a8a9e45 Mon Sep 17 00:00:00 2001
From: John Michael Hall <john.michael.hall@gmail.com>
Date: Tue, 3 May 2022 07:24:23 -0400
Subject: [PATCH 349/573] Update gitignore

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 4c9b2661..9bc5b5ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,4 @@ _build_dir_
 .VSCodeCounter
 builddir/
 mir-algorithm-test-secure
+*.pdb

From ac6533c509d21e1a7ee2cb3b30e0181d982384a2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 3 May 2022 22:24:45 +0700
Subject: [PATCH 350/573] fixup test

---
 source/mir/ndslice/package.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index 01854cca..347819c1 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -763,5 +763,5 @@ unittest
 
     assert(combine1[0, 0] == 1);
     assert(combine2[0, 0] == 1);
-    static assert(is(typeof(combine2[0, 0]) == long));
+    static assert(is(typeof(combine2[0, 0]) == sizediff_t));
 }

From ce427ddccb28bbc029f19ee9aa7c60b19e07c457 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 7 May 2022 01:32:00 +0700
Subject: [PATCH 351/573] fix normalCDF

---
 dub.sdl                       |  2 +-
 source/mir/math/func/normal.d | 11 ++++++++++-
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 5d93f070..ed502ed1 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.106"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
+    // versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/math/func/normal.d b/source/mir/math/func/normal.d
index bc9e566e..2d17b982 100644
--- a/source/mir/math/func/normal.d
+++ b/source/mir/math/func/normal.d
@@ -99,6 +99,8 @@ T normalCDF(T)(const T a)
         /* Multiply by exp(-x^2 / 2)  */
         z = expx2(a, -1);
         y = y * sqrt(z);
+        if (y != y)
+            y = 0;
         if (x > 0)
             y = 1 - y;
         return y;
@@ -113,9 +115,16 @@ T normalCDF(T)(const T x, const T mean, const T stdDev)
 }
 
 version(mir_test)
-@safe unittest
+@safe
+unittest
 {
     assert(fabs(normalCDF(1.0L) - (0.841344746068543))< 0.0000000000000005);
+    assert(normalCDF(double.infinity) == 1);
+    assert(normalCDF(100.0) == 1);
+    assert(normalCDF(8.0) < 1);
+    assert(normalCDF(-double.infinity) == 0);
+    assert(normalCDF(-100.0) == 0);
+    assert(normalCDF(-8.0) > 0);
 }
 
 ///

From f80fdf36f7cc97db3c953cc999f59fb3eca128bf Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 10 May 2022 07:02:00 -0400
Subject: [PATCH 352/573] Add documentation comments for public imports (#403)

---
 source/mir/algebraic_alias/ion.d    | 4 ++++
 source/mir/algebraic_alias/json.d   | 1 +
 source/mir/bignum/decimal.d         | 1 +
 source/mir/interpolate/constant.d   | 2 ++
 source/mir/interpolate/generic.d    | 2 ++
 source/mir/interpolate/linear.d     | 2 ++
 source/mir/interpolate/polynomial.d | 1 +
 source/mir/interpolate/spline.d     | 2 ++
 source/mir/math/stat.d              | 2 ++
 source/mir/ndslice/internal.d       | 1 +
 source/mir/ndslice/slice.d          | 1 +
 source/mir/rc/package.d             | 3 +++
 source/mir/series.d                 | 2 ++
 13 files changed, 24 insertions(+)

diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
index b66b6f42..a13b66b1 100644
--- a/source/mir/algebraic_alias/ion.d
+++ b/source/mir/algebraic_alias/ion.d
@@ -12,9 +12,13 @@ Macros:
 module mir.algebraic_alias.ion;
 
 import mir.algebraic: TaggedVariant, This;
+///
 public import mir.annotated: Annotated;
+///
 public import mir.lob: Clob, Blob;
+///
 public import mir.string_map: StringMap;
+///
 public import mir.timestamp: Timestamp;
 
 
diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index 4925f4d4..ab456d45 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -12,6 +12,7 @@ Macros:
 module mir.algebraic_alias.json;
 
 import mir.algebraic: TaggedVariant, This;
+///
 public import mir.string_map: StringMap;
 
 /++
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 84ec95ad..22c15522 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -8,6 +8,7 @@ module mir.bignum.decimal;
 
 import mir.serde: serdeProxy, serdeScoped;
 import std.traits: isSomeChar;
+///
 public import mir.bignum.low_level_view: DecimalExponentKey;
 import mir.bignum.low_level_view: ceilLog10Exp2;
 
diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 56180653..e355f34e 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -49,6 +49,8 @@ import mir.rc.array;
 import mir.utility: min, max;
 import std.meta: AliasSeq, staticMap;
 import std.traits;
+
+///
 public import mir.interpolate: atInterval;
 
 /++
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index 3fc2f503..93e03f4b 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -71,6 +71,8 @@ import mir.rc.array;
 import mir.utility: min, max;
 import std.meta: AliasSeq, staticMap;
 import std.traits;
+
+///
 public import mir.interpolate: atInterval;
 
 /++
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index cda6cff2..71472228 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -24,6 +24,8 @@ import mir.rc.array;
 import mir.utility: min, max;
 import std.meta: AliasSeq, staticMap;
 import std.traits;
+
+///
 public import mir.interpolate: atInterval;
 
 enum  msg_min =  "linear interpolant: minimal allowed length for the grid equals 2.";
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index d05fe13d..46297bb8 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -13,6 +13,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 +/
 module mir.interpolate.polynomial;
 
+///
 public import mir.interpolate: atInterval;
 import core.lifetime: move;
 import mir.functional: RefTuple;
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 2cfd4807..4aafec7a 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -26,6 +26,8 @@ import mir.rc.array;
 import mir.utility: min, max;
 import std.meta: AliasSeq, staticMap;
 import std.traits: Unqual;
+
+///
 public import mir.interpolate: atInterval;
 
 static immutable msg_min =  "spline interpolant: minimal allowed length for the grid equals 2.";
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index f5e7f1bf..3617d199 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -25,6 +25,8 @@ import mir.math.sum;
 import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
 import mir.primitives;
 import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType;
+
+///
 public import mir.math.sum: Summation;
 
 ///
diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index 93d001fa..2e58ecd2 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -18,6 +18,7 @@ template ConstIfPointer(T)
         alias ConstIfPointer = T;
 }
 
+///
 public import mir.utility: _expect;
 
 struct RightOp(string op, T)
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index adf6436a..7ef935a1 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -45,6 +45,7 @@ import mir.utility;
 import std.meta;
 import std.traits;
 
+///
 public import mir.primitives: DeepElementType;
 
 /++
diff --git a/source/mir/rc/package.d b/source/mir/rc/package.d
index 85a0eed7..7a26a36c 100644
--- a/source/mir/rc/package.d
+++ b/source/mir/rc/package.d
@@ -20,8 +20,11 @@ See_also: $(MREF mir,ndslice).
 +/
 module mir.rc;
 
+///
 public import mir.rc.array;
+///
 public import mir.rc.ptr;
+///
 public import mir.rc.slim_ptr;
 
 import mir.ndslice.slice;
diff --git a/source/mir/series.d b/source/mir/series.d
index 236b8652..d68b56ab 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -20,7 +20,9 @@ import mir.ndslice.sorting: transitionIndex;
 import mir.qualifier;
 import mir.serde: serdeIgnore, serdeFields;
 import std.traits;
+///
 public import mir.ndslice.slice;
+///
 public import mir.ndslice.sorting: sort;
 
 /++

From cdf3de4fadb70b991ddf606cbfe30d970d626b83 Mon Sep 17 00:00:00 2001
From: Iain Buclaw <ibuclaw@gdcproject.org>
Date: Wed, 11 May 2022 15:16:56 +0200
Subject: [PATCH 353/573] Fix DIP1000 related deprecation warnings and errors
 in v2.100.0-rc.1 (#404)

---
 source/mir/algorithm/iteration.d    |   6 +-
 source/mir/appender.d               |   2 +-
 source/mir/bignum/low_level_view.d  |  30 +++----
 source/mir/container/binaryheap.d   |   2 +-
 source/mir/interpolate/constant.d   |   4 +-
 source/mir/interpolate/generic.d    |   4 +-
 source/mir/interpolate/linear.d     |   6 +-
 source/mir/interpolate/polynomial.d |   2 +-
 source/mir/interpolate/spline.d     |   6 +-
 source/mir/ndslice/iterator.d       |   2 +-
 source/mir/ndslice/slice.d          | 116 ++++++++++++++--------------
 source/mir/rc/array.d               |  12 +--
 source/mir/rc/context.d             |   6 +-
 source/mir/rc/ptr.d                 |   4 +-
 source/mir/rc/slim_ptr.d            |   4 +-
 source/mir/small_array.d            |   2 +-
 source/mir/small_string.d           |   6 +-
 17 files changed, 107 insertions(+), 107 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index d8159ddf..1a2186f4 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -3871,7 +3871,7 @@ struct Uniq(alias pred, Range)
             while (!_input.empty && pred(last, _input.back));
         }
 
-        auto ref back() scope return @property
+        auto ref back() return scope @property
         {
             assert(!empty, "Attempting to fetch the back of an empty uniq.");
             return _input.back;
@@ -3891,7 +3891,7 @@ struct Uniq(alias pred, Range)
 
     static if (isForwardRange!Range)
     {
-        @property typeof(this) save() scope return
+        @property typeof(this) save() return scope
         {
             return typeof(this)(_input.save);
         }
@@ -4028,7 +4028,7 @@ struct Filter(alias pred, Range)
     import std.range.primitives: isForwardRange;
     static if (isForwardRange!Range)
     {
-        @property typeof(this) save() scope return
+        @property typeof(this) save() return scope
         {
             return typeof(this)(_input.save);
         }
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 7f8299d8..335f099d 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -214,7 +214,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     }
 
     ///
-    inout(T)[] data() inout @property @safe scope
+    inout(T)[] data() inout @property @safe scope return
     {
         return _buffer.length ? _buffer[0 .. _currentLength] : _scopeBuffer[0 .. _currentLength];
     }
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index ef838f69..a29f33ce 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -414,7 +414,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     static if (endian == TargetEndian)
     ///
     @trusted pure nothrow @nogc
-    BigUIntView!V opCast(T : BigUIntView!V, V)() scope return
+    BigUIntView!V opCast(T : BigUIntView!V, V)() return scope
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(cast(V[])this.coefficients);
@@ -422,7 +422,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     BigUIntView!(const W, endian) lightConst()()
-        const @safe pure nothrow @nogc @property scope return
+        const @safe pure nothrow @nogc @property return scope
     {
         return typeof(return)(coefficients);
     }
@@ -451,7 +451,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    ref inout(W) mostSignificant() inout @property scope return
+    ref inout(W) mostSignificant() inout @property return scope
     {
         static if (endian == WordEndian.big)
             return coefficients[0];
@@ -461,7 +461,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    ref inout(W) leastSignificant() inout @property scope return
+    ref inout(W) leastSignificant() inout @property return scope
     {
         static if (endian == WordEndian.little)
             return coefficients[0];
@@ -491,7 +491,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigUIntView topMostSignificantPart(size_t length) scope return
+    BigUIntView topMostSignificantPart(size_t length) return scope
     {
         static if (endian == WordEndian.big)
             return BigUIntView(coefficients[0 .. length]);
@@ -501,7 +501,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigUIntView topLeastSignificantPart(size_t length) scope return
+    BigUIntView topLeastSignificantPart(size_t length) return scope
     {
         static if (endian == WordEndian.little)
             return BigUIntView(coefficients[0 .. length]);
@@ -1128,7 +1128,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Returns: the same intger view with inversed sign
     +/
-    BigIntView!(W, endian) opUnary(string op : "-")() scope return
+    BigIntView!(W, endian) opUnary(string op : "-")() return scope
     {
         return typeof(return)(this, true);
     }
@@ -1160,7 +1160,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: a slice of coefficients starting from the least significant.
     +/
     auto leastSignificantFirst()
-        @safe pure nothrow @nogc @property scope return
+        @safe pure nothrow @nogc @property return scope
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.little)
@@ -1176,7 +1176,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     auto leastSignificantFirst()
-        const @safe pure nothrow @nogc @property scope return
+        const @safe pure nothrow @nogc @property return scope
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.little)
@@ -1194,7 +1194,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: a slice of coefficients starting from the most significant.
     +/
     auto mostSignificantFirst()
-        @safe pure nothrow @nogc @property scope return
+        @safe pure nothrow @nogc @property return scope
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.big)
@@ -1210,7 +1210,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     auto mostSignificantFirst()
-        const @safe pure nothrow @nogc @property scope return
+        const @safe pure nothrow @nogc @property return scope
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.big)
@@ -1227,7 +1227,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Strips most significant zero coefficients.
     +/
-    BigUIntView normalized() scope return
+    BigUIntView normalized() return scope
     {
         auto number = this;
         if (number.coefficients.length) do
@@ -1549,7 +1549,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     bool sign;
 
     ///
-    inout(W)[] coefficients() inout @property scope return
+    inout(W)[] coefficients() inout @property return scope
     {
         return unsigned.coefficients;
     }
@@ -1912,14 +1912,14 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     static if (endian == TargetEndian)
     ///
-    BigIntView!V opCast(T : BigIntView!V, V)() scope return
+    BigIntView!V opCast(T : BigIntView!V, V)() return scope
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
     }
 
     ///
-    BigIntView!(const W, endian) lightConst()() scope return
+    BigIntView!(const W, endian) lightConst()() return scope
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(unsigned.lightConst, sign);
diff --git a/source/mir/container/binaryheap.d b/source/mir/container/binaryheap.d
index 880d4fb7..81d866ac 100644
--- a/source/mir/container/binaryheap.d
+++ b/source/mir/container/binaryheap.d
@@ -180,7 +180,7 @@ public:
     Returns a _front of the heap, which is the largest element
     according to `less`.
     +/
-    @property auto ref ElementType!Store front() scope return
+    @property auto ref ElementType!Store front() return scope
     {
         assert(!empty, "Cannot call front on an empty heap.");
         return _store.front;
diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index e355f34e..29f5e632 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -130,14 +130,14 @@ extern(D):
     Constant lightConst()() const @property { return *cast(Constant*)&this; }
 
     ///
-    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property
+    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property
         if (dimension < N)
     {
         return _grid[dimension].lightConst.sliced(_data._lengths[dimension]);
     }
 
     ///
-    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
         if (dimension < N)
     {
         return _grid[dimension]._iterator[0 .. _data._lengths[dimension]];
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index 93e03f4b..1d1c1703 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -149,14 +149,14 @@ extern(D):
     Generic lightConst()() const @property { return *cast(Generic*)&this; }
 
     ///
-    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property
+    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property
         if (dimension == 0)
     {
         return _grid.lightConst.sliced(_data._lengths[0]);
     }
 
     ///
-    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
         if (dimension == 0)
     {
         return _grid._iterator[0 .. _data._lengths[0]];
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 71472228..c9f4e081 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -236,14 +236,14 @@ struct Linear(F, size_t N = 1, X = F)
     Linear lightConst()() const @property { return *cast(Linear*)&this; }
 
     ///
-    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property
+    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property
         if (dimension < N)
     {
         return _grid[dimension].lightConst.sliced(_data._lengths[dimension]);
     }
 
     ///
-    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
         if (dimension < N)
     {
         return _grid[dimension]._iterator[0 .. _data._lengths[dimension]];
@@ -478,7 +478,7 @@ struct MetaLinear(T, X)
     MetaLinear lightConst()() const @property { return *cast(MetaLinear*)&this; }
 
     ///
-    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
         if (dimension == 0)
     {
         return grid[];
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index 46297bb8..aa7753f8 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -161,7 +161,7 @@ scope const:
         ///
         ref const(Slice!(RCI!(immutable X))) grid() { return _grid; }
         ///
-        immutable(X)[] gridScopeView() scope return const @property @trusted { return _grid.lightScope.field; }
+        immutable(X)[] gridScopeView() return scope const @property @trusted { return _grid.lightScope.field; }
         ///
         ref const(RCArray!(immutable T)) inversedBarycentricWeights() { return _inversedBarycentricWeights; }
         ///
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 4aafec7a..646359e2 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -846,14 +846,14 @@ struct Spline(F, size_t N = 1, X = F)
     Spline lightImmutable() immutable @property { return *cast(Spline*)&this; }
 
     ///
-    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property
+    Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() return scope const @property
         if (dimension < N)
     {
         return _grid[dimension].lightConst.sliced(_data._lengths[dimension]);
     }
 
     ///
-    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
         if (dimension < N)
     {
         return _grid[dimension]._iterator[0 .. _data._lengths[dimension]];
@@ -1668,7 +1668,7 @@ struct MetaSpline(T, X)
     MetaLinear lightConst()() const @property { return *cast(MetaLinear*)&this; }
 
     ///
-    immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
         if (dimension == 0)
     {
         return splines[0].gridScopeView;
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 794b8c6c..eb5732f2 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -1958,7 +1958,7 @@ struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
 
     static if (isMutable!(_slice.DeepElement) && !_slice.hasAccessByRef)
     ///
-    auto ref opIndexAssign(E)(scope ref E elem, size_t index) scope return
+    auto ref opIndexAssign(E)(scope ref E elem, size_t index) return scope
     {
         return _slice._iterator[getShift(index)] = elem;
     }
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 7ef935a1..bf2c5440 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -945,7 +945,7 @@ public:
     Returns: View with stripped out reference counted context.
     The lifetime of the result mustn't be longer then the lifetime of the original slice.
     +/
-    auto lightScope()() scope return @property
+    auto lightScope()() return scope @property
     {
         auto ret = Slice!(LightScopeOf!Iterator, N, kind, staticMap!(LightScopeOf, Labels))
             (_structure, .lightScope(_iterator));
@@ -955,7 +955,7 @@ public:
     }
 
     /// ditto
-    auto lightScope()() scope const return @property
+    auto lightScope()() return const scope @property
     {
         auto ret = Slice!(LightConstOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightConstOfLightScopeOf, Labels))
             (_structure, .lightScope(_iterator));
@@ -965,7 +965,7 @@ public:
     }
 
     /// ditto
-    auto lightScope()() scope immutable return @property
+    auto lightScope()() return immutable scope @property
     {
         auto ret =  Slice!(LightImmutableOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightImmutableOfLightConstOf(Labels)))
             (_structure, .lightScope(_iterator));
@@ -975,7 +975,7 @@ public:
     }
 
     /// Returns: Mutable slice over immutable data.
-    Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightImmutable()() scope return immutable @property
+    Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightImmutable()() return scope immutable @property
     {
         auto ret = typeof(return)(_structure, .lightImmutable(_iterator));
         foreach(i; Iota!L)
@@ -984,7 +984,7 @@ public:
     }
 
     /// Returns: Mutable slice over const data.
-    Slice!(LightConstOf!Iterator, N, kind, staticMap!(LightConstOf, Labels)) lightConst()() scope return const @property @trusted
+    Slice!(LightConstOf!Iterator, N, kind, staticMap!(LightConstOf, Labels)) lightConst()() return scope const @property @trusted
     {
         auto ret = typeof(return)(_structure, .lightConst(_iterator));
         foreach(i; Iota!L)
@@ -993,7 +993,7 @@ public:
     }
 
     /// ditto
-    Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightConst()() scope return immutable @property
+    Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightConst()() return scope immutable @property
     {
         return this.lightImmutable;
     }
@@ -1070,14 +1070,14 @@ public:
         /++
         Cast to const and immutable slices in case of underlying range is a pointer.
         +/
-        auto toImmutable()() scope return immutable @trusted pure nothrow @nogc
+        auto toImmutable()() return scope immutable @trusted pure nothrow @nogc
         {
             return Slice!(ImmutableOfUnqualOfPointerTarget!Iterator, N, kind, staticMap!(ImmutableOfUnqualOfPointerTarget, Labels))
                 (_structure, _iterator, _labels);
         }
 
         /// ditto
-        auto toConst()() scope return const @trusted pure nothrow @nogc
+        auto toConst()() return scope const @trusted pure nothrow @nogc
         {
             version(LDC) pragma(inline, true);
             return Slice!(ConstOfUnqualOfPointerTarget!Iterator, N, kind, staticMap!(ConstOfUnqualOfPointerTarget, Labels))
@@ -1162,7 +1162,7 @@ public:
     Returns:
         Iterator (pointer) to the $(LREF .Slice.first) element.
     +/
-    auto iterator()() inout scope return @property
+    auto iterator()() inout return scope @property
     {
         return _iterator;
     }
@@ -1176,7 +1176,7 @@ public:
     {
         import mir.rc.array: mir_rci;
         static if (kind == Contiguous && is(Iterator : mir_rci!ET, ET))
-        auto ptr() scope return inout @property
+        auto ptr() return scope inout @property
         {
             return _iterator._iterator;
         }
@@ -1189,7 +1189,7 @@ public:
     Constraints:
         Field is defined only for contiguous slices.
     +/
-    auto field()() scope return @trusted @property
+    auto field()() return scope @trusted @property
     {
         static assert(kind == Contiguous, "Slice.field is defined only for contiguous slices. Slice kind is " ~ kind.stringof);
         static if (is(typeof(_iterator[size_t(0) .. elementCount])))
@@ -1204,13 +1204,13 @@ public:
     }
 
     /// ditto
-    auto field()() scope const return @trusted @property
+    auto field()() return const scope @trusted @property
     {
         return this.lightConst.field;
     }
 
     /// ditto
-    auto field()() scope immutable return @trusted @property
+    auto field()() return immutable scope @trusted @property
     {
         return this.lightImmutable.field;
     }
@@ -1482,7 +1482,7 @@ public:
     static if (N == 1)
     {
         ///ditto
-        auto ref front(size_t dimension = 0)() scope return @trusted @property
+        auto ref front(size_t dimension = 0)() return scope @trusted @property
             if (dimension == 0)
         {
             assert(!empty!dimension);
@@ -1490,7 +1490,7 @@ public:
         }
 
         ///ditto
-        auto ref front(size_t dimension = 0)() scope return @trusted @property const
+        auto ref front(size_t dimension = 0)() return scope @trusted @property const
             if (dimension == 0)
         {
             assert(!empty!dimension);
@@ -1498,7 +1498,7 @@ public:
         }
 
         ///ditto
-        auto ref front(size_t dimension = 0)() scope return @trusted @property immutable
+        auto ref front(size_t dimension = 0)() return scope @trusted @property immutable
             if (dimension == 0)
         {
             assert(!empty!dimension);
@@ -1508,7 +1508,7 @@ public:
     else
     {
         /// ditto
-        Element!dimension front(size_t dimension = 0)() scope return @property
+        Element!dimension front(size_t dimension = 0)() return scope @property
             if (dimension < N)
         {
             typeof(return)._Structure structure_ = typeof(return)._Structure.init;
@@ -1534,7 +1534,7 @@ public:
         }
 
         ///ditto
-        auto front(size_t dimension = 0)() scope return @trusted @property const
+        auto front(size_t dimension = 0)() return scope @trusted @property const
             if (dimension < N)
         {
             assert(!empty!dimension);
@@ -1542,7 +1542,7 @@ public:
         }
 
         ///ditto
-        auto front(size_t dimension = 0)() scope return @trusted @property immutable
+        auto front(size_t dimension = 0)() return scope @trusted @property immutable
             if (dimension < N)
         {
             assert(!empty!dimension);
@@ -1553,7 +1553,7 @@ public:
     static if (N == 1 && isMutable!DeepElement && !hasAccessByRef)
     {
         ///ditto
-        auto ref front(size_t dimension = 0, T)(T value) scope return @trusted @property
+        auto ref front(size_t dimension = 0, T)(T value) return scope @trusted @property
             if (dimension == 0)
         {
             // check assign safety
@@ -1572,7 +1572,7 @@ public:
     ///ditto
     static if (N == 1)
     auto ref Element!dimension
-    back(size_t dimension = 0)() scope return @trusted @property
+    back(size_t dimension = 0)() return scope @trusted @property
         if (dimension < N)
     {
         assert(!empty!dimension);
@@ -1580,7 +1580,7 @@ public:
     }
     else
     auto ref Element!dimension
-    back(size_t dimension = 0)() scope return @trusted @property
+    back(size_t dimension = 0)() return scope @trusted @property
         if (dimension < N)
     {
         assert(!empty!dimension);
@@ -1609,7 +1609,7 @@ public:
     static if (N == 1 && isMutable!DeepElement && !hasAccessByRef)
     {
         ///ditto
-        auto ref back(size_t dimension = 0, T)(T value) scope return @trusted @property
+        auto ref back(size_t dimension = 0, T)(T value) return scope @trusted @property
             if (dimension == 0)
         {
             // check assign safety
@@ -1734,7 +1734,7 @@ public:
     static if (N > 1)
     {
         /// Accesses the first deep element of the slice.
-        auto ref first()() scope return @trusted @property
+        auto ref first()() return scope @trusted @property
         {
             assert(!anyEmpty);
             return *_iterator;
@@ -1742,7 +1742,7 @@ public:
 
         static if (isMutable!DeepElement && !hasAccessByRef)
         ///ditto
-        auto ref first(T)(T value) scope return @trusted @property
+        auto ref first(T)(T value) return scope @trusted @property
         {
             assert(!anyEmpty);
             static if (__traits(compiles, *_iterator = value))
@@ -1761,7 +1761,7 @@ public:
         }
 
         /// Accesses the last deep element of the slice.
-        auto ref last()() @trusted scope return @property
+        auto ref last()() @trusted return scope @property
         {
             assert(!anyEmpty);
             return _iterator[lastIndex];
@@ -1769,7 +1769,7 @@ public:
 
         static if (isMutable!DeepElement && !hasAccessByRef)
         ///ditto
-        auto ref last(T)(T value) @trusted scope return @property
+        auto ref last(T)(T value) @trusted return scope @property
         {
             assert(!anyEmpty);
             return _iterator[lastIndex] = value;
@@ -1869,7 +1869,7 @@ public:
 
     Returns: `this[$-index[0], $-index[1], ..., $-index[N-1]]`
     +/
-    auto ref backward()(size_t[N] index) scope return
+    auto ref backward()(size_t[N] index) return scope
     {
         foreach (i; Iota!N)
             index[i] = _lengths[i] - index[i];
@@ -1877,13 +1877,13 @@ public:
     }
 
     /// ditto
-    auto ref backward()(size_t[N] index) scope return const
+    auto ref backward()(size_t[N] index) return scope const
     {
         return this.lightConst.backward(index);
     }
 
     /// ditto
-    auto ref backward()(size_t[N] index) scope return const
+    auto ref backward()(size_t[N] index) return scope const
     {
         return this.lightConst.backward(index);
     }
@@ -1970,7 +1970,7 @@ public:
         n = count of elements for the dimension
     Returns: ndslice with `length!dimension` equal to `n`.
     +/
-    auto selectFront(size_t dimension)(size_t n) scope return
+    auto selectFront(size_t dimension)(size_t n) return scope
     {
         static if (kind == Contiguous && dimension)
         {
@@ -2002,7 +2002,7 @@ public:
         n = count of elements for the dimension
     Returns: ndslice with `length!dimension` equal to `n`.
     +/
-    auto selectBack(size_t dimension)(size_t n) scope return
+    auto selectBack(size_t dimension)(size_t n) return scope
     {
         static if (kind == Contiguous && dimension)
         {
@@ -2129,13 +2129,13 @@ public:
     /++
     $(BOLD Fully defined index)
     +/
-    auto ref opIndex()(size_t[N] _indices...) scope return @trusted
+    auto ref opIndex()(size_t[N] _indices...) return scope @trusted
     {
         return _iterator[indexStride(_indices)];
     }
 
     /// ditto
-    auto ref opIndex()(size_t[N] _indices...) scope return const @trusted
+    auto ref opIndex()(size_t[N] _indices...) return scope const @trusted
     {
         static if (is(typeof(_iterator[indexStride(_indices)])))
             return _iterator[indexStride(_indices)];
@@ -2144,7 +2144,7 @@ public:
     }
 
     /// ditto
-    auto ref opIndex()(size_t[N] _indices...) scope return immutable @trusted
+    auto ref opIndex()(size_t[N] _indices...) return scope immutable @trusted
     {
         static if (is(typeof(_iterator[indexStride(_indices)])))
             return _iterator[indexStride(_indices)];
@@ -2155,7 +2155,7 @@ public:
     /++
     $(BOLD Partially defined index)
     +/
-    auto opIndex(size_t I)(size_t[I] _indices...) scope return @trusted
+    auto opIndex(size_t I)(size_t[I] _indices...) return scope @trusted
         if (I && I < N)
     {
         enum size_t diff = N - I;
@@ -2167,14 +2167,14 @@ public:
     }
 
     /// ditto
-    auto opIndex(size_t I)(size_t[I] _indices...) scope return const
+    auto opIndex(size_t I)(size_t[I] _indices...) return scope const
         if (I && I < N)
     {
         return this.lightConst.opIndex(_indices);
     }
 
     /// ditto
-    auto opIndex(size_t I)(size_t[I] _indices...) scope return immutable
+    auto opIndex(size_t I)(size_t[I] _indices...) return scope immutable
         if (I && I < N)
     {
         return this.lightImmutable.opIndex(_indices);
@@ -2183,7 +2183,7 @@ public:
     /++
     $(BOLD Partially or fully defined slice.)
     +/
-    auto opIndex(Slices...)(Slices slices) scope return @trusted
+    auto opIndex(Slices...)(Slices slices) return scope @trusted
         if (isPureSlice!Slices)
     {
         static if (Slices.length)
@@ -2295,7 +2295,7 @@ public:
     /++
     $(BOLD Indexed slice.)
     +/
-    auto opIndex(Slices...)(scope return Slices slices) scope return
+    auto opIndex(Slices...)(return scope Slices slices) return scope
         if (isIndexedSlice!Slices)
     {
         import mir.ndslice.topology: indexed, cartesian;
@@ -2376,7 +2376,7 @@ public:
     Note:
         Does not allocate neither new slice nor a closure.
     +/
-    auto opUnary(string op)() scope return
+    auto opUnary(string op)() return scope
         if (op == "*" || op == "~" || op == "-" || op == "+")
     {
         import mir.ndslice.topology: map;
@@ -2412,7 +2412,7 @@ public:
     Note:
         Does not allocate neither new slice nor a closure.
     +/
-    auto opBinary(string op, T)(scope return T value) scope return
+    auto opBinary(string op, T)(scope return T value) return scope
         if(!isSlice!T)
     {
         import mir.ndslice.topology: vmap;
@@ -2420,7 +2420,7 @@ public:
     }
 
     /// ditto
-    auto opBinaryRight(string op, T)(scope return T value) scope return
+    auto opBinaryRight(string op, T)(scope return T value) return scope
         if(!isSlice!T)
     {
         import mir.ndslice.topology: vmap;
@@ -2465,7 +2465,7 @@ public:
         Does not allocate neither new slice nor a closure.
     +/
     auto opBinary(string op, RIterator, size_t RN, SliceKind rkind)
-        (scope return Slice!(RIterator, RN, rkind) rhs) scope return
+        (scope return Slice!(RIterator, RN, rkind) rhs) return scope
         if(N == RN && (kind == Contiguous && rkind == Contiguous || N == 1) && op != "~")
     {
         import mir.ndslice.topology: zip, map;
@@ -2641,7 +2641,7 @@ public:
         value of flattened slice at `index`
     See_also: $(SUBREF topology, flattened)
     +/
-    auto ref accessFlat(size_t index) scope return @trusted
+    auto ref accessFlat(size_t index) return scope @trusted
     {
         return _iterator[indexStrideValue(index)];
     }
@@ -2716,7 +2716,7 @@ public:
         Assignment of a value of `Slice` type to a $(B fully defined slice).
         +/
         void opIndexAssign(RIterator, size_t RN, SliceKind rkind, Slices...)
-            (Slice!(RIterator, RN, rkind) value, Slices slices) scope return
+            (Slice!(RIterator, RN, rkind) value, Slices slices) return scope
             if (isFullPureSlice!Slices || isIndexedSlice!Slices)
         {
             auto sl = this.lightScope.opIndex(slices);
@@ -2840,7 +2840,7 @@ public:
         /++
         Assignment of a regular multidimensional array to a $(B fully defined slice).
         +/
-        void opIndexAssign(T, Slices...)(T[] value, Slices slices) scope return
+        void opIndexAssign(T, Slices...)(T[] value, Slices slices) return scope
             if ((isFullPureSlice!Slices || isIndexedSlice!Slices)
                 && (!isDynamicArray!DeepElement || isDynamicArray!T)
                 && DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= typeof(this.opIndex(slices)).N)
@@ -2922,7 +2922,7 @@ public:
         }
 
         ///
-        void opIndexAssign(T, Slices...)(T concatenation, Slices slices) scope return
+        void opIndexAssign(T, Slices...)(T concatenation, Slices slices) return scope
             if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T)
         {
             auto sl = this.lightScope.opIndex(slices);
@@ -2997,7 +2997,7 @@ public:
         /++
         Assignment of a value (e.g. a number) to a $(B fully defined index).
         +/
-        auto ref opIndexAssign(T)(T value, size_t[N] _indices...) scope return @trusted
+        auto ref opIndexAssign(T)(T value, size_t[N] _indices...) return scope @trusted
         {
             // check assign safety
             static auto ref fun(ref DeepElement t, ref T v) @safe
@@ -3007,7 +3007,7 @@ public:
             return _iterator[indexStride(_indices)] = value;
         }
         ///ditto
-        auto ref opIndexAssign()(DeepElement value, size_t[N] _indices...) scope return @trusted
+        auto ref opIndexAssign()(DeepElement value, size_t[N] _indices...) return scope @trusted
         {
             import mir.functional: forward;
             // check assign safety
@@ -3041,7 +3041,7 @@ public:
         /++
         Op Assignment `op=` of a value (e.g. a number) to a $(B fully defined index).
         +/
-        auto ref opIndexOpAssign(string op, T)(T value, size_t[N] _indices...) scope return @trusted
+        auto ref opIndexOpAssign(string op, T)(T value, size_t[N] _indices...) return scope @trusted
         {
             // check op safety
             static auto ref fun(ref DeepElement t, ref T v) @safe
@@ -3082,7 +3082,7 @@ public:
         Op Assignment `op=` of a value of `Slice` type to a $(B fully defined slice).
         +/
         void opIndexOpAssign(string op, RIterator, SliceKind rkind, size_t RN, Slices...)
-            (Slice!(RIterator, RN, rkind) value, Slices slices) scope return
+            (Slice!(RIterator, RN, rkind) value, Slices slices) return scope
             if (isFullPureSlice!Slices || isIndexedSlice!Slices)
         {
             auto sl = this.lightScope.opIndex(slices);
@@ -3147,7 +3147,7 @@ public:
         /++
         Op Assignment `op=` of a regular multidimensional array to a $(B fully defined slice).
         +/
-        void opIndexOpAssign(string op, T, Slices...)(T[] value, Slices slices) scope return
+        void opIndexOpAssign(string op, T, Slices...)(T[] value, Slices slices) return scope
             if (isFullPureSlice!Slices
                 && (!isDynamicArray!DeepElement || isDynamicArray!T)
                 && DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= typeof(this.opIndex(slices)).N)
@@ -3193,7 +3193,7 @@ public:
                      [2, 2, 3, 3]]);
         }
 
-        private void opIndexOpAssignImplValue(string op, T)(T value) scope return
+        private void opIndexOpAssignImplValue(string op, T)(T value) return scope
         {
             static if (N > 1 && kind == Contiguous)
             {
@@ -3223,7 +3223,7 @@ public:
         /++
         Op Assignment `op=` of a value (e.g. a number) to a $(B fully defined slice).
        +/
-        void opIndexOpAssign(string op, T, Slices...)(T value, Slices slices) scope return
+        void opIndexOpAssign(string op, T, Slices...)(T value, Slices slices) return scope
             if ((isFullPureSlice!Slices || isIndexedSlice!Slices)
                 && (!isDynamicArray!T || isDynamicArray!DeepElement)
                 && DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement
@@ -3253,7 +3253,7 @@ public:
         }
 
         ///
-        void opIndexOpAssign(string op,T, Slices...)(T concatenation, Slices slices) scope return
+        void opIndexOpAssign(string op,T, Slices...)(T concatenation, Slices slices) return scope
             if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T)
         {
             auto sl = this.lightScope.opIndex(slices);
@@ -3277,7 +3277,7 @@ public:
         /++
         Increment `++` and Decrement `--` operators for a $(B fully defined index).
         +/
-        auto ref opIndexUnary(string op)(size_t[N] _indices...) scope return
+        auto ref opIndexUnary(string op)(size_t[N] _indices...) return scope
             @trusted
             // @@@workaround@@@ for Issue 16473
             //if (op == `++` || op == `--`)
@@ -3341,7 +3341,7 @@ public:
         /++
         Increment `++` and Decrement `--` operators for a $(B fully defined slice).
         +/
-        void opIndexUnary(string op, Slices...)(Slices slices) scope return
+        void opIndexUnary(string op, Slices...)(Slices slices) return scope
             if (isFullPureSlice!Slices && (op == `++` || op == `--`))
         {
             auto sl = this.lightScope.opIndex(slices);
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 7cee7e79..806c59b8 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -27,7 +27,7 @@ struct mir_rcarray(T)
     import mir.internal.utility: isComplex, realType;
     ///
     package T* _payload;
-    package ref mir_rc_context context() inout scope return pure nothrow @nogc @trusted @property
+    package ref mir_rc_context context() inout return scope pure nothrow @nogc @trusted @property
     {
         assert(_payload);
         return (cast(mir_rc_context*)_payload)[-1];
@@ -577,7 +577,7 @@ struct mir_rci(T)
     }
 
     ///
-    inout(T)* lightScope()() scope return inout @property @trusted
+    inout(T)* lightScope()() return scope inout @property @trusted
     {
         debug
         {
@@ -615,15 +615,15 @@ struct mir_rci(T)
     }
 
     ///
-    mir_rci!(const T) lightConst()() scope return const nothrow @property
+    mir_rci!(const T) lightConst()() return scope const nothrow @property
     { return typeof(return)(_iterator, _array.lightConst); }
 
     ///
-    mir_rci!(immutable T) lightImmutable()() scope return immutable nothrow @property
+    mir_rci!(immutable T) lightImmutable()() return scope immutable nothrow @property
     { return typeof(return)(_iterator, _array.lightImmutable); }
 
     ///
-    ref inout(T) opUnary(string op : "*")() inout scope return
+    ref inout(T) opUnary(string op : "*")() inout return scope
     {
         debug
         {
@@ -636,7 +636,7 @@ struct mir_rci(T)
     }
 
     ///
-    ref inout(T) opIndex(ptrdiff_t index) inout scope return @trusted
+    ref inout(T) opIndex(ptrdiff_t index) inout return scope @trusted
     {
         debug
         {
diff --git a/source/mir/rc/context.d b/source/mir/rc/context.d
index 74f2d7bc..9ba44b50 100644
--- a/source/mir/rc/context.d
+++ b/source/mir/rc/context.d
@@ -176,15 +176,15 @@ mir_rc_context* mir_rc_create(
 package mixin template CommonRCImpl()
 {
     ///
-    ThisTemplate!(const T) lightConst()() scope return const @nogc nothrow @trusted @property
+    ThisTemplate!(const T) lightConst()() return scope const @nogc nothrow @trusted @property
     { return *cast(typeof(return)*) &this; }
 
     /// ditto
-    ThisTemplate!(immutable T) lightImmutable()() scope return immutable @nogc nothrow @trusted @property
+    ThisTemplate!(immutable T) lightImmutable()() return scope immutable @nogc nothrow @trusted @property
     { return *cast(typeof(return)*) &this; }
 
     ///
-    ThisTemplate!(const Unqual!T) moveToConst()() scope return @nogc nothrow @trusted @property
+    ThisTemplate!(const Unqual!T) moveToConst()() return scope @nogc nothrow @trusted @property
     {
         import core.lifetime: move;
         return move(*cast(typeof(return)*) &this);
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index 22f8d7b9..f3975cc1 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -39,7 +39,7 @@ struct mir_rcptr(T)
         package T* _value;
     package mir_rc_context* _context;
 
-    package ref mir_rc_context context() inout scope return @trusted @property
+    package ref mir_rc_context context() inout return scope @trusted @property
     {
         return *cast(mir_rc_context*)_context;
     }
@@ -50,7 +50,7 @@ struct mir_rcptr(T)
         _context = null;
     }
 
-    inout(void)* _thisPtr() inout scope return @trusted @property
+    inout(void)* _thisPtr() inout return scope @trusted @property
     {
         return cast(inout(void)*) _value;
     }
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index a1717ce9..0ef3aa14 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -36,7 +36,7 @@ struct mir_slim_rcptr(T)
     else
         package T* _value;
 
-    package ref mir_rc_context context() inout scope return pure nothrow @nogc @trusted @property
+    package ref mir_rc_context context() inout return scope pure nothrow @nogc @trusted @property
     {
         assert(_value);
         return (cast(mir_rc_context*)_value)[-1];
@@ -47,7 +47,7 @@ struct mir_slim_rcptr(T)
         _value = null;
     }
 
-    inout(void)* _thisPtr() inout scope return @trusted @property
+    inout(void)* _thisPtr() inout return scope @trusted @property
     {
         return cast(inout(void)*) _value;
     }
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 0431f22b..411bbdc1 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -238,7 +238,7 @@ template SmallArray(T, uint maxLength)
         }
 
         ///
-        ref inout(T) opIndex(size_t index) inout scope return
+        ref inout(T) opIndex(size_t index) inout return scope
         {
             return opIndex[index];
         }
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index bb5eaa7f..4e03aaa0 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -207,7 +207,7 @@ scope nothrow:
 
     The method implement with `[]` operation.
     +/
-    inout(char)[] opIndex() inout @trusted scope return
+    inout(char)[] opIndex() inout @trusted return scope
     {
         size_t i;
         if (__ctfe)
@@ -218,7 +218,7 @@ scope nothrow:
     }
 
     ///
-    ref inout(char) opIndex(size_t index) inout scope return
+    ref inout(char) opIndex(size_t index) inout return scope
     {
         return opIndex[index];
     }
@@ -228,7 +228,7 @@ scope nothrow:
 
     The method implement with `[i .. j]` operation.
     +/
-    inout(char)[] opIndex(size_t[2] range) inout @trusted scope return
+    inout(char)[] opIndex(size_t[2] range) inout @trusted return scope
     in (range[0] <= range[1])
     in (range[1] <= this.length)
     {

From 9d7c8a3e7810ea07fd241cfb0173a3319edb8a79 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 11 May 2022 09:17:53 -0400
Subject: [PATCH 354/573] Update normal visibility (#405)

Update normal visibility for access in other packages
---
 source/mir/math/func/normal.d | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/source/mir/math/func/normal.d b/source/mir/math/func/normal.d
index 2d17b982..2a7e24bd 100644
--- a/source/mir/math/func/normal.d
+++ b/source/mir/math/func/normal.d
@@ -221,7 +221,7 @@ enum real SQRT2PI = 2.50662827463100050241576528481104525L; // sqrt(2pi)
 ///
 enum real SQRT2PIINV = 1 / SQRT2PI; // 1 / sqrt(2pi)
 
-private:
+package(mir) {
 
 enum real EXP_2  = 0.135335283236612691893999494972484403L; /* exp(-2) */
 
@@ -229,6 +229,7 @@ enum real EXP_2  = 0.135335283236612691893999494972484403L; /* exp(-2) */
 enum T MAXLOG(T) = log(T.max);
 ///
 enum T MINLOG(T) = log(T.min_normal * T.epsilon); // log(smallest denormal);
+}
 
 /**
  *  Exponential of squared argument
@@ -244,6 +245,7 @@ enum T MINLOG(T) = log(T.min_normal * T.epsilon); // log(smallest denormal);
  * arithmetic      domain        # trials      peak         rms
  *   IEEE     -106.566, 106.566    10^5       1.6e-19     4.4e-20
  */
+package(mir)
 T expx2(T)(const T x_, int sign)
 {
     /*
@@ -285,7 +287,7 @@ T expx2(T)(const T x_, int sign)
    exp(x^2) erfc(x)
    valid for x > 1.
    Use with ndtrl and expx2l.  */
-
+package(mir)
 T erfce(T)(const T x)
 {
     T y = 1 / x;
@@ -317,6 +319,8 @@ private T poly(alias vec, T)(const T x)
     return y;
 }
 
+private {
+
 /* erfc(x) = exp(-x^2) P(1/x)/Q(1/x)
    1/8 <= 1/x <= 1
    Peak relative error 5.8e-21  */
@@ -357,6 +361,9 @@ immutable real[7] U = [ 0x1.dde6025c395ae34ep+19, 0x1.c4bc8b6235df35aap+18,
     0x1.6a0fed103f1c68a6p+5, 1.0
 ];
 
+}
+
+package(mir)
 F erf(F)(const F x)
  if(isFloatingPoint!F)
 {
@@ -389,6 +396,7 @@ F erf(F)(const F x)
  * A special function expx2(x) is used to suppress error amplification
  * in computing exp(-x^2).
  */
+package(mir)
 T erfc(T)(const T a)
 {
     if (a == T.infinity)
@@ -430,6 +438,8 @@ T erfc(T)(const T a)
     return y;
 }
 
+private:
+
 static immutable real[8] P0 =
 [ -0x1.758f4d969484bfdcp-7, 0x1.53cee17a59259dd2p-3,
 -0x1.ea01e4400a9427a2p-1,  0x1.61f7504a0105341ap+1, -0x1.09475a594d0399f6p+2,

From b773d5c115535737d663c4dd8bc06fb08cdc3174 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 24 May 2022 15:50:31 +0400
Subject: [PATCH 355/573] fix dub

---
 dub.sdl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index ed502ed1..5d93f070 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.106"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    // versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {

From cb5d12def54c0f13d75cf483c3019bacbb453309 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 24 May 2022 16:55:52 +0400
Subject: [PATCH 356/573] rework fuseShape

---
 source/mir/ndslice/fuse.d | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index cc488f5b..8751d26c 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -277,10 +277,7 @@ template fuseImpl(bool RC, T_, Dimensions...)
 private template fuseDimensionCount(R)
 {
     static if (is(typeof(R.init.shape) : size_t[N], size_t N) && (isDynamicArray!R || __traits(hasMember, R, "front")))
-    {
-        import mir.ndslice.topology: repeat;
         enum size_t fuseDimensionCount = N + fuseDimensionCount!(DeepElementType!R);
-    }
     else
         enum size_t fuseDimensionCount = 0;
 }
@@ -309,16 +306,24 @@ size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r)
         import mir.ndslice.topology: repeat;
         typeof(return) ret;
         ret[0 .. N] = r.shape;
-        if (!ret[0 .. N].anyEmptyShape)
+        bool next;
+        if (!ret[0 .. N].anyEmptyShape) foreach (ref elem; r)
         {
-            ret[N .. $] = fuseShape(mixin("r" ~ ".front".repeat(N).fuseCells.field));
-            import mir.algorithm.iteration: all;
-            if (!all!((a) => cast(size_t[M]) ret[N .. $] == .fuseShape(a))(r))
+            auto elemShape = fuseShape(elem);
+            if (next)
             {
-                version (D_Exceptions)
-                    throw shapeException;
-                else
-                    assert(0, shapeExceptionMsg);
+                if (elemShape != ret[N .. $])
+                {
+                    version (D_Exceptions)
+                        throw shapeException;
+                    else
+                        assert(0, shapeExceptionMsg);
+                }
+                next = true;
+            }
+            else
+            {
+                ret[N .. $] = elemShape;
             }
         }
         return ret;
@@ -326,9 +331,12 @@ size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r)
 }
 
 private template FuseElementType(NDRange)
+    if (fuseDimensionCount!NDRange >= 1)
 {
-    import mir.ndslice.topology: repeat;
-    alias FuseElementType = typeof(mixin("NDRange.init" ~ ".front".repeat(fuseDimensionCount!NDRange).fuseCells.field));
+    static if (fuseDimensionCount!NDRange == 1)
+        alias FuseElementType = typeof(NDRange.init.front);
+    else
+        alias FuseElementType = FuseElementType!(typeof(NDRange.init.front));
 }
 
 /++

From f9e5badf6e32b7d81be18f26da19889566237613 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 24 May 2022 16:56:45 +0400
Subject: [PATCH 357/573] strip attribute from fuseImpl

---
 source/mir/ndslice/fuse.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index 8751d26c..3caad1fe 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -180,7 +180,7 @@ template fuseImpl(bool RC, T_, Dimensions...)
     Params:
         r = parallelotope (ndrange) with length/shape and input range primitives.
     +/
-    auto fuseImpl(NDRange)(NDRange r) @safe
+    auto fuseImpl(NDRange)(NDRange r)
         if (hasShape!NDRange)
     {
         import mir.conv: emplaceRef;

From 91e7ed3a4ee7bd9f4274349a34a2fb1014e31c22 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 24 May 2022 21:53:33 +0400
Subject: [PATCH 358/573] adopt for dip1000

---
 source/mir/algorithm/iteration.d | 40 ++++++++++++------------
 source/mir/ndslice/internal.d    | 52 +++++++++++++++++++++-----------
 source/mir/ndslice/slice.d       |  8 ++---
 3 files changed, 58 insertions(+), 42 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 1a2186f4..3d45344b 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -603,12 +603,12 @@ package(mir) template allFlattened(args...)
     static if (args.length)
     {
         alias arg = args[0];
-        @optmath @property ls()()
+        @optmath @property allFlattenedMod()()
         {
             import mir.ndslice.topology: flattened;
             return flattened(arg);
         }
-        alias allFlattened = AliasSeq!(ls, allFlattened!(args[1..$]));
+        alias allFlattened = AliasSeq!(allFlattenedMod, allFlattened!(args[1..$]));
     }
     else
         alias allFlattened = AliasSeq!();
@@ -697,7 +697,7 @@ else
     private enum Mir_disable_inlining_in_reduce = false;
 }
 
-S reduceImpl(alias fun, S, Slices...)(S seed, scope Slices slices)
+S reduceImpl(alias fun, S, Slices...)(S seed, Slices slices)
 {
     do
     {
@@ -744,7 +744,7 @@ template reduce(alias fun)
     Returns:
         the accumulated `result`
     +/
-    @optmath auto reduce(S, Slices...)(S seed, scope Slices slices)
+    @optmath auto reduce(S, Slices...)(S seed, Slices slices)
         if (Slices.length)
     {
         static if (Slices.length > 1)
@@ -767,7 +767,7 @@ template reduce(alias fun)
     }
     else version(Mir_disable_inlining_in_reduce)
     //As above, but with inlining disabled.
-    @optmath auto reduce(S, Slices...)(S seed, scope Slices slices)
+    @optmath auto reduce(S, Slices...)(S seed, Slices slices)
         if (Slices.length)
     {
         static if (Slices.length > 1)
@@ -952,7 +952,7 @@ version(mir_test) unittest
     assert(a == 7);
 }
 
-void eachImpl(alias fun, Slices...)(scope Slices slices)
+void eachImpl(alias fun, Slices...)(Slices slices)
 {
     foreach(ref slice; slices)
         assert(!slice.empty);
@@ -961,14 +961,14 @@ void eachImpl(alias fun, Slices...)(scope Slices slices)
         static if (DimensionCount!(Slices[0]) == 1)
             fun(frontOf!slices);
         else
-            .eachImpl!fun(frontOf!slices);
+            .eachImpl!fun(frontOf2!slices);
         foreach_reverse(i; Iota!(Slices.length))
             slices[i].popFront;
     }
     while(!slices[0].empty);
 }
 
-void chequerEachImpl(alias fun, Slices...)(Chequer color, scope Slices slices)
+void chequerEachImpl(alias fun, Slices...)(Chequer color, Slices slices)
 {
     foreach(ref slice; slices)
         assert(!slice.empty);
@@ -1116,7 +1116,7 @@ template each(alias fun)
         Params:
             slices = One or more slices, ranges, and arrays.
         +/
-        @optmath auto each(Slices...)(scope Slices slices)
+        @optmath auto each(Slices...)(Slices slices)
             if (Slices.length && !is(Slices[0] : Chequer))
         {
             static if (Slices.length > 1)
@@ -1140,7 +1140,7 @@ template each(alias fun)
             color = $(LREF Chequer).
             slices = One or more slices.
         +/
-        @optmath auto each(Slices...)(Chequer color, scope Slices slices)
+        @optmath auto each(Slices...)(Chequer color, Slices slices)
             if (Slices.length && allSatisfy!(isSlice, Slices))
         {
             static if (Slices.length > 1)
@@ -2139,7 +2139,7 @@ version(mir_test) unittest
     assert(bi == [0, 0]);
 }
 
-size_t anyImpl(alias fun, Slices...)(scope Slices slices)
+size_t anyImpl(alias fun, Slices...)(Slices slices)
     if (Slices.length)
 {
     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
@@ -2164,7 +2164,7 @@ size_t anyImpl(alias fun, Slices...)(scope Slices slices)
             }
             else
             {
-                if (anyImpl!fun(frontOf!slices))
+                if (anyImpl!fun(frontOf2!slices))
                     return true;
             }
             foreach_reverse(ref slice; slices)
@@ -2195,7 +2195,7 @@ template any(alias pred = "a")
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath bool any(Slices...)(scope Slices slices)
+    @optmath bool any(Slices...)(Slices slices)
         if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
     {
         static if (Slices.length > 1)
@@ -2299,7 +2299,7 @@ version(mir_test) unittest
                   [8, 8, 5]]);
 }
 
-size_t allImpl(alias fun, Slices...)(scope Slices slices)
+size_t allImpl(alias fun, Slices...)(Slices slices)
     if (Slices.length)
 {
     static if (__traits(isSame, fun, naryFun!"a") && is(S : Slice!(FieldIterator!(BitField!(Field, I))), Field, I))
@@ -2324,7 +2324,7 @@ size_t allImpl(alias fun, Slices...)(scope Slices slices)
             }
             else
             {
-                if (!allImpl!fun(frontOf!slices))
+                if (!allImpl!fun(frontOf2!slices))
                     return false;
             }
             foreach_reverse(ref slice; slices)
@@ -2355,7 +2355,7 @@ template all(alias pred = "a")
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath bool all(Slices...)(scope Slices slices)
+    @optmath bool all(Slices...)(Slices slices)
         if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
     {
         static if (Slices.length > 1)
@@ -2485,7 +2485,7 @@ template count(alias fun)
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath size_t count(Slices...)(scope Slices slices)
+    @optmath size_t count(Slices...)(Slices slices)
         if (Slices.length)
     {
         static if (Slices.length > 1)
@@ -2605,7 +2605,7 @@ template equal(alias pred = "a == b")
         Returns:
             `true` any of the elements verify `pred` and `false` otherwise.
         +/
-        bool equal(Slices...)(scope Slices slices)
+        bool equal(Slices...)(Slices slices)
             if (Slices.length >= 2)
         {
             import mir.internal.utility;
@@ -2823,7 +2823,7 @@ version(mir_test) unittest
     assert(cmp(sl1[0 .. $ - 1, 0 .. $ - 3], sl1[0 .. $, 0 .. $ - 3]) < 0);
 }
 
-size_t countImpl(alias fun, Slices...)(scope Slices slices)
+size_t countImpl(alias fun, Slices...)(Slices slices)
 {
     size_t ret;
     alias S = Slices[0];
@@ -2850,7 +2850,7 @@ size_t countImpl(alias fun, Slices...)(scope Slices slices)
                 ret++;
         }
         else
-            ret += .countImpl!fun(frontOf!slices);
+            ret += .countImpl!fun(frontOf2!slices);
         foreach_reverse(ref slice; slices)
             slice.popFront;
     }
diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index 2e58ecd2..8f996d05 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -365,11 +365,25 @@ package(mir) template frontOf(args...)
         enum frontOf = args;
     else
     {
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref frontOfMod()()
+        {
+            return args[0].front;
+        }
+        alias frontOf = AliasSeq!(frontOfMod, frontOf!(args[1..$]));
+    }
+}
+
+package(mir) template frontOf2(args...)
+{
+    static if (args.length == 0)
+        enum frontOf2 = args;
+    else
+    {
+        @optmath @property auto frontOf2Mod()()
         {
             return args[0].front;
         }
-        alias frontOf = AliasSeq!(ls, frontOf!(args[1..$]));
+        alias frontOf2 = AliasSeq!(frontOf2Mod, frontOf2!(args[1..$]));
     }
 }
 
@@ -379,11 +393,11 @@ package(mir) template backOf(args...)
         enum backOf = args;
     else
     {
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref backOfMod()()
         {
             return args[0].back;
         }
-        alias backOf = AliasSeq!(ls, backOf!(args[1..$]));
+        alias backOf = AliasSeq!(backOfMod, backOf!(args[1..$]));
     }
 }
 
@@ -393,11 +407,11 @@ package(mir) template frontOfD(size_t dimension, args...)
         enum frontOfD = args;
     else
     {
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref frontOfDMod()()
         {
             return args[0].front!dimension;
         }
-        alias frontOfD = AliasSeq!(ls, frontOfD!(dimension, args[1..$]));
+        alias frontOfD = AliasSeq!(frontOfDMod, frontOfD!(dimension, args[1..$]));
     }
 }
 
@@ -407,11 +421,11 @@ package(mir) template backOfD(size_t dimension, args...)
         enum backOfD = args;
     else
     {
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref backOfDMod()()
         {
             return args[0].back!dimension;
         }
-        alias backOfD = AliasSeq!(ls, backOfD!(dimension, args[1..$]));
+        alias backOfD = AliasSeq!(backOfDMod, backOfD!(dimension, args[1..$]));
     }
 }
 
@@ -422,11 +436,11 @@ package(mir) template frontOfDim(size_t dim, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref ls()
+        @optmath @property auto ref frontOfDimMod()
         {
             return arg.front!dim;
         }
-        alias frontOfDim = AliasSeq!(ls, frontOfDim!(dim, args[1..$]));
+        alias frontOfDim = AliasSeq!(frontOfDimMod, frontOfDim!(dim, args[1..$]));
     }
 }
 
@@ -437,11 +451,11 @@ package(mir) template selectFrontOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref selectFrontOfMod()()
         {
             return arg.lightScope.selectFront!0(input);
         }
-        alias selectFrontOf = AliasSeq!(ls, selectFrontOf!(input, args[1..$]));
+        alias selectFrontOf = AliasSeq!(selectFrontOfMod, selectFrontOf!(input, args[1..$]));
     }
 }
 
@@ -452,11 +466,11 @@ package(mir) template selectBackOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref selectBackOfMod()()
         {
             return arg.selectBack!0(input);
         }
-        alias selectBackOf = AliasSeq!(ls, selectBackOf!(input, args[1..$]));
+        alias selectBackOf = AliasSeq!(selectBackOfMod, selectBackOf!(input, args[1..$]));
     }
 }
 
@@ -467,11 +481,11 @@ package(mir) template frontSelectFrontOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref frontSelectFrontOfMod()()
         {
             return arg.lightScope.front.selectFront!0(input);
         }
-        alias frontSelectFrontOf = AliasSeq!(ls, frontSelectFrontOf!(input, args[1..$]));
+        alias frontSelectFrontOf = AliasSeq!(frontSelectFrontOfMod, frontSelectFrontOf!(input, args[1..$]));
     }
 }
 
@@ -482,10 +496,12 @@ package(mir) template frontSelectBackOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref ls()()
+        @optmath @property auto ref frontSelectBackOfMod
+        ()()
         {
             return arg.lightScope.front.selectBack!0(input);
         }
-        alias frontSelectBackOf = AliasSeq!(ls, frontSelectBackOf!(input, args[1..$]));
+        alias frontSelectBackOf = AliasSeq!(frontSelectBackOfMod
+        , frontSelectBackOf!(input, args[1..$]));
     }
 }
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index bf2c5440..6f83a063 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -477,15 +477,15 @@ package(mir) template allLightScope(args...)
         static if(!isDynamicArray!Arg)
         {
             static if(!is(LightScopeOf!Arg == Arg))
-            @optmath @property ls()()
+            @optmath @property allLightScopeMod()()
             {
                 import mir.qualifier: lightScope;
                 return lightScope(arg);
             }
-            else alias ls = arg;
+            else alias allLightScopeMod = arg;
         }
-        else alias ls = arg;
-        alias allLightScope = AliasSeq!(ls, allLightScope!(args[1..$]));
+        else alias allLightScopeMod = arg;
+        alias allLightScope = AliasSeq!(allLightScopeMod, allLightScope!(args[1..$]));
     }
     else
         alias allLightScope = AliasSeq!();

From 8069c59e24d7334d6c102a5a59b59afb51a5a1cb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 24 May 2022 22:36:30 +0400
Subject: [PATCH 359/573] ditto

---
 source/mir/algorithm/iteration.d |  2 +-
 source/mir/appender.d            |  2 +-
 source/mir/ndslice/slice.d       | 11 +++++------
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 3d45344b..c7577c28 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -576,7 +576,7 @@ private void checkShapesMatch(
     string fun = __FUNCTION__,
     string pfun = __PRETTY_FUNCTION__,
     Slices...)
-    (scope ref const Slices slices)
+    (Slices slices)
     if (Slices.length > 1)
 {
     enum msgShape = "all slices must have the same shape"  ~ tailErrorMessage!(fun, pfun);
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 335f099d..9c54182e 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -214,7 +214,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     }
 
     ///
-    inout(T)[] data() inout @property @safe scope return
+    inout(T)[] data() inout @property @trusted scope return
     {
         return _buffer.length ? _buffer[0 .. _currentLength] : _scopeBuffer[0 .. _currentLength];
     }
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 6f83a063..7d8c21c8 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -472,19 +472,18 @@ package(mir) template allLightScope(args...)
 {
     static if (args.length)
     {
-        alias arg = args[0];
-        alias Arg = typeof(arg);
+        alias Arg = typeof(args[0]);
         static if(!isDynamicArray!Arg)
         {
             static if(!is(LightScopeOf!Arg == Arg))
-            @optmath @property allLightScopeMod()()
+            @optmath @property auto allLightScopeMod()()
             {
                 import mir.qualifier: lightScope;
-                return lightScope(arg);
+                return args[0].lightScope;
             }
-            else alias allLightScopeMod = arg;
+            else alias allLightScopeMod = args[0];
         }
-        else alias allLightScopeMod = arg;
+        else alias allLightScopeMod = args[0];
         alias allLightScope = AliasSeq!(allLightScopeMod, allLightScope!(args[1..$]));
     }
     else

From 427c732018b517cc61290406f1041ab5a6f0ed7f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 13:04:37 +0400
Subject: [PATCH 360/573] ditto

---
 source/mir/rc/array.d | 18 ++----------------
 source/mir/series.d   |  2 +-
 2 files changed, 3 insertions(+), 17 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 806c59b8..f6598eb1 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -347,30 +347,16 @@ auto rcarray(T = void, Range)(Range range)
     }
 }
 
-/// ditto
-RCArray!V rcarray(T = void, V)(V[] values...)
-    if (is(T == void) && hasIndirections!V)
-{
-    return .rcarray(values, true);
-}
-
 /// ditto
 RCArray!V rcarray(T = void, V)(scope V[] values...)
-    if (is(T == void) && !hasIndirections!V)
+    if (is(T == void))
 {
     return .rcarray(values, true);
 }
 
-/// ditto
-RCArray!V rcarray(T = void, V)(V[] values, bool deallocate)
-    if (is(T == void) && hasIndirections!V)
-{
-    return .rcarray!V(values, deallocate);
-}
-
 /// ditto
 RCArray!V rcarray(T = void, V)(scope V[] values, bool deallocate)
-    if (is(T == void) && !hasIndirections!V)
+    if (is(T == void))
 {
     return .rcarray!V(values, deallocate);
 }
diff --git a/source/mir/series.d b/source/mir/series.d
index d68b56ab..51641d86 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -406,7 +406,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     }
 
     /// ditto
-    auto opBinary(string op : "~")(const typeof(this) rhs) const
+    auto opBinary(string op : "~")(const typeof(this) rhs) const @trusted
     {
         return unionSeries(this.lightScope, rhs.lightScope);
     }

From 37eefeff97d5ade12bf5be22abc2bf5cd7c71711 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 13:15:05 +0400
Subject: [PATCH 361/573] ditto

---
 source/mir/rc/array.d     | 33 +--------------------------------
 source/mir/small_array.d  |  4 ++--
 source/mir/small_string.d |  4 ++--
 3 files changed, 5 insertions(+), 36 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index f6598eb1..39f6cbe7 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -423,47 +423,16 @@ template rcarray(T)
         }
     }
 
-    /// ditto
-    RCArray!T rcarray(V)(V[] values...)
-        if (hasIndirections!V)
-    {
-        return .rcarray!T(values, true);
-    }
-
     /// ditto
     RCArray!T rcarray(V)(scope V[] values...)
-        if (!hasIndirections!V)
     {
         return .rcarray!T(values, true);
     }
 
-    /// ditto
-    RCArray!T rcarray(V)(V[] values, bool deallocate)
-        if (hasIndirections!V)
-    {
-        auto ret = mir_rcarray!T(values.length, false, deallocate);
-        static if (!hasElaborateAssign!(Unqual!T) && is(Unqual!V == Unqual!T))
-        {
-            ()@trusted {
-                import core.stdc.string: memcpy;
-                memcpy(cast(void*)ret.ptr, cast(const void*)values.ptr, values.length * T.sizeof);
-            }();
-        }
-        else
-        {
-            import  mir.conv: emplaceRef;
-            auto lhs = ret[];
-            foreach (i, ref e; values)
-                lhs[i].emplaceRef!T(e);
-        }
-        return ret;
-    }
-
     /// ditto
     RCArray!T rcarray(V)(scope V[] values, bool deallocate)
-        if (!hasIndirections!V)
     {
-        auto ret = mir_rcarray!T(values.length, false);
+        auto ret = mir_rcarray!T(values.length, hasElaborateDestructor!T, deallocate);
         static if (!hasElaborateAssign!(Unqual!T) && is(Unqual!V == Unqual!T))
         {
             ()@trusted {
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 411bbdc1..315045c2 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -309,10 +309,10 @@ template SmallArray(T, uint maxLength)
 
     auto s8 = SmallArray!(char, 8)("Hellow!!");
     assert(!s8.empty);
-    assert(s8 == "Hellow!!", s8[]);
+    assert(s8 == "Hellow!!");
 
     s16 = s8;
-    assert(s16 == "Hellow!!", s16[]);
+    assert(s16 == "Hellow!!");
     s16[7] = '@';
     s8 = null;
     assert(s8.empty);
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 4e03aaa0..cb97c7fc 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -311,13 +311,13 @@ const:
     assert(s16.empty);
 
     auto s8 = SmallString!8("Hellow!!");
-    assert(s8 == "Hellow!!", s8[]);
+    assert(s8 == "Hellow!!");
     assert(s8[] == "Hellow!!");
     assert(s8[0 .. $] == "Hellow!!");
     assert(s8[1 .. 4] == "ell");
 
     s16 = s8;
-    assert(s16 == "Hellow!!", s16[]);
+    assert(s16 == "Hellow!!");
     s16[7] = '@';
     s8 = null;
     assert(s8.empty);

From 47a9975e9e6a21eb41afdc0a4193fc1c5b265663 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 13:16:36 +0400
Subject: [PATCH 362/573] ditto

---
 source/mir/format.d | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 28841cc5..615de2e3 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -423,7 +423,7 @@ version (mir_test) unittest
     stringBuf w;
     assert(w.printEscaped("Hi \a\v\0\f\t\b \\\r\n" ~ `"@nogc"`).data == `Hi \a\v\0\f\t\b \\\r\n\"@nogc\"`);
     w.reset;
-    assert(w.printEscaped("\x03").data == `\x03`, w.data);
+    assert(w.printEscaped("\x03").data == `\x03`);
 }
 
 /++
@@ -548,7 +548,7 @@ version (mir_test) unittest
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
     w.print(Flag.yes);
-    assert(w.data == "yes", w.data);
+    assert(w.data == "yes");
 }
 
 /// Prints boolean
@@ -575,9 +575,9 @@ version (mir_test) unittest
 {
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
-    assert(w.print(true).data == `true`, w.data);
+    assert(w.print(true).data == `true`);
     w.reset;
-    assert(w.print(false).data == `false`, w.data);
+    assert(w.print(false).data == `false`);
 }
 
 /// Prints associative array
@@ -612,7 +612,7 @@ version (mir_test) unittest
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
     w.print(["a": 1, "b": 2]);
-    assert(w.data == `["a": 1, "b": 2]` || w.data == `["b": 2, "a": 1]`, w.data);
+    assert(w.data == `["a": 1, "b": 2]` || w.data == `["b": 2, "a": 1]`);
 }
 
 /// Prints null
@@ -661,7 +661,7 @@ version (mir_test) unittest
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
     string[2] array = ["a\na", "b"];
-    assert(w.print(array[]).data == `["a\na", "b"]`, w.data);
+    assert(w.print(array[]).data == `["a\na", "b"]`);
 }
 
 /// Prints escaped character in the form `'c'`.
@@ -1099,7 +1099,7 @@ version (mir_test) unittest
     w.put(' ');
     w.printZeroPad(123, 5);
 
-    assert(w.data == "-0123 00123", w.data);
+    assert(w.data == "-0123 00123");
 }
 
 ///

From 5f3a21a50b21c18124df19d66bbad24638e7a059 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 13:23:28 +0400
Subject: [PATCH 363/573] ditto

---
 source/mir/bignum/decimal.d | 2 +-
 source/mir/bignum/fixed.d   | 2 +-
 source/mir/bignum/integer.d | 2 +-
 source/mir/format.d         | 3 ++-
 source/mir/ndslice/slice.d  | 2 +-
 source/mir/series.d         | 5 +++--
 6 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 22c15522..4585f136 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -874,7 +874,7 @@ struct Decimal(size_t maxSize64)
         auto decimal = Decimal!1(str);
         stringBuf buffer;
         buffer << decimal;
-        assert(buffer.data == str, buffer.data);
+        assert(buffer.data == str);
     }
 
     /++
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 2561dc01..5be88591 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -173,7 +173,7 @@ struct UInt(size_t size)
         auto integer = UInt!256(str);
         stringBuf buffer;
         buffer << integer;
-        assert(buffer.data == str, buffer.data);
+        assert(buffer.data == str);
     }
 
     ///
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index f01d32a6..38515c2b 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -642,7 +642,7 @@ struct BigInt(size_t maxSize64)
         auto integer = BigInt!4(str);
         stringBuf buffer;
         buffer << integer;
-        assert(buffer.data == str, buffer.data);
+        assert(buffer.data == str);
     }
 }
 
diff --git a/source/mir/format.d b/source/mir/format.d
index 615de2e3..7bf5e260 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -769,7 +769,8 @@ unittest
 
     void check(double num, string value)
     {
-        assert(buffer.print(num, spec).data == value, buffer.data); buffer.reset;
+        assert(buffer.print(num, spec).data == value);
+        buffer.reset;
     }
 
     check(-0.0, "-0.0");
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 7d8c21c8..384a21dd 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1369,7 +1369,7 @@ public:
     /++
     Save primitive.
     +/
-    auto save()() scope return inout @property
+    auto save()() inout @property
     {
         return this;
     }
diff --git a/source/mir/series.d b/source/mir/series.d
index 51641d86..204e26df 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -2205,7 +2205,8 @@ See_also $(LREF Series.opBinary) $(LREF makeUnionSeries)
 auto unionSeries(IndexIterator, Iterator, size_t N, SliceKind kind, size_t C)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple...)
     if (C > 1)
 {
-    return unionSeriesImplPrivate!false(seriesTuple);
+    import core.lifetime: move;
+    return unionSeriesImplPrivate!false(move(seriesTuple));
 }
 
 ///
@@ -2411,7 +2412,7 @@ auto unionSeriesImpl(I, E,
     }
 }
 
-private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator...)(ref Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple, ref Allocator allocator)
+private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator...)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple, ref Allocator allocator)
     if (C > 1 && Allocator.length <= 1)
 {
     import mir.algorithm.setops: unionLength;

From e3c023a5a7788be5643ddac68280821bbfeca522 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 13:56:14 +0400
Subject: [PATCH 364/573] ditto

---
 source/mir/series.d | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/source/mir/series.d b/source/mir/series.d
index 204e26df..d0f7cc01 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -402,13 +402,15 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     ///
     typeof(this) opBinary(string op : "~")(typeof(this) rhs)
     {
-        return unionSeries(this.lightScope, rhs.lightScope);
+        typeof(this.lightScope)[2] lhsAndRhs = [this.lightScope, rhs.lightScope];
+        return unionSeriesImplPrivate!false(lhsAndRhs);
     }
 
     /// ditto
     auto opBinary(string op : "~")(const typeof(this) rhs) const @trusted
     {
-        return unionSeries(this.lightScope, rhs.lightScope);
+        typeof(this.lightScope)[2] lhsAndRhs = [this.lightScope, rhs.lightScope];
+        return unionSeriesImplPrivate!false(lhsAndRhs);
     }
 
     static if (doUnittest)

From 7e12052bf280a99ee267ca57529fe2a90b7d4b40 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 15:41:23 +0400
Subject: [PATCH 365/573] dip1000 support

---
 source/mir/rc/array.d | 6 +++---
 source/mir/series.d   | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 39f6cbe7..7e54d5dc 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -253,20 +253,20 @@ struct mir_rcarray(T)
     }
 
     ///
-    ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc
+    ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc
     {
         this = typeof(this).init;
     }
 
     ///
-    ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc
+    ref opAssign(scope return typeof(this) rhs) scope return @trusted // pure nothrow @nogc
     {
         this.proxySwap(rhs);
         return this;
     }
 
     ///
-    ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc
+    ref opAssign(Q)(scope return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc
         if (isImplicitlyConvertible!(Q*, T*))
     {
         this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
diff --git a/source/mir/series.d b/source/mir/series.d
index d0f7cc01..28deebac 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -348,7 +348,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     private static immutable defaultExc() = new Exception(defaultMsg!() ~ " required key");
 
     ///
-    void serdeFinalize()() @trusted
+    void serdeFinalize()() @trusted scope
     {
         import mir.algorithm.iteration: any;
         import mir.ndslice.topology: pairwise;

From c136f93b689ffc7650e4a853a66fedcf78b1a394 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 16:51:43 +0400
Subject: [PATCH 366/573] serdeParseEnum for DIP1000

---
 source/mir/serde.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index afa0b3e2..27b51114 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -882,7 +882,7 @@ alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value
 
 /++
 +/
-bool serdeParseEnum(E)(scope const char[] str, ref E res)
+bool serdeParseEnum(E)(scope const char[] str, scope ref E res)
     @safe pure nothrow @nogc
     if (is(E == enum))
 {
@@ -2316,7 +2316,7 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
 
 private:
 
-auto fastLazyToUpper()(const(char)[] name)
+auto fastLazyToUpper()(return scope const(char)[] name)
 {
     import mir.ndslice.topology: map;
     return name.map!fastToUpper;

From 8516bcccebd5b0841949eb683946a3ef15df7812 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 20:25:31 +0400
Subject: [PATCH 367/573] add `factorial`

---
 source/mir/bignum/fp.d    | 15 ++++++++-
 source/mir/math/numeric.d | 65 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 49e045f1..79d07aa8 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -263,8 +263,21 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
         assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
     }
 
+    /// ditto
+    this(ulong value)
+    {
+        this(UInt!64(value));
+    }
+
+    ///
+    this(long value)
+    {
+        this(ulong(value >= 0 ? value : -value));
+        this.sign = !(value >= 0);
+    }
+
     ///
-    ref Fp opOpAssign(string op : "*")(Fp rhs) nothrow return
+    ref Fp opOpAssign(string op : "*")(Fp rhs) nothrow scope return
     {
         this = this.opBinary!op(rhs);
         return this;
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index 7bfc07e3..741b485e 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -511,3 +511,68 @@ unittest
     assert(isNaN(sumOfLog2s([-real.infinity])));
     assert(sumOfLog2s([ 0.25, 0.25, 0.25, 0.125 ]) == -9);
 }
+
+/++
+Quickly computes factorial using extended
+precision floating point type $(MREF mir,bignum,fp).
+
+Params:
+    count = number of product members
+    start = initial member value (optional)
+Returns: `(count + start - 1)! / (start - 1)!`
++/
+auto factorial
+    (size_t coefficientSize = 128, Exp = sizediff_t)
+    (ulong count, ulong start = 1)
+    if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
+    in (start)
+{
+    import mir.utility: _expect;
+    import mir.bignum.fp: Fp;
+    alias R = Fp!(coefficientSize, Exp);
+    R prod = 1LU;
+    import mir.checkedint: addu, mulu;
+
+    if (count)
+    {
+        ulong tempProd = start;
+        while(--count)
+        {
+            bool overflow;
+            ulong nextTempProd = mulu(tempProd, ++start, overflow);
+            if (_expect(!overflow, true))
+            {
+                tempProd = nextTempProd;
+                continue;
+            }
+            else
+            {
+                prod *= R(tempProd);
+                tempProd = start;
+            }
+        }
+        prod *= R(tempProd);
+    }
+
+    return prod;
+}
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.bignum.fp: Fp;
+    import mir.math.common: approxEqual;
+    import mir.math.numeric: prod;
+    import mir.ndslice.topology: iota;
+
+    static assert(is(typeof(factorial(33)) == Fp!128));
+    static assert(is(typeof(factorial!256(33)) == Fp!256));
+    static assert(cast(double) factorial(33) == 8.68331761881188649551819440128e+36);
+
+    assert(cast(double) factorial(0) == 1);
+    assert(cast(double) factorial(0, 100) == 1);
+    assert(cast(double) factorial(1, 100) == 100);
+    assert(approxEqual(cast(double) factorial(100, 1000), iota([100], 1000).prod!double));
+}

From e3183be23ae6b257d0b3c27d1f8de94774c27292 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 20:26:08 +0400
Subject: [PATCH 368/573] fix import

---
 source/mir/math/numeric.d | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index 741b485e..e61afecb 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -527,11 +527,12 @@ auto factorial
     if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
     in (start)
 {
-    import mir.utility: _expect;
     import mir.bignum.fp: Fp;
+    import mir.checkedint: mulu;
+    import mir.utility: _expect;
+
     alias R = Fp!(coefficientSize, Exp);
     R prod = 1LU;
-    import mir.checkedint: addu, mulu;
 
     if (count)
     {

From d2c8f52c5cee78731a836625d6d05aad2eb81941 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 20:45:48 +0400
Subject: [PATCH 369/573] add fp_log2 and fp_log

---
 source/mir/bignum/fp.d | 50 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 79d07aa8..d3d110b8 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -460,3 +460,53 @@ Fp!(coefficientizeA + coefficientizeB) extendedMul(size_t coefficientizeA, size_
     }
     return typeof(return)(sign, exponent, coefficient);
 }
+
+///
+template fp_log2(T)
+    if (__traits(isFloating, T))
+{
+    ///
+    double fp_log2(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
+    {
+        import mir.math.common: log2;
+        auto exponent = x.exponent + coefficientSize;
+        x.exponent = -coefficientSize;
+        return log2(cast(double)x) + exponent;
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.math.common: log2, approxEqual;
+    import mir.bignum.fp: fp_log2;
+
+    double x = 123456789.0e+123;
+    assert(approxEqual(x.Fp!128.fp_log2!double, x.log2));
+}
+
+///
+template fp_log(T)
+    if (__traits(isFloating, T))
+{
+    ///
+    double fp_log(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
+    {
+        import mir.math.constant: LN2;
+        return T(LN2) * fp_log2!T(x);
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.math.common: log, approxEqual;
+    import mir.bignum.fp: fp_log;
+
+    double x = 123456789.0e+123;
+    assert(approxEqual(x.Fp!128.fp_log!double, x.log));
+}

From fd82feb8e44ae7ea2683767e18ca2ba01ad85a8f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 20:48:15 +0400
Subject: [PATCH 370/573] fixup

---
 source/mir/bignum/fp.d | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index d3d110b8..97731b3f 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -466,12 +466,12 @@ template fp_log2(T)
     if (__traits(isFloating, T))
 {
     ///
-    double fp_log2(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
+    T fp_log2(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
     {
         import mir.math.common: log2;
         auto exponent = x.exponent + coefficientSize;
         x.exponent = -coefficientSize;
-        return log2(cast(double)x) + exponent;
+        return log2(cast(T)x) + exponent;
     }
 }
 
@@ -492,7 +492,7 @@ template fp_log(T)
     if (__traits(isFloating, T))
 {
     ///
-    double fp_log(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
+    T fp_log(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
     {
         import mir.math.constant: LN2;
         return T(LN2) * fp_log2!T(x);

From 10be2e53192ba80ea0ffedd17196961db9c80b0f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 21:15:29 +0400
Subject: [PATCH 371/573] add Complexity for factorial

---
 source/mir/math/numeric.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index e61afecb..ae12de12 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -520,6 +520,7 @@ Params:
     count = number of product members
     start = initial member value (optional)
 Returns: `(count + start - 1)! / (start - 1)!`
+Complexity: O(count)
 +/
 auto factorial
     (size_t coefficientSize = 128, Exp = sizediff_t)

From 8492de727ca474b497827046955e12aaac3071c4 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 22:32:46 +0400
Subject: [PATCH 372/573] add binomialCoefficient

---
 source/mir/math/numeric.d | 59 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index ae12de12..0d64c8ae 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -578,3 +578,62 @@ unittest
     assert(cast(double) factorial(1, 100) == 100);
     assert(approxEqual(cast(double) factorial(100, 1000), iota([100], 1000).prod!double));
 }
+
+/++
+Quickly computes binomial coefficient using extended
+precision floating point type $(MREF mir,bignum,fp).
+
+Params:
+    DivisionType = the type used for a single division at the final.
+        By defualt, uses `real` precision. TODO: full `Fp` precision.
+    n = number elements in the set 
+    k = number elements in the subset 
+Returns: n choose k
+Complexity: O(min(k, n - k))
++/
+template binomialCoefficient(DivisionType = void)
+    if (is(DivisionType == void) || __traits(isFloating, DivisionType))
+{
+    auto binomialCoefficient
+        (size_t coefficientSize = 128, Exp = sizediff_t)
+        (ulong n, uint k)
+        if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
+        in (k <= n)
+    {
+        import mir.bignum.fp: Fp;
+    
+        alias R = Fp!(coefficientSize, Exp);
+
+        // TODO: For DivisionType == void
+        //       use full precision division for void when Fp division is ready
+        static if (is(DivisionType == void))
+            alias T = real;
+        else
+            alias T = DivisionType;
+
+        if (k > n - k)
+            k = cast(uint)(n - k);
+
+        auto a = factorial!(coefficientSize, Exp)(k, n - k + 1);
+        auto b = factorial!(coefficientSize, Exp)(k);
+        auto exponent = a.exponent - b.exponent;
+        a.exponent = b.exponent = -coefficientSize;
+        auto ret = R(cast(T) a / cast(T) b);
+        ret.exponent += exponent;
+        return ret;
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.bignum.fp: Fp;
+    import mir.math.common: approxEqual;
+
+    static assert(is(typeof(binomialCoefficient!double(30, 18)) == Fp!128), typeof(binomialCoefficient!double(30, 18)).stringof);
+    static assert(cast(double) binomialCoefficient(30, 18) == 86493225);
+
+    assert(approxEqual(cast(double) binomialCoefficient(100, 40), 1.374623414580281150126736972e+28));
+}

From 19c77bbe26279f6dee3b644cc29ccc61a10f9370 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 23:27:38 +0400
Subject: [PATCH 373/573] simplify binomialCoefficient

---
 source/mir/bignum/fp.d    | 16 +++++++++++++-
 source/mir/math/numeric.d | 45 +++++++++++++--------------------------
 2 files changed, 30 insertions(+), 31 deletions(-)

diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 97731b3f..381187ce 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -277,7 +277,8 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
     }
 
     ///
-    ref Fp opOpAssign(string op : "*")(Fp rhs) nothrow scope return
+    ref Fp opOpAssign(string op)(Fp rhs) nothrow scope return
+        if (op == "*" || op == "/")
     {
         this = this.opBinary!op(rhs);
         return this;
@@ -305,6 +306,19 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
         assert(fp.coefficient == UInt!128.fromHexString("c6841dd302415d785373ab6d93712988"));
     }
 
+    /// Uses approximate division for now
+    /// TODO: use full precision division for void when Fp division is ready
+    Fp opBinary(string op : "/")(Fp rhs) nothrow const
+    {
+        Fp a = this;
+        alias b = rhs;
+        auto exponent = a.exponent - b.exponent;
+        a.exponent = b.exponent = -coefficientSize;
+        auto ret = Fp(cast(real) a / cast(real) b);
+        ret.exponent += exponent;
+        return ret;
+    }
+
     ///
     T opCast(T)() nothrow const
         if (is(Unqual!T == bool))
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index 0d64c8ae..a8f8f637 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -591,37 +591,22 @@ Params:
 Returns: n choose k
 Complexity: O(min(k, n - k))
 +/
-template binomialCoefficient(DivisionType = void)
-    if (is(DivisionType == void) || __traits(isFloating, DivisionType))
+auto binomialCoefficient
+    (size_t coefficientSize = 128, Exp = sizediff_t)
+    (ulong n, uint k)
+    if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
+    in (k <= n)
 {
-    auto binomialCoefficient
-        (size_t coefficientSize = 128, Exp = sizediff_t)
-        (ulong n, uint k)
-        if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
-        in (k <= n)
-    {
-        import mir.bignum.fp: Fp;
-    
-        alias R = Fp!(coefficientSize, Exp);
+    import mir.bignum.fp: Fp;
 
-        // TODO: For DivisionType == void
-        //       use full precision division for void when Fp division is ready
-        static if (is(DivisionType == void))
-            alias T = real;
-        else
-            alias T = DivisionType;
-
-        if (k > n - k)
-            k = cast(uint)(n - k);
-
-        auto a = factorial!(coefficientSize, Exp)(k, n - k + 1);
-        auto b = factorial!(coefficientSize, Exp)(k);
-        auto exponent = a.exponent - b.exponent;
-        a.exponent = b.exponent = -coefficientSize;
-        auto ret = R(cast(T) a / cast(T) b);
-        ret.exponent += exponent;
-        return ret;
-    }
+    alias R = Fp!(coefficientSize, Exp);
+
+    if (k > n - k)
+        k = cast(uint)(n - k);
+
+    auto a = factorial!(coefficientSize, Exp)(k, n - k + 1);
+    auto b = factorial!(coefficientSize, Exp)(k);
+    return a / b;
 }
 
 ///
@@ -632,7 +617,7 @@ unittest
     import mir.bignum.fp: Fp;
     import mir.math.common: approxEqual;
 
-    static assert(is(typeof(binomialCoefficient!double(30, 18)) == Fp!128), typeof(binomialCoefficient!double(30, 18)).stringof);
+    static assert(is(typeof(binomialCoefficient(30, 18)) == Fp!128), typeof(binomialCoefficient(30, 18)).stringof);
     static assert(cast(double) binomialCoefficient(30, 18) == 86493225);
 
     assert(approxEqual(cast(double) binomialCoefficient(100, 40), 1.374623414580281150126736972e+28));

From cdad5d793518061d2d5bbe1d9c1fe912499eef6e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 25 May 2022 23:28:10 +0400
Subject: [PATCH 374/573] ditto

---
 source/mir/math/numeric.d | 2 --
 1 file changed, 2 deletions(-)

diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index a8f8f637..d0eff547 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -599,8 +599,6 @@ auto binomialCoefficient
 {
     import mir.bignum.fp: Fp;
 
-    alias R = Fp!(coefficientSize, Exp);
-
     if (k > n - k)
         k = cast(uint)(n - k);
 

From 42ddd592e080f90d0a79bf3136ef500bd0aa733e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 10:57:22 +0400
Subject: [PATCH 375/573] simplify bignum generic types

---
 source/mir/bignum/decimal.d                  | 16 ++++++++--------
 source/mir/bignum/fixed.d                    |  2 +-
 source/mir/bignum/fp.d                       | 18 +++++++++---------
 source/mir/bignum/integer.d                  |  8 ++++----
 source/mir/bignum/internal/ryu/generic_128.d | 18 +++++++++---------
 source/mir/bignum/low_level_view.d           |  2 +-
 source/mir/math/numeric.d                    |  4 ++--
 7 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 4585f136..b3ef307c 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -12,7 +12,7 @@ import std.traits: isSomeChar;
 public import mir.bignum.low_level_view: DecimalExponentKey;
 import mir.bignum.low_level_view: ceilLog10Exp2;
 
-private enum expBufferLength = 2 + ceilLog10Exp2(size_t.sizeof * 8);
+private enum expBufferLength = 2 + ceilLog10Exp2(ulong.sizeof * 8);
 private static immutable C[9] zerosImpl(C) = "0.00000.0";
 
 /++
@@ -21,7 +21,7 @@ Params:
     maxSize64 = count of 64bit words in coefficient
 +/
 @serdeScoped @serdeProxy!(const(char)[])
-struct Decimal(size_t maxSize64)
+struct Decimal(uint maxSize64)
     if (maxSize64 && maxSize64 <= ushort.max)
 {
     import mir.format: NumericSpec;
@@ -30,7 +30,7 @@ struct Decimal(size_t maxSize64)
     import std.traits: isMutable, isFloatingPoint;
 
     ///
-    sizediff_t exponent;
+    long exponent;
     ///
     BigInt!maxSize64 coefficient;
 
@@ -108,7 +108,7 @@ struct Decimal(size_t maxSize64)
     }
 
     ///
-    ref opAssign(size_t rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return
+    ref opAssign(uint rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return
         if (rhsMaxSize64 < maxSize64)
     {
         this.exponent = rhs.exponent;
@@ -737,7 +737,7 @@ struct Decimal(size_t maxSize64)
 
         C[1] sign = coefficient.sign ? "-" : "+";
         bool addSign = coefficient.sign || spec.plus;
-        sizediff_t s = this.exponent + coefficientLength;
+        long s = this.exponent + coefficientLength;
 
         alias zeros = zerosImpl!C;
 
@@ -840,7 +840,7 @@ struct Decimal(size_t maxSize64)
 
         assert(coefficientLength);
 
-        sizediff_t exponent = s - 1;
+        long exponent = s - 1;
 
         if (coefficientLength > 1)
         {
@@ -854,7 +854,7 @@ struct Decimal(size_t maxSize64)
 
         import mir.format_impl: printSignedToTail;
 
-        static if (sizediff_t.sizeof == 8)
+        static if (exponent.sizeof == 8)
             enum N = 21;
         else
             enum N = 11;
@@ -1081,7 +1081,7 @@ unittest
 
 deprecated("use decimal.fromStringImpl insteade")
 @trusted @nogc pure nothrow
-bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
+bool parseDecimal(uint maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
     if (isSomeChar!C)
 {
     return decimal.fromStringImpl(str, key);
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 5be88591..d930bf51 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -334,7 +334,7 @@ struct UInt(size_t size)
     }
 
     /// ditto
-    bool opOpAssign(string op, size_t rsize)(UInt!rsize rhs, bool overflow = false)
+    bool opOpAssign(string op, uint rsize)(UInt!rsize rhs, bool overflow = false)
         @safe pure nothrow @nogc scope
         if ((op == "+" || op == "-") && rsize < size)
     {
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 381187ce..a732691c 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -8,7 +8,7 @@ import std.traits;
 import mir.bitop;
 import mir.utility;
 
-package enum half(size_t hs) = (){
+package enum half(uint hs) = (){
     import mir.bignum.fixed: UInt;
     UInt!hs ret; ret.signBit = true; return ret;
 }();
@@ -21,8 +21,8 @@ Params:
 
 Note: the implementation doesn't support NaN and Infinity values.
 +/
-struct Fp(size_t coefficientSize, Exp = sizediff_t)
-    if ((is(Exp == int) || is(Exp == long)) && coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
+struct Fp(uint coefficientSize, Exp = long)
+    if ((is(Exp == int) || is(Exp == long)) && coefficientSize % (uint.sizeof * 8) == 0 && coefficientSize >= (uint.sizeof * 8))
 {
     import mir.bignum.fixed: UInt;
 
@@ -161,7 +161,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
 
     /++
     +/
-    this(size_t size)(UInt!size integer, bool normalizedInteger = false)
+    this(uint size)(UInt!size integer, bool normalizedInteger = false)
         nothrow
     {
         import mir.bignum.fixed: UInt;
@@ -313,7 +313,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
         Fp a = this;
         alias b = rhs;
         auto exponent = a.exponent - b.exponent;
-        a.exponent = b.exponent = -coefficientSize;
+        a.exponent = b.exponent = -Exp(coefficientSize);
         auto ret = Fp(cast(real) a / cast(real) b);
         ret.exponent += exponent;
         return ret;
@@ -460,7 +460,7 @@ struct Fp(size_t coefficientSize, Exp = sizediff_t)
 }
 
 ///
-Fp!(coefficientizeA + coefficientizeB) extendedMul(size_t coefficientizeA, size_t coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
+Fp!(coefficientizeA + coefficientizeB) extendedMul(uint coefficientizeA, size_t coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
     @safe pure nothrow @nogc
 {
     import mir.bignum.fixed: extendedMul;
@@ -480,11 +480,11 @@ template fp_log2(T)
     if (__traits(isFloating, T))
 {
     ///
-    T fp_log2(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
+    T fp_log2(uint coefficientSize, Exp = long)(Fp!(coefficientSize, Exp) x)
     {
         import mir.math.common: log2;
         auto exponent = x.exponent + coefficientSize;
-        x.exponent = -coefficientSize;
+        x.exponent = -Exp(coefficientSize);
         return log2(cast(T)x) + exponent;
     }
 }
@@ -506,7 +506,7 @@ template fp_log(T)
     if (__traits(isFloating, T))
 {
     ///
-    T fp_log(size_t coefficientSize, Exp = sizediff_t)(Fp!(coefficientSize, Exp) x)
+    T fp_log(uint coefficientSize, Exp = long)(Fp!(coefficientSize, Exp) x)
     {
         import mir.math.constant: LN2;
         return T(LN2) * fp_log2!T(x);
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 38515c2b..83cfd2a1 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -15,7 +15,7 @@ Params:
     maxSize64 = count of 64bit words in coefficient
 +/
 @serdeScoped @serdeProxy!(const(char)[])
-struct BigInt(size_t maxSize64)
+struct BigInt(uint maxSize64)
     if (maxSize64 && maxSize64 <= ushort.max)
 {
     import mir.bignum.low_level_view;
@@ -29,13 +29,13 @@ struct BigInt(size_t maxSize64)
     size_t[ulong.sizeof / size_t.sizeof * maxSize64] data = void;
 
     ///
-    this(size_t size)(UInt!size fixedInt)
+    this(uint size)(UInt!size fixedInt)
     {
         this(fixedInt.data);
     }
 
     ///
-    this(size_t N)(size_t[N] data)
+    this(uint N)(size_t[N] data)
         if (N <= this.data.length)
     {
         sign = false;
@@ -115,7 +115,7 @@ struct BigInt(size_t maxSize64)
     }
 
     ///
-    ref opAssign(size_t rhsMaxSize64)(auto ref scope const BigInt!rhsMaxSize64 rhs) return
+    ref opAssign(uint rhsMaxSize64)(auto ref scope const BigInt!rhsMaxSize64 rhs) return
         if (rhsMaxSize64 < maxSize64)
     {
         this.sign = rhs.sign;
diff --git a/source/mir/bignum/internal/ryu/generic_128.d b/source/mir/bignum/internal/ryu/generic_128.d
index db870bfd..cb71d98f 100644
--- a/source/mir/bignum/internal/ryu/generic_128.d
+++ b/source/mir/bignum/internal/ryu/generic_128.d
@@ -142,7 +142,7 @@ else
 
 enum baseDiv5 = UInt!128([0x3333333333333333, 0x3333333333333333]);
 
-uint divRem5(size_t size)(ref UInt!size value)
+uint divRem5(uint size)(ref UInt!size value)
 {
     auto q = div5(value);
     auto r = cast(uint)(value - q * 5);
@@ -150,7 +150,7 @@ uint divRem5(size_t size)(ref UInt!size value)
     return r;
 }
 
-uint divRem10(size_t size)(ref UInt!size value)
+uint divRem10(uint size)(ref UInt!size value)
 {
     auto q = div10(value);
     auto r = cast(uint)(value - q * 10);
@@ -158,28 +158,28 @@ uint divRem10(size_t size)(ref UInt!size value)
     return r;
 }
 
-uint rem5(size_t size)(UInt!size value)
+uint rem5(uint size)(UInt!size value)
 {
     return divRem5(value);
 }
 
-uint rem10(size_t size)(UInt!size value)
+uint rem10(uint size)(UInt!size value)
 {
     return divRem10(value);
 }
 
-UInt!size div5(size_t size)(UInt!size value)
+UInt!size div5(uint size)(UInt!size value)
 {
     return extendedMulHigh(value, fiveReciprocal.toSize!size) >> 2;
 }
 
-UInt!size div10(size_t size)(UInt!size value)
+UInt!size div10(uint size)(UInt!size value)
 {
     return extendedMulHigh(value, fiveReciprocal.toSize!size) >> 3;
 }
 
 // Returns true if value is divisible by 5^p.
-bool multipleOfPowerOf5(size_t size)(UInt!size value, const uint p)
+bool multipleOfPowerOf5(uint size)(UInt!size value, const uint p)
 {
     enum fiveReciprocal = .fiveReciprocal.toSize!size;
     enum baseDiv5 = .baseDiv5.toSize!size;
@@ -206,7 +206,7 @@ version(mir_bignum_test) unittest
 }
 
 // Returns true if value is divisible by 2^p.
-bool multipleOfPowerOf2(size_t size)(const UInt!size value, const uint p)
+bool multipleOfPowerOf2(uint size)(const UInt!size value, const uint p)
 {
     version(LDC) pragma(inline, true);
     return (value & ((UInt!size(1) << p) - 1)) == 0;
@@ -224,7 +224,7 @@ version(mir_bignum_test) unittest
     assert(multipleOfPowerOf2(UInt!128(8), 4) == false);
 }
 
-UInt!size mulShift(size_t size)(const UInt!size m, const UInt!256 mul, const uint j)
+UInt!size mulShift(uint size)(const UInt!size m, const UInt!256 mul, const uint j)
 {
     version(LDC) pragma(inline, true);
     assert(j > 128);
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index a29f33ce..b9ab96e2 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2460,7 +2460,7 @@ enum DecimalExponentKey
 
 /++
 +/
-struct DecimalView(W, WordEndian endian = TargetEndian, Exp = sizediff_t)
+struct DecimalView(W, WordEndian endian = TargetEndian, Exp = long)
     if (isUnsigned!W)
 {
     ///
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index d0eff547..c2547a0a 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -523,7 +523,7 @@ Returns: `(count + start - 1)! / (start - 1)!`
 Complexity: O(count)
 +/
 auto factorial
-    (size_t coefficientSize = 128, Exp = sizediff_t)
+    (uint coefficientSize = 128, Exp = long)
     (ulong count, ulong start = 1)
     if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
     in (start)
@@ -592,7 +592,7 @@ Returns: n choose k
 Complexity: O(min(k, n - k))
 +/
 auto binomialCoefficient
-    (size_t coefficientSize = 128, Exp = sizediff_t)
+    (uint coefficientSize = 128, Exp = long)
     (ulong n, uint k)
     if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
     in (k <= n)

From 88d99b60e783167356e06683b1ce6d1a797ea089 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 10:59:53 +0400
Subject: [PATCH 376/573] fixup

---
 source/mir/math/numeric.d | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index c2547a0a..f374dd7f 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -571,7 +571,8 @@ unittest
 
     static assert(is(typeof(factorial(33)) == Fp!128));
     static assert(is(typeof(factorial!256(33)) == Fp!256));
-    static assert(cast(double) factorial(33) == 8.68331761881188649551819440128e+36);
+    static immutable double f33 = 8.68331761881188649551819440128e+36;
+    static assert(cast(double) factorial(33) == f33);
 
     assert(cast(double) factorial(0) == 1);
     assert(cast(double) factorial(0, 100) == 1);
@@ -597,11 +598,8 @@ auto binomialCoefficient
     if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
     in (k <= n)
 {
-    import mir.bignum.fp: Fp;
-
     if (k > n - k)
         k = cast(uint)(n - k);
-
     auto a = factorial!(coefficientSize, Exp)(k, n - k + 1);
     auto b = factorial!(coefficientSize, Exp)(k);
     return a / b;

From 69e7b6ba437fc8a30299ad59aa5eb6b593b07524 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 16:36:21 +0400
Subject: [PATCH 377/573] extend Decimal API

---
 source/mir/bignum/decimal.d | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index b3ef307c..a3d9d29c 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -967,15 +967,21 @@ struct Decimal(uint maxSize64)
     }
 
     ///
-    bool isNaN() const @property
+    bool isNaN() scope const @property
     {
         return exponent == exponent.max && coefficient.length;
     }
 
     ///
-    bool isInfinity() const @property
+    bool isInfinity() scope const @property
     {
-        return exponent == exponent.max && !coefficient.length;
+        return exponent == exponent.max && coefficient.length == 0;
+    }
+
+    ///
+    bool isSpecial() scope const @property
+    {
+        return exponent == exponent.max;
     }
 
     ///

From 524ed5e2358596b45ef2732fd86bdda381d7fcf9 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 16:36:55 +0400
Subject: [PATCH 378/573] extend fixed API

---
 source/mir/bignum/fixed.d | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index d930bf51..7ebacd26 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -15,7 +15,7 @@ Params:
     size = size in bits
 +/
 struct UInt(size_t size)
-    if (size % (size_t.sizeof * 8) == 0 && size >= (size_t.sizeof * 8))
+    if (size % (size_t.sizeof * 8) == 0 && size >= size_t.sizeof * 8)
 {
     import mir.bignum.fixed: UInt;
     /++
@@ -25,7 +25,7 @@ struct UInt(size_t size)
 
     ///
     this(size_t N)(auto ref const size_t[N] data)
-        if (N <= this.data.length)
+        if (N && N <= this.data.length)
     {
         version(LittleEndian)
             this.data[0 .. N] = data;
@@ -306,6 +306,15 @@ struct UInt(size_t size)
         return this;
     }
 
+    /++
+    +/
+    ref UInt!size opAssign(uint rhsSize)(UInt!rhsSize rhs) scope return
+        @safe pure nothrow @nogc
+    {
+        this.data = UInt!size(rhs).data;
+        return this;
+    }
+
     /++
     `bool overflow = a += b ` and `bool overflow = a -= b` operations.
     +/

From c883b7c56cd90d8eaedaac01447cfe09a251cf2a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 16:37:29 +0400
Subject: [PATCH 379/573] simplify DecimalView

---
 source/mir/bignum/low_level_view.d | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index b9ab96e2..9d84ffde 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2460,13 +2460,13 @@ enum DecimalExponentKey
 
 /++
 +/
-struct DecimalView(W, WordEndian endian = TargetEndian, Exp = long)
+struct DecimalView(W, WordEndian endian = TargetEndian)
     if (isUnsigned!W)
 {
     ///
     bool sign;
     ///
-    Exp exponent;
+    long exponent;
     ///
     BigUIntView!(W, endian) coefficient;
 
@@ -2745,7 +2745,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = long)
     }
 
     ///
-    DecimalView!(const W, endian, Exp) lightConst()()
+    DecimalView!(const W, endian) lightConst()()
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(sign, exponent, coefficient.lightConst);
@@ -2799,7 +2799,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = long)
 
         static if (T.mant_dig < 64)
         {
-            Fp!64 load(Exp e)
+            Fp!64 load(long e)
             {
                 auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
                 auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
@@ -2840,7 +2840,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = long)
             enum P = 1 << S;
             static assert(min_p10_e <= -P);
             static assert(max_p10_e >= P);
-            Fp!128 load(Exp e)
+            Fp!128 load(long e)
             {
                 auto idx = cast(sizediff_t)e - min_p10_e;
                 ulong h = p10_coefficients[idx][0];
@@ -2857,14 +2857,14 @@ struct DecimalView(W, WordEndian endian = TargetEndian, Exp = long)
 
             {
                 auto expSign = exponent < 0;
-                Unsigned!Exp exp = exponent;
+                ulong exp = exponent;
                 exp = expSign ? -exp : exp;
                 if (exp >= 5000)
                 {
                     ret = expSign ? 0 : T.infinity;
                     goto R;
                 }
-                Exp index = exp & 0x1FF;
+                long index = exp & 0x1FF;
                 bool gotoAlgoR;
                 auto c = load(expSign ? -index : index);
                 {
@@ -3301,17 +3301,17 @@ unittest
 
 /++
 +/
-struct BinaryView(W, WordEndian endian = TargetEndian, Exp = int)
+struct BinaryView(W, WordEndian endian = TargetEndian)
 {
     ///
     bool sign;
     ///
-    Exp exponent;
+    long exponent;
     ///
     BigUIntView!(W, endian) coefficient;
 
     ///
-    DecimalView!(const W, endian, Exp) lightConst()()
+    DecimalView!(const W, endian) lightConst()()
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(sign, exponent, coefficient.lightConst);

From dc9d236cebd039dae74cb074cddd181b9dcb7754 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 17:15:03 +0400
Subject: [PATCH 380/573] remove unused imports

---
 source/mir/bignum/decimal.d   | 1 -
 source/mir/ndslice/topology.d | 3 ---
 source/mir/rc/array.d         | 2 --
 source/mir/string_map.d       | 1 -
 4 files changed, 7 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index a3d9d29c..f5e36418 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -1014,7 +1014,6 @@ struct Decimal(uint maxSize64)
     ///
     version(mir_bignum_test) @safe pure @nogc unittest
     {
-        import std.stdio;
         auto a = Decimal!1("777.7");
         auto b = Decimal!1("777");
         import mir.format;
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 05ddde30..b987ca99 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -1074,7 +1074,6 @@ Slice!(Iterator, 1, Universal)
     //->
     // | 1 2 |
     static immutable c = [1, 2];
-    import std.stdio;
     assert(iota(2, 2).antidiagonal == c);
 }
 
@@ -1969,7 +1968,6 @@ auto linspace(T, size_t N)(size_t[N] lengths, T[2][N] intervals...)
 version(mir_ndslice_test) unittest
 {
     import mir.ndslice;
-    // import std.stdio: writefln;
 
     enum fmt = "%(%(%.2f %)\n%)\n";
 
@@ -4321,7 +4319,6 @@ version(mir_ndslice_test) unittest
     // 4 5  6  7
     // 8 9 10 11
     auto s = iota(3, 4);
-    import std.stdio;
     assert(iota(3, 4).byDim!0.diff == [
         [4, 4, 4, 4],
         [4, 4, 4, 4]]);
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 7e54d5dc..b12b8d67 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -799,8 +799,6 @@ unittest
 version(mir_test)
 @safe unittest
 {
-    import core.stdc.stdio;
-
     struct S
     {
         uint s;
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 42576bce..8819b1fb 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -683,7 +683,6 @@ struct StringMap(T, U = uint)
     +/
     ref T require()(string key, lazy T value = T.init)
     {
-        import std.stdio;
         size_t index;
         if (_expect(!implementation, false))
         {

From 6ddfb2f2eb83ba6e77ef5690e9b2af4ad6640a34 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 17:17:35 +0400
Subject: [PATCH 381/573] rework Fp, sumOfLog2s, prod. deprecate ProdAcc

---
 source/mir/bignum/fp.d    | 318 +++++++++++++++++++++++++++-----------
 source/mir/math/numeric.d | 187 +++++++++-------------
 2 files changed, 302 insertions(+), 203 deletions(-)

diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index a732691c..d54e5dd5 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -4,9 +4,9 @@ Note:
 +/
 module mir.bignum.fp;
 
-import std.traits;
 import mir.bitop;
 import mir.utility;
+import std.traits;
 
 package enum half(uint hs) = (){
     import mir.bignum.fixed: UInt;
@@ -17,23 +17,23 @@ package enum half(uint hs) = (){
 Software floating point number.
 
 Params:
-    coefficientSize = coefficient size in bits
+    size = coefficient size in bits
 
 Note: the implementation doesn't support NaN and Infinity values.
 +/
-struct Fp(uint coefficientSize, Exp = long)
-    if ((is(Exp == int) || is(Exp == long)) && coefficientSize % (uint.sizeof * 8) == 0 && coefficientSize >= (uint.sizeof * 8))
+struct Fp(uint size)
+    if (size % (uint.sizeof * 8) == 0 && size >= (uint.sizeof * 8))
 {
     import mir.bignum.fixed: UInt;
 
     bool sign;
-    Exp exponent;
-    UInt!coefficientSize coefficient;
+    long exponent;
+    UInt!size coefficient;
 
     /++
     +/
     nothrow
-    this(bool sign, Exp exponent, UInt!coefficientSize normalizedCoefficient)
+    this(bool sign, long exponent, UInt!size normalizedCoefficient)
     {
         this.coefficient = normalizedCoefficient;
         this.exponent = exponent;
@@ -48,30 +48,48 @@ struct Fp(uint coefficientSize, Exp = long)
     +/
     this(T)(const T value, bool normalize = true)
         @safe pure nothrow @nogc
-        if (isFloatingPoint!T && T.mant_dig <= coefficientSize)
+        if (isFloatingPoint!T && T.mant_dig <= size)
     {
         import mir.math.common : fabs;
         import mir.math.ieee : frexp, signbit, ldexp;
-        assert(value == value);
-        assert(value.fabs < T.infinity);
         this.sign = value.signbit != 0;
         if (value == 0)
             return;
         T x = value.fabs;
+        if (_expect(!(x < T.infinity), false))
+        {
+            this.exponent = this.exponent.max;
+            this.coefficient = x != T.infinity;
+            return;
+        }
         int exp;
         {
             enum scale = T(2) ^^ T.mant_dig;
             x = frexp(x, exp) * scale;
-            exp -= T.mant_dig;
         }
+        auto dd = this.coefficient.view.leastSignificantFirst;
         static if (T.mant_dig < 64)
         {
-            this.coefficient = UInt!coefficientSize(cast(ulong)cast(long)x);
+            auto xx = cast(ulong)cast(long)x;
+            if (normalize)
+            {
+                auto shift = ctlz(xx);
+                exp -= shift + T.mant_dig + size - 64;
+                xx <<= shift;
+            }
+            dd[normalize ? $ - 1 : 0] = xx;
         }
         else
         static if (T.mant_dig == 64)
         {
-            this.coefficient = UInt!coefficientSize(cast(ulong)x);
+            auto xx = cast(ulong)x;
+            if (normalize)
+            {
+                auto shift = ctlz(xx);
+                exp -= shift + T.mant_dig + size - 64;
+                xx <<= shift;
+            }
+            dd[normalize ? $ - 1 : 0] = xx;
         }
         else
         {
@@ -83,16 +101,21 @@ struct Fp(uint coefficientSize, Exp = long)
                 --high;
             x -= high;
             x *= scale;
-            this.coefficient = (UInt!coefficientSize(ulong(high)) << 64) | cast(ulong)x;
-        }
-        if (normalize)
-        {
-            auto shift = cast(int)this.coefficient.ctlz;
-            exp -= shift;
-            this.coefficient <<= shift;
+            auto most = ulong(high);
+            auto least = cast(ulong)x;
+
+            dd[normalize ? $ - 1 : 1] = most;
+            dd[normalize ? $ - 2 : 0] = least;
+            if (normalize)
+            {
+                auto shift = most ? ctlz(most) : ctlz(least) + 64;
+                exp -= shift + T.mant_dig + size - 64 * (1 + (T.mant_dig > 64));
+                this.coefficient <<= shift;
+            }
         }
-        else
+        if (!normalize)
         {
+            exp -= T.mant_dig;
             int shift = T.min_exp - T.mant_dig - exp;
             if (shift > 0)
             {
@@ -103,7 +126,7 @@ struct Fp(uint coefficientSize, Exp = long)
         this.exponent = exp;
     }
 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc nothrow
@@ -145,47 +168,58 @@ struct Fp(uint coefficientSize, Exp = long)
         // subnormals
         static assert(cast(real) Fp!64(real.min_normal / 2, false) == real.min_normal / 2);
         static assert(cast(real) Fp!64(real.min_normal * real.epsilon, false) == real.min_normal * real.epsilon);
-    }
 
-    static if (coefficientSize == 128)
-    /// Without normalization
-    version(mir_bignum_test)
-    @safe pure @nogc nothrow
-    unittest
-    {
-        auto f = Fp!64(-33.0 * 2.0 ^^ -10, false);
-        assert(f.sign);
-        assert(f.exponent == -10 - (double.mant_dig - 6));
-        assert(f.coefficient == 33UL << (double.mant_dig - 6));
+        import mir.bignum.fixed: UInt;
+
+        assert(cast(double)Fp!128(+double.infinity) == +double.infinity);
+        assert(cast(double)Fp!128(-double.infinity) == -double.infinity);
+
+        import mir.math.ieee : signbit;
+        auto r = cast(double)Fp!128(-double.nan);
+        assert(r != r && r.signbit);
     }
 
+    // static if (size == 128)
+    // /// Without normalization
+    // version(mir_bignum_test)
+    // @safe pure @nogc nothrow
+    // unittest
+    // {
+    //     auto f = Fp!64(-33.0 * 2.0 ^^ -10, false);
+    //     assert(f.sign);
+    //     assert(f.exponent == -10 - (double.mant_dig - 6));
+    //     assert(f.coefficient == 33UL << (double.mant_dig - 6));
+    // }
+
     /++
     +/
-    this(uint size)(UInt!size integer, bool normalizedInteger = false)
+    this(uint isize)(UInt!isize integer, bool normalizedInteger = false)
         nothrow
     {
         import mir.bignum.fixed: UInt;
-        static if (size < coefficientSize)
+        static if (isize < size)
         {
             if (normalizedInteger)
             {
-                this(false, Exp(size) - coefficientSize, integer.rightExtend!(coefficientSize - size));
+                this(false, long(isize) - size, integer.rightExtend!(size - isize));
             }
             else
             {
-                this(integer.toSize!coefficientSize, false);
+                this(integer.toSize!size, false);
             }
         }
         else
         {
-            this.exponent = size - coefficientSize;
+            if (integer == integer.init)
+                return;
+            this.exponent = isize - size;
             if (!normalizedInteger)
             {
                 auto c = integer.ctlz;
                 integer <<= c;
                 this.exponent -= c;
             }
-            static if (size == coefficientSize)
+            static if (isize == size)
             {
                 coefficient = integer;
             }
@@ -196,13 +230,13 @@ struct Fp(uint coefficientSize, Exp = long)
                     coefficient.data = integer.data[$ - N .. $];
                 else
                     coefficient.data = integer.data[0 .. N];
-                enum tailSize = size - coefficientSize;
+                enum tailSize = isize - size;
                 auto cr = integer.toSize!tailSize.opCmp(half!tailSize);
                 if (cr > 0 || cr == 0 && coefficient.bt(0))
                 {
                     if (auto overflow = coefficient += 1)
                     {
-                        coefficient = half!coefficientSize;
+                        coefficient = half!size;
                         exponent++;
                     }
                 }
@@ -210,7 +244,7 @@ struct Fp(uint coefficientSize, Exp = long)
         }
     }
 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc
@@ -276,6 +310,50 @@ struct Fp(uint coefficientSize, Exp = long)
         this.sign = !(value >= 0);
     }
 
+    ///
+    this(int value)
+    {
+        this(long(value));
+    }
+
+    ///
+    this(uint value)
+    {
+        this(ulong(value));
+    }
+
+    ///
+    bool isNaN() scope const @property
+    {
+        return this.exponent == this.exponent.max && this.coefficient != this.coefficient.init;
+    }
+
+    ///
+    bool isInfinity() scope const @property
+    {
+        return this.exponent == this.exponent.max && this.coefficient == coefficient.init;
+    }
+
+    ///
+    bool isSpecial() scope const @property
+    {
+        return this.exponent == this.exponent.max;
+    }
+
+    ///
+    bool opEquals(const Fp rhs) scope const
+    {
+        if (this.exponent != rhs.exponent)
+            return false;
+        if (this.coefficient != rhs.coefficient)
+            return false;
+        if (this.coefficient == 0)
+            return !this.isSpecial || this.sign == rhs.sign;
+        if (this.sign != rhs.sign)
+            return false;
+        return !this.isSpecial;
+    }
+
     ///
     ref Fp opOpAssign(string op)(Fp rhs) nothrow scope return
         if (op == "*" || op == "/")
@@ -285,12 +363,12 @@ struct Fp(uint coefficientSize, Exp = long)
     }
 
     ///
-    Fp opBinary(string op : "*")(Fp rhs) nothrow const
+    Fp!(max(size, rhsSize)) opBinary(string op : "*", uint rhsSize)(Fp!rhsSize rhs) nothrow const
     {
-        return cast(Fp) .extendedMul(this, rhs);
+        return cast(Fp) .extendedMul!(size, rhsSize)(this, rhs);
     }
 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc
@@ -300,7 +378,7 @@ struct Fp(uint coefficientSize, Exp = long)
 
         auto a = Fp!128(0, -13, UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d"));
         auto b = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314"));
-        auto fp = a * b;
+        auto fp = a.opBinary!"*"(b);
         assert(fp.sign);
         assert(fp.exponent == 100 - 13 + 128);
         assert(fp.coefficient == UInt!128.fromHexString("c6841dd302415d785373ab6d93712988"));
@@ -308,13 +386,13 @@ struct Fp(uint coefficientSize, Exp = long)
 
     /// Uses approximate division for now
     /// TODO: use full precision division for void when Fp division is ready
-    Fp opBinary(string op : "/")(Fp rhs) nothrow const
+    Fp!(max(size, rhsSize)) opBinary(string op : "/", uint rhsSize)(Fp!rhsSize rhs) nothrow const
     {
         Fp a = this;
         alias b = rhs;
         auto exponent = a.exponent - b.exponent;
-        a.exponent = b.exponent = -Exp(coefficientSize);
-        auto ret = Fp(cast(real) a / cast(real) b);
+        a.exponent = b.exponent = -long(size);
+        auto ret = typeof(return)(cast(real) a / cast(real) b);
         ret.exponent += exponent;
         return ret;
     }
@@ -328,48 +406,55 @@ struct Fp(uint coefficientSize, Exp = long)
     
     ///
     T opCast(T, bool noHalf = false)() nothrow const
-        if (isFloatingPoint!T)
+        if (is(T == float) || is(T == double) || is(T == real))
     {
         import mir.math.ieee: ldexp;
-        auto exp = cast()exponent;
-        static if (coefficientSize == 32)
+        if (_expect(this.isSpecial, false))
+        {
+            T ret = this.coefficient ? T.nan : T.infinity;
+            if (this.sign)
+                ret = -ret;
+            return ret;
+        }
+        auto exp = cast()this.exponent;
+        static if (size == 32)
         {
-            Unqual!T c = cast(uint) coefficient;
+            T c = cast(uint) coefficient;
         }
         else
-        static if (coefficientSize == 64)
+        static if (size == 64)
         {
-            Unqual!T c = cast(ulong) coefficient;
+            T c = cast(ulong) coefficient;
         }
         else
         {
-            enum shift = coefficientSize - T.mant_dig;
-            enum rMask = (UInt!coefficientSize(1) << shift) - UInt!coefficientSize(1);
-            enum rHalf = UInt!coefficientSize(1) << (shift - 1);
-            enum rInc = UInt!coefficientSize(1) << shift;
-            UInt!coefficientSize adC = coefficient;
+            enum shift = size - T.mant_dig;
+            enum rMask = (UInt!size(1) << shift) - UInt!size(1);
+            enum rHalf = UInt!size(1) << (shift - 1);
+            enum rInc = UInt!size(1) << shift;
+            UInt!size adC = this.coefficient;
             static if (!noHalf)
             {
-                auto cr = (coefficient & rMask).opCmp(rHalf);
-                if ((cr > 0) | (cr == 0) & coefficient.bt(shift))
+                auto cr = (this.coefficient & rMask).opCmp(rHalf);
+                if ((cr > 0) | (cr == 0) & this.coefficient.bt(shift))
                 {
                     if (auto overflow = adC += rInc)
                     {
-                        adC = half!coefficientSize;
+                        adC = half!size;
                         exp++;
                     }
                 }
             }
             adC >>= shift;
             exp += shift;
-            Unqual!T c = cast(ulong) adC;
+            T c = cast(ulong) adC;
             static if (T.mant_dig > 64) //
             {
                 static assert (T.mant_dig <= 128);
                 c += ldexp(cast(T) cast(ulong) (adC >> 64), 64);
             }
         }
-        if (sign)
+        if (this.sign)
             c = -c;
         static if (exp.sizeof > int.sizeof)
         {
@@ -379,7 +464,7 @@ struct Fp(uint coefficientSize, Exp = long)
         return ldexp(c, cast(int)exp);
     }
 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc
@@ -388,9 +473,17 @@ struct Fp(uint coefficientSize, Exp = long)
         import mir.bignum.fixed: UInt;
         auto fp = Fp!128(1, 100, UInt!128.fromHexString("e3251bacb112cb8b71ad3f85a970a314"));
         assert(cast(double)fp == -0xE3251BACB112C8p+172);
+
+        fp = Fp!128(1, long.max, UInt!128.init);
+        assert(cast(double)fp == -double.infinity);
+
+        import mir.math.ieee : signbit;
+        fp = Fp!128(1, long.max, UInt!128(123));
+        auto r = cast(double)fp;
+        assert(r != r && r.signbit);
     }
 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc
@@ -402,7 +495,7 @@ struct Fp(uint coefficientSize, Exp = long)
             assert(cast(real)fp == -0xe3251bacb112cb8bp+164L);
     }
 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc
@@ -425,7 +518,7 @@ struct Fp(uint coefficientSize, Exp = long)
 // -0xe.3251bacb112cb8bp+160 = 
 // -0x1.c64a37596225ap+163 = 
 // -0xe.3251bacb112cb8bp+160 = 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc
@@ -439,14 +532,26 @@ struct Fp(uint coefficientSize, Exp = long)
 
     ///
     T opCast(T : Fp!newCoefficientSize, size_t newCoefficientSize)() nothrow const
+        if (newCoefficientSize != size)
     {
-        auto ret = Fp!newCoefficientSize(coefficient, true);
-        ret.exponent += exponent;
-        ret.sign = sign;
+        Fp!newCoefficientSize ret = void;
+        if (_expect(isSpecial, false))
+        {
+            ret.exponent = ret.exponent.max;
+            ret.coefficient = !!this.coefficient;
+        }
+        else
+        {
+            ret = Fp!newCoefficientSize(this.coefficient, true);
+            // TODO: exponent overflow / underflow
+            // with care of special values
+            ret.exponent = ret.exponent + this.exponent;
+        }
+        ret.sign = this.sign;
         return ret;
     }
 
-    static if (coefficientSize == 128)
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     @safe pure @nogc
@@ -456,35 +561,74 @@ struct Fp(uint coefficientSize, Exp = long)
         auto fp = cast(Fp!64) Fp!128(UInt!128.fromHexString("afbbfae3cd0aff2784a1de7022b0029d"));
         assert(fp.exponent == 64);
         assert(fp.coefficient == UInt!64.fromHexString("afbbfae3cd0aff28"));
+
+        assert(Fp!128(-double.infinity) * Fp!128(1) == Fp!128(-double.infinity));
     }
 }
 
 ///
-Fp!(coefficientizeA + coefficientizeB) extendedMul(uint coefficientizeA, size_t coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
+Fp!(coefficientizeA + coefficientizeB) extendedMul(uint coefficientizeA, uint coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
     @safe pure nothrow @nogc
 {
     import mir.bignum.fixed: extendedMul;
-    auto coefficient = extendedMul(a.coefficient, b.coefficient);
-    auto exponent = a.exponent + b.exponent;
-    auto sign = a.sign ^ b.sign;
-    if (!coefficient.signBit)
+    import mir.checkedint: adds;
+
+    typeof(return) ret = void;
+    // nan * any -> nan
+    // inf * fin -> inf
+    ret.coefficient = extendedMul(a.coefficient, b.coefficient);
+    if (_expect(a.isSpecial | b.isSpecial, false))
+    {   // set nan
+        ret.exponent = ret.exponent.max;
+        // nan inf case
+        if (a.isSpecial & b.isSpecial)
+            ret.coefficient = a.coefficient | b.coefficient;
+    }
+    else
     {
-        --exponent;
-        coefficient = coefficient.smallLeftShift(1);
+        bool overflow;
+        ret.exponent = adds(a.exponent, b.exponent, overflow);
+        // exponent underflow -> 0 or subnormal
+        // overflow -> inf
+        if (_expect(overflow, false))
+        {
+            // overflow
+            if (a.exponent > 0) //  && b.exponent > 0 is always true
+            {
+                ret.exponent = ret.exponent.max;
+                ret.coefficient = 0;
+            }
+            //  underflow
+            else // a.exponent < 0 and b.exponent < 0
+            {
+                // TODO: subnormal
+                ret.exponent = 0;
+                ret.coefficient = 0;
+            }
+        }
+        else
+        if (!ret.coefficient.signBit)
+        {
+            auto normal = ret.exponent != ret.exponent.min;
+            ret.exponent -= normal; // check overflow
+            ret.coefficient = ret.coefficient.smallLeftShift(normal);
+        }
     }
-    return typeof(return)(sign, exponent, coefficient);
+    ret.sign = a.sign ^ b.sign;
+    return ret;
 }
 
 ///
 template fp_log2(T)
-    if (__traits(isFloating, T))
+    if (is(T == float) || is(T == double) || is(T == real))
 {
     ///
-    T fp_log2(uint coefficientSize, Exp = long)(Fp!(coefficientSize, Exp) x)
+    T fp_log2(uint size)(Fp!size x)
     {
         import mir.math.common: log2;
-        auto exponent = x.exponent + coefficientSize;
-        x.exponent = -Exp(coefficientSize);
+        auto exponent = x.exponent + size;
+        if (!x.isSpecial)
+            x.exponent = -long(size);
         return log2(cast(T)x) + exponent;
     }
 }
@@ -503,10 +647,10 @@ unittest
 
 ///
 template fp_log(T)
-    if (__traits(isFloating, T))
+    if (is(T == float) || is(T == double) || is(T == real))
 {
     ///
-    T fp_log(uint coefficientSize, Exp = long)(Fp!(coefficientSize, Exp) x)
+    T fp_log(uint size)(Fp!size x)
     {
         import mir.math.constant: LN2;
         return T(LN2) * fp_log2!T(x);
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index f374dd7f..8fcc9d41 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -10,23 +10,19 @@ module mir.math.numeric;
 import mir.math.common;
 import mir.primitives;
 import mir.primitives: isInputRange;
-import std.traits: CommonType, Unqual, isIterable, ForeachType, isPointer;
+import std.traits: CommonType, Unqual, isIterable, ForeachType, isPointer, isNumeric;
 import mir.internal.utility: isFloatingPoint;
 
-///
+deprecated("Use mir.bignum.fp.Fp!128 instead.")
 struct ProdAccumulator(T)
     if (isFloatingPoint!T)
 {
     alias F = Unqual!T;
 
-    ///
     long exp = 1L;
-    ///
     F x = cast(F) 0.5;
-    ///
     alias mantissa = x;
 
-    ///
     @safe pure @nogc nothrow
     this(F value)
     {
@@ -37,7 +33,6 @@ struct ProdAccumulator(T)
         this.exp = lexp;
     }
 
-    ///
     @safe pure @nogc nothrow
     this(long exp, F x)
     {
@@ -45,7 +40,6 @@ struct ProdAccumulator(T)
         this.x = x;
     }
 
-    ///
     @safe pure @nogc nothrow
     void put(U)(U e)
         if (is(U : T))
@@ -66,7 +60,6 @@ struct ProdAccumulator(T)
         }
     }
 
-    ///
     @safe pure @nogc nothrow
     void put(ProdAccumulator!T value)
     {
@@ -79,7 +72,6 @@ struct ProdAccumulator(T)
         }
     }
 
-    ///
     void put(Range)(Range r)
         if (isIterable!Range)
     {
@@ -109,7 +101,6 @@ struct ProdAccumulator(T)
         }
     }
 
-    ///
     @safe pure @nogc nothrow
     T prod() const scope @property
     {
@@ -121,24 +112,20 @@ struct ProdAccumulator(T)
         return ldexp(mantissa, e);
     }
 
-    ///
     @safe pure @nogc nothrow
     ProdAccumulator!T ldexp(long exp) const
     {
         return typeof(return)(this.exp + exp, mantissa);
     }
 
-    // ///
     alias opOpAssign(string op : "*") = put;
 
-    ///
     @safe pure @nogc nothrow
     ProdAccumulator!T opUnary(string op : "-")() const
     {
         return typeof(return)(exp, -mantissa);
     }
 
-    ///
     @safe pure @nogc nothrow
     ProdAccumulator!T opUnary(string op : "+")() const
     {
@@ -146,49 +133,6 @@ struct ProdAccumulator(T)
     }
 }
 
-///
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-
-    ProdAccumulator!float x;
-    x.put([1, 2, 3].sliced);
-    assert(x.prod == 6f);
-    x.put(4);
-    assert(x.prod == 24f);
-}
-
-version(mir_test)
-@safe pure @nogc nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-
-    static immutable a = [1, 2, 3];
-    ProdAccumulator!float x;
-    x.put(a);
-    assert(x.prod == 6f);
-    x.put(4);
-    assert(x.prod == 24f);
-    static assert(is(typeof(x.prod) == float));
-}
-
-version(mir_test)
-@safe pure nothrow
-unittest
-{
-    import mir.ndslice.slice: sliced;
-
-    ProdAccumulator!double x;
-    x.put([1.0, 2.0, 3.0]);
-    assert(x.prod == 6.0);
-    x.put(4.0);
-    assert(x.prod == 24.0);
-    static assert(is(typeof(x.prod) == double));
-}
-
 package template prodType(T)
 {
     import mir.math.sum: elementType;
@@ -227,30 +171,48 @@ See_also:
 $(MREF mir, algorithm, iteration, reduce)
 $(MREF mir, algorithm, iteration, fold)
 +/
-F prod(F, Range)(Range r)
-    if (isFloatingPoint!F && isIterable!Range)
+template prod(uint size)
 {
-    import core.lifetime: move;
+    import mir.bignum.fp: Fp;
 
-    ProdAccumulator!F prod;
-    prod.put(r.move);
-    return prod.prod;
+    Fp!size prod(Range)(Range r)
+        if (isIterable!Range)
+    {
+        import core.lifetime: move;
+        import mir.algorithm.iteration: each;
+        Fp!size prod = 1LU;
+        each!((e) {
+            prod *= Fp!128(e);
+        })(move(r));
+        return prod;
+    }
 }
 
-/++
-Params:
-    r = finite iterable range
-    exp = value of exponent
-Returns:
-    The mantissa, such that the product equals the mantissa times 2^^exp
-+/
+/// ditto
+template prod(F)
+    if (isFloatingPoint!F)
+{
+    F prod(Range)(Range r)
+        if (isFloatingPoint!F && isIterable!Range)
+    {
+        import core.lifetime: move;
+        return cast(F) .prod!128(r);
+    }
+}
+
+
+deprecated("Use prod!128 instead.")
 F prod(F, Range)(Range r, ref long exp)
     if (isFloatingPoint!F && isIterable!Range)
 {
     import core.lifetime: move;
 
+    import core.lifetime: move;
+    
+    auto p = .prod!128(r.move);
+
     ProdAccumulator!F prod;
-    prod.put(r.move);
+    prod.put(cast(F) p);
     exp = prod.exp;
     return prod.x;
 }
@@ -266,17 +228,10 @@ prodType!Range prod(Range)(Range r)
 {
     import core.lifetime: move;
     
-    alias F = typeof(return);
-    return .prod!(F, Range)(r.move);
+    return cast(typeof(return)) .prod!128(r.move);
 }
 
-/++
-Params:
-    r = finite iterable range
-    exp = value of exponent
-Returns:
-    The mantissa, such that the product equals the mantissa times 2^^exp
-+/
+deprecated("Use prod!128 instead.")
 prodType!Range prod(Range)(Range r, ref long exp)
     if (isIterable!Range)
 {
@@ -294,10 +249,7 @@ Returns:
 +/
 prodType!T prod(T)(scope const T[] ar...)
 {
-    alias F = typeof(return);
-    ProdAccumulator!F prod;
-    prod.put(ar);
-    return prod.prod;
+    return cast(typeof(return)) .prod!128(ar);
 }
 
 /// Product of arbitrary inputs
@@ -321,11 +273,6 @@ unittest
     auto r = [l, l, l, s, s, s, 0.8 * 2.0 ^^ 10];
     
     assert(r.prod == 0.8 * 2.0 ^^ 10);
-    
-    // Can get the mantissa and exponent
-    long e;
-    assert(r.prod(e).approxEqual(0.8));
-    assert(e == 10);
 }
 
 /// Product of vector
@@ -344,10 +291,6 @@ unittest
     auto r = [l, l, l, s, s, s, u, u, u].sliced;
               
     assert(r.prod == reduce!"a * b"(1.0, [u, u, u]));
-
-    long e;
-    assert(r.prod(e).approxEqual(reduce!"a * b"(1.0, [c, c, c])));
-    assert(e == 30);
 }
 
 /// Product of matrix
@@ -369,10 +312,6 @@ unittest
     ].fuse;
               
     assert(r.prod == reduce!"a * b"(1.0, [u, u, u]));
-
-    long e;
-    assert(r.prod(e) == reduce!"a * b"(1.0, [c, c, c]));
-    assert(e == 30);
 }
 
 /// Column prod of matrix
@@ -452,10 +391,6 @@ unittest
     static immutable result2 = [c, c, c];
               
     assert(r.sliced.prod.approxEqual(reduce!"a * b"(1.0, result1)));
-
-    long e;
-    assert(r.sliced.prod(e).approxEqual(reduce!"a * b"(1.0, result2)));
-    assert(e == 30);
 }
 
 version(mir_test)
@@ -475,10 +410,6 @@ unittest
     static immutable result2 = [c, c, c];
               
     assert(r.sliced.prod!double.approxEqual(reduce!"a * b"(1.0, result1)));
-
-    long e;
-    assert(r.sliced.prod!double(e).approxEqual(reduce!"a * b"(1.0, result2)));
-    assert(e == 30);
 }
 
 /++
@@ -486,11 +417,11 @@ Compute the sum of binary logarithms of the input range $(D r).
 The error of this method is much smaller than with a naive sum of log2.
 +/
 Unqual!(DeepElementType!Range) sumOfLog2s(Range)(Range r)
-    if (isFloatingPoint!(DeepElementType!Range))
+    if (isNumeric!(DeepElementType!Range))
 {
-    long exp = 0;
-    auto x = .prod(r, exp);
-    return exp + log2(x);
+    import mir.bignum.fp: fp_log2;
+    import core.lifetime: move;
+    return prod!128(move(r)).fp_log2!(typeof(return));
 }
 
 ///
@@ -498,6 +429,8 @@ version(mir_test)
 @safe pure
 unittest
 {
+    import mir.format: text;
+    import mir.bignum.fp;
     alias isNaN = x => x != x;
 
     assert(sumOfLog2s(new double[0]) == 0);
@@ -512,6 +445,27 @@ unittest
     assert(sumOfLog2s([ 0.25, 0.25, 0.25, 0.125 ]) == -9);
 }
 
+/++
+Compute the sum of binary logarithms of the input range $(D r).
+The error of this method is much smaller than with a naive sum of log.
++/
+Unqual!(DeepElementType!Range) sumOfLogs(Range)(Range r)
+    if (isNumeric!(DeepElementType!Range))
+{
+    import core.lifetime: move;
+    import mir.math.constant: LN2;
+    return typeof(return)(LN2) * sumOfLog2s(move(r));
+}
+
+///
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.math: LN2, approxEqual;
+    assert([ 0.25, 0.25, 0.25, 0.125 ].sumOfLogs.approxEqual(-9 * double(LN2)));
+}
+
 /++
 Quickly computes factorial using extended
 precision floating point type $(MREF mir,bignum,fp).
@@ -523,7 +477,7 @@ Returns: `(count + start - 1)! / (start - 1)!`
 Complexity: O(count)
 +/
 auto factorial
-    (uint coefficientSize = 128, Exp = long)
+    (uint coefficientSize = 128)
     (ulong count, ulong start = 1)
     if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
     in (start)
@@ -532,7 +486,7 @@ auto factorial
     import mir.checkedint: mulu;
     import mir.utility: _expect;
 
-    alias R = Fp!(coefficientSize, Exp);
+    alias R = Fp!coefficientSize;
     R prod = 1LU;
 
     if (count)
@@ -572,7 +526,8 @@ unittest
     static assert(is(typeof(factorial(33)) == Fp!128));
     static assert(is(typeof(factorial!256(33)) == Fp!256));
     static immutable double f33 = 8.68331761881188649551819440128e+36;
-    static assert(cast(double) factorial(33) == f33);
+    import mir.format: text;
+    static assert(cast(double) factorial(33) == f33, (cast(double) factorial(33)).text);
 
     assert(cast(double) factorial(0) == 1);
     assert(cast(double) factorial(0, 100) == 1);
@@ -593,15 +548,15 @@ Returns: n choose k
 Complexity: O(min(k, n - k))
 +/
 auto binomialCoefficient
-    (uint coefficientSize = 128, Exp = long)
+    (uint coefficientSize = 128)
     (ulong n, uint k)
     if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
     in (k <= n)
 {
     if (k > n - k)
         k = cast(uint)(n - k);
-    auto a = factorial!(coefficientSize, Exp)(k, n - k + 1);
-    auto b = factorial!(coefficientSize, Exp)(k);
+    auto a = factorial!coefficientSize(k, n - k + 1);
+    auto b = factorial!coefficientSize(k);
     return a / b;
 }
 

From 31504c63006f31c9d1d21478b3e1dd06bee6c680 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 17:30:34 +0400
Subject: [PATCH 382/573] rework mir.math.stat

---
 source/mir/math/stat.d | 63 ++++++++++++++++++++++--------------------
 1 file changed, 33 insertions(+), 30 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 3617d199..8ad0a1d2 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -24,7 +24,7 @@ import mir.math.common: fmamath;
 import mir.math.sum;
 import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
 import mir.primitives;
-import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType;
+import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType, isNumeric;
 
 ///
 public import mir.math.sum: Summation;
@@ -939,46 +939,49 @@ Output range for gmean.
 struct GMeanAccumulator(T) 
     if (isMutable!T && isFloatingPoint!T)
 {
-    import mir.math.numeric: ProdAccumulator;
+    import mir.bignum.fp: Fp;
 
     ///
     size_t count;
     ///
-    ProdAccumulator!T prodAccumulator;
+    Fp!128 prodAccumulator = 1u;
 
     ///
-    F gmean(F = T)() @property
+    F gmean(F = T)() const @property
         if (isFloatingPoint!F)
     {
         import mir.math.common: exp2;
 
-        return nthroot(cast(F) prodAccumulator.mantissa, count) * exp2(cast(F) prodAccumulator.exp / count);
+        if (count == 0)
+            return 0;
+
+        long exponent;
+        Fp!128 value = prodAccumulator;
+
+        if (!value.isSpecial)
+        {
+            exponent = value.exponent + 128;
+            value.exponent = -128;
+        }
+
+        return nthroot(cast(F) value, count) * exp2(cast(F) exponent / count);
     }
 
     ///
     void put(Range)(Range r)
         if (isIterable!Range)
     {
-        static if (hasShape!Range)
-        {
-            count += r.elementCount;
-            prodAccumulator.put(r);
-        }
-        else
-        {
-            foreach(x; r)
-            {
-                count++;
-                prodAccumulator.put(x);
-            }
-        }
+        import core.lifetime: move;
+        import mir.algorithm.iteration: each;
+        each!(e => this.put(e))(move(r));
     }
 
     ///
-    void put()(T x)
+    void put()(const T x)
+        if (isNumeric!T)
     {
         count++;
-        prodAccumulator.put(x);
+        prodAccumulator *= Fp!128(x);
     }
 }
 
@@ -1910,7 +1913,7 @@ struct MapSummator(alias fun, T, Summation summation)
     Summator!(T, summation) summator;
 
     ///
-    F sum(F = T)() @property
+    F sum(F = T)() const @property
     {
         return cast(F) summator.sum;
     }
@@ -2052,13 +2055,13 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     MeanAccumulator!(T, summation) meanAccumulator;
 
     ///
-    size_t count() @property
+    size_t count() const @property
     {
         return meanAccumulator.count;
     }
 
     ///
-    F mean(F = T)() @property
+    F mean(F = T)() const @property
     {
         return meanAccumulator.mean;
     }
@@ -2084,7 +2087,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     }
 
     ///
-    F variance(F = T)(bool isPopulation) @property
+    F variance(F = T)(bool isPopulation) const @property
     {
         if (isPopulation == false)
             return cast(F) sumOfSquares.sum / cast(F) (count - 1) - 
@@ -2148,13 +2151,13 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     MeanAccumulator!(T, summation) meanAccumulator;
 
     ///
-    size_t count() @property
+    size_t count() const @property
     {
         return meanAccumulator.count;
     }
 
     ///
-    F mean(F = T)() @property
+    F mean(F = T)() const @property
     {
         return meanAccumulator.mean;
     }
@@ -2362,13 +2365,13 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     MeanAccumulator!(T, summation) meanAccumulator;
 
     ///
-    size_t count() @property
+    size_t count() const @property
     {
         return meanAccumulator.count;
     }
 
     ///
-    F mean(F = T)() @property
+    F mean(F = T)() const @property
     {
         return meanAccumulator.mean;
     }
@@ -2485,13 +2488,13 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     private size_t _count;
     
     ///
-    size_t count() @property
+    size_t count() const @property
     {
         return _count;
     }
     
     ///
-    F mean(F = T)() @property
+    F mean(F = T)() const @property
     {
         return cast(F) 0;
     }

From d2b20caf1255a512261aa13fcead5b810e2985de Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 18:13:01 +0400
Subject: [PATCH 383/573] fixup fp

---
 source/mir/bignum/fp.d | 50 ++++++++++++++++++++++++++++++++----------
 1 file changed, 39 insertions(+), 11 deletions(-)

diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index d54e5dd5..b60595d3 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -210,7 +210,7 @@ struct Fp(uint size)
         }
         else
         {
-            if (integer == integer.init)
+            if (!integer)
                 return;
             this.exponent = isize - size;
             if (!normalizedInteger)
@@ -401,7 +401,7 @@ struct Fp(uint size)
     T opCast(T)() nothrow const
         if (is(Unqual!T == bool))
     {
-        return coefficient != 0;
+        return exponent || coefficient;
     }
     
     ///
@@ -531,23 +531,51 @@ struct Fp(uint size)
     }
 
     ///
-    T opCast(T : Fp!newCoefficientSize, size_t newCoefficientSize)() nothrow const
-        if (newCoefficientSize != size)
+    T opCast(T : Fp!newSize, size_t newSize)() nothrow const
+        if (newSize != size)
     {
-        Fp!newCoefficientSize ret = void;
-        if (_expect(isSpecial, false))
+        Fp!newSize ret;
+        ret.sign = this.sign;
+        if (_expect(this.isSpecial, false))
         {
             ret.exponent = ret.exponent.max;
             ret.coefficient = !!this.coefficient;
+            return ret;
         }
-        else
+        if (!this)
         {
-            ret = Fp!newCoefficientSize(this.coefficient, true);
-            // TODO: exponent overflow / underflow
-            // with care of special values
-            ret.exponent = ret.exponent + this.exponent;
+            return ret;
         }
+
+        ret = Fp!newSize(this.coefficient, true);
         ret.sign = this.sign;
+        static if (newSize < size)
+        {
+            // underflow
+            if (this.exponent == this.exponent.min && !ret.coefficient)
+            {
+                ret.exponent = 0;
+                return ret;
+            }
+        }
+
+        import mir.checkedint: adds;
+        /// overflow
+        bool overflow;
+        ret.exponent = adds(ret.exponent, this.exponent, overflow);
+        if (_expect(overflow, false))
+        {
+            // overflow
+            static if (newSize < size)
+            {
+                assert(this.exponent > 0);
+            }
+            // underflow
+            else
+            {
+                assert(this.exponent < 0);
+            }
+        }
         return ret;
     }
 

From 818322a0e67fe11691bdeedf2429d66f1087d51f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 18:16:27 +0400
Subject: [PATCH 384/573] revert ProdAccumulator deprecation

---
 source/mir/math/numeric.d | 155 +++++++++++++++++++++++++++-----------
 source/mir/math/stat.d    |  51 ++++++-------
 2 files changed, 135 insertions(+), 71 deletions(-)

diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index 8fcc9d41..6a5c7035 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -10,19 +10,23 @@ module mir.math.numeric;
 import mir.math.common;
 import mir.primitives;
 import mir.primitives: isInputRange;
-import std.traits: CommonType, Unqual, isIterable, ForeachType, isPointer, isNumeric;
+import std.traits: CommonType, Unqual, isIterable, ForeachType, isPointer;
 import mir.internal.utility: isFloatingPoint;
 
-deprecated("Use mir.bignum.fp.Fp!128 instead.")
+///
 struct ProdAccumulator(T)
     if (isFloatingPoint!T)
 {
     alias F = Unqual!T;
 
+    ///
     long exp = 1L;
+    ///
     F x = cast(F) 0.5;
+    ///
     alias mantissa = x;
 
+    ///
     @safe pure @nogc nothrow
     this(F value)
     {
@@ -33,6 +37,7 @@ struct ProdAccumulator(T)
         this.exp = lexp;
     }
 
+    ///
     @safe pure @nogc nothrow
     this(long exp, F x)
     {
@@ -40,6 +45,7 @@ struct ProdAccumulator(T)
         this.x = x;
     }
 
+    ///
     @safe pure @nogc nothrow
     void put(U)(U e)
         if (is(U : T))
@@ -60,6 +66,7 @@ struct ProdAccumulator(T)
         }
     }
 
+    ///
     @safe pure @nogc nothrow
     void put(ProdAccumulator!T value)
     {
@@ -72,6 +79,7 @@ struct ProdAccumulator(T)
         }
     }
 
+    ///
     void put(Range)(Range r)
         if (isIterable!Range)
     {
@@ -101,6 +109,7 @@ struct ProdAccumulator(T)
         }
     }
 
+    ///
     @safe pure @nogc nothrow
     T prod() const scope @property
     {
@@ -112,20 +121,24 @@ struct ProdAccumulator(T)
         return ldexp(mantissa, e);
     }
 
+    ///
     @safe pure @nogc nothrow
     ProdAccumulator!T ldexp(long exp) const
     {
         return typeof(return)(this.exp + exp, mantissa);
     }
 
+    // ///
     alias opOpAssign(string op : "*") = put;
 
+    ///
     @safe pure @nogc nothrow
     ProdAccumulator!T opUnary(string op : "-")() const
     {
         return typeof(return)(exp, -mantissa);
     }
 
+    ///
     @safe pure @nogc nothrow
     ProdAccumulator!T opUnary(string op : "+")() const
     {
@@ -133,6 +146,49 @@ struct ProdAccumulator(T)
     }
 }
 
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+
+    ProdAccumulator!float x;
+    x.put([1, 2, 3].sliced);
+    assert(x.prod == 6f);
+    x.put(4);
+    assert(x.prod == 24f);
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+
+    static immutable a = [1, 2, 3];
+    ProdAccumulator!float x;
+    x.put(a);
+    assert(x.prod == 6f);
+    x.put(4);
+    assert(x.prod == 24f);
+    static assert(is(typeof(x.prod) == float));
+}
+
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+
+    ProdAccumulator!double x;
+    x.put([1.0, 2.0, 3.0]);
+    assert(x.prod == 6.0);
+    x.put(4.0);
+    assert(x.prod == 24.0);
+    static assert(is(typeof(x.prod) == double));
+}
+
 package template prodType(T)
 {
     import mir.math.sum: elementType;
@@ -171,48 +227,30 @@ See_also:
 $(MREF mir, algorithm, iteration, reduce)
 $(MREF mir, algorithm, iteration, fold)
 +/
-template prod(uint size)
+F prod(F, Range)(Range r)
+    if (isFloatingPoint!F && isIterable!Range)
 {
-    import mir.bignum.fp: Fp;
-
-    Fp!size prod(Range)(Range r)
-        if (isIterable!Range)
-    {
-        import core.lifetime: move;
-        import mir.algorithm.iteration: each;
-        Fp!size prod = 1LU;
-        each!((e) {
-            prod *= Fp!128(e);
-        })(move(r));
-        return prod;
-    }
-}
+    import core.lifetime: move;
 
-/// ditto
-template prod(F)
-    if (isFloatingPoint!F)
-{
-    F prod(Range)(Range r)
-        if (isFloatingPoint!F && isIterable!Range)
-    {
-        import core.lifetime: move;
-        return cast(F) .prod!128(r);
-    }
+    ProdAccumulator!F prod;
+    prod.put(r.move);
+    return prod.prod;
 }
 
-
-deprecated("Use prod!128 instead.")
+/++
+Params:
+    r = finite iterable range
+    exp = value of exponent
+Returns:
+    The mantissa, such that the product equals the mantissa times 2^^exp
++/
 F prod(F, Range)(Range r, ref long exp)
     if (isFloatingPoint!F && isIterable!Range)
 {
     import core.lifetime: move;
 
-    import core.lifetime: move;
-    
-    auto p = .prod!128(r.move);
-
     ProdAccumulator!F prod;
-    prod.put(cast(F) p);
+    prod.put(r.move);
     exp = prod.exp;
     return prod.x;
 }
@@ -228,10 +266,17 @@ prodType!Range prod(Range)(Range r)
 {
     import core.lifetime: move;
     
-    return cast(typeof(return)) .prod!128(r.move);
+    alias F = typeof(return);
+    return .prod!(F, Range)(r.move);
 }
 
-deprecated("Use prod!128 instead.")
+/++
+Params:
+    r = finite iterable range
+    exp = value of exponent
+Returns:
+    The mantissa, such that the product equals the mantissa times 2^^exp
++/
 prodType!Range prod(Range)(Range r, ref long exp)
     if (isIterable!Range)
 {
@@ -249,7 +294,10 @@ Returns:
 +/
 prodType!T prod(T)(scope const T[] ar...)
 {
-    return cast(typeof(return)) .prod!128(ar);
+    alias F = typeof(return);
+    ProdAccumulator!F prod;
+    prod.put(ar);
+    return prod.prod;
 }
 
 /// Product of arbitrary inputs
@@ -273,6 +321,11 @@ unittest
     auto r = [l, l, l, s, s, s, 0.8 * 2.0 ^^ 10];
     
     assert(r.prod == 0.8 * 2.0 ^^ 10);
+    
+    // Can get the mantissa and exponent
+    long e;
+    assert(r.prod(e).approxEqual(0.8));
+    assert(e == 10);
 }
 
 /// Product of vector
@@ -291,6 +344,10 @@ unittest
     auto r = [l, l, l, s, s, s, u, u, u].sliced;
               
     assert(r.prod == reduce!"a * b"(1.0, [u, u, u]));
+
+    long e;
+    assert(r.prod(e).approxEqual(reduce!"a * b"(1.0, [c, c, c])));
+    assert(e == 30);
 }
 
 /// Product of matrix
@@ -312,6 +369,10 @@ unittest
     ].fuse;
               
     assert(r.prod == reduce!"a * b"(1.0, [u, u, u]));
+
+    long e;
+    assert(r.prod(e) == reduce!"a * b"(1.0, [c, c, c]));
+    assert(e == 30);
 }
 
 /// Column prod of matrix
@@ -391,6 +452,10 @@ unittest
     static immutable result2 = [c, c, c];
               
     assert(r.sliced.prod.approxEqual(reduce!"a * b"(1.0, result1)));
+
+    long e;
+    assert(r.sliced.prod(e).approxEqual(reduce!"a * b"(1.0, result2)));
+    assert(e == 30);
 }
 
 version(mir_test)
@@ -410,6 +475,10 @@ unittest
     static immutable result2 = [c, c, c];
               
     assert(r.sliced.prod!double.approxEqual(reduce!"a * b"(1.0, result1)));
+
+    long e;
+    assert(r.sliced.prod!double(e).approxEqual(reduce!"a * b"(1.0, result2)));
+    assert(e == 30);
 }
 
 /++
@@ -417,11 +486,11 @@ Compute the sum of binary logarithms of the input range $(D r).
 The error of this method is much smaller than with a naive sum of log2.
 +/
 Unqual!(DeepElementType!Range) sumOfLog2s(Range)(Range r)
-    if (isNumeric!(DeepElementType!Range))
+    if (isFloatingPoint!(DeepElementType!Range))
 {
-    import mir.bignum.fp: fp_log2;
-    import core.lifetime: move;
-    return prod!128(move(r)).fp_log2!(typeof(return));
+    long exp = 0;
+    auto x = .prod(r, exp);
+    return exp + log2(x);
 }
 
 ///
@@ -429,8 +498,6 @@ version(mir_test)
 @safe pure
 unittest
 {
-    import mir.format: text;
-    import mir.bignum.fp;
     alias isNaN = x => x != x;
 
     assert(sumOfLog2s(new double[0]) == 0);
@@ -450,7 +517,7 @@ Compute the sum of binary logarithms of the input range $(D r).
 The error of this method is much smaller than with a naive sum of log.
 +/
 Unqual!(DeepElementType!Range) sumOfLogs(Range)(Range r)
-    if (isNumeric!(DeepElementType!Range))
+    if (isFloatingPoint!(DeepElementType!Range))
 {
     import core.lifetime: move;
     import mir.math.constant: LN2;
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 8ad0a1d2..95c8c66e 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -24,7 +24,7 @@ import mir.math.common: fmamath;
 import mir.math.sum;
 import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
 import mir.primitives;
-import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType, isNumeric;
+import std.traits: Unqual, isArray, isMutable, isIterable, isIntegral, CommonType;
 
 ///
 public import mir.math.sum: Summation;
@@ -939,12 +939,12 @@ Output range for gmean.
 struct GMeanAccumulator(T) 
     if (isMutable!T && isFloatingPoint!T)
 {
-    import mir.bignum.fp: Fp;
+    import mir.math.numeric: ProdAccumulator;
 
     ///
     size_t count;
     ///
-    Fp!128 prodAccumulator = 1u;
+    ProdAccumulator!T prodAccumulator;
 
     ///
     F gmean(F = T)() const @property
@@ -952,36 +952,33 @@ struct GMeanAccumulator(T)
     {
         import mir.math.common: exp2;
 
-        if (count == 0)
-            return 0;
-
-        long exponent;
-        Fp!128 value = prodAccumulator;
-
-        if (!value.isSpecial)
-        {
-            exponent = value.exponent + 128;
-            value.exponent = -128;
-        }
-
-        return nthroot(cast(F) value, count) * exp2(cast(F) exponent / count);
+        return nthroot(cast(F) prodAccumulator.mantissa, count) * exp2(cast(F) prodAccumulator.exp / count);
     }
 
     ///
     void put(Range)(Range r)
         if (isIterable!Range)
     {
-        import core.lifetime: move;
-        import mir.algorithm.iteration: each;
-        each!(e => this.put(e))(move(r));
+        static if (hasShape!Range)
+        {
+            count += r.elementCount;
+            prodAccumulator.put(r);
+        }
+        else
+        {
+            foreach(x; r)
+            {
+                count++;
+                prodAccumulator.put(x);
+            }
+        }
     }
 
     ///
-    void put()(const T x)
-        if (isNumeric!T)
+    void put()(T x)
     {
         count++;
-        prodAccumulator *= Fp!128(x);
+        prodAccumulator.put(x);
     }
 }
 
@@ -2055,7 +2052,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     MeanAccumulator!(T, summation) meanAccumulator;
 
     ///
-    size_t count() const @property
+    size_t count() @property
     {
         return meanAccumulator.count;
     }
@@ -2087,7 +2084,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     }
 
     ///
-    F variance(F = T)(bool isPopulation) const @property
+    F variance(F = T)(bool isPopulation) @property
     {
         if (isPopulation == false)
             return cast(F) sumOfSquares.sum / cast(F) (count - 1) - 
@@ -2151,7 +2148,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     MeanAccumulator!(T, summation) meanAccumulator;
 
     ///
-    size_t count() const @property
+    size_t count() @property
     {
         return meanAccumulator.count;
     }
@@ -2365,7 +2362,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     MeanAccumulator!(T, summation) meanAccumulator;
 
     ///
-    size_t count() const @property
+    size_t count() @property
     {
         return meanAccumulator.count;
     }
@@ -2488,7 +2485,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     private size_t _count;
     
     ///
-    size_t count() const @property
+    size_t count() @property
     {
         return _count;
     }

From 8690f3904f4dc05f681e071de274fc457a31a8ae Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 19:11:09 +0400
Subject: [PATCH 385/573] fixup

---
 source/mir/bignum/fp.d    | 28 ++++++++++++++++------------
 source/mir/math/numeric.d |  3 +--
 2 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index b60595d3..b9a382ca 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -547,18 +547,20 @@ struct Fp(uint size)
             return ret;
         }
 
-        ret = Fp!newSize(this.coefficient, true);
-        ret.sign = this.sign;
-        static if (newSize < size)
+        UInt!size coefficient = this.coefficient;
+        int shift;
+        // subnormal
+
+        if (this.exponent == this.exponent.min)
         {
-            // underflow
-            if (this.exponent == this.exponent.min && !ret.coefficient)
-            {
-                ret.exponent = 0;
-                return ret;
-            }
+            shift = cast(int)coefficient.ctlz;
+            coefficient <<= shift;
         }
 
+        ret = Fp!newSize(coefficient, true);
+        ret.exponent -= shift;
+        ret.sign = this.sign;
+
         import mir.checkedint: adds;
         /// overflow
         bool overflow;
@@ -566,14 +568,16 @@ struct Fp(uint size)
         if (_expect(overflow, false))
         {
             // overflow
-            static if (newSize < size)
+            if (this.exponent > 0)
             {
-                assert(this.exponent > 0);
+                ret.exponent = ret.exponent.max;
+                ret.coefficient = 0u;
             }
             // underflow
             else
             {
-                assert(this.exponent < 0);
+                ret.coefficient >>= ret.exponent - exponent.min;
+                ret.exponent = ret.coefficient ? ret.exponent.min : 0;
             }
         }
         return ret;
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index 6a5c7035..ff378d2f 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -593,8 +593,7 @@ unittest
     static assert(is(typeof(factorial(33)) == Fp!128));
     static assert(is(typeof(factorial!256(33)) == Fp!256));
     static immutable double f33 = 8.68331761881188649551819440128e+36;
-    import mir.format: text;
-    static assert(cast(double) factorial(33) == f33, (cast(double) factorial(33)).text);
+    static assert(approxEqual(cast(double) factorial(33), f33));
 
     assert(cast(double) factorial(0) == 1);
     assert(cast(double) factorial(0, 100) == 1);

From 448e2c3f9f6c4065a7d5afe1ec88eaf96036f756 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 19:27:49 +0400
Subject: [PATCH 386/573] fixup

---
 source/mir/bignum/fixed.d | 18 ++++++++++++------
 source/mir/bignum/fp.d    | 25 ++++++++++++++++++++-----
 2 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 7ebacd26..c14c7a03 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -718,14 +718,20 @@ struct UInt(size_t size)
     ///
     UInt!(size + additionalRightBits) rightExtend(size_t additionalRightBits)()
         const @safe pure @nogc nothrow
-        if (additionalRightBits)
     {
-        typeof(return) ret;
-        version (BigEndian)
-            ret.data[0 .. data.length] = data;
+        static if (additionalRightBits)
+        {
+            typeof(return) ret;
+            version (BigEndian)
+                ret.data[0 .. data.length] = data;
+            else
+                ret.data[$ - data.length .. $] = data;
+            return ret;
+        }
         else
-            ret.data[$ - data.length .. $] = data;
-        return ret;
+        {
+            return this;
+        }
     }
 
     /++
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index b9a382ca..470b13f2 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -67,7 +67,7 @@ struct Fp(uint size)
             enum scale = T(2) ^^ T.mant_dig;
             x = frexp(x, exp) * scale;
         }
-        auto dd = this.coefficient.view.leastSignificantFirst;
+
         static if (T.mant_dig < 64)
         {
             auto xx = cast(ulong)cast(long)x;
@@ -76,8 +76,12 @@ struct Fp(uint size)
                 auto shift = ctlz(xx);
                 exp -= shift + T.mant_dig + size - 64;
                 xx <<= shift;
+                this.coefficient = UInt!64(xx).rightExtend!(size - 64);
+            }
+            else
+            {
+                this.coefficient = xx;
             }
-            dd[normalize ? $ - 1 : 0] = xx;
         }
         else
         static if (T.mant_dig == 64)
@@ -88,8 +92,12 @@ struct Fp(uint size)
                 auto shift = ctlz(xx);
                 exp -= shift + T.mant_dig + size - 64;
                 xx <<= shift;
+                this.coefficient = UInt!64(xx).rightExtend!(size - 64);
+            }
+            else
+            {
+                this.coefficient = xx;
             }
-            dd[normalize ? $ - 1 : 0] = xx;
         }
         else
         {
@@ -103,15 +111,22 @@ struct Fp(uint size)
             x *= scale;
             auto most = ulong(high);
             auto least = cast(ulong)x;
+            version(LittleEndian)
+                ulong[2] pair = [most, least];
+            else
+                ulong[2] pair = [least, most];
 
-            dd[normalize ? $ - 1 : 1] = most;
-            dd[normalize ? $ - 2 : 0] = least;
             if (normalize)
             {
+                this.coefficient = UInt!128(pair).rightExtend!(size - 128);
                 auto shift = most ? ctlz(most) : ctlz(least) + 64;
                 exp -= shift + T.mant_dig + size - 64 * (1 + (T.mant_dig > 64));
                 this.coefficient <<= shift;
             }
+            else
+            {
+                this.coefficient = pair;
+            }
         }
         if (!normalize)
         {

From 6b7c568e4717d7d70a2e2b9c0b204c7ef92f1a67 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 20:18:19 +0400
Subject: [PATCH 387/573] fixup x86

---
 source/mir/bignum/decimal.d | 14 +++++++-------
 source/mir/bignum/fp.d      |  4 ++--
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index f5e36418..bcf37680 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -781,7 +781,7 @@ struct Decimal(uint maxSize64)
                 {
                     if (addSign)
                         w.put(sign[]);
-                    w.put(zeros[0 .. -s + 2]);
+                    w.put(zeros[0 .. cast(sizediff_t)(-s + 2)]);
                     w.put(buffer[$ - coefficientLength .. $]);
                     return;
                 }
@@ -796,7 +796,7 @@ struct Decimal(uint maxSize64)
                     {
                         buffer[$ - coefficientLength - 1] = sign[0];
                         w.put(buffer[$ - coefficientLength - addSign .. $]);
-                        w.put(zeros[$ - (this.exponent + 2) .. $]);
+                        w.put(zeros[($ - (cast(sizediff_t)this.exponent + 2)) .. $]);
                         return;
                     }
                 }
@@ -805,7 +805,7 @@ struct Decimal(uint maxSize64)
                     if (s <= 12)
                     {
                         buffer0[$ - 16 .. $] = '0';
-                        putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + this.exponent]);
+                        putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + cast(sizediff_t)this.exponent]);
                         w.put(zeros[$ - 2 .. $]);
                         return;
                     }
@@ -822,8 +822,8 @@ struct Decimal(uint maxSize64)
                         buffer[$ - coefficientLength - 1] = sign[0];
                         w.put(buffer[$ - coefficientLength  - addSign .. $ - coefficientLength + s]);
                     T2:
-                        buffer[$ - coefficientLength + s - 1] = '.';
-                        w.put(buffer[$ - coefficientLength + s - 1 .. $]);
+                        buffer[$ - coefficientLength + cast(sizediff_t)s - 1] = '.';
+                        w.put(buffer[$ - coefficientLength + cast(sizediff_t)s - 1 .. $]);
                         return;
                     }
                 }
@@ -831,7 +831,7 @@ struct Decimal(uint maxSize64)
                 {
                     if (s <= 12 || coefficientLength <= 12)
                     {
-                        putL(buffer[$ - coefficientLength .. $ - coefficientLength + s]);
+                        putL(buffer[$ - coefficientLength .. $ - coefficientLength + cast(sizediff_t)s]);
                         goto T2;
                     }
                 }
@@ -991,7 +991,7 @@ struct Decimal(uint maxSize64)
         import mir.utility: max;
         BigInt!(max(rhsMaxSize64, maxSize64, 256u)) rhsCopy = void;
         BigIntView!(const size_t) rhsView;
-        auto expDiff = exponent - rhs.exponent;
+        auto expDiff = cast(sizediff_t) (exponent - rhs.exponent);
         if (expDiff >= 0)
         {
             exponent = rhs.exponent;
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 470b13f2..007ccfd9 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -380,7 +380,7 @@ struct Fp(uint size)
     ///
     Fp!(max(size, rhsSize)) opBinary(string op : "*", uint rhsSize)(Fp!rhsSize rhs) nothrow const
     {
-        return cast(Fp) .extendedMul!(size, rhsSize)(this, rhs);
+        return cast(Fp) .extendedMul!(size, rhsSize)(cast()this, rhs);
     }
 
     static if (size == 128)
@@ -591,7 +591,7 @@ struct Fp(uint size)
             // underflow
             else
             {
-                ret.coefficient >>= ret.exponent - exponent.min;
+                ret.coefficient >>= cast(uint)(ret.exponent - exponent.min);
                 ret.exponent = ret.coefficient ? ret.exponent.min : 0;
             }
         }

From 33b541af2b92b68c4d895e6846dd474a9955ec13 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 20:23:44 +0400
Subject: [PATCH 388/573] fixup

---
 source/mir/bignum/decimal.d | 2 +-
 source/mir/format.d         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index bcf37680..cece609e 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -820,7 +820,7 @@ struct Decimal(uint maxSize64)
                     if (s <= 6 || coefficientLength <= 6)
                     {
                         buffer[$ - coefficientLength - 1] = sign[0];
-                        w.put(buffer[$ - coefficientLength  - addSign .. $ - coefficientLength + s]);
+                        w.put(buffer[$ - coefficientLength  - addSign .. $ - coefficientLength + cast(sizediff_t)s]);
                     T2:
                         buffer[$ - coefficientLength + cast(sizediff_t)s - 1] = '.';
                         w.put(buffer[$ - coefficientLength + cast(sizediff_t)s - 1 .. $]);
diff --git a/source/mir/format.d b/source/mir/format.d
index 7bf5e260..201342da 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -769,7 +769,7 @@ unittest
 
     void check(double num, string value)
     {
-        assert(buffer.print(num, spec).data == value);
+        assert(buffer.print(num, spec).data == value, value);
         buffer.reset;
     }
 

From d930eca9d0370124e1cdbe8d19f17466e9a1641a Mon Sep 17 00:00:00 2001
From: Harrison <harrison@0xcc.pw>
Date: Thu, 26 May 2022 12:35:19 -0400
Subject: [PATCH 389/573] Implement big integer division (#387)

---
 source/mir/bignum/fixed.d          | 118 +++++++++++-
 source/mir/bignum/integer.d        | 291 ++++++++++++++++++++++++++++-
 source/mir/bignum/low_level_view.d | 201 ++++++++++++++++++++
 3 files changed, 608 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index c14c7a03..a78b8c13 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -397,6 +397,105 @@ struct UInt(size_t size)
         return overflow;
     }
 
+    /++
+    Performs division & extracts the remainder.
+    Params:
+        rhs = unsigned value to divide by
+    Returns:
+        unsigned remainder value
+    +/ 
+    auto divRem(size_t rhsSize)(const UInt!rhsSize rhs)
+        @safe pure nothrow @nogc scope const
+    {
+        import mir.bignum.low_level_view : divm, BigUIntView;
+
+        enum m = (size / (uint.sizeof * 8)), n = (rhsSize / (uint.sizeof * 8));
+
+        static struct QuotientAndRemainder
+        {
+            // XXX: we have to assume the quotient size here,
+            // checking at runtime would be more accurate for better space savings
+            UInt!size quotient;
+            UInt!rhsSize remainder;
+        }
+
+        QuotientAndRemainder val;
+
+        uint[m + 1] _div = void;
+        // shouldn't be unaligned here -- maybe a possibility though
+        _div[$ - 1] = 0;
+        _div[0 .. $ - 1] = (cast(uint[data.sizeof / 4])data);
+
+        BigUIntView!uint dividend = BigUIntView!uint(_div);
+        BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view;
+        BigUIntView!uint quotient = cast(BigUIntView!uint)val.quotient.view;
+        divm(dividend, divisor, quotient);
+
+        BigUIntView!uint remainder = cast(BigUIntView!uint)val.remainder.view;
+        remainder.coefficients[0 .. $] = (cast(uint[])_div)[0 .. remainder.coefficients.length];
+
+        return val;
+    }
+
+    /++
+    Performs `big /= rhs` operation.
+    Params:
+        rhs = unsigned value to divide by
+    Returns:
+        quotient from division 
+    +/
+    ref opOpAssign(string op : "/", size_t rhsSize)(UInt!rhsSize rhs)
+        @safe pure nothrow @nogc scope return
+    {
+        auto result = divRem(rhs);
+        // XXX: fix this if we manage to calculate the quotient size better
+        this.data = result.quotient.data;
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "/")(ulong rhs)
+        @safe pure nothrow @nogc scope return
+    {
+        return opOpAssign!(op, ulong.sizeof * 8)(UInt!(ulong.sizeof * 8)(rhs));
+    }
+
+    /++
+    Performs `big %= rhs` operation.
+    Params:
+        rhs = unsigned value to divide by
+    Returns:
+        remainder from division
+    +/
+    ref opOpAssign(string op : "%", size_t rhsSize)(UInt!rhsSize rhs)
+        @safe pure nothrow @nogc scope return
+    {
+        auto result = divRem(rhs);
+        this.data = 0;
+        // We ensure that size >= rhsSize, so this is fine
+        this.data[0 .. result.remainder.data.length] = result.remainder.data;
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "%")(ulong rhs)
+        @safe pure nothrow @nogc scope
+    {
+        return opOpAssign!(op, ulong.sizeof * 8)(UInt!(ulong.sizeof * 8)(rhs));
+    }
+
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("e3251bacb112c88b71ad3f85a970a314");
+        auto b = UInt!128.fromHexString("dfbbfae3cd0aff2714a1de7022b0029d");
+        assert(a / b == UInt!128.fromHexString("1"));
+        assert(a % b == UInt!128.fromHexString("36920c8e407c9645d0b611586c0a077"));
+    }
+
     ///
     ref UInt!size opOpAssign(string op)(UInt!size rhs) nothrow return
         if (op == "^" || op == "|" || op == "&")
@@ -524,7 +623,7 @@ struct UInt(size_t size)
     Binary operations
     +/
     template opBinary(string op)
-        if (op == "^" || op == "|" || op == "&" || op == "+" || op == "-" || op == "*") //  || op == "/" || op == "%"
+        if (op == "^" || op == "|" || op == "&" || op == "+" || op == "-" || op == "*" || op == "/" || op == "%")
     {
         ///
         UInt!size opBinary(size_t rsize)(UInt!rsize rhs)
@@ -546,6 +645,23 @@ struct UInt(size_t size)
         }
     }
 
+    static if (size == 128)
+    ///
+    version(mir_bignum_test)
+    @safe pure @nogc
+    unittest
+    {
+        auto a = UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+        assert(a / UInt!128.fromHexString("5") == UInt!128.fromHexString("23259893f5ceffd49db9f949a0899a1f"));
+        assert(a == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+        assert(a % UInt!128.fromHexString("5") == UInt!128.fromHexString("2"));
+        assert(a == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+
+        assert(a / 5 == UInt!128.fromHexString("23259893f5ceffd49db9f949a0899a1f"));
+        assert(a % 5 == UInt!64.fromHexString("2"));
+        assert(a % 5 == 2);
+    }
+
     /// ditto
     template opBinaryRight(string op)
         if (op == "^" || op == "|" || op == "&" || op == "+" || op == "*")
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 83cfd2a1..4a43ccbd 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -317,6 +317,208 @@ struct BigInt(uint maxSize64)
         return overflow;
     }
 
+    /++
+    Performs `big /= rhs` operation.
+    Params:
+        rhs = unsigned value to divide by
+    Returns:
+        quotient from division
+    +/
+    ref opOpAssign(string op : "/", size_t size)(UInt!size rhs)
+        @safe pure nothrow @nogc return
+    {
+        import mir.bignum.low_level_view : divm, BigUIntView;
+
+        enum m = ((data.length * (size_t.sizeof * 8)) / (uint.sizeof * 8));
+
+        if (length)
+        {
+            uint[m + 1] _div = void;
+            // shouldn't be unaligned here -- maybe a possibility though
+            _div[0 .. $ - 1] = cast(uint[m])data;
+            _div[$ - 1] = 0;
+
+            BigUIntView!uint dividend = BigUIntView!uint(_div);
+            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view;
+            BigUIntView!uint quotient = cast(BigUIntView!uint)view.unsigned;
+            data = 0;
+            divm(dividend, divisor, quotient);
+            length = cast(uint)view.normalized.coefficients.length;
+        }
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "/")(ulong rhs)
+        @safe pure nothrow @nogc return
+    {
+        if (length)
+        {
+            return this.opOpAssign!"/"(UInt!64(rhs));
+        }
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "/")(long rhs)
+        @safe pure nothrow @nogc return
+    {
+        if (length)
+        {
+            ulong div = rhs < 0 ? rhs * -1 : rhs;
+            auto result = this.opOpAssign!"/"(div);
+            // If this is a negative number, then we should keep the negative sign if rhs > 0.
+            // If it is not, then we should check if we're dividing by a negative number (rhs < 0),
+            // and apply the negative sign as such.
+            this.sign = result.sign ? rhs > 0 : rhs < 0; 
+        }
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "/", size_t rhsMaxSize64)(const ref BigInt!rhsMaxSize64 rhs)
+        @safe pure nothrow @nogc return
+    {
+        import mir.bignum.low_level_view : divm, BigUIntView;
+
+        enum m = (data.length * (size_t.sizeof * 8)), n = (rhsMaxSize64 / (uint.sizeof * 8));
+
+        if (length)
+        {
+            UInt!(m + (size_t.sizeof * 8)) _div;
+            // shouldn't be unaligned here -- maybe a possibility though
+            _div.data[0 .. $ - 1] = data;
+
+            BigUIntView!uint dividend = cast(BigUIntView!uint)_div.view;
+            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view.unsigned;
+            BigUIntView!uint quotient = cast(BigUIntView!uint)view.unsigned;
+            data = 0;
+            divm(dividend, divisor, quotient);
+            length = cast(uint)view.normalized.coefficients.length;
+
+            // If this is a negative number, then we should keep the negative sign if we are not negative.
+            // If we are not negative, then we should check if we're dividing by a negative number (,
+            // and apply the negative sign as such.
+            this.sign = rhs.sign ? this.sign != rhs.sign : this.sign;
+        }
+        return this;
+    }
+
+    /++
+    Performs `big %= rhs` operation.
+    Params:
+        rhs = unsigned value to divide by
+    Returns:
+        remainder from division
+    +/
+    ref opOpAssign(string op : "%", size_t size)(UInt!size rhs)
+        @safe pure nothrow @nogc return
+    {
+        import mir.bignum.low_level_view : divm, BigUIntView;
+
+        enum m = ((data.length * (size_t.sizeof * 8)) / (uint.sizeof * 8));
+
+        if (length)
+        {
+            uint[m+1] _div = void;
+            // We don't necessarily care about the quotient,
+            // so we should avoid an expensive 0-initialization here.
+            uint[m] q = void;
+            // shouldn't be unaligned here -- maybe a possibility though
+            _div[0 .. $ - 1] = cast(uint[m])data;
+            _div[$ - 1] = 0;
+
+            BigUIntView!uint dividend = BigUIntView!uint(_div);
+            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view;
+            BigUIntView!uint quotient = BigUIntView!uint(q); 
+            divm(dividend, divisor, quotient);
+
+            data = 0;
+            // FIXME: ugly ugly cast
+            (cast(uint[m])data)[0 .. dividend.coefficients.length] = dividend.coefficients;
+            auto normLen = cast(uint)dividend.normalized.coefficients.length;
+
+            static if (uint.sizeof != size_t.sizeof) {
+                // Round up where size_t is not as large as uint
+                normLen += (normLen % (size_t.sizeof / uint.sizeof));
+            } 
+            length = (normLen * (uint.sizeof * 8)) / (size_t.sizeof * 8);
+        }
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "%")(ulong rhs)
+        @safe pure nothrow @nogc return
+    {
+        if (length)
+        {
+            return this.opOpAssign!"%"(UInt!64(rhs));
+        }
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "%")(long rhs)
+        @safe pure nothrow @nogc return
+    {
+        if (length)
+        {
+            ulong div = rhs < 0 ? rhs * -1 : rhs;
+            this.opOpAssign!"%"(div);
+
+            // Add back if it is necessary (XXX: is this even correct??) 
+            if ((this.sign && rhs > 0) || (!this.sign && rhs < 0))
+            {
+                this.opOpAssign!("+")(BigIntView!(const size_t)(UInt!64(div).view, rhs < 0));
+            }
+        }
+        return this;
+    }
+
+    /// ditto
+    ref opOpAssign(string op : "%", size_t rhsMaxSize64)(ref const BigInt!rhsMaxSize64 rhs)
+    {
+        import mir.bignum.low_level_view : divm, BigUIntView;
+
+        enum m = ((data.length * (size_t.sizeof * 8)) / (uint.sizeof * 8));
+
+        if (length)
+        {
+            uint[m+1] _div = void;
+            // We don't necessarily care about the quotient,
+            // so we should avoid an expensive 0-initialization here.
+            uint[m] q = void;
+            // shouldn't be unaligned here -- maybe a possibility though
+            _div[0 .. $ - 1] = cast(uint[m])data;
+            _div[$ - 1] = 0;
+
+            BigUIntView!uint dividend = BigUIntView!uint(_div);
+            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view.unsigned;
+            BigUIntView!uint quotient = BigUIntView!uint(q); 
+            divm(dividend, divisor, quotient);
+
+            data = 0;
+            (cast(uint[m])data)[0 .. dividend.coefficients.length] = dividend.coefficients;
+
+            // Round to even upwards
+            auto normLen = cast(uint)dividend.normalized.coefficients.length;
+
+            static if (uint.sizeof != size_t.sizeof) {
+                // Round up where size_t is not as large as uint
+                normLen += (normLen % (size_t.sizeof / uint.sizeof));
+            } 
+            length = (normLen * (uint.sizeof * 8)) / (size_t.sizeof * 8);
+            // Add back if it is necessary (XXX: is this even correct??) 
+            if ((this.sign && !rhs.sign) || (!this.sign && rhs.sign))
+            {
+                this.opOpAssign!("+")(rhs);
+            }
+        }
+
+        return this;
+    }
+
     /++
     Performs `size_t overflow = big *= fixed` operatrion.
     Params:
@@ -363,7 +565,6 @@ struct BigInt(uint maxSize64)
         }
         return overflow;
     }
-
     /++
     +/
     static BigInt fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
@@ -647,6 +848,94 @@ struct BigInt(uint maxSize64)
 }
 
 
+///
+version(mir_bignum_test)
+unittest
+{
+    import mir.bignum.fixed;
+    import mir.bignum.low_level_view;
+
+    {
+        auto a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
+        auto b = UInt!128.fromHexString("f79a222050aaeaaa417fa25a2ac93291");
+
+        assert((a /= b) == BigInt!4.fromHexString("ca3d7e25aebe687b7cc1b250b44690fb"));
+        a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
+        assert((a %= b) == BigInt!4.fromHexString("bf4c87424431d21563f23b1fc00d75ac"));
+    }
+
+    {
+        auto a = BigInt!4.fromHexString("7fff000080000000000000000000");
+        auto b = UInt!128.fromHexString("80000000000000000001"); 
+
+        assert((a /= b) == BigInt!4.fromHexString("fffe0000"));
+        a = BigInt!4.fromHexString("7fff000080000000000000000000");
+        assert((a %= b) == BigInt!4.fromHexString("7fffffffffff00020000"));
+    }
+
+    {
+        auto a = BigInt!16.fromHexString("76d053cdcc87ec8c9455c375d6a08c799fad73cf07415e70af5dfacaff4bd306647a7cceb98839cce89ae65900938821564fd2af3c9d881c172264bb17e3530ce79b938d5eb7ffec558be43ab5b684978417c5053fb8df63fc65c9efd8b2e86469c53259509eb597f81647930f24ef05a79bfecf04e5ec52414c6a3f7481d533");
+        auto b = UInt!128.fromHexString("9c5c1aa6ad7ad18065a3a74598e27bee");
+
+        assert((a /= b) == BigInt!16.fromHexString("c2871f2b07522bda1e63de12850d2208bb242c716b5739d6744ee1d9c937b8d765d3742e18785d08c2405e5c83f3c875d5726d09dfaee29e813675a4f91bfee01e8cbbbca9588325d54cf2a625faffde4d8709e0517f786f609d8ce6997e0e71d2f976ae169b0c4be7a7dba3135af96c"));
+        a = BigInt!16.fromHexString("76d053cdcc87ec8c9455c375d6a08c799fad73cf07415e70af5dfacaff4bd306647a7cceb98839cce89ae65900938821564fd2af3c9d881c172264bb17e3530ce79b938d5eb7ffec558be43ab5b684978417c5053fb8df63fc65c9efd8b2e86469c53259509eb597f81647930f24ef05a79bfecf04e5ec52414c6a3f7481d533");
+        assert((a %= b) == BigInt!16.fromHexString("85d81587a8b62af1874315d26ebf0ecb"));
+    }
+
+    {
+        auto a = BigInt!4.fromHexString("DEADBEEF");
+        auto b = UInt!256.fromHexString("18aeff9fa4aace484a9f8f9002cdf38fa6e53fc0f6c035051dc86931c1c08316");
+
+        assert((a /= b) == 0);
+        a = BigInt!4.fromHexString("DEADBEEF");
+        assert((a %= b) == 0xDEADBEEF);
+    }
+
+    {
+        auto a = BigInt!4.fromHexString("DEADBEEF");
+        assert((a /= -0xDEADL) == -65536);
+        a = BigInt!4.fromHexString("DEADBEEF"); 
+        assert((a %= 0xDEADL) == 48879);
+        a = BigInt!4.fromHexString("DEADBEEF"); 
+        assert((a %= -0xDEADL) == -8126);
+        a = BigInt!4.fromHexString("-DEADBEEF");
+        assert((a %= 0xDEADL) == 8126);
+        a = BigInt!4.fromHexString("-DEADBEEF");
+        assert((a %= -0xDEADL) == -48879);
+    }
+
+    {
+        // Test whether or not our sign flipping is actually correct in division
+        auto a = BigInt!4.fromHexString("DEADBEEF");
+        auto b = BigInt!4.fromHexString("DEAD");
+        assert((a /= b) == 65536);
+        a = BigInt!4.fromHexString("-DEADBEEF");
+        b = BigInt!4.fromHexString("DEAD");
+        assert((a /= b) == -65536);
+        a = BigInt!4.fromHexString("DEADBEEF");
+        b = BigInt!4.fromHexString("-DEAD");
+        assert((a /= b) == -65536);
+        a = BigInt!4.fromHexString("-DEADBEEF");
+        b = BigInt!4.fromHexString("-DEAD");
+        assert((a /= b) == 65536);
+
+        // Test whether or not our sign flipping is actually correct in rem
+        a = BigInt!4.fromHexString("DEADBEEF");
+        b = BigInt!4.fromHexString("DEAD");
+        assert((a %= b) == 48879);
+        a = BigInt!4.fromHexString("DEADBEEF");
+        b = BigInt!4.fromHexString("-DEAD");
+        assert((a %= b) == -8126);
+        a = BigInt!4.fromHexString("-DEADBEEF");
+        b = BigInt!4.fromHexString("DEAD");
+        assert((a %= b) == 8126);
+        a = BigInt!4.fromHexString("-DEADBEEF");
+        b = BigInt!4.fromHexString("-DEAD");
+        assert((a %= b) == -48879);
+    }
+    
+}
+
 ///
 version(mir_bignum_test)
 unittest
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 9d84ffde..6826671c 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -122,6 +122,207 @@ version(mir_bignum_test)
     assert(ceilLog10Exp2(10UL) == 4); // ulong
 }
 
+/++
+Performs `q = u / v, u = u % v` (unsigned)
+Params:
+    _u = The dividend (which will contain the remainder after the algorithm has run)
+    _v = The divisor
+    _q = The quotient
+Preconditions:
+    * non-empty coefficients
+    * little-endian order
+    * _u.coefficients.length >= _u.normalized.coefficients.length + 1
+    * _v.coefficients.length >= _v.normalized.coefficients.length
+    * _q.coefficients.length >= (_u.normalized.coefficients.length - _v.normalized.coefficients.length) + 1
++/
+void divm(scope ref BigUIntView!uint _u, scope BigUIntView!uint _v, scope ref BigUIntView!uint _q)
+    @trusted pure nothrow @nogc
+{
+    // Adopted from Hacker's Delight (2nd Edition), 4-2
+    // License permits inclusion:
+    // You are free to use, copy, and distribute any of the code on this web site, whether modified by you or not.
+    // You need not give attribution. This includes the algorithms (some of which appear in Hacker's Delight), the Hacker's Assistant, and any code submitted by readers. Submitters implicitly agree to this.
+    // The textural material and pictures are copyright by the author, and the usual copyright rules apply. E.g., you may store the material on your computer and make hard or soft copies for your own use.
+    // However, you may not incorporate this material into another publication without written permission from the author (which the author may give by email).
+    // The author has taken care in the preparation of this material, but makes no expressed or implied warranty of any kind and assumes no responsibility for errors or omissions.
+    // No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. 
+    import mir.bitop: ctlz;
+    import mir.utility: _expect;
+
+    enum shift = (uint.sizeof * 8);
+
+    // Convert all of our input arguments to little-endian
+    auto u = _u.leastSignificantFirst();
+    auto v = _v.leastSignificantFirst();
+    auto q = _q.leastSignificantFirst();
+    size_t m = _u.normalized.coefficients.length, n = _v.normalized.coefficients.length;
+
+    assert(m != 0 && n != 0);
+    assert(_u.coefficients.length >= m + 1, "Dividend array is too small (should be m+1)");
+    assert(_v.coefficients.length >= n, "Divisor array is too small");
+    assert(v[n - 1] != 0);
+
+    // Handle a common edge case with division (where the dividend is smaller then the divisor)
+    // We don't care about how big the quotient array is here (and in fact, our math will fail)
+    // since we just dump the dividend into the remainder 
+    if (_expect(m < n, false))
+    {
+        _u.coefficients = _u.normalized.coefficients;
+        return;
+    }
+
+    // From this point on, now we care if the quotient array is appropriately sized
+    assert(_q.coefficients.length >= (m - n) + 1, "Quotient array is too small");
+
+    // According to Knuth, when the divisor length is equal to one,
+    // we should fall back to a simplified algorithm
+    if (_expect(n == 1, false))
+    {
+        uint k = 0;
+        foreach_reverse(j; 0 .. m)
+        {
+            // Same optimization as down below (left-shifting / ORing in place of multiplication / addition)
+            auto ut = ((cast(ulong)(k) << shift) | u[j]); 
+            q[j] = cast(uint)(ut / v[0]);
+            k = cast(uint)(ut % v[0]);
+            u[j] = 0;
+        }
+        _u.leastSignificant = k;
+    }
+    else
+    {
+        // pre-condition for this algorithm to succeed.
+        // assert(m >= n, "failed algorithm pre-condition");
+
+        // D1: Normalize
+        // In TAOCP, this is specified as b/(v[n - 1] + 1)
+        auto s = cast(uint)ctlz(v[n - 1]);
+        assert(s <= 31 && s >= 0);
+
+        // This normally uses a for loop, but we just use
+        // a LLV primitive here instead.
+        _v.smallLeftShiftInPlace(s);
+        _u.smallLeftShiftInPlace(s);
+
+        foreach_reverse(j; 0 .. (m - n) + 1)
+        {
+            // D3: Calculate qhat, rhat == remainder
+            // In TOACP, this is specified as u[j+n]*b + u[j+n-1] / v[n - 1]
+            // but, since we assume b is a power of 2, we can rewrite it to just
+            // use left-shifting (as left-shifting effectively multiples by powers of 2)
+            // and use an OR in place of the addition (as we've cleared the bottom 32 bits)
+            auto ut = ((cast(ulong)(u[j+n]) << shift) | u[j + n - 1]); 
+            ulong qhat = ut / v[n - 1];
+            ulong rhat = ut % v[n - 1];
+
+            // Similarly here, this is specified as qhat*v[n-2] > rhat*b + u[j+n-2]
+            // but we can just get away with a shift instead.
+            while (qhat >= uint.max || qhat*v[n-2] > (rhat << shift) + u[j+n-2])
+            {
+                qhat -= 1;
+                rhat += v[n - 1];
+
+                if (rhat > uint.max)
+                {
+                    break;
+                }
+            }
+
+            long t = 0, k = 0;
+            // D4: Multiply and subtract
+            foreach(i; 0 .. n)
+            {
+                ulong p = qhat * v[i];
+                // t = u[i + j] - k - (p & cast(uint)((ulong(1) << shift) - 1));
+                t = u[i + j] - k - (cast(uint)p);
+                u[i + j] = cast(uint)t;
+                k = (p >> shift) - (t >> shift);
+            }
+            
+            t = u[j + n] - k;
+            // These are admittedly super sketchy casts, but
+            // we really are only interested in the bottom 32-bits here.
+            u[j + n] = cast(uint)t;
+            q[j] = cast(uint)(qhat);
+            // D5: Test remainder
+            if (_expect(t < 0, false))
+            {
+                q[j] -= 1;
+                k = 0;
+                foreach(i; 0 .. n)
+                {
+                    t = cast(ulong)(u[i + j]) + v[i] + k;
+                    u[i + j] = cast(uint)t;
+                    k = t >> shift;
+                }
+                u[j + n] = cast(uint)(u[j + n] + k);
+            }
+        }
+
+        // D8: Un-normalize (obtain the remainder)
+        _u.smallRightShiftInPlace(s);
+        _v.smallRightShiftInPlace(s);
+    }
+
+    // Finally, normalize these when they're "headed out the door"
+    _u.coefficients = _u.normalized.coefficients;
+    _q.coefficients = _q.normalized.coefficients;
+}
+
+///
+version(mir_bignum_test)
+unittest
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.low_level_view: BigUIntView;
+
+    {
+        // Test division by a single-digit divisor here.
+        auto dividend = BigUIntView!uint.fromHexString("000000005");
+        auto divisor = BigUIntView!uint.fromHexString("5");
+        auto quotient = BigUIntView!uint.fromHexString("0");
+        // auto remainder = BigUIntView!uint.fromHexString("0");
+
+        divm(dividend, divisor, quotient);
+        assert(quotient == BigUIntView!uint.fromHexString("1"));
+        assert(dividend.coefficients.length == 0);
+    }
+
+    {
+        // Test division by a single-digit divisor here.
+        auto dividend = BigUIntView!uint.fromHexString("055a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
+        auto divisor = BigUIntView!uint.fromHexString("5");
+        auto quotient = BigUIntView!uint.fromHexString("0000000000000000000000000000000000000000000000000000000000000000");
+        divm(dividend, divisor, quotient);
+        assert(dividend.normalized == BigUIntView!uint.fromHexString("3"));
+        assert(quotient == BigUIntView!uint.fromHexString("1120a1229e8a217d0691b02b819107174a4b677088ef0df874259b283c1d588c"));
+    }
+
+    // Test big number division
+    {
+        auto dividend = BigUIntView!uint.fromHexString("055a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
+        auto divisor = BigUIntView!uint.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
+        auto quotient = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000");
+        // auto remainder = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+
+        divm(dividend, divisor, quotient);
+        assert(quotient.normalized == BigUIntView!uint.fromHexString("ff3a8aa4da35237811a0ffbf007fe938630dee8a810f2f82ae01f80c033291f6"));
+        assert(dividend.normalized == BigUIntView!uint.fromHexString("27926263cf248bef1c2cd63ea004d9f7041bffc8568560ec30fc9a9548057857"));
+    }
+
+    // Trigger the adding back sequence
+    {
+        auto dividend = BigUIntView!uint.fromHexString("0800000000000000000000003");
+        auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001");
+        auto quotient = BigUIntView!uint.fromHexString("0");
+        // auto remainder = BigUIntView!uint.fromHexString("000000000000000000000000");
+        divm(dividend, divisor, quotient);
+        assert(quotient == BigUIntView!uint.fromHexString("3"));
+        assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000"));
+    }
+    
+}
+
 /++
 Arbitrary length unsigned integer view.
 +/

From 1e911fa5cf64e957a1796b1566758d4732eb7c5f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 26 May 2022 20:54:51 +0400
Subject: [PATCH 390/573] fixup bigint

---
 source/mir/bignum/integer.d | 50 +++++++++++++++----------------------
 1 file changed, 20 insertions(+), 30 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 4a43ccbd..605e7f1e 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -329,7 +329,7 @@ struct BigInt(uint maxSize64)
     {
         import mir.bignum.low_level_view : divm, BigUIntView;
 
-        enum m = ((data.length * (size_t.sizeof * 8)) / (uint.sizeof * 8));
+        enum m = maxSize64 * 2;
 
         if (length)
         {
@@ -352,44 +352,36 @@ struct BigInt(uint maxSize64)
     ref opOpAssign(string op : "/")(ulong rhs)
         @safe pure nothrow @nogc return
     {
-        if (length)
-        {
-            return this.opOpAssign!"/"(UInt!64(rhs));
-        }
-        return this;
+        return this.opOpAssign!"/"(UInt!64(rhs));
     }
 
     /// ditto
     ref opOpAssign(string op : "/")(long rhs)
         @safe pure nothrow @nogc return
     {
-        if (length)
-        {
-            ulong div = rhs < 0 ? rhs * -1 : rhs;
-            auto result = this.opOpAssign!"/"(div);
-            // If this is a negative number, then we should keep the negative sign if rhs > 0.
-            // If it is not, then we should check if we're dividing by a negative number (rhs < 0),
-            // and apply the negative sign as such.
-            this.sign = result.sign ? rhs > 0 : rhs < 0; 
-        }
-        return this;
+        this.sign ^= rhs < 0; 
+        ulong div = rhs < 0 ? -rhs : rhs;
+        return this.opOpAssign!"/"(div);
     }
 
     /// ditto
-    ref opOpAssign(string op : "/", size_t rhsMaxSize64)(const ref BigInt!rhsMaxSize64 rhs)
+    ref opOpAssign(string op : "/", size_t rhsMaxSize64)(scope ref const BigInt!rhsMaxSize64 rhs)
         @safe pure nothrow @nogc return
     {
         import mir.bignum.low_level_view : divm, BigUIntView;
 
-        enum m = (data.length * (size_t.sizeof * 8)), n = (rhsMaxSize64 / (uint.sizeof * 8));
+        enum m = maxSize64 * 2;
+        enum n = rhsMaxSize64 / uint.sizeof;
 
         if (length)
         {
-            UInt!(m + (size_t.sizeof * 8)) _div;
+
+            uint[m + 1] _div = void;
             // shouldn't be unaligned here -- maybe a possibility though
-            _div.data[0 .. $ - 1] = data;
+            _div[0 .. $ - 1] = cast(uint[m])data;
+            _div[$ - 1] = 0;
 
-            BigUIntView!uint dividend = cast(BigUIntView!uint)_div.view;
+            BigUIntView!uint dividend = BigUIntView!uint(_div);
             BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view.unsigned;
             BigUIntView!uint quotient = cast(BigUIntView!uint)view.unsigned;
             data = 0;
@@ -416,11 +408,11 @@ struct BigInt(uint maxSize64)
     {
         import mir.bignum.low_level_view : divm, BigUIntView;
 
-        enum m = ((data.length * (size_t.sizeof * 8)) / (uint.sizeof * 8));
+        enum m = maxSize64 * 2;
 
         if (length)
         {
-            uint[m+1] _div = void;
+            uint[m + 1] _div = void;
             // We don't necessarily care about the quotient,
             // so we should avoid an expensive 0-initialization here.
             uint[m] q = void;
@@ -451,14 +443,11 @@ struct BigInt(uint maxSize64)
     ref opOpAssign(string op : "%")(ulong rhs)
         @safe pure nothrow @nogc return
     {
-        if (length)
-        {
-            return this.opOpAssign!"%"(UInt!64(rhs));
-        }
-        return this;
+        return this.opOpAssign!"%"(UInt!64(rhs));
     }
 
     /// ditto
+    // TODO: check
     ref opOpAssign(string op : "%")(long rhs)
         @safe pure nothrow @nogc return
     {
@@ -481,11 +470,11 @@ struct BigInt(uint maxSize64)
     {
         import mir.bignum.low_level_view : divm, BigUIntView;
 
-        enum m = ((data.length * (size_t.sizeof * 8)) / (uint.sizeof * 8));
+        enum m = maxSize64 * 2;
 
         if (length)
         {
-            uint[m+1] _div = void;
+            uint[m + 1] _div = void;
             // We don't necessarily care about the quotient,
             // so we should avoid an expensive 0-initialization here.
             uint[m] q = void;
@@ -509,6 +498,7 @@ struct BigInt(uint maxSize64)
                 normLen += (normLen % (size_t.sizeof / uint.sizeof));
             } 
             length = (normLen * (uint.sizeof * 8)) / (size_t.sizeof * 8);
+            // TODO: check
             // Add back if it is necessary (XXX: is this even correct??) 
             if ((this.sign && !rhs.sign) || (!this.sign && rhs.sign))
             {

From 81b8b38d985d860fdf055e44da5ff5e6960b71e4 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 27 May 2022 14:27:51 +0400
Subject: [PATCH 391/573] rework bigint division

---
 source/mir/bignum/fixed.d          |  76 +++---
 source/mir/bignum/integer.d        | 367 +++++++++++++----------------
 source/mir/bignum/low_level_view.d | 162 ++++++++-----
 3 files changed, 306 insertions(+), 299 deletions(-)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index a78b8c13..81dc20c5 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -401,40 +401,26 @@ struct UInt(size_t size)
     Performs division & extracts the remainder.
     Params:
         rhs = unsigned value to divide by
-    Returns:
-        unsigned remainder value
+    Returns: quotient, sets `rhs` to remainder
     +/ 
-    auto divRem(size_t rhsSize)(const UInt!rhsSize rhs)
-        @safe pure nothrow @nogc scope const
+    ref divRem(size_t rhsSize)(scope ref UInt!rhsSize rhs)
+        @safe pure nothrow @nogc scope return
     {
         import mir.bignum.low_level_view : divm, BigUIntView;
 
-        enum m = (size / (uint.sizeof * 8)), n = (rhsSize / (uint.sizeof * 8));
-
-        static struct QuotientAndRemainder
-        {
-            // XXX: we have to assume the quotient size here,
-            // checking at runtime would be more accurate for better space savings
-            UInt!size quotient;
-            UInt!rhsSize remainder;
-        }
-
-        QuotientAndRemainder val;
-
-        uint[m + 1] _div = void;
-        // shouldn't be unaligned here -- maybe a possibility though
-        _div[$ - 1] = 0;
-        _div[0 .. $ - 1] = (cast(uint[data.sizeof / 4])data);
+        UInt!size quotient;
 
-        BigUIntView!uint dividend = BigUIntView!uint(_div);
-        BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view;
-        BigUIntView!uint quotient = cast(BigUIntView!uint)val.quotient.view;
-        divm(dividend, divisor, quotient);
+        BigUIntView!uint dividendV = cast(BigUIntView!uint) this.view;
+        BigUIntView!uint divisorV = cast(BigUIntView!uint) rhs.view;
+        BigUIntView!uint quotientV = cast(BigUIntView!uint) quotient.view;
+        divisorV = divisorV.normalized;
+        dividendV = dividendV.normalized;
 
-        BigUIntView!uint remainder = cast(BigUIntView!uint)val.remainder.view;
-        remainder.coefficients[0 .. $] = (cast(uint[])_div)[0 .. remainder.coefficients.length];
+        divm(dividendV, divisorV, quotientV);
+        rhs = cast(UInt!rhsSize) this;
+        this = quotient;
 
-        return val;
+        return this;
     }
 
     /++
@@ -447,10 +433,7 @@ struct UInt(size_t size)
     ref opOpAssign(string op : "/", size_t rhsSize)(UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        auto result = divRem(rhs);
-        // XXX: fix this if we manage to calculate the quotient size better
-        this.data = result.quotient.data;
-        return this;
+        return divRem(rhs);
     }
 
     /// ditto
@@ -470,11 +453,8 @@ struct UInt(size_t size)
     ref opOpAssign(string op : "%", size_t rhsSize)(UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        auto result = divRem(rhs);
-        this.data = 0;
-        // We ensure that size >= rhsSize, so this is fine
-        this.data[0 .. result.remainder.data.length] = result.remainder.data;
-        return this;
+        divRem(rhs);
+        this = cast(UInt!size)rhs;
     }
 
     /// ditto
@@ -795,6 +775,30 @@ struct UInt(size_t size)
         }
     }
 
+    /++
+    +/
+    T opCast(T)() const @safe pure nothrow @nogc
+        if (is(T == UInt!newSize, uint newSize))
+    {
+        enum newLength = typeof(return).data.length;
+        static if (newLength <= data.length)
+        {
+            version (LittleEndian)
+                return typeof(return)(data[0 .. newLength]);
+            else
+                return typeof(return)(data[$ - newLength .. $]);
+        }
+        else
+        {
+            typeof(return) ret;
+            version (LittleEndian)
+                ret.data[0 .. data.length] = data;
+            else
+                ret.data[$ - data.length .. $] = data;
+            return ret;
+        }
+    }
+
     /++
     +/
     T opCast(T)() const
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 605e7f1e..9702736b 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -53,17 +53,35 @@ struct BigInt(uint maxSize64)
         sign = false;
         static if (size_t.sizeof == ulong.sizeof)
         {
-            length = 1;
+            length = data != 0;
             view.leastSignificantFirst[0] = data;
         }
         else
         {
-            length = 2;
             auto d = view.leastSignificantFirst;
             d[0] = cast(uint) data;
             d[1] = cast(uint) (data >> 32);
+            length = data && (data >> 32);
         }
-        normalize;
+    }
+
+    ///
+    this(long data)
+    {
+        this(ulong(data < 0 ? -data : data));
+        this.sign = data < 0;
+    }
+
+    ///
+    this(int data)
+    {
+        this(long(data));
+    }
+
+    ///
+    this(uint data)
+    {
+        this(ulong(data));
     }
 
     ///
@@ -115,8 +133,9 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    ref opAssign(uint rhsMaxSize64)(auto ref scope const BigInt!rhsMaxSize64 rhs) return
-        if (rhsMaxSize64 < maxSize64)
+    ref opAssign(uint rhsSize64)(auto ref scope const BigInt!rhsSize64 rhs) return
+        @safe pure nothrow @nogc
+        in (rhs.length <= maxSize64)
     {
         this.sign = rhs.sign;
         this.length = rhs.length;
@@ -170,7 +189,7 @@ struct BigInt(uint maxSize64)
     bool opEquals()(size_t rhs, bool rhsSign = false)
         const @safe pure nothrow @nogc
     {
-        return rhs == 0 && length == 0 || length == 1 && sign == rhsSign && view.unsigned.leastSignificant == rhs;
+        return rhs == 0 && this.length == 0 || this.length == 1 && this.sign == rhsSign && view.unsigned.leastSignificant == rhs;
     }
 
     ///
@@ -190,26 +209,27 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    BigIntView!size_t view()() @property
+    BigIntView!size_t view()() scope return @property
     {
         version (LittleEndian)
-            return typeof(return)(data[0 .. length], sign);
+            return typeof(return)(this.data[0 .. this.length], this.sign);
         else
-            return typeof(return)(data[$ - length .. $], sign);
+            return typeof(return)(this.data[$ - this.length .. $], this.sign);
     }
 
     ///
-    BigIntView!(const size_t) view()() const @property
+    BigIntView!(const size_t) view()() const scope return @property
     {
         version (LittleEndian)
-            return typeof(return)(data[0 .. length], sign);
+            return typeof(return)(this.data[0 .. this.length], this.sign);
         else
-            return typeof(return)(data[$ - length .. $], sign);
+            return typeof(return)(this.data[$ - this.length .. $], this.sign);
     }
 
     ///
     void normalize()()
     {
+        pragma(inline, false);
         auto norm = view.normalized;
         this.length = cast(uint) norm.unsigned.coefficients.length;
         this.sign = norm.sign;
@@ -317,195 +337,164 @@ struct BigInt(uint maxSize64)
         return overflow;
     }
 
-    /++
-    Performs `big /= rhs` operation.
-    Params:
-        rhs = unsigned value to divide by
-    Returns:
-        quotient from division
-    +/
-    ref opOpAssign(string op : "/", size_t size)(UInt!size rhs)
-        @safe pure nothrow @nogc return
+    ///
+    ref opOpAssign(string op, size_t size)(UInt!size rhs)
+        @safe pure nothrow @nogc scope return
+        if (op == "/" || op == "%")
     {
-        import mir.bignum.low_level_view : divm, BigUIntView;
-
-        enum m = maxSize64 * 2;
-
-        if (length)
-        {
-            uint[m + 1] _div = void;
-            // shouldn't be unaligned here -- maybe a possibility though
-            _div[0 .. $ - 1] = cast(uint[m])data;
-            _div[$ - 1] = 0;
-
-            BigUIntView!uint dividend = BigUIntView!uint(_div);
-            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view;
-            BigUIntView!uint quotient = cast(BigUIntView!uint)view.unsigned;
-            data = 0;
-            divm(dividend, divisor, quotient);
-            length = cast(uint)view.normalized.coefficients.length;
-        }
-        return this;
+        BigInt!(size / 64) bigRhs = rhs;
+        return this.opOpAssign!op(bigRhs);
     }
 
     /// ditto
-    ref opOpAssign(string op : "/")(ulong rhs)
-        @safe pure nothrow @nogc return
+    ref opOpAssign(string op)(ulong rhs)
+        @safe pure nothrow @nogc scope return
+        if (op == "/" || op == "%")
     {
-        return this.opOpAssign!"/"(UInt!64(rhs));
+        BigInt!1 bigRhs = rhs;
+        return this.opOpAssign!op(bigRhs);
     }
 
     /// ditto
-    ref opOpAssign(string op : "/")(long rhs)
-        @safe pure nothrow @nogc return
+    ref opOpAssign(string op)(long rhs)
+        @safe pure nothrow @nogc scope return
+        if (op == "/" || op == "%")
     {
-        this.sign ^= rhs < 0; 
-        ulong div = rhs < 0 ? -rhs : rhs;
-        return this.opOpAssign!"/"(div);
+        BigInt!1 bigRhs = rhs;
+        return this.opOpAssign!op(bigRhs);
     }
 
-    /// ditto
-    ref opOpAssign(string op : "/", size_t rhsMaxSize64)(scope ref const BigInt!rhsMaxSize64 rhs)
-        @safe pure nothrow @nogc return
+    private BigUIntView!uint divmView()() scope @property
     {
-        import mir.bignum.low_level_view : divm, BigUIntView;
-
-        enum m = maxSize64 * 2;
-        enum n = rhsMaxSize64 / uint.sizeof;
-
-        if (length)
+        static if (size_t.sizeof == 4)
+            return view.unsigned;
+        else
         {
-
-            uint[m + 1] _div = void;
-            // shouldn't be unaligned here -- maybe a possibility though
-            _div[0 .. $ - 1] = cast(uint[m])data;
-            _div[$ - 1] = 0;
-
-            BigUIntView!uint dividend = BigUIntView!uint(_div);
-            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view.unsigned;
-            BigUIntView!uint quotient = cast(BigUIntView!uint)view.unsigned;
-            data = 0;
-            divm(dividend, divisor, quotient);
-            length = cast(uint)view.normalized.coefficients.length;
-
-            // If this is a negative number, then we should keep the negative sign if we are not negative.
-            // If we are not negative, then we should check if we're dividing by a negative number (,
-            // and apply the negative sign as such.
-            this.sign = rhs.sign ? this.sign != rhs.sign : this.sign;
+            auto view = this.view.unsigned.opCast!(BigUIntView!(uint));
+            if (view.coefficients.length && view.mostSignificant == 0)
+                view.popMostSignificant;
+            return view;
         }
-        return this;
     }
 
-    /++
-    Performs `big %= rhs` operation.
-    Params:
-        rhs = unsigned value to divide by
-    Returns:
-        remainder from division
-    +/
-    ref opOpAssign(string op : "%", size_t size)(UInt!size rhs)
-        @safe pure nothrow @nogc return
+    private BigUIntView!(const uint) divmView()() const scope @property
     {
-        import mir.bignum.low_level_view : divm, BigUIntView;
-
-        enum m = maxSize64 * 2;
-
-        if (length)
+        static if (size_t.sizeof == 4)
+            return view.unsigned;
+        else
         {
-            uint[m + 1] _div = void;
-            // We don't necessarily care about the quotient,
-            // so we should avoid an expensive 0-initialization here.
-            uint[m] q = void;
-            // shouldn't be unaligned here -- maybe a possibility though
-            _div[0 .. $ - 1] = cast(uint[m])data;
-            _div[$ - 1] = 0;
-
-            BigUIntView!uint dividend = BigUIntView!uint(_div);
-            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view;
-            BigUIntView!uint quotient = BigUIntView!uint(q); 
-            divm(dividend, divisor, quotient);
-
-            data = 0;
-            // FIXME: ugly ugly cast
-            (cast(uint[m])data)[0 .. dividend.coefficients.length] = dividend.coefficients;
-            auto normLen = cast(uint)dividend.normalized.coefficients.length;
-
-            static if (uint.sizeof != size_t.sizeof) {
-                // Round up where size_t is not as large as uint
-                normLen += (normLen % (size_t.sizeof / uint.sizeof));
-            } 
-            length = (normLen * (uint.sizeof * 8)) / (size_t.sizeof * 8);
+            auto view = this.view.unsigned.opCast!(BigUIntView!(const uint));
+            if (view.coefficients.length && view.mostSignificant == 0)
+                view.popMostSignificant;
+            return view;
         }
-        return this;
     }
 
-    /// ditto
-    ref opOpAssign(string op : "%")(ulong rhs)
-        @safe pure nothrow @nogc return
-    {
-        return this.opOpAssign!"%"(UInt!64(rhs));
-    }
 
-    /// ditto
-    // TODO: check
-    ref opOpAssign(string op : "%")(long rhs)
-        @safe pure nothrow @nogc return
+    // TODO
+    ref modPow(scope BigUIntView!(const size_t) exponent, scope ref const BigInt modulus)
+        @safe pure nothrow @nogc return scope
     {
-        if (length)
+        pragma(inline, false);
+
+        import mir.ndslice.topology: bitwise;
+
+        if (modulus == 1 || modulus == -1)
         {
-            ulong div = rhs < 0 ? rhs * -1 : rhs;
-            this.opOpAssign!"%"(div);
+            this.sign = 0;
+            this.length = 0;
+            return this;
+        }
+
+        BigInt!256 bas = void;
+        bas = this;
+        BigInt!256 res = void;
+        res = 1u;
 
-            // Add back if it is necessary (XXX: is this even correct??) 
-            if ((this.sign && rhs > 0) || (!this.sign && rhs < 0))
+        auto expBits = exponent.leastSignificantFirst.bitwise;
+        expBits.popBackN(exponent.ctlz);
+
+        while (expBits.length)
+        {
+            bas %= modulus;
+            if (expBits.front)
             {
-                this.opOpAssign!("+")(BigIntView!(const size_t)(UInt!64(div).view, rhs < 0));
+                // res *= bas;
+                res %= modulus;
             }
+            expBits.popBack;
+            // bas *= bas;
         }
+        this = res;
         return this;
     }
 
-    /// ditto
-    ref opOpAssign(string op : "%", size_t rhsMaxSize64)(ref const BigInt!rhsMaxSize64 rhs)
+    /++
+    +/
+    ref divRem(uint rhsSize64)(
+        scope ref const BigInt!rhsSize64 divisor,
+        scope ref BigInt quotient)
+        @trusted pure nothrow @nogc
     {
         import mir.bignum.low_level_view : divm, BigUIntView;
 
-        enum m = maxSize64 * 2;
+        pragma(inline, false);
 
-        if (length)
+        if (divisor.length == 0 || divisor.view.unsigned.mostSignificant == 0)
+            assert(0, "Zero or denormalized BigInt divizor");
+
+        if (this.length < divisor.length)
         {
-            uint[m + 1] _div = void;
-            // We don't necessarily care about the quotient,
-            // so we should avoid an expensive 0-initialization here.
-            uint[m] q = void;
-            // shouldn't be unaligned here -- maybe a possibility though
-            _div[0 .. $ - 1] = cast(uint[m])data;
-            _div[$ - 1] = 0;
-
-            BigUIntView!uint dividend = BigUIntView!uint(_div);
-            BigUIntView!uint divisor = cast(BigUIntView!uint)rhs.view.unsigned;
-            BigUIntView!uint quotient = BigUIntView!uint(q); 
-            divm(dividend, divisor, quotient);
-
-            data = 0;
-            (cast(uint[m])data)[0 .. dividend.coefficients.length] = dividend.coefficients;
-
-            // Round to even upwards
-            auto normLen = cast(uint)dividend.normalized.coefficients.length;
-
-            static if (uint.sizeof != size_t.sizeof) {
-                // Round up where size_t is not as large as uint
-                normLen += (normLen % (size_t.sizeof / uint.sizeof));
-            } 
-            length = (normLen * (uint.sizeof * 8)) / (size_t.sizeof * 8);
-            // TODO: check
-            // Add back if it is necessary (XXX: is this even correct??) 
-            if ((this.sign && !rhs.sign) || (!this.sign && rhs.sign))
+            quotient.sign = 0;
+            quotient.length = 0;
+            return this;
+        }
+
+        quotient.length = this.length;
+
+        BigInt!(min(rhsSize64, maxSize64)) _divisor = void;
+        _divisor = divisor;
+
+        quotient.length = cast(uint) divm(
+            this.divmView,
+            _divisor.divmView,
+            cast(BigUIntView!uint) quotient.view.unsigned,
+        );
+
+        static if (size_t.sizeof == 8)
+        {
+            bool half = quotient.length & 1;
+            quotient.length >>= 1;
+            if (half)
             {
-                this.opOpAssign!("+")(rhs);
+                quotient.data[quotient.length++] &= uint.max;
             }
         }
 
+        quotient.sign = (this.sign ^ divisor.sign) && quotient.length;
+
+        this.length = _divisor.length;
+        this.normalize;
+
+        return this;
+    }
+
+    /++
+    Performs `this %= rhs` and `this /= rhs` operation.
+    Params:
+        rhs = value to divide by
+    Returns:
+        remainder or quotient from the truncated division
+    +/
+    ref opOpAssign(string op, size_t rhsSize64)(scope const ref BigInt!rhsSize64 rhs)
+        @safe pure nothrow @nogc return
+        if (op == "/" || op == "%")
+    {
+        import mir.bignum.low_level_view : divm, BigUIntView;
+        BigInt quotient = void;
+        divRem(rhs, quotient);
+        static if (op == "/")
+            this = quotient;
         return this;
     }
 
@@ -516,7 +505,7 @@ struct BigInt(uint maxSize64)
     Returns:
         overflow
     +/
-    bool opOpAssign(string op, size_t rhsMaxSize64)(ref const BigInt!rhsMaxSize64 rhs)
+    bool opOpAssign(string op, size_t rhsSize64)(ref const BigInt!rhsSize64 rhs)
         @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {
@@ -881,49 +870,25 @@ unittest
         assert((a %= b) == 0xDEADBEEF);
     }
 
+    void test(const long av, const long bv)
     {
-        auto a = BigInt!4.fromHexString("DEADBEEF");
-        assert((a /= -0xDEADL) == -65536);
-        a = BigInt!4.fromHexString("DEADBEEF"); 
-        assert((a %= 0xDEADL) == 48879);
-        a = BigInt!4.fromHexString("DEADBEEF"); 
-        assert((a %= -0xDEADL) == -8126);
-        a = BigInt!4.fromHexString("-DEADBEEF");
-        assert((a %= 0xDEADL) == 8126);
-        a = BigInt!4.fromHexString("-DEADBEEF");
-        assert((a %= -0xDEADL) == -48879);
+        auto a = BigInt!4(av);
+        const b = BigInt!4(bv);
+        a /= b;
+        assert(a == av / bv);
+        a = BigInt!4(av);
+        a %= b;
+        assert(a == av % bv);
     }
 
     {
-        // Test whether or not our sign flipping is actually correct in division
-        auto a = BigInt!4.fromHexString("DEADBEEF");
-        auto b = BigInt!4.fromHexString("DEAD");
-        assert((a /= b) == 65536);
-        a = BigInt!4.fromHexString("-DEADBEEF");
-        b = BigInt!4.fromHexString("DEAD");
-        assert((a /= b) == -65536);
-        a = BigInt!4.fromHexString("DEADBEEF");
-        b = BigInt!4.fromHexString("-DEAD");
-        assert((a /= b) == -65536);
-        a = BigInt!4.fromHexString("-DEADBEEF");
-        b = BigInt!4.fromHexString("-DEAD");
-        assert((a /= b) == 65536);
-
-        // Test whether or not our sign flipping is actually correct in rem
-        a = BigInt!4.fromHexString("DEADBEEF");
-        b = BigInt!4.fromHexString("DEAD");
-        assert((a %= b) == 48879);
-        a = BigInt!4.fromHexString("DEADBEEF");
-        b = BigInt!4.fromHexString("-DEAD");
-        assert((a %= b) == -8126);
-        a = BigInt!4.fromHexString("-DEADBEEF");
-        b = BigInt!4.fromHexString("DEAD");
-        assert((a %= b) == 8126);
-        a = BigInt!4.fromHexString("-DEADBEEF");
-        b = BigInt!4.fromHexString("-DEAD");
-        assert((a %= b) == -48879);
-    }
-    
+        auto av = 0xDEADBEEF;
+        auto bv = 0xDEAD;
+        test(+av, +bv);
+        test(+av, -bv);
+        test(-av, +bv);
+        test(+av, +bv);
+    }
 }
 
 ///
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 6826671c..865b640c 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -122,6 +122,17 @@ version(mir_bignum_test)
     assert(ceilLog10Exp2(10UL) == 4); // ulong
 }
 
+// size_t mulm(scope BigUIntView!size_t _c, scope BigUIntView!(const size_t) _a, scope BigUIntView!(const size_t) _b)
+//     @trusted pure nothrow @nogc
+//     in (_c.length >= _a.length * _b.length)
+// {
+//     if (_a.length == 0 || _b.length == 0)
+//         return 0;
+//     auto a = _a.leastSignificantFirst;
+//     auto b = _b.leastSignificantFirst;
+//     auto c = _c.leastSignificantFirst;
+// }
+
 /++
 Performs `q = u / v, u = u % v` (unsigned)
 Params:
@@ -131,12 +142,15 @@ Params:
 Preconditions:
     * non-empty coefficients
     * little-endian order
-    * _u.coefficients.length >= _u.normalized.coefficients.length + 1
-    * _v.coefficients.length >= _v.normalized.coefficients.length
+    * _u.coefficients.length >= _u.normalized.coefficients.length
     * _q.coefficients.length >= (_u.normalized.coefficients.length - _v.normalized.coefficients.length) + 1
 +/
-void divm(scope ref BigUIntView!uint _u, scope BigUIntView!uint _v, scope ref BigUIntView!uint _q)
+size_t divm(scope BigUIntView!uint _u, scope BigUIntView!uint _v, scope BigUIntView!uint _q)
     @trusted pure nothrow @nogc
+    in (_u.coefficients.length == 0 || _u.mostSignificant)
+    in (_v.coefficients.length)
+    in (_v.mostSignificant)
+    in (sizediff_t(_q.coefficients.length) >= sizediff_t(_u.coefficients.length - _v.coefficients.length + 1))
 {
     // Adopted from Hacker's Delight (2nd Edition), 4-2
     // License permits inclusion:
@@ -149,45 +163,43 @@ void divm(scope ref BigUIntView!uint _u, scope BigUIntView!uint _v, scope ref Bi
     import mir.bitop: ctlz;
     import mir.utility: _expect;
 
-    enum shift = (uint.sizeof * 8);
-
     // Convert all of our input arguments to little-endian
+    size_t m = _u.coefficients.length;
+    size_t n = _v.coefficients.length;
     auto u = _u.leastSignificantFirst();
     auto v = _v.leastSignificantFirst();
     auto q = _q.leastSignificantFirst();
-    size_t m = _u.normalized.coefficients.length, n = _v.normalized.coefficients.length;
 
-    assert(m != 0 && n != 0);
-    assert(_u.coefficients.length >= m + 1, "Dividend array is too small (should be m+1)");
-    assert(_v.coefficients.length >= n, "Divisor array is too small");
-    assert(v[n - 1] != 0);
 
     // Handle a common edge case with division (where the dividend is smaller then the divisor)
     // We don't care about how big the quotient array is here (and in fact, our math will fail)
     // since we just dump the dividend into the remainder 
     if (_expect(m < n, false))
     {
-        _u.coefficients = _u.normalized.coefficients;
-        return;
+        return 0;
     }
 
     // From this point on, now we care if the quotient array is appropriately sized
-    assert(_q.coefficients.length >= (m - n) + 1, "Quotient array is too small");
 
     // According to Knuth, when the divisor length is equal to one,
     // we should fall back to a simplified algorithm
+    sizediff_t lastQ = -1;
     if (_expect(n == 1, false))
     {
         uint k = 0;
+        uint d = v[0];
         foreach_reverse(j; 0 .. m)
         {
             // Same optimization as down below (left-shifting / ORing in place of multiplication / addition)
-            auto ut = ((cast(ulong)(k) << shift) | u[j]); 
-            q[j] = cast(uint)(ut / v[0]);
-            k = cast(uint)(ut % v[0]);
+            auto ut = (cast(ulong)k << 32) | u[j]; 
+            q[j] = cast(uint)(ut / d);
+            k = ut % d;
             u[j] = 0;
+            if (lastQ == -1 && q[j])
+                lastQ = j;
         }
-        _u.leastSignificant = k;
+        u[0] = k;
+        _u = _u.topLeastSignificantPart(k != 0);
     }
     else
     {
@@ -201,23 +213,28 @@ void divm(scope ref BigUIntView!uint _u, scope BigUIntView!uint _v, scope ref Bi
 
         // This normally uses a for loop, but we just use
         // a LLV primitive here instead.
-        _v.smallLeftShiftInPlace(s);
+        uint umn = s ? _u.mostSignificant >> (32 - s) : 0;
         _u.smallLeftShiftInPlace(s);
+        _v.smallLeftShiftInPlace(s);
 
-        foreach_reverse(j; 0 .. (m - n) + 1)
+        uint* ujnp = &umn;
+        ref uint ujn() @property { return *ujnp; }
+        size_t j = m - n;
+        do
         {
             // D3: Calculate qhat, rhat == remainder
-            // In TOACP, this is specified as u[j+n]*b + u[j+n-1] / v[n - 1]
+            // In TOACP, this is specified as ujn*b + u[j + n - 1] / v[n - 1]
             // but, since we assume b is a power of 2, we can rewrite it to just
             // use left-shifting (as left-shifting effectively multiples by powers of 2)
             // and use an OR in place of the addition (as we've cleared the bottom 32 bits)
-            auto ut = ((cast(ulong)(u[j+n]) << shift) | u[j + n - 1]); 
+
+            auto ut = ((cast(ulong) ujn << 32) | u[j + n - 1]); 
             ulong qhat = ut / v[n - 1];
             ulong rhat = ut % v[n - 1];
 
-            // Similarly here, this is specified as qhat*v[n-2] > rhat*b + u[j+n-2]
+            // Similarly here, this is specified as qhat*v[n - 2] > rhat*b + u[j + n - 2]
             // but we can just get away with a shift instead.
-            while (qhat >= uint.max || qhat*v[n-2] > (rhat << shift) + u[j+n-2])
+            while (qhat >= uint.max || qhat * v[n - 2] > (rhat << 32) + u[j + n - 2])
             {
                 qhat -= 1;
                 rhat += v[n - 1];
@@ -230,43 +247,50 @@ void divm(scope ref BigUIntView!uint _u, scope BigUIntView!uint _v, scope ref Bi
 
             long t = 0, k = 0;
             // D4: Multiply and subtract
-            foreach(i; 0 .. n)
+            size_t i;
+            do
             {
                 ulong p = qhat * v[i];
-                // t = u[i + j] - k - (p & cast(uint)((ulong(1) << shift) - 1));
-                t = u[i + j] - k - (cast(uint)p);
-                u[i + j] = cast(uint)t;
-                k = (p >> shift) - (t >> shift);
+                // t = u[i + j] - k - (p & cast(uint)((ulong(1) << 32) - 1));
+                t = u[i + j] - k - cast(uint) p;
+                u[i + j] = cast(uint) t;
+                k = (p >> 32) - (t >> 32);
             }
+            while(++i < n);
             
-            t = u[j + n] - k;
+            t = ujn - k;
             // These are admittedly super sketchy casts, but
             // we really are only interested in the bottom 32-bits here.
-            u[j + n] = cast(uint)t;
-            q[j] = cast(uint)(qhat);
+            ujn = cast(uint) t;
+            q[j] = cast(uint) qhat;
             // D5: Test remainder
             if (_expect(t < 0, false))
             {
                 q[j] -= 1;
                 k = 0;
-                foreach(i; 0 .. n)
+                i = 0;
+                do
                 {
-                    t = cast(ulong)(u[i + j]) + v[i] + k;
-                    u[i + j] = cast(uint)t;
-                    k = t >> shift;
+                    t = cast(ulong) u[i + j] + v[i] + k;
+                    u[i + j] = cast(uint) t;
+                    k = t >> 32;
                 }
-                u[j + n] = cast(uint)(u[j + n] + k);
+                while(++i < n);
+                ujn = cast(uint) (ujn + k);
             }
+            if (lastQ == -1 && q[j])
+                lastQ = j;
+            ujnp = &u[j - 1 + n];
         }
+        while(j--);
 
         // D8: Un-normalize (obtain the remainder)
         _u.smallRightShiftInPlace(s);
-        _v.smallRightShiftInPlace(s);
+        _u.mostSignificant |= s ? umn << (32 - s) : 0;
+        // _v.smallRightShiftInPlace(s);
     }
 
-    // Finally, normalize these when they're "headed out the door"
-    _u.coefficients = _u.normalized.coefficients;
-    _q.coefficients = _q.normalized.coefficients;
+    return lastQ + 1;
 }
 
 ///
@@ -278,19 +302,18 @@ unittest
 
     {
         // Test division by a single-digit divisor here.
-        auto dividend = BigUIntView!uint.fromHexString("000000005");
+        auto dividend = BigUIntView!uint.fromHexString("5");
         auto divisor = BigUIntView!uint.fromHexString("5");
         auto quotient = BigUIntView!uint.fromHexString("0");
         // auto remainder = BigUIntView!uint.fromHexString("0");
 
         divm(dividend, divisor, quotient);
         assert(quotient == BigUIntView!uint.fromHexString("1"));
-        assert(dividend.coefficients.length == 0);
     }
 
     {
         // Test division by a single-digit divisor here.
-        auto dividend = BigUIntView!uint.fromHexString("055a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
+        auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
         auto divisor = BigUIntView!uint.fromHexString("5");
         auto quotient = BigUIntView!uint.fromHexString("0000000000000000000000000000000000000000000000000000000000000000");
         divm(dividend, divisor, quotient);
@@ -300,7 +323,7 @@ unittest
 
     // Test big number division
     {
-        auto dividend = BigUIntView!uint.fromHexString("055a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
+        auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
         auto divisor = BigUIntView!uint.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
         auto quotient = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000");
         // auto remainder = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
@@ -312,7 +335,7 @@ unittest
 
     // Trigger the adding back sequence
     {
-        auto dividend = BigUIntView!uint.fromHexString("0800000000000000000000003");
+        auto dividend = BigUIntView!uint.fromHexString("800000000000000000000003");
         auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001");
         auto quotient = BigUIntView!uint.fromHexString("0");
         // auto remainder = BigUIntView!uint.fromHexString("000000000000000000000000");
@@ -320,7 +343,6 @@ unittest
         assert(quotient == BigUIntView!uint.fromHexString("3"));
         assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000"));
     }
-    
 }
 
 /++
@@ -614,8 +636,15 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     static if (endian == TargetEndian)
     ///
-    @trusted pure nothrow @nogc
-    BigUIntView!V opCast(T : BigUIntView!V, V)() return scope
+    pure nothrow @nogc
+    BigUIntView!V opCast(T : BigUIntView!V, V)()
+        if (V.sizeof <= W.sizeof)
+    {
+        return typeof(return)(cast(V[])this.coefficients);
+    }
+
+    pure nothrow @nogc
+    BigUIntView!V opCast(T : BigUIntView!V, V)() const
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(cast(V[])this.coefficients);
@@ -623,7 +652,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     BigUIntView!(const W, endian) lightConst()()
-        const @safe pure nothrow @nogc @property return scope
+        const @safe pure nothrow @nogc @property
     {
         return typeof(return)(coefficients);
     }
@@ -652,7 +681,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    ref inout(W) mostSignificant() inout @property return scope
+    ref inout(W) mostSignificant() inout @property
     {
         static if (endian == WordEndian.big)
             return coefficients[0];
@@ -662,7 +691,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    ref inout(W) leastSignificant() inout @property return scope
+    ref inout(W) leastSignificant() inout @property
     {
         static if (endian == WordEndian.little)
             return coefficients[0];
@@ -692,7 +721,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigUIntView topMostSignificantPart(size_t length) return scope
+    BigUIntView topMostSignificantPart(size_t length)
     {
         static if (endian == WordEndian.big)
             return BigUIntView(coefficients[0 .. length]);
@@ -702,7 +731,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigUIntView topLeastSignificantPart(size_t length) return scope
+    BigUIntView topLeastSignificantPart(size_t length)
     {
         static if (endian == WordEndian.little)
             return BigUIntView(coefficients[0 .. length]);
@@ -1329,7 +1358,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Returns: the same intger view with inversed sign
     +/
-    BigIntView!(W, endian) opUnary(string op : "-")() return scope
+    BigIntView!(W, endian) opUnary(string op : "-")()
     {
         return typeof(return)(this, true);
     }
@@ -1361,7 +1390,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: a slice of coefficients starting from the least significant.
     +/
     auto leastSignificantFirst()
-        @safe pure nothrow @nogc @property return scope
+        @safe pure nothrow @nogc @property
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.little)
@@ -1377,7 +1406,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     auto leastSignificantFirst()
-        const @safe pure nothrow @nogc @property return scope
+        const @safe pure nothrow @nogc @property
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.little)
@@ -1395,7 +1424,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: a slice of coefficients starting from the most significant.
     +/
     auto mostSignificantFirst()
-        @safe pure nothrow @nogc @property return scope
+        @safe pure nothrow @nogc @property
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.big)
@@ -1411,7 +1440,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     auto mostSignificantFirst()
-        const @safe pure nothrow @nogc @property return scope
+        const @safe pure nothrow @nogc @property
     {
         import mir.ndslice.slice: sliced;
         static if (endian == WordEndian.big)
@@ -1428,8 +1457,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Strips most significant zero coefficients.
     +/
-    BigUIntView normalized() return scope
+    BigUIntView normalized()
     {
+        pragma(inline, false);
         auto number = this;
         if (number.coefficients.length) do
         {
@@ -1750,7 +1780,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     bool sign;
 
     ///
-    inout(W)[] coefficients() inout @property return scope
+    inout(W)[] coefficients() inout @property
     {
         return unsigned.coefficients;
     }
@@ -2113,14 +2143,22 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     static if (endian == TargetEndian)
     ///
-    BigIntView!V opCast(T : BigIntView!V, V)() return scope
+    BigIntView!V opCast(T : BigIntView!V, V)()
+        if (V.sizeof <= W.sizeof)
+    {
+        return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
+    }
+
+    static if (endian == TargetEndian)
+    ///
+    BigIntView!V opCast(T : BigIntView!V, V)() const
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
     }
 
     ///
-    BigIntView!(const W, endian) lightConst()() return scope
+    BigIntView!(const W, endian) lightConst()()
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(unsigned.lightConst, sign);

From 21f5a48ea0eb42b4a38d30b6eecb7a598351d1e1 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 27 May 2022 15:18:19 +0400
Subject: [PATCH 392/573] fixup

---
 source/mir/bignum/integer.d        |  8 ++++----
 source/mir/bignum/low_level_view.d | 20 ++++++++++----------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 9702736b..1e2a4a56 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -392,7 +392,7 @@ struct BigInt(uint maxSize64)
 
 
     // TODO
-    ref modPow(scope BigUIntView!(const size_t) exponent, scope ref const BigInt modulus)
+    ref modPow()(scope BigUIntView!(const size_t) exponent, scope ref const BigInt modulus)
         @safe pure nothrow @nogc return scope
     {
         pragma(inline, false);
@@ -406,9 +406,9 @@ struct BigInt(uint maxSize64)
             return this;
         }
 
-        BigInt!256 bas = void;
+        BigInt!(maxSize64 * 2) bas = void;
         bas = this;
-        BigInt!256 res = void;
+        BigInt!(maxSize64 * 2) res = void;
         res = 1u;
 
         auto expBits = exponent.leastSignificantFirst.bitwise;
@@ -870,7 +870,7 @@ unittest
         assert((a %= b) == 0xDEADBEEF);
     }
 
-    void test(const long av, const long bv)
+    void test(const int av, const int bv)
     {
         auto a = BigInt!4(av);
         const b = BigInt!4(bv);
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 865b640c..3a20446b 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -370,7 +370,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T && isMutable!T)
     {
         import mir.bignum.fp;
@@ -394,7 +394,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     ///
     @safe
-    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const
+    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
         if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
     {
         static if (isMutable!W)
@@ -1481,7 +1481,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///ditto
-    BigUIntView!(const W, endian) normalized() scope const
+    BigUIntView!(const W, endian) normalized() const
     {
         return lightConst.normalized;
     }
@@ -1497,7 +1497,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    size_t ctlz()() scope const @property
+    size_t ctlz()() const @property
         @safe pure nothrow @nogc
     {
         import mir.bitop: ctlz;
@@ -1520,7 +1520,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    size_t cttz()() scope const @property
+    size_t cttz()() const @property
         @safe pure nothrow @nogc
     {
         import mir.bitop: cttz;
@@ -1553,7 +1553,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns: true on success
     +/
     bool get(U)(scope out U value)
-        @safe pure nothrow @nogc scope const
+        @safe pure nothrow @nogc const
         if (isUnsigned!U)
     {
         auto d = lightConst.mostSignificantFirst;
@@ -1921,7 +1921,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T && isMutable!T)
     {
         auto ret = this.unsigned.opCast!(T, wordNormalized, nonZero);
@@ -1971,7 +1971,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    T opCast(T, bool nonZero = false)() scope const
+    T opCast(T, bool nonZero = false)() const
         if (is(T == long) || is(T == int))
     {
         auto ret = this.unsigned.opCast!(Unsigned!T, nonZero);
@@ -2104,7 +2104,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() scope const
+    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
         if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
     {
         auto ret = unsigned.opCast!(Fp!coefficientSize, internalRoundLastBits, wordNormalized, nonZero);
@@ -3003,7 +3003,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.
     +/
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() scope const
+    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
         if (isFloatingPoint!T && isMutable!T)
     {
         version(LDC)

From cfaa9a9cb50324d3b61394cd991d570ae19dcb51 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 27 May 2022 23:28:25 +0400
Subject: [PATCH 393/573] wip bigint division

---
 source/mir/bignum/fixed.d          |  10 +-
 source/mir/bignum/integer.d        | 155 +++++++++++++--
 source/mir/bignum/low_level_view.d | 299 ++++++++++++++++++-----------
 3 files changed, 332 insertions(+), 132 deletions(-)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 81dc20c5..0e482047 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -403,10 +403,10 @@ struct UInt(size_t size)
         rhs = unsigned value to divide by
     Returns: quotient, sets `rhs` to remainder
     +/ 
-    ref divRem(size_t rhsSize)(scope ref UInt!rhsSize rhs)
+    ref divMod(size_t rhsSize)(scope ref UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        import mir.bignum.low_level_view : divm, BigUIntView;
+        import mir.bignum.low_level_view : divMod, BigUIntView;
 
         UInt!size quotient;
 
@@ -416,7 +416,7 @@ struct UInt(size_t size)
         divisorV = divisorV.normalized;
         dividendV = dividendV.normalized;
 
-        divm(dividendV, divisorV, quotientV);
+        divMod(dividendV, divisorV, quotientV);
         rhs = cast(UInt!rhsSize) this;
         this = quotient;
 
@@ -433,7 +433,7 @@ struct UInt(size_t size)
     ref opOpAssign(string op : "/", size_t rhsSize)(UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        return divRem(rhs);
+        return this.divMod(rhs);
     }
 
     /// ditto
@@ -453,7 +453,7 @@ struct UInt(size_t size)
     ref opOpAssign(string op : "%", size_t rhsSize)(UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        divRem(rhs);
+        this.divMod(rhs);
         this = cast(UInt!size)rhs;
     }
 
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 1e2a4a56..dd8d7fd7 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -26,7 +26,7 @@ struct BigInt(uint maxSize64)
     ///
     uint length;
     ///
-    size_t[ulong.sizeof / size_t.sizeof * maxSize64] data = void;
+    size_t[ulong.sizeof / size_t.sizeof * maxSize64] data;// = void;
 
     ///
     this(uint size)(UInt!size fixedInt)
@@ -86,7 +86,6 @@ struct BigInt(uint maxSize64)
 
     ///
     this()(scope const(char)[] str) @safe pure @nogc
-        // if (isSomeChar!C)
     {
         if (fromStringImpl(str))
             return;
@@ -105,6 +104,7 @@ struct BigInt(uint maxSize64)
     ///
     ref opAssign(ulong data) return
     {
+        this.sign = 0;
         static if (size_t.sizeof == ulong.sizeof)
         {
             length = 1;
@@ -390,9 +390,17 @@ struct BigInt(uint maxSize64)
         }
     }
 
+    /++
+    +/
+    ref powMod(uint expSize)(scope ref const BigInt!expSize exponent, scope ref const BigInt modulus)
+        @safe pure nothrow @nogc return scope
+        in(!exponent.sign)
+    {
+        return this.powMod(exponent.view.unsigned, modulus);
+    }
 
-    // TODO
-    ref modPow()(scope BigUIntView!(const size_t) exponent, scope ref const BigInt modulus)
+    ///ditto
+    ref powMod()(scope BigUIntView!(const size_t) exponent, scope ref const BigInt modulus)
         @safe pure nothrow @nogc return scope
     {
         pragma(inline, false);
@@ -411,32 +419,75 @@ struct BigInt(uint maxSize64)
         BigInt!(maxSize64 * 2) res = void;
         res = 1u;
 
-        auto expBits = exponent.leastSignificantFirst.bitwise;
-        expBits.popBackN(exponent.ctlz);
-
-        while (expBits.length)
+        foreach (b; exponent.leastSignificantFirst.bitwise[0 .. $ - exponent.ctlz])
         {
             bas %= modulus;
-            if (expBits.front)
+            if (b)
             {
-                // res *= bas;
+                res *= bas;
                 res %= modulus;
             }
-            expBits.popBack;
-            // bas *= bas;
+            bas *= bas;
         }
+
         this = res;
         return this;
     }
 
+    ///
+    static if (maxSize64 == 3)
+    version (mir_bignum_test)
+    unittest
+    {
+        BigInt!3 x = 2;
+        BigInt!3 e = 10;
+        BigInt!3 m = 100;
+
+        x.powMod(e, m);
+        assert(x == 24);
+    }
+
+    ///
+    static if (maxSize64 == 3)
+    version (mir_bignum_test)
+    unittest
+    {
+        BigInt!3 x = 564321;
+        BigInt!3 e = "13763753091226345046315979581580902400000310";
+        BigInt!3 m = "13763753091226345046315979581580902400000311";
+
+        x.powMod(e, m);
+        assert(x == 1);
+    }
+
     /++
     +/
-    ref divRem(uint rhsSize64)(
+    ref multiply(uint aSize64, uint bSize64)
+    (
+        scope ref const BigInt!aSize64 a,
+        scope ref const BigInt!bSize64 b,
+    )
+        @safe pure nothrow @nogc scope return
+    {
+        import mir.utility: max;
+        import mir.bignum.low_level_view : multiply;
+
+        this.length = this.data.length;
+        multiply(this.view.unsigned, a.view.unsigned, b.view.unsigned);
+        this.length = this.length.min(a.length + b.length);
+        this.length -= this.length && !this.view.unsigned.mostSignificant;
+        this.sign = (this.length != 0) & (a.sign ^ b.sign);
+        return this;
+    }
+
+    ref divMod(uint rhsSize64)
+    (
         scope ref const BigInt!rhsSize64 divisor,
-        scope ref BigInt quotient)
-        @trusted pure nothrow @nogc
+        scope ref BigInt quotient
+    )
+        @trusted pure nothrow @nogc scope return
     {
-        import mir.bignum.low_level_view : divm, BigUIntView;
+        import mir.bignum.low_level_view : divMod, BigUIntView;
 
         pragma(inline, false);
 
@@ -455,7 +506,7 @@ struct BigInt(uint maxSize64)
         BigInt!(min(rhsSize64, maxSize64)) _divisor = void;
         _divisor = divisor;
 
-        quotient.length = cast(uint) divm(
+        quotient.length = cast(uint) divMod(
             this.divmView,
             _divisor.divmView,
             cast(BigUIntView!uint) quotient.view.unsigned,
@@ -480,7 +531,7 @@ struct BigInt(uint maxSize64)
     }
 
     /++
-    Performs `this %= rhs` and `this /= rhs` operation.
+    Performs `this %= rhs` and `this /= rhs` operations.
     Params:
         rhs = value to divide by
     Returns:
@@ -490,14 +541,41 @@ struct BigInt(uint maxSize64)
         @safe pure nothrow @nogc return
         if (op == "/" || op == "%")
     {
-        import mir.bignum.low_level_view : divm, BigUIntView;
         BigInt quotient = void;
-        divRem(rhs, quotient);
+        this.divMod(rhs, quotient);
         static if (op == "/")
             this = quotient;
         return this;
     }
 
+    /++
+    Performs `this %= rhs` and `this /= rhs` operations.
+    Params:
+        rhs = value to divide by
+    Returns:
+        remainder or quotient from the truncated division
+    +/
+    ref opOpAssign(string op : "*", size_t rhsSize64)(scope const ref BigInt!rhsSize64 rhs)
+        @safe pure nothrow @nogc return
+    {
+        BigInt c = void;
+        c.multiply(this, rhs);
+        this = c;
+        return this;
+    }
+
+    ///
+    static if (maxSize64 == 3)
+    version (mir_bignum_test)
+    unittest
+    {
+        BigInt!32 x = "236089459999051800787306800176765276560685597708945239133346035689205694959466543423391020917332149321603397284295007899190053323478336179162578944";
+        BigInt!32 y = "19095614279677503764429420557677401943131308638097701560446870251856566051181587499424174939645900335127490246389509326965738171086235365599977209919032320327138167362675030414072140005376";
+        BigInt!32 z = "4508273263639244391466225874448166762388283627989411942887789415132291146444880491003321910228134369483394456858712486391978856464207606191606690798518090459546799016472580324664149788791167494389789813815605288815981925073283892089331019170542792502117265455020551819803771537458327634120582677504637693661973404860326560198184402944";
+        x *= y;
+        assert(x == z);
+    }
+
     /++
     Performs `size_t overflow = big *= fixed` operatrion.
     Params:
@@ -544,6 +622,43 @@ struct BigInt(uint maxSize64)
         }
         return overflow;
     }
+
+    /// ditto
+    bool opOpAssign(string op)(ulong rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        import mir.ndslice.bignum.fixed: UInt;
+        return this.opOpAssign!op(rhs.UInt!64.view.signed);
+    }
+
+    /// ditto
+    bool opOpAssign(string op)(uint rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return this.opOpAssign!op(ulong(rhs));
+    }
+
+    /// ditto
+    bool opOpAssign(string op)(long rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        import mir.bignum.fixed: UInt;
+        auto sign = rhs < 0;
+        rhs = sign ? -rhs : rhs;
+        return this.opOpAssign!op(rhs.UInt!64.view.normalized.signed(sign));
+    }
+
+    /// ditto
+    bool opOpAssign(string op)(int rhs)
+        @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        return this.opOpAssign!op(long(rhs));
+    }
+
     /++
     +/
     static BigInt fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 3a20446b..18610281 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -122,16 +122,136 @@ version(mir_bignum_test)
     assert(ceilLog10Exp2(10UL) == 4); // ulong
 }
 
-// size_t mulm(scope BigUIntView!size_t _c, scope BigUIntView!(const size_t) _a, scope BigUIntView!(const size_t) _b)
-//     @trusted pure nothrow @nogc
-//     in (_c.length >= _a.length * _b.length)
-// {
-//     if (_a.length == 0 || _b.length == 0)
-//         return 0;
-//     auto a = _a.leastSignificantFirst;
-//     auto b = _b.leastSignificantFirst;
-//     auto c = _c.leastSignificantFirst;
-// }
+private U multiplyAddKernel(string op, U, C, A)(
+    scope C c,
+    scope A a,
+    U b,
+    U overflow = 0,
+)
+    @trusted pure nothrow @nogc
+    if (op == "+" || op == "-")
+    in (a.length == c.length)
+{
+    size_t j;
+    do
+    {
+        import mir.checkedint: addu;
+        import mir.utility: extMul;
+
+        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
+        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
+        // - n + overflow <= - 1
+        // overflow <= n - 1
+        auto ext = a.front.extMul(b);
+        {
+            bool bit;
+            ext.low = ext.low.addu(overflow, bit);
+            assert(ext.high < ext.high.max);
+            ext.high += bit;
+        }
+        {
+            bool bit;
+            c.front = c.front.cop!op(ext.low, bit);
+            assert(bit == 0 || ext.high < ext.high.max);
+            overflow = ext.high + bit;
+        }
+
+        a.popFront;
+        c.popFront;
+    }
+    while(c.length);
+    return overflow;
+}
+
+private U addKernel(string op, U, C, A)(
+    scope C c,
+    scope A a,
+    U overflow = 0,
+)
+    @trusted pure nothrow @nogc
+    if (op == "+" || op == "-")
+    in (a.length == c.length)
+{
+    size_t j;
+    do
+    {
+        import mir.checkedint: addu;
+        import mir.utility: extMul;
+
+        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
+        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
+        // - n + overflow <= - 1
+        // overflow <= n - 1
+        auto ext = a.front;
+        {
+            bool bit;
+            ext = ext.addu(overflow, bit);
+            overflow = bit;
+        }
+        {
+            bool bit;
+            c.front = c.front.cop!op(ext, bit);
+            overflow += bit;
+        }
+
+        a.popFront;
+        c.popFront;
+    }
+    while(c.length);
+    return overflow;
+}
+
+void multiply(
+    BigUIntView!size_t _c,
+    BigUIntView!(const size_t) _a,
+    BigUIntView!(const size_t) _b,
+)
+    @trusted pure nothrow @nogc
+{
+    pragma (inline, false);
+
+    auto a = _a.leastSignificantFirst;
+    auto b = _b.leastSignificantFirst;
+    auto c = _c.leastSignificantFirst;
+
+    if (a.length < b.length)
+    {
+        auto t = a;
+        a = b;
+        b = t;
+    }
+
+    if (a.length > c.length)
+        a = a[0 .. c.length];
+
+    if (b.length > c.length)
+        b = b[0 .. c.length];
+
+    if (b.length == 0)
+        return;
+
+    assert(a.length);
+    assert(b.length);
+    assert(c.length);
+    c[0 .. a.length] = 0;
+
+    do
+    {
+        size_t j;
+        import mir.utility: min;
+        auto length = min(a.length, c.length);
+        auto overflow = multiplyAddKernel!("+", size_t)(c[0 .. length], a[0 .. length], b.front);
+        if (length < c.length)
+            c[a.length] = overflow;
+
+    skipOverflow:
+        c.popFront;
+        b.popFront;
+    }
+    while(b.length);
+}
+
+
 
 /++
 Performs `q = u / v, u = u % v` (unsigned)
@@ -142,55 +262,41 @@ Params:
 Preconditions:
     * non-empty coefficients
     * little-endian order
-    * _u.coefficients.length >= _u.normalized.coefficients.length
-    * _q.coefficients.length >= (_u.normalized.coefficients.length - _v.normalized.coefficients.length) + 1
+    * _u.length >= _u.normalized.length
+    * _q.length >= (_u.normalized.length - _v.normalized.length) + 1
 +/
-size_t divm(scope BigUIntView!uint _u, scope BigUIntView!uint _v, scope BigUIntView!uint _q)
+size_t divMod(
+    BigUIntView!uint _u,
+    BigUIntView!uint _v,
+    BigUIntView!uint _q,
+)
     @trusted pure nothrow @nogc
-    in (_u.coefficients.length == 0 || _u.mostSignificant)
-    in (_v.coefficients.length)
+    in (_u.length == 0 || _u.mostSignificant)
+    in (_v.length)
     in (_v.mostSignificant)
-    in (sizediff_t(_q.coefficients.length) >= sizediff_t(_u.coefficients.length - _v.coefficients.length + 1))
+    in (sizediff_t(_q.length) >= sizediff_t(_u.length - _v.length + 1))
 {
-    // Adopted from Hacker's Delight (2nd Edition), 4-2
-    // License permits inclusion:
-    // You are free to use, copy, and distribute any of the code on this web site, whether modified by you or not.
-    // You need not give attribution. This includes the algorithms (some of which appear in Hacker's Delight), the Hacker's Assistant, and any code submitted by readers. Submitters implicitly agree to this.
-    // The textural material and pictures are copyright by the author, and the usual copyright rules apply. E.g., you may store the material on your computer and make hard or soft copies for your own use.
-    // However, you may not incorporate this material into another publication without written permission from the author (which the author may give by email).
-    // The author has taken care in the preparation of this material, but makes no expressed or implied warranty of any kind and assumes no responsibility for errors or omissions.
-    // No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. 
+    pragma (inline, false);
+
     import mir.bitop: ctlz;
     import mir.utility: _expect;
 
-    // Convert all of our input arguments to little-endian
     size_t m = _u.coefficients.length;
     size_t n = _v.coefficients.length;
     auto u = _u.leastSignificantFirst();
     auto v = _v.leastSignificantFirst();
     auto q = _q.leastSignificantFirst();
 
-
-    // Handle a common edge case with division (where the dividend is smaller then the divisor)
-    // We don't care about how big the quotient array is here (and in fact, our math will fail)
-    // since we just dump the dividend into the remainder 
-    if (_expect(m < n, false))
-    {
+    if (m < n)
         return 0;
-    }
-
-    // From this point on, now we care if the quotient array is appropriately sized
 
-    // According to Knuth, when the divisor length is equal to one,
-    // we should fall back to a simplified algorithm
     sizediff_t lastQ = -1;
-    if (_expect(n == 1, false))
+    if (n == 1)
     {
         uint k = 0;
         uint d = v[0];
         foreach_reverse(j; 0 .. m)
         {
-            // Same optimization as down below (left-shifting / ORing in place of multiplication / addition)
             auto ut = (cast(ulong)k << 32) | u[j]; 
             q[j] = cast(uint)(ut / d);
             k = ut % d;
@@ -203,88 +309,60 @@ size_t divm(scope BigUIntView!uint _u, scope BigUIntView!uint _v, scope BigUIntV
     }
     else
     {
-        // pre-condition for this algorithm to succeed.
-        // assert(m >= n, "failed algorithm pre-condition");
-
-        // D1: Normalize
-        // In TAOCP, this is specified as b/(v[n - 1] + 1)
         auto s = cast(uint)ctlz(v[n - 1]);
         assert(s <= 31 && s >= 0);
 
-        // This normally uses a for loop, but we just use
-        // a LLV primitive here instead.
         uint umn = s ? _u.mostSignificant >> (32 - s) : 0;
         _u.smallLeftShiftInPlace(s);
         _v.smallLeftShiftInPlace(s);
 
+        uint vhi = v[$ - 1];
+        uint vlo = v[$ - 2];
+
         uint* ujnp = &umn;
         ref uint ujn() @property { return *ujnp; }
         size_t j = m - n;
         do
         {
-            // D3: Calculate qhat, rhat == remainder
-            // In TOACP, this is specified as ujn*b + u[j + n - 1] / v[n - 1]
-            // but, since we assume b is a power of 2, we can rewrite it to just
-            // use left-shifting (as left-shifting effectively multiples by powers of 2)
-            // and use an OR in place of the addition (as we've cleared the bottom 32 bits)
-
-            auto ut = ((cast(ulong) ujn << 32) | u[j + n - 1]); 
-            ulong qhat = ut / v[n - 1];
-            ulong rhat = ut % v[n - 1];
-
-            // Similarly here, this is specified as qhat*v[n - 2] > rhat*b + u[j + n - 2]
-            // but we can just get away with a shift instead.
-            while (qhat >= uint.max || qhat * v[n - 2] > (rhat << 32) + u[j + n - 2])
+            uint qhat;
+            if (ujn == vhi)
             {
-                qhat -= 1;
-                rhat += v[n - 1];
-
-                if (rhat > uint.max)
-                {
-                    break;
-                }
+                qhat = uint.max;
             }
-
-            long t = 0, k = 0;
-            // D4: Multiply and subtract
-            size_t i;
-            do
+            else
             {
-                ulong p = qhat * v[i];
-                // t = u[i + j] - k - (p & cast(uint)((ulong(1) << 32) - 1));
-                t = u[i + j] - k - cast(uint) p;
-                u[i + j] = cast(uint) t;
-                k = (p >> 32) - (t >> 32);
+                uint ulo = u[j + n - 2];
+
+                ulong uu = (cast(ulong) ujn << 32) | u[j + n - 1];
+                immutable bigqhat = uu / vhi;
+                ulong rhat =  uu - bigqhat * vhi;
+                qhat = cast(uint) bigqhat;
+    again:
+                if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo))
+                {
+                    --qhat;
+                    rhat += vhi;
+                    if (!(rhat & 0xFFFF_FFFF_0000_0000L))
+                        goto again;
+                }
             }
-            while(++i < n);
             
-            t = ujn - k;
-            // These are admittedly super sketchy casts, but
-            // we really are only interested in the bottom 32-bits here.
-            ujn = cast(uint) t;
-            q[j] = cast(uint) qhat;
-            // D5: Test remainder
-            if (_expect(t < 0, false))
+            uint overflow = multiplyAddKernel!("-", uint)(u[j .. j + n], v, qhat);
+
+            if (ujn < overflow)
             {
-                q[j] -= 1;
-                k = 0;
-                i = 0;
-                do
-                {
-                    t = cast(ulong) u[i + j] + v[i] + k;
-                    u[i + j] = cast(uint) t;
-                    k = t >> 32;
-                }
-                while(++i < n);
-                ujn = cast(uint) (ujn + k);
+                --qhat;
+                overflow -= addKernel!("+", uint)(u[j .. j + n], v);
             }
-            if (lastQ == -1 && q[j])
+
+            if (lastQ == -1 && qhat)
                 lastQ = j;
+            q[j] = cast(uint) qhat;
+            ujn -= overflow;
             ujnp = &u[j - 1 + n];
         }
         while(j--);
 
-        // D8: Un-normalize (obtain the remainder)
         _u.smallRightShiftInPlace(s);
         _u.mostSignificant |= s ? umn << (32 - s) : 0;
         // _v.smallRightShiftInPlace(s);
@@ -307,7 +385,7 @@ unittest
         auto quotient = BigUIntView!uint.fromHexString("0");
         // auto remainder = BigUIntView!uint.fromHexString("0");
 
-        divm(dividend, divisor, quotient);
+        divMod(dividend, divisor, quotient);
         assert(quotient == BigUIntView!uint.fromHexString("1"));
     }
 
@@ -316,7 +394,7 @@ unittest
         auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
         auto divisor = BigUIntView!uint.fromHexString("5");
         auto quotient = BigUIntView!uint.fromHexString("0000000000000000000000000000000000000000000000000000000000000000");
-        divm(dividend, divisor, quotient);
+        divMod(dividend, divisor, quotient);
         assert(dividend.normalized == BigUIntView!uint.fromHexString("3"));
         assert(quotient == BigUIntView!uint.fromHexString("1120a1229e8a217d0691b02b819107174a4b677088ef0df874259b283c1d588c"));
     }
@@ -328,7 +406,7 @@ unittest
         auto quotient = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000");
         // auto remainder = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
 
-        divm(dividend, divisor, quotient);
+        divMod(dividend, divisor, quotient);
         assert(quotient.normalized == BigUIntView!uint.fromHexString("ff3a8aa4da35237811a0ffbf007fe938630dee8a810f2f82ae01f80c033291f6"));
         assert(dividend.normalized == BigUIntView!uint.fromHexString("27926263cf248bef1c2cd63ea004d9f7041bffc8568560ec30fc9a9548057857"));
     }
@@ -339,7 +417,7 @@ unittest
         auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001");
         auto quotient = BigUIntView!uint.fromHexString("0");
         // auto remainder = BigUIntView!uint.fromHexString("000000000000000000000000");
-        divm(dividend, divisor, quotient);
+        divMod(dividend, divisor, quotient);
         assert(quotient == BigUIntView!uint.fromHexString("3"));
         assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000"));
     }
@@ -364,9 +442,17 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Retrurns: signed integer view using the same data payload
     +/
-    BigIntView!(W, endian) signed()() @safe pure nothrow @nogc scope @property
+    size_t length()() @safe pure nothrow @nogc const @property
     {
-        return typeof(return)(this);
+        return coefficients.length;
+    }
+
+    /++
+    Retrurns: signed integer view using the same data payload
+    +/
+    BigIntView!(W, endian) signed()(bool sign = false) @safe pure nothrow @nogc scope @property
+    {
+        return typeof(return)(this, sign);
     }
 
     ///
@@ -661,7 +747,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    sizediff_t opCmp(scope BigUIntView!(const W, endian) rhs)
+    sizediff_t opCmp(BigUIntView!(const W, endian) rhs)
         const @safe pure nothrow @nogc scope
     {
         import mir.algorithm.iteration: cmp;
@@ -673,7 +759,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    bool opEquals(scope BigUIntView!(const W, endian) rhs)
+    bool opEquals(BigUIntView!(const W, endian) rhs)
         const @safe pure nothrow @nogc scope
     {
         return this.coefficients == rhs.coefficients;
@@ -1181,7 +1267,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns:
         true in case of unsigned overflow
     +/
-    bool opOpAssign(string op)(scope BigUIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
     @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
@@ -1459,7 +1545,6 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     +/
     BigUIntView normalized()
     {
-        pragma(inline, false);
         auto number = this;
         if (number.coefficients.length) do
         {
@@ -2254,7 +2339,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     static if (isMutable!W && W.sizeof >= 4)
     /// ditto
-    bool opOpAssign(string op)(scope BigUIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
     @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {

From ef412d8d69745abd109d529f4341bf57e41f39e0 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 28 May 2022 03:15:57 +0400
Subject: [PATCH 394/573] wip

---
 bigint_benchmark/dub.sdl           |  2 ++
 bigint_benchmark/source/app.d      | 57 ++++++++++++++++++++++++++++++
 source/mir/bignum/integer.d        |  3 +-
 source/mir/bignum/low_level_view.d | 13 ++++---
 4 files changed, 67 insertions(+), 8 deletions(-)
 create mode 100644 bigint_benchmark/dub.sdl
 create mode 100644 bigint_benchmark/source/app.d

diff --git a/bigint_benchmark/dub.sdl b/bigint_benchmark/dub.sdl
new file mode 100644
index 00000000..37e74f1e
--- /dev/null
+++ b/bigint_benchmark/dub.sdl
@@ -0,0 +1,2 @@
+name "bigint_benchmark"
+dependency "mir-algorithm" path="../"
diff --git a/bigint_benchmark/source/app.d b/bigint_benchmark/source/app.d
new file mode 100644
index 00000000..4b09f132
--- /dev/null
+++ b/bigint_benchmark/source/app.d
@@ -0,0 +1,57 @@
+import mir.bignum.integer: BigInt;
+import std.datetime.stopwatch;
+import std.bigint: StdBigInt = BigInt;
+import std.stdio: writeln;
+
+immutable ps = "E5B5B1EDC8DF0F307C2220151CFCBE31F69B15659A5D6FBA1E50F";//55A08B341218312D707CFC16ED86A1765F5AEAFA7E6A11C4431038914C76F0F398FE6BE031E289B220D13D9E02226C691D15BC6E1186EA18222D93F52A393BE1DA1A42853512419B5E6E304FD02E962A4C2D0ECDDB8F44AC094FACA8333AE94110A5B10DA539C24A96F08530E7699E3F705165CF14B7F90A2F32ED28D21615F91D7C808AC566D6EEEF6773450AB53542CDAC337C3124530CB16319752267C3422149D41543D8742586BAB578F4E06360745AE0BD8F0E800D1920DC1F3661287367A78967458383A82465C5D966E7299EFCF58BD860185F96655E1F8D300F6B096DFE883CF15";
+immutable qs = "D9757338E9A6B363F227F3104EDEF6240C0CAF53B7D509F488705";//53C4A821F460469AE5616301B9CC30FBF4598A176B84284AF3A41D697A34CDC2C8D88A4C4BE82AE8DB5347511FE5B4DD915CA6A728CCFD0444CE38FC7190824059D86A9083C273581EA5AD1D5E3A8D8EC6858F291A5EADA98B0F5FD7C8E8CA6226657B8B7955796B22899B087714E293A86C78D42A7021754A6220F1D0A9588C280DD9AEC376E421D539F30A3053D95C7D70F24B471D14ECF282FA3E0B1CED2C405BA22404F3B75CD961A46097D7C098324FC47281D298734DA0DFCD8AF82E685657C926672727296147867EAEDFDEF89A79DE81FF104CF7D9157EF65A1BC333C98A7FED685";
+immutable es = ps ~ qs;
+
+void powMod(ref StdBigInt base, StdBigInt exponent, StdBigInt modulus)
+{
+    StdBigInt result = 1;
+
+    while (exponent != 0)
+    {
+        base %= modulus;
+        if (exponent & 1)
+        {
+            result *= base;
+            result %= modulus;
+        }
+        exponent >>= 1;
+        base *= base;
+    }
+    base = result;
+}
+
+void testStd()
+{
+    StdBigInt p = "0x" ~ ps;
+    StdBigInt q = "0x" ~ qs;
+    StdBigInt m = p;
+    m *= q;
+    StdBigInt e = "0x" ~ es;
+    StdBigInt b = e;
+    b.powMod(e, m);
+    // debug writeln(b);
+}
+
+void testMir()
+{
+    auto p = BigInt!32.fromHexString(ps);
+    auto q = BigInt!32.fromHexString(qs);
+    BigInt!32 m = p;
+    m *= q;
+    auto e = BigInt!32.fromHexString(es);
+    BigInt!32 b = e;
+    b.powMod(e, m);
+    // debug writeln(b);
+}
+
+void main()
+{
+    auto res = 300.benchmark!(testStd, testMir);
+    writeln("std = ", res[0]);
+    writeln("mir = ", res[1]);
+}
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index dd8d7fd7..ba412133 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -444,7 +444,8 @@ struct BigInt(uint maxSize64)
         BigInt!3 m = 100;
 
         x.powMod(e, m);
-        assert(x == 24);
+        import mir.format;
+        assert(x == 24, x.text);
     }
 
     ///
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 18610281..a7400b06 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -201,10 +201,10 @@ private U addKernel(string op, U, C, A)(
     return overflow;
 }
 
-void multiply(
-    BigUIntView!size_t _c,
-    BigUIntView!(const size_t) _a,
-    BigUIntView!(const size_t) _b,
+void multiply(U)(
+    BigUIntView!U _c,
+    BigUIntView!(const U) _a,
+    BigUIntView!(const U) _b,
 )
     @trusted pure nothrow @nogc
 {
@@ -240,11 +240,10 @@ void multiply(
         size_t j;
         import mir.utility: min;
         auto length = min(a.length, c.length);
-        auto overflow = multiplyAddKernel!("+", size_t)(c[0 .. length], a[0 .. length], b.front);
+        auto overflow = multiplyAddKernel!("+", U)(c[0 .. length], a[0 .. length], b.front);
         if (length < c.length)
-            c[a.length] = overflow;
+            c[length] = overflow;
 
-    skipOverflow:
         c.popFront;
         b.popFront;
     }

From 70905b638e8250b33e9f1ad82a9538623b44ce5f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 28 May 2022 03:16:12 +0400
Subject: [PATCH 395/573] wip

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 9bc5b5ed..9ca52b2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,4 @@ _build_dir_
 builddir/
 mir-algorithm-test-secure
 *.pdb
+bigint_benchmark/bigint_benchmark

From 67b2bfe1a025c413251e6e36327bc134a2a1eea5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 28 May 2022 11:22:26 +0400
Subject: [PATCH 396/573] wip

---
 .gitignore                             |   1 +
 dub.sdl                                |   2 +-
 source/mir/algebraic_alias/transform.d |   2 +
 source/mir/bignum/decimal.d            |  12 +-
 source/mir/bignum/fixed.d              | 143 ++--
 source/mir/bignum/integer.d            |  63 +-
 source/mir/bignum/low_level_view.d     | 926 ++++++++-----------------
 source/mir/format.d                    |   1 +
 8 files changed, 361 insertions(+), 789 deletions(-)

diff --git a/.gitignore b/.gitignore
index 9ca52b2a..0d683dcd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,4 @@ builddir/
 mir-algorithm-test-secure
 *.pdb
 bigint_benchmark/bigint_benchmark
+bigint_benchmark/temp.d
diff --git a/dub.sdl b/dub.sdl
index 5d93f070..cab8b939 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,7 @@ dependency "mir-core" version=">=1.1.106"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test_llv" "mir_bignum_test" "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/algebraic_alias/transform.d b/source/mir/algebraic_alias/transform.d
index fcd92765..8e033e7e 100644
--- a/source/mir/algebraic_alias/transform.d
+++ b/source/mir/algebraic_alias/transform.d
@@ -22,6 +22,7 @@ Recursion is done for `This[]`, `StringMap!This`, `This[string]`, and `Annotated
 alias transformLeafs(visitors...) = transformLeafsImpl!(visit, naryFun!visitors);
 
 ///
+version(mir_test)
 unittest
 {
     import mir.format: text;
@@ -51,6 +52,7 @@ Throws: Exception if `naryFun!visitors` can't be called with provided arguments
 alias tryTransformLeafs(visitors...) = transformLeafsImpl!(tryVisit, naryFun!visitors);
 
 ///
+version(mir_test)
 unittest
 {
     import mir.format: text;
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index cece609e..3d9e8001 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -708,16 +708,8 @@ struct Decimal(uint maxSize64)
                 {
                     auto l = cast(uint)coefficient.data[i];
                     auto h = cast(uint)(coefficient.data[i] >> 32);
-                    version (LittleEndian)
-                    {
-                        data[i * 2 + 0] = l;
-                        data[i * 2 + 1] = h;
-                    }
-                    else
-                    {
-                        data[$ - 1 - (i * 2 + 0)] = l;
-                        data[$ - 1 - (i * 2 + 1)] = h;
-                    }
+                    data[i * 2 + 0] = l;
+                    data[i * 2 + 1] = h;
                 }
                 auto work = BigUIntView!uint(data);
                 work = work.topLeastSignificantPart(coefficient.length * 2).normalized;
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 0e482047..a65b820f 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -42,7 +42,7 @@ struct UInt(size_t size)
 
     static if (size_t.sizeof == uint.sizeof && data.length % 2 == 0)
     ///
-    this()(auto ref const ulong[data.length / 2] data)
+    this(auto ref const ulong[data.length / 2] data)
     {
         if (!__ctfe)
         {
@@ -72,15 +72,14 @@ struct UInt(size_t size)
     ///
     this(ulong data)
     {
-        auto d = view.leastSignificantFirst;
         static if (size_t.sizeof == ulong.sizeof)
         {
-            d.front = data;
+            this.data[0] = data;
         }
         else
         {
-            d.front = cast(uint) data;
-            d[1] = cast(uint) (data >> 32);
+            this.data[0] = cast(uint) data;
+            this.data[1] = cast(uint) (data >> 32);
         }
     }
 
@@ -88,7 +87,7 @@ struct UInt(size_t size)
     ///
     this(uint data)
     {
-        view.leastSignificant = data;
+        data[0] = data;
     }
 
     ///
@@ -185,13 +184,13 @@ struct UInt(size_t size)
     import mir.bignum.low_level_view: BigUIntView;
 
     ///
-    BigUIntView!size_t view()() @property pure nothrow @nogc scope @safe
+    BigUIntView!size_t view() @property pure nothrow @nogc scope @safe
     {
         return BigUIntView!size_t(data);
     }
 
     ///
-    BigUIntView!(const size_t) view()() const @property pure nothrow @nogc scope @safe
+    BigUIntView!(const size_t) view() const @property pure nothrow @nogc scope @safe
     {
         return BigUIntView!(const size_t)(data);
     }
@@ -273,22 +272,14 @@ struct UInt(size_t size)
     +/
     auto opCmp(UInt!size rhs) const
     {
-        version (LittleEndian) // workaround for CTFE bug
+        foreach_reverse(i; 0 .. data.length)
         {
-            foreach_reverse(i; 0 .. data.length)
-            {
-                if (this.data[i] < rhs.data[i])
-                    return -1;
-                if (this.data[i] > rhs.data[i])
-                    return +1;
-            }
-            return 0;
-        }
-        else
-        {
-            import mir.algorithm.iteration: cmp;
-            return cmp(this.view.mostSignificantFirst, rhs.view.mostSignificantFirst);
+            if (this.data[i] < rhs.data[i])
+                return -1;
+            if (this.data[i] > rhs.data[i])
+                return +1;
         }
+        return 0;
     }
 
     /// ditto
@@ -500,7 +491,7 @@ struct UInt(size_t size)
     ref UInt!size opOpAssign(string op)(size_t rhs) nothrow return scope
         if (op == "^" || op == "|" || op == "&")
     {
-        mixin(`view.leastSignificantFirst[0] ` ~ op ~ `= rhs;`);
+        mixin(`view.coefficients[0] ` ~ op ~ `= rhs;`);
         return this;
     }
 
@@ -518,7 +509,7 @@ struct UInt(size_t size)
         @safe pure nothrow @nogc return
         if (op == "<<" || op == ">>")
     {
-        auto d = view.leastSignificantFirst;
+        auto d = view.coefficients;
         assert(shift < size);
         auto index = shift / (size_t.sizeof * 8);
         auto bs = shift % (size_t.sizeof * 8);
@@ -539,7 +530,7 @@ struct UInt(size_t size)
                     d[j] = d[j + index];
                 }
             }
-            d[$ - (index + 1)] = d.back >>> bs;
+            d[$ - (index + 1)] = d[$ - 1] >>> bs;
             foreach (j; data.length - index .. data.length)
             {
                 d[j] = 0;
@@ -561,7 +552,7 @@ struct UInt(size_t size)
                     d[j] = d[j - index];
                 }
             }
-            d[index] = d.front << bs;
+            d[index] = d[0] << bs;
             foreach_reverse (j; 0 .. index)
             {
                 d[j] = 0;
@@ -669,29 +660,18 @@ struct UInt(size_t size)
     /++
     Shifts left using at most `size_t.sizeof * 8 - 1` bits
     +/
-    UInt!size smallLeftShift()(uint shift) const
+    UInt!size smallLeftShift(uint shift) const
     {
         assert(shift < size_t.sizeof * 8);
         UInt!size ret = this;
         if (shift)
         {
             auto csh = size_t.sizeof * 8 - shift;
-            version (LittleEndian)
+            static foreach_reverse (i; 1 .. data.length)
             {
-                static foreach_reverse (i; 1 .. data.length)
-                {
-                    ret.data[i] = (ret.data[i] << shift) | (ret.data[i - 1] >>> csh);
-                }
-                ret.data[0] = ret.data[0] << shift;
-            }
-            else
-            {
-                static foreach (i; 0 .. data.length - 1)
-                {
-                    ret.data[i] = (ret.data[i] << shift) | (ret.data[i + 1] >>> csh);
-                }
-                ret.data[$ - 1] = ret.data[$ - 1] << shift;
+                ret.data[i] = (ret.data[i] << shift) | (ret.data[i - 1] >>> csh);
             }
+            ret.data[0] = ret.data[0] << shift;
         }
         return ret;
     }
@@ -709,29 +689,18 @@ struct UInt(size_t size)
     /++
     Shifts right using at most `size_t.sizeof * 8 - 1` bits
     +/
-    UInt!size smallRightShift()(uint shift) const
+    UInt!size smallRightShift(uint shift) const
     {
         assert(shift < size_t.sizeof * 8);
         UInt!size ret = this;
         if (shift)
         {
             auto csh = size_t.sizeof * 8 - shift;
-            version (LittleEndian)
+            static foreach (i; 0 .. data.length - 1)
             {
-                static foreach (i; 0 .. data.length - 1)
-                {
-                    ret.data[i] = (ret.data[i] >>> shift) | (ret.data[i + 1] << csh);
-                }
-                ret.data[$ - 1] = ret.data[$ - 1] >>> shift;
-            }
-            else
-            {
-                static foreach_reverse (i; 1 .. data.length)
-                {
-                    ret.data[i] = (ret.data[i] >>> shift) | (ret.data[i - 1] << csh);
-                }
-                ret.data[0] = ret.data[0] >>> shift;
+                ret.data[i] = (ret.data[i] >>> shift) | (ret.data[i + 1] << csh);
             }
+            ret.data[$ - 1] = ret.data[$ - 1] >>> shift;
         }
         return ret;
     }
@@ -764,14 +733,13 @@ struct UInt(size_t size)
     T opCast(T)() const
         if (is(Unqual!T == ulong))
     {
-        auto d = view.leastSignificantFirst;
         static if (size_t.sizeof == ulong.sizeof)
         {
-            return d.front;
+            return data[0];
         }
         else
         {
-            return d.front | (ulong(d[1]) << 32);
+            return data[0] | (ulong(data[1]) << 32);
         }
     }
 
@@ -783,18 +751,12 @@ struct UInt(size_t size)
         enum newLength = typeof(return).data.length;
         static if (newLength <= data.length)
         {
-            version (LittleEndian)
-                return typeof(return)(data[0 .. newLength]);
-            else
-                return typeof(return)(data[$ - newLength .. $]);
+            return typeof(return)(data[0 .. newLength]);
         }
         else
         {
             typeof(return) ret;
-            version (LittleEndian)
-                ret.data[0 .. data.length] = data;
-            else
-                ret.data[$ - data.length .. $] = data;
+            ret.data[0 .. data.length] = data;
             return ret;
         }
     }
@@ -804,8 +766,7 @@ struct UInt(size_t size)
     T opCast(T)() const
         if (is(Unqual!T == uint))
     {
-        auto d = view.leastSignificantFirst;
-        return cast(uint) d.front;
+        return cast(uint) data[0];
     }
 
     /++
@@ -820,17 +781,11 @@ struct UInt(size_t size)
         enum N = min(ret.data.length, data.length);
         static if (lowerBits)
         {
-            version (LittleEndian)
                 ret.data[0 .. N] = data[0 .. N];
-            else
-                ret.data[$ - N .. $] = data[$ - N .. $];
         }
         else
         {
-            version (LittleEndian)
                 ret.data[0 .. N] = data[$ - N .. $];
-            else
-                ret.data[$ - N .. $] = data[0 .. N];
         }
         return ret;
     }
@@ -856,7 +811,7 @@ struct UInt(size_t size)
 
     /++
     +/
-    bool bt()(size_t position) const
+    bool bt(size_t position) const
         @safe pure nothrow @nogc
     {
         assert(position < data.sizeof * 8);
@@ -882,7 +837,7 @@ struct UInt(size_t size)
 
     /++
     +/
-    size_t ctlz()() const @property
+    size_t ctlz() const @property
         @safe pure nothrow @nogc
     {
         return view.ctlz;
@@ -904,7 +859,7 @@ struct UInt(size_t size)
 
     /++
     +/
-    size_t cttz()() const @property
+    size_t cttz() const @property
         @safe pure nothrow @nogc
     {
         return view.cttz;
@@ -926,22 +881,16 @@ struct UInt(size_t size)
 
     /++
     +/
-    bool signBit()() const @property
+    bool signBit() const @property
     {
-        version (LittleEndian)
             return data[$ - 1] >> (size_t.sizeof * 8 - 1);
-        else
-            return data[0] >> (size_t.sizeof * 8 - 1);
     }
 
     /// ditto
-    void signBit()(bool value) @property
+    void signBit(bool value) @property
     {
         enum signMask = ptrdiff_t.max;
-        version (LittleEndian)
             data[$ - 1] = (data[$ - 1] & ptrdiff_t.max) | (size_t(value) << (size_t.sizeof * 8 - 1));
-        else
-            data[    0] = (data[    0] & ptrdiff_t.max) | (size_t(value) << (size_t.sizeof * 8 - 1));
     }
 
     ///
@@ -961,44 +910,39 @@ struct UInt(size_t size)
 /++
 +/
 UInt!sizeB extendedMulHigh(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
+    @safe pure nothrow @nogc
 {
     return (extendedMul(a, b) >> sizeA).toSize!sizeB;
 }
 
 /++
 +/
-UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b) @safe
+UInt!(sizeA + sizeB) extendedMul(size_t sizeA, size_t sizeB)(UInt!sizeA a, UInt!sizeB b)
+    @safe pure nothrow @nogc
 {
     UInt!(sizeA + sizeB) ret;
     enum al = a.data.length;
     enum alp1 = a.data.length + 1;
-    version (LittleEndian)
-    {
         ret.data[0 .. alp1] = extendedMul(a, b.data[0]).data;
         static foreach ( i; 1 .. b.data.length)
             ret.data[i .. i + alp1] = extendedMulAdd(a, b.data[i], UInt!sizeA(ret.data[i .. i + al])).data;
-    }
-    else
-    {
-        ret.data[$ - alp1 .. $] = extendedMul(a, b.data[$ - 1]).data;
-        static foreach_reverse ( i; 0 .. b.data.length - 1)
-            ret.data[i .. i + alp1] = extendedMulAdd(a, b.data[i], UInt!sizeA(ret.data[i .. i + al])).data;
-    }
     return ret;
 }
 
 /// ditto
 UInt!(size + size_t.sizeof * 8)
-    extendedMul(size_t size)(UInt!size a, size_t b) @safe
+    extendedMul(size_t size)(UInt!size a, size_t b)
+    @safe pure nothrow @nogc
 {
     size_t overflow = a.view *= b;
     auto ret = a.toSize!(size + size_t.sizeof * 8);
-    ret.view.mostSignificant = overflow;
+    ret.data[$ - 1] = overflow;
     return ret;
 }
 
 /// ditto
 UInt!128 extendedMul()(ulong a, ulong b)
+    @safe pure nothrow @nogc
 {
     static if (size_t.sizeof == ulong.sizeof)
     {
@@ -1017,6 +961,7 @@ UInt!128 extendedMul()(ulong a, ulong b)
 
 /// ditto
 UInt!64 extendedMul()(uint a, uint b)
+    @safe pure nothrow @nogc
 {
     static if (size_t.sizeof == uint.sizeof)
     {
@@ -1089,6 +1034,6 @@ UInt!(size + size_t.sizeof * 8)
 {
     auto ret = extendedMul(a, b);
     auto view = ret.view;
-    view.mostSignificant += view.topLeastSignificantPart(a.data.length) += c.view;
+    view.coefficients[$ - 1] += view.topLeastSignificantPart(a.data.length) += c.view;
     return ret;
 }
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index ba412133..931965d8 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -54,11 +54,11 @@ struct BigInt(uint maxSize64)
         static if (size_t.sizeof == ulong.sizeof)
         {
             length = data != 0;
-            view.leastSignificantFirst[0] = data;
+            view.coefficients[0] = data;
         }
         else
         {
-            auto d = view.leastSignificantFirst;
+            auto d = view.coefficients;
             d[0] = cast(uint) data;
             d[1] = cast(uint) (data >> 32);
             length = data && (data >> 32);
@@ -108,12 +108,12 @@ struct BigInt(uint maxSize64)
         static if (size_t.sizeof == ulong.sizeof)
         {
             length = 1;
-            view.leastSignificantFirst[0] = data;
+            view.coefficients[0] = data;
         }
         else
         {
             length = 2;
-            auto d = view.leastSignificantFirst;
+            auto d = view.coefficients;
             d[0] = cast(uint) data;
             d[1] = cast(uint) (data >> 32);
         }
@@ -189,7 +189,7 @@ struct BigInt(uint maxSize64)
     bool opEquals()(size_t rhs, bool rhsSign = false)
         const @safe pure nothrow @nogc
     {
-        return rhs == 0 && this.length == 0 || this.length == 1 && this.sign == rhsSign && view.unsigned.leastSignificant == rhs;
+        return rhs == 0 && this.length == 0 || this.length == 1 && this.sign == rhsSign && this.data[0] == rhs;
     }
 
     ///
@@ -197,7 +197,7 @@ struct BigInt(uint maxSize64)
         const @safe pure nothrow @nogc
     {
         auto sign = rhs < 0;
-        return opEquals(sign ? ulong(-rhs) : ulong(rhs), sign);
+        return this.opEquals(sign ? ulong(-rhs) : ulong(rhs), sign);
     }
 
     /++
@@ -211,19 +211,13 @@ struct BigInt(uint maxSize64)
     ///
     BigIntView!size_t view()() scope return @property
     {
-        version (LittleEndian)
             return typeof(return)(this.data[0 .. this.length], this.sign);
-        else
-            return typeof(return)(this.data[$ - this.length .. $], this.sign);
     }
 
     ///
     BigIntView!(const size_t) view()() const scope return @property
     {
-        version (LittleEndian)
             return typeof(return)(this.data[0 .. this.length], this.sign);
-        else
-            return typeof(return)(this.data[$ - this.length .. $], this.sign);
     }
 
     ///
@@ -240,10 +234,7 @@ struct BigInt(uint maxSize64)
     void putCoefficient(size_t value)
     {
         assert(length < data.length);
-        version (LittleEndian)
             data[length++] = value;
-        else
-            data[$ - ++length] = value;
     }
 
     /++
@@ -371,7 +362,7 @@ struct BigInt(uint maxSize64)
         else
         {
             auto view = this.view.unsigned.opCast!(BigUIntView!(uint));
-            if (view.coefficients.length && view.mostSignificant == 0)
+            if (view.coefficients.length && view.coefficients[$ - 1] == 0)
                 view.popMostSignificant;
             return view;
         }
@@ -384,7 +375,7 @@ struct BigInt(uint maxSize64)
         else
         {
             auto view = this.view.unsigned.opCast!(BigUIntView!(const uint));
-            if (view.coefficients.length && view.mostSignificant == 0)
+            if (view.coefficients.length && view.coefficients[$ - 1] == 0)
                 view.popMostSignificant;
             return view;
         }
@@ -419,7 +410,7 @@ struct BigInt(uint maxSize64)
         BigInt!(maxSize64 * 2) res = void;
         res = 1u;
 
-        foreach (b; exponent.leastSignificantFirst.bitwise[0 .. $ - exponent.ctlz])
+        foreach (b; exponent.coefficients.bitwise[0 .. $ - exponent.ctlz])
         {
             bas %= modulus;
             if (b)
@@ -476,7 +467,7 @@ struct BigInt(uint maxSize64)
         this.length = this.data.length;
         multiply(this.view.unsigned, a.view.unsigned, b.view.unsigned);
         this.length = this.length.min(a.length + b.length);
-        this.length -= this.length && !this.view.unsigned.mostSignificant;
+        this.length -= this.length && !this.view.unsigned.coefficients[$ - 1];
         this.sign = (this.length != 0) & (a.sign ^ b.sign);
         return this;
     }
@@ -492,7 +483,7 @@ struct BigInt(uint maxSize64)
 
         pragma(inline, false);
 
-        if (divisor.length == 0 || divisor.view.unsigned.mostSignificant == 0)
+        if (divisor.length == 0 || divisor.view.unsigned.coefficients[$ - 1] == 0)
             assert(0, "Zero or denormalized BigInt divizor");
 
         if (this.length < divisor.length)
@@ -601,7 +592,7 @@ struct BigInt(uint maxSize64)
         {
             auto oldLength = length;
             length = cast(int)rhs.coefficients.length;
-            view.unsigned.leastSignificantFirst[oldLength .. $] = 0;
+            view.unsigned.coefficients[oldLength .. $] = 0;
         }
         else
         if (rhs.coefficients.length == 0)
@@ -775,7 +766,7 @@ struct BigInt(uint maxSize64)
                 length = 0;
                 return this;
             }
-            auto d = view.leastSignificantFirst;
+            auto d = view.coefficients;
             if (bs)
             {
                 foreach (j; 0 .. d.length - (index + 1))
@@ -790,7 +781,7 @@ struct BigInt(uint maxSize64)
                     d[j] = d[j + index];
                 }
             }
-            auto most = d[$ - (index + 1)] = d.back >>> bs;
+            auto most = d[$ - (index + 1)] = d[$ - 1] >>> bs;
             length -= index + (most == 0);
         }
         else
@@ -803,14 +794,14 @@ struct BigInt(uint maxSize64)
 
             if (bs)
             {
-                auto most = view.unsigned.mostSignificant >> ss;
+                auto most = view.unsigned.coefficients[$ - 1] >> ss;
                 length += index;
                 if (length < data.length)
                 {
                     if (most)
                     {
                         length++;
-                        view.unsigned.mostSignificant = most;
+                        view.unsigned.coefficients[$ - 1] = most;
                         length--;
                     }
                 }
@@ -819,25 +810,25 @@ struct BigInt(uint maxSize64)
                     length = data.length;
                 }
 
-                auto d = view.leastSignificantFirst;
+                auto d = view.coefficients;
                 foreach_reverse (j; index + 1 .. length)
                 {
                     d[j] = (d[j - index] << bs) | (d[j - (index + 1)] >> ss);
                 }
-                d[index] = d.front << bs;
+                d[index] = d[0] << bs;
                 if (length < data.length)
                     length += most != 0;
             }
             else
             {
                 length = cast(uint) min(length + index, cast(uint)data.length);
-                auto d = view.leastSignificantFirst;
+                auto d = view.coefficients;
                 foreach_reverse (j; index .. length)
                 {
                     d[j] = d[j - index];
                 }
             }
-            view.leastSignificantFirst[0 .. index] = 0;
+            view.coefficients[0 .. index] = 0;
         }
         return this;
     }
@@ -859,9 +850,9 @@ struct BigInt(uint maxSize64)
     /++
     Returns: overflow flag
     +/
-    bool copyFrom(W, WordEndian endian)(BigIntView!(const W, endian) view)
+    bool copyFrom(W)(BigIntView!(const W) view)
     {
-        static if (W.sizeof > size_t.sizeof && endian == TargetEndian)
+        static if (W.sizeof > size_t.sizeof)
         {
             return this.copyFrom(cast(BigIntView!(const size_t))view);
         }
@@ -872,25 +863,25 @@ struct BigInt(uint maxSize64)
             auto rhs = view;
             auto overflow = lhs.coefficients.length < rhs.coefficients.length;
             auto n = overflow ? lhs.coefficients.length : rhs.coefficients.length;
-            lhs.leastSignificantFirst[0 .. n] = rhs.leastSignificantFirst[0 .. n];
+            lhs.coefficients[0 .. n] = rhs.coefficients[0 .. n];
             this.length = cast(uint)(n / (size_t.sizeof / W.sizeof));
             if (auto tail = n % (size_t.sizeof / W.sizeof))
             {
                 this.length++;
                 auto shift = ((size_t.sizeof / W.sizeof) - tail) * (W.sizeof * 8);
-                auto value = this.view.unsigned.mostSignificant;
+                auto value = this.view.unsigned.coefficients[$ - 1];
                 value <<= shift;
                 value >>= shift;
-                this.view.unsigned.mostSignificant = value;
+                this.view.unsigned.coefficients[$ - 1] = value;
             }
             return overflow;
         }
     }
 
     /// ditto
-    bool copyFrom(W, WordEndian endian)(BigUIntView!(const W, endian) view)
+    bool copyFrom(W)(BigUIntView!(const W) view)
     {
-        return this.copyFrom(BigIntView!(const W, endian)(view));
+        return this.copyFrom(BigIntView!(const W)(view));
     }
 
     ///
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index a7400b06..ba4b1ddc 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -1,7 +1,7 @@
 /++
 Low-level betterC utilities for big integer arithmetic libraries.
 
-The module provides $(REF BigUIntAccumulator), $(REF BigUIntView), and $(LREF BigIntView),  $(REF DecimalView).
+The module provides $(REF BigUIntView), and $(LREF BigIntView),  $(REF DecimalView).
 
 Note:
     The module doesn't provide full arithmetic API for now.
@@ -26,29 +26,6 @@ version (D_Exceptions)
     package immutable binaryStringException = new Exception(binaryStringErrorMsg);
 }
 
-/++
-+/
-enum WordEndian
-{
-    ///
-    little,
-    ///
-    big,
-}
-
-version(LittleEndian)
-{
-    /++
-    +/
-    enum TargetEndian = WordEndian.little;
-}
-else
-{
-    /++
-    +/
-    enum TargetEndian = WordEndian.big;
-}
-
 package template MaxWordPow10(T)
 {
     static if (is(T == ubyte))
@@ -114,7 +91,7 @@ T ceilLog10Exp2(T)(const T e)
 }
 
 ///
-version(mir_bignum_test)
+version(mir_bignum_test_llv)
 @safe pure nothrow @nogc unittest
 {
     assert(ceilLog10Exp2(ubyte(10)) == 4); // ubyte
@@ -142,7 +119,7 @@ private U multiplyAddKernel(string op, U, C, A)(
         // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
         // - n + overflow <= - 1
         // overflow <= n - 1
-        auto ext = a.front.extMul(b);
+        auto ext = a[0].extMul(b);
         {
             bool bit;
             ext.low = ext.low.addu(overflow, bit);
@@ -151,13 +128,13 @@ private U multiplyAddKernel(string op, U, C, A)(
         }
         {
             bool bit;
-            c.front = c.front.cop!op(ext.low, bit);
+            c[0] = c[0].cop!op(ext.low, bit);
             assert(bit == 0 || ext.high < ext.high.max);
             overflow = ext.high + bit;
         }
 
-        a.popFront;
-        c.popFront;
+        a = a[1 .. $];
+        c = c[1 .. $];
     }
     while(c.length);
     return overflow;
@@ -182,7 +159,7 @@ private U addKernel(string op, U, C, A)(
         // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
         // - n + overflow <= - 1
         // overflow <= n - 1
-        auto ext = a.front;
+        auto ext = a[0];
         {
             bool bit;
             ext = ext.addu(overflow, bit);
@@ -190,12 +167,12 @@ private U addKernel(string op, U, C, A)(
         }
         {
             bool bit;
-            c.front = c.front.cop!op(ext, bit);
+            c[0] = c[0].cop!op(ext, bit);
             overflow += bit;
         }
 
-        a.popFront;
-        c.popFront;
+        a = a[1 .. $];
+        c = c[1 .. $];
     }
     while(c.length);
     return overflow;
@@ -210,9 +187,9 @@ void multiply(U)(
 {
     pragma (inline, false);
 
-    auto a = _a.leastSignificantFirst;
-    auto b = _b.leastSignificantFirst;
-    auto c = _c.leastSignificantFirst;
+    auto a = _a.coefficients;
+    auto b = _b.coefficients;
+    auto c = _c.coefficients;
 
     if (a.length < b.length)
     {
@@ -240,12 +217,12 @@ void multiply(U)(
         size_t j;
         import mir.utility: min;
         auto length = min(a.length, c.length);
-        auto overflow = multiplyAddKernel!("+", U)(c[0 .. length], a[0 .. length], b.front);
+        auto overflow = multiplyAddKernel!("+", U)(c[0 .. length], a[0 .. length], b[0]);
         if (length < c.length)
             c[length] = overflow;
 
-        c.popFront;
-        b.popFront;
+        c = c[1 .. $];
+        b = b[1 .. $];
     }
     while(b.length);
 }
@@ -270,9 +247,9 @@ size_t divMod(
     BigUIntView!uint _q,
 )
     @trusted pure nothrow @nogc
-    in (_u.length == 0 || _u.mostSignificant)
+    in (_u.length == 0 || _u.coefficients[$ - 1])
     in (_v.length)
-    in (_v.mostSignificant)
+    in (_v.coefficients[$ - 1])
     in (sizediff_t(_q.length) >= sizediff_t(_u.length - _v.length + 1))
 {
     pragma (inline, false);
@@ -282,9 +259,9 @@ size_t divMod(
 
     size_t m = _u.coefficients.length;
     size_t n = _v.coefficients.length;
-    auto u = _u.leastSignificantFirst();
-    auto v = _v.leastSignificantFirst();
-    auto q = _q.leastSignificantFirst();
+    auto u = _u.coefficients[];
+    auto v = _v.coefficients[];
+    auto q = _q.coefficients[];
 
     if (m < n)
         return 0;
@@ -311,7 +288,7 @@ size_t divMod(
         auto s = cast(uint)ctlz(v[n - 1]);
         assert(s <= 31 && s >= 0);
 
-        uint umn = s ? _u.mostSignificant >> (32 - s) : 0;
+        uint umn = s ? _u.coefficients[$ - 1] >> (32 - s) : 0;
         _u.smallLeftShiftInPlace(s);
         _v.smallLeftShiftInPlace(s);
 
@@ -363,7 +340,7 @@ size_t divMod(
         while(j--);
 
         _u.smallRightShiftInPlace(s);
-        _u.mostSignificant |= s ? umn << (32 - s) : 0;
+        _u.coefficients[$ - 1] |= s ? umn << (32 - s) : 0;
         // _v.smallRightShiftInPlace(s);
     }
 
@@ -371,7 +348,7 @@ size_t divMod(
 }
 
 ///
-version(mir_bignum_test)
+version(mir_bignum_test_llv)
 unittest
 {
     import mir.bignum.fixed: UInt;
@@ -425,7 +402,7 @@ unittest
 /++
 Arbitrary length unsigned integer view.
 +/
-struct BigUIntView(W, WordEndian endian = TargetEndian)
+struct BigUIntView(W)
     if (__traits(isUnsigned, W))
 {
     import mir.bignum.fp: Fp, half;
@@ -449,7 +426,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Retrurns: signed integer view using the same data payload
     +/
-    BigIntView!(W, endian) signed()(bool sign = false) @safe pure nothrow @nogc scope @property
+    BigIntView!W signed()(bool sign = false) @safe pure nothrow @nogc scope @property
     {
         return typeof(return)(this, sign);
     }
@@ -466,9 +443,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return this.opCast!(Fp!s, s - md, wordNormalized, nonZero).opCast!(T, true);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     unittest
     {
         auto a = cast(double) BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
@@ -480,7 +457,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     ///
     @safe
     T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
-        if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
+        if (internalRoundLastBits < size_t.sizeof * 8)
     {
         static if (isMutable!W)
         {
@@ -512,16 +489,16 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                 {
                     static if (N == 1 && W.sizeof == size_t.sizeof)
                     {
-                        ret.coefficient.data[0] = integer.mostSignificant;
+                        ret.coefficient.data[0] = integer.coefficients[$ - 1];
                     }
                     else
                     {
                         BigUIntView!size_t(ret.coefficient.data)
                             .opCast!(BigUIntView!(Unqual!W))
-                            .leastSignificantFirst
-                                [$ - integer.coefficients.length .. $] = integer.leastSignificantFirst;
+                            .coefficients
+                                [$ - integer.coefficients.length .. $] = integer.coefficients;
                     }
-                    auto c = cast(uint) ctlz(ret.coefficient.view.mostSignificant);
+                    auto c = cast(uint) ctlz(ret.coefficient.view.coefficients[$ - 1]);
                     ret.exponent -= c;
                     ret.coefficient = ret.coefficient.smallLeftShift(c);
                 }
@@ -532,37 +509,29 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
                     static if (N == 1 && W.sizeof == size_t.sizeof)
                     {
-                        version (BigEndian)
-                        {
-                            holder.data[0] = integer.mostSignificantFirst[0];
-                            holder.data[1] = integer.mostSignificantFirst[1];
-                        }
-                        else
-                        {
-                            holder.data[0] = integer.mostSignificantFirst[1];
-                            holder.data[1] = integer.mostSignificantFirst[0];
-                        }
+                        holder.data[0] = integer.mostSignificantFirst[1];
+                        holder.data[1] = integer.mostSignificantFirst[0];
                     }
                     else
                     {
                         auto holderView = holder
                             .view
                             .opCast!(BigUIntView!(Unqual!W))
-                            .leastSignificantFirst;
+                            .coefficients;
                         import mir.utility: min;
                         auto minLength = min(integer.coefficients.length, holderView.length);
-                        holderView[$ - minLength .. $] = integer.leastSignificantFirst[$ - minLength .. $];
+                        holderView[$ - minLength .. $] = integer.coefficients[$ - minLength .. $];
                     }
 
-                    auto c = cast(uint) ctlz(holder.view.mostSignificant);
+                    auto c = cast(uint) ctlz(holder.view.coefficients[$ - 1]);
                     ret.exponent -= c;
                     holder = holder.smallLeftShift(c);
                     ret.coefficient = holder.toSize!(coefficientSize, false);
-                    auto tail = BigUIntView!size_t(holder.data).leastSignificant;
+                    auto tail = BigUIntView!size_t(holder.data).coefficients[0];
 
                     bool nonZeroTail()
                     {
-                        while(_expect(integer.leastSignificant == 0, false))
+                        while(_expect(integer.coefficients[0] == 0, false))
                         {
                             integer.popLeastSignificant;
                             assert(integer.coefficients.length);
@@ -574,12 +543,12 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                     {
                         enum half = size_t(1) << (internalRoundLastBits - 1);
                         enum mask0 = (size_t(1) << internalRoundLastBits) - 1;
-                        auto tail0 = BigUIntView!size_t(ret.coefficient.data).leastSignificant & mask0;
-                        BigUIntView!size_t(ret.coefficient.data).leastSignificant &= ~mask0;
+                        auto tail0 = BigUIntView!size_t(ret.coefficient.data).coefficients[0] & mask0;
+                        BigUIntView!size_t(ret.coefficient.data).coefficients[0] &= ~mask0;
                         auto condInc = tail0 >= half
                             && (   tail0 > half
                                 || tail
-                                || (BigUIntView!size_t(ret.coefficient.data).leastSignificant & 1)
+                                || (BigUIntView!size_t(ret.coefficient.data).coefficients[0] & 1)
                                 || nonZeroTail);
                     }
                     else
@@ -587,7 +556,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                         enum half = cast(size_t)Signed!size_t.min;
                         auto condInc = tail >= half
                             && (    tail > half
-                                || (BigUIntView!size_t(ret.coefficient.data).leastSignificant & 1)
+                                || (BigUIntView!size_t(ret.coefficient.data).coefficients[0] & 1)
                                 || nonZeroTail);
                     }
 
@@ -608,9 +577,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         }
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -669,7 +638,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         }
         static if (T.sizeof <= W.sizeof)
         {
-            return cast(T) work.leastSignificant;
+            return cast(T) work.coefficients[0];
         }
         else
         {
@@ -677,7 +646,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
             do
             {
                 ret <<= W.sizeof * 8;
-                ret |= work.mostSignificant;
+                ret |= work.coefficients[$ - 1];
                 work.popMostSignificant;
             }
             while(work.coefficients.length);
@@ -685,9 +654,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         }
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -697,8 +666,8 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         assert(cast(ubyte) view == 0x9d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -708,8 +677,8 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         assert(cast(ubyte) view == 0x9d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -719,7 +688,6 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         assert(cast(ubyte) view == 0x9d);
     }
 
-    static if (endian == TargetEndian)
     ///
     pure nothrow @nogc
     BigUIntView!V opCast(T : BigUIntView!V, V)()
@@ -736,7 +704,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    BigUIntView!(const W, endian) lightConst()()
+    BigUIntView!(const W) lightConst()()
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(coefficients);
@@ -746,7 +714,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    sizediff_t opCmp(BigUIntView!(const W, endian) rhs)
+    sizediff_t opCmp(BigUIntView!(const W) rhs)
         const @safe pure nothrow @nogc scope
     {
         import mir.algorithm.iteration: cmp;
@@ -758,70 +726,38 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    bool opEquals(BigUIntView!(const W, endian) rhs)
+    bool opEquals(BigUIntView!(const W) rhs)
         const @safe pure nothrow @nogc scope
     {
         return this.coefficients == rhs.coefficients;
     }
 
-    /++
-    +/
-    ref inout(W) mostSignificant() inout @property
-    {
-        static if (endian == WordEndian.big)
-            return coefficients[0];
-        else
-            return coefficients[$ - 1];
-    }
-
-    /++
-    +/
-    ref inout(W) leastSignificant() inout @property
-    {
-        static if (endian == WordEndian.little)
-            return coefficients[0];
-        else
-            return coefficients[$ - 1];
-    }
-
     /++
     +/
     void popMostSignificant() scope
     {
-        static if (endian == WordEndian.big)
-            coefficients = coefficients[1 .. $];
-        else
-            coefficients = coefficients[0 .. $ - 1];
+        coefficients = coefficients[0 .. $ - 1];
     }
 
     /++
     +/
     void popLeastSignificant() scope
     {
-        static if (endian == WordEndian.little)
-            coefficients = coefficients[1 .. $];
-        else
-            coefficients = coefficients[0 .. $ - 1];
+        coefficients = coefficients[1 .. $];
     }
 
     /++
     +/
     BigUIntView topMostSignificantPart(size_t length)
     {
-        static if (endian == WordEndian.big)
-            return BigUIntView(coefficients[0 .. length]);
-        else
-            return BigUIntView(coefficients[$ - length .. $]);
+        return BigUIntView(coefficients[$ - length .. $]);
     }
 
     /++
     +/
     BigUIntView topLeastSignificantPart(size_t length)
     {
-        static if (endian == WordEndian.little)
-            return BigUIntView(coefficients[0 .. length]);
-        else
-            return BigUIntView(coefficients[$ - length .. $]);
+        return BigUIntView(coefficients[0 .. length]);
     }
 
     /++
@@ -833,16 +769,16 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         if (shift == 0)
             return;
         auto csh = W.sizeof * 8 - shift;
-        auto d = leastSignificantFirst;
+        auto d = coefficients[];
         assert(d.length);
         foreach_reverse (i; 1 .. d.length)
             d[i] = (d[i] << shift) | (d[i - 1] >>> csh);
-        d.front <<= shift;
+        d[0] <<= shift;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -862,16 +798,16 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         if (shift == 0)
             return;
         auto csh = W.sizeof * 8 - shift;
-        auto d = leastSignificantFirst;
+        auto d = coefficients[];
         assert(d.length);
         foreach (i; 0 .. d.length - 1)
             d[i] = (d[i] >>> shift) | (d[i + 1] << csh);
-        d.back >>>= shift;
+        d[$ - 1] >>>= shift;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -888,7 +824,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     {
         auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
         auto data = new Unqual!W[length];
-        auto view = BigUIntView!(Unqual!W, endian)(data);
+        auto view = BigUIntView!(Unqual!W)(data);
         if (view.fromHexStringImpl!(C, allowUnderscores)(str))
             return BigUIntView(cast(W[])view.coefficients);
         version(D_Exceptions)
@@ -914,7 +850,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                 return false;
         }
 
-        leastSignificant = 0;
+        coefficients[0] = 0;
         auto work = topLeastSignificantPart(1);
         W current;
         size_t i, j;
@@ -968,7 +904,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
             current |= cc;
             if (j % (W.sizeof * 2) == 0) // is this packed var full? 
             {
-                work.mostSignificant = current;
+                work.coefficients[$ - 1] = current;
                 current = 0;
                 if (_expect(work.coefficients.length < coefficients.length, true))
                 {
@@ -991,7 +927,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         if (current)
         {
             current >>>= 4 * (W.sizeof * 2 - j % (W.sizeof * 2));
-            work.mostSignificant = current;
+            work.coefficients[$ - 1] = current;
         }
 
         coefficients = coefficients[0 .. (j / (W.sizeof * 2) + (j % (W.sizeof * 2) != 0))];
@@ -999,9 +935,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return true;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -1009,9 +945,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         assert(cast(ulong)view == 0xabcd_efab_cdef);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -1047,7 +983,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     {
         auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
         auto data = new Unqual!W[length];
-        auto view = BigUIntView!(Unqual!W, endian)(data);
+        auto view = BigUIntView!(Unqual!W)(data);
         if (view.fromBinaryStringImpl!(C, allowUnderscores)(str))
             return BigUIntView(cast(W[])view.coefficients);
         version(D_Exceptions)
@@ -1073,7 +1009,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                 return false;
         }
 
-        leastSignificant = 0;
+        coefficients[0] = 0;
         auto work = topLeastSignificantPart(1);
         W current;
         size_t i, j;
@@ -1107,7 +1043,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
             current |= cc;
             if (j % (W.sizeof * 8) == 0) // is this packed var full? 
             {
-                work.mostSignificant = current;
+                work.coefficients[$ - 1] = current;
                 current = 0;
                 if (_expect(work.coefficients.length < coefficients.length, true))
                 {
@@ -1130,7 +1066,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         if (current)
         {
             current >>>= (W.sizeof * 8 - j % (W.sizeof * 8));
-            work.mostSignificant = current;
+            work.coefficients[$ - 1] = current;
         }
 
         coefficients = coefficients[0 .. (j / (W.sizeof * 8) + (j % (W.sizeof * 8) != 0))];
@@ -1138,9 +1074,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return true;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -1148,9 +1084,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         assert(cast(ulong)view == 0b1111_0000_0101);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -1194,7 +1130,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         if (_expect(str.length == 0, false))
             return false;
 
-        leastSignificant = 0;
+        coefficients[0] = 0;
         uint d = str[0] - '0';
         str = str[1 .. $];
 
@@ -1237,7 +1173,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                     if (_expect(work.coefficients.length < coefficients.length, true))
                     {
                         work = topLeastSignificantPart(work.coefficients.length + 1);
-                        work.mostSignificant = overflow;
+                        work.coefficients[$ - 1] = overflow;
                     }
                     else
                     {
@@ -1266,21 +1202,21 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     Returns:
         true in case of unsigned overflow
     +/
-    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(BigUIntView!(const W) rhs, bool overflow = false)
     @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
         assert(this.coefficients.length > 0);
         assert(rhs.coefficients.length <= this.coefficients.length);
-        auto ls = this.leastSignificantFirst;
-        auto rs = rhs.leastSignificantFirst;
+        auto ls = this.coefficients;
+        auto rs = rhs.coefficients;
         do
         {
             bool overflowM, overflowG;
-            ls.front = ls.front.cop!op(rs.front, overflowM).cop!op(overflow, overflowG);
+            ls[0] = ls[0].cop!op(rs[0], overflowM).cop!op(overflow, overflowG);
             overflow = overflowG | overflowM;
-            ls.popFront;
-            rs.popFront;
+            ls = ls[1 .. $];
+            rs = rs[1 .. $];
         }
         while(rs.length);
         if (overflow && ls.length)
@@ -1290,7 +1226,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 
     static if (isMutable!W && W.sizeof >= 4)
     /// ditto
-    bool opOpAssign(string op)(scope BigIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(scope BigIntView!(const W) rhs, bool overflow = false)
     @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
@@ -1313,16 +1249,16 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         if ((op == "+" || op == "-") && is(T == W))
     {
         assert(this.coefficients.length > 0);
-        auto ns = this.leastSignificantFirst;
+        auto ns = this.coefficients;
         W additive = rhs;
         do
         {
             bool overflow;
-            ns.front = ns.front.cop!op(additive, overflow);
+            ns[0] = ns[0].cop!op(additive, overflow);
             if (!overflow)
                 return overflow;
             additive = overflow;
-            ns.popFront;
+            ns = ns[1 .. $];
         }
         while (ns.length);
         return true;
@@ -1353,21 +1289,21 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         @safe pure nothrow @nogc scope
     {
         assert(coefficients.length);
-        auto ns = this.leastSignificantFirst;
+        auto ns = this.coefficients;
         do
         {
             import mir.utility: extMul;
-            auto ext = ns.front.extMul(rhs);
+            auto ext = ns[0].extMul(rhs);
             bool overflowM;
-            ns.front = ext.low.cop!"+"(overflow, overflowM);
+            ns[0] = ext.low.cop!"+"(overflow, overflowM);
             overflow = ext.high + overflowM;
-            ns.popFront;
+            ns = ns[1 .. $];
         }
         while (ns.length);
         return overflow;
     }
 
-    static if (isMutable!W && W.sizeof == 4 || W.sizeof == 8 && endian == TargetEndian)
+    static if (isMutable!W && W.sizeof == 4 || W.sizeof == 8)
     /++
     Performs `uint remainder = (overflow$big) /= scalar` operatrion, where `$` denotes big-endian concatenation.
     Precondition: non-empty coefficients, `overflow < rhs`
@@ -1393,14 +1329,14 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
                 overflow = ext % rhs;
             }
             while (++i < ns.length);
-            if (mostSignificant == 0)
+            if (coefficients[$ - 1] == 0)
                 popMostSignificant;
             return overflow;
         }
         else
         {
             auto work = opCast!(BigUIntView!uint);
-            if (work.mostSignificant == 0)
+            if (work.coefficients[$ - 1] == 0)
                 work.popMostSignificant;
             auto remainder = work.opOpAssign!op(rhs, overflow);
             coefficients = coefficients[0 .. work.coefficients.length / 2 + work.coefficients.length % 2];
@@ -1423,18 +1359,18 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         @safe pure nothrow @nogc scope
     {
         assert(coefficients.length);
-        auto ns = this.leastSignificantFirst;
+        auto ns = this.coefficients;
         do
         {
             auto t = rhs;
-            auto overflowW = t.view *= ns.front;
+            auto overflowW = t.view *= ns[0];
             auto overflowM = t += overflow;
             overflowW += overflowM;
-            ns.front = cast(size_t) t;
+            ns[0] = cast(size_t) t;
             static if (size > size_t.sizeof * 8)
                 overflow = t.toSize!(size - size_t.sizeof * 8, false).toSize!size;
-            BigUIntView!size_t(overflow.data).mostSignificant = overflowW;
-            ns.popFront;
+            BigUIntView!size_t(overflow.data).coefficients[$ - 1] = overflowW;
+            ns = ns[1 .. $];
         }
         while (ns.length);
         return overflow;
@@ -1443,7 +1379,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     /++
     Returns: the same intger view with inversed sign
     +/
-    BigIntView!(W, endian) opUnary(string op : "-")()
+    BigIntView!W opUnary(string op : "-")()
     {
         return typeof(return)(this, true);
     }
@@ -1471,40 +1407,6 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return this.opOpAssign!"+"(W(1));
     }
 
-    /++
-    Returns: a slice of coefficients starting from the least significant.
-    +/
-    auto leastSignificantFirst()
-        @safe pure nothrow @nogc @property
-    {
-        import mir.ndslice.slice: sliced;
-        static if (endian == WordEndian.little)
-        {
-            return coefficients.sliced;
-        }
-        else
-        {
-            import mir.ndslice.topology: retro;
-            return coefficients.sliced.retro;
-        }
-    }
-
-    ///
-    auto leastSignificantFirst()
-        const @safe pure nothrow @nogc @property
-    {
-        import mir.ndslice.slice: sliced;
-        static if (endian == WordEndian.little)
-        {
-            return coefficients.sliced;
-        }
-        else
-        {
-            import mir.ndslice.topology: retro;
-            return coefficients.sliced.retro;
-        }
-    }
-
     /++
     Returns: a slice of coefficients starting from the most significant.
     +/
@@ -1512,15 +1414,8 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         @safe pure nothrow @nogc @property
     {
         import mir.ndslice.slice: sliced;
-        static if (endian == WordEndian.big)
-        {
-            return coefficients.sliced;
-        }
-        else
-        {
-            import mir.ndslice.topology: retro;
-            return coefficients.sliced.retro;
-        }
+        import mir.ndslice.topology: retro;
+        return coefficients.sliced.retro;
     }
 
     ///
@@ -1528,15 +1423,8 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         const @safe pure nothrow @nogc @property
     {
         import mir.ndslice.slice: sliced;
-        static if (endian == WordEndian.big)
-        {
-            return coefficients.sliced;
-        }
-        else
-        {
-            import mir.ndslice.topology: retro;
-            return coefficients.sliced.retro;
-        }
+        import mir.ndslice.topology: retro;
+        return coefficients.sliced.retro;
     }
 
     /++
@@ -1547,25 +1435,16 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         auto number = this;
         if (number.coefficients.length) do
         {
-            static if (endian == WordEndian.big)
-            {
-                if (number.coefficients[0])
-                    break;
-                number.coefficients = number.coefficients[1 .. $];
-            }
-            else
-            {
-                if (number.coefficients[$ - 1])
-                    break;
-                number.coefficients = number.coefficients[0 .. $ - 1];
-            }
+            if (number.coefficients[$ - 1])
+                break;
+            number.coefficients = number.coefficients[0 .. $ - 1];
         }
         while (number.coefficients.length);
         return number;
     }
 
     ///ditto
-    BigUIntView!(const W, endian) normalized() const
+    BigUIntView!(const W) normalized() const
     {
         return lightConst.normalized;
     }
@@ -1576,7 +1455,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     {
         import mir.ndslice.topology: bitwise;
         assert(position < coefficients.length * W.sizeof * 8);
-        return leastSignificantFirst.bitwise[position];
+        return coefficients.bitwise[position];
     }
 
     /++
@@ -1590,13 +1469,13 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         size_t ret;
         do
         {
-            if (auto c = d.front)
+            if (auto c = d[0])
             {
                 ret += ctlz(c);
                 break;
             }
             ret += W.sizeof * 8;
-            d.popFront;
+            d = d[1 .. $];
         }
         while(d.length);
         return ret;
@@ -1609,24 +1488,24 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     {
         import mir.bitop: cttz;
         assert(coefficients.length);
-        auto d = leastSignificantFirst;
+        auto d = coefficients[];
         size_t ret;
         do
         {
-            if (auto c = d.front)
+            if (auto c = d[0])
             {
                 ret += cttz(c);
                 break;
             }
             ret += W.sizeof * 8;
-            d.popFront;
+            d = d[1 .. $];
         }
         while(d.length);
         return ret;
     }
 
     ///
-    BigIntView!(W, endian) withSign()(bool sign)
+    BigIntView!W withSign()(bool sign)
     {
         return typeof(return)(this, sign);
     }
@@ -1687,7 +1566,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
     bool opEquals(ulong rhs)
         @safe pure nothrow @nogc const scope
     {
-        foreach (d; lightConst.leastSignificantFirst)
+        foreach (d; lightConst.coefficients)
         {
             static if (W.sizeof >= ulong.sizeof)
             {
@@ -1705,19 +1584,6 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return rhs == 0;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    ///
-    version(mir_bignum_test)
-    @safe pure
-    unittest
-    {
-        auto view2 = BigUIntView!(const(ubyte), WordEndian.big)([1, 0]);
-        assert(view2 == 256); // false
-        assert(cast(ulong)view2 == 256); // true
-        auto view = BigUIntView!(const(ubyte), WordEndian.big)([15, 255, 255]);
-        assert(view == 1048575); // false
-        assert(cast(ulong)view == 1048575); // true
-    }
 
     static if (isMutable!W && W.sizeof >= 4)
     /++
@@ -1756,9 +1622,9 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
         return str.length - i;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure @nogc
     unittest
     {
@@ -1772,7 +1638,7 @@ struct BigUIntView(W, WordEndian endian = TargetEndian)
 }
 
 ///
-version(mir_bignum_test)
+version(mir_bignum_test_llv)
 @safe pure nothrow
 unittest
 {
@@ -1780,63 +1646,54 @@ unittest
     alias AliasSeq(T...) = T;
 
     foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
-    foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big))
     {
-        static if (endian == WordEndian.little)
-        {
-            T[3] lhsData = [1, T.max-1, 0];
-            T[3] rhsData = [T.max, T.max, 0];
-        }
-        else
-        {
-            T[3] lhsData = [0, T.max-1, 1];
-            T[3] rhsData = [0, T.max, T.max];
-        }
+        T[3] lhsData = [1, T.max-1, 0];
+        T[3] rhsData = [T.max, T.max, 0];
 
-        auto lhs = BigUIntView!(T, endian)(lhsData).normalized;
+        auto lhs = BigUIntView!T(lhsData).normalized;
 
         /// bool overflow = bigUInt op= scalar
-        assert(lhs.leastSignificantFirst == [1, T.max-1]);
+        assert(lhs.coefficients == [1, T.max-1]);
         assert(lhs.mostSignificantFirst == [T.max-1, 1]);
         static if (T.sizeof >= 4)
         {
             assert((lhs += T.max) == false);
-            assert(lhs.leastSignificantFirst == [0, T.max]);
+            assert(lhs.coefficients == [0, T.max]);
             assert((lhs += T.max) == false);
             assert((lhs += T.max) == true); // overflow bit
-            assert(lhs.leastSignificantFirst == [T.max-1, 0]);
+            assert(lhs.coefficients == [T.max-1, 0]);
             assert((lhs -= T(1)) == false);
-            assert(lhs.leastSignificantFirst == [T.max-2, 0]);
+            assert(lhs.coefficients == [T.max-2, 0]);
             assert((lhs -= T.max) == true); // underflow bit
-            assert(lhs.leastSignificantFirst == [T.max-1, T.max]);
+            assert(lhs.coefficients == [T.max-1, T.max]);
             assert((lhs -= Signed!T(-4)) == true); // overflow bit
-            assert(lhs.leastSignificantFirst == [2, 0]);
+            assert(lhs.coefficients == [2, 0]);
             assert((lhs += Signed!T.max) == false); // overflow bit
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0]);
+            assert(lhs.coefficients == [Signed!T.max + 2, 0]);
 
             ///  bool overflow = bigUInt op= bigUInt/bigInt
-            lhs = BigUIntView!(T, endian)(lhsData);
-            auto rhs = BigUIntView!(T, endian)(rhsData).normalized;
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
-            assert(rhs.leastSignificantFirst == [T.max, T.max]);
+            lhs = BigUIntView!T(lhsData);
+            auto rhs = BigUIntView!T(rhsData).normalized;
+            assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
+            assert(rhs.coefficients == [T.max, T.max]);
             assert((lhs += rhs) == false);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 1, 0, 1]);
+            assert(lhs.coefficients == [Signed!T.max + 1, 0, 1]);
             assert((lhs -= rhs) == false);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
             assert((lhs += -rhs) == true);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 3, 0, T.max]);
+            assert(lhs.coefficients == [Signed!T.max + 3, 0, T.max]);
             assert((lhs += -(-rhs)) == true);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
 
             /// W overflow = bigUInt *= scalar
             assert((lhs *= T.max) == 0);
             assert((lhs += T(Signed!T.max + 2)) == false);
-            assert(lhs.leastSignificantFirst == [0, Signed!T.max + 2, 0]);
+            assert(lhs.coefficients == [0, Signed!T.max + 2, 0]);
             lhs = lhs.normalized;
-            lhs.leastSignificantFirst[1] = T.max / 2 + 3;
-            assert(lhs.leastSignificantFirst == [0, T.max / 2 + 3]);
+            lhs.coefficients[1] = T.max / 2 + 3;
+            assert(lhs.coefficients == [0, T.max / 2 + 3]);
             assert((lhs *= 8u) == 4);
-            assert(lhs.leastSignificantFirst == [0, 16]);
+            assert(lhs.coefficients == [0, 16]);
         }
     }
 }
@@ -1844,7 +1701,7 @@ unittest
 /++
 Arbitrary length signed integer view.
 +/
-struct BigIntView(W, WordEndian endian = TargetEndian)
+struct BigIntView(W)
     if (is(Unqual!W == ubyte) || is(Unqual!W == ushort) || is(Unqual!W == uint) || is(Unqual!W == ulong))
 {
     import mir.bignum.fp: Fp;
@@ -1856,7 +1713,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     The number is encoded as pair of `unsigned` and `sign`.
     +/
-    BigUIntView!(W, endian) unsigned;
+    BigUIntView!W unsigned;
 
     /++
     Sign bit
@@ -1872,11 +1729,11 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     ///
     this(W[] coefficients, bool sign = false)
     {
-        this(BigUIntView!(W, endian)(coefficients), sign);
+        this(BigUIntView!W(coefficients), sign);
     }
 
     ///
-    this(BigUIntView!(W, endian) unsigned, bool sign = false)
+    this(BigUIntView!W unsigned, bool sign = false)
     {
         this.unsigned = unsigned;
         this.sign = sign;
@@ -1917,7 +1774,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         if (isSomeChar!C)
     {
         auto length = str.length / (W.sizeof * 2) + (str.length % (W.sizeof * 2) != 0);
-        auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]);
+        auto ret = BigIntView!(Unqual!W)(new Unqual!W[length]);
         if (ret.fromHexStringImpl!(C, allowUnderscores)(str))
             return  cast(BigIntView) ret;
         version(D_Exceptions)
@@ -1964,7 +1821,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         if (isSomeChar!C)
     {
         auto length = str.length / (W.sizeof * 8) + (str.length % (W.sizeof * 8) != 0);
-        auto ret = BigIntView!(Unqual!W, endian)(new Unqual!W[length]);
+        auto ret = BigIntView!(Unqual!W)(new Unqual!W[length]);
         if (ret.fromBinaryStringImpl!(C, allowUnderscores)(str))
             return cast(BigIntView) ret;
         version(D_Exceptions)
@@ -2014,9 +1871,9 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         return ret;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2024,9 +1881,9 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2034,9 +1891,9 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2044,9 +1901,9 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(a == -0xa.fbbfae3cd0bp+124);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2064,9 +1921,9 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         return ret;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2075,9 +1932,9 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2086,8 +1943,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2096,8 +1953,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
     
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2106,8 +1963,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2116,8 +1973,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2126,8 +1983,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2136,8 +1993,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2146,8 +2003,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2156,8 +2013,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2166,8 +2023,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2176,8 +2033,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(cast(int) view == -0x22b0021d);
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2189,16 +2046,16 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     /++
     +/
     T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
-        if (internalRoundLastBits < size_t.sizeof * 8 && (size_t.sizeof >= W.sizeof || endian == TargetEndian))
+        if (internalRoundLastBits < size_t.sizeof * 8)
     {
         auto ret = unsigned.opCast!(Fp!coefficientSize, internalRoundLastBits, wordNormalized, nonZero);
         ret.sign = sign;
         return ret;
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
+    static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2211,8 +2068,8 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
     }
 
-    static if (W.sizeof == size_t.sizeof && endian == TargetEndian)
-    version(mir_bignum_test)
+    static if (W.sizeof == size_t.sizeof)
+    version(mir_bignum_test_llv)
     @safe pure
     unittest
     {
@@ -2225,7 +2082,6 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
     }
 
-    static if (endian == TargetEndian)
     ///
     BigIntView!V opCast(T : BigIntView!V, V)()
         if (V.sizeof <= W.sizeof)
@@ -2233,7 +2089,6 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
     }
 
-    static if (endian == TargetEndian)
     ///
     BigIntView!V opCast(T : BigIntView!V, V)() const
         if (V.sizeof <= W.sizeof)
@@ -2242,7 +2097,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    BigIntView!(const W, endian) lightConst()()
+    BigIntView!(const W) lightConst()()
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(unsigned.lightConst, sign);
@@ -2253,7 +2108,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    sizediff_t opCmp(BigIntView!(const W, endian) rhs) 
+    sizediff_t opCmp(BigIntView!(const W) rhs) 
         const @safe pure nothrow @nogc scope
     {
         import mir.algorithm.iteration: cmp;
@@ -2267,7 +2122,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    bool opEquals(BigIntView!(const W, endian) rhs)
+    bool opEquals(BigIntView!(const W) rhs)
         const @safe pure nothrow @nogc scope
     {
         return (this.sign == rhs.sign || unsigned.coefficients.length == 0) && this.unsigned == rhs.unsigned;
@@ -2310,7 +2165,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     Returns:
         true in case of unsigned overflow
     +/
-    bool opOpAssign(string op)(scope BigIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(scope BigIntView!(const W) rhs, bool overflow = false)
     @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {
@@ -2338,7 +2193,7 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
 
     static if (isMutable!W && W.sizeof >= 4)
     /// ditto
-    bool opOpAssign(string op)(BigUIntView!(const W, endian) rhs, bool overflow = false)
+    bool opOpAssign(string op)(BigUIntView!(const W) rhs, bool overflow = false)
     @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {
@@ -2428,10 +2283,10 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     /++
     Returns: a slice of coefficients starting from the least significant.
     +/
-    auto leastSignificantFirst()
+    auto coefficients()
         @safe pure nothrow @nogc @property
     {
-        return unsigned.leastSignificantFirst;
+        return unsigned.coefficients;
     }
 
     /++
@@ -2457,14 +2312,14 @@ struct BigIntView(W, WordEndian endian = TargetEndian)
     }
 
     ///ditto
-    BigIntView!(const W, endian) normalized() const
+    BigIntView!(const W) normalized() const
     {
         return lightConst.normalized;
     }
 }
 
 ///
-version(mir_bignum_test)
+version(mir_bignum_test_llv)
 @safe pure nothrow
 unittest
 {
@@ -2472,66 +2327,57 @@ unittest
     alias AliasSeq(T...) = T;
 
     foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
-    foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big))
     {
-        static if (endian == WordEndian.little)
-        {
-            T[3] lhsData = [1, T.max-1, 0];
-            T[3] rhsData = [T.max, T.max, 0];
-        }
-        else
-        {
-            T[3] lhsData = [0, T.max-1, 1];
-            T[3] rhsData = [0, T.max, T.max];
-        }
+        T[3] lhsData = [1, T.max-1, 0];
+        T[3] rhsData = [T.max, T.max, 0];
 
-        auto lhs = BigIntView!(T, endian)(lhsData).normalized;
+        auto lhs = BigIntView!T(lhsData).normalized;
 
         ///  bool overflow = bigUInt op= scalar
-        assert(lhs.leastSignificantFirst == [1, T.max-1]);
+        assert(lhs.coefficients == [1, T.max-1]);
         assert(lhs.mostSignificantFirst == [T.max-1, 1]);
 
         static if (T.sizeof >= 4)
         {
 
             assert((lhs += T.max) == false);
-            assert(lhs.leastSignificantFirst == [0, T.max]);
+            assert(lhs.coefficients == [0, T.max]);
             assert((lhs += T.max) == false);
             assert((lhs += T.max) == true); // overflow bit
-            assert(lhs.leastSignificantFirst == [T.max-1, 0]);
+            assert(lhs.coefficients == [T.max-1, 0]);
             assert((lhs -= T(1)) == false);
-            assert(lhs.leastSignificantFirst == [T.max-2, 0]);
+            assert(lhs.coefficients == [T.max-2, 0]);
             assert((lhs -= T.max) == false);
-            assert(lhs.leastSignificantFirst == [2, 0]);
+            assert(lhs.coefficients == [2, 0]);
             assert(lhs.sign);
             assert((lhs -= Signed!T(-4)) == false);
-            assert(lhs.leastSignificantFirst == [2, 0]);
+            assert(lhs.coefficients == [2, 0]);
             assert(lhs.sign == false);
             assert((lhs += Signed!T.max) == false);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0]);
+            assert(lhs.coefficients == [Signed!T.max + 2, 0]);
 
             ///  bool overflow = bigUInt op= bigUInt/bigInt
-            lhs = BigIntView!(T, endian)(lhsData);
-            auto rhs = BigUIntView!(T, endian)(rhsData).normalized;
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
-            assert(rhs.leastSignificantFirst == [T.max, T.max]);
+            lhs = BigIntView!T(lhsData);
+            auto rhs = BigUIntView!T(rhsData).normalized;
+            assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
+            assert(rhs.coefficients == [T.max, T.max]);
             assert((lhs += rhs) == false);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 1, 0, 1]);
+            assert(lhs.coefficients == [Signed!T.max + 1, 0, 1]);
             assert((lhs -= rhs) == false);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
             assert((lhs += -rhs) == false);
             assert(lhs.sign);
-            assert(lhs.leastSignificantFirst == [T.max - (Signed!T.max + 2), T.max, 0]);
+            assert(lhs.coefficients == [T.max - (Signed!T.max + 2), T.max, 0]);
             assert(lhs.sign);
             assert((lhs -= -rhs) == false);
-            assert(lhs.leastSignificantFirst == [Signed!T.max + 2, 0, 0]);
+            assert(lhs.coefficients == [Signed!T.max + 2, 0, 0]);
             assert(lhs.sign == false);
         }
     }
 }
 
 ///
-version(mir_bignum_test)
+version(mir_bignum_test_llv)
 unittest
 {
     import mir.bignum.fixed: UInt;
@@ -2545,7 +2391,7 @@ unittest
 }
 
 ///
-version(mir_bignum_test)
+version(mir_bignum_test_llv)
 unittest
 {
     import mir.bignum.fixed: UInt;
@@ -2558,208 +2404,6 @@ unittest
     assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
 }
 
-/++
-An utility type to wrap a local buffer to accumulate unsigned numbers.
-+/
-struct BigUIntAccumulator(W, WordEndian endian = TargetEndian)
-    if (is(Unqual!W == uint) || is(Unqual!W == ulong))
-{
-    /++
-    A group of coefficients for a $(MREF DecimalRadix)`!W`.
-
-    The order corresponds to endianness.
-
-    The unused part can be uninitialized.
-    +/
-    W[] coefficients;
-
-    /++
-    Current length of initialized coefficients.
-
-    The initialization order corresponds to endianness.
-
-    The `view` method may return a view with empty coefficients, which isn't usable.
-    Put `0` or another number first to make the accumulator maintain a non-empty view.
-    +/
-    size_t length;
-
-    /++
-    Returns:
-        Current unsigned integer view.
-    Note:
-        The method may return a view with empty coefficients, which isn't usable.
-        Put `0` or another number first to make the accumulator maintain a non-empty view.
-    +/
-    BigUIntView!(W, endian) view() @safe pure nothrow @nogc @property
-    {
-        static if (endian == WordEndian.little)
-            return typeof(return)(coefficients[0 .. length]);
-        else
-            return typeof(return)(coefficients[$ - length .. $]);
-    }
-
-    /++
-    Returns:
-        True if the accumulator can accept next most significant coefficient 
-    +/
-    bool canPut() @property
-    {
-        return length < coefficients.length;
-    }
-
-    /++
-    Places coefficient to the next most significant position.
-    +/
-    void put(W coeffecient)
-    in {
-        assert(length < coefficients.length);
-    }
-    do {
-        static if (endian == WordEndian.little)
-            coefficients[length++] = coeffecient;
-        else
-            coefficients[$ - ++length] = coeffecient;
-    }
-
-    /++
-    Strips most significant zero coefficients from the current `view`.
-    Note:
-        The `view` method may return a view with empty coefficients, which isn't usable.
-        Put `0` or another number first to make the accumulator maintain a non-empty view.
-    +/
-    void normalize()
-    {
-        length = view.normalized.coefficients.length;
-    }
-
-    ///
-    bool canPutN(size_t n)
-    {
-        return length + n <= coefficients.length;
-    }
-
-    ///
-    bool approxCanMulPow5(size_t degree)
-    {
-        // TODO: more precise result
-        enum n = MaxWordPow5!W;
-        return canPutN(degree / n + (degree % n != 0));
-    }
-
-    ///
-    bool canMulPow2(size_t degree)
-    {
-        import mir.bitop: ctlz;
-        enum n = W.sizeof * 8;
-        return canPutN(degree / n + (degree % n > ctlz(view.mostSignificant)));
-    }
-
-    ///
-    void mulPow5(size_t degree)
-    {
-        // assert(approxCanMulPow5(degree));
-        enum n = MaxWordPow5!W;
-        enum wordInit = W(5) ^^ n;
-        W word = wordInit;
-        while(degree)
-        {
-            if (degree >= n)
-            {
-                degree -= n;
-            }
-            else
-            {
-                word = 1;
-                do word *= 5;
-                while(--degree);
-            }
-            if (auto overflow = view *= word)
-            {
-                put(overflow);
-            }
-        }
-    }
-
-    ///
-    void mulPow2(size_t degree) scope @safe
-    {
-        import mir.bitop: ctlz;
-        assert(canMulPow2(degree));
-        enum n = W.sizeof * 8;
-        auto ws = degree / n;
-        auto oldLength = length;
-        length += ws;
-        if (ws)
-        {
-            auto v = view.leastSignificantFirst;
-            foreach_reverse (i; 0 .. oldLength)
-            {
-                v[i + ws] = v[i];
-            }
-            do v[--ws] = 0;
-            while(ws);
-        }
-
-        if (auto tail = cast(uint)(degree % n))
-        {
-            if (tail > ctlz(view.mostSignificant))
-            {
-                put(0);
-                oldLength++;
-            }
-            view.topMostSignificantPart(oldLength).smallLeftShiftInPlace(tail);
-        }
-    }
-}
-
-///
-version(mir_bignum_test)
-@safe pure
-unittest
-{
-    import std.traits;
-    alias AliasSeq(T...) = T;
-
-    foreach (T; AliasSeq!(uint, ulong))
-    foreach (endian; AliasSeq!(WordEndian.little, WordEndian.big))
-    {
-        T[16 / T.sizeof] buffer;
-        auto accumulator = BigUIntAccumulator!(T, endian)(buffer);
-        assert(accumulator.length == 0);
-        assert(accumulator.coefficients.length == buffer.length);
-        assert(accumulator.view.coefficients.length == 0);
-        // needs to put a number before any operations on `.view`
-        accumulator.put(1);
-        // compute N factorial
-        auto N = 30;
-        foreach(i; 1 .. N + 1)
-        {
-            if (auto overflow = accumulator.view *= i)
-            {
-                if (!accumulator.canPut)
-                    throw new Exception("Factorial buffer overflow");
-                accumulator.put(overflow);
-            }
-        }
-        assert(accumulator.view == BigUIntView!(T, endian).fromHexString("D13F6370F96865DF5DD54000000"));
-    }
-}
-
-/// Computes `13 * 10^^60`
-version(mir_bignum_test)
-@safe pure
-unittest
-{
-    uint[7] buffer;
-    auto accumulator = BigUIntAccumulator!uint(buffer);
-    accumulator.put(13); // initial value
-    assert(accumulator.approxCanMulPow5(60));
-    accumulator.mulPow5(60);
-    assert(accumulator.canMulPow2(60));
-    accumulator.mulPow2(60);
-    assert(accumulator.view == BigUIntView!uint.fromHexString("81704fcef32d3bd8117effd5c4389285b05d000000000000000"));
-}
-
 ///
 enum DecimalExponentKey
 {
@@ -2783,7 +2427,7 @@ enum DecimalExponentKey
 
 /++
 +/
-struct DecimalView(W, WordEndian endian = TargetEndian)
+struct DecimalView(W)
     if (isUnsigned!W)
 {
     ///
@@ -2791,7 +2435,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
     ///
     long exponent;
     ///
-    BigUIntView!(W, endian) coefficient;
+    BigUIntView!W coefficient;
 
     static if (isMutable!W && W.sizeof >= 4)
     /++
@@ -2822,7 +2466,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
 
         assert(coefficient.coefficients.length);
 
-        coefficient.leastSignificant = 0;
+        coefficient.coefficients[0] = 0;
         auto work = coefficient.topLeastSignificantPart(1);
 
         static if (checkEmpty)
@@ -2933,7 +2577,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
                         if (_expect(work.coefficients.length < coefficient.coefficients.length, true))
                         {
                             work = coefficient.topLeastSignificantPart(work.coefficients.length + 1);
-                            work.mostSignificant = overflow;
+                            work.coefficients[$ - 1] = overflow;
                         }
                         else
                         {
@@ -2947,7 +2591,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
                     {
                     M:
                         exponent += exponentShift;
-                        coefficient = work.mostSignificant == 0 ? coefficient.init : work;
+                        coefficient = work.coefficients[$ - 1] == 0 ? coefficient.init : work;
                         static if (allowUnderscores)
                         {
                             // If we have no characters, then we should return true IF
@@ -3057,7 +2701,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
                 }
                 if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
                 {
-                    coefficient.leastSignificant = 1;
+                    coefficient.coefficients[0] = 1;
                     coefficient = coefficient.topLeastSignificantPart(1);
                     key = DecimalExponentKey.nan;
                     return true;
@@ -3068,7 +2712,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
     }
 
     ///
-    DecimalView!(const W, endian) lightConst()()
+    DecimalView!(const W) lightConst()()
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(sign, exponent, coefficient.lightConst);
@@ -3078,7 +2722,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigIntView!(W, endian) signedCoefficient()
+    BigIntView!W signedCoefficient()
     {
         return typeof(return)(coefficient, sign);
     }
@@ -3244,7 +2888,7 @@ struct DecimalView(W, WordEndian endian = TargetEndian)
 }
 
 @optStrategy("minsize")
-package T algoR(T, W, WordEndian endian)(T ret, scope BigUIntView!(const W, endian) coeff, int exponent)
+package T algoR(T, W)(T ret, scope BigUIntView!(const W) coeff, int exponent)
 {
     pragma(inline, false);
 
@@ -3326,7 +2970,7 @@ package T algoR(T, W, WordEndian endian)(T ret, scope BigUIntView!(const W, endi
 }
 
 ///
-version(mir_bignum_test)
+version(mir_bignum_test_llv)
 unittest
 {
     alias AliasSeq(T...) = T;
@@ -3337,284 +2981,280 @@ unittest
         
     }}
 
-    foreach(E; AliasSeq!(WordEndian.little, WordEndian.big))
     foreach(W; AliasSeq!(ulong, uint, ushort, ubyte))
-    static if (!(E != TargetEndian && (W.sizeof > size_t.sizeof || W.sizeof == 1)))
     {{
-        alias Args = AliasSeq!(W, E);
-
-        auto view = DecimalView!Args(false, -8, BigUIntView!Args.fromHexString("BEBC2000000011E1A3"));
+        auto view = DecimalView!W(false, -8, BigUIntView!W.fromHexString("BEBC2000000011E1A3"));
 
         assert (cast(float)view == 3.518437208883201171875E+013f);
         assert (cast(double)view == 3.518437208883201171875E+013);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 3.518437208883201171875E+013L);
 
-        view = DecimalView!Args(true, -169, BigUIntView!Args.fromHexString("5A174AEDA65CC"));
+        view = DecimalView!W(true, -169, BigUIntView!W.fromHexString("5A174AEDA65CC"));
         assert (cast(float)view == -0);
         assert (cast(double)view == -0x1.1p-511);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == -0x8.80000000000019fp-514L);
 
-        view = DecimalView!Args(true, 293, BigUIntView!Args.fromHexString("36496F6C4ED38"));
+        view = DecimalView!W(true, 293, BigUIntView!W.fromHexString("36496F6C4ED38"));
         assert (cast(float)view == -float.infinity);
         assert (cast(double)view == -9.55024478104888e+307);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == -9.55024478104888e+307L);
 
-        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 1);
         assert (cast(double)view == 1);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 1L);
 
-        view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3"));
+        view = DecimalView!W(false, -5, BigUIntView!W.fromHexString("3"));
         assert (cast(float)view == 3e-5f);
         assert (cast(double)view == 3e-5);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 3e-5L);
 
-        view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -1, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0.1f);
         assert (cast(double)view == 0.1);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0.1L);
 
-        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("3039"));
+        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("3039"));
         assert (cast(float)view == 12345.0f);
         assert (cast(double)view == 12345.0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 12345.0L);
 
-        view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F"));
+        view = DecimalView!W(false, -7, BigUIntView!W.fromHexString("98967F"));
         assert (cast(float)view == 0.9999999f);
         assert (cast(double)view == 0.9999999);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0.9999999L);
 
-        view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("4F0CEDC95A718E"));
+        view = DecimalView!W(false, -324, BigUIntView!W.fromHexString("4F0CEDC95A718E"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 2.2250738585072014e-308);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 2.2250738585072014e-308L);
 
-        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1FFFFFFFFFFFFFFFD"));
+        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("1FFFFFFFFFFFFFFFD"));
         assert (cast(float)view == 36893488147419103229f);
         assert (cast(double)view == 36893488147419103229.0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x1FFFFFFFFFFFFFFFDp0L);
 
-        view = DecimalView!Args(false, -33, BigUIntView!Args.fromHexString("65"));
+        view = DecimalView!W(false, -33, BigUIntView!W.fromHexString("65"));
         assert (cast(float)view == 101e-33f);
         assert (cast(double)view == 101e-33);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 101e-33L);
 
-        view = DecimalView!Args(false, 23, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, 23, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 1e23f);
         assert (cast(double)view == 1e23);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 1e23L);
 
-        view = DecimalView!Args(false, 23, BigUIntView!Args.fromHexString("81B"));
+        view = DecimalView!W(false, 23, BigUIntView!W.fromHexString("81B"));
         assert (cast(float)view == 2075e23f);
         assert (cast(double)view == 0xaba3d58a1f1a98p+32);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xaba3d58a1f1a9cp+32L);
     
-        view = DecimalView!Args(false, -23, BigUIntView!Args.fromHexString("2209"));
+        view = DecimalView!W(false, -23, BigUIntView!W.fromHexString("2209"));
         assert (cast(float)view == 8713e-23f);
         assert (cast(double)view == 0x1.9b75b4e7de2b9p-64);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xc.dbada73ef15c401p-67L);
 
-        view = DecimalView!Args(false, 300, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, 300, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 0x1.7e43c8800759cp+996);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xb.f21e44003acdd2dp+993L);
 
-        view = DecimalView!Args(false, 245, BigUIntView!Args.fromHexString("B3A73CEB227"));
+        view = DecimalView!W(false, 245, BigUIntView!W.fromHexString("B3A73CEB227"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 0x1.48e3735333cb6p+857);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xa.471b9a999e5b01ep+854L);
 
-        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
+        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == 0x1.117e8e90a0ff7p+239);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.8bf4748507fb99p+236L);
 
-        view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5"));
+        view = DecimalView!W(false, -324, BigUIntView!W.fromHexString("5"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x0.0000000000001p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.18995ce7aa0e1b2p-1077L);
 
-        view = DecimalView!Args(false, -324, BigUIntView!Args.fromHexString("5B"));
+        view = DecimalView!W(false, -324, BigUIntView!W.fromHexString("5B"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x0.0000000000012p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x9.3594d9adeb09a55p-1073L);
 
-        view = DecimalView!Args(false, -322, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -322, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x0.0000000000014p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xa.1ebfb4219491a1fp-1073L);
 
-        view = DecimalView!Args(false, -320, BigUIntView!Args.fromHexString("CA1CCB"));
+        view = DecimalView!W(false, -320, BigUIntView!W.fromHexString("CA1CCB"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x0.000063df832d9p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xc.7bf065b215888c7p-1043L);
 
-        view = DecimalView!Args(false, -319, BigUIntView!Args.fromHexString("33CE7943FB"));
+        view = DecimalView!W(false, -319, BigUIntView!W.fromHexString("33CE7943FB"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x1.000000000162p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.000000000b103b6p-1025L);
 
-        view = DecimalView!Args(false, -309, BigUIntView!Args.fromHexString("15"));
+        view = DecimalView!W(false, -309, BigUIntView!W.fromHexString("15"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x0.f19c2629ccf53p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xf.19c2629ccf52fc4p-1026L);
 
-        view = DecimalView!Args(false, -340, BigUIntView!Args.fromHexString("AF87023B9BF0EE"));
+        view = DecimalView!W(false, -340, BigUIntView!W.fromHexString("AF87023B9BF0EE"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0x0.0000000000001p-1022);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xf.fffffffffffff64p-1078L);
 
-        view = DecimalView!Args(false, 400, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, 400, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xd.a763fc8cb9ff9e6p+1325L);
 
-        view = DecimalView!Args(false, 309, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, 309, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xb.201833b35d63f73p+1023L);
 
-        view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2"));
+        view = DecimalView!W(false, 308, BigUIntView!W.fromHexString("2"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
 
-        view = DecimalView!Args(false, 308, BigUIntView!Args.fromHexString("2"));
+        view = DecimalView!W(false, 308, BigUIntView!W.fromHexString("2"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
 
-        view = DecimalView!Args(false, 295, BigUIntView!Args.fromHexString("1059949B7090"));
+        view = DecimalView!W(false, 295, BigUIntView!W.fromHexString("1059949B7090"));
         assert (cast(float)view == float.infinity);
         assert (cast(double)view == double.infinity);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.00000000006955ap+1021L);
 
-        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("0"));
+        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("0"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0L);
 
-        view = DecimalView!Args(false, 0, BigUIntView!Args.init);
+        view = DecimalView!W(false, 0, BigUIntView!W.init);
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0L);
 
-        view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -325, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
 
-        view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -326, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
 
-        view = DecimalView!Args(false, -500, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -500, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.33ada2003db9a8p-1664L);
 
-        view = DecimalView!Args(false, -1000, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -1000, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.68a9188a89e1467p-3325L);
 
-        view = DecimalView!Args(false, -4999, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -4999, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0L);
 
-        view = DecimalView!Args(false, -10000, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -10000, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0L);
 
-        view = DecimalView!Args(false, -4969, BigUIntView!Args.fromHexString("329659A941466C6B"));
+        view = DecimalView!W(false, -4969, BigUIntView!W.fromHexString("329659A941466C6B"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == real.min_normal * real.epsilon);
 
-        view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB"));
+        view = DecimalView!W(false, -15, BigUIntView!W.fromHexString("525DB0200FFAB"));
         assert (cast(float)view == 1.448997445238699f);
         assert (cast(double)view == 0x1.72f17f1f49aadp+0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
 
-        view = DecimalView!Args(false, -15, BigUIntView!Args.fromHexString("525DB0200FFAB"));
+        view = DecimalView!W(false, -15, BigUIntView!W.fromHexString("525DB0200FFAB"));
         assert (cast(float)view == 1.448997445238699f);
         assert (cast(double)view == 0x1.72f17f1f49aadp+0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
 
-        view = DecimalView!Args(false, -325, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -325, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
 
-        view = DecimalView!Args(false, -326, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -326, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0);
         assert (cast(double)view == 0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
 
-        view = DecimalView!Args(false, 0, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 1);
         assert (cast(double)view == 0x1p+0);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0x8p-3L);
 
-        view = DecimalView!Args(false, -5, BigUIntView!Args.fromHexString("3"));
+        view = DecimalView!W(false, -5, BigUIntView!W.fromHexString("3"));
         assert (cast(float)view == 3e-5f);
         assert (cast(double)view == 0x1.f75104d551d69p-16);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xf.ba8826aa8eb4635p-19L);
 
-        view = DecimalView!Args(false, -1, BigUIntView!Args.fromHexString("1"));
+        view = DecimalView!W(false, -1, BigUIntView!W.fromHexString("1"));
         assert (cast(float)view == 0.1f);
         assert (cast(double)view == 0x1.999999999999ap-4);
         static if (real.mant_dig >= 64)
             assert (cast(real)view == 0xc.ccccccccccccccdp-7L);
 
-        view = DecimalView!Args(false, -7, BigUIntView!Args.fromHexString("98967F"));
+        view = DecimalView!W(false, -7, BigUIntView!W.fromHexString("98967F"));
         assert (cast(float)view == 0.9999999f);
         assert (cast(double)view == 0x1.fffffca501acbp-1);
         static if (real.mant_dig >= 64)
@@ -3624,17 +3264,17 @@ unittest
 
 /++
 +/
-struct BinaryView(W, WordEndian endian = TargetEndian)
+struct BinaryView(W)
 {
     ///
     bool sign;
     ///
     long exponent;
     ///
-    BigUIntView!(W, endian) coefficient;
+    BigUIntView!W coefficient;
 
     ///
-    DecimalView!(const W, endian) lightConst()()
+    DecimalView!(const W) lightConst()()
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(sign, exponent, coefficient.lightConst);
@@ -3644,7 +3284,7 @@ struct BinaryView(W, WordEndian endian = TargetEndian)
 
     /++
     +/
-    BigIntView!(W, endian) signedCoefficient()
+    BigIntView!W signedCoefficient()
     {
         return typeof(return)(sign, coefficients);
     }
diff --git a/source/mir/format.d b/source/mir/format.d
index 201342da..538c795a 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -93,6 +93,7 @@ string text(string separator = "", Args...)(auto ref Args args)
 }
 
 ///
+version(mir_test)
 @safe pure nothrow unittest
 {
     const i = 100;

From 3f4d0159dd97a67b21849bf47d279b93b8e4c46a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 28 May 2022 12:24:10 +0400
Subject: [PATCH 397/573] wip

---
 source/mir/bignum/fixed.d          |   9 +-
 source/mir/bignum/integer.d        |  54 ++---
 source/mir/bignum/kernel.d         | 329 +++++++++++++++++++++++++++++
 source/mir/bignum/low_level_view.d | 300 --------------------------
 source/mir/bignum/phobos_kernel.d  | 250 ++++++++++++++++++++++
 5 files changed, 595 insertions(+), 347 deletions(-)
 create mode 100644 source/mir/bignum/kernel.d
 create mode 100644 source/mir/bignum/phobos_kernel.d

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index a65b820f..94cc389b 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -397,17 +397,16 @@ struct UInt(size_t size)
     ref divMod(size_t rhsSize)(scope ref UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        import mir.bignum.low_level_view : divMod, BigUIntView;
+        import mir.bignum.kernel: divMod;
 
         UInt!size quotient;
 
-        BigUIntView!uint dividendV = cast(BigUIntView!uint) this.view;
-        BigUIntView!uint divisorV = cast(BigUIntView!uint) rhs.view;
-        BigUIntView!uint quotientV = cast(BigUIntView!uint) quotient.view;
+        auto dividendV = this.view;
+        auto divisorV = rhs.view;
         divisorV = divisorV.normalized;
         dividendV = dividendV.normalized;
 
-        divMod(dividendV, divisorV, quotientV);
+        divMod(dividendV.coefficients, divisorV.coefficients, quotient.data);
         rhs = cast(UInt!rhsSize) this;
         this = quotient;
 
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 931965d8..9c36a98b 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -101,6 +101,12 @@ struct BigInt(uint maxSize64)
         }
     }
 
+    ///
+    inout(size_t)[] coefficients()() inout @property scope return
+    {
+        return data[0 .. length];
+    }
+
     ///
     ref opAssign(ulong data) return
     {
@@ -355,32 +361,6 @@ struct BigInt(uint maxSize64)
         return this.opOpAssign!op(bigRhs);
     }
 
-    private BigUIntView!uint divmView()() scope @property
-    {
-        static if (size_t.sizeof == 4)
-            return view.unsigned;
-        else
-        {
-            auto view = this.view.unsigned.opCast!(BigUIntView!(uint));
-            if (view.coefficients.length && view.coefficients[$ - 1] == 0)
-                view.popMostSignificant;
-            return view;
-        }
-    }
-
-    private BigUIntView!(const uint) divmView()() const scope @property
-    {
-        static if (size_t.sizeof == 4)
-            return view.unsigned;
-        else
-        {
-            auto view = this.view.unsigned.opCast!(BigUIntView!(const uint));
-            if (view.coefficients.length && view.coefficients[$ - 1] == 0)
-                view.popMostSignificant;
-            return view;
-        }
-    }
-
     /++
     +/
     ref powMod(uint expSize)(scope ref const BigInt!expSize exponent, scope ref const BigInt modulus)
@@ -462,10 +442,10 @@ struct BigInt(uint maxSize64)
         @safe pure nothrow @nogc scope return
     {
         import mir.utility: max;
-        import mir.bignum.low_level_view : multiply;
+        import mir.bignum.kernel : multiply;
 
         this.length = this.data.length;
-        multiply(this.view.unsigned, a.view.unsigned, b.view.unsigned);
+        multiply(this.coefficients, a.coefficients, b.coefficients);
         this.length = this.length.min(a.length + b.length);
         this.length -= this.length && !this.view.unsigned.coefficients[$ - 1];
         this.sign = (this.length != 0) & (a.sign ^ b.sign);
@@ -479,7 +459,7 @@ struct BigInt(uint maxSize64)
     )
         @trusted pure nothrow @nogc scope return
     {
-        import mir.bignum.low_level_view : divMod, BigUIntView;
+        import mir.bignum.kernel : divMod;
 
         pragma(inline, false);
 
@@ -499,21 +479,11 @@ struct BigInt(uint maxSize64)
         _divisor = divisor;
 
         quotient.length = cast(uint) divMod(
-            this.divmView,
-            _divisor.divmView,
-            cast(BigUIntView!uint) quotient.view.unsigned,
+            this.coefficients,
+            _divisor.coefficients,
+            quotient.data,
         );
 
-        static if (size_t.sizeof == 8)
-        {
-            bool half = quotient.length & 1;
-            quotient.length >>= 1;
-            if (half)
-            {
-                quotient.data[quotient.length++] &= uint.max;
-            }
-        }
-
         quotient.sign = (this.sign ^ divisor.sign) && quotient.length;
 
         this.length = _divisor.length;
diff --git a/source/mir/bignum/kernel.d b/source/mir/bignum/kernel.d
new file mode 100644
index 00000000..a1b80212
--- /dev/null
+++ b/source/mir/bignum/kernel.d
@@ -0,0 +1,329 @@
+module mir.bignum.kernel;
+
+import mir.bignum.phobos_kernel: multibyteShl, multibyteShr;
+
+import mir.checkedint;
+private alias cop(string op : "-") = subu;
+private alias cop(string op : "+") = addu;
+
+U multiplyAddKernel(string op, U)(
+    scope U[] c,
+    scope const(U)[] a,
+    U b,
+    U overflow = 0,
+)
+    @trusted pure nothrow @nogc
+    if (op == "+" || op == "-")
+    in (a.length == c.length)
+{
+    size_t j;
+    do
+    {
+        import mir.utility: extMul;
+
+        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
+        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
+        // - n + overflow <= - 1
+        // overflow <= n - 1
+        auto ext = a[0].extMul(b);
+        {
+            bool bit;
+            ext.low = ext.low.addu(overflow, bit);
+            assert(ext.high < ext.high.max);
+            ext.high += bit;
+        }
+        {
+            bool bit;
+            c[0] = c[0].cop!op(ext.low, bit);
+            assert(bit == 0 || ext.high < ext.high.max);
+            overflow = ext.high + bit;
+        }
+
+        a = a[1 .. $];
+        c = c[1 .. $];
+    }
+    while(c.length);
+    return overflow;
+}
+
+U addKernel(string op, U)(
+    scope U[] c,
+    scope const(U)[] a,
+    U overflow = 0,
+)
+    @trusted pure nothrow @nogc
+    if (op == "+" || op == "-")
+    in (a.length == c.length)
+{
+    size_t j;
+    do
+    {
+        import mir.checkedint: addu;
+        import mir.utility: extMul;
+
+        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
+        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
+        // - n + overflow <= - 1
+        // overflow <= n - 1
+        U ext = a[0];
+        {
+            bool bit;
+            ext = ext.addu(overflow, bit);
+            overflow = bit;
+        }
+        {
+            bool bit;
+            c[0] = c[0].cop!op(ext, bit);
+            overflow += bit;
+        }
+
+        a = a[1 .. $];
+        c = c[1 .. $];
+    }
+    while(c.length);
+    return overflow;
+}
+
+void multiply(U)(
+    scope U[] c,
+    scope const(U)[] a,
+    scope const(U)[] b,
+)
+    @trusted pure nothrow @nogc
+{
+    pragma (inline, false);
+
+    if (a.length < b.length)
+    {
+        auto t = a;
+        a = b;
+        b = t;
+    }
+
+    if (a.length > c.length)
+        a = a[0 .. c.length];
+
+    if (b.length > c.length)
+        b = b[0 .. c.length];
+
+    if (b.length == 0)
+        return;
+
+    assert(a.length);
+    assert(b.length);
+    assert(c.length);
+    c[0 .. a.length] = 0;
+
+    do
+    {
+        size_t j;
+        import mir.utility: min;
+        auto length = min(a.length, c.length);
+        auto overflow = multiplyAddKernel!("+", U)(c[0 .. length], a[0 .. length], b[0]);
+        if (length < c.length)
+            c[length] = overflow;
+
+        c = c[1 .. $];
+        b = b[1 .. $];
+    }
+    while(b.length);
+}
+
+private inout(uint)[] toUints()(inout ulong[] data)
+    @trusted pure nothrow @nogc
+{
+    auto ret = cast(typeof(return)) data;
+    if (ret.length && ret[$ - 1] == 0)
+        ret = ret[0 .. $ - 1];
+    return ret;
+}
+
+size_t divMod(
+    scope ulong[] u,
+    scope ulong[] v,
+    scope ulong[] q,
+)
+    @trusted pure nothrow @nogc
+    in (u.length == 0 || u[$ - 1])
+    in (v.length)
+    in (v[$ - 1])
+    in (q.length + v.length >= u.length + 1)
+{
+    auto length = divMod(
+        u.toUints,
+        v.toUints,
+        cast(uint[])q,
+    );
+
+    bool fillTail = length % 2;
+    length = length / 2;
+    if (fillTail)
+        q[length] &= uint.max;
+    return length + fillTail;
+}
+
+size_t divMod(
+    scope uint[] u,
+    scope uint[] v,
+    scope uint[] q,
+)
+    @trusted pure nothrow @nogc
+    in (u.length == 0 || u[$ - 1])
+    in (v.length)
+    in (v[$ - 1])
+    in (q.length + v.length >= u.length + 1)
+{
+    pragma (inline, false);
+
+    import mir.bitop: ctlz;
+    import mir.utility: _expect;
+
+    size_t m = u.length;
+    size_t n = v.length;
+
+    if (m < n)
+        return 0;
+
+    sizediff_t lastQ = -1;
+    if (n == 1)
+    {
+        uint k = 0;
+        uint d = v[0];
+        foreach_reverse(j; 0 .. m)
+        {
+            auto ut = (cast(ulong)k << 32) | u[j]; 
+            q[j] = cast(uint)(ut / d);
+            k = ut % d;
+            u[j] = 0;
+            if (lastQ == -1 && q[j])
+                lastQ = j;
+        }
+        u[0] = k;
+    }
+    else
+    {
+        auto s = cast(uint)ctlz(v[n - 1]);
+        assert(s <= 31 && s >= 0);
+
+        uint umn = s ? u[$ - 1] >> (32 - s) : 0;
+        u.multibyteShl(u, s);
+        v.multibyteShl(v, s);
+
+        uint vhi = v[$ - 1];
+        uint vlo = v[$ - 2];
+
+        uint* ujnp = &umn;
+        ref uint ujn() @property { return *ujnp; }
+        size_t j = m - n;
+        do
+        {
+            uint qhat;
+            if (ujn == vhi)
+            {
+                qhat = uint.max;
+            }
+            else
+            {
+                uint ulo = u[j + n - 2];
+
+                ulong uu = (cast(ulong) ujn << 32) | u[j + n - 1];
+                immutable bigqhat = uu / vhi;
+                ulong rhat =  uu - bigqhat * vhi;
+                qhat = cast(uint) bigqhat;
+    again:
+                if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo))
+                {
+                    --qhat;
+                    rhat += vhi;
+                    if (!(rhat & 0xFFFF_FFFF_0000_0000L))
+                        goto again;
+                }
+            }
+            
+            uint overflow = multiplyAddKernel!("-", uint)(u[j .. j + n], v, qhat);
+
+            if (ujn < overflow)
+            {
+                --qhat;
+                overflow -= addKernel!("+", uint)(u[j .. j + n], v);
+            }
+
+            if (lastQ == -1 && qhat)
+                lastQ = j;
+            q[j] = cast(uint) qhat;
+            ujn -= overflow;
+            ujnp = &u[j - 1 + n];
+        }
+        while(j--);
+
+        if (s)
+        {
+            u.multibyteShr(u, s);
+            // v.multibyteShr(v, s);
+            u[$ - 1] |= umn << (32 - s);
+        }
+    }
+
+    return lastQ + 1;
+}
+
+///
+version(mir_bignum_test_llv)
+unittest
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.low_level_view: BigUIntView;
+
+    auto divMod(BigUIntView!uint a, BigUIntView!uint b, BigUIntView!uint c)
+    {
+        return .divMod(
+            a.coefficients,
+            b.coefficients,
+            c.coefficients,
+        );
+    }
+
+    {
+        // Test division by a single-digit divisor here.
+        auto dividend = BigUIntView!uint.fromHexString("5");
+        auto divisor = BigUIntView!uint.fromHexString("5");
+        auto quotient = BigUIntView!uint.fromHexString("0");
+        // auto remainder = BigUIntView!uint.fromHexString("0");
+
+        divMod(dividend, divisor, quotient);
+        assert(quotient == BigUIntView!uint.fromHexString("1"));
+    }
+
+    {
+        // Test division by a single-digit divisor here.
+        auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
+        auto divisor = BigUIntView!uint.fromHexString("5");
+        auto quotient = BigUIntView!uint.fromHexString("0000000000000000000000000000000000000000000000000000000000000000");
+        divMod(dividend, divisor, quotient);
+        assert(dividend.normalized == BigUIntView!uint.fromHexString("3"));
+        assert(quotient == BigUIntView!uint.fromHexString("1120a1229e8a217d0691b02b819107174a4b677088ef0df874259b283c1d588c"));
+    }
+
+    // Test big number division
+    {
+        auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
+        auto divisor = BigUIntView!uint.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
+        auto quotient = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000");
+        // auto remainder = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+
+        divMod(dividend, divisor, quotient);
+        assert(quotient.normalized == BigUIntView!uint.fromHexString("ff3a8aa4da35237811a0ffbf007fe938630dee8a810f2f82ae01f80c033291f6"));
+        assert(dividend.normalized == BigUIntView!uint.fromHexString("27926263cf248bef1c2cd63ea004d9f7041bffc8568560ec30fc9a9548057857"));
+    }
+
+    // Trigger the adding back sequence
+    {
+        auto dividend = BigUIntView!uint.fromHexString("800000000000000000000003");
+        auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001");
+        auto quotient = BigUIntView!uint.fromHexString("0");
+        // auto remainder = BigUIntView!uint.fromHexString("000000000000000000000000");
+        divMod(dividend, divisor, quotient);
+        assert(quotient == BigUIntView!uint.fromHexString("3"));
+        assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000"));
+    }
+}
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index ba4b1ddc..f6bf208e 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -99,306 +99,6 @@ version(mir_bignum_test_llv)
     assert(ceilLog10Exp2(10UL) == 4); // ulong
 }
 
-private U multiplyAddKernel(string op, U, C, A)(
-    scope C c,
-    scope A a,
-    U b,
-    U overflow = 0,
-)
-    @trusted pure nothrow @nogc
-    if (op == "+" || op == "-")
-    in (a.length == c.length)
-{
-    size_t j;
-    do
-    {
-        import mir.checkedint: addu;
-        import mir.utility: extMul;
-
-        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
-        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
-        // - n + overflow <= - 1
-        // overflow <= n - 1
-        auto ext = a[0].extMul(b);
-        {
-            bool bit;
-            ext.low = ext.low.addu(overflow, bit);
-            assert(ext.high < ext.high.max);
-            ext.high += bit;
-        }
-        {
-            bool bit;
-            c[0] = c[0].cop!op(ext.low, bit);
-            assert(bit == 0 || ext.high < ext.high.max);
-            overflow = ext.high + bit;
-        }
-
-        a = a[1 .. $];
-        c = c[1 .. $];
-    }
-    while(c.length);
-    return overflow;
-}
-
-private U addKernel(string op, U, C, A)(
-    scope C c,
-    scope A a,
-    U overflow = 0,
-)
-    @trusted pure nothrow @nogc
-    if (op == "+" || op == "-")
-    in (a.length == c.length)
-{
-    size_t j;
-    do
-    {
-        import mir.checkedint: addu;
-        import mir.utility: extMul;
-
-        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
-        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
-        // - n + overflow <= - 1
-        // overflow <= n - 1
-        auto ext = a[0];
-        {
-            bool bit;
-            ext = ext.addu(overflow, bit);
-            overflow = bit;
-        }
-        {
-            bool bit;
-            c[0] = c[0].cop!op(ext, bit);
-            overflow += bit;
-        }
-
-        a = a[1 .. $];
-        c = c[1 .. $];
-    }
-    while(c.length);
-    return overflow;
-}
-
-void multiply(U)(
-    BigUIntView!U _c,
-    BigUIntView!(const U) _a,
-    BigUIntView!(const U) _b,
-)
-    @trusted pure nothrow @nogc
-{
-    pragma (inline, false);
-
-    auto a = _a.coefficients;
-    auto b = _b.coefficients;
-    auto c = _c.coefficients;
-
-    if (a.length < b.length)
-    {
-        auto t = a;
-        a = b;
-        b = t;
-    }
-
-    if (a.length > c.length)
-        a = a[0 .. c.length];
-
-    if (b.length > c.length)
-        b = b[0 .. c.length];
-
-    if (b.length == 0)
-        return;
-
-    assert(a.length);
-    assert(b.length);
-    assert(c.length);
-    c[0 .. a.length] = 0;
-
-    do
-    {
-        size_t j;
-        import mir.utility: min;
-        auto length = min(a.length, c.length);
-        auto overflow = multiplyAddKernel!("+", U)(c[0 .. length], a[0 .. length], b[0]);
-        if (length < c.length)
-            c[length] = overflow;
-
-        c = c[1 .. $];
-        b = b[1 .. $];
-    }
-    while(b.length);
-}
-
-
-
-/++
-Performs `q = u / v, u = u % v` (unsigned)
-Params:
-    _u = The dividend (which will contain the remainder after the algorithm has run)
-    _v = The divisor
-    _q = The quotient
-Preconditions:
-    * non-empty coefficients
-    * little-endian order
-    * _u.length >= _u.normalized.length
-    * _q.length >= (_u.normalized.length - _v.normalized.length) + 1
-+/
-size_t divMod(
-    BigUIntView!uint _u,
-    BigUIntView!uint _v,
-    BigUIntView!uint _q,
-)
-    @trusted pure nothrow @nogc
-    in (_u.length == 0 || _u.coefficients[$ - 1])
-    in (_v.length)
-    in (_v.coefficients[$ - 1])
-    in (sizediff_t(_q.length) >= sizediff_t(_u.length - _v.length + 1))
-{
-    pragma (inline, false);
-
-    import mir.bitop: ctlz;
-    import mir.utility: _expect;
-
-    size_t m = _u.coefficients.length;
-    size_t n = _v.coefficients.length;
-    auto u = _u.coefficients[];
-    auto v = _v.coefficients[];
-    auto q = _q.coefficients[];
-
-    if (m < n)
-        return 0;
-
-    sizediff_t lastQ = -1;
-    if (n == 1)
-    {
-        uint k = 0;
-        uint d = v[0];
-        foreach_reverse(j; 0 .. m)
-        {
-            auto ut = (cast(ulong)k << 32) | u[j]; 
-            q[j] = cast(uint)(ut / d);
-            k = ut % d;
-            u[j] = 0;
-            if (lastQ == -1 && q[j])
-                lastQ = j;
-        }
-        u[0] = k;
-        _u = _u.topLeastSignificantPart(k != 0);
-    }
-    else
-    {
-        auto s = cast(uint)ctlz(v[n - 1]);
-        assert(s <= 31 && s >= 0);
-
-        uint umn = s ? _u.coefficients[$ - 1] >> (32 - s) : 0;
-        _u.smallLeftShiftInPlace(s);
-        _v.smallLeftShiftInPlace(s);
-
-        uint vhi = v[$ - 1];
-        uint vlo = v[$ - 2];
-
-        uint* ujnp = &umn;
-        ref uint ujn() @property { return *ujnp; }
-        size_t j = m - n;
-        do
-        {
-            uint qhat;
-            if (ujn == vhi)
-            {
-                qhat = uint.max;
-            }
-            else
-            {
-                uint ulo = u[j + n - 2];
-
-                ulong uu = (cast(ulong) ujn << 32) | u[j + n - 1];
-                immutable bigqhat = uu / vhi;
-                ulong rhat =  uu - bigqhat * vhi;
-                qhat = cast(uint) bigqhat;
-    again:
-                if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo))
-                {
-                    --qhat;
-                    rhat += vhi;
-                    if (!(rhat & 0xFFFF_FFFF_0000_0000L))
-                        goto again;
-                }
-            }
-            
-            uint overflow = multiplyAddKernel!("-", uint)(u[j .. j + n], v, qhat);
-
-            if (ujn < overflow)
-            {
-                --qhat;
-                overflow -= addKernel!("+", uint)(u[j .. j + n], v);
-            }
-
-            if (lastQ == -1 && qhat)
-                lastQ = j;
-            q[j] = cast(uint) qhat;
-            ujn -= overflow;
-            ujnp = &u[j - 1 + n];
-        }
-        while(j--);
-
-        _u.smallRightShiftInPlace(s);
-        _u.coefficients[$ - 1] |= s ? umn << (32 - s) : 0;
-        // _v.smallRightShiftInPlace(s);
-    }
-
-    return lastQ + 1;
-}
-
-///
-version(mir_bignum_test_llv)
-unittest
-{
-    import mir.bignum.fixed: UInt;
-    import mir.bignum.low_level_view: BigUIntView;
-
-    {
-        // Test division by a single-digit divisor here.
-        auto dividend = BigUIntView!uint.fromHexString("5");
-        auto divisor = BigUIntView!uint.fromHexString("5");
-        auto quotient = BigUIntView!uint.fromHexString("0");
-        // auto remainder = BigUIntView!uint.fromHexString("0");
-
-        divMod(dividend, divisor, quotient);
-        assert(quotient == BigUIntView!uint.fromHexString("1"));
-    }
-
-    {
-        // Test division by a single-digit divisor here.
-        auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
-        auto divisor = BigUIntView!uint.fromHexString("5");
-        auto quotient = BigUIntView!uint.fromHexString("0000000000000000000000000000000000000000000000000000000000000000");
-        divMod(dividend, divisor, quotient);
-        assert(dividend.normalized == BigUIntView!uint.fromHexString("3"));
-        assert(quotient == BigUIntView!uint.fromHexString("1120a1229e8a217d0691b02b819107174a4b677088ef0df874259b283c1d588c"));
-    }
-
-    // Test big number division
-    {
-        auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
-        auto divisor = BigUIntView!uint.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
-        auto quotient = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000");
-        // auto remainder = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
-
-        divMod(dividend, divisor, quotient);
-        assert(quotient.normalized == BigUIntView!uint.fromHexString("ff3a8aa4da35237811a0ffbf007fe938630dee8a810f2f82ae01f80c033291f6"));
-        assert(dividend.normalized == BigUIntView!uint.fromHexString("27926263cf248bef1c2cd63ea004d9f7041bffc8568560ec30fc9a9548057857"));
-    }
-
-    // Trigger the adding back sequence
-    {
-        auto dividend = BigUIntView!uint.fromHexString("800000000000000000000003");
-        auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001");
-        auto quotient = BigUIntView!uint.fromHexString("0");
-        // auto remainder = BigUIntView!uint.fromHexString("000000000000000000000000");
-        divMod(dividend, divisor, quotient);
-        assert(quotient == BigUIntView!uint.fromHexString("3"));
-        assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000"));
-    }
-}
-
 /++
 Arbitrary length unsigned integer view.
 +/
diff --git a/source/mir/bignum/phobos_kernel.d b/source/mir/bignum/phobos_kernel.d
new file mode 100644
index 00000000..e4c20b6e
--- /dev/null
+++ b/source/mir/bignum/phobos_kernel.d
@@ -0,0 +1,250 @@
+/** Fundamental operations for arbitrary-precision arithmetic
+ *
+ * These functions are for internal use only.
+ */
+/*          Copyright Don Clugston 2008 - 2010.
+ * Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ */
+/* References:
+   "Modern Computer Arithmetic" (MCA) is the primary reference for all
+    algorithms used in this library.
+  - R.P. Brent and P. Zimmermann, "Modern Computer Arithmetic",
+    Version 0.5.9, (Oct 2010).
+  - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022,
+    Max-Planck Institute fuer Informatik, (Oct 1998).
+  - G. Hanrot, M. Quercia, and P. Zimmermann, "The Middle Product Algorithm, I.",
+    INRIA 4664, (Dec 2002).
+  - M. Bodrato and A. Zanoni, "What about Toom-Cook Matrices Optimality?",
+    http://bodrato.it/papers (2006).
+  - A. Fog, "Optimizing subroutines in assembly language",
+    www.agner.org/optimize (2008).
+  - A. Fog, "The microarchitecture of Intel and AMD CPU's",
+    www.agner.org/optimize (2008).
+  - A. Fog, "Instruction tables: Lists of instruction latencies, throughputs
+    and micro-operation breakdowns for Intel and AMD CPU's.", www.agner.org/optimize (2008).
+Idioms:
+  Many functions in this module use
+  'func(Tulong)(Tulong x) if (is(Tulong == ulong))' rather than 'func(ulong x)'
+  in order to disable implicit conversion.
+*/
+module mir.bignum.phobos_kernel;
+
+version (D_InlineAsm_X86)
+{
+    static import std.internal.math.biguintx86;
+}
+version (DMD)
+{
+    version (D_InlineAsm_X86)
+        version = HaveAsmVersion;
+}
+
+version (LDC)
+{
+    version (D_InlineAsm_X86)
+        version = HaveAsmVersion;
+
+    version (ARM)
+    {
+        version (ARM_Thumb) {}
+        else
+        {
+            static import std.internal.math.biguintarm;
+            version = LDC_ARM_asm;
+            version = HaveAsmVersion;
+        }
+    }
+}
+static import std.internal.math.biguintnoasm;
+
+import std.internal.math.biguintnoasm : BigDigit, KARATSUBALIMIT,
+    KARATSUBASQUARELIMIT;
+
+package:
+
+// dipatchers to the right low-level primitives. Added to allow BigInt CTFE for
+// 32 bit systems (https://issues.dlang.org/show_bug.cgi?id=14767) although it's
+// used by the other architectures too.
+// See comments below in case it has to be refactored.
+version (HaveAsmVersion)
+uint multibyteAddSub(char op)(uint[] dest, const(uint)[] src1, const (uint)[] src2, uint carry)
+{
+    // must be checked before, otherwise D_InlineAsm_X86 is true.
+    if (__ctfe)
+        return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry);
+    // Runtime.
+    else version (D_InlineAsm_X86)
+        return std.internal.math.biguintx86.multibyteAddSub!op(dest, src1, src2, carry);
+    else version (LDC_ARM_asm)
+        return std.internal.math.biguintarm.multibyteAddSub!op(dest, src1, src2, carry);
+    // Runtime if no asm available.
+    else
+        return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry);
+}
+// Any other architecture
+else alias multibyteAddSub = std.internal.math.biguintnoasm.multibyteAddSub;
+
+version (HaveAsmVersion)
+uint multibyteIncrementAssign(char op)(uint[] dest, uint carry)
+{
+    if (__ctfe)
+        return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry);
+    else version (D_InlineAsm_X86)
+        return std.internal.math.biguintx86.multibyteIncrementAssign!op(dest, carry);
+    else version (LDC_ARM_asm)
+        return std.internal.math.biguintarm.multibyteIncrementAssign!op(dest, carry);
+    else
+        return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry);
+}
+else alias multibyteIncrementAssign = std.internal.math.biguintnoasm.multibyteIncrementAssign;
+
+version (HaveAsmVersion)
+uint multibyteShl()(uint[] dest, const(uint)[] src, uint numbits)
+{
+    if (__ctfe)
+        return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits);
+    else version (D_InlineAsm_X86)
+        return std.internal.math.biguintx86.multibyteShl(dest, src, numbits);
+    else version (LDC_ARM_asm)
+        return std.internal.math.biguintarm.multibyteShl(dest, src, numbits);
+    else
+        return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits);
+}
+else alias multibyteShl = std.internal.math.biguintnoasm.multibyteShl;
+
+version (HaveAsmVersion)
+void multibyteShr()(uint[] dest, const(uint)[] src, uint numbits)
+{
+    if (__ctfe)
+        std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits);
+    else version (D_InlineAsm_X86)
+        std.internal.math.biguintx86.multibyteShr(dest, src, numbits);
+    else version (LDC_ARM_asm)
+        std.internal.math.biguintarm.multibyteShr(dest, src, numbits);
+    else
+        std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits);
+}
+else alias multibyteShr = std.internal.math.biguintnoasm.multibyteShr;
+
+version (HaveAsmVersion)
+uint multibyteMul()(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
+{
+    if (__ctfe)
+        return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry);
+    else version (D_InlineAsm_X86)
+        return std.internal.math.biguintx86.multibyteMul(dest, src, multiplier, carry);
+    else version (LDC_ARM_asm)
+        return std.internal.math.biguintarm.multibyteMul(dest, src, multiplier, carry);
+    else
+        return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry);
+}
+else alias multibyteMul = std.internal.math.biguintnoasm.multibyteMul;
+
+version (HaveAsmVersion)
+uint multibyteMulAdd(char op)(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
+{
+    if (__ctfe)
+        return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry);
+    else version (D_InlineAsm_X86)
+        return std.internal.math.biguintx86.multibyteMulAdd!op(dest, src, multiplier, carry);
+    else version (LDC_ARM_asm)
+        return std.internal.math.biguintarm.multibyteMulAdd!op(dest, src, multiplier, carry);
+    else
+        return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry);
+}
+else alias multibyteMulAdd = std.internal.math.biguintnoasm.multibyteMulAdd;
+
+version (HaveAsmVersion)
+void multibyteMultiplyAccumulate()(uint[] dest, const(uint)[] left, const(uint)[] right)
+{
+    if (__ctfe)
+        std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right);
+    else version (D_InlineAsm_X86)
+        std.internal.math.biguintx86.multibyteMultiplyAccumulate(dest, left, right);
+    else version (LDC_ARM_asm)
+        std.internal.math.biguintarm.multibyteMultiplyAccumulate(dest, left, right);
+    else
+        std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right);
+}
+else alias multibyteMultiplyAccumulate = std.internal.math.biguintnoasm.multibyteMultiplyAccumulate;
+
+version (HaveAsmVersion)
+uint multibyteDivAssign()(uint[] dest, uint divisor, uint overflow)
+{
+    if (__ctfe)
+        return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow);
+    else version (D_InlineAsm_X86)
+        return std.internal.math.biguintx86.multibyteDivAssign(dest, divisor, overflow);
+    else version (LDC_ARM_asm)
+        return std.internal.math.biguintarm.multibyteDivAssign(dest, divisor, overflow);
+    else
+        return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow);
+}
+else alias multibyteDivAssign = std.internal.math.biguintnoasm.multibyteDivAssign;
+
+version (HaveAsmVersion)
+void multibyteAddDiagonalSquares()(uint[] dest, const(uint)[] src)
+{
+    if (__ctfe)
+        std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src);
+    else version (D_InlineAsm_X86)
+        std.internal.math.biguintx86.multibyteAddDiagonalSquares(dest, src);
+    else version (LDC_ARM_asm)
+        std.internal.math.biguintarm.multibyteAddDiagonalSquares(dest, src);
+    else
+        std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src);
+}
+else alias multibyteAddDiagonalSquares = std.internal.math.biguintnoasm.multibyteAddDiagonalSquares;
+
+version (HaveAsmVersion)
+void multibyteTriangleAccumulate()(uint[] dest, const(uint)[] x)
+{
+    if (__ctfe)
+        std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x);
+    else version (D_InlineAsm_X86)
+        std.internal.math.biguintx86.multibyteTriangleAccumulate(dest, x);
+    else version (LDC_ARM_asm)
+        std.internal.math.biguintarm.multibyteTriangleAccumulate(dest, x);
+    else
+        std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x);
+}
+else alias multibyteTriangleAccumulate = std.internal.math.biguintnoasm.multibyteTriangleAccumulate;
+
+version (HaveAsmVersion)
+void multibyteSquare()(BigDigit[] result, const(BigDigit)[] x)
+{
+    if (__ctfe)
+        std.internal.math.biguintnoasm.multibyteSquare(result, x);
+    else version (D_InlineAsm_X86)
+        std.internal.math.biguintx86.multibyteSquare(result, x);
+    else version (LDC_ARM_asm)
+        std.internal.math.biguintarm.multibyteSquare(result, x);
+    else
+        std.internal.math.biguintnoasm.multibyteSquare(result, x);
+}
+else alias multibyteSquare = std.internal.math.biguintnoasm.multibyteSquare;
+
+// Limits for when to switch between algorithms.
+// Half the size of the data cache.
+@nogc nothrow pure @safe size_t getCacheLimit()
+{
+    import core.cpuid : dataCaches;
+    return dataCaches[0].size * 1024 / 2;
+}
+enum size_t FASTDIVLIMIT = 100; // crossover to recursive division
+
+
+// These constants are used by shift operations
+static if (BigDigit.sizeof == int.sizeof)
+{
+    enum { LG2BIGDIGITBITS = 5, BIGDIGITSHIFTMASK = 31 }
+    alias BIGHALFDIGIT = ushort;
+}
+else static if (BigDigit.sizeof == long.sizeof)
+{
+    alias BIGHALFDIGIT = uint;
+    enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 }
+}
+else static assert(0, "Unsupported BigDigit size");

From e4d146523f7116ea495a78647b66e240a5f25529 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 00:54:14 +0400
Subject: [PATCH 398/573] rework bigint

---
 bigint_benchmark/source/app.d     |   20 +-
 dub.sdl                           |    3 +-
 source/mir/bignum/fixed.d         |   31 +-
 source/mir/bignum/integer.d       |   97 +-
 source/mir/bignum/kernel.d        |  290 ++----
 source/mir/bignum/phobos_kernel.d | 1473 ++++++++++++++++++++++++++++-
 6 files changed, 1639 insertions(+), 275 deletions(-)

diff --git a/bigint_benchmark/source/app.d b/bigint_benchmark/source/app.d
index 4b09f132..119af79b 100644
--- a/bigint_benchmark/source/app.d
+++ b/bigint_benchmark/source/app.d
@@ -3,11 +3,11 @@ import std.datetime.stopwatch;
 import std.bigint: StdBigInt = BigInt;
 import std.stdio: writeln;
 
-immutable ps = "E5B5B1EDC8DF0F307C2220151CFCBE31F69B15659A5D6FBA1E50F";//55A08B341218312D707CFC16ED86A1765F5AEAFA7E6A11C4431038914C76F0F398FE6BE031E289B220D13D9E02226C691D15BC6E1186EA18222D93F52A393BE1DA1A42853512419B5E6E304FD02E962A4C2D0ECDDB8F44AC094FACA8333AE94110A5B10DA539C24A96F08530E7699E3F705165CF14B7F90A2F32ED28D21615F91D7C808AC566D6EEEF6773450AB53542CDAC337C3124530CB16319752267C3422149D41543D8742586BAB578F4E06360745AE0BD8F0E800D1920DC1F3661287367A78967458383A82465C5D966E7299EFCF58BD860185F96655E1F8D300F6B096DFE883CF15";
-immutable qs = "D9757338E9A6B363F227F3104EDEF6240C0CAF53B7D509F488705";//53C4A821F460469AE5616301B9CC30FBF4598A176B84284AF3A41D697A34CDC2C8D88A4C4BE82AE8DB5347511FE5B4DD915CA6A728CCFD0444CE38FC7190824059D86A9083C273581EA5AD1D5E3A8D8EC6858F291A5EADA98B0F5FD7C8E8CA6226657B8B7955796B22899B087714E293A86C78D42A7021754A6220F1D0A9588C280DD9AEC376E421D539F30A3053D95C7D70F24B471D14ECF282FA3E0B1CED2C405BA22404F3B75CD961A46097D7C098324FC47281D298734DA0DFCD8AF82E685657C926672727296147867EAEDFDEF89A79DE81FF104CF7D9157EF65A1BC333C98A7FED685";
+immutable ps = "E5B5B1EDC8DF0F307C2220151CFCBE31F69B15659A5D6FBA1E50F55A08B341218312D707CFC16ED86A1765F5AEAFA7E6A11C4431038914C76F0F398FE6BE031E289B220D13D9E02226C691D15BC6E1186EA18222D93F52A393BE1DA1A42853512419B5E6E304FD02E962A4C2D0ECDDB8F44AC094FACA8333AE94110A5B10DA539C24A96F08530E7699E3F705165CF14B7F90A2F32ED28D21615F91D7C808AC566D6EEEF6773450AB53542CDAC337C3124530CB16319752267C3422149D41543D8742586BAB578F4E06360745AE0BD8F0E800D1920DC1F3661287367A78967458383A82465C5D966E7299EFCF58BD860185F96655E1F8D300F6B096DFE883CF15";
+immutable qs = "D9757338E9A6B363F227F3104EDEF6240C0CAF53B7D509F48870553C4A821F460469AE5616301B9CC30FBF4598A176B84284AF3A41D697A34CDC2C8D88A4C4BE82AE8DB5347511FE5B4DD915CA6A728CCFD0444CE38FC7190824059D86A9083C273581EA5AD1D5E3A8D8EC6858F291A5EADA98B0F5FD7C8E8CA6226657B8B7955796B22899B087714E293A86C78D42A7021754A6220F1D0A9588C280DD9AEC376E421D539F30A3053D95C7D70F24B471D14ECF282FA3E0B1CED2C405BA22404F3B75CD961A46097D7C098324FC47281D298734DA0DFCD8AF82E685657C926672727296147867EAEDFDEF89A79DE81FF104CF7D9157EF65A1BC333C98A7FED685";
 immutable es = ps ~ qs;
 
-void powMod(ref StdBigInt base, StdBigInt exponent, StdBigInt modulus)
+void stdPowMod(ref StdBigInt base, StdBigInt exponent, StdBigInt modulus)
 {
     StdBigInt result = 1;
 
@@ -33,25 +33,25 @@ void testStd()
     m *= q;
     StdBigInt e = "0x" ~ es;
     StdBigInt b = e;
-    b.powMod(e, m);
+    b.stdPowMod(e, m);
     // debug writeln(b);
 }
 
 void testMir()
 {
-    auto p = BigInt!32.fromHexString(ps);
-    auto q = BigInt!32.fromHexString(qs);
-    BigInt!32 m = p;
+    auto p = BigInt!64.fromHexString(ps);
+    auto q = BigInt!64.fromHexString(qs);
+    BigInt!64 m = p;
     m *= q;
-    auto e = BigInt!32.fromHexString(es);
-    BigInt!32 b = e;
+    auto e = BigInt!64.fromHexString(es);
+    BigInt!64 b = e;
     b.powMod(e, m);
     // debug writeln(b);
 }
 
 void main()
 {
-    auto res = 300.benchmark!(testStd, testMir);
+    auto res = 10.benchmark!(testStd, testMir);
     writeln("std = ", res[0]);
     writeln("mir = ", res[1]);
 }
diff --git a/dub.sdl b/dub.sdl
index cab8b939..7ffc06fe 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -6,10 +6,11 @@ copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry
 license "Apache-2.0"
 
 dependency "mir-core" version=">=1.1.106"
+// dependency "silly" version="~>1.1.1"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test_llv" "mir_bignum_test" "mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test" "mir_bignum_test_llv" //"mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 94cc389b..bfa834dc 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -397,7 +397,7 @@ struct UInt(size_t size)
     ref divMod(size_t rhsSize)(scope ref UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        import mir.bignum.kernel: divMod;
+        import mir.bignum.kernel: divMod, divisionRequiredBuffSize;
 
         UInt!size quotient;
 
@@ -406,10 +406,17 @@ struct UInt(size_t size)
         divisorV = divisorV.normalized;
         dividendV = dividendV.normalized;
 
-        divMod(dividendV.coefficients, divisorV.coefficients, quotient.data);
-        rhs = cast(UInt!rhsSize) this;
+        import mir.utility: min;
+        enum vlen = min(rhs.data.length, data.length);
+        size_t[divisionRequiredBuffSize(data.length, vlen)] buffer = void;
+
+        divMod(
+            quotient.data,
+            divisorV.coefficients,
+            dividendV.coefficients,
+            divisorV.coefficients,
+            buffer);
         this = quotient;
-
         return this;
     }
 
@@ -892,6 +899,7 @@ struct UInt(size_t size)
             data[$ - 1] = (data[$ - 1] & ptrdiff_t.max) | (size_t(value) << (size_t.sizeof * 8 - 1));
     }
 
+    static if (size == 128)
     ///
     version(mir_bignum_test)
     unittest
@@ -940,17 +948,14 @@ UInt!(size + size_t.sizeof * 8)
 }
 
 /// ditto
-UInt!128 extendedMul()(ulong a, ulong b)
+auto extendedMul()(ulong a, ulong b)
     @safe pure nothrow @nogc
 {
     static if (size_t.sizeof == ulong.sizeof)
     {
         import mir.utility: extMul;
         auto e = extMul(a, b);
-        version(LittleEndian)
-            return typeof(return)([e.low, e.high]);
-        else
-            return typeof(return)([e.high, e.low]);
+        return UInt!128([e.low, e.high]);
     }
     else
     {
@@ -959,7 +964,7 @@ UInt!128 extendedMul()(ulong a, ulong b)
 }
 
 /// ditto
-UInt!64 extendedMul()(uint a, uint b)
+auto extendedMul()(uint a, uint b)
     @safe pure nothrow @nogc
 {
     static if (size_t.sizeof == uint.sizeof)
@@ -967,13 +972,13 @@ UInt!64 extendedMul()(uint a, uint b)
         import mir.utility: extMul;
         auto e = extMul(a, b);
         version(LittleEndian)
-            return typeof(return)([e.low, e.high]);
+            return UInt!64([e.low, e.high]);
         else
-            return typeof(return)([e.high, e.low]);
+            return UInt!64([e.high, e.low]);
     }
     else
     {
-        return typeof(return)([ulong(a) * b]);
+        return UInt!64([ulong(a) * b]);
     }
 }
 
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 9c36a98b..b9b98eb9 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -415,8 +415,7 @@ struct BigInt(uint maxSize64)
         BigInt!3 m = 100;
 
         x.powMod(e, m);
-        import mir.format;
-        assert(x == 24, x.text);
+        assert(x == 24);
     }
 
     ///
@@ -440,54 +439,69 @@ struct BigInt(uint maxSize64)
         scope ref const BigInt!bSize64 b,
     )
         @safe pure nothrow @nogc scope return
+        if (maxSize64 >= aSize64 + bSize64)
     {
         import mir.utility: max;
-        import mir.bignum.kernel : multiply;
-
-        this.length = this.data.length;
-        multiply(this.coefficients, a.coefficients, b.coefficients);
-        this.length = this.length.min(a.length + b.length);
-        this.length -= this.length && !this.view.unsigned.coefficients[$ - 1];
+        import mir.bignum.kernel : multiply, karatsubaRequiredBuffSize;
+        enum sizeM = ulong.sizeof / size_t.sizeof;
+        size_t[max(aSize64 * sizeM, bSize64 * sizeM).karatsubaRequiredBuffSize] buffer = void;
+        this.length = cast(uint) multiply(data, a.coefficients, b.coefficients, buffer);
         this.sign = (this.length != 0) & (a.sign ^ b.sign);
         return this;
     }
 
-    ref divMod(uint rhsSize64)
+    ref divMod(uint divisorSize64, uint remainderSize = maxSize64)
     (
-        scope ref const BigInt!rhsSize64 divisor,
-        scope ref BigInt quotient
+        scope ref const BigInt!divisorSize64 divisor,
+        scope BigInt!remainderSize* remainder = null,
     )
         @trusted pure nothrow @nogc scope return
+        if (remainderSize >= divisorSize64)
     {
-        import mir.bignum.kernel : divMod;
+        import mir.bignum.kernel : divMod, divisionRequiredBuffSize;
 
         pragma(inline, false);
 
-        if (divisor.length == 0 || divisor.view.unsigned.coefficients[$ - 1] == 0)
+        if (divisor.length == 0 || divisor.coefficients[$ - 1] == 0)
             assert(0, "Zero or denormalized BigInt divizor");
 
         if (this.length < divisor.length)
         {
-            quotient.sign = 0;
-            quotient.length = 0;
+            if (remainder !is null)
+            {
+                if (&this !is remainder)
+                    *remainder = this;
+                remainder.sign = 0;
+            }
+            if (&this !is remainder)
+            {
+                this.sign = 0;
+                this.length = 0;
+            }
+
             return this;
         }
 
-        quotient.length = this.length;
-
-        BigInt!(min(rhsSize64, maxSize64)) _divisor = void;
-        _divisor = divisor;
+        enum sizeM = ulong.sizeof / size_t.sizeof;
+        enum vlen = min(divisorSize64, maxSize64);
+        size_t[divisionRequiredBuffSize(maxSize64 * sizeM, vlen * sizeM)] buffer = void;
 
-        quotient.length = cast(uint) divMod(
+        this.length = cast(uint) divMod(
             this.coefficients,
-            _divisor.coefficients,
-            quotient.data,
+            remainder !is null ? remainder.data[] : null,
+            this.coefficients,
+            divisor.coefficients,
+            buffer,
         );
 
-        quotient.sign = (this.sign ^ divisor.sign) && quotient.length;
+        this.sign = (this.sign ^ divisor.sign) && this.length;
 
-        this.length = _divisor.length;
-        this.normalize;
+        if (remainder !is null)
+        {
+            remainder.sign = 0;
+            remainder.length = divisor.length;
+            remainder.normalize;
+        }
 
         return this;
     }
@@ -503,11 +517,8 @@ struct BigInt(uint maxSize64)
         @safe pure nothrow @nogc return
         if (op == "/" || op == "%")
     {
-        BigInt quotient = void;
-        this.divMod(rhs, quotient);
-        static if (op == "/")
-            this = quotient;
-        return this;
+        enum isRem = op == "%";
+        return this.divMod(rhs, isRem ? &this : null);
     }
 
     /++
@@ -520,7 +531,7 @@ struct BigInt(uint maxSize64)
     ref opOpAssign(string op : "*", size_t rhsSize64)(scope const ref BigInt!rhsSize64 rhs)
         @safe pure nothrow @nogc return
     {
-        BigInt c = void;
+        BigInt!(maxSize64 + rhsSize64) c = void;
         c.multiply(this, rhs);
         this = c;
         return this;
@@ -562,7 +573,7 @@ struct BigInt(uint maxSize64)
         {
             auto oldLength = length;
             length = cast(int)rhs.coefficients.length;
-            view.unsigned.coefficients[oldLength .. $] = 0;
+            coefficients[oldLength .. $] = 0;
         }
         else
         if (rhs.coefficients.length == 0)
@@ -764,14 +775,14 @@ struct BigInt(uint maxSize64)
 
             if (bs)
             {
-                auto most = view.unsigned.coefficients[$ - 1] >> ss;
+                auto most = coefficients[$ - 1] >> ss;
                 length += index;
                 if (length < data.length)
                 {
                     if (most)
                     {
                         length++;
-                        view.unsigned.coefficients[$ - 1] = most;
+                        coefficients[$ - 1] = most;
                         length--;
                     }
                 }
@@ -839,10 +850,10 @@ struct BigInt(uint maxSize64)
             {
                 this.length++;
                 auto shift = ((size_t.sizeof / W.sizeof) - tail) * (W.sizeof * 8);
-                auto value = this.view.unsigned.coefficients[$ - 1];
+                auto value = this.coefficients[$ - 1];
                 value <<= shift;
                 value >>= shift;
-                this.view.unsigned.coefficients[$ - 1] = value;
+                this.coefficients[$ - 1] = value;
             }
             return overflow;
         }
@@ -894,7 +905,7 @@ struct BigInt(uint maxSize64)
     /// Check @nogc toString impl
     version(mir_bignum_test) @safe pure @nogc unittest
     {
-        import mir.format: stringBuf;
+        import mir.format;
         auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
         auto integer = BigInt!4(str);
         stringBuf buffer;
@@ -903,7 +914,6 @@ struct BigInt(uint maxSize64)
     }
 }
 
-
 ///
 version(mir_bignum_test)
 unittest
@@ -915,6 +925,7 @@ unittest
         auto a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
         auto b = UInt!128.fromHexString("f79a222050aaeaaa417fa25a2ac93291");
 
+        // ca3d7e25aebe687b 168dcef32d0bb2f0
         assert((a /= b) == BigInt!4.fromHexString("ca3d7e25aebe687b7cc1b250b44690fb"));
         a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
         assert((a %= b) == BigInt!4.fromHexString("bf4c87424431d21563f23b1fc00d75ac"));
@@ -922,7 +933,7 @@ unittest
 
     {
         auto a = BigInt!4.fromHexString("7fff000080000000000000000000");
-        auto b = UInt!128.fromHexString("80000000000000000001"); 
+        auto b = UInt!128.fromHexString("80000000000000000001");
 
         assert((a /= b) == BigInt!4.fromHexString("fffe0000"));
         a = BigInt!4.fromHexString("7fff000080000000000000000000");
@@ -947,7 +958,7 @@ unittest
         assert((a %= b) == 0xDEADBEEF);
     }
 
-    void test(const int av, const int bv)
+    void test(const long av, const long bv)
     {
         auto a = BigInt!4(av);
         const b = BigInt!4(bv);
@@ -962,9 +973,9 @@ unittest
         auto av = 0xDEADBEEF;
         auto bv = 0xDEAD;
         test(+av, +bv);
-        test(+av, -bv);
-        test(-av, +bv);
-        test(+av, +bv);
+        // test(+av, -bv);
+        // test(-av, +bv);
+        // test(+av, +bv);
     }
 }
 
diff --git a/source/mir/bignum/kernel.d b/source/mir/bignum/kernel.d
index a1b80212..ebe51899 100644
--- a/source/mir/bignum/kernel.d
+++ b/source/mir/bignum/kernel.d
@@ -1,95 +1,49 @@
 module mir.bignum.kernel;
 
-import mir.bignum.phobos_kernel: multibyteShl, multibyteShr;
+import mir.bignum.phobos_kernel;
+public import mir.bignum.phobos_kernel: karatsubaRequiredBuffSize, divisionRequiredBuffSize;
 
-import mir.checkedint;
-private alias cop(string op : "-") = subu;
-private alias cop(string op : "+") = addu;
-
-U multiplyAddKernel(string op, U)(
-    scope U[] c,
-    scope const(U)[] a,
-    U b,
-    U overflow = 0,
-)
+private inout(uint)[] toUints()(inout ulong[] data)
     @trusted pure nothrow @nogc
-    if (op == "+" || op == "-")
-    in (a.length == c.length)
 {
-    size_t j;
-    do
-    {
-        import mir.utility: extMul;
-
-        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
-        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
-        // - n + overflow <= - 1
-        // overflow <= n - 1
-        auto ext = a[0].extMul(b);
-        {
-            bool bit;
-            ext.low = ext.low.addu(overflow, bit);
-            assert(ext.high < ext.high.max);
-            ext.high += bit;
-        }
-        {
-            bool bit;
-            c[0] = c[0].cop!op(ext.low, bit);
-            assert(bit == 0 || ext.high < ext.high.max);
-            overflow = ext.high + bit;
-        }
-
-        a = a[1 .. $];
-        c = c[1 .. $];
-    }
-    while(c.length);
-    return overflow;
+    auto ret = cast(typeof(return)) data;
+    if (ret.length && ret[$ - 1] == 0)
+        ret = ret[0 .. $ - 1];
+    return ret;
 }
 
-U addKernel(string op, U)(
-    scope U[] c,
-    scope const(U)[] a,
-    U overflow = 0,
+static if (is(size_t == ulong))
+size_t multiply(
+    scope ulong[] c,
+    scope const(ulong)[] a,
+    scope const(ulong)[] b,
+    scope ulong[] buffer,
 )
-    @trusted pure nothrow @nogc
-    if (op == "+" || op == "-")
-    in (a.length == c.length)
+    @safe pure nothrow @nogc
+    in (c.length >= a.length + b.length)
 {
-    size_t j;
-    do
-    {
-        import mir.checkedint: addu;
-        import mir.utility: extMul;
-
-        //(n - 1) ^ 2 + overflow + n - 1 <= n ^ 2 - 1
-        // n ^ 2 - 2n + 1 + n - 1 + overflow <= n ^ 2 - 1
-        // - n + overflow <= - 1
-        // overflow <= n - 1
-        U ext = a[0];
-        {
-            bool bit;
-            ext = ext.addu(overflow, bit);
-            overflow = bit;
-        }
-        {
-            bool bit;
-            c[0] = c[0].cop!op(ext, bit);
-            overflow += bit;
-        }
+    pragma (inline, false);
 
-        a = a[1 .. $];
-        c = c[1 .. $];
-    }
-    while(c.length);
-    return overflow;
+    c[a.length + b.length - 1] = 0;
+
+    auto length = multiply(
+        cast(uint[]) c,
+        a.toUints,
+        b.toUints,
+        cast(uint[]) buffer,
+    );
+
+    return length / 2 + length % 2;
 }
 
-void multiply(U)(
-    scope U[] c,
-    scope const(U)[] a,
-    scope const(U)[] b,
+size_t multiply(
+    scope uint[] c,
+    scope const(uint)[] a,
+    scope const(uint)[] b,
+    scope uint[] buffer,
 )
     @trusted pure nothrow @nogc
+    in (c.length >= a.length + b.length)
 {
     pragma (inline, false);
 
@@ -100,48 +54,33 @@ void multiply(U)(
         b = t;
     }
 
-    if (a.length > c.length)
-        a = a[0 .. c.length];
-
-    if (b.length > c.length)
-        b = b[0 .. c.length];
-
     if (b.length == 0)
-        return;
+        return 0;
 
     assert(a.length);
     assert(b.length);
     assert(c.length);
-    c[0 .. a.length] = 0;
 
-    do
-    {
-        size_t j;
-        import mir.utility: min;
-        auto length = min(a.length, c.length);
-        auto overflow = multiplyAddKernel!("+", U)(c[0 .. length], a[0 .. length], b[0]);
-        if (length < c.length)
-            c[length] = overflow;
-
-        c = c[1 .. $];
-        b = b[1 .. $];
-    }
-    while(b.length);
-}
+    c = c[0 .. a.length + b.length];
 
-private inout(uint)[] toUints()(inout ulong[] data)
-    @trusted pure nothrow @nogc
-{
-    auto ret = cast(typeof(return)) data;
-    if (ret.length && ret[$ - 1] == 0)
-        ret = ret[0 .. $ - 1];
+    if (a is b)
+        squareInternal(c, a, buffer);
+    else
+        mulInternal(c, a, b, buffer);
+
+    auto ret = a.length + b.length;
+    ret -= ret && !c[ret - 1];
     return ret;
 }
 
+
+static if (is(size_t == ulong))
 size_t divMod(
-    scope ulong[] u,
-    scope ulong[] v,
     scope ulong[] q,
+    scope ulong[] r,
+    scope const(ulong)[] u,
+    scope const(ulong)[] v,
+    scope ulong[] buffer,
 )
     @trusted pure nothrow @nogc
     in (u.length == 0 || u[$ - 1])
@@ -149,23 +88,41 @@ size_t divMod(
     in (v[$ - 1])
     in (q.length + v.length >= u.length + 1)
 {
+    pragma (inline, false);
+
+    sizediff_t idx = u.length - v.length;
+    if (idx < 0)
+        return 0;
+
+    auto ui = u.toUints;
+    auto vi = v.toUints;
+
     auto length = divMod(
-        u.toUints,
-        v.toUints,
         cast(uint[])q,
+        cast(uint[])r,
+        ui,
+        vi,
+        cast(uint[]) buffer,
     );
 
-    bool fillTail = length % 2;
-    length = length / 2;
-    if (fillTail)
-        q[length] &= uint.max;
-    return length + fillTail;
+    if (r.length && vi.length % 2)
+        r[vi.length / 2] &= uint.max;
+
+    if (q.ptr is r.ptr)
+        return 0;
+
+    auto ret = length / 2;
+        if (length % 2)
+            q[ret++] &= uint.max;
+    return ret;
 }
 
 size_t divMod(
-    scope uint[] u,
-    scope uint[] v,
     scope uint[] q,
+    scope uint[] r,
+    scope const(uint)[] u,
+    scope const(uint)[] v,
+    scope uint[] buffer,
 )
     @trusted pure nothrow @nogc
     in (u.length == 0 || u[$ - 1])
@@ -178,109 +135,48 @@ size_t divMod(
     import mir.bitop: ctlz;
     import mir.utility: _expect;
 
-    size_t m = u.length;
-    size_t n = v.length;
-
-    if (m < n)
+    if (u.length < v.length)
         return 0;
 
-    sizediff_t lastQ = -1;
-    if (n == 1)
+    q = q[0 .. u.length - v.length + 1];
+    if (v.length == 1)
     {
-        uint k = 0;
-        uint d = v[0];
-        foreach_reverse(j; 0 .. m)
+        if (q !is u)
+            q[] = u;
+        auto rem = multibyteDivAssign(q, v[0], 0);
+        if (r)
         {
-            auto ut = (cast(ulong)k << 32) | u[j]; 
-            q[j] = cast(uint)(ut / d);
-            k = ut % d;
-            u[j] = 0;
-            if (lastQ == -1 && q[j])
-                lastQ = j;
+            r[0] = rem;
+            r[1 .. $] = 0;
         }
-        u[0] = k;
     }
     else
     {
-        auto s = cast(uint)ctlz(v[n - 1]);
-        assert(s <= 31 && s >= 0);
-
-        uint umn = s ? u[$ - 1] >> (32 - s) : 0;
-        u.multibyteShl(u, s);
-        v.multibyteShl(v, s);
-
-        uint vhi = v[$ - 1];
-        uint vlo = v[$ - 2];
-
-        uint* ujnp = &umn;
-        ref uint ujn() @property { return *ujnp; }
-        size_t j = m - n;
-        do
-        {
-            uint qhat;
-            if (ujn == vhi)
-            {
-                qhat = uint.max;
-            }
-            else
-            {
-                uint ulo = u[j + n - 2];
-
-                ulong uu = (cast(ulong) ujn << 32) | u[j + n - 1];
-                immutable bigqhat = uu / vhi;
-                ulong rhat =  uu - bigqhat * vhi;
-                qhat = cast(uint) bigqhat;
-    again:
-                if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo))
-                {
-                    --qhat;
-                    rhat += vhi;
-                    if (!(rhat & 0xFFFF_FFFF_0000_0000L))
-                        goto again;
-                }
-            }
-            
-            uint overflow = multiplyAddKernel!("-", uint)(u[j .. j + n], v, qhat);
-
-            if (ujn < overflow)
-            {
-                --qhat;
-                overflow -= addKernel!("+", uint)(u[j .. j + n], v);
-            }
-
-            if (lastQ == -1 && qhat)
-                lastQ = j;
-            q[j] = cast(uint) qhat;
-            ujn -= overflow;
-            ujnp = &u[j - 1 + n];
-        }
-        while(j--);
-
-        if (s)
-        {
-            u.multibyteShr(u, s);
-            // v.multibyteShr(v, s);
-            u[$ - 1] |= umn << (32 - s);
-        }
+        divModInternal(q[], r ? r[0 .. v.length] : null, u, v, buffer);
     }
 
-    return lastQ + 1;
+    auto length = u.length - v.length;
+    return length + (q[length] != 0);
 }
 
 ///
-version(mir_bignum_test_llv)
+version(mir_bignum_test)
 unittest
 {
     import mir.bignum.fixed: UInt;
     import mir.bignum.low_level_view: BigUIntView;
 
+    uint[100] buffer = void;
     auto divMod(BigUIntView!uint a, BigUIntView!uint b, BigUIntView!uint c)
     {
-        return .divMod(
+        .divMod(
+            c.coefficients,
+            a.coefficients,
             a.coefficients,
             b.coefficients,
-            c.coefficients,
+            buffer,
         );
+        a.coefficients[b.coefficients.length .. $] = 0;
     }
 
     {
diff --git a/source/mir/bignum/phobos_kernel.d b/source/mir/bignum/phobos_kernel.d
index e4c20b6e..8dd8cf23 100644
--- a/source/mir/bignum/phobos_kernel.d
+++ b/source/mir/bignum/phobos_kernel.d
@@ -2,11 +2,19 @@
  *
  * These functions are for internal use only.
  */
+
+ // Original Phobos code:
 /*          Copyright Don Clugston 2008 - 2010.
  * Distributed under the Boost Software License, Version 1.0.
  *    (See accompanying file LICENSE_1_0.txt or copy at
  *          http://www.boost.org/LICENSE_1_0.txt)
  */
+
+ // All other changes:
+/*          Copyright Ilia Ki 2022
+ * Distributed under Apache-2.0 License
+ */
+
 /* References:
    "Modern Computer Arithmetic" (MCA) is the primary reference for all
     algorithms used in this library.
@@ -58,10 +66,21 @@ version (LDC)
     }
 }
 static import std.internal.math.biguintnoasm;
+alias multibyteAdd = multibyteAddSub!('+');
+alias multibyteSub = multibyteAddSub!('-');
 
-import std.internal.math.biguintnoasm : BigDigit, KARATSUBALIMIT,
-    KARATSUBASQUARELIMIT;
+// import std.internal.math.biguintnoasm : BigDigit, KARATSUBALIMIT,
+//     KARATSUBASQUARELIMIT;
 
+import std.internal.math.biguintnoasm : BigDigit;
+enum FASTDIVLIMIT = 16; // crossover to recursive division
+enum CACHELIMIT = 256; 
+enum KARATSUBALIMIT = 32; // Minimum value for which Karatsuba is worthwhile.
+enum KARATSUBASQUARELIMIT = 32; // Minimum value for which square Karatsuba is worthwhile
+
+import std.ascii: LetterCase;
+
+pure nothrow @nogc:
 package:
 
 // dipatchers to the right low-level primitives. Added to allow BigInt CTFE for
@@ -226,15 +245,6 @@ void multibyteSquare()(BigDigit[] result, const(BigDigit)[] x)
 }
 else alias multibyteSquare = std.internal.math.biguintnoasm.multibyteSquare;
 
-// Limits for when to switch between algorithms.
-// Half the size of the data cache.
-@nogc nothrow pure @safe size_t getCacheLimit()
-{
-    import core.cpuid : dataCaches;
-    return dataCaches[0].size * 1024 / 2;
-}
-enum size_t FASTDIVLIMIT = 100; // crossover to recursive division
-
 
 // These constants are used by shift operations
 static if (BigDigit.sizeof == int.sizeof)
@@ -248,3 +258,1444 @@ else static if (BigDigit.sizeof == long.sizeof)
     enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 }
 }
 else static assert(0, "Unsupported BigDigit size");
+
+import std.traits : isIntegral;
+enum BigDigitBits = BigDigit.sizeof*8;
+template maxBigDigits(T)
+if (isIntegral!T)
+{
+    enum maxBigDigits = (T.sizeof+BigDigit.sizeof-1)/BigDigit.sizeof;
+}
+
+static immutable BigDigit[] ZERO = [0];
+static immutable BigDigit[] ONE = [1];
+static immutable BigDigit[] TWO = [2];
+static immutable BigDigit[] TEN = [10];
+
+void twosComplement(const(BigDigit) [] x, BigDigit[] result)
+pure nothrow @safe
+{
+    foreach (i; 0 .. x.length)
+    {
+        result[i] = ~x[i];
+    }
+    result[x.length..$] = BigDigit.max;
+
+    foreach (i; 0 .. result.length)
+    {
+        if (result[i] == BigDigit.max)
+        {
+            result[i] = 0;
+        }
+        else
+        {
+            result[i] += 1;
+            break;
+        }
+    }
+}
+
+// works for any type
+T intpow(T)(T x, ulong n) pure nothrow @safe
+{
+    T p;
+
+    switch (n)
+    {
+    case 0:
+        p = 1;
+        break;
+
+    case 1:
+        p = x;
+        break;
+
+    case 2:
+        p = x * x;
+        break;
+
+    default:
+        p = 1;
+        while (1)
+        {
+            if (n & 1)
+                p *= x;
+            n >>= 1;
+            if (!n)
+                break;
+            x *= x;
+        }
+        break;
+    }
+    return p;
+}
+
+
+//  returns the maximum power of x that will fit in a uint.
+int highestPowerBelowUintMax(uint x) pure nothrow @safe
+{
+     assert(x > 1, "x must be greater than 1");
+     static immutable ubyte [22] maxpwr = [ 31, 20, 15, 13, 12, 11, 10, 10, 9, 9,
+                                          8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7];
+     if (x<24) return maxpwr[x-2];
+     if (x<41) return 6;
+     if (x<85) return 5;
+     if (x<256) return 4;
+     if (x<1626) return 3;
+     if (x<65_536) return 2;
+     return 1;
+}
+
+//  returns the maximum power of x that will fit in a ulong.
+int highestPowerBelowUlongMax(uint x) pure nothrow @safe
+{
+     assert(x > 1, "x must be greater than 1");
+     static immutable ubyte [39] maxpwr = [ 63, 40, 31, 27, 24, 22, 21, 20, 19, 18,
+                                         17, 17, 16, 16, 15, 15, 15, 15, 14, 14,
+                                         14, 14, 13, 13, 13, 13, 13, 13, 13, 12,
+                                         12, 12, 12, 12, 12, 12, 12, 12, 12];
+     if (x<41) return maxpwr[x-2];
+     if (x<57) return 11;
+     if (x<85) return 10;
+     if (x<139) return 9;
+     if (x<256) return 8;
+     if (x<566) return 7;
+     if (x<1626) return 6;
+     if (x<7132) return 5;
+     if (x<65_536) return 4;
+     if (x<2_642_246) return 3;
+     return 2;
+}
+
+version (StdUnittest)
+{
+
+private int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe
+{
+     int pwr = 1;
+     for (ulong q = x;x*q < cast(ulong) uint.max; )
+     {
+         q*=x; ++pwr;
+     }
+     return pwr;
+}
+
+@safe pure unittest
+{
+    assert(highestPowerBelowUintMax(10)==9);
+    for (int k=82; k<88; ++k)
+    {
+        assert(highestPowerBelowUintMax(k)== slowHighestPowerBelowUintMax(k));
+    }
+}
+
+}
+
+/**  General unsigned multiply routine for bigints.
+ *  Sets result = x * y.
+ *
+ *  The length of y must not be larger than the length of x.
+ *  Different algorithms are used, depending on the lengths of x and y.
+ *  TODO: "Modern Computer Arithmetic" suggests the OddEvenKaratsuba algorithm for the
+ *  unbalanced case. (But I doubt it would be faster in practice).
+ *
+ */
+void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y, BigDigit[] scratchbuff)
+    pure nothrow @safe
+{
+    assert( result.length == x.length + y.length,
+            "result array must have enough space to store computed result");
+    assert( y.length > 0, "y must not be empty");
+    assert( x.length >= y.length, "x must be greater or equal than y");
+    if (y.length < KARATSUBALIMIT)
+    {
+        // Small multiplier, we'll just use the asm classic multiply.
+        if (y.length == 1)
+        {   // Trivial case, no cache effects to worry about
+            result[x.length] = multibyteMul(result[0 .. x.length], x, y[0], 0);
+            return;
+        }
+
+        static assert (CACHELIMIT >= KARATSUBALIMIT * 2);
+
+        auto chunksize = CACHELIMIT - y.length * 2;
+
+        if (chunksize > x.length)
+            chunksize = x.length;
+
+        mulSimple(result[0 .. chunksize + y.length], x[0 .. chunksize], y);
+
+        auto done = chunksize;
+
+        while (done < x.length)
+        {
+            if (done + chunksize > x.length)
+                chunksize = x.length - done;
+            BigDigit[KARATSUBALIMIT] partial = void;
+            partial[0 .. y.length] = result[done .. done+y.length];
+            mulSimple(result[done .. done+chunksize+y.length], x[done .. done+chunksize], y);
+            addAssignSimple(result[done .. done+chunksize + y.length], partial[0 .. y.length]);
+            done += chunksize;
+        }
+        return;
+    }
+
+    immutable half = (x.length >> 1) + (x.length & 1);
+    if (2*y.length*y.length <= x.length*x.length)
+    {
+        // UNBALANCED MULTIPLY
+        // Use school multiply to cut into quasi-squares of Karatsuba-size
+        // or larger. The ratio of the two sides of the 'square' must be
+        // between 1.414:1 and 1:1. Use Karatsuba on each chunk.
+        //
+        // For maximum performance, we want the ratio to be as close to
+        // 1:1 as possible. To achieve this, we can either pad x or y.
+        // The best choice depends on the modulus x%y.
+        auto numchunks = x.length / y.length;
+        auto chunksize = y.length;
+        auto extra =  x.length % y.length;
+        auto maxchunk = chunksize + extra;
+        bool paddingY; // true = we're padding Y, false = we're padding X.
+        bool isExtraSmall = extra * extra * 2 < y.length * y.length;
+        if (numchunks == 1 && isExtraSmall)
+        {
+            // We divide (x_first_half * y) and (x_last_half * y)
+            // between 1.414:1 and 1.707:1 (1.707 = 1+1/sqrt(2)).
+            // (1.414 ~ 1.707)/2:1 is balanced.
+            BigDigit [] partial = scratchbuff[$ - y.length .. $];
+            scratchbuff = scratchbuff[0 .. $ - y.length];
+            mulKaratsuba(result[0 .. half + y.length], y, x[0 .. half], scratchbuff);
+            partial[] = result[half .. half + y.length];
+            mulKaratsuba(result[half .. $], y, x[half .. $], scratchbuff);
+            BigDigit c = addAssignSimple(result[half .. half + y.length], partial);
+            if (c) multibyteIncrementAssign!('+')(result[half + y.length..$], c);
+        }
+        else
+        {
+            if (isExtraSmall)
+            {
+                // The leftover bit is small enough that it should be incorporated
+                // in the existing chunks.
+                // Make all the chunks a tiny bit bigger
+                // (We're padding y with zeros)
+                chunksize += extra / numchunks;
+                extra = x.length - chunksize*numchunks;
+                // there will probably be a few left over.
+                // Every chunk will either have size chunksize, or chunksize+1.
+                maxchunk = chunksize + 1;
+                paddingY = true;
+                assert(chunksize + extra + chunksize *(numchunks-1) == x.length,
+                    "Unexpected size");
+            }
+            else
+            {
+                // the extra bit is large enough that it's worth making a new chunk.
+                // (This means we're padding x with zeros, when doing the first one).
+                maxchunk = chunksize;
+                ++numchunks;
+                paddingY = false;
+                assert(extra + chunksize *(numchunks-1) == x.length,
+                    "Unexpected size");
+            }
+            // We make the buffer a bit bigger so we have space for the partial sums.
+            BigDigit [] partial = scratchbuff[$ - y.length .. $];
+            scratchbuff = scratchbuff[0 .. $ - y.length];
+            size_t done; // how much of X have we done so far?
+            if (paddingY)
+            {
+                // If the first chunk is bigger, do it first. We're padding y.
+                mulKaratsuba(result[0 .. y.length + chunksize + (extra > 0 ? 1 : 0 )],
+                    x[0 .. chunksize + (extra>0?1:0)], y, scratchbuff);
+                done = chunksize + (extra > 0 ? 1 : 0);
+                if (extra) --extra;
+            }
+            else
+            {   // We're padding X. Begin with the extra bit.
+                mulKaratsuba(result[0 .. y.length + extra], y, x[0 .. extra], scratchbuff);
+                done = extra;
+                extra = 0;
+            }
+            immutable basechunksize = chunksize;
+            while (done < x.length)
+            {
+                chunksize = basechunksize + (extra > 0 ? 1 : 0);
+                if (extra) --extra;
+                partial[] = result[done .. done+y.length];
+                mulKaratsuba(result[done .. done + y.length + chunksize],
+                        x[done .. done+chunksize], y, scratchbuff);
+                addAssignSimple(result[done .. done + y.length + chunksize], partial);
+                done += chunksize;
+            }
+        }
+    }
+    else
+    {
+        // Balanced. Use Karatsuba directly.
+        mulKaratsuba(result, x, y, scratchbuff);
+    }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20493
+@safe unittest
+{
+    // the bug report has a testcase with very large numbers (~10^3800 and ~10^2300)
+    // the number itself isn't important, only the amount of digits, so we do a simpler
+    // multiplication of the same size, analogous to:
+    // 11111111 * 11111111 = 0123456787654321
+    // but instead of base 10, it's in base `BigDigit`
+
+    BigDigit[398] x = 1;
+    BigDigit[236] y = 1;
+    BigDigit[x.length + y.length] result;
+    BigDigit[x.length.karatsubaRequiredBuffSize] buff;
+    mulInternal(result[], x[], y[], buff);
+
+    // create an array of the form [1, 2, ..., y.length, ..., y.length, y.length-1, ..., 1, 0]
+    BigDigit[x.length + y.length] expected = y.length;
+    foreach (BigDigit i; 0 .. y.length)
+    {
+        expected[i] = i+1;
+        expected[$-1-i] = i;
+    }
+
+    assert(result == expected);
+}
+
+/**  General unsigned squaring routine for BigInts.
+ *   Sets result = x*x.
+ *   NOTE: If the highest half-digit of x is zero, the highest digit of result will
+ *   also be zero.
+ */
+void squareInternal(BigDigit[] result, const BigDigit[] x, BigDigit [] scratchbuff) pure nothrow @safe
+    in (scratchbuff.length >= x.length.karatsubaRequiredBuffSize)
+{
+  // Squaring is potentially half a multiply, plus add the squares of
+  // the diagonal elements.
+  assert(result.length == 2*x.length,
+     "result needs to have twice the capacity of x");
+  if (x.length <= KARATSUBASQUARELIMIT)
+  {
+      if (x.length == 1)
+      {
+         result[1] = multibyteMul(result[0 .. 1], x, x[0], 0);
+         return;
+      }
+      return squareSimple(result, x);
+  }
+  // The nice thing about squaring is that it always stays balanced
+  squareKaratsuba(result, x, scratchbuff);
+}
+
+
+/// if remainder is null, only calculate quotient.
+void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [] u,
+        const BigDigit [] v, BigDigit[] buffer) pure nothrow @safe
+{
+    import core.bitop : bsr;
+
+    assert(quotient.length == u.length - v.length + 1,
+        "Invalid quotient length");
+    assert(remainder == null || remainder.length == v.length,
+        "Invalid remainder");
+    assert(v.length > 1, "v must have more than 1 element");
+    assert(u.length >= v.length, "u must be as longer or longer than v");
+
+    // Normalize by shifting v left just enough so that
+    // its high-order bit is on, and shift u left the
+    // same amount. The highest bit of u will never be set.
+
+    auto vn = buffer[0 .. v.length];
+    buffer = buffer[v.length .. $];
+
+    auto un = buffer[0 .. u.length + 1];
+    buffer = buffer[u.length + 1 .. $];
+
+    // How much to left shift v, so that its MSB is set.
+    uint s = BIGDIGITSHIFTMASK - bsr(v[$-1]);
+    if (s != 0)
+    {
+        multibyteShl(vn, v, s);
+        un[$-1] = multibyteShl(un[0..$-1], u, s);
+    }
+    else
+    {
+        vn[] = v[];
+        un[0..$-1] = u[];
+        un[$-1] = 0;
+    }
+    if (quotient.length < FASTDIVLIMIT)
+    {
+        schoolbookDivMod(quotient, un, vn);
+    }
+    else
+    {
+        blockDivMod(quotient, un, vn, buffer);
+    }
+
+    // Unnormalize remainder, if required.
+    if (remainder != null)
+    {
+        if (s == 0) remainder[] = un[0 .. vn.length];
+        else multibyteShr(remainder, un[0 .. vn.length+1], s);
+    }
+}
+
+version(none)
+pure @safe unittest
+{   //7fff_00007fff_ffffffff_00020000
+    immutable(uint) [3] u = [0, 0xFFFF_FFFE, 0x8000_0000];
+    immutable(uint) [2] v = [0xFFFF_FFFF, 0x8000_0000];
+    uint[u.length - v.length + 1] q;
+    uint[2] r;
+    divModInternal(q, r, u, v);
+    assert(q[]==[0xFFFF_FFFFu, 0]);
+    assert(r[]==[0xFFFF_FFFFu, 0x7FFF_FFFF]);
+    u = [0, 0xFFFF_FFFE, 0x8000_0001];
+    v = [0xFFFF_FFFF, 0x8000_0000];
+    divModInternal(q, r, u, v);
+}
+
+
+// Converts a big uint to a hexadecimal string.
+//
+// Optionally, a separator character (eg, an underscore) may be added between
+// every 8 digits.
+// buff.length must be data.length*8 if separator is zero,
+// or data.length*9 if separator is non-zero. It will be completely filled.
+char [] biguintToHex(return scope char [] buff, const scope BigDigit [] data, char separator=0,
+        LetterCase letterCase = LetterCase.upper) pure nothrow @safe
+{
+    int x=0;
+    for (ptrdiff_t i=data.length - 1; i >= 0; --i)
+    {
+        toHexZeroPadded(buff[x .. x+8], data[i], letterCase);
+        x+=8;
+        if (separator)
+        {
+            if (i>0) buff[x] = separator;
+            ++x;
+        }
+    }
+    return buff;
+}
+
+/**
+ * Convert a big uint into an octal string.
+ *
+ * Params:
+ *  buff = The destination buffer for the octal string. Must be large enough to
+ *      store the result, including leading zeroes, which is
+ *      ceil(data.length * BigDigitBits / 3) characters.
+ *      The buffer is filled from back to front, starting from `buff[$-1]`.
+ *  data = The biguint to be converted.
+ *
+ * Returns: The index of the leading non-zero digit in `buff`. Will be
+ * `buff.length - 1` if the entire big uint is zero.
+ */
+size_t biguintToOctal(char[] buff, const(BigDigit)[] data)
+    pure nothrow @safe @nogc
+{
+    ubyte carry = 0;
+    int shift = 0;
+    size_t penPos = buff.length - 1;
+    size_t lastNonZero = buff.length - 1;
+
+    pragma(inline) void output(uint digit) @nogc nothrow
+    {
+        if (digit != 0)
+            lastNonZero = penPos;
+        buff[penPos--] = cast(char)('0' + digit);
+    }
+
+    foreach (bigdigit; data)
+    {
+        if (shift < 0)
+        {
+            // Some bits were carried over from previous word.
+            assert(shift > -3, "shift must be greater than -3");
+            output(((bigdigit << -shift) | carry) & 0b111);
+            shift += 3;
+            assert(shift > 0, "shift must be 1 or greater");
+        }
+
+        while (shift <= BigDigitBits - 3)
+        {
+            output((bigdigit >>> shift) & 0b111);
+            shift += 3;
+        }
+
+        if (shift < BigDigitBits)
+        {
+            // Some bits need to be carried forward.
+            carry = (bigdigit >>> shift) & 0b11;
+        }
+        shift -= BigDigitBits;
+        assert(shift >= -2 && shift <= 0, "shift must in [-2,0]");
+    }
+
+    if (shift < 0)
+    {
+        // Last word had bits that haven't been output yet.
+        assert(shift > -3, "Shift must be greater than -3");
+        output(carry);
+    }
+
+    return lastNonZero;
+}
+
+/** Convert a big uint into a decimal string.
+ *
+ * Params:
+ *  data    The biguint to be converted. Will be destroyed.
+ *  buff    The destination buffer for the decimal string. Must be
+ *          large enough to store the result, including leading zeros.
+ *          Will be filled backwards, starting from buff[$-1].
+ *
+ * buff.length must be >= (data.length*32)/log2(10) = 9.63296 * data.length.
+ * Returns:
+ *    the lowest index of buff which was used.
+ */
+size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow @safe
+{
+    ptrdiff_t sofar = buff.length;
+    // Might be better to divide by (10^38/2^32) since that gives 38 digits for
+    // the price of 3 divisions and a shr; this version only gives 27 digits
+    // for 3 divisions.
+    while (data.length>1)
+    {
+        uint rem = multibyteDivAssign(data, 10_0000_0000, 0);
+        itoaZeroPadded(buff[sofar-9 .. sofar], rem);
+        sofar -= 9;
+        if (data[$-1] == 0 && data.length > 1)
+        {
+            data = data[0 .. $ - 1];
+        }
+    }
+    itoaZeroPadded(buff[sofar-10 .. sofar], data[0]);
+    sofar -= 10;
+    // and strip off the leading zeros
+    while (sofar != buff.length-1 && buff[sofar] == '0')
+        sofar++;
+    return sofar;
+}
+
+/** Convert a decimal string into a big uint.
+ *
+ * Params:
+ *  data    The biguint to be receive the result. Must be large enough to
+ *          store the result.
+ *  s       The decimal string. May contain _ or 0 .. 9
+ *
+ * The required length for the destination buffer is slightly less than
+ *  1 + s.length/log2(10) = 1 + s.length/3.3219.
+ *
+ * Returns:
+ *    the highest index of data which was used. 0 if case of failure.
+ */
+int biguintFromDecimal(Range)(BigDigit[] data, Range s)
+if (
+    isInputRange!Range &&
+    isSomeChar!(ElementType!Range) &&
+    !isInfinite!Range)
+in
+{
+    static if (hasLength!Range)
+        assert((data.length >= 2) || (data.length == 1 && s.length == 1),
+            "data has a invalid length");
+}
+do
+{
+    // Convert to base 1e19 = 10_000_000_000_000_000_000.
+    // (this is the largest power of 10 that will fit into a long).
+    // The length will be less than 1 + s.length/log2(10) = 1 + s.length/3.3219.
+    // 485 bits will only just fit into 146 decimal digits.
+    // As we convert the string, we record the number of digits we've seen in base 19:
+    // hi is the number of digits/19, lo is the extra digits (0 to 18).
+    // TODO: This is inefficient for very large strings (it is O(n^^2)).
+    // We should take advantage of fast multiplication once the numbers exceed
+    // Karatsuba size.
+    uint lo = 0; // number of powers of digits, 0 .. 18
+    uint x = 0;
+    ulong y = 0;
+    uint hi = 0; // number of base 1e19 digits
+    data[0] = 0; // initially number is 0.
+    if (data.length > 1)
+        data[1] = 0;
+
+    foreach (character; s)
+    {
+        if (character == '_')
+            continue;
+
+        if (character < '0' || character > '9')
+            return 0;
+        x *= 10;
+        x += character - '0';
+        ++lo;
+        if (lo == 9)
+        {
+            y = x;
+            x = 0;
+        }
+        if (lo == 18)
+        {
+            y *= 10_0000_0000;
+            y += x;
+            x = 0;
+        }
+        if (lo == 19)
+        {
+            y *= 10;
+            y += x;
+            x = 0;
+            // Multiply existing number by 10^19, then add y1.
+            if (hi>0)
+            {
+                data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 1_220_703_125*2u, 0); // 5^13*2 = 0x9184_E72A
+                ++hi;
+                data[hi] = multibyteMul(data[0 .. hi], data[0 .. hi], 15_625*262_144u, 0); // 5^6*2^18 = 0xF424_0000
+                ++hi;
+            }
+            else
+                hi = 2;
+            uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF));
+            c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32));
+            if (c != 0)
+            {
+                data[hi]=c;
+                ++hi;
+            }
+            y = 0;
+            lo = 0;
+        }
+    }
+    // Now set y = all remaining digits.
+    if (lo >= 18)
+    {
+    }
+    else if (lo >= 9)
+    {
+        for (int k=9; k<lo; ++k) y*=10;
+        y+=x;
+    }
+    else
+    {
+        for (int k=0; k<lo; ++k) y*=10;
+        y+=x;
+    }
+    if (lo != 0)
+    {
+        if (hi == 0)
+        {
+            data[0] = cast(uint) y;
+            if (data.length == 1)
+            {
+                hi = 1;
+            }
+            else
+            {
+                data[1] = cast(uint)(y >>> 32);
+                hi=2;
+            }
+        }
+        else
+        {
+            while (lo>0)
+            {
+                immutable c = multibyteMul(data[0 .. hi], data[0 .. hi], 10, 0);
+                if (c != 0)
+                {
+                    data[hi]=c;
+                    ++hi;
+                }
+                --lo;
+            }
+            uint c = multibyteIncrementAssign!('+')(data[0 .. hi], cast(uint)(y&0xFFFF_FFFF));
+            if (y > 0xFFFF_FFFFL)
+            {
+                c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32));
+            }
+            if (c != 0)
+            {
+                data[hi]=c;
+                ++hi;
+            }
+        }
+    }
+    while (hi>1 && data[hi-1]==0)
+        --hi;
+    return hi;
+}
+
+
+// ------------------------
+// These in-place functions are only for internal use; they are incompatible
+// with COW.
+
+// Classic 'schoolbook' multiplication.
+void mulSimple(BigDigit[] result, const(BigDigit) [] left,
+        const(BigDigit)[] right) pure nothrow @safe
+in
+{
+    assert(result.length == left.length + right.length,
+        "Result must be able to store left + right");
+    assert(right.length>1, "right must not be empty");
+}
+do
+{
+    result[left.length] = multibyteMul(result[0 .. left.length], left, right[0], 0);
+    multibyteMultiplyAccumulate(result[1..$], left, right[1..$]);
+}
+
+// Classic 'schoolbook' squaring
+void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure nothrow @safe
+in
+{
+    assert(result.length == 2*x.length, "result must be twice as long as x");
+    assert(x.length>1, "x must not be empty");
+}
+do
+{
+    multibyteSquare(result, x);
+}
+
+
+// add two uints of possibly different lengths. Result must be as long
+// as the larger length.
+// Returns carry (0 or 1).
+uint addSimple(BigDigit[] result, const BigDigit [] left, const BigDigit [] right)
+pure nothrow @safe
+in
+{
+    assert(result.length == left.length,
+        "result and left must be of the same length");
+    assert(left.length >= right.length,
+        "left must be longer or of equal length to right");
+    assert(right.length > 0, "right must not be empty");
+}
+do
+{
+    uint carry = multibyteAdd(result[0 .. right.length],
+            left[0 .. right.length], right, 0);
+    if (right.length < left.length)
+    {
+        result[right.length .. left.length] = left[right.length .. $];
+        carry = multibyteIncrementAssign!('+')(result[right.length..$], carry);
+    }
+    return carry;
+}
+
+//  result = left - right
+// returns carry (0 or 1)
+BigDigit subSimple(BigDigit [] result,const(BigDigit) [] left,
+        const(BigDigit) [] right) pure nothrow
+in
+{
+    assert(result.length == left.length,
+        "result and left must be of the same length");
+    assert(left.length >= right.length,
+        "left must be longer or of equal length to right");
+    assert(right.length > 0, "right must not be empty");
+}
+do
+{
+    BigDigit carry = multibyteSub(result[0 .. right.length],
+            left[0 .. right.length], right, 0);
+    if (right.length < left.length)
+    {
+        result[right.length .. left.length] = left[right.length .. $];
+        carry = multibyteIncrementAssign!('-')(result[right.length..$], carry);
+    } //else if (result.length == left.length+1) { result[$-1] = carry; carry=0; }
+    return carry;
+}
+
+
+/* result = result - right
+ * Returns carry = 1 if result was less than right.
+*/
+BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right)
+pure nothrow @safe
+{
+    assert(result.length >= right.length,
+       "result must be longer or of equal length to right");
+    uint c = multibyteSub(result[0 .. right.length], result[0 .. right.length], right, 0);
+    if (c && result.length > right.length)
+        c = multibyteIncrementAssign!('-')(result[right.length .. $], c);
+    return c;
+}
+
+/* result = result + right
+*/
+BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right)
+pure nothrow @safe
+{
+    assert(result.length >= right.length,
+       "result must be longer or of equal length to right");
+    uint c = multibyteAdd(result[0 .. right.length], result[0 .. right.length], right, 0);
+    if (c && result.length > right.length)
+       c = multibyteIncrementAssign!('+')(result[right.length .. $], c);
+    return c;
+}
+
+/* performs result += wantSub? - right : right;
+*/
+BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right,
+        bool wantSub) pure nothrow @safe
+{
+    if (wantSub)
+        return subAssignSimple(result, right);
+    else
+        return addAssignSimple(result, right);
+}
+
+
+// return true if x<y, considering leading zeros
+bool less(const(BigDigit)[] x, const(BigDigit)[] y) pure nothrow @safe
+{
+    assert(x.length >= y.length,
+       "x must be longer or of equal length to y");
+    auto k = x.length-1;
+    while (x[k]==0 && k >= y.length)
+        --k;
+    if (k >= y.length)
+        return false;
+    while (k>0 && x[k]==y[k])
+        --k;
+    return x[k] < y[k];
+}
+
+// Set result = abs(x-y), return true if result is negative(x<y), false if x <= y.
+bool inplaceSub(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
+    pure nothrow @safe
+{
+    assert(result.length == ((x.length >= y.length) ? x.length : y.length),
+        "result must capable to store the maximum of x and y");
+
+    size_t minlen;
+    bool negative;
+    if (x.length >= y.length)
+    {
+        minlen = y.length;
+        negative = less(x, y);
+    }
+    else
+    {
+       minlen = x.length;
+       negative = !less(y, x);
+    }
+    const (BigDigit)[] large, small;
+    if (negative)
+    {
+        large = y; small = x;
+    }
+    else
+    {
+        large = x; small = y;
+    }
+
+    BigDigit carry = multibyteSub(result[0 .. minlen], large[0 .. minlen], small[0 .. minlen], 0);
+    if (x.length != y.length)
+    {
+        result[minlen .. large.length]= large[minlen..$];
+        result[large.length..$] = 0;
+        if (carry)
+            multibyteIncrementAssign!('-')(result[minlen..$], carry);
+    }
+    return negative;
+}
+
+/* Determine how much space is required for the temporaries
+ * when performing a Karatsuba multiplication.
+ * TODO: determining a tight bound is non-trivial and depends on KARATSUBALIMIT, see:
+ * https://issues.dlang.org/show_bug.cgi?id=20493
+ */
+size_t karatsubaRequiredBuffSize()(size_t xlen) pure nothrow @safe
+{
+    return xlen < KARATSUBALIMIT ? 0 : (xlen * 9) / 4;
+}
+
+size_t divisionRequiredBuffSize()(size_t ulen, size_t vlen) pure nothrow @safe
+{
+    return 2 * vlen + ulen + 2 + vlen.karatsubaRequiredBuffSize;
+}
+
+/* Sets result = x*y, using Karatsuba multiplication.
+* x must be longer or equal to y.
+* Valid only for balanced multiplies, where x is not shorter than y.
+* It is superior to schoolbook multiplication if and only if
+*    sqrt(2)*y.length > x.length > y.length.
+* Karatsuba multiplication is O(n^1.59), whereas schoolbook is O(n^2)
+* The maximum allowable length of x and y is uint.max; but better algorithms
+* should be used far before that length is reached.
+* Params:
+* scratchbuff      An array long enough to store all the temporaries. Will be destroyed.
+*/
+void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x,
+        const(BigDigit)[] y, BigDigit [] scratchbuff) pure nothrow @safe
+{
+    assert(x.length >= y.length, "x must be greater or equal to y");
+    assert(result.length < uint.max, "Operands too large");
+    assert(result.length == x.length + y.length,
+        "result must be as large as x + y");
+    if (x.length < KARATSUBALIMIT)
+    {
+        return mulSimple(result, x, y);
+    }
+    // Must be almost square (otherwise, a schoolbook iteration is better)
+    assert(2L * y.length * y.length > (x.length-1) * (x.length-1),
+        "Bigint Internal Error: Asymmetric Karatsuba");
+
+    // The subtractive version of Karatsuba multiply uses the following result:
+    // (Nx1 + x0)*(Ny1 + y0) = (N*N)*x1y1 + x0y0 + N * (x0y0 + x1y1 - mid)
+    // where mid = (x0-x1)*(y0-y1)
+    // requiring 3 multiplies of length N, instead of 4.
+    // The advantage of the subtractive over the additive version is that
+    // the mid multiply cannot exceed length N. But there are subtleties:
+    // (x0-x1),(y0-y1) may be negative or zero. To keep it simple, we
+    // retain all of the leading zeros in the subtractions
+
+    // half length, round up.
+    auto half = (x.length >> 1) + (x.length & 1);
+
+    const(BigDigit) [] x0 = x[0 .. half];
+    const(BigDigit) [] x1 = x[half .. $];
+    const(BigDigit) [] y0 = y[0 .. half];
+    const(BigDigit) [] y1 = y[half .. $];
+    BigDigit [] mid = scratchbuff[0 .. half*2];
+    BigDigit [] newscratchbuff = scratchbuff[half*2 .. $];
+    BigDigit [] resultLow = result[0 .. 2*half];
+    BigDigit [] resultHigh = result[2*half .. $];
+     // initially use result to store temporaries
+    BigDigit [] xdiff= result[0 .. half];
+    BigDigit [] ydiff = result[half .. half*2];
+
+    // First, we calculate mid, and sign of mid
+    immutable bool midNegative = inplaceSub(xdiff, x0, x1)
+                      ^ inplaceSub(ydiff, y0, y1);
+    mulKaratsuba(mid, xdiff, ydiff, newscratchbuff);
+
+    // Low half of result gets x0 * y0. High half gets x1 * y1
+
+    mulKaratsuba(resultLow, x0, y0, newscratchbuff);
+
+    if (2L * y1.length * y1.length < x1.length * x1.length)
+    {
+        // an asymmetric situation has been created.
+        // Worst case is if x:y = 1.414 : 1, then x1:y1 = 2.41 : 1.
+        // Applying one schoolbook multiply gives us two pieces each 1.2:1
+        if (y1.length < KARATSUBALIMIT)
+            mulSimple(resultHigh, x1, y1);
+        else
+        {
+            // divide x1 in two, then use schoolbook multiply on the two pieces.
+            auto quarter = (x1.length >> 1) + (x1.length & 1);
+            immutable ysmaller = (quarter >= y1.length);
+            mulKaratsuba(resultHigh[0 .. quarter+y1.length], ysmaller ? x1[0 .. quarter] : y1,
+                ysmaller ? y1 : x1[0 .. quarter], newscratchbuff);
+            // Save the part which will be overwritten.
+            immutable ysmaller2 = ((x1.length - quarter) >= y1.length);
+            newscratchbuff[0 .. y1.length] = resultHigh[quarter .. quarter + y1.length];
+            mulKaratsuba(resultHigh[quarter..$], ysmaller2 ? x1[quarter..$] : y1,
+                ysmaller2 ? y1 : x1[quarter..$], newscratchbuff[y1.length..$]);
+
+            resultHigh[quarter..$].addAssignSimple(newscratchbuff[0 .. y1.length]);
+        }
+    }
+    else
+        mulKaratsuba(resultHigh, x1, y1, newscratchbuff);
+
+    /* We now have result = x0y0 + (N*N)*x1y1
+       Before adding or subtracting mid, we must calculate
+       result += N * (x0y0 + x1y1)
+       We can do this with three half-length additions. With a = x0y0, b = x1y1:
+                      aHI aLO
+        +       aHI   aLO
+        +       bHI   bLO
+        +  bHI  bLO
+        =  R3   R2    R1   R0
+        R1 = aHI + bLO + aLO
+        R2 = aHI + bLO + aHI + carry_from_R1
+        R3 = bHi + carry_from_R2
+     It might actually be quicker to do it in two full-length additions:
+     newscratchbuff[2*half] = addSimple(newscratchbuff[0 .. 2*half], result[0 .. 2*half], result[2*half..$]);
+     addAssignSimple(result[half..$], newscratchbuff[0 .. 2*half+1]);
+   */
+    BigDigit[] R1 = result[half .. half*2];
+    BigDigit[] R2 = result[half*2 .. half*3];
+    BigDigit[] R3 = result[half*3..$];
+    BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1
+    BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0
+    BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3
+    if (c1+c2)
+        multibyteIncrementAssign!('+')(result[half*2..$], c1+c2);
+    if (c1+c3)
+        multibyteIncrementAssign!('+')(R3, c1+c3);
+
+    // And finally we subtract mid
+    addOrSubAssignSimple(result[half..$], mid, !midNegative);
+}
+
+void squareKaratsuba(BigDigit [] result, const BigDigit [] x,
+        BigDigit [] scratchbuff) pure nothrow @safe
+{
+    // See mulKaratsuba for implementation comments.
+    // Squaring is simpler, since it never gets asymmetric.
+    assert(result.length < uint.max, "Operands too large");
+    assert(result.length == 2*x.length,
+        "result must be twice the length of x");
+    if (x.length <= KARATSUBASQUARELIMIT)
+    {
+        return squareSimple(result, x);
+    }
+    // half length, round up.
+    auto half = (x.length >> 1) + (x.length & 1);
+
+    const(BigDigit)[] x0 = x[0 .. half];
+    const(BigDigit)[] x1 = x[half .. $];
+    BigDigit [] mid = scratchbuff[0 .. half*2];
+    BigDigit [] newscratchbuff = scratchbuff[half*2 .. $];
+     // initially use result to store temporaries
+    BigDigit [] xdiff= result[0 .. half];
+    const BigDigit [] ydiff = result[half .. half*2];
+
+    // First, we calculate mid. We don't need its sign
+    inplaceSub(xdiff, x0, x1);
+    squareKaratsuba(mid, xdiff, newscratchbuff);
+
+    // Set result = x0x0 + (N*N)*x1x1
+    squareKaratsuba(result[0 .. 2*half], x0, newscratchbuff);
+    squareKaratsuba(result[2*half .. $], x1, newscratchbuff);
+
+    /* result += N * (x0x0 + x1x1)
+       Do this with three half-length additions. With a = x0x0, b = x1x1:
+        R1 = aHI + bLO + aLO
+        R2 = aHI + bLO + aHI + carry_from_R1
+        R3 = bHi + carry_from_R2
+    */
+    BigDigit[] R1 = result[half .. half*2];
+    BigDigit[] R2 = result[half*2 .. half*3];
+    BigDigit[] R3 = result[half*3..$];
+    BigDigit c1 = multibyteAdd(R2, R2, R1, 0); // c1:R2 = R2 + R1
+    BigDigit c2 = multibyteAdd(R1, R2, result[0 .. half], 0); // c2:R1 = R2 + R1 + R0
+    BigDigit c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3
+    if (c1+c2) multibyteIncrementAssign!('+')(result[half*2..$], c1+c2);
+    if (c1+c3) multibyteIncrementAssign!('+')(R3, c1+c3);
+
+    // And finally we subtract mid, which is always positive
+    subAssignSimple(result[half..$], mid);
+}
+
+/* Knuth's Algorithm D, as presented in
+ * H.S. Warren, "Hacker's Delight", Addison-Wesley Professional (2002).
+ * Also described in "Modern Computer Arithmetic" 0.2, Exercise 1.8.18.
+ * Given u and v, calculates  quotient  = u / v, u = u % v.
+ * v must be normalized (ie, the MSB of v must be 1).
+ * The most significant words of quotient and u may be zero.
+ * u[0 .. v.length] holds the remainder.
+ */
+void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v)
+    pure nothrow @safe
+{
+    assert(quotient.length == u.length - v.length,
+        "quotient has wrong length");
+    assert(v.length > 1, "v must not be empty");
+    assert(u.length >= v.length, "u must be larger or equal to v");
+    assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]");
+    assert(u[$ - 1] < v[$ - 1], "u[$ - 1] must be less than v[$ - 1]");
+    // BUG: This code only works if BigDigit is uint.
+    uint vhi = v[$-1];
+    uint vlo = v[$-2];
+
+    for (ptrdiff_t j = u.length - v.length - 1; j >= 0; j--)
+    {
+        // Compute estimate of quotient[j],
+        // qhat = (three most significant words of u)/(two most sig words of v).
+        uint qhat;
+        if (u[j + v.length] == vhi)
+        {
+            // uu/vhi could exceed uint.max (it will be 0x8000_0000 or 0x8000_0001)
+            qhat = uint.max;
+        }
+        else
+        {
+            uint ulo = u[j + v.length - 2];
+            version (D_InlineAsm_X86)
+            {
+                // Note: On DMD, this is only ~10% faster than the non-asm code.
+                uint *p = &u[j + v.length - 1];
+                asm pure nothrow @trusted
+                {
+                    mov EAX, p;
+                    mov EDX, [EAX+4];
+                    mov EAX, [EAX];
+                    div dword ptr [vhi];
+                    mov qhat, EAX;
+                    mov ECX, EDX;
+div3by2correction:
+                    mul dword ptr [vlo]; // EDX:EAX = qhat * vlo
+                    sub EAX, ulo;
+                    sbb EDX, ECX;
+                    jbe div3by2done;
+                    mov EAX, qhat;
+                    dec EAX;
+                    mov qhat, EAX;
+                    add ECX, dword ptr [vhi];
+                    jnc div3by2correction;
+div3by2done:    ;
+                }
+            }
+            else
+            { // version (InlineAsm)
+                ulong uu = (cast(ulong)(u[j + v.length]) << 32) | u[j + v.length - 1];
+                immutable bigqhat = uu / vhi;
+                ulong rhat =  uu - bigqhat * vhi;
+                qhat = cast(uint) bigqhat;
+again:
+                if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo))
+                {
+                    --qhat;
+                    rhat += vhi;
+                    if (!(rhat & 0xFFFF_FFFF_0000_0000L))
+                        goto again;
+                }
+            } // version (InlineAsm)
+        }
+        // Multiply and subtract.
+        uint carry = multibyteMulAdd!('-')(u[j .. j + v.length], v, qhat, 0);
+
+        if (u[j+v.length] < carry)
+        {
+            // If we subtracted too much, add back
+            --qhat;
+            carry -= multibyteAdd(u[j .. j + v.length],u[j .. j + v.length], v, 0);
+        }
+        quotient[j] = qhat;
+        u[j + v.length] = u[j + v.length] - carry;
+    }
+}
+
+// TODO: Replace with a library call
+void itoaZeroPadded(char[] output, uint value)
+    pure nothrow @safe @nogc
+{
+    for (auto i = output.length; i--;)
+    {
+        if (value < 10)
+        {
+            output[i] = cast(char)(value + '0');
+            value = 0;
+        }
+        else
+        {
+            output[i] = cast(char)(value % 10 + '0');
+            value /= 10;
+        }
+    }
+}
+
+void toHexZeroPadded(char[] output, uint value,
+        LetterCase letterCase = LetterCase.upper) pure nothrow @safe
+{
+    ptrdiff_t x = output.length - 1;
+    static immutable string upperHexDigits = "0123456789ABCDEF";
+    static immutable string lowerHexDigits = "0123456789abcdef";
+    for ( ; x >= 0; --x)
+    {
+        if (letterCase == LetterCase.upper)
+        {
+            output[x] = upperHexDigits[value & 0xF];
+        }
+        else
+        {
+            output[x] = lowerHexDigits[value & 0xF];
+        }
+        value >>= 4;
+    }
+}
+
+// Returns the highest value of i for which left[i]!=right[i],
+// or 0 if left[] == right[]
+size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right)
+pure nothrow @nogc @safe
+{
+    assert(left.length == right.length,
+        "left have a length equal to that of right");
+    for (ptrdiff_t i = left.length - 1; i>0; --i)
+    {
+        if (left[i] != right[i])
+            return i;
+    }
+    return 0;
+}
+
+// Returns the lowest value of i for which x[i]!=0.
+int firstNonZeroDigit(const BigDigit [] x) pure nothrow @nogc @safe
+{
+    int k = 0;
+    while (x[k]==0)
+    {
+        ++k;
+        assert(k < x.length, "k must be less than x.length");
+    }
+    return k;
+}
+
+/*
+    Calculate quotient and remainder of u / v using fast recursive division.
+    v must be normalised, and must be at least half as long as u.
+    Given u and v, v normalised, calculates  quotient  = u/v, u = u%v.
+    scratch is temporary storage space, length must be >= quotient + 1.
+Returns:
+    u[0 .. v.length] is the remainder. u[v.length..$] is corrupted.
+    Implements algorithm 1.8 from MCA.
+    This algorithm has an annoying special case. After the first recursion, the
+    highest bit of the quotient may be set. This means that in the second
+    recursive call, the 'in' contract would be violated. (This happens only
+    when the top quarter of u is equal to the top half of v. A base 10
+    equivalent example of this situation is 5517/56; the first step gives
+    55/5 = 11). To maintain the in contract, we pad a zero to the top of both
+    u and the quotient. 'mayOverflow' indicates that that the special case
+    has occurred.
+    (In MCA, a different strategy is used: the in contract is weakened, and
+    schoolbookDivMod is more general: it allows the high bit of u to be set).
+    See also:
+    - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022,
+      Max-Planck Institute fuer Informatik, (Oct 1998).
+*/
+void recursiveDivMod(BigDigit[] quotient, BigDigit[] u, const(BigDigit)[] v,
+                     BigDigit[] scratch, bool mayOverflow = false)
+                     pure nothrow @safe
+in
+{
+    // v must be normalized
+    assert(v.length > 1, "v must not be empty");
+    assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]");
+    assert(!(u[$ - 1] & 0x8000_0000), "Invalid value at u[$ - 1]");
+    assert(quotient.length == u.length - v.length,
+        "quotient must be of equal length of u - v");
+    if (mayOverflow)
+    {
+        assert(u[$-1] == 0, "Invalid value at u[$ - 1]");
+        assert(u[$-2] & 0x8000_0000, "Invalid value at u[$ - 2]");
+    }
+
+    // Must be symmetric. Use block schoolbook division if not.
+    assert((mayOverflow ? u.length-1 : u.length) <= 2 * v.length,
+        "Invalid length of u");
+    assert((mayOverflow ? u.length-1 : u.length) >= v.length,
+        "Invalid length of u");
+    assert(scratch.length >= quotient.length + (mayOverflow ? 0 : 1),
+        "Invalid quotient length");
+}
+do
+{
+    if (quotient.length < FASTDIVLIMIT)
+    {
+        return schoolbookDivMod(quotient, u, v);
+    }
+
+    // Split quotient into two halves, but keep padding in the top half
+    auto k = (mayOverflow ?  quotient.length - 1 : quotient.length) >> 1;
+
+    // RECURSION 1: Calculate the high half of the quotient
+
+    // Note that if u and quotient were padded, they remain padded during
+    // this call, so in contract is satisfied.
+    recursiveDivMod(quotient[k .. $], u[2 * k .. $], v[k .. $],
+        scratch, mayOverflow);
+
+    // quotient[k..$] is our guess at the high quotient.
+    // u[2*k .. 2.*k + v.length - k = k + v.length] is the high part of the
+    // first remainder. u[0 .. 2*k] is the low part.
+
+    // Calculate the full first remainder to be
+    //    remainder - highQuotient * lowDivisor
+    // reducing highQuotient until the remainder is positive.
+    // The low part of the remainder, u[0 .. k], cannot be altered by this.
+
+    adjustRemainder(quotient[k .. $], u[k .. k + v.length], v, k,
+            scratch, mayOverflow);
+
+    // RECURSION 2: Calculate the low half of the quotient
+    // The full first remainder is now in u[0 .. k + v.length].
+
+    if (u[k + v.length - 1] & 0x8000_0000)
+    {
+        // Special case. The high quotient is 0x1_00...000 or 0x1_00...001.
+        // This means we need an extra quotient word for the next recursion.
+        // We need to restore the invariant for the recursive calls.
+        // We do this by padding both u and quotient. Extending u is trivial,
+        // because the higher words will not be used again. But for the
+        // quotient, we're clobbering the low word of the high quotient,
+        // so we need save it, and add it back in after the recursive call.
+
+        auto clobberedQuotient = quotient[k];
+        u[k + v.length] = 0;
+
+        recursiveDivMod(quotient[0 .. k + 1], u[k .. k + v.length + 1],
+            v[k .. $], scratch, true);
+        adjustRemainder(quotient[0 .. k + 1], u[0 .. v.length], v, k,
+            scratch, true);
+
+        // Now add the quotient word that got clobbered earlier.
+        multibyteIncrementAssign!('+')(quotient[k..$], clobberedQuotient);
+    }
+    else
+    {
+        // The special case has NOT happened.
+        recursiveDivMod(quotient[0 .. k], u[k .. k + v.length], v[k .. $],
+            scratch, false);
+
+        // high remainder is in u[k .. k+(v.length-k)] == u[k .. v.length]
+
+        adjustRemainder(quotient[0 .. k], u[0 .. v.length], v, k,
+            scratch);
+    }
+}
+
+// rem -= quot * v[0 .. k].
+// If would make rem negative, decrease quot until rem is >= 0.
+// Needs (quot.length * k) scratch space to store the result of the multiply.
+void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v,
+        ptrdiff_t k,
+        BigDigit[] scratch, bool mayOverflow = false) pure nothrow @safe
+{
+    assert(rem.length == v.length, "rem must be as long as v");
+    auto res = scratch[0 .. quot.length + k];
+    mulInternal(res, quot, v[0 .. k], scratch[quot.length + k .. $]);
+    uint carry = subAssignSimple(rem, res[0..$-mayOverflow]);
+    if (mayOverflow)
+        carry += res[$-1];
+    while (carry)
+    {
+        multibyteIncrementAssign!('-')(quot, 1); // quot--
+        carry -= multibyteAdd(rem, rem, v, 0);
+    }
+}
+//u + 1, v, v kur,
+// Cope with unbalanced division by performing block schoolbook division.
+void blockDivMod(BigDigit [] q, BigDigit [] u, in BigDigit [] v, BigDigit [] scratch)
+pure nothrow @safe
+{
+    assert(q.length == u.length - v.length,
+        "quotient must be of equal length of u - v");
+    assert(v.length > 1, "v must not be empty");
+    assert(u.length >= v.length, "u must be longer or of equal length as v");
+    assert((v[$-1] & 0x8000_0000)!=0, "Invalid value at v[$ - 1]");
+    assert((u[$-1] & 0x8000_0000)==0, "Invalid value at u[$ - 1]");
+
+    // Perform block schoolbook division, with 'v.length' blocks.
+    auto m = u.length - v.length;
+    auto n = u.length - v.length;
+    do
+    {
+        if (n <= v.length)
+            n = v.length;
+        auto mayOverflow = (u[m + v.length -1 ] & 0x8000_0000)!=0;
+        BigDigit saveq;
+        if (mayOverflow)
+        {
+            u[m + v.length] = 0;
+            saveq = q[m];
+        }
+        import std.stdio;
+        recursiveDivMod(
+            q[n - v.length .. m + mayOverflow],
+            u[n - v.length .. m + mayOverflow + v.length],
+            v, scratch, mayOverflow);
+        if (mayOverflow)
+        {
+            assert(q[m] == 0, "q must not be 0");
+            q[m] = saveq;
+        }
+        m -= v.length;
+        n -= v.length;
+    }
+    while(n);
+}
+
+@system unittest
+{
+    version (none)
+    {
+        import core.stdc.stdio;
+
+        void printBiguint(const uint [] data)
+        {
+            char [] buff = biguintToHex(new char[data.length*9], data, '_');
+            printf("%.*s\n", cast(int) buff.length, buff.ptr);
+        }
+
+        void printDecimalBigUint(BigUint data)
+        {
+            auto str = data.toDecimalString(0);
+            printf("%.*s\n", cast(int) str.length, str.ptr);
+        }
+    }
+
+    uint[43] a;
+    uint[179] b;
+    for (int i=0; i<a.length; ++i) a[i] = 0x1234_B6E9 + i;
+    for (int i=0; i<b.length; ++i) b[i] = 0x1BCD_8763 - i*546;
+
+    a[$-1] |= 0x8000_0000;
+    uint[a.length] r;
+    uint[b.length-a.length+1] q;
+
+    uint[divisionRequiredBuffSize(b.length, a.length)] buff = void;
+
+    divModInternal(q, r, b, a, buff);
+    auto r1 = r;
+    auto q1 = q;
+    blockDivMod(q[0 .. $-1], b, a, buff);
+    assert(b[0 .. a.length][] == r1[]);
+    assert(q[0 .. $ - 1] == q1[0 .. $ - 1]);
+}
+
+// biguintToOctal
+@safe unittest
+{
+    enum bufSize = 5 * BigDigitBits / 3 + 1;
+    char[bufSize] buf;
+    size_t i;
+
+    // Basic functionality with single word
+    {
+        BigDigit[1] data = [ 342391 ];
+        i = biguintToOctal(buf, data);
+        assert(i == bufSize - 7 && buf[i .. $] == "1234567");
+    }
+
+    // Test carrying bits between words
+    {
+        BigDigit[3] data = [ 0x77053977, 0x39770539, 0x00000005 ];
+        i = biguintToOctal(buf, data);
+        assert(i == bufSize - 23 && buf[i .. $] == "12345670123456701234567");
+    }
+
+    // Test carried bits in the last word
+    {
+        BigDigit[1] data = [ 0x80000000 ];
+        i = biguintToOctal(buf, data);
+        assert(buf[i .. $] == "20000000000");
+    }
+
+    // Test boundary between 3rd and 4th word where the number of bits is
+    // divisible by 3 and no bits should be carried.
+    //
+    // The 0xC0000000's are for "poisoning" the carry to be non-zero when the
+    // rollover happens, so that if any bugs happen in wrongly adding the carry
+    // to the next word, non-zero bits will show up in the output.
+    {
+        BigDigit[4] data = [ 0xC0000000, 0xC0000000, 0xC0000000, 0x00000010 ];
+        i = biguintToOctal(buf, data);
+        assert(buf[i .. $] == "2060000000001400000000030000000000");
+    }
+
+    // Boundary case: 0
+    {
+        BigDigit[1] data = [ 0 ];
+        i = biguintToOctal(buf, data);
+        assert(buf[i .. $] == "0");
+    }
+}

From a8c383c490e33ca2369b9f75ec89e462e3416836 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 00:58:21 +0400
Subject: [PATCH 399/573] fix meson

---
 meson.build                                      | 2 ++
 source/mir/bignum/fixed.d                        | 2 +-
 source/mir/bignum/integer.d                      | 4 ++--
 source/mir/bignum/{ => internal}/kernel.d        | 6 +++---
 source/mir/bignum/{ => internal}/phobos_kernel.d | 4 ++--
 5 files changed, 10 insertions(+), 8 deletions(-)
 rename source/mir/bignum/{ => internal}/kernel.d (97%)
 rename source/mir/bignum/{ => internal}/phobos_kernel.d (99%)

diff --git a/meson.build b/meson.build
index 22ba9604..91c017cf 100644
--- a/meson.build
+++ b/meson.build
@@ -22,6 +22,8 @@ sources_list = [
     'mir/bignum/fp',
     'mir/bignum/integer',
     'mir/bignum/internal/dec2flt_table',
+    'mir/bignum/internal/kernel',
+    'mir/bignum/internal/phobos_kernel',
     'mir/bignum/internal/ryu/generic_128',
     'mir/bignum/low_level_view',
     'mir/combinatorics/package',
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index bfa834dc..ea1c2f2b 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -397,7 +397,7 @@ struct UInt(size_t size)
     ref divMod(size_t rhsSize)(scope ref UInt!rhsSize rhs)
         @safe pure nothrow @nogc scope return
     {
-        import mir.bignum.kernel: divMod, divisionRequiredBuffSize;
+        import mir.bignum.internal.kernel: divMod, divisionRequiredBuffSize;
 
         UInt!size quotient;
 
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index b9b98eb9..83bd5aa7 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -442,7 +442,7 @@ struct BigInt(uint maxSize64)
         if (maxSize64 >= aSize64 + bSize64)
     {
         import mir.utility: max;
-        import mir.bignum.kernel : multiply, karatsubaRequiredBuffSize;
+        import mir.bignum.internal.kernel : multiply, karatsubaRequiredBuffSize;
         enum sizeM = ulong.sizeof / size_t.sizeof;
         size_t[max(aSize64 * sizeM, bSize64 * sizeM).karatsubaRequiredBuffSize] buffer = void;
         this.length = cast(uint) multiply(data, a.coefficients, b.coefficients, buffer);
@@ -458,7 +458,7 @@ struct BigInt(uint maxSize64)
         @trusted pure nothrow @nogc scope return
         if (remainderSize >= divisorSize64)
     {
-        import mir.bignum.kernel : divMod, divisionRequiredBuffSize;
+        import mir.bignum.internal.kernel : divMod, divisionRequiredBuffSize;
 
         pragma(inline, false);
 
diff --git a/source/mir/bignum/kernel.d b/source/mir/bignum/internal/kernel.d
similarity index 97%
rename from source/mir/bignum/kernel.d
rename to source/mir/bignum/internal/kernel.d
index ebe51899..f6c7f4a7 100644
--- a/source/mir/bignum/kernel.d
+++ b/source/mir/bignum/internal/kernel.d
@@ -1,7 +1,7 @@
-module mir.bignum.kernel;
+module mir.bignum.internal.kernel;
 
-import mir.bignum.phobos_kernel;
-public import mir.bignum.phobos_kernel: karatsubaRequiredBuffSize, divisionRequiredBuffSize;
+import mir.bignum.internal.phobos_kernel;
+public import mir.bignum.internal.phobos_kernel: karatsubaRequiredBuffSize, divisionRequiredBuffSize;
 
 private inout(uint)[] toUints()(inout ulong[] data)
     @trusted pure nothrow @nogc
diff --git a/source/mir/bignum/phobos_kernel.d b/source/mir/bignum/internal/phobos_kernel.d
similarity index 99%
rename from source/mir/bignum/phobos_kernel.d
rename to source/mir/bignum/internal/phobos_kernel.d
index 8dd8cf23..4efbc780 100644
--- a/source/mir/bignum/phobos_kernel.d
+++ b/source/mir/bignum/internal/phobos_kernel.d
@@ -37,7 +37,7 @@ Idioms:
   'func(Tulong)(Tulong x) if (is(Tulong == ulong))' rather than 'func(ulong x)'
   in order to disable implicit conversion.
 */
-module mir.bignum.phobos_kernel;
+module mir.bignum.internal.phobos_kernel;
 
 version (D_InlineAsm_X86)
 {
@@ -81,7 +81,7 @@ enum KARATSUBASQUARELIMIT = 32; // Minimum value for which square Karatsuba is w
 import std.ascii: LetterCase;
 
 pure nothrow @nogc:
-package:
+package(mir.bignum):
 
 // dipatchers to the right low-level primitives. Added to allow BigInt CTFE for
 // 32 bit systems (https://issues.dlang.org/show_bug.cgi?id=14767) although it's

From 3341d5a6c33e919de868ce8249d153e744f9f3cd Mon Sep 17 00:00:00 2001
From: hatf0 <harrison@0xcc.pw>
Date: Wed, 5 Jan 2022 13:00:30 -0500
Subject: [PATCH 400/573] Implement Algorithm M

---
 source/mir/bignum/low_level_view.d | 199 +++++++++++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index f6bf208e..c87b819e 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2587,6 +2587,205 @@ struct DecimalView(W)
     }
 }
 
+@optStrategy("minsize")
+package T algoM(T)(T ret, scope BigUIntView!(const(uint)) coeff, int exponent)
+{
+    import mir.bignum.integer: BigInt;
+    import mir.bignum.fixed: UInt;
+    import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
+
+    BigInt!256 u = coeff, v = 1;
+
+    if (exponent < 0)
+    {
+        auto degree = -exponent;
+
+        if (v.approxCanMulPow5(degree)) {
+            v.mulPow5(degree);
+        }
+        else
+        {
+            enum n = MaxWordPow5!uint;
+            auto growFactor = degree / n + (degree % n != 0);
+            v.coefficients.length += growFactor;
+            v.mulPow5(degree);
+        }
+
+        if (v.canMulPow2(degree))
+        {
+            v.mulPow2(degree);
+        }
+        else
+        {
+            import mir.bitop: ctlz;
+            enum n = uint.sizeof * 8;
+            auto growFactor = degree / n + (degree % n > ctlz(v.view.mostSignificant));
+            v.coefficients.length += growFactor;
+            v.mulPow2(degree);
+        }
+
+    }
+    else
+    {
+        auto degree = exponent;
+
+        if (u.approxCanMulPow5(degree))
+        {
+            u.mulPow5(degree);
+        }
+        else
+        {
+            enum n = MaxWordPow5!uint;
+            auto growFactor = degree / n + (degree % n != 0);
+
+        }
+        u.mulPow5(exponent);
+        u.mulPow2(exponent);
+    }
+
+    // XXX: is this needed?
+    // v.coefficients = v.view.normalized.coefficients;
+
+    // Adopted from Rust's rawfp implementation (TBD: do we have these already?)
+    enum EXP_BITS = (T.sizeof * 8) - T.mant_dig;
+    enum MAX_EXP = (1 << (EXP_BITS - 1)) - 1;
+    enum MIN_EXP = -MAX_EXP + 1;
+    enum MAX_EXP_INT = MAX_EXP - (T.mant_dig - 1);
+    enum MIN_EXP_INT = MIN_EXP - (T.mant_dig - 1);
+
+    // MAX_SIG
+    enum betaN = (ulong(1) << T.mant_dig) - 1;
+    // MIN_SIG
+    enum betaNMinus1 = ulong(1) << (T.mant_dig - 1);
+
+    int k = 0;
+
+    // Ugly ugly ugly, but we don't want to cast to ulong to compare
+    // as there's a potential for truncation
+    auto min_sig = cast(BigUIntView!uint)UInt!64(betaNMinus1).view;
+    auto max_sig = cast(BigUIntView!uint)UInt!64(betaN).view;
+
+    BigUIntView!uint x, rem;
+    while (true)
+    {
+        auto m = u.view.normalized.coefficients.length;
+        auto n = v.view.normalized.coefficients.length;
+        // As part of "scaling", we'll initially start with m < n as true, 
+        // so we need to have this fall back here
+        if (m < n) {
+            x.coefficients.length = m;
+        } 
+        else {
+            x.coefficients.length = (m - n) + 1;
+        }
+
+        // Division requires us to introduce
+        // a new digit in the MSB-place -- we do that here
+        rem.coefficients.length = u.view.coefficients.length + 1;
+        rem.coefficients[0 .. $ - 1] = u.view.coefficients;
+        rem.coefficients[$ - 1] = 0;
+        divm(rem, v.view, x);
+
+        // Check if we're about to underflow
+        if (k == MAX_EXP_INT)
+        {
+            if (x >= min_sig && x <= max_sig)
+            {
+                break;
+            }
+            // XXX: deal with underflow
+            assert(0, "underflow");
+        }
+
+        // Check if our exponent is out of range
+        if (k > MAX_EXP_INT)
+        {
+            return T.infinity;
+        }
+
+        if (x < min_sig)
+        {
+            if (!u.canMulPow2(1))
+            {
+                u.coefficients.length++;
+            }
+
+            u.mulPow2(1); 
+            k -= 1;
+        }
+        else if (max_sig <= x)
+        {
+            if (!v.canMulPow2(1))
+            {
+                v.coefficients.length++;
+            }
+
+            v.mulPow2(1);
+            k += 1;
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    // This part is specified as ratio->float within the paper
+    ulong q = cast(ulong)x;
+    T z = ldexp(cast(T)q, k);
+    auto vmr = v.view;
+    vmr -= rem;
+
+    if (rem < vmr)
+    {
+        ret = z;
+    }
+    else if (rem > vmr)
+    {
+        ret = nextUp(z);
+    }
+    else if (q % 2 == 0)
+    {
+        ret = z;
+    }
+    else
+    {
+        ret = nextUp(z);
+    }
+
+    return ret;
+}
+
+///
+version(mir_bignum_test)
+unittest
+{
+    {
+        auto view = DecimalView!ulong(false, -8, BigUIntView!ulong.fromHexString("BEBC2000000011E1A3"));
+        auto coeff = (cast(BigUIntView!uint)view.coefficient).lightConst;
+        assert (algoM!double(0.0, coeff, cast(int)view.exponent) == 3.518437208883201171875E+013);
+    }
+
+    // TBD: triggers underflow
+    // {
+    //     auto view = DecimalView!ulong(false, 0, BigUIntView!ulong.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
+    //     auto coeff = (cast(BigUIntView!uint)view.coefficient).lightConst;
+    //     debug {
+    //         import std.stdio;
+    //         writefln("%s", algoM!float(0.0, coeff, cast(int)view.exponent));
+    //         writefln("%s", algoM!double(0.0, coeff, cast(int)view.exponent));
+    //     }
+    //     assert (algoM!float(0.0, coeff, cast(int)view.exponent) == float.infinity);
+    //     assert (algoM!double(0.0, coeff, cast(int)view.exponent) == 0x1.117e8e90a0ff7p+239);
+    // }
+
+    {
+        auto view = DecimalView!ulong(false, -324, BigUIntView!ulong.fromHexString("4F0CEDC95A718E"));
+        auto coeff = (cast(BigUIntView!uint)view.coefficient).lightConst;
+        assert (algoM!float(0.0, coeff, cast(int)view.exponent) == 0);
+        assert (algoM!double(0.0, coeff, cast(int)view.exponent) == 2.2250738585072014e-308);
+    }
+}
+
 @optStrategy("minsize")
 package T algoR(T, W)(T ret, scope BigUIntView!(const W) coeff, int exponent)
 {

From 4cf9d48faff68ec9588d57c3fe898d1c3f30fdcd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 10:22:25 +0400
Subject: [PATCH 401/573] get rid of algoM

---
 source/mir/bignum/low_level_view.d | 169 +----------------------------
 1 file changed, 1 insertion(+), 168 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index c87b819e..70bebb38 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -2587,174 +2587,7 @@ struct DecimalView(W)
     }
 }
 
-@optStrategy("minsize")
-package T algoM(T)(T ret, scope BigUIntView!(const(uint)) coeff, int exponent)
-{
-    import mir.bignum.integer: BigInt;
-    import mir.bignum.fixed: UInt;
-    import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
-
-    BigInt!256 u = coeff, v = 1;
-
-    if (exponent < 0)
-    {
-        auto degree = -exponent;
-
-        if (v.approxCanMulPow5(degree)) {
-            v.mulPow5(degree);
-        }
-        else
-        {
-            enum n = MaxWordPow5!uint;
-            auto growFactor = degree / n + (degree % n != 0);
-            v.coefficients.length += growFactor;
-            v.mulPow5(degree);
-        }
-
-        if (v.canMulPow2(degree))
-        {
-            v.mulPow2(degree);
-        }
-        else
-        {
-            import mir.bitop: ctlz;
-            enum n = uint.sizeof * 8;
-            auto growFactor = degree / n + (degree % n > ctlz(v.view.mostSignificant));
-            v.coefficients.length += growFactor;
-            v.mulPow2(degree);
-        }
-
-    }
-    else
-    {
-        auto degree = exponent;
-
-        if (u.approxCanMulPow5(degree))
-        {
-            u.mulPow5(degree);
-        }
-        else
-        {
-            enum n = MaxWordPow5!uint;
-            auto growFactor = degree / n + (degree % n != 0);
-
-        }
-        u.mulPow5(exponent);
-        u.mulPow2(exponent);
-    }
-
-    // XXX: is this needed?
-    // v.coefficients = v.view.normalized.coefficients;
-
-    // Adopted from Rust's rawfp implementation (TBD: do we have these already?)
-    enum EXP_BITS = (T.sizeof * 8) - T.mant_dig;
-    enum MAX_EXP = (1 << (EXP_BITS - 1)) - 1;
-    enum MIN_EXP = -MAX_EXP + 1;
-    enum MAX_EXP_INT = MAX_EXP - (T.mant_dig - 1);
-    enum MIN_EXP_INT = MIN_EXP - (T.mant_dig - 1);
-
-    // MAX_SIG
-    enum betaN = (ulong(1) << T.mant_dig) - 1;
-    // MIN_SIG
-    enum betaNMinus1 = ulong(1) << (T.mant_dig - 1);
-
-    int k = 0;
-
-    // Ugly ugly ugly, but we don't want to cast to ulong to compare
-    // as there's a potential for truncation
-    auto min_sig = cast(BigUIntView!uint)UInt!64(betaNMinus1).view;
-    auto max_sig = cast(BigUIntView!uint)UInt!64(betaN).view;
-
-    BigUIntView!uint x, rem;
-    while (true)
-    {
-        auto m = u.view.normalized.coefficients.length;
-        auto n = v.view.normalized.coefficients.length;
-        // As part of "scaling", we'll initially start with m < n as true, 
-        // so we need to have this fall back here
-        if (m < n) {
-            x.coefficients.length = m;
-        } 
-        else {
-            x.coefficients.length = (m - n) + 1;
-        }
-
-        // Division requires us to introduce
-        // a new digit in the MSB-place -- we do that here
-        rem.coefficients.length = u.view.coefficients.length + 1;
-        rem.coefficients[0 .. $ - 1] = u.view.coefficients;
-        rem.coefficients[$ - 1] = 0;
-        divm(rem, v.view, x);
-
-        // Check if we're about to underflow
-        if (k == MAX_EXP_INT)
-        {
-            if (x >= min_sig && x <= max_sig)
-            {
-                break;
-            }
-            // XXX: deal with underflow
-            assert(0, "underflow");
-        }
-
-        // Check if our exponent is out of range
-        if (k > MAX_EXP_INT)
-        {
-            return T.infinity;
-        }
-
-        if (x < min_sig)
-        {
-            if (!u.canMulPow2(1))
-            {
-                u.coefficients.length++;
-            }
-
-            u.mulPow2(1); 
-            k -= 1;
-        }
-        else if (max_sig <= x)
-        {
-            if (!v.canMulPow2(1))
-            {
-                v.coefficients.length++;
-            }
-
-            v.mulPow2(1);
-            k += 1;
-        }
-        else
-        {
-            break;
-        }
-    }
-
-    // This part is specified as ratio->float within the paper
-    ulong q = cast(ulong)x;
-    T z = ldexp(cast(T)q, k);
-    auto vmr = v.view;
-    vmr -= rem;
-
-    if (rem < vmr)
-    {
-        ret = z;
-    }
-    else if (rem > vmr)
-    {
-        ret = nextUp(z);
-    }
-    else if (q % 2 == 0)
-    {
-        ret = z;
-    }
-    else
-    {
-        ret = nextUp(z);
-    }
-
-    return ret;
-}
-
+version(none)
 ///
 version(mir_bignum_test)
 unittest

From 9310b397e14f58f3857b079abf7b9756ebef04e1 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 19:32:57 +0400
Subject: [PATCH 402/573] rework all bignum

---
 .gitignore                                    |    1 +
 meson.build                                   |    6 +-
 source/mir/algebraic_alias/ion.d              |    1 +
 source/mir/algebraic_alias/json.d             |    1 +
 source/mir/algebraic_alias/transform.d        |    1 +
 source/mir/algorithm/iteration.d              |    3 +
 source/mir/annotated.d                        |    2 +
 source/mir/base64.d                           |    6 +
 source/mir/bignum/decimal.d                   |  666 +++----
 source/mir/bignum/fixed.d                     |    2 +-
 source/mir/bignum/fp.d                        |    6 +-
 source/mir/bignum/integer.d                   |  128 +-
 source/mir/bignum/internal/dec2float.d        |  488 ++++++
 .../{dec2flt_table.d => dec2float_table.d}    |    4 +-
 source/mir/bignum/internal/phobos_kernel.d    |   17 +-
 source/mir/bignum/internal/ryu/generic_128.d  |  338 +---
 source/mir/bignum/internal/ryu/table.d        |  336 ++++
 source/mir/bignum/low_level_view.d            | 1537 ++++++-----------
 source/mir/ediff.d                            |    9 -
 source/mir/format.d                           |   34 +-
 source/mir/interpolate/utility.d              |    3 +
 source/mir/math/func/normal.d                 |   28 -
 source/mir/ndslice/field.d                    |    2 +
 source/mir/ndslice/topology.d                 |    1 +
 source/mir/numeric.d                          |    4 +
 source/mir/parse.d                            |    2 +-
 source/mir/rc/array.d                         |    1 +
 source/mir/serde.d                            |    8 +-
 source/mir/series.d                           |   17 +-
 source/mir/small_string.d                     |    3 +-
 source/mir/stdio.d                            |  265 +++
 source/mir/test.d                             |    6 +-
 source/mir/timestamp.d                        |    2 +
 33 files changed, 2107 insertions(+), 1821 deletions(-)
 create mode 100644 source/mir/bignum/internal/dec2float.d
 rename source/mir/bignum/internal/{dec2flt_table.d => dec2float_table.d} (99%)
 create mode 100644 source/mir/bignum/internal/ryu/table.d
 create mode 100644 source/mir/stdio.d

diff --git a/.gitignore b/.gitignore
index 0d683dcd..53b25574 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,4 @@ mir-algorithm-test-secure
 *.pdb
 bigint_benchmark/bigint_benchmark
 bigint_benchmark/temp.d
+mir-algorithm-test-silly
diff --git a/meson.build b/meson.build
index 91c017cf..b1090242 100644
--- a/meson.build
+++ b/meson.build
@@ -21,10 +21,12 @@ sources_list = [
     'mir/bignum/fixed',
     'mir/bignum/fp',
     'mir/bignum/integer',
-    'mir/bignum/internal/dec2flt_table',
+    'mir/bignum/internal/dec2float_table',
+    'mir/bignum/internal/dec2float',
     'mir/bignum/internal/kernel',
     'mir/bignum/internal/phobos_kernel',
     'mir/bignum/internal/ryu/generic_128',
+    'mir/bignum/internal/ryu/table',
     'mir/bignum/low_level_view',
     'mir/combinatorics/package',
     'mir/container/binaryheap',
@@ -122,7 +124,7 @@ this_dep = declare_dependency(
     dependencies: required_deps,
 )
 
-test_versions = ['mir_test', 'mir_bignum_test', 'mir_ndslice_test', 'mir_secure_memory']
+test_versions = ['mir_test', 'mir_bignum_test', 'mir_bignum_test_llv', 'mir_ndslice_test', 'mir_secure_memory']
 
 if has_cpp_headers
     install_subdir('include/',
diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
index a13b66b1..69a97d15 100644
--- a/source/mir/algebraic_alias/ion.d
+++ b/source/mir/algebraic_alias/ion.d
@@ -60,6 +60,7 @@ See also $(MREF mir,string_map) - ordered string-value associative array.
 alias IonAlgebraic = TaggedVariant!IonAlgebraicUnion;
 
 ///
+version(mir_test)
 unittest
 {
     import mir.ndslice.topology: map;
diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index ab456d45..cf24a231 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -45,6 +45,7 @@ See also $(MREF mir,string_map) - ordered string-value associative array.
 alias JsonAlgebraic = TaggedVariant!JsonAlgebraicUnion;
 
 ///
+version(mir_test)
 unittest
 {
     import mir.ndslice.topology: map;
diff --git a/source/mir/algebraic_alias/transform.d b/source/mir/algebraic_alias/transform.d
index 8e033e7e..4ffe5ee5 100644
--- a/source/mir/algebraic_alias/transform.d
+++ b/source/mir/algebraic_alias/transform.d
@@ -72,6 +72,7 @@ The function ignores leafs that can't be handled by the visiting functions.
 alias optionalTransformLeafs(visitors...) = transformLeafsImpl!(optionalVisit, naryFun!visitors);
 
 ///
+version(mir_test)
 unittest
 {
     import mir.format: text;
diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index c7577c28..3d988c61 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -558,6 +558,7 @@ sizediff_t nBitsToCount(Field, I)(Slice!(RetroIterator!(FieldIterator!(BitField!
 }
 
 ///
+version(mir_test)
 pure unittest
 {
     import mir.ndslice.allocation: bitSlice;
@@ -2568,6 +2569,7 @@ unittest
     assert(sl2[41 .. $ - 1].count!"a" == 1);
 }
 
+version(mir_test)
 unittest
 {
     import mir.ndslice.allocation: slice;
@@ -4285,6 +4287,7 @@ unittest
     assert(arr.length == 1);
 }
 
+version(mir_test)
 unittest
 {
     import mir.rc.array: RCArray;
diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index ff4e18bb..2245ae1d 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -103,6 +103,7 @@ struct Annotated(T) {
 }
 
 ///
+version(mir_test)
 unittest
 {
     auto annotations = ["annotation"];
@@ -118,6 +119,7 @@ unittest
 }
 
 ///
+version(mir_test)
 unittest
 {
     import mir.algebraic;
diff --git a/source/mir/base64.d b/source/mir/base64.d
index d7f971ee..d958f6ed 100644
--- a/source/mir/base64.d
+++ b/source/mir/base64.d
@@ -209,6 +209,7 @@ void decodeBase64(Appender)(scope const(char)[] data,
 
 /// Test decoding of data which has a length which can be
 /// cleanly decoded.
+version(mir_test)
 @safe pure unittest
 {
     {
@@ -253,6 +254,7 @@ void decodeBase64(Appender)(scope const(char)[] data,
 }
 
 /// Test decoding invalid data
+version(mir_test)
 @safe pure unittest
 {
     void testFail(const(char)[] input) @safe pure
@@ -358,6 +360,7 @@ void encodeBase64(Appender)(scope const(ubyte)[] input,
 
 /// Test encoding of data which has a length that can be cleanly
 /// encoded.
+version(mir_test)
 @safe pure unittest
 {
     // 3 bytes
@@ -387,6 +390,7 @@ void encodeBase64(Appender)(scope const(ubyte)[] input,
 
 /// Test encoding of data which has a length which CANNOT be cleanly encoded.
 /// This typically means that there's padding.
+version(mir_test)
 @safe pure unittest
 {
     // 1 byte 
@@ -417,6 +421,7 @@ void encodeBase64(Appender)(scope const(ubyte)[] input,
 }
 
 /// Test nogc encoding
+version(mir_test)
 @safe pure @nogc unittest
 {
     import mir.appender : scopedBuffer;
@@ -437,6 +442,7 @@ void encodeBase64(Appender)(scope const(ubyte)[] input,
 }
 
 /// Make sure we can decode what we encode.
+version(mir_test)
 @safe pure unittest
 {
     // Test an example string
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 3d9e8001..05e8cba5 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -65,15 +65,6 @@ struct Decimal(uint maxSize64)
         }
     }
 
-    static if (maxSize64 == 3)
-    ///
-    version(mir_test) @safe pure @nogc unittest
-    {
-        import mir.math.constant: PI;
-        Decimal!2 decimal = "3.141592653589793378e-40"; // constructor
-        assert(cast(double) decimal == double(PI) / 1e40);
-    }
-
     /++
     Constructs Decimal from the floating point number using the $(HTTPS github.com/ulfjack/ryu, Ryu algorithm).
 
@@ -85,27 +76,6 @@ struct Decimal(uint maxSize64)
         import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
         this = genericBinaryToDecimal(x);
     }
-    
-    static if (maxSize64 == 3)
-    ///
-    version(mir_bignum_test)
-    @safe pure nothrow @nogc
-    unittest
-    {
-        // float and double can be used to construct Decimal of any length
-        auto decimal64 = Decimal!1(-1.235e-7);
-        assert(decimal64.exponent == -10);
-        assert(decimal64.coefficient == -1235);
-
-        // real number may need Decimal at least length of 2
-        auto decimal128 = Decimal!2(-1.235e-7L);
-        assert(decimal128.exponent == -10);
-        assert(decimal128.coefficient == -1235);
-
-        decimal128 = Decimal!2(1234e3f);
-        assert(decimal128.exponent == 3);
-        assert(decimal128.coefficient == 1234);
-    }
 
     ///
     ref opAssign(uint rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return
@@ -140,7 +110,7 @@ struct Decimal(uint maxSize64)
         import mir.ndslice.slice: sliced;
         import mir.ndslice.topology: retro;
 
-        stringBuf buffer;
+        auto buffer = stringBuf;
         assert(thousandsSeparator != fractionSeparator);
         if (str.length && (str[0] == '+' || str[0] == '-'))
         {
@@ -193,28 +163,6 @@ struct Decimal(uint maxSize64)
         )(buffer.data, key, exponentShift);
     }
 
-    static if (maxSize64 == 3)
-    ///
-    version(mir_bignum_test) 
-    @safe pure nothrow @nogc
-    unittest
-    {
-        Decimal!3 decimal;
-        DecimalExponentKey key;
-
-        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345.678", ',', '.', key));
-        assert(cast(double) decimal == 12345.678);
-        assert(key == DecimalExponentKey.dot);
-
-        assert(decimal.fromStringWithThousandsSeparatorImpl("12,345,678", ',', '.', key, -3));
-        assert(cast(double) decimal == 12345.678);
-        assert(key == DecimalExponentKey.none);
-
-        assert(decimal.fromStringWithThousandsSeparatorImpl("021 345,678", ' ', ',', key));
-        assert(cast(double) decimal == 21345.678);
-        assert(key == DecimalExponentKey.dot);
-    }
-
     /++
     Returns: false in case of overflow or incorrect string.
     +/
@@ -238,7 +186,7 @@ struct Decimal(uint maxSize64)
             static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false)
                 pragma(inline, true);
         }
-        static if (optimize)
+        static if (optimize && false)
         {
             import mir.utility: _expect;
             static if (checkEmpty)
@@ -468,191 +416,6 @@ struct Decimal(uint maxSize64)
         }
     }
 
-    static if (maxSize64 == 3)
-    ///
-    version(mir_bignum_test) 
-    @safe pure nothrow @nogc
-    unittest
-    {
-        import mir.conv: to;
-        Decimal!3 decimal;
-        DecimalExponentKey key;
-
-        // Check precise percentate parsing
-        assert(decimal.fromStringImpl("71.7", key, -2));
-        assert(key == DecimalExponentKey.dot);
-        // The result is exact value instead of 0.7170000000000001 = 71.7 / 100
-        assert(cast(double) decimal == 0.717);
-
-        assert(decimal.fromStringImpl("+0.334e-5"w, key));
-        assert(key == DecimalExponentKey.e);
-        assert(cast(double) decimal == 0.334e-5);
-
-        assert(decimal.fromStringImpl("100_000_000"w, key));
-        assert(key == DecimalExponentKey.none);
-        assert(cast(double) decimal == 1e8);
-
-        assert(decimal.fromStringImpl("-334D-5"d, key));
-        assert(key == DecimalExponentKey.D);
-        assert(cast(double) decimal == -334e-5);
-
-        assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
-        assert(key == DecimalExponentKey.none);
-        assert(cast(double) decimal == 2482734692817364218734682973648217364981273648923423.0);
-
-        assert(decimal.fromStringImpl(".023", key));
-        assert(key == DecimalExponentKey.dot);
-        assert(cast(double) decimal == .023);
-
-        assert(decimal.fromStringImpl("0E100", key));
-        assert(key == DecimalExponentKey.E);
-        assert(cast(double) decimal == 0);
-
-        foreach (str; ["-nan", "-NaN", "-NAN"])
-        {
-            assert(decimal.fromStringImpl(str, key));
-            assert(decimal.coefficient.length > 0);
-            assert(decimal.exponent == decimal.exponent.max);
-            assert(decimal.coefficient.sign);
-            assert(key == DecimalExponentKey.nan);
-            assert(cast(double) decimal != cast(double) decimal);
-        }
-
-        foreach (str; ["inf", "Inf", "INF"])
-        {
-            assert(decimal.fromStringImpl(str, key));
-            assert(decimal.coefficient.length == 0);
-            assert(decimal.exponent == decimal.exponent.max);
-            assert(key == DecimalExponentKey.infinity);
-            assert(cast(double) decimal == double.infinity);
-        }
-
-        assert(decimal.fromStringImpl("-inf", key));
-        assert(decimal.coefficient.length == 0);
-        assert(decimal.exponent == decimal.exponent.max);
-        assert(key == DecimalExponentKey.infinity);
-        assert(cast(double) decimal == -double.infinity);
-
-        assert(!decimal.fromStringImpl("3.3.4", key));
-        assert(!decimal.fromStringImpl("3.4.", key));
-        assert(decimal.fromStringImpl("4.", key));
-        assert(!decimal.fromStringImpl(".", key));
-        assert(decimal.fromStringImpl("0.", key));
-        assert(decimal.fromStringImpl("00", key));
-        assert(!decimal.fromStringImpl("0d", key));
-    }
-
-    static if (maxSize64 == 3)
-    version(mir_bignum_test)
-    @safe pure nothrow @nogc
-    unittest
-    {
-        import mir.conv: to;
-        Decimal!1 decimal;
-        DecimalExponentKey key;
-
-        assert(decimal.fromStringImpl("1.334", key));
-        assert(key == DecimalExponentKey.dot);
-        assert(cast(double) decimal == 1.334);
-
-        assert(decimal.fromStringImpl("+0.334e-5"w, key));
-        assert(key == DecimalExponentKey.e);
-        assert(cast(double) decimal == 0.334e-5);
-
-        assert(decimal.fromStringImpl("-334D-5"d, key));
-        assert(key == DecimalExponentKey.D);
-        assert(cast(double) decimal == -334e-5);
-
-        assert(!decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
-
-        assert(decimal.fromStringImpl(".023", key));
-        assert(key == DecimalExponentKey.dot);
-        assert(cast(double) decimal == .023);
-
-        assert(decimal.fromStringImpl("0E100", key));
-        assert(key == DecimalExponentKey.E);
-        assert(cast(double) decimal == 0);
-
-        /++ Test that Issue #365 is handled properly +/
-        assert(decimal.fromStringImpl("123456.e0", key));
-        assert(key == DecimalExponentKey.e);
-        assert(cast(double) decimal == 123_456.0);
-
-        assert(decimal.fromStringImpl("123_456.e0", key));
-        assert(key == DecimalExponentKey.e);
-        assert(cast(double) decimal == 123_456.0);
-
-        assert(decimal.fromStringImpl("123456.E0", key));
-        assert(key == DecimalExponentKey.E);
-        assert(cast(double) decimal == 123_456.0);
-
-        assert(decimal.fromStringImpl("123_456.E0", key));
-        assert(key == DecimalExponentKey.E);
-        assert(cast(double) decimal == 123_456.0);
-
-        assert(decimal.fromStringImpl("123456.d0", key));
-        assert(key == DecimalExponentKey.d);
-        assert(cast(double) decimal == 123_456.0);
-
-        assert(decimal.fromStringImpl("123_456.d0", key));
-        assert(key == DecimalExponentKey.d);
-        assert(cast(double) decimal == 123_456.0);
-
-        assert(decimal.fromStringImpl("123456.D0", key));
-        assert(key == DecimalExponentKey.D);
-        assert(cast(double) decimal == 123_456.0);
-
-        assert(decimal.fromStringImpl("123_456.D0", key));
-        assert(key == DecimalExponentKey.D);
-        assert(cast(double) decimal == 123_456.0);
-
-        /++ Test invalid examples with the fix introduced for Issue #365 +/
-        assert(!decimal.fromStringImpl("123_456_.D0", key));
-        assert(!decimal.fromStringImpl("123_456.DD0", key));
-        assert(!decimal.fromStringImpl("123_456_.E0", key));
-        assert(!decimal.fromStringImpl("123_456.EE0", key));
-        assert(!decimal.fromStringImpl("123456.ED0", key));
-        assert(!decimal.fromStringImpl("123456E0D0", key));
-        assert(!decimal.fromStringImpl("123456._D0", key));
-        assert(!decimal.fromStringImpl("123456_.D0", key));
-        assert(!decimal.fromStringImpl("123456.E0D0", key));
-        assert(!decimal.fromStringImpl("123456.D0_", key));
-        assert(!decimal.fromStringImpl("123456_", key));
-
-        foreach (str; ["-nan", "-NaN", "-NAN"])
-        {
-            assert(decimal.fromStringImpl(str, key));
-            assert(decimal.coefficient.length > 0);
-            assert(decimal.exponent == decimal.exponent.max);
-            assert(decimal.coefficient.sign);
-            assert(key == DecimalExponentKey.nan);
-            assert(cast(double) decimal != cast(double) decimal);
-        }
-
-        foreach (str; ["inf", "Inf", "INF"])
-        {
-            assert(decimal.fromStringImpl(str, key));
-            assert(decimal.coefficient.length == 0);
-            assert(decimal.exponent == decimal.exponent.max);
-            assert(key == DecimalExponentKey.infinity);
-            assert(cast(double) decimal == double.infinity);
-        }
-
-        assert(decimal.fromStringImpl("-inf", key));
-        assert(decimal.coefficient.length == 0);
-        assert(decimal.exponent == decimal.exponent.max);
-        assert(key == DecimalExponentKey.infinity);
-        assert(cast(double) decimal == -double.infinity);
-
-        assert(!decimal.fromStringImpl("3.3.4", key));
-        assert(!decimal.fromStringImpl("3.4.", key));
-        assert(decimal.fromStringImpl("4.", key));
-        assert(!decimal.fromStringImpl(".", key));
-        assert(decimal.fromStringImpl("0.", key));
-        assert(decimal.fromStringImpl("00", key));
-        assert(!decimal.fromStringImpl("0d", key));
-    }
-
     private enum coefficientBufferLength = 2 + ceilLog10Exp2(coefficient.data.length * (size_t.sizeof * 8)); // including dot and sign
     private enum eDecimalLength = coefficientBufferLength + expBufferLength;
 
@@ -667,18 +430,6 @@ struct Decimal(uint maxSize64)
         return buffer.data.idup;
     }
 
-    static if (maxSize64 == 3)
-    ///
-    version(mir_bignum_test) @safe pure unittest
-    {
-        auto str = "-3.4010447314490204552169750449563978034784726557588085989975288830070948234680e-13245";
-        auto decimal = Decimal!4(str);
-        assert(decimal.toString == str, decimal.toString);
-
-        decimal = Decimal!4.init;
-        assert(decimal.toString == "0.0");
-    }
-
     ///
     void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
         if(isSomeChar!C && isMutable!C)
@@ -857,105 +608,13 @@ struct Decimal(uint maxSize64)
         w.put(buffer[$ - expLength .. $]);
     }
 
-    static if (maxSize64 == 3)
-    /// Check @nogc toString impl
-    version(mir_bignum_test) @safe pure @nogc unittest
-    {
-        import mir.format: stringBuf;
-        auto str = "5.28238923728e-876543210";
-        auto decimal = Decimal!1(str);
-        stringBuf buffer;
-        buffer << decimal;
-        assert(buffer.data == str);
-    }
-
     /++
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T, bool wordNormalized = true)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-
-        enum optimize = maxSize64 == 1 && size_t.sizeof == 8 && T.mant_dig < 64;
-
-        version(LDC)
-        {
-            static if (optimize || wordNormalized)
-                pragma(inline, true);
-        }
-
-        static if (optimize)
-        {
-            import mir.bignum.fixed: UInt;
-            import mir.bignum.fp: Fp, extendedMul;
-            import mir.bignum.internal.dec2flt_table;
-            import mir.bignum.low_level_view: MaxWordPow5, MaxFpPow5;
-            import mir.math.common: floor;
-            import mir.utility: _expect;
-
-            T ret = 0;
-            size_t length = coefficient.length;
-
-
-            static if (!wordNormalized)
-            {
-                if (coefficient.data[0] == 0)
-                    length = 0;
-            }
-
-            if (_expect(exponent == exponent.max, false))
-            {
-                ret = length ? T.nan : T.infinity;
-                goto R;
-            }
-
-            static if (!nonZero)
-                if (length == 0)
-                    goto R;
-            enum S = 9;
-
-            Fp!64 load(typeof(exponent) e)
-            {
-                auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
-                auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
-                return Fp!64(false, p10exp, UInt!64(p10coeff));
-            }
-            {
-                auto expSign = exponent < 0;
-                if (_expect((expSign ? -exponent : exponent) >>> S == 0, true))
-                {
-                    enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
-                    enum ulong half = (1UL << (64 - T.mant_dig - 1));
-                    enum ulong bound = ulong(1) << T.mant_dig;
-
-                    auto c = Fp!64(UInt!64(coefficient.data[0]));
-                    auto z = c.extendedMul(load(exponent));
-                    ret = cast(T) z;
-                    long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
-                    if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > 3 * expSign, true))
-                        goto R;
-                    if (!expSign && exponent <= MaxWordPow5!ulong || exponent == 0)
-                        goto R;
-                    if (expSign && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
-                    {
-                        auto e = load(-exponent);
-                        ret =  c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
-                        goto R;
-                    }
-                    ret = algoR!T(ret, view.coefficient, cast(int) exponent);
-                    goto R;
-                }
-                ret = expSign ? 0 : T.infinity;
-            }
-        R:
-            if (coefficient.sign)
-                ret = -ret;
-            return ret;
-        }
-        else
-        {
-            return view.opCast!(T, wordNormalized, nonZero);
-        }
+        return view.opCast!(T, wordNormalized);
     }
 
     ///
@@ -993,7 +652,7 @@ struct Decimal(uint maxSize64)
         }
         else
         {
-            rhsCopy.copyFrom(rhs.coefficient.view);
+            rhsCopy.copyFrom(rhs.coefficient.coefficients, rhs.coefficient.sign);
             rhsCopy.mulPow5(-expDiff);
             rhsCopy.opOpAssign!"<<"(-expDiff);
             rhsView = rhsCopy.view;
@@ -1001,33 +660,6 @@ struct Decimal(uint maxSize64)
         coefficient.opOpAssign!op(rhsView);
         return this;
     }
-
-    static if (maxSize64 == 3)
-    ///
-    version(mir_bignum_test) @safe pure @nogc unittest
-    {
-        auto a = Decimal!1("777.7");
-        auto b = Decimal!1("777");
-        import mir.format;
-        assert(stringBuf() << cast(double)a - cast(double)b << getData == "0.7000000000000455");
-        a -= b;
-        assert(stringBuf() << a << getData == "0.7");
-
-        a = Decimal!1("-777.7");
-        b = Decimal!1("777");
-        a += b;
-        assert(stringBuf() << a << getData == "-0.7");
-
-        a = Decimal!1("777.7");
-        b = Decimal!1("-777");
-        a += b;
-        assert(stringBuf() << a << getData == "0.7");
-
-        a = Decimal!1("777");
-        b = Decimal!1("777.7");
-        a -= b;
-        assert(stringBuf() << a << getData == "-0.7");
-    }
 }
 
 ///
@@ -1036,7 +668,7 @@ version(mir_bignum_test)
 unittest
 {
     import mir.conv: to;
-    Decimal!3 decimal;
+    Decimal!256 decimal = void;
     DecimalExponentKey key;
 
     assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
@@ -1044,7 +676,6 @@ unittest
     assert(key == DecimalExponentKey.e);
 }
 
-
 ///
 version(mir_bignum_test) 
 @safe pure nothrow @nogc
@@ -1076,15 +707,44 @@ unittest
     assert(cast(double) decimal.coefficient == 0);
 }
 
-deprecated("use decimal.fromStringImpl insteade")
-@trusted @nogc pure nothrow
-bool parseDecimal(uint maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key)
-    if (isSomeChar!C)
+///
+version(mir_bignum_test) @safe pure @nogc unittest
 {
-    return decimal.fromStringImpl(str, key);
+    auto a = Decimal!1("777.7");
+    auto b = Decimal!1("777");
+    import mir.format;
+    assert(stringBuf() << cast(double)a - cast(double)b << getData == "0.7000000000000455");
+    a -= b;
+    assert(stringBuf() << a << getData == "0.7");
+
+    a = Decimal!1("-777.7");
+    b = Decimal!1("777");
+    a += b;
+    assert(stringBuf() << a << getData == "-0.7");
+
+    a = Decimal!1("777.7");
+    b = Decimal!1("-777");
+    a += b;
+    assert(stringBuf() << a << getData == "0.7");
+
+    a = Decimal!1("777");
+    b = Decimal!1("777.7");
+    a -= b;
+    assert(stringBuf() << a << getData == "-0.7");
 }
 
+/// Check @nogc toString impl
+version(mir_bignum_test) @safe pure @nogc unittest
+{
+    import mir.format: stringBuf;
+    auto str = "5.28238923728e-876543210";
+    auto decimal = Decimal!1(str);
+    auto buffer = stringBuf;
+    buffer << decimal;
+    assert(buffer.data == str);
+}
 
+///
 version(mir_bignum_test)
 @safe pure @nogc unittest
 {
@@ -1094,4 +754,250 @@ version(mir_bignum_test)
     assert(i.coefficient.view.unsigned.coefficients.length == 0);
     assert(i.coefficient.view == 0L);
     assert(cast(long) i.coefficient == 0);
+    assert(i.coefficient.sign);
+}
+
+///
+version(mir_bignum_test) @safe pure unittest
+{
+    auto str = "-3.4010447314490204552169750449563978034784726557588085989975288830070948234680e-13245";
+    auto decimal = Decimal!4(str);
+    assert(decimal.toString == str, decimal.toString);
+
+    decimal = Decimal!4.init;
+    assert(decimal.toString == "0.0");
+}
+
+///
+version(mir_bignum_test) 
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.conv: to;
+    Decimal!3 decimal;
+    DecimalExponentKey key;
+
+    // Check precise percentate parsing
+    assert(decimal.fromStringImpl("71.7", key, -2));
+    assert(key == DecimalExponentKey.dot);
+    // The result is exact value instead of 0.7170000000000001 = 71.7 / 100
+    assert(cast(double) decimal == 0.717);
+
+    assert(decimal.fromStringImpl("+0.334e-5"w, key));
+    assert(key == DecimalExponentKey.e);
+    assert(cast(double) decimal == 0.334e-5);
+
+    assert(decimal.fromStringImpl("100_000_000"w, key));
+    assert(key == DecimalExponentKey.none);
+    assert(cast(double) decimal == 1e8);
+
+    assert(decimal.fromStringImpl("-334D-5"d, key));
+    assert(key == DecimalExponentKey.D);
+    assert(cast(double) decimal == -334e-5);
+
+    assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
+    assert(key == DecimalExponentKey.none);
+    assert(cast(double) decimal == 2482734692817364218734682973648217364981273648923423.0);
+
+    assert(decimal.fromStringImpl(".023", key));
+    assert(key == DecimalExponentKey.dot);
+    assert(cast(double) decimal == .023);
+
+    assert(decimal.fromStringImpl("0E100", key));
+    assert(key == DecimalExponentKey.E);
+    assert(cast(double) decimal == 0);
+
+    foreach (str; ["-nan", "-NaN", "-NAN"])
+    {
+        assert(decimal.fromStringImpl(str, key));
+        assert(decimal.coefficient.length > 0);
+        assert(decimal.exponent == decimal.exponent.max);
+        assert(decimal.coefficient.sign);
+        assert(key == DecimalExponentKey.nan);
+        assert(cast(double) decimal != cast(double) decimal);
+    }
+
+    foreach (str; ["inf", "Inf", "INF"])
+    {
+        assert(decimal.fromStringImpl(str, key));
+        assert(decimal.coefficient.length == 0);
+        assert(decimal.exponent == decimal.exponent.max);
+        assert(key == DecimalExponentKey.infinity);
+        assert(cast(double) decimal == double.infinity);
+    }
+
+    assert(decimal.fromStringImpl("-inf", key));
+    assert(decimal.coefficient.length == 0);
+    assert(decimal.exponent == decimal.exponent.max);
+    assert(key == DecimalExponentKey.infinity);
+    assert(cast(double) decimal == -double.infinity);
+
+    assert(!decimal.fromStringImpl("3.3.4", key));
+    assert(!decimal.fromStringImpl("3.4.", key));
+    assert(decimal.fromStringImpl("4.", key));
+    assert(!decimal.fromStringImpl(".", key));
+    assert(decimal.fromStringImpl("0.", key));
+    assert(decimal.fromStringImpl("00", key));
+    assert(!decimal.fromStringImpl("0d", key));
+}
+
+version(mir_bignum_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.conv: to;
+    Decimal!1 decimal;
+    DecimalExponentKey key;
+
+    assert(decimal.fromStringImpl("1.334", key));
+    assert(key == DecimalExponentKey.dot);
+    assert(cast(double) decimal == 1.334);
+
+    assert(decimal.fromStringImpl("+0.334e-5"w, key));
+    assert(key == DecimalExponentKey.e);
+    assert(cast(double) decimal == 0.334e-5);
+
+    assert(decimal.fromStringImpl("-334D-5"d, key));
+    assert(key == DecimalExponentKey.D);
+    assert(cast(double) decimal == -334e-5);
+
+    assert(!decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
+
+    assert(decimal.fromStringImpl(".023", key));
+    assert(key == DecimalExponentKey.dot);
+    assert(cast(double) decimal == .023);
+
+    assert(decimal.fromStringImpl("0E100", key));
+    assert(key == DecimalExponentKey.E);
+    assert(cast(double) decimal == 0);
+
+    /++ Test that Issue #365 is handled properly +/
+    assert(decimal.fromStringImpl("123456.e0", key));
+    assert(key == DecimalExponentKey.e);
+    assert(cast(double) decimal == 123_456.0);
+
+    assert(decimal.fromStringImpl("123_456.e0", key));
+    assert(key == DecimalExponentKey.e);
+    assert(cast(double) decimal == 123_456.0);
+
+    assert(decimal.fromStringImpl("123456.E0", key));
+    assert(key == DecimalExponentKey.E);
+    assert(cast(double) decimal == 123_456.0);
+
+    assert(decimal.fromStringImpl("123_456.E0", key));
+    assert(key == DecimalExponentKey.E);
+    assert(cast(double) decimal == 123_456.0);
+
+    assert(decimal.fromStringImpl("123456.d0", key));
+    assert(key == DecimalExponentKey.d);
+    assert(cast(double) decimal == 123_456.0);
+
+    assert(decimal.fromStringImpl("123_456.d0", key));
+    assert(key == DecimalExponentKey.d);
+    assert(cast(double) decimal == 123_456.0);
+
+    assert(decimal.fromStringImpl("123456.D0", key));
+    assert(key == DecimalExponentKey.D);
+    assert(cast(double) decimal == 123_456.0);
+
+    assert(decimal.fromStringImpl("123_456.D0", key));
+    assert(key == DecimalExponentKey.D);
+    assert(cast(double) decimal == 123_456.0);
+
+    /++ Test invalid examples with the fix introduced for Issue #365 +/
+    assert(!decimal.fromStringImpl("123_456_.D0", key));
+    assert(!decimal.fromStringImpl("123_456.DD0", key));
+    assert(!decimal.fromStringImpl("123_456_.E0", key));
+    assert(!decimal.fromStringImpl("123_456.EE0", key));
+    assert(!decimal.fromStringImpl("123456.ED0", key));
+    assert(!decimal.fromStringImpl("123456E0D0", key));
+    assert(!decimal.fromStringImpl("123456._D0", key));
+    assert(!decimal.fromStringImpl("123456_.D0", key));
+    assert(!decimal.fromStringImpl("123456.E0D0", key));
+    assert(!decimal.fromStringImpl("123456.D0_", key));
+    assert(!decimal.fromStringImpl("123456_", key));
+
+    foreach (str; ["-nan", "-NaN", "-NAN"])
+    {
+        assert(decimal.fromStringImpl(str, key));
+        assert(decimal.coefficient.length > 0);
+        assert(decimal.exponent == decimal.exponent.max);
+        assert(decimal.coefficient.sign);
+        assert(key == DecimalExponentKey.nan);
+        assert(cast(double) decimal != cast(double) decimal);
+    }
+
+    foreach (str; ["inf", "Inf", "INF"])
+    {
+        assert(decimal.fromStringImpl(str, key));
+        assert(decimal.coefficient.length == 0);
+        assert(decimal.exponent == decimal.exponent.max);
+        assert(key == DecimalExponentKey.infinity);
+        assert(cast(double) decimal == double.infinity);
+    }
+
+    assert(decimal.fromStringImpl("-inf", key));
+    assert(decimal.coefficient.length == 0);
+    assert(decimal.exponent == decimal.exponent.max);
+    assert(key == DecimalExponentKey.infinity);
+    assert(cast(double) decimal == -double.infinity);
+
+    assert(!decimal.fromStringImpl("3.3.4", key));
+    assert(!decimal.fromStringImpl("3.4.", key));
+    assert(decimal.fromStringImpl("4.", key));
+    assert(!decimal.fromStringImpl(".", key));
+    assert(decimal.fromStringImpl("0.", key));
+    assert(decimal.fromStringImpl("00", key));
+    assert(!decimal.fromStringImpl("0d", key));
+}
+
+///
+version(mir_bignum_test)
+@safe pure @nogc unittest
+{
+    import mir.math.constant: PI;
+    Decimal!2 decimal = "3.141592653589793378e-40"; // constructor
+    assert(cast(double) decimal == double(PI) / 1e40);
+}
+
+
+///
+version(mir_bignum_test)
+@safe pure nothrow @nogc
+unittest
+{
+    // float and double can be used to construct Decimal of any length
+    auto decimal64 = Decimal!1(-1.235e-7);
+    assert(decimal64.exponent == -10);
+    assert(decimal64.coefficient == -1235);
+
+    // real number may need Decimal at least length of 2
+    auto decimal128 = Decimal!2(-1.235e-7L);
+    assert(decimal128.exponent == -10);
+    assert(decimal128.coefficient == -1235);
+
+    decimal128 = Decimal!2(1234e3f);
+    assert(decimal128.exponent == 3);
+    assert(decimal128.coefficient == 1234);
+}
+
+///
+version(mir_bignum_test) 
+@safe pure nothrow @nogc
+unittest
+{
+    Decimal!3 decimal;
+    DecimalExponentKey key;
+
+    assert(decimal.fromStringWithThousandsSeparatorImpl("12,345.678", ',', '.', key));
+    assert(cast(double) decimal == 12345.678);
+    assert(key == DecimalExponentKey.dot);
+
+    assert(decimal.fromStringWithThousandsSeparatorImpl("12,345,678", ',', '.', key, -3));
+    assert(cast(double) decimal == 12345.678);
+    assert(key == DecimalExponentKey.none);
+
+    assert(decimal.fromStringWithThousandsSeparatorImpl("021 345,678", ' ', ',', key));
+    assert(cast(double) decimal == 21345.678);
+    assert(key == DecimalExponentKey.dot);
 }
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index ea1c2f2b..660311a3 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -170,7 +170,7 @@ struct UInt(size_t size)
         import mir.format: stringBuf;
         auto str = "34010447314490204552169750449563978034784726557588085989975288830070948234680";
         auto integer = UInt!256(str);
-        stringBuf buffer;
+        auto buffer = stringBuf;
         buffer << integer;
         assert(buffer.data == str);
     }
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 007ccfd9..61338fdc 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -18,8 +18,6 @@ Software floating point number.
 
 Params:
     size = coefficient size in bits
-
-Note: the implementation doesn't support NaN and Infinity values.
 +/
 struct Fp(uint size)
     if (size % (uint.sizeof * 8) == 0 && size >= (uint.sizeof * 8))
@@ -681,7 +679,7 @@ template fp_log2(T)
 }
 
 ///
-version(mir_test)
+version(mir_bignum_test)
 @safe pure nothrow @nogc
 unittest
 {
@@ -705,7 +703,7 @@ template fp_log(T)
 }
 
 ///
-version(mir_test)
+version(mir_bignum_test)
 @safe pure nothrow @nogc
 unittest
 {
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 83bd5aa7..010ef964 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -38,13 +38,29 @@ struct BigInt(uint maxSize64)
     this(uint N)(size_t[N] data)
         if (N <= this.data.length)
     {
-        sign = false;
-        version(LittleEndian)
-            this.data[0 .. N] = data;
+        static if (data.length == 0)
+        {
+            sign = false;
+            length = 0;
+        }
+        static if (data.length == 1)
+        {
+            this(data[0]);
+        }
         else
-            this.data[$ - N .. $] = data;
-        length = data.length;
-        normalize;
+        static if (data.length == 2)
+        {
+            this.data[0] = data[0];
+            this.data[1] = data[1];
+            this.length = data[1] ? 2 : data[0] != 0;
+        }
+        else
+        {
+            sign = false;
+            this.data[0 .. N] = data;
+            length = data.length;
+            normalize;
+        }
     }
 
     ///
@@ -108,22 +124,37 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    ref opAssign(ulong data) return
+    ref opAssign(ulong data) return scope
     {
-        this.sign = 0;
-        static if (size_t.sizeof == ulong.sizeof)
-        {
-            length = 1;
-            view.coefficients[0] = data;
-        }
-        else
-        {
-            length = 2;
-            auto d = view.coefficients;
-            d[0] = cast(uint) data;
-            d[1] = cast(uint) (data >> 32);
-        }
-        normalize;
+        __ctor(data);
+        return this;
+    }
+
+    ///
+    ref opAssign(long data) return scope
+    {
+        __ctor(data);
+        return this;
+    }
+
+    ///
+    ref opAssign(uint data) return scope
+    {
+        __ctor(data);
+        return this;
+    }
+
+    ///
+    ref opAssign(int data) return scope
+    {
+        __ctor(data);
+        return this;
+    }
+
+    ///
+    ref opAssign(uint rhsSize)(UInt!rhsSize data) return scope
+    {
+        __ctor(data);
         return this;
     }
 
@@ -164,7 +195,7 @@ struct BigInt(uint maxSize64)
         scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
-        auto work = BigIntView!size_t(data[]); 
+        auto work = data[].BigIntView!size_t; 
         if (work.fromStringImpl(str))
         {
             length = cast(uint) work.coefficients.length;
@@ -703,6 +734,7 @@ struct BigInt(uint maxSize64)
     ///
     bool mulPow5(size_t degree)
     {
+        import mir.bignum.internal.dec2float: MaxWordPow5;
         // assert(approxCanMulPow5(degree));
         if (length == 0)
             return false;
@@ -815,10 +847,10 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-        return view.opCast!(T, wordNormalized, nonZero);
+        return view.opCast!T;
     }
 
     ///
@@ -831,40 +863,37 @@ struct BigInt(uint maxSize64)
     /++
     Returns: overflow flag
     +/
-    bool copyFrom(W)(BigIntView!(const W) view)
+    bool copyFrom(W)(scope const(W)[] coefficients, bool sign = false)
+        if (__traits(isUnsigned, W))
     {
         static if (W.sizeof > size_t.sizeof)
         {
-            return this.copyFrom(cast(BigIntView!(const size_t))view);
+            return this.copyFrom(cast(BigIntView!(const size_t))view, sign);
         }
         else
         {
-            this.sign = view.sign;
-            auto lhs = BigUIntView!W(cast(W[])data);
-            auto rhs = view;
-            auto overflow = lhs.coefficients.length < rhs.coefficients.length;
-            auto n = overflow ? lhs.coefficients.length : rhs.coefficients.length;
-            lhs.coefficients[0 .. n] = rhs.coefficients[0 .. n];
+            this.sign = sign;
+            auto dest = cast(W[])data;
+            auto overflow = dest.length < coefficients.length;
+            auto n = overflow ? dest.length : coefficients.length;
             this.length = cast(uint)(n / (size_t.sizeof / W.sizeof));
-            if (auto tail = n % (size_t.sizeof / W.sizeof))
+            dest[0 .. n] = coefficients[0 .. n];
+            static if (size_t.sizeof / W.sizeof > 1)
             {
-                this.length++;
-                auto shift = ((size_t.sizeof / W.sizeof) - tail) * (W.sizeof * 8);
-                auto value = this.coefficients[$ - 1];
-                value <<= shift;
-                value >>= shift;
-                this.coefficients[$ - 1] = value;
+                if (auto tail = n % (size_t.sizeof / W.sizeof))
+                {
+                    this.length++;
+                    auto shift = ((size_t.sizeof / W.sizeof) - tail) * (W.sizeof * 8);
+                    auto value = this.coefficients[$ - 1];
+                    value <<= shift;
+                    value >>= shift;
+                    this.coefficients[$ - 1] = value;
+                }
             }
             return overflow;
         }
     }
 
-    /// ditto
-    bool copyFrom(W)(BigUIntView!(const W) view)
-    {
-        return this.copyFrom(BigIntView!(const W)(view));
-    }
-
     ///
     immutable(C)[] toString(C = char)() const @safe pure nothrow
         if(isSomeChar!C && isMutable!C)
@@ -908,7 +937,7 @@ struct BigInt(uint maxSize64)
         import mir.format;
         auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
         auto integer = BigInt!4(str);
-        stringBuf buffer;
+        auto buffer = stringBuf;
         buffer << integer;
         assert(buffer.data == str);
     }
@@ -1027,11 +1056,11 @@ unittest
     assert(d == c);
     assert(c != b);
     b.sign = true;
-    assert(!c.copyFrom(b.view));
+    assert(!c.copyFrom(b.coefficients, b.sign));
     assert(c == b);
     b >>= 18;
     auto bView = cast(BigIntView!ushort)b.view;
-    assert(!c.copyFrom(bView.topLeastSignificantPart(bView.unsigned.coefficients.length - 1)));
+    assert(!c.copyFrom(bView.coefficients[0 .. $ - 1], bView.sign));
     assert(c == b);
 }
 
@@ -1039,6 +1068,7 @@ version(mir_bignum_test)
 @safe pure @nogc unittest
 {
     BigInt!4 i = "-0";
-    assert(i.view.coefficients.length == 0);
+    assert(i.coefficients.length == 0);
+    assert(!i.sign);
     assert(cast(long) i == 0);
 }
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
new file mode 100644
index 00000000..4f0d66f0
--- /dev/null
+++ b/source/mir/bignum/internal/dec2float.d
@@ -0,0 +1,488 @@
+module mir.bignum.internal.dec2float;
+
+version(LDC) import ldc.attributes: optStrategy;
+else struct optStrategy { string opt; }
+
+template MaxWordPow5(T)
+{
+    static if (is(T == ubyte))
+        enum MaxWordPow5 = 3;
+    else
+    static if (is(T == ushort))
+        enum MaxWordPow5 = 6;
+    else
+    static if (is(T == uint))
+        enum MaxWordPow5 = 13;
+    else
+    static if (is(T == ulong))
+        enum MaxWordPow5 = 27;
+    else
+        static assert(0);
+}
+
+template MaxFpPow5(T)
+{
+    static if (T.mant_dig == 24)
+        enum MaxFpPow5 = 6;
+    else
+    static if (T.mant_dig == 53)
+        enum MaxFpPow5 = 10;
+    else
+    static if (T.mant_dig == 64)
+        enum MaxFpPow5 = 27;
+    else
+    static if (T.mant_dig == 113)
+        enum MaxFpPow5 = 48;
+    else
+        static assert(0, "floating point format isn't supported");
+}
+
+@safe pure nothrow @nogc:
+
+alias decimalTo(T : float) = decimalToFloat32;
+alias decimalTo(T : double) = decimalToFloat64;
+alias decimalTo(T : real) = decimalToReal;
+
+private float decimalToFloat32(scope const size_t[] coefficients, long exponent)
+{
+    pragma(inline, false);
+    return decimalToFloatImpl!float(coefficients, exponent);
+}
+
+private double decimalToFloat64(scope const size_t[] coefficients, long exponent)
+{
+    pragma(inline, false);
+    return decimalToFloatImpl!double(coefficients, exponent);
+}
+
+private real decimalToReal(scope const size_t[] coefficients, long exponent)
+{
+    pragma(inline, real.mant_dig == double.mant_dig);
+    static if (real.mant_dig == double.mant_dig)
+        return decimalToFloat64(coefficients, exponent);
+    else
+        return decimalToFloatImpl!real(coefficients, exponent);
+}
+
+private float decimalToFloat32(scope const size_t[] coefficients)
+{
+    pragma(inline, false);
+    return decimalToFloatImpl!float(coefficients);
+}
+
+private double decimalToFloat64(scope const size_t[] coefficients)
+{
+    pragma(inline, false);
+    return decimalToFloatImpl!double(coefficients);
+}
+
+private real decimalToReal(scope const size_t[] coefficients)
+{
+    pragma(inline, real.mant_dig == double.mant_dig);
+    static if (real.mant_dig == double.mant_dig)
+        return decimalToFloat64(coefficients);
+    else
+        return decimalToFloatImpl!real(coefficients);
+}
+
+T decimalToFloat(T)(ulong coefficient, long exponent)
+    if (is(T == real))
+{
+    version(LDC)
+        pragma(inline, true);
+    static if (T.mant_dig < 64)
+    {
+        return decimalToFloat!double(coefficient, exponent);
+    }
+    else
+    {
+        ulong[1] coefficients = [coefficient];
+        return decimalTo!real(approx, coefficients[0 .. !!coefficient], exponent);
+    }
+}
+
+T decimalToFloat(T)(ulong coefficient, long exponent)
+    if (is(T == float) || is(T == double))
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp, extendedMul;
+    import mir.utility: _expect;
+
+    version(LDC)
+        pragma(inline, true);
+
+    enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
+    enum ulong half = (1UL << (64 - T.mant_dig - 1));
+    enum ulong bound = ulong(1) << T.mant_dig;
+
+    auto expSign = exponent < 0;
+    if (_expect((expSign ? -exponent : exponent) >>> dec_S == 0, true))
+    {
+        auto c = Fp!64(UInt!64(coefficient));
+        auto z = c.extendedMul(load64(exponent));
+        auto approx = cast(T) z;
+        long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
+        if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > 3 * expSign, true))
+            return approx;
+        assert(coefficient);
+        if (!expSign && exponent <= MaxWordPow5!ulong || exponent == 0)
+            return approx;
+        if (expSign && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
+        {
+            auto e = load64(-exponent);
+            return c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
+        }
+        ulong[1] coefficients = [coefficient];
+        return decimalToFloatFallback(approx, coefficients, cast(int) exponent);
+    }
+    return expSign ? 0 : exponent != exponent.max ? T.infinity : coefficient ? T.nan : T.infinity;
+}
+
+private T decimalToFloatImpl(T)(scope const size_t[] coefficients)
+    @safe
+    if ((is(T == float) || is(T == double) || is(T == real)))
+    in (coefficients.length == 0 || coefficients[$ - 1])
+{
+    version(LDC)
+        pragma(inline, true);
+
+    enum md = T.mant_dig;
+    enum b = size_t.sizeof * 8;
+    enum n = md / b + (md % b != 0);
+    enum s = n * b;
+
+    return coefficients.length ? coefficients.decimalToFp!(s, s - md).opCast!(T, true) : 0;
+}
+
+private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent)
+    @safe
+    if ((is(T == float) || is(T == double) || is(T == real)))
+    in (coefficients.length == 0 || coefficients[$ - 1])
+{
+    import mir.bignum.low_level_view: BigUIntView;
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp, extendedMul;
+    import mir.math.common: floor;
+    import mir.utility: _expect;
+
+    static if (T.mant_dig < 64)
+    {
+        enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
+        enum ulong half = (1UL << (64 - T.mant_dig - 1));
+        enum ulong bound = ulong(1) << T.mant_dig;
+
+        if (coefficients.length == 1)
+            return decimalToFloat!T(coefficients[0], exponent);
+        if (coefficients.length < 1)
+            return exponent == exponent.max ? T.infinity : 0;
+        auto expSign = exponent < 0;
+        if (_expect((expSign ? -exponent : exponent) >>> dec_S == 0, true))
+        {
+            auto c = coefficients.decimalToFp!64;
+            auto z = c.extendedMul(load64(exponent));
+            auto approx = cast(T) z;
+            auto slop = (coefficients.length > (ulong.sizeof / size_t.sizeof)) + 3 * expSign;
+            long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
+            if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
+                return approx;
+            if (slop == 0 && exponent <= MaxWordPow5!ulong || exponent == 0)
+                return approx;
+            if (slop == 3 && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
+            {
+                auto e = load64(-exponent);
+                approx =  c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
+                return approx;
+            }
+            return decimalToFloatFallback(approx, coefficients, cast(int) exponent);
+        }
+        return expSign ? 0 : exponent != exponent.max ? T.infinity : T.nan;
+    }
+    else
+    {
+        if (exponent == 0)
+            return decimalTo!T(coeff);
+
+        if (coefficients.length == 0)
+            return exponent == exponent.max ? T.infinity : 0;
+
+        auto expSign = exponent < 0;
+        ulong exp = exponent;
+        exp = expSign ? -exp : exp;
+        if (exp >= 5000)
+        {
+            return expSign ? 0 : exponent != exponent.max ? T.infinity : T.nan;
+        }
+        long index = exp & 0x1FF;
+        bool gotoAlgoR;
+        auto c = load128(expSign ? -index : index);
+        {
+            exp >>= dec_S;
+            gotoAlgoR = exp != 0;
+            if (_expect(gotoAlgoR, false))
+            {
+                auto v = load128(expSign ? -dec_P : dec_P);
+                do
+                {
+                    if (exp & 1)
+                        c *= v;
+                    exp >>>= 1;
+                    if (exp == 0)
+                        break;
+                    v *= v;
+                }
+                while(true);
+            }
+        }
+        auto z = coefficients.decimalToFp!64.extendedMul(c);
+        auto ret = cast(T) z;
+        if (!gotoAlgoR)
+        {
+            static if (T.mant_dig > 64)
+                enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
+            else
+                enum ulong mask = ulong.max;
+            enum ulong half = (1UL << (128 - T.mant_dig - 1));
+            enum UInt!128 bound = UInt!128(1) << T.mant_dig;
+
+            auto slop = (coefficients.length > (ulong.sizeof * 2 / size_t.sizeof)) + 3 * expSign;
+            long bitsDiff = (cast(ulong) z.opCast!(Fp!128).coefficient & mask) - half;
+            if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
+                return ret;
+            if (slop == 0 && exponent <= 55 || exponent == 0)
+                return ret;
+            if (slop == 3 && MaxFpPow5!T >= -exponent && c.coefficient < bound)
+            {
+                auto e = load128(-exponent);
+                return c.opCast!(T, true) / cast(T) e;
+            }
+        }
+        return decimalToFloatFallback(ret, coefficients, cast(int) exponent);
+    }
+}
+
+@optStrategy("minsize")
+private T decimalToFloatFallback(T)(T ret, scope const size_t[] coeff, int exponent)
+    if ((is(T == float) || is(T == double) || is(T == real)))
+    in (0 <= ret && ret <= T.infinity)
+{
+    pragma(inline, false);
+
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.integer: BigInt;
+    import mir.math.common: floor;
+    import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
+    import mir.utility: _expect;
+
+    BigInt!256 x = void, y = void; // max value is 2^(2^14)-1
+
+    if (exponent == 0)
+        return decimalTo!T(coeff);
+
+    // if no overflow
+    if (exponent > 0 && !x.copyFrom(coeff) && !x.mulPow5(exponent))
+        return x.coefficients.decimalTo!T.ldexp(exponent);
+
+    do
+    {
+        if (ret < ret.min_normal)
+            break;
+        int k;
+        auto m0 = frexp(ret, k);
+        k -= T.mant_dig;
+        static if (T.mant_dig <= 64)
+        {
+            enum p2 = T(2) ^^ T.mant_dig;
+            auto m = UInt!64(cast(ulong) (m0 * p2));
+        }
+        else
+        {
+            enum p2h = T(2) ^^ (T.mant_dig - 64);
+            enum p2l = T(2) ^^ 64;
+            m0 *= p2h;
+            auto mhf = floor(m0);
+            auto mh = cast(ulong) mhf;
+            m0 -= mhf;
+            m0 *= p2l;
+            auto ml = cast(ulong) m0;
+            auto m = UInt!128(mh);
+            m <<= 64;
+            m |= UInt!128(ml);
+        }
+        auto mtz = m.cttz;
+        if (mtz != m.sizeof * 8)
+        {
+            m >>= mtz;
+            k += mtz;
+        }
+
+        if (x.copyFrom(coeff)) // if overflow
+            break;
+        y = m;
+        y.mulPow5(-exponent);
+        auto shift = k - exponent;
+        (shift >= 0  ? y : x) <<= shift >= 0 ? shift : -shift;
+        x -= y;
+        if (x.length == 0)
+            break;
+        x <<= 1;
+        x *= m;
+        auto cond = mtz == T.mant_dig - 1 && x.sign;
+        auto cmp = x.view.unsigned.opCmp(y.view.unsigned);
+        if (cmp < 0)
+        {
+            if (!cond)
+                break;
+            x <<= 1;
+            if (x.view.unsigned <= y.view.unsigned)
+                break;
+        }
+        else
+        if (!cmp && !cond && !mtz)
+            break;
+        ret = x.sign ? nextDown(ret) : nextUp(ret);
+        if (ret == 0)
+            break;
+    }
+    while (T.mant_dig >= 64 && exponent < -512);
+    return ret;
+}
+
+auto decimalToFp(uint coefficientSize, uint internalRoundLastBits = 0)
+    (scope const(size_t)[] coefficients)
+    if (internalRoundLastBits < size_t.sizeof * 8)
+    in (coefficients.length)
+    in (coefficients[$ - 1])
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp;
+    import mir.bitop: ctlz;
+    import mir.utility: _expect;
+
+    version (LDC)
+        pragma(inline, internalRoundLastBits != 0);
+
+    Fp!coefficientSize ret;
+
+    assert(coefficients.length);
+    enum N = ret.coefficient.data.length;
+    sizediff_t size = coefficients.length * (size_t.sizeof * 8);
+    sizediff_t expShift = size - coefficientSize;
+    ret.exponent = expShift;
+    if (_expect(expShift <= 0, true))
+    {
+        static if (N == 1)
+        {
+            ret.coefficient.data[0] = coefficients[$ - 1];
+        }
+        else
+        {
+            ret.coefficient.data[$ - coefficients.length .. $] = coefficients;
+        }
+        auto c = cast(uint) ctlz(ret.coefficient.view.coefficients[$ - 1]);
+        ret.exponent -= c;
+        ret.coefficient = ret.coefficient.smallLeftShift(c);
+    }
+    else
+    {
+        UInt!(coefficientSize + size_t.sizeof * 8) holder;
+
+        static if (N == 1)
+        {
+            holder.data[0] = coefficients[$ - 2];
+            holder.data[1] = coefficients[$ - 1];
+        }
+        else
+        {
+            import mir.utility: min;
+            auto minLength = min(coefficients.length, holder.data.length);
+            holder.data[$ - minLength .. $] = coefficients[$ - minLength .. $];
+        }
+
+        auto c = cast(uint) ctlz(holder.data[$ - 1]);
+        ret.exponent -= c;
+        holder = holder.smallLeftShift(c);
+        ret.coefficient = holder.toSize!(coefficientSize, false);
+        auto tail = holder.data[0];
+
+        bool nonZeroTail()
+        {
+            while(_expect(coefficients[0] == 0, false))
+            {
+                coefficients = coefficients[1 .. $];
+                assert(coefficients.length);
+            }
+            return coefficients.length > (N + 1);
+        }
+
+        static if (internalRoundLastBits)
+        {
+            enum half = size_t(1) << (internalRoundLastBits - 1);
+            enum mask0 = (size_t(1) << internalRoundLastBits) - 1;
+            auto tail0 = ret.coefficient.data[0] & mask0;
+            ret.coefficient.data[0] &= ~mask0;
+            auto condInc = tail0 >= half
+                && (   tail0 > half
+                    || tail
+                    || (ret.coefficient.data[0] & 1)
+                    || nonZeroTail);
+        }
+        else
+        {
+            enum half = cast(size_t)sizediff_t.min;
+            auto condInc = tail >= half
+                && (    tail > half
+                    || (ret.coefficient.data[0] & 1)
+                    || nonZeroTail);
+        }
+
+        if (condInc)
+        {
+            enum inc = size_t(1) << internalRoundLastBits;
+            if (auto overflow = ret.coefficient += inc)
+            {
+                import mir.bignum.fp: half;
+                ret.coefficient = half!coefficientSize;
+                ret.exponent++;
+            }
+        }
+    }
+    return ret;
+}
+
+private enum dec_S = 9;
+private enum dec_P = 1 << dec_S;
+
+private auto load64()(long e)
+{
+    version(LDC)
+        pragma(inline, true);
+
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp;
+    import mir.bignum.internal.dec2float_table;
+
+    auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
+    auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
+    return Fp!64(false, p10exp, UInt!64(p10coeff));
+}
+
+private auto load128()(long e)
+{
+    version(LDC)
+        pragma(inline, true);
+
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp;
+    import mir.bignum.internal.dec2float_table;
+
+    static assert(min_p10_e <= -dec_P);
+    static assert(max_p10_e >= dec_P);
+    auto idx = cast(sizediff_t)e - min_p10_e;
+    ulong h = p10_coefficients[idx][0];
+    ulong l = p10_coefficients[idx][1];
+    if (l >= cast(ulong)long.min)
+        h--;
+    auto p10coeff = UInt!128(cast(ulong[2])[l, h]);
+    auto p10exp = p10_exponents[idx] - 64;
+    return Fp!128(false, p10exp, p10coeff);
+}
diff --git a/source/mir/bignum/internal/dec2flt_table.d b/source/mir/bignum/internal/dec2float_table.d
similarity index 99%
rename from source/mir/bignum/internal/dec2flt_table.d
rename to source/mir/bignum/internal/dec2float_table.d
index 89d043db..73691085 100644
--- a/source/mir/bignum/internal/dec2flt_table.d
+++ b/source/mir/bignum/internal/dec2float_table.d
@@ -1,8 +1,8 @@
 /++
 Tables of approximations of powers of ten.
-DO NOT MODIFY: Generated by `etc/dec2flt_table.py`
+DO NOT MODIFY: Generated by `etc/dec2float_table.py`
 +/
-module mir.bignum.internal.dec2flt_table;
+module mir.bignum.internal.dec2float_table;
 
 enum min_p10_e = -512;
 enum max_p10_e = 512;
diff --git a/source/mir/bignum/internal/phobos_kernel.d b/source/mir/bignum/internal/phobos_kernel.d
index 4efbc780..2c5a3425 100644
--- a/source/mir/bignum/internal/phobos_kernel.d
+++ b/source/mir/bignum/internal/phobos_kernel.d
@@ -380,6 +380,7 @@ private int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe
      return pwr;
 }
 
+version(mir_bignum_test)
 @safe pure unittest
 {
     assert(highestPowerBelowUintMax(10)==9);
@@ -536,6 +537,7 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y, Bi
 }
 
 // https://issues.dlang.org/show_bug.cgi?id=20493
+version(mir_bignum_test)
 @safe unittest
 {
     // the bug report has a testcase with very large numbers (~10^3800 and ~10^2300)
@@ -640,19 +642,20 @@ void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [
     }
 }
 
-version(none)
+version(mir_bignum_test)
 pure @safe unittest
 {   //7fff_00007fff_ffffffff_00020000
-    immutable(uint) [3] u = [0, 0xFFFF_FFFE, 0x8000_0000];
-    immutable(uint) [2] v = [0xFFFF_FFFF, 0x8000_0000];
+    uint[3] u = [0, 0xFFFF_FFFE, 0x8000_0000];
+    uint[2] v = [0xFFFF_FFFF, 0x8000_0000];
+    uint[10] buffer = void;
     uint[u.length - v.length + 1] q;
     uint[2] r;
-    divModInternal(q, r, u, v);
+    divModInternal(q, r, u, v, buffer);
     assert(q[]==[0xFFFF_FFFFu, 0]);
     assert(r[]==[0xFFFF_FFFFu, 0x7FFF_FFFF]);
     u = [0, 0xFFFF_FFFE, 0x8000_0001];
     v = [0xFFFF_FFFF, 0x8000_0000];
-    divModInternal(q, r, u, v);
+    divModInternal(q, r, u, v, buffer);
 }
 
 
@@ -1598,7 +1601,7 @@ pure nothrow @safe
             u[m + v.length] = 0;
             saveq = q[m];
         }
-        import std.stdio;
+
         recursiveDivMod(
             q[n - v.length .. m + mayOverflow],
             u[n - v.length .. m + mayOverflow + v.length],
@@ -1614,6 +1617,7 @@ pure nothrow @safe
     while(n);
 }
 
+version(mir_bignum_test)
 @system unittest
 {
     version (none)
@@ -1653,6 +1657,7 @@ pure nothrow @safe
 }
 
 // biguintToOctal
+version(mir_bignum_test)
 @safe unittest
 {
     enum bufSize = 5 * BigDigitBits / 3 + 1;
diff --git a/source/mir/bignum/internal/ryu/generic_128.d b/source/mir/bignum/internal/ryu/generic_128.d
index cb71d98f..a9cf6fab 100644
--- a/source/mir/bignum/internal/ryu/generic_128.d
+++ b/source/mir/bignum/internal/ryu/generic_128.d
@@ -46,6 +46,8 @@ void mul_128_256_shift(const UInt!128 a, const UInt!256 b, const uint shift, con
 // Computes 5^i in the form required by Ryu, and stores it in the given pointer.
 void generic_computePow5(const uint i, ref UInt!256 result)
 {
+    import mir.bignum.internal.ryu.table;
+
     version(LDC) pragma(inline, true);
     const uint base = i / POW5_TABLE_SIZE;
     const uint base2 = base * POW5_TABLE_SIZE;
@@ -93,6 +95,8 @@ version(mir_bignum_test) unittest
 // Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
 void generic_computeInvPow5(const uint i, ref UInt!256 result)
 {
+    import mir.bignum.internal.ryu.table;
+
     version(LDC) pragma(inline, true);
     const uint base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE;
     const uint base2 = base * POW5_TABLE_SIZE;
@@ -508,337 +512,3 @@ Decimal!(T.mant_dig < 64 ? 1 : 2) genericBinaryToDecimal(T)(const T x)
 
 private enum FLOAT_128_POW5_INV_BITCOUNT = 249;
 private enum FLOAT_128_POW5_BITCOUNT = 249;
-private enum POW5_TABLE_SIZE = 56;
-
-// These tables are ~4.5 kByte total, compared to ~160 kByte for the fUL tables.
-
-// There's no way to define 128-bit constants in C, so we use little-endian
-// pairs of 64-bit constants.
-private static immutable ulong[2] [POW5_TABLE_SIZE] GENERIC_POW5_TABLE = [
-    [                   1u,                    0u],
-    [                   5u,                    0u],
-    [                  25u,                    0u],
-    [                 125u,                    0u],
-    [                 625u,                    0u],
-    [                3125u,                    0u],
-    [               15625u,                    0u],
-    [               78125u,                    0u],
-    [              390625u,                    0u],
-    [             1953125u,                    0u],
-    [             9765625u,                    0u],
-    [            48828125u,                    0u],
-    [           244140625u,                    0u],
-    [          1220703125u,                    0u],
-    [          6103515625u,                    0u],
-    [         30517578125u,                    0u],
-    [        152587890625u,                    0u],
-    [        762939453125u,                    0u],
-    [       3814697265625u,                    0u],
-    [      19073486328125u,                    0u],
-    [      95367431640625u,                    0u],
-    [     476837158203125u,                    0u],
-    [    2384185791015625u,                    0u],
-    [   11920928955078125u,                    0u],
-    [   59604644775390625u,                    0u],
-    [  298023223876953125u,                    0u],
-    [ 1490116119384765625u,                    0u],
-    [ 7450580596923828125u,                    0u],
-    [  359414837200037393u,                    2u],
-    [ 1797074186000186965u,                   10u],
-    [ 8985370930000934825u,                   50u],
-    [ 8033366502585570893u,                  252u],
-    [ 3273344365508751233u,                 1262u],
-    [16366721827543756165u,                 6310u],
-    [ 8046632842880574361u,                31554u],
-    [ 3339676066983768573u,               157772u],
-    [16698380334918842865u,               788860u],
-    [ 9704925379756007861u,              3944304u],
-    [11631138751360936073u,             19721522u],
-    [ 2815461535676025517u,             98607613u],
-    [14077307678380127585u,            493038065u],
-    [15046306170771983077u,           2465190328u],
-    [ 1444554559021708921u,          12325951644u],
-    [ 7222772795108544605u,          61629758220u],
-    [17667119901833171409u,         308148791101u],
-    [14548623214327650581u,        1540743955509u],
-    [17402883850509598057u,        7703719777548u],
-    [13227442957709783821u,       38518598887744u],
-    [10796982567420264257u,      192592994438723u],
-    [17091424689682218053u,      962964972193617u],
-    [11670147153572883801u,     4814824860968089u],
-    [ 3010503546735764157u,    24074124304840448u],
-    [15052517733678820785u,   120370621524202240u],
-    [ 1475612373555897461u,   601853107621011204u],
-    [ 7378061867779487305u,  3009265538105056020u],
-    [18443565265187884909u, 15046327690525280101u],
-];
-
-private static immutable ulong[4][89] GENERIC_POW5_SPLIT = [
-    [                    0u,                    0u,                    0u,    72057594037927936u],
-    [                    0u,  5206161169240293376u,  4575641699882439235u,    73468396926392969u],
-    [  3360510775605221349u,  6983200512169538081u,  4325643253124434363u,    74906821675075173u],
-    [ 11917660854915489451u,  9652941469841108803u,   946308467778435600u,    76373409087490117u],
-    [  1994853395185689235u, 16102657350889591545u,  6847013871814915412u,    77868710555449746u],
-    [   958415760277438274u, 15059347134713823592u,  7329070255463483331u,    79393288266368765u],
-    [  2065144883315240188u,  7145278325844925976u, 14718454754511147343u,    80947715414629833u],
-    [  8980391188862868935u, 13709057401304208685u,  8230434828742694591u,    82532576417087045u],
-    [   432148644612782575u,  7960151582448466064u, 12056089168559840552u,    84148467132788711u],
-    [   484109300864744403u, 15010663910730448582u, 16824949663447227068u,    85795995087002057u],
-    [ 14793711725276144220u, 16494403799991899904u, 10145107106505865967u,    87475779699624060u],
-    [ 15427548291869817042u, 12330588654550505203u, 13980791795114552342u,    89188452518064298u],
-    [  9979404135116626552u, 13477446383271537499u, 14459862802511591337u,    90934657454687378u],
-    [ 12385121150303452775u,  9097130814231585614u,  6523855782339765207u,    92715051028904201u],
-    [  1822931022538209743u, 16062974719797586441u,  3619180286173516788u,    94530302614003091u],
-    [ 12318611738248470829u, 13330752208259324507u, 10986694768744162601u,    96381094688813589u],
-    [ 13684493829640282333u,  7674802078297225834u, 15208116197624593182u,    98268123094297527u],
-    [  5408877057066295332u,  6470124174091971006u, 15112713923117703147u,   100192097295163851u],
-    [ 11407083166564425062u, 18189998238742408185u,  4337638702446708282u,   102153740646605557u],
-    [  4112405898036935485u,   924624216579956435u, 14251108172073737125u,   104153790666259019u],
-    [ 16996739107011444789u, 10015944118339042475u,  2395188869672266257u,   106192999311487969u],
-    [  4588314690421337879u,  5339991768263654604u, 15441007590670620066u,   108272133262096356u],
-    [  2286159977890359825u, 14329706763185060248u,  5980012964059367667u,   110391974208576409u],
-    [  9654767503237031099u, 11293544302844823188u, 11739932712678287805u,   112553319146000238u],
-    [ 11362964448496095896u,  7990659682315657680u,   251480263940996374u,   114756980673665505u],
-    [  1423410421096377129u, 14274395557581462179u, 16553482793602208894u,   117003787300607788u],
-    [  2070444190619093137u, 11517140404712147401u, 11657844572835578076u,   119294583757094535u],
-    [  7648316884775828921u, 15264332483297977688u,   247182277434709002u,   121630231312217685u],
-    [ 17410896758132241352u, 10923914482914417070u, 13976383996795783649u,   124011608097704390u],
-    [  9542674537907272703u,  3079432708831728956u, 14235189590642919676u,   126439609438067572u],
-    [ 10364666969937261816u,  8464573184892924210u, 12758646866025101190u,   128915148187220428u],
-    [ 14720354822146013883u, 11480204489231511423u,  7449876034836187038u,   131439155071681461u],
-    [  1692907053653558553u, 17835392458598425233u,  1754856712536736598u,   134012579040499057u],
-    [  5620591334531458755u, 11361776175667106627u, 13350215315297937856u,   136636387622027174u],
-    [ 17455759733928092601u, 10362573084069962561u, 11246018728801810510u,   139311567287686283u],
-    [  2465404073814044982u, 17694822665274381860u,  1509954037718722697u,   142039123822846312u],
-    [  2152236053329638369u, 11202280800589637091u, 16388426812920420176u,    72410041352485523u],
-    [ 17319024055671609028u, 10944982848661280484u,  2457150158022562661u,    73827744744583080u],
-    [ 17511219308535248024u,  5122059497846768077u,  2089605804219668451u,    75273205100637900u],
-    [ 10082673333144031533u, 14429008783411894887u, 12842832230171903890u,    76746965869337783u],
-    [ 16196653406315961184u, 10260180891682904501u, 10537411930446752461u,    78249581139456266u],
-    [ 15084422041749743389u,   234835370106753111u, 16662517110286225617u,    79781615848172976u],
-    [  8199644021067702606u,  3787318116274991885u,  7438130039325743106u,    81343645993472659u],
-    [ 12039493937039359765u,  9773822153580393709u,  5945428874398357806u,    82936258850702722u],
-    [   984543865091303961u,  7975107621689454830u,  6556665988501773347u,    84560053193370726u],
-    [  9633317878125234244u, 16099592426808915028u,  9706674539190598200u,    86215639518264828u],
-    [  6860695058870476186u,  4471839111886709592u,  7828342285492709568u,    87903640274981819u],
-    [ 14583324717644598331u,  4496120889473451238u,  5290040788305728466u,    89624690099949049u],
-    [ 18093669366515003715u, 12879506572606942994u, 18005739787089675377u,    91379436055028227u],
-    [ 17997493966862379937u, 14646222655265145582u, 10265023312844161858u,    93168537870790806u],
-    [ 12283848109039722318u, 11290258077250314935u,  9878160025624946825u,    94992668194556404u],
-    [  8087752761883078164u,  5262596608437575693u, 11093553063763274413u,    96852512843287537u],
-    [ 15027787746776840781u, 12250273651168257752u,  9290470558712181914u,    98748771061435726u],
-    [ 15003915578366724489u,  2937334162439764327u,  5404085603526796602u,   100682155783835929u],
-    [  5225610465224746757u, 14932114897406142027u,  2774647558180708010u,   102653393903748137u],
-    [ 17112957703385190360u, 12069082008339002412u,  3901112447086388439u,   104663226546146909u],
-    [  4062324464323300238u,  3992768146772240329u, 15757196565593695724u,   106712409346361594u],
-    [  5525364615810306701u, 11855206026704935156u, 11344868740897365300u,   108801712734172003u],
-    [  9274143661888462646u,  4478365862348432381u, 18010077872551661771u,   110931922223466333u],
-    [ 12604141221930060148u,  8930937759942591500u,  9382183116147201338u,   113103838707570263u],
-    [ 14513929377491886653u,  1410646149696279084u,   587092196850797612u,   115318278760358235u],
-    [  2226851524999454362u,  7717102471110805679u,  7187441550995571734u,   117576074943260147u],
-    [  5527526061344932763u,  2347100676188369132u, 16976241418824030445u,   119878076118278875u],
-    [  6088479778147221611u, 17669593130014777580u, 10991124207197663546u,   122225147767136307u],
-    [ 11107734086759692041u,  3391795220306863431u, 17233960908859089158u,   124618172316667879u],
-    [  7913172514655155198u, 17726879005381242552u,   641069866244011540u,   127058049470587962u],
-    [ 12596991768458713949u, 15714785522479904446u,  6035972567136116512u,   129545696547750811u],
-    [ 16901996933781815980u,  4275085211437148707u, 14091642539965169063u,   132082048827034281u],
-    [  7524574627987869240u, 15661204384239316051u,  2444526454225712267u,   134668059898975949u],
-    [  8199251625090479942u,  6803282222165044067u, 16064817666437851504u,   137304702024293857u],
-    [  4453256673338111920u, 15269922543084434181u,  3139961729834750852u,   139992966499426682u],
-    [ 15841763546372731299u,  3013174075437671812u,  4383755396295695606u,   142733864029230733u],
-    [  9771896230907310329u,  4900659362437687569u, 12386126719044266361u,    72764212553486967u],
-    [  9420455527449565190u,  1859606122611023693u,  6555040298902684281u,    74188850200884818u],
-    [  5146105983135678095u,  2287300449992174951u,  4325371679080264751u,    75641380576797959u],
-    [ 11019359372592553360u,  8422686425957443718u,  7175176077944048210u,    77122349788024458u],
-    [ 11005742969399620716u,  4132174559240043701u,  9372258443096612118u,    78632314633490790u],
-    [  8887589641394725840u,  8029899502466543662u, 14582206497241572853u,    80171842813591127u],
-    [   360247523705545899u, 12568341805293354211u, 14653258284762517866u,    81741513143625247u],
-    [ 12314272731984275834u,  4740745023227177044u,  6141631472368337539u,    83341915771415304u],
-    [   441052047733984759u,  7940090120939869826u, 11750200619921094248u,    84973652399183278u],
-    [  3436657868127012749u,  9187006432149937667u, 16389726097323041290u,    86637336509772529u],
-    [ 13490220260784534044u, 15339072891382896702u,  8846102360835316895u,    88333593597298497u],
-    [  4125672032094859833u,   158347675704003277u, 10592598512749774447u,    90063061402315272u],
-    [ 12189928252974395775u,  2386931199439295891u,  7009030566469913276u,    91826390151586454u],
-    [  9256479608339282969u,  2844900158963599229u, 11148388908923225596u,    93624242802550437u],
-    [ 11584393507658707408u,  2863659090805147914u,  9873421561981063551u,    95457295292572042u],
-    [ 13984297296943171390u,  1931468383973130608u, 12905719743235082319u,    97326236793074198u],
-    [  5837045222254987499u, 10213498696735864176u, 14893951506257020749u,    99231769968645227u],
-];
-
-// Unfortunately, the results are sometimes off by one or two. We use an additional
-// lookup table to store those cases and adjust the result.
-private static immutable ulong[156] POW5_ERRORS = [
-    0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x9555596400000000u,
-    0x65a6569525565555u, 0x4415551445449655u, 0x5105015504144541u, 0x65a69969a6965964u,
-    0x5054955969959656u, 0x5105154515554145u, 0x4055511051591555u, 0x5500514455550115u,
-    0x0041140014145515u, 0x1005440545511051u, 0x0014405450411004u, 0x0414440010500000u,
-    0x0044000440010040u, 0x5551155000004001u, 0x4554555454544114u, 0x5150045544005441u,
-    0x0001111400054501u, 0x6550955555554554u, 0x1504159645559559u, 0x4105055141454545u,
-    0x1411541410405454u, 0x0415555044545555u, 0x0014154115405550u, 0x1540055040411445u,
-    0x0000000500000000u, 0x5644000000000000u, 0x1155555591596555u, 0x0410440054569565u,
-    0x5145100010010005u, 0x0555041405500150u, 0x4141450455140450u, 0x0000000144000140u,
-    0x5114004001105410u, 0x4444100404005504u, 0x0414014410001015u, 0x5145055155555015u,
-    0x0141041444445540u, 0x0000100451541414u, 0x4105041104155550u, 0x0500501150451145u,
-    0x1001050000004114u, 0x5551504400141045u, 0x5110545410151454u, 0x0100001400004040u,
-    0x5040010111040000u, 0x0140000150541100u, 0x4400140400104110u, 0x5011014405545004u,
-    0x0000000044155440u, 0x0000000010000000u, 0x1100401444440001u, 0x0040401010055111u,
-    0x5155155551405454u, 0x0444440015514411u, 0x0054505054014101u, 0x0451015441115511u,
-    0x1541411401140551u, 0x4155104514445110u, 0x4141145450145515u, 0x5451445055155050u,
-    0x4400515554110054u, 0x5111145104501151u, 0x565a655455500501u, 0x5565555555525955u,
-    0x0550511500405695u, 0x4415504051054544u, 0x6555595965555554u, 0x0100915915555655u,
-    0x5540001510001001u, 0x5450051414000544u, 0x1405010555555551u, 0x5555515555644155u,
-    0x5555055595496555u, 0x5451045004415000u, 0x5450510144040144u, 0x5554155555556455u,
-    0x5051555495415555u, 0x5555554555555545u, 0x0000000010005455u, 0x4000005000040000u,
-    0x5565555555555954u, 0x5554559555555505u, 0x9645545495552555u, 0x4000400055955564u,
-    0x0040000000000001u, 0x4004100100000000u, 0x5540040440000411u, 0x4565555955545644u,
-    0x1140659549651556u, 0x0100000410010000u, 0x5555515400004001u, 0x5955545555155255u,
-    0x5151055545505556u, 0x5051454510554515u, 0x0501500050415554u, 0x5044154005441005u,
-    0x1455445450550455u, 0x0010144055144545u, 0x0000401100000004u, 0x1050145050000010u,
-    0x0415004554011540u, 0x1000510100151150u, 0x0100040400001144u, 0x0000000000000000u,
-    0x0550004400000100u, 0x0151145041451151u, 0x0000400400005450u, 0x0000100044010004u,
-    0x0100054100050040u, 0x0504400005410010u, 0x4011410445500105u, 0x0000404000144411u,
-    0x0101504404500000u, 0x0000005044400400u, 0x0000000014000100u, 0x0404440414000000u,
-    0x5554100410000140u, 0x4555455544505555u, 0x5454105055455455u, 0x0115454155454015u,
-    0x4404110000045100u, 0x4400001100101501u, 0x6596955956966a94u, 0x0040655955665965u,
-    0x5554144400100155u, 0xa549495401011041u, 0x5596555565955555u, 0x5569965959549555u,
-    0x969565a655555456u, 0x0000001000000000u, 0x0000000040000140u, 0x0000040100000000u,
-    0x1415454400000000u, 0x5410415411454114u, 0x0400040104000154u, 0x0504045000000411u,
-    0x0000001000000010u, 0x5554000000001040u, 0x5549155551556595u, 0x1455541055515555u,
-    0x0510555454554541u, 0x9555555555540455u, 0x6455456555556465u, 0x4524565555654514u,
-    0x5554655255559545u, 0x9555455441155556u, 0x0000000051515555u, 0x0010005040000550u,
-    0x5044044040000000u, 0x1045040440010500u, 0x0000400000040000u, 0x0000000000000000u,
-];
-
-private static immutable ulong[4][89] GENERIC_POW5_INV_SPLIT = [
-    [                    0u,                    0u,                    0u,   144115188075855872u ],
-    [  1573859546583440065u,  2691002611772552616u,  6763753280790178510u,   141347765182270746u ],
-    [ 12960290449513840412u, 12345512957918226762u, 18057899791198622765u,   138633484706040742u ],
-    [  7615871757716765416u,  9507132263365501332u,  4879801712092008245u,   135971326161092377u ],
-    [  7869961150745287587u,  5804035291554591636u,  8883897266325833928u,   133360288657597085u ],
-    [  2942118023529634767u, 15128191429820565086u, 10638459445243230718u,   130799390525667397u ],
-    [ 14188759758411913794u,  5362791266439207815u,  8068821289119264054u,   128287668946279217u ],
-    [  7183196927902545212u,  1952291723540117099u, 12075928209936341512u,   125824179589281448u ],
-    [  5672588001402349748u, 17892323620748423487u,  9874578446960390364u,   123407996258356868u ],
-    [  4442590541217566325u,  4558254706293456445u, 10343828952663182727u,   121038210542800766u ],
-    [  3005560928406962566u,  2082271027139057888u, 13961184524927245081u,   118713931475986426u ],
-    [ 13299058168408384786u, 17834349496131278595u,  9029906103900731664u,   116434285200389047u ],
-    [  5414878118283973035u, 13079825470227392078u, 17897304791683760280u,   114198414639042157u ],
-    [ 14609755883382484834u, 14991702445765844156u,  3269802549772755411u,   112005479173303009u ],
-    [ 15967774957605076027u,  2511532636717499923u, 16221038267832563171u,   109854654326805788u ],
-    [  9269330061621627145u,  3332501053426257392u, 16223281189403734630u,   107745131455483836u ],
-    [ 16739559299223642282u,  1873986623300664530u,  6546709159471442872u,   105676117443544318u ],
-    [ 17116435360051202055u,  1359075105581853924u,  2038341371621886470u,   103646834405281051u ],
-    [ 17144715798009627550u,  3201623802661132408u,  9757551605154622431u,   101656519392613377u ],
-    [ 17580479792687825857u,  6546633380567327312u, 15099972427870912398u,    99704424108241124u ],
-    [  9726477118325522902u, 14578369026754005435u, 11728055595254428803u,    97789814624307808u ],
-    [   134593949518343635u,  5715151379816901985u,  1660163707976377376u,    95911971106466306u ],
-    [  5515914027713859358u,  7124354893273815720u,  5548463282858794077u,    94070187543243255u ],
-    [  6188403395862945512u,  5681264392632320838u, 15417410852121406654u,    92263771480600430u ],
-    [ 15908890877468271457u, 10398888261125597540u,  4817794962769172309u,    90492043761593298u ],
-    [  1413077535082201005u, 12675058125384151580u,  7731426132303759597u,    88754338271028867u ],
-    [  1486733163972670293u, 11369385300195092554u, 11610016711694864110u,    87050001685026843u ],
-    [  8788596583757589684u,  3978580923851924802u,  9255162428306775812u,    85378393225389919u ],
-    [  7203518319660962120u, 15044736224407683725u,  2488132019818199792u,    83738884418690858u ],
-    [  4004175967662388707u, 18236988667757575407u, 15613100370957482671u,    82130858859985791u ],
-    [ 18371903370586036463u,    53497579022921640u, 16465963977267203307u,    80553711981064899u ],
-    [ 10170778323887491315u,  1999668801648976001u, 10209763593579456445u,    79006850823153334u ],
-    [ 17108131712433974546u, 16825784443029944237u,  2078700786753338945u,    77489693813976938u ],
-    [ 17221789422665858532u, 12145427517550446164u,  5391414622238668005u,    76001670549108934u ],
-    [  4859588996898795878u,  1715798948121313204u,  3950858167455137171u,    74542221577515387u ],
-    [ 13513469241795711526u,   631367850494860526u, 10517278915021816160u,    73110798191218799u ],
-    [ 11757513142672073111u,  2581974932255022228u, 17498959383193606459u,   143413724438001539u ],
-    [ 14524355192525042817u,  5640643347559376447u,  1309659274756813016u,   140659771648132296u ],
-    [  2765095348461978538u, 11021111021896007722u,  3224303603779962366u,   137958702611185230u ],
-    [ 12373410389187981037u, 13679193545685856195u, 11644609038462631561u,   135309501808182158u ],
-    [ 12813176257562780151u,  3754199046160268020u,  9954691079802960722u,   132711173221007413u ],
-    [ 17557452279667723458u,  3237799193992485824u, 17893947919029030695u,   130162739957935629u ],
-    [ 14634200999559435155u,  4123869946105211004u,  6955301747350769239u,   127663243886350468u ],
-    [  2185352760627740240u,  2864813346878886844u, 13049218671329690184u,   125211745272516185u ],
-    [  6143438674322183002u, 10464733336980678750u,  6982925169933978309u,   122807322428266620u ],
-    [  1099509117817174576u, 10202656147550524081u,   754997032816608484u,   120449071364478757u ],
-    [  2410631293559367023u, 17407273750261453804u, 15307291918933463037u,   118136105451200587u ],
-    [ 12224968375134586697u,  1664436604907828062u, 11506086230137787358u,   115867555084305488u ],
-    [  3495926216898000888u, 18392536965197424288u, 10992889188570643156u,   113642567358547782u ],
-    [  8744506286256259680u,  3966568369496879937u, 18342264969761820037u,   111460305746896569u ],
-    [  7689600520560455039u,  5254331190877624630u,  9628558080573245556u,   109319949786027263u ],
-    [ 11862637625618819436u,  3456120362318976488u, 14690471063106001082u,   107220694767852583u ],
-    [  5697330450030126444u, 12424082405392918899u,   358204170751754904u,   105161751436977040u ],
-    [ 11257457505097373622u, 15373192700214208870u,   671619062372033814u,   103142345693961148u ],
-    [ 16850355018477166700u,  1913910419361963966u,  4550257919755970531u,   101161718304283822u ],
-    [  9670835567561997011u, 10584031339132130638u,  3060560222974851757u,    99219124612893520u ],
-    [  7698686577353054710u, 11689292838639130817u, 11806331021588878241u,    97313834264240819u ],
-    [ 12233569599615692137u,  3347791226108469959u, 10333904326094451110u,    95445130927687169u ],
-    [ 13049400362825383933u, 17142621313007799680u,  3790542585289224168u,    93612312028186576u ],
-    [ 12430457242474442072u,  5625077542189557960u, 14765055286236672238u,    91814688482138969u ],
-    [  4759444137752473128u,  2230562561567025078u,  4954443037339580076u,    90051584438315940u ],
-    [  7246913525170274758u,  8910297835195760709u,  4015904029508858381u,    88322337023761438u ],
-    [ 12854430245836432067u,  8135139748065431455u, 11548083631386317976u,    86626296094571907u ],
-    [  4848827254502687803u,  4789491250196085625u,  3988192420450664125u,    84962823991462151u ],
-    [  7435538409611286684u,   904061756819742353u, 14598026519493048444u,    83331295300025028u ],
-    [ 11042616160352530997u,  8948390828345326218u, 10052651191118271927u,    81731096615594853u ],
-    [ 11059348291563778943u, 11696515766184685544u,  3783210511290897367u,    80161626312626082u ],
-    [  7020010856491885826u,  5025093219346041680u,  8960210401638911765u,    78622294318500592u ],
-    [ 17732844474490699984u,  7820866704994446502u,  6088373186798844243u,    77112521891678506u ],
-    [   688278527545590501u,  3045610706602776618u,  8684243536999567610u,    75631741404109150u ],
-    [  2734573255120657297u,  3903146411440697663u,  9470794821691856713u,    74179396127820347u ],
-    [ 15996457521023071259u,  4776627823451271680u, 12394856457265744744u,    72754940025605801u ],
-    [ 13492065758834518331u,  7390517611012222399u,  1630485387832860230u,   142715675091463768u ],
-    [ 13665021627282055864u,  9897834675523659302u, 17907668136755296849u,   139975126841173266u ],
-    [  9603773719399446181u, 10771916301484339398u, 10672699855989487527u,   137287204938390542u ],
-    [  3630218541553511265u,  8139010004241080614u,  2876479648932814543u,   134650898807055963u ],
-    [  8318835909686377084u,  9525369258927993371u,  2796120270400437057u,   132065217277054270u ],
-    [ 11190003059043290163u, 12424345635599592110u, 12539346395388933763u,   129529188211565064u ],
-    [  8701968833973242276u,   820569587086330727u,  2315591597351480110u,   127041858141569228u ],
-    [  5115113890115690487u, 16906305245394587826u,  9899749468931071388u,   124602291907373862u ],
-    [ 15543535488939245974u, 10945189844466391399u,  3553863472349432246u,   122209572307020975u ],
-    [  7709257252608325038u,  1191832167690640880u, 15077137020234258537u,   119862799751447719u ],
-    [  7541333244210021737u,  9790054727902174575u,  5160944773155322014u,   117561091926268545u ],
-    [ 12297384708782857832u,  1281328873123467374u,  4827925254630475769u,   115303583460052092u ],
-    [ 13243237906232367265u, 15873887428139547641u,  3607993172301799599u,   113089425598968120u ],
-    [ 11384616453739611114u, 15184114243769211033u, 13148448124803481057u,   110917785887682141u ],
-    [ 17727970963596660683u,  1196965221832671990u, 14537830463956404138u,   108787847856377790u ],
-    [ 17241367586707330931u,  8880584684128262874u, 11173506540726547818u,   106698810713789254u ],
-    [  7184427196661305643u, 14332510582433188173u, 14230167953789677901u,   104649889046128358u ],
-];
-
-private static immutable ulong[154] POW5_INV_ERRORS = [
-    0x1144155514145504u, 0x0000541555401141u, 0x0000000000000000u, 0x0154454000000000u,
-    0x4114105515544440u, 0x0001001111500415u, 0x4041411410011000u, 0x5550114515155014u,
-    0x1404100041554551u, 0x0515000450404410u, 0x5054544401140004u, 0x5155501005555105u,
-    0x1144141000105515u, 0x0541500000500000u, 0x1104105540444140u, 0x4000015055514110u,
-    0x0054010450004005u, 0x4155515404100005u, 0x5155145045155555u, 0x1511555515440558u,
-    0x5558544555515555u, 0x0000000000000010u, 0x5004000000000050u, 0x1415510100000010u,
-    0x4545555444514500u, 0x5155151555555551u, 0x1441540144044554u, 0x5150104045544400u,
-    0x5450545401444040u, 0x5554455045501400u, 0x4655155555555145u, 0x1000010055455055u,
-    0x1000004000055004u, 0x4455405104000005u, 0x4500114504150545u, 0x0000000014000000u,
-    0x5450000000000000u, 0x5514551511445555u, 0x4111501040555451u, 0x4515445500054444u,
-    0x5101500104100441u, 0x1545115155545055u, 0x0000000000000000u, 0x1554000000100000u,
-    0x5555545595551555u, 0x5555051851455955u, 0x5555555555555559u, 0x0000400011001555u,
-    0x0000004400040000u, 0x5455511555554554u, 0x5614555544115445u, 0x6455156145555155u,
-    0x5455855455415455u, 0x5515555144555545u, 0x0114400000145155u, 0x0000051000450511u,
-    0x4455154554445100u, 0x4554150141544455u, 0x65955555559a5965u, 0x5555555854559559u,
-    0x9569654559616595u, 0x1040044040005565u, 0x1010010500011044u, 0x1554015545154540u,
-    0x4440555401545441u, 0x1014441450550105u, 0x4545400410504145u, 0x5015111541040151u,
-    0x5145051154000410u, 0x1040001044545044u, 0x4001400000151410u, 0x0540000044040000u,
-    0x0510555454411544u, 0x0400054054141550u, 0x1001041145001100u, 0x0000000140000000u,
-    0x0000000014100000u, 0x1544005454000140u, 0x4050055505445145u, 0x0011511104504155u,
-    0x5505544415045055u, 0x1155154445515554u, 0x0000000000004555u, 0x0000000000000000u,
-    0x5101010510400004u, 0x1514045044440400u, 0x5515519555515555u, 0x4554545441555545u,
-    0x1551055955551515u, 0x0150000011505515u, 0x0044005040400000u, 0x0004001004010050u,
-    0x0000051004450414u, 0x0114001101001144u, 0x0401000001000001u, 0x4500010001000401u,
-    0x0004100000005000u, 0x0105000441101100u, 0x0455455550454540u, 0x5404050144105505u,
-    0x4101510540555455u, 0x1055541411451555u, 0x5451445110115505u, 0x1154110010101545u,
-    0x1145140450054055u, 0x5555565415551554u, 0x1550559555555555u, 0x5555541545045141u,
-    0x4555455450500100u, 0x5510454545554555u, 0x1510140115045455u, 0x1001050040111510u,
-    0x5555454555555504u, 0x9954155545515554u, 0x6596656555555555u, 0x0140410051555559u,
-    0x0011104010001544u, 0x965669659a680501u, 0x5655a55955556955u, 0x4015111014404514u,
-    0x1414155554505145u, 0x0540040011051404u, 0x1010000000015005u, 0x0010054050004410u,
-    0x5041104014000100u, 0x4440010500100001u, 0x1155510504545554u, 0x0450151545115541u,
-    0x4000100400110440u, 0x1004440010514440u, 0x0000115050450000u, 0x0545404455541500u,
-    0x1051051555505101u, 0x5505144554544144u, 0x4550545555515550u, 0x0015400450045445u,
-    0x4514155400554415u, 0x4555055051050151u, 0x1511441450001014u, 0x4544554510404414u,
-    0x4115115545545450u, 0x5500541555551555u, 0x5550010544155015u, 0x0144414045545500u,
-    0x4154050001050150u, 0x5550511111000145u, 0x1114504055000151u, 0x5104041101451040u,
-    0x0010501401051441u, 0x0010501450504401u, 0x4554585440044444u, 0x5155555951450455u,
-    0x0040000400105555u, 0x0000000000000001u,
-];
diff --git a/source/mir/bignum/internal/ryu/table.d b/source/mir/bignum/internal/ryu/table.d
new file mode 100644
index 00000000..3a6b0eb4
--- /dev/null
+++ b/source/mir/bignum/internal/ryu/table.d
@@ -0,0 +1,336 @@
+module mir.bignum.internal.ryu.table;
+
+package enum POW5_TABLE_SIZE = 56;
+
+// These tables are ~4.5 kByte total, compared to ~160 kByte for the fUL tables.
+
+// There's no way to define 128-bit constants in C, so we use little-endian
+// pairs of 64-bit constants.
+package static immutable ulong[2] [POW5_TABLE_SIZE] GENERIC_POW5_TABLE = [
+    [                   1u,                    0u],
+    [                   5u,                    0u],
+    [                  25u,                    0u],
+    [                 125u,                    0u],
+    [                 625u,                    0u],
+    [                3125u,                    0u],
+    [               15625u,                    0u],
+    [               78125u,                    0u],
+    [              390625u,                    0u],
+    [             1953125u,                    0u],
+    [             9765625u,                    0u],
+    [            48828125u,                    0u],
+    [           244140625u,                    0u],
+    [          1220703125u,                    0u],
+    [          6103515625u,                    0u],
+    [         30517578125u,                    0u],
+    [        152587890625u,                    0u],
+    [        762939453125u,                    0u],
+    [       3814697265625u,                    0u],
+    [      19073486328125u,                    0u],
+    [      95367431640625u,                    0u],
+    [     476837158203125u,                    0u],
+    [    2384185791015625u,                    0u],
+    [   11920928955078125u,                    0u],
+    [   59604644775390625u,                    0u],
+    [  298023223876953125u,                    0u],
+    [ 1490116119384765625u,                    0u],
+    [ 7450580596923828125u,                    0u],
+    [  359414837200037393u,                    2u],
+    [ 1797074186000186965u,                   10u],
+    [ 8985370930000934825u,                   50u],
+    [ 8033366502585570893u,                  252u],
+    [ 3273344365508751233u,                 1262u],
+    [16366721827543756165u,                 6310u],
+    [ 8046632842880574361u,                31554u],
+    [ 3339676066983768573u,               157772u],
+    [16698380334918842865u,               788860u],
+    [ 9704925379756007861u,              3944304u],
+    [11631138751360936073u,             19721522u],
+    [ 2815461535676025517u,             98607613u],
+    [14077307678380127585u,            493038065u],
+    [15046306170771983077u,           2465190328u],
+    [ 1444554559021708921u,          12325951644u],
+    [ 7222772795108544605u,          61629758220u],
+    [17667119901833171409u,         308148791101u],
+    [14548623214327650581u,        1540743955509u],
+    [17402883850509598057u,        7703719777548u],
+    [13227442957709783821u,       38518598887744u],
+    [10796982567420264257u,      192592994438723u],
+    [17091424689682218053u,      962964972193617u],
+    [11670147153572883801u,     4814824860968089u],
+    [ 3010503546735764157u,    24074124304840448u],
+    [15052517733678820785u,   120370621524202240u],
+    [ 1475612373555897461u,   601853107621011204u],
+    [ 7378061867779487305u,  3009265538105056020u],
+    [18443565265187884909u, 15046327690525280101u],
+];
+
+package static immutable ulong[4][89] GENERIC_POW5_SPLIT = [
+    [                    0u,                    0u,                    0u,    72057594037927936u],
+    [                    0u,  5206161169240293376u,  4575641699882439235u,    73468396926392969u],
+    [  3360510775605221349u,  6983200512169538081u,  4325643253124434363u,    74906821675075173u],
+    [ 11917660854915489451u,  9652941469841108803u,   946308467778435600u,    76373409087490117u],
+    [  1994853395185689235u, 16102657350889591545u,  6847013871814915412u,    77868710555449746u],
+    [   958415760277438274u, 15059347134713823592u,  7329070255463483331u,    79393288266368765u],
+    [  2065144883315240188u,  7145278325844925976u, 14718454754511147343u,    80947715414629833u],
+    [  8980391188862868935u, 13709057401304208685u,  8230434828742694591u,    82532576417087045u],
+    [   432148644612782575u,  7960151582448466064u, 12056089168559840552u,    84148467132788711u],
+    [   484109300864744403u, 15010663910730448582u, 16824949663447227068u,    85795995087002057u],
+    [ 14793711725276144220u, 16494403799991899904u, 10145107106505865967u,    87475779699624060u],
+    [ 15427548291869817042u, 12330588654550505203u, 13980791795114552342u,    89188452518064298u],
+    [  9979404135116626552u, 13477446383271537499u, 14459862802511591337u,    90934657454687378u],
+    [ 12385121150303452775u,  9097130814231585614u,  6523855782339765207u,    92715051028904201u],
+    [  1822931022538209743u, 16062974719797586441u,  3619180286173516788u,    94530302614003091u],
+    [ 12318611738248470829u, 13330752208259324507u, 10986694768744162601u,    96381094688813589u],
+    [ 13684493829640282333u,  7674802078297225834u, 15208116197624593182u,    98268123094297527u],
+    [  5408877057066295332u,  6470124174091971006u, 15112713923117703147u,   100192097295163851u],
+    [ 11407083166564425062u, 18189998238742408185u,  4337638702446708282u,   102153740646605557u],
+    [  4112405898036935485u,   924624216579956435u, 14251108172073737125u,   104153790666259019u],
+    [ 16996739107011444789u, 10015944118339042475u,  2395188869672266257u,   106192999311487969u],
+    [  4588314690421337879u,  5339991768263654604u, 15441007590670620066u,   108272133262096356u],
+    [  2286159977890359825u, 14329706763185060248u,  5980012964059367667u,   110391974208576409u],
+    [  9654767503237031099u, 11293544302844823188u, 11739932712678287805u,   112553319146000238u],
+    [ 11362964448496095896u,  7990659682315657680u,   251480263940996374u,   114756980673665505u],
+    [  1423410421096377129u, 14274395557581462179u, 16553482793602208894u,   117003787300607788u],
+    [  2070444190619093137u, 11517140404712147401u, 11657844572835578076u,   119294583757094535u],
+    [  7648316884775828921u, 15264332483297977688u,   247182277434709002u,   121630231312217685u],
+    [ 17410896758132241352u, 10923914482914417070u, 13976383996795783649u,   124011608097704390u],
+    [  9542674537907272703u,  3079432708831728956u, 14235189590642919676u,   126439609438067572u],
+    [ 10364666969937261816u,  8464573184892924210u, 12758646866025101190u,   128915148187220428u],
+    [ 14720354822146013883u, 11480204489231511423u,  7449876034836187038u,   131439155071681461u],
+    [  1692907053653558553u, 17835392458598425233u,  1754856712536736598u,   134012579040499057u],
+    [  5620591334531458755u, 11361776175667106627u, 13350215315297937856u,   136636387622027174u],
+    [ 17455759733928092601u, 10362573084069962561u, 11246018728801810510u,   139311567287686283u],
+    [  2465404073814044982u, 17694822665274381860u,  1509954037718722697u,   142039123822846312u],
+    [  2152236053329638369u, 11202280800589637091u, 16388426812920420176u,    72410041352485523u],
+    [ 17319024055671609028u, 10944982848661280484u,  2457150158022562661u,    73827744744583080u],
+    [ 17511219308535248024u,  5122059497846768077u,  2089605804219668451u,    75273205100637900u],
+    [ 10082673333144031533u, 14429008783411894887u, 12842832230171903890u,    76746965869337783u],
+    [ 16196653406315961184u, 10260180891682904501u, 10537411930446752461u,    78249581139456266u],
+    [ 15084422041749743389u,   234835370106753111u, 16662517110286225617u,    79781615848172976u],
+    [  8199644021067702606u,  3787318116274991885u,  7438130039325743106u,    81343645993472659u],
+    [ 12039493937039359765u,  9773822153580393709u,  5945428874398357806u,    82936258850702722u],
+    [   984543865091303961u,  7975107621689454830u,  6556665988501773347u,    84560053193370726u],
+    [  9633317878125234244u, 16099592426808915028u,  9706674539190598200u,    86215639518264828u],
+    [  6860695058870476186u,  4471839111886709592u,  7828342285492709568u,    87903640274981819u],
+    [ 14583324717644598331u,  4496120889473451238u,  5290040788305728466u,    89624690099949049u],
+    [ 18093669366515003715u, 12879506572606942994u, 18005739787089675377u,    91379436055028227u],
+    [ 17997493966862379937u, 14646222655265145582u, 10265023312844161858u,    93168537870790806u],
+    [ 12283848109039722318u, 11290258077250314935u,  9878160025624946825u,    94992668194556404u],
+    [  8087752761883078164u,  5262596608437575693u, 11093553063763274413u,    96852512843287537u],
+    [ 15027787746776840781u, 12250273651168257752u,  9290470558712181914u,    98748771061435726u],
+    [ 15003915578366724489u,  2937334162439764327u,  5404085603526796602u,   100682155783835929u],
+    [  5225610465224746757u, 14932114897406142027u,  2774647558180708010u,   102653393903748137u],
+    [ 17112957703385190360u, 12069082008339002412u,  3901112447086388439u,   104663226546146909u],
+    [  4062324464323300238u,  3992768146772240329u, 15757196565593695724u,   106712409346361594u],
+    [  5525364615810306701u, 11855206026704935156u, 11344868740897365300u,   108801712734172003u],
+    [  9274143661888462646u,  4478365862348432381u, 18010077872551661771u,   110931922223466333u],
+    [ 12604141221930060148u,  8930937759942591500u,  9382183116147201338u,   113103838707570263u],
+    [ 14513929377491886653u,  1410646149696279084u,   587092196850797612u,   115318278760358235u],
+    [  2226851524999454362u,  7717102471110805679u,  7187441550995571734u,   117576074943260147u],
+    [  5527526061344932763u,  2347100676188369132u, 16976241418824030445u,   119878076118278875u],
+    [  6088479778147221611u, 17669593130014777580u, 10991124207197663546u,   122225147767136307u],
+    [ 11107734086759692041u,  3391795220306863431u, 17233960908859089158u,   124618172316667879u],
+    [  7913172514655155198u, 17726879005381242552u,   641069866244011540u,   127058049470587962u],
+    [ 12596991768458713949u, 15714785522479904446u,  6035972567136116512u,   129545696547750811u],
+    [ 16901996933781815980u,  4275085211437148707u, 14091642539965169063u,   132082048827034281u],
+    [  7524574627987869240u, 15661204384239316051u,  2444526454225712267u,   134668059898975949u],
+    [  8199251625090479942u,  6803282222165044067u, 16064817666437851504u,   137304702024293857u],
+    [  4453256673338111920u, 15269922543084434181u,  3139961729834750852u,   139992966499426682u],
+    [ 15841763546372731299u,  3013174075437671812u,  4383755396295695606u,   142733864029230733u],
+    [  9771896230907310329u,  4900659362437687569u, 12386126719044266361u,    72764212553486967u],
+    [  9420455527449565190u,  1859606122611023693u,  6555040298902684281u,    74188850200884818u],
+    [  5146105983135678095u,  2287300449992174951u,  4325371679080264751u,    75641380576797959u],
+    [ 11019359372592553360u,  8422686425957443718u,  7175176077944048210u,    77122349788024458u],
+    [ 11005742969399620716u,  4132174559240043701u,  9372258443096612118u,    78632314633490790u],
+    [  8887589641394725840u,  8029899502466543662u, 14582206497241572853u,    80171842813591127u],
+    [   360247523705545899u, 12568341805293354211u, 14653258284762517866u,    81741513143625247u],
+    [ 12314272731984275834u,  4740745023227177044u,  6141631472368337539u,    83341915771415304u],
+    [   441052047733984759u,  7940090120939869826u, 11750200619921094248u,    84973652399183278u],
+    [  3436657868127012749u,  9187006432149937667u, 16389726097323041290u,    86637336509772529u],
+    [ 13490220260784534044u, 15339072891382896702u,  8846102360835316895u,    88333593597298497u],
+    [  4125672032094859833u,   158347675704003277u, 10592598512749774447u,    90063061402315272u],
+    [ 12189928252974395775u,  2386931199439295891u,  7009030566469913276u,    91826390151586454u],
+    [  9256479608339282969u,  2844900158963599229u, 11148388908923225596u,    93624242802550437u],
+    [ 11584393507658707408u,  2863659090805147914u,  9873421561981063551u,    95457295292572042u],
+    [ 13984297296943171390u,  1931468383973130608u, 12905719743235082319u,    97326236793074198u],
+    [  5837045222254987499u, 10213498696735864176u, 14893951506257020749u,    99231769968645227u],
+];
+
+// Unfortunately, the results are sometimes off by one or two. We use an additional
+// lookup table to store those cases and adjust the result.
+package static immutable ulong[156] POW5_ERRORS = [
+    0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x9555596400000000u,
+    0x65a6569525565555u, 0x4415551445449655u, 0x5105015504144541u, 0x65a69969a6965964u,
+    0x5054955969959656u, 0x5105154515554145u, 0x4055511051591555u, 0x5500514455550115u,
+    0x0041140014145515u, 0x1005440545511051u, 0x0014405450411004u, 0x0414440010500000u,
+    0x0044000440010040u, 0x5551155000004001u, 0x4554555454544114u, 0x5150045544005441u,
+    0x0001111400054501u, 0x6550955555554554u, 0x1504159645559559u, 0x4105055141454545u,
+    0x1411541410405454u, 0x0415555044545555u, 0x0014154115405550u, 0x1540055040411445u,
+    0x0000000500000000u, 0x5644000000000000u, 0x1155555591596555u, 0x0410440054569565u,
+    0x5145100010010005u, 0x0555041405500150u, 0x4141450455140450u, 0x0000000144000140u,
+    0x5114004001105410u, 0x4444100404005504u, 0x0414014410001015u, 0x5145055155555015u,
+    0x0141041444445540u, 0x0000100451541414u, 0x4105041104155550u, 0x0500501150451145u,
+    0x1001050000004114u, 0x5551504400141045u, 0x5110545410151454u, 0x0100001400004040u,
+    0x5040010111040000u, 0x0140000150541100u, 0x4400140400104110u, 0x5011014405545004u,
+    0x0000000044155440u, 0x0000000010000000u, 0x1100401444440001u, 0x0040401010055111u,
+    0x5155155551405454u, 0x0444440015514411u, 0x0054505054014101u, 0x0451015441115511u,
+    0x1541411401140551u, 0x4155104514445110u, 0x4141145450145515u, 0x5451445055155050u,
+    0x4400515554110054u, 0x5111145104501151u, 0x565a655455500501u, 0x5565555555525955u,
+    0x0550511500405695u, 0x4415504051054544u, 0x6555595965555554u, 0x0100915915555655u,
+    0x5540001510001001u, 0x5450051414000544u, 0x1405010555555551u, 0x5555515555644155u,
+    0x5555055595496555u, 0x5451045004415000u, 0x5450510144040144u, 0x5554155555556455u,
+    0x5051555495415555u, 0x5555554555555545u, 0x0000000010005455u, 0x4000005000040000u,
+    0x5565555555555954u, 0x5554559555555505u, 0x9645545495552555u, 0x4000400055955564u,
+    0x0040000000000001u, 0x4004100100000000u, 0x5540040440000411u, 0x4565555955545644u,
+    0x1140659549651556u, 0x0100000410010000u, 0x5555515400004001u, 0x5955545555155255u,
+    0x5151055545505556u, 0x5051454510554515u, 0x0501500050415554u, 0x5044154005441005u,
+    0x1455445450550455u, 0x0010144055144545u, 0x0000401100000004u, 0x1050145050000010u,
+    0x0415004554011540u, 0x1000510100151150u, 0x0100040400001144u, 0x0000000000000000u,
+    0x0550004400000100u, 0x0151145041451151u, 0x0000400400005450u, 0x0000100044010004u,
+    0x0100054100050040u, 0x0504400005410010u, 0x4011410445500105u, 0x0000404000144411u,
+    0x0101504404500000u, 0x0000005044400400u, 0x0000000014000100u, 0x0404440414000000u,
+    0x5554100410000140u, 0x4555455544505555u, 0x5454105055455455u, 0x0115454155454015u,
+    0x4404110000045100u, 0x4400001100101501u, 0x6596955956966a94u, 0x0040655955665965u,
+    0x5554144400100155u, 0xa549495401011041u, 0x5596555565955555u, 0x5569965959549555u,
+    0x969565a655555456u, 0x0000001000000000u, 0x0000000040000140u, 0x0000040100000000u,
+    0x1415454400000000u, 0x5410415411454114u, 0x0400040104000154u, 0x0504045000000411u,
+    0x0000001000000010u, 0x5554000000001040u, 0x5549155551556595u, 0x1455541055515555u,
+    0x0510555454554541u, 0x9555555555540455u, 0x6455456555556465u, 0x4524565555654514u,
+    0x5554655255559545u, 0x9555455441155556u, 0x0000000051515555u, 0x0010005040000550u,
+    0x5044044040000000u, 0x1045040440010500u, 0x0000400000040000u, 0x0000000000000000u,
+];
+
+package static immutable ulong[4][89] GENERIC_POW5_INV_SPLIT = [
+    [                    0u,                    0u,                    0u,   144115188075855872u ],
+    [  1573859546583440065u,  2691002611772552616u,  6763753280790178510u,   141347765182270746u ],
+    [ 12960290449513840412u, 12345512957918226762u, 18057899791198622765u,   138633484706040742u ],
+    [  7615871757716765416u,  9507132263365501332u,  4879801712092008245u,   135971326161092377u ],
+    [  7869961150745287587u,  5804035291554591636u,  8883897266325833928u,   133360288657597085u ],
+    [  2942118023529634767u, 15128191429820565086u, 10638459445243230718u,   130799390525667397u ],
+    [ 14188759758411913794u,  5362791266439207815u,  8068821289119264054u,   128287668946279217u ],
+    [  7183196927902545212u,  1952291723540117099u, 12075928209936341512u,   125824179589281448u ],
+    [  5672588001402349748u, 17892323620748423487u,  9874578446960390364u,   123407996258356868u ],
+    [  4442590541217566325u,  4558254706293456445u, 10343828952663182727u,   121038210542800766u ],
+    [  3005560928406962566u,  2082271027139057888u, 13961184524927245081u,   118713931475986426u ],
+    [ 13299058168408384786u, 17834349496131278595u,  9029906103900731664u,   116434285200389047u ],
+    [  5414878118283973035u, 13079825470227392078u, 17897304791683760280u,   114198414639042157u ],
+    [ 14609755883382484834u, 14991702445765844156u,  3269802549772755411u,   112005479173303009u ],
+    [ 15967774957605076027u,  2511532636717499923u, 16221038267832563171u,   109854654326805788u ],
+    [  9269330061621627145u,  3332501053426257392u, 16223281189403734630u,   107745131455483836u ],
+    [ 16739559299223642282u,  1873986623300664530u,  6546709159471442872u,   105676117443544318u ],
+    [ 17116435360051202055u,  1359075105581853924u,  2038341371621886470u,   103646834405281051u ],
+    [ 17144715798009627550u,  3201623802661132408u,  9757551605154622431u,   101656519392613377u ],
+    [ 17580479792687825857u,  6546633380567327312u, 15099972427870912398u,    99704424108241124u ],
+    [  9726477118325522902u, 14578369026754005435u, 11728055595254428803u,    97789814624307808u ],
+    [   134593949518343635u,  5715151379816901985u,  1660163707976377376u,    95911971106466306u ],
+    [  5515914027713859358u,  7124354893273815720u,  5548463282858794077u,    94070187543243255u ],
+    [  6188403395862945512u,  5681264392632320838u, 15417410852121406654u,    92263771480600430u ],
+    [ 15908890877468271457u, 10398888261125597540u,  4817794962769172309u,    90492043761593298u ],
+    [  1413077535082201005u, 12675058125384151580u,  7731426132303759597u,    88754338271028867u ],
+    [  1486733163972670293u, 11369385300195092554u, 11610016711694864110u,    87050001685026843u ],
+    [  8788596583757589684u,  3978580923851924802u,  9255162428306775812u,    85378393225389919u ],
+    [  7203518319660962120u, 15044736224407683725u,  2488132019818199792u,    83738884418690858u ],
+    [  4004175967662388707u, 18236988667757575407u, 15613100370957482671u,    82130858859985791u ],
+    [ 18371903370586036463u,    53497579022921640u, 16465963977267203307u,    80553711981064899u ],
+    [ 10170778323887491315u,  1999668801648976001u, 10209763593579456445u,    79006850823153334u ],
+    [ 17108131712433974546u, 16825784443029944237u,  2078700786753338945u,    77489693813976938u ],
+    [ 17221789422665858532u, 12145427517550446164u,  5391414622238668005u,    76001670549108934u ],
+    [  4859588996898795878u,  1715798948121313204u,  3950858167455137171u,    74542221577515387u ],
+    [ 13513469241795711526u,   631367850494860526u, 10517278915021816160u,    73110798191218799u ],
+    [ 11757513142672073111u,  2581974932255022228u, 17498959383193606459u,   143413724438001539u ],
+    [ 14524355192525042817u,  5640643347559376447u,  1309659274756813016u,   140659771648132296u ],
+    [  2765095348461978538u, 11021111021896007722u,  3224303603779962366u,   137958702611185230u ],
+    [ 12373410389187981037u, 13679193545685856195u, 11644609038462631561u,   135309501808182158u ],
+    [ 12813176257562780151u,  3754199046160268020u,  9954691079802960722u,   132711173221007413u ],
+    [ 17557452279667723458u,  3237799193992485824u, 17893947919029030695u,   130162739957935629u ],
+    [ 14634200999559435155u,  4123869946105211004u,  6955301747350769239u,   127663243886350468u ],
+    [  2185352760627740240u,  2864813346878886844u, 13049218671329690184u,   125211745272516185u ],
+    [  6143438674322183002u, 10464733336980678750u,  6982925169933978309u,   122807322428266620u ],
+    [  1099509117817174576u, 10202656147550524081u,   754997032816608484u,   120449071364478757u ],
+    [  2410631293559367023u, 17407273750261453804u, 15307291918933463037u,   118136105451200587u ],
+    [ 12224968375134586697u,  1664436604907828062u, 11506086230137787358u,   115867555084305488u ],
+    [  3495926216898000888u, 18392536965197424288u, 10992889188570643156u,   113642567358547782u ],
+    [  8744506286256259680u,  3966568369496879937u, 18342264969761820037u,   111460305746896569u ],
+    [  7689600520560455039u,  5254331190877624630u,  9628558080573245556u,   109319949786027263u ],
+    [ 11862637625618819436u,  3456120362318976488u, 14690471063106001082u,   107220694767852583u ],
+    [  5697330450030126444u, 12424082405392918899u,   358204170751754904u,   105161751436977040u ],
+    [ 11257457505097373622u, 15373192700214208870u,   671619062372033814u,   103142345693961148u ],
+    [ 16850355018477166700u,  1913910419361963966u,  4550257919755970531u,   101161718304283822u ],
+    [  9670835567561997011u, 10584031339132130638u,  3060560222974851757u,    99219124612893520u ],
+    [  7698686577353054710u, 11689292838639130817u, 11806331021588878241u,    97313834264240819u ],
+    [ 12233569599615692137u,  3347791226108469959u, 10333904326094451110u,    95445130927687169u ],
+    [ 13049400362825383933u, 17142621313007799680u,  3790542585289224168u,    93612312028186576u ],
+    [ 12430457242474442072u,  5625077542189557960u, 14765055286236672238u,    91814688482138969u ],
+    [  4759444137752473128u,  2230562561567025078u,  4954443037339580076u,    90051584438315940u ],
+    [  7246913525170274758u,  8910297835195760709u,  4015904029508858381u,    88322337023761438u ],
+    [ 12854430245836432067u,  8135139748065431455u, 11548083631386317976u,    86626296094571907u ],
+    [  4848827254502687803u,  4789491250196085625u,  3988192420450664125u,    84962823991462151u ],
+    [  7435538409611286684u,   904061756819742353u, 14598026519493048444u,    83331295300025028u ],
+    [ 11042616160352530997u,  8948390828345326218u, 10052651191118271927u,    81731096615594853u ],
+    [ 11059348291563778943u, 11696515766184685544u,  3783210511290897367u,    80161626312626082u ],
+    [  7020010856491885826u,  5025093219346041680u,  8960210401638911765u,    78622294318500592u ],
+    [ 17732844474490699984u,  7820866704994446502u,  6088373186798844243u,    77112521891678506u ],
+    [   688278527545590501u,  3045610706602776618u,  8684243536999567610u,    75631741404109150u ],
+    [  2734573255120657297u,  3903146411440697663u,  9470794821691856713u,    74179396127820347u ],
+    [ 15996457521023071259u,  4776627823451271680u, 12394856457265744744u,    72754940025605801u ],
+    [ 13492065758834518331u,  7390517611012222399u,  1630485387832860230u,   142715675091463768u ],
+    [ 13665021627282055864u,  9897834675523659302u, 17907668136755296849u,   139975126841173266u ],
+    [  9603773719399446181u, 10771916301484339398u, 10672699855989487527u,   137287204938390542u ],
+    [  3630218541553511265u,  8139010004241080614u,  2876479648932814543u,   134650898807055963u ],
+    [  8318835909686377084u,  9525369258927993371u,  2796120270400437057u,   132065217277054270u ],
+    [ 11190003059043290163u, 12424345635599592110u, 12539346395388933763u,   129529188211565064u ],
+    [  8701968833973242276u,   820569587086330727u,  2315591597351480110u,   127041858141569228u ],
+    [  5115113890115690487u, 16906305245394587826u,  9899749468931071388u,   124602291907373862u ],
+    [ 15543535488939245974u, 10945189844466391399u,  3553863472349432246u,   122209572307020975u ],
+    [  7709257252608325038u,  1191832167690640880u, 15077137020234258537u,   119862799751447719u ],
+    [  7541333244210021737u,  9790054727902174575u,  5160944773155322014u,   117561091926268545u ],
+    [ 12297384708782857832u,  1281328873123467374u,  4827925254630475769u,   115303583460052092u ],
+    [ 13243237906232367265u, 15873887428139547641u,  3607993172301799599u,   113089425598968120u ],
+    [ 11384616453739611114u, 15184114243769211033u, 13148448124803481057u,   110917785887682141u ],
+    [ 17727970963596660683u,  1196965221832671990u, 14537830463956404138u,   108787847856377790u ],
+    [ 17241367586707330931u,  8880584684128262874u, 11173506540726547818u,   106698810713789254u ],
+    [  7184427196661305643u, 14332510582433188173u, 14230167953789677901u,   104649889046128358u ],
+];
+
+package static immutable ulong[154] POW5_INV_ERRORS = [
+    0x1144155514145504u, 0x0000541555401141u, 0x0000000000000000u, 0x0154454000000000u,
+    0x4114105515544440u, 0x0001001111500415u, 0x4041411410011000u, 0x5550114515155014u,
+    0x1404100041554551u, 0x0515000450404410u, 0x5054544401140004u, 0x5155501005555105u,
+    0x1144141000105515u, 0x0541500000500000u, 0x1104105540444140u, 0x4000015055514110u,
+    0x0054010450004005u, 0x4155515404100005u, 0x5155145045155555u, 0x1511555515440558u,
+    0x5558544555515555u, 0x0000000000000010u, 0x5004000000000050u, 0x1415510100000010u,
+    0x4545555444514500u, 0x5155151555555551u, 0x1441540144044554u, 0x5150104045544400u,
+    0x5450545401444040u, 0x5554455045501400u, 0x4655155555555145u, 0x1000010055455055u,
+    0x1000004000055004u, 0x4455405104000005u, 0x4500114504150545u, 0x0000000014000000u,
+    0x5450000000000000u, 0x5514551511445555u, 0x4111501040555451u, 0x4515445500054444u,
+    0x5101500104100441u, 0x1545115155545055u, 0x0000000000000000u, 0x1554000000100000u,
+    0x5555545595551555u, 0x5555051851455955u, 0x5555555555555559u, 0x0000400011001555u,
+    0x0000004400040000u, 0x5455511555554554u, 0x5614555544115445u, 0x6455156145555155u,
+    0x5455855455415455u, 0x5515555144555545u, 0x0114400000145155u, 0x0000051000450511u,
+    0x4455154554445100u, 0x4554150141544455u, 0x65955555559a5965u, 0x5555555854559559u,
+    0x9569654559616595u, 0x1040044040005565u, 0x1010010500011044u, 0x1554015545154540u,
+    0x4440555401545441u, 0x1014441450550105u, 0x4545400410504145u, 0x5015111541040151u,
+    0x5145051154000410u, 0x1040001044545044u, 0x4001400000151410u, 0x0540000044040000u,
+    0x0510555454411544u, 0x0400054054141550u, 0x1001041145001100u, 0x0000000140000000u,
+    0x0000000014100000u, 0x1544005454000140u, 0x4050055505445145u, 0x0011511104504155u,
+    0x5505544415045055u, 0x1155154445515554u, 0x0000000000004555u, 0x0000000000000000u,
+    0x5101010510400004u, 0x1514045044440400u, 0x5515519555515555u, 0x4554545441555545u,
+    0x1551055955551515u, 0x0150000011505515u, 0x0044005040400000u, 0x0004001004010050u,
+    0x0000051004450414u, 0x0114001101001144u, 0x0401000001000001u, 0x4500010001000401u,
+    0x0004100000005000u, 0x0105000441101100u, 0x0455455550454540u, 0x5404050144105505u,
+    0x4101510540555455u, 0x1055541411451555u, 0x5451445110115505u, 0x1154110010101545u,
+    0x1145140450054055u, 0x5555565415551554u, 0x1550559555555555u, 0x5555541545045141u,
+    0x4555455450500100u, 0x5510454545554555u, 0x1510140115045455u, 0x1001050040111510u,
+    0x5555454555555504u, 0x9954155545515554u, 0x6596656555555555u, 0x0140410051555559u,
+    0x0011104010001544u, 0x965669659a680501u, 0x5655a55955556955u, 0x4015111014404514u,
+    0x1414155554505145u, 0x0540040011051404u, 0x1010000000015005u, 0x0010054050004410u,
+    0x5041104014000100u, 0x4440010500100001u, 0x1155510504545554u, 0x0450151545115541u,
+    0x4000100400110440u, 0x1004440010514440u, 0x0000115050450000u, 0x0545404455541500u,
+    0x1051051555505101u, 0x5505144554544144u, 0x4550545555515550u, 0x0015400450045445u,
+    0x4514155400554415u, 0x4555055051050151u, 0x1511441450001014u, 0x4544554510404414u,
+    0x4115115545545450u, 0x5500541555551555u, 0x5550010544155015u, 0x0144414045545500u,
+    0x4154050001050150u, 0x5550511111000145u, 0x1114504055000151u, 0x5104041101451040u,
+    0x0010501401051441u, 0x0010501450504401u, 0x4554585440044444u, 0x5155555951450455u,
+    0x0040000400105555u, 0x0000000000000001u,
+];
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 70bebb38..6abf0713 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -11,9 +11,6 @@ module mir.bignum.low_level_view;
 import mir.checkedint;
 import std.traits;
 
-version(LDC) import ldc.attributes: optStrategy;
-else struct optStrategy { string opt; }
-
 private alias cop(string op : "-") = subu;
 private alias cop(string op : "+") = addu;
 private enum inverseSign(string op) = op == "+" ? "-" : "+";
@@ -43,40 +40,6 @@ package template MaxWordPow10(T)
         static assert(0);
 }
 
-package template MaxWordPow5(T)
-{
-    static if (is(T == ubyte))
-        enum MaxWordPow5 = 3;
-    else
-    static if (is(T == ushort))
-        enum MaxWordPow5 = 6;
-    else
-    static if (is(T == uint))
-        enum MaxWordPow5 = 13;
-    else
-    static if (is(T == ulong))
-        enum MaxWordPow5 = 27;
-    else
-        static assert(0);
-}
-
-package template MaxFpPow5(T)
-{
-    static if (T.mant_dig == 24)
-        enum MaxFpPow5 = 6;
-    else
-    static if (T.mant_dig == 53)
-        enum MaxFpPow5 = 10;
-    else
-    static if (T.mant_dig == 64)
-        enum MaxFpPow5 = 27;
-    else
-    static if (T.mant_dig == 113)
-        enum MaxFpPow5 = 48;
-    else
-        static assert(0, "floating point format isn't supported");
-}
-
 /++
 Fast integer computation of `ceil(log10(exp2(e)))` with 64-bit mantissa precision.
 The result is guaranted to be greater then `log10(exp2(e))`, which is irrational number.
@@ -132,198 +95,36 @@ struct BigUIntView(W)
     }
 
     ///
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-        import mir.bignum.fp;
-        enum md = T.mant_dig;
-        enum b = size_t.sizeof * 8;
-        enum n = md / b + (md % b != 0);
-        enum s = n * b;
-        return this.opCast!(Fp!s, s - md, wordNormalized, nonZero).opCast!(T, true);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    unittest
-    {
-        auto a = cast(double) BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
-        assert(a == 0xa.fbbfae3cd0bp+124);
-        assert(cast(double) BigUIntView!size_t.init == 0);
-        assert(cast(double) BigUIntView!size_t([0]) == 0);
+        import mir.bignum.internal.dec2float: decimalTo;
+        return normalized.coefficients.decimalTo!T;
     }
 
     ///
     @safe
-    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
-        if (internalRoundLastBits < size_t.sizeof * 8)
+    T opCast(T : Fp!coefficientSize, uint coefficientSize)() const
     {
         static if (isMutable!W)
         {
-            return lightConst.opCast!(T, internalRoundLastBits, wordNormalized, nonZero);
+            return lightConst.opCast!T;
         }
         else
         static if (W.sizeof > size_t.sizeof)
         {
-            return lightConst.opCast!(BigUIntView!size_t).opCast!(T, internalRoundLastBits, false, nonZero);
+            return lightConst.opCast!(BigUIntView!size_t).opCast!T;
         }
         else
         {
-            import mir.utility: _expect;
-            import mir.bitop: ctlz;
-            Fp!coefficientSize ret;
-            auto integer = lightConst;
-            static if (!wordNormalized)
-                integer = integer.normalized;
-            static if (!nonZero)
-                if (integer.coefficients.length == 0)
-                    goto R;
-            {
-                assert(integer.coefficients.length);
-                enum N = ret.coefficient.data.length;
-                sizediff_t size = integer.coefficients.length * (W.sizeof * 8);
-                sizediff_t expShift = size - coefficientSize;
-                ret.exponent = expShift;
-                if (_expect(expShift <= 0, true))
-                {
-                    static if (N == 1 && W.sizeof == size_t.sizeof)
-                    {
-                        ret.coefficient.data[0] = integer.coefficients[$ - 1];
-                    }
-                    else
-                    {
-                        BigUIntView!size_t(ret.coefficient.data)
-                            .opCast!(BigUIntView!(Unqual!W))
-                            .coefficients
-                                [$ - integer.coefficients.length .. $] = integer.coefficients;
-                    }
-                    auto c = cast(uint) ctlz(ret.coefficient.view.coefficients[$ - 1]);
-                    ret.exponent -= c;
-                    ret.coefficient = ret.coefficient.smallLeftShift(c);
-                }
-                else
-                {
-                    UInt!(coefficientSize + size_t.sizeof * 8) holder;
-
-
-                    static if (N == 1 && W.sizeof == size_t.sizeof)
-                    {
-                        holder.data[0] = integer.mostSignificantFirst[1];
-                        holder.data[1] = integer.mostSignificantFirst[0];
-                    }
-                    else
-                    {
-                        auto holderView = holder
-                            .view
-                            .opCast!(BigUIntView!(Unqual!W))
-                            .coefficients;
-                        import mir.utility: min;
-                        auto minLength = min(integer.coefficients.length, holderView.length);
-                        holderView[$ - minLength .. $] = integer.coefficients[$ - minLength .. $];
-                    }
-
-                    auto c = cast(uint) ctlz(holder.view.coefficients[$ - 1]);
-                    ret.exponent -= c;
-                    holder = holder.smallLeftShift(c);
-                    ret.coefficient = holder.toSize!(coefficientSize, false);
-                    auto tail = BigUIntView!size_t(holder.data).coefficients[0];
-
-                    bool nonZeroTail()
-                    {
-                        while(_expect(integer.coefficients[0] == 0, false))
-                        {
-                            integer.popLeastSignificant;
-                            assert(integer.coefficients.length);
-                        }
-                        return integer.coefficients.length > (N + 1) * (size_t.sizeof / W.sizeof);
-                    }
-
-                    static if (internalRoundLastBits)
-                    {
-                        enum half = size_t(1) << (internalRoundLastBits - 1);
-                        enum mask0 = (size_t(1) << internalRoundLastBits) - 1;
-                        auto tail0 = BigUIntView!size_t(ret.coefficient.data).coefficients[0] & mask0;
-                        BigUIntView!size_t(ret.coefficient.data).coefficients[0] &= ~mask0;
-                        auto condInc = tail0 >= half
-                            && (   tail0 > half
-                                || tail
-                                || (BigUIntView!size_t(ret.coefficient.data).coefficients[0] & 1)
-                                || nonZeroTail);
-                    }
-                    else
-                    {
-                        enum half = cast(size_t)Signed!size_t.min;
-                        auto condInc = tail >= half
-                            && (    tail > half
-                                || (BigUIntView!size_t(ret.coefficient.data).coefficients[0] & 1)
-                                || nonZeroTail);
-                    }
-
-                    if (condInc)
-                    {
-                        enum inc = size_t(1) << internalRoundLastBits;
-                        if (auto overflow = ret.coefficient += inc)
-                        {
-                            import mir.bignum.fp: half;
-                            ret.coefficient = half!coefficientSize;
-                            ret.exponent++;
-                        }
-                    }
-                }
-            }
-        R:
-            return ret;
+            import mir.bignum.internal.dec2float: decimalToFp;
+            auto coefficients = normalized.coefficients;
+            return coefficients.length
+                ? coefficients.decimalToFp!coefficientSize
+                : Fp!coefficientSize.init;
         }
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        import mir.bignum.fp: Fp;
-        import mir.bignum.fixed: UInt;
-
-        auto fp = cast(Fp!128) BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
-        assert(fp.exponent == 0);
-        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
-
-        fp = cast(Fp!128) BigUIntView!uint.fromHexString("ae3cd0aff2714a1de7022b0029d");
-        assert(fp.exponent == -20);
-        assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000"));
-
-        fp = cast(Fp!128) BigUIntView!ushort.fromHexString("e7022b0029d");
-        assert(fp.exponent == -84);
-        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
-
-        fp = cast(Fp!128) BigUIntView!ubyte.fromHexString("e7022b0029d");
-        assert(fp.exponent == -84);
-        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
-
-        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
-        assert(fp.exponent == -84);
-        assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
-    
-        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff1000000000000000");
-        assert(fp.exponent == 64);
-        assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
-
-        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff8000000000000000");
-        assert(fp.exponent == 65);
-        assert(fp.coefficient == UInt!128.fromHexString("80000000000000000000000000000000"));
-
-        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000000");
-        assert(fp.exponent == 64);
-        assert(fp.coefficient == UInt!128.fromHexString("fffffffffffffffffffffffffffffffe"));
-
-        fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000001");
-        assert(fp.exponent == 64);
-        assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
-    }
-
-
     ///
     T opCast(T, bool nonZero = false)() const scope
         if (isIntegral!T && isUnsigned!T && isMutable!T)
@@ -354,40 +155,6 @@ struct BigUIntView(W)
         }
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto view = BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
-        assert(cast(ulong) view == 0x14a1de7022b0029d);
-        assert(cast(uint) view == 0x22b0029d);
-        assert(cast(ubyte) view == 0x9d);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto view = BigUIntView!ushort.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
-        assert(cast(ulong) view == 0x14a1de7022b0029d);
-        assert(cast(uint) view == 0x22b0029d);
-        assert(cast(ubyte) view == 0x9d);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto view = BigUIntView!uint.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
-        assert(cast(ulong) view == 0x14a1de7022b0029d);
-        assert(cast(uint) view == 0x22b0029d);
-        assert(cast(ubyte) view == 0x9d);
-    }
-
     ///
     pure nothrow @nogc
     BigUIntView!V opCast(T : BigUIntView!V, V)()
@@ -476,19 +243,6 @@ struct BigUIntView(W)
         d[0] <<= shift;
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
-        a.smallLeftShiftInPlace(4);
-        assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
-        a.smallLeftShiftInPlace(0);
-        assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
-    }
-
     /++
     Shifts right using at most `size_t.sizeof * 8 - 1` bits
     +/
@@ -505,17 +259,6 @@ struct BigUIntView(W)
         d[$ - 1] >>>= shift;
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
-        a.smallRightShiftInPlace(4);
-        assert(a == BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
-    }
-
     /++
     +/
     static BigUIntView fromHexString(C, bool allowUnderscores = false)(scope const(C)[] str)
@@ -635,46 +378,6 @@ struct BigUIntView(W)
         return true;
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto view = BigUIntView!size_t.fromHexString!(char, true)("abcd_efab_cdef");
-        assert(cast(ulong)view == 0xabcd_efab_cdef);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        // Check that invalid underscores in hex literals throw an error.
-        void expectThrow(const(char)[] input) {
-            bool caught = false;
-            try { 
-                auto view = BigUIntView!size_t.fromHexString!(char, true)(input);
-            } catch (Exception e) {
-                caught = true;
-            }
-
-            assert(caught);
-        }
-
-        expectThrow("abcd_efab_cef_");
-        expectThrow("abcd__efab__cef");
-        expectThrow("_abcd_efab_cdef");
-        expectThrow("_abcd_efab_cdef_");
-        expectThrow("_abcd_efab_cdef__");
-        expectThrow("__abcd_efab_cdef");
-        expectThrow("__abcd_efab_cdef_");
-        expectThrow("__abcd_efab_cdef__");
-        expectThrow("__abcd__efab_cdef__");
-        expectThrow("__abcd__efab__cdef__");
-    }
- 
     /++
     +/
     static BigUIntView fromBinaryString(C, bool allowUnderscores = false)(scope const(C)[] str)
@@ -774,45 +477,6 @@ struct BigUIntView(W)
         return true;
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto view = BigUIntView!size_t.fromBinaryString!(char, true)("1111_0000_0101");
-        assert(cast(ulong)view == 0b1111_0000_0101);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        // Check that invalid underscores in hex literals throw an error.
-        void expectThrow(const(char)[] input) {
-            bool caught = false;
-            try { 
-                auto view = BigUIntView!size_t.fromBinaryString!(char, true)(input);
-            } catch (Exception e) {
-                caught = true;
-            }
-
-            assert(caught);
-        }
-
-        expectThrow("abcd");
-        expectThrow("0101__1011__0111");
-        expectThrow("_0101_1011_0111");
-        expectThrow("_0101_1011_0111_");
-        expectThrow("_0101_1011_0111__");
-        expectThrow("__0101_1011_0111_");
-        expectThrow("__0101_1011_0111__");
-        expectThrow("__0101__1011_0111__");
-        expectThrow("__1011__0111__1011__");
-    }
-
     static if (isMutable!W && W.sizeof >= 4)
     /++
     Returns: false in case of overflow or incorrect string.
@@ -850,7 +514,7 @@ struct BigUIntView(W)
         if (d >= 10)
             return false;
 
-        auto work = topLeastSignificantPart(1);
+        size_t len = 1;
         goto S;
 
         for(;;)
@@ -868,12 +532,11 @@ struct BigUIntView(W)
             if (_expect(t == mp10 || str.length == 0, false))
             {
             L:
-                if (auto overflow = work.opOpAssign!"*"(t, v))
+                if (auto overflow = topLeastSignificantPart(len).opOpAssign!"*"(t, v))
                 {
-                    if (_expect(work.coefficients.length < coefficients.length, true))
+                    if (_expect(len < coefficients.length, true))
                     {
-                        work = topLeastSignificantPart(work.coefficients.length + 1);
-                        work.coefficients[$ - 1] = overflow;
+                        coefficients[len++] = overflow;
                     }
                     else
                     {
@@ -884,7 +547,7 @@ struct BigUIntView(W)
                 t = 1;
                 if (str.length == 0)
                 {
-                    this = work;
+                    this = topLeastSignificantPart(len);
                     return true;
                 }
             }
@@ -1322,21 +985,205 @@ struct BigUIntView(W)
         return str.length - i;
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure @nogc
-    unittest
-    {
-        import mir.bignum.integer;
+}
 
-        auto a = BigInt!2("123456789098765432123456789098765432100");
-        char[ceilLog10Exp2(a.data.length * (size_t.sizeof * 8))] buffer;
-        auto len = a.view.unsigned.toStringImpl(buffer);
-        assert(buffer[$ - len .. $] == "123456789098765432123456789098765432100");
-    }
+///
+version(mir_bignum_test_llv)
+@safe pure @nogc
+unittest
+{
+    import mir.bignum.integer;
+
+    auto a = BigInt!2("123456789098765432123456789098765432100");
+    char[ceilLog10Exp2(a.data.length * (size_t.sizeof * 8))] buffer;
+    auto len = a.view.unsigned.toStringImpl(buffer);
+    assert(buffer[$ - len .. $] == "123456789098765432123456789098765432100");
+}
+
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    auto view = BigUIntView!size_t.fromHexString!(char, true)("abcd_efab_cdef");
+    assert(cast(ulong)view == 0xabcd_efab_cdef);
+}
+
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+    a.smallLeftShiftInPlace(4);
+    assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
+    a.smallLeftShiftInPlace(0);
+    assert(a == BigUIntView!size_t.fromHexString("fbbfae3cd0aff2714a1de7022b0029d0"));
+}
+
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    auto a = BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+    a.smallRightShiftInPlace(4);
+    assert(a == BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029"));
+}
+
+
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    // Check that invalid underscores in hex literals throw an error.
+    void expectThrow(const(char)[] input) {
+        bool caught = false;
+        try { 
+            auto view = BigUIntView!size_t.fromHexString!(char, true)(input);
+        } catch (Exception e) {
+            caught = true;
+        }
+
+        assert(caught);
+    }
+
+    expectThrow("abcd_efab_cef_");
+    expectThrow("abcd__efab__cef");
+    expectThrow("_abcd_efab_cdef");
+    expectThrow("_abcd_efab_cdef_");
+    expectThrow("_abcd_efab_cdef__");
+    expectThrow("__abcd_efab_cdef");
+    expectThrow("__abcd_efab_cdef_");
+    expectThrow("__abcd_efab_cdef__");
+    expectThrow("__abcd__efab_cdef__");
+    expectThrow("__abcd__efab__cdef__");
+}
+
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    auto view = BigUIntView!size_t.fromBinaryString!(char, true)("1111_0000_0101");
+    assert(cast(ulong)view == 0b1111_0000_0101);
+}
+
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    // Check that invalid underscores in hex literals throw an error.
+    void expectThrow(const(char)[] input) {
+        bool caught = false;
+        try { 
+            auto view = BigUIntView!size_t.fromBinaryString!(char, true)(input);
+        } catch (Exception e) {
+            caught = true;
+        }
+
+        assert(caught);
+    }
+
+    expectThrow("abcd");
+    expectThrow("0101__1011__0111");
+    expectThrow("_0101_1011_0111");
+    expectThrow("_0101_1011_0111_");
+    expectThrow("_0101_1011_0111__");
+    expectThrow("__0101_1011_0111_");
+    expectThrow("__0101_1011_0111__");
+    expectThrow("__0101__1011_0111__");
+    expectThrow("__1011__0111__1011__");
+}
+
+///
+version(mir_bignum_test_llv)
+unittest
+{
+    auto a = cast(double) BigUIntView!size_t.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+    assert(a == 0xa.fbbfae3cd0bp+124);
+    assert(cast(double) BigUIntView!size_t.init == 0);
+    assert(cast(double) BigUIntView!size_t([0]) == 0);
 }
 
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    import mir.bignum.fp: Fp;
+    import mir.bignum.fixed: UInt;
+
+    auto fp = cast(Fp!128) BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+    assert(fp.exponent == 0);
+    assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+
+    fp = cast(Fp!128) BigUIntView!uint.fromHexString("ae3cd0aff2714a1de7022b0029d");
+    assert(fp.exponent == -20);
+    assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000"));
+
+    fp = cast(Fp!128) BigUIntView!ushort.fromHexString("e7022b0029d");
+    assert(fp.exponent == -84);
+    assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+
+    fp = cast(Fp!128) BigUIntView!ubyte.fromHexString("e7022b0029d");
+    assert(fp.exponent == -84);
+    assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
+    assert(fp.exponent == -84);
+    assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
+
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff1000000000000000");
+    assert(fp.exponent == 64);
+    assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
+
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ffffffffffffffffffffffffffffffff8000000000000000");
+    assert(fp.exponent == 65);
+    assert(fp.coefficient == UInt!128.fromHexString("80000000000000000000000000000000"));
+
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000000");
+    assert(fp.exponent == 64);
+    assert(fp.coefficient == UInt!128.fromHexString("fffffffffffffffffffffffffffffffe"));
+
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("fffffffffffffffffffffffffffffffe8000000000000001");
+    assert(fp.exponent == 64);
+    assert(fp.coefficient == UInt!128.fromHexString("ffffffffffffffffffffffffffffffff"));
+}
+
+///
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    auto view = BigUIntView!ulong.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+    assert(cast(ulong) view == 0x14a1de7022b0029d);
+    assert(cast(uint) view == 0x22b0029d);
+    assert(cast(ubyte) view == 0x9d);
+}
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    auto view = BigUIntView!ushort.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+    assert(cast(ulong) view == 0x14a1de7022b0029d);
+    assert(cast(uint) view == 0x22b0029d);
+    assert(cast(ubyte) view == 0x9d);
+}
+
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    auto view = BigUIntView!uint.fromHexString("afbbfae3cd0aff2714a1de7022b0029d");
+    assert(cast(ulong) view == 0x14a1de7022b0029d);
+    assert(cast(uint) view == 0x22b0029d);
+    assert(cast(ubyte) view == 0x9d);
+}
+
+
 ///
 version(mir_bignum_test_llv)
 @safe pure nothrow
@@ -1464,7 +1311,10 @@ struct BigIntView(W)
             str = str[1 .. $];
         }
 
-        return unsigned.fromStringImpl(str);
+        if (!unsigned.fromStringImpl(str))
+            return false;
+        sign &= unsigned.coefficients.length != 0;
+        return true;
     }
 
     /++
@@ -1562,55 +1412,15 @@ struct BigIntView(W)
     }
 
     ///
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-        auto ret = this.unsigned.opCast!(T, wordNormalized, nonZero);
+        auto ret = this.unsigned.opCast!T;
         if (sign)
             ret = -ret;
         return ret;
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto a = cast(double) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
-        assert(a == -0xa.fbbfae3cd0bp+124);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto a = cast(double) BigIntView!size_t.fromBinaryString("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001010011101");
-        assert(a == -0xa.fbbfae3cd0bp+124);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto a = cast(double) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
-        assert(a == -0xa.fbbfae3cd0bp+124);
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        auto a = cast(double) BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_1001_1101");
-        assert(a == -0xa.fbbfae3cd0bp+124);
-    }
-
     ///
     T opCast(T, bool nonZero = false)() const
         if (is(T == long) || is(T == int))
@@ -1623,7 +1433,7 @@ struct BigIntView(W)
 
     static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1634,7 +1444,7 @@ struct BigIntView(W)
 
     static if (W.sizeof == size_t.sizeof)
     ///
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1644,7 +1454,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1664,7 +1474,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1674,7 +1484,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1684,7 +1494,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1694,7 +1504,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1704,7 +1514,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1714,7 +1524,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1724,7 +1534,7 @@ struct BigIntView(W)
     }
 
     static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
+    version(mir_bignum_test)
     @safe pure
     unittest
     {
@@ -1745,43 +1555,13 @@ struct BigIntView(W)
 
     /++
     +/
-    T opCast(T : Fp!coefficientSize, size_t internalRoundLastBits = 0, bool wordNormalized = false, bool nonZero = false, size_t coefficientSize)() const
-        if (internalRoundLastBits < size_t.sizeof * 8)
+    T opCast(T : Fp!coefficientSize, uint coefficientSize)() const
     {
-        auto ret = unsigned.opCast!(Fp!coefficientSize, internalRoundLastBits, wordNormalized, nonZero);
+        auto ret = unsigned.opCast!(Fp!coefficientSize);
         ret.sign = sign;
         return ret;
     }
 
-    static if (W.sizeof == size_t.sizeof)
-    ///
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        import mir.bignum.fixed: UInt;
-        import mir.bignum.fp: Fp;
-
-        auto fp = cast(Fp!128) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
-        assert(fp.sign);
-        assert(fp.exponent == 0);
-        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
-    }
-
-    static if (W.sizeof == size_t.sizeof)
-    version(mir_bignum_test_llv)
-    @safe pure
-    unittest
-    {
-        import mir.bignum.fixed: UInt;
-        import mir.bignum.fp: Fp;
-
-        auto fp = cast(Fp!128) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
-        assert(fp.sign);
-        assert(fp.exponent == 0);
-        assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
-    }
-
     ///
     BigIntView!V opCast(T : BigIntView!V, V)()
         if (V.sizeof <= W.sizeof)
@@ -2018,6 +1798,69 @@ struct BigIntView(W)
     }
 }
 
+///
+version(mir_bignum_test)
+@safe pure
+unittest
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp;
+
+    auto fp = cast(Fp!128) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
+    assert(fp.sign);
+    assert(fp.exponent == 0);
+    assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+}
+
+///
+version(mir_bignum_test)
+@safe pure
+unittest
+{
+    auto a = cast(double) BigIntView!size_t.fromHexString("-afbbfae3cd0aff2714a1de7022b0029d");
+    assert(a == -0xa.fbbfae3cd0bp+124);
+}
+
+///
+version(mir_bignum_test)
+@safe pure
+unittest
+{
+    auto a = cast(double) BigIntView!size_t.fromBinaryString("-10101111101110111111101011100011110011010000101011111111001001110001010010100001110111100111000000100010101100000000001010011101");
+    assert(a == -0xa.fbbfae3cd0bp+124);
+}
+
+///
+version(mir_bignum_test)
+@safe pure
+unittest
+{
+    auto a = cast(double) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
+    assert(a == -0xa.fbbfae3cd0bp+124);
+}
+
+///
+version(mir_bignum_test)
+@safe pure
+unittest
+{
+    auto a = cast(double) BigIntView!size_t.fromBinaryString!(char, true)("-1010_1111_1011_1011_1111_1010_1110_0011_1100_1101_0000_1010_1111_1111_0010_0111_0001_0100_1010_0001_1101_1110_0111_0000_0010_0010_1011_0000_0000_0010_1001_1101");
+    assert(a == -0xa.fbbfae3cd0bp+124);
+}
+
+version(mir_bignum_test_llv)
+@safe pure
+unittest
+{
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp;
+
+    auto fp = cast(Fp!128) BigIntView!size_t.fromHexString!(char, true)("-afbb_fae3_cd0a_ff27_14a1_de70_22b0_029d");
+    assert(fp.sign);
+    assert(fp.exponent == 0);
+    assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
+}
+
 ///
 version(mir_bignum_test_llv)
 @safe pure nothrow
@@ -2128,7 +1971,7 @@ enum DecimalExponentKey
 /++
 +/
 struct DecimalView(W)
-    if (isUnsigned!W)
+    if (is(Unqual!W == size_t))
 {
     ///
     bool sign;
@@ -2431,156 +2274,16 @@ struct DecimalView(W)
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.
     +/
-    T opCast(T, bool wordNormalized = false, bool nonZero = false)() const
+    T opCast(T, bool wordNormalized = false)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-        version(LDC)
-        {
-            static if (wordNormalized)
-                pragma(inline, true);
-        }
-
-        import mir.bignum.fixed: UInt;
-        import mir.bignum.fp: Fp, extendedMul;
-        import mir.bignum.internal.dec2flt_table;
-        import mir.math.common: floor;
-        import mir.utility: _expect;
+        import mir.bignum.internal.dec2float;
 
         auto coeff = coefficient.lightConst;
-        T ret = 0;
 
         static if (!wordNormalized)
             coeff = coeff.normalized;
-
-        if (_expect(exponent == exponent.max, false))
-        {
-            ret = coeff.coefficients.length ? T.nan : T.infinity;
-            goto R;
-        }
-
-        static if (!nonZero)
-            if (coeff.coefficients.length == 0)
-                goto R;
-
-        enum S = 9;
-
-        static if (T.mant_dig < 64)
-        {
-            Fp!64 load(long e)
-            {
-                auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
-                auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
-                return Fp!64(false, p10exp, UInt!64(p10coeff));
-            }
-            {
-                auto expSign = exponent < 0;
-                if (_expect((expSign ? -exponent : exponent) >>> S == 0, true))
-                {
-                    enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
-                    enum ulong half = (1UL << (64 - T.mant_dig - 1));
-                    enum ulong bound = ulong(1) << T.mant_dig;
-
-                    auto c = coeff.opCast!(Fp!64, 0, true, true);
-                    auto z = c.extendedMul(load(exponent));
-                    ret = cast(T) z;
-                    auto slop = (coeff.coefficients.length > (ulong.sizeof / W.sizeof)) + 3 * expSign;
-                    long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
-                    if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
-                        goto R;
-                    if (slop == 0 && exponent <= MaxWordPow5!ulong || exponent == 0)
-                        goto R;
-                    if (slop == 3 && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
-                    {
-                        auto e = load(-exponent);
-                        ret =  c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
-                        goto R;
-                    }
-                    ret = algoR!T(ret, coeff, cast(int) exponent);
-                    goto R;
-                }
-                ret = expSign ? 0 : T.infinity;
-                goto R;
-            }
-        }
-        else
-        {
-            enum P = 1 << S;
-            static assert(min_p10_e <= -P);
-            static assert(max_p10_e >= P);
-            Fp!128 load(long e)
-            {
-                auto idx = cast(sizediff_t)e - min_p10_e;
-                ulong h = p10_coefficients[idx][0];
-                ulong l = p10_coefficients[idx][1];
-                if (l >= cast(ulong)long.min)
-                    h--;
-                version(BigEndian)
-                    auto p10coeff = UInt!128(cast(ulong[2])[h, l]);
-                else
-                    auto p10coeff = UInt!128(cast(ulong[2])[l, h]);
-                auto p10exp = p10_exponents[idx] - 64;
-                return Fp!128(false, p10exp, p10coeff);
-            }
-
-            {
-                auto expSign = exponent < 0;
-                ulong exp = exponent;
-                exp = expSign ? -exp : exp;
-                if (exp >= 5000)
-                {
-                    ret = expSign ? 0 : T.infinity;
-                    goto R;
-                }
-                long index = exp & 0x1FF;
-                bool gotoAlgoR;
-                auto c = load(expSign ? -index : index);
-                {
-                    exp >>= S;
-                    gotoAlgoR = exp != 0;
-                    if (_expect(gotoAlgoR, false))
-                    {
-                        auto v = load(expSign ? -P : P);
-                        do
-                        {
-                            if (exp & 1)
-                                c *= v;
-                            exp >>>= 1;
-                            if (exp == 0)
-                                break;
-                            v *= v;
-                        }
-                        while(true);
-                    }
-                }
-                auto z = coeff.opCast!(Fp!128, 0, true, true).extendedMul(c);
-                ret = cast(T) z;
-                if (!gotoAlgoR)
-                {
-                    static if (T.mant_dig == 64)
-                        enum ulong mask = ulong.max;
-                    else
-                        enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
-                    enum ulong half = (1UL << (128 - T.mant_dig - 1));
-                    enum UInt!128 bound = UInt!128(1) << T.mant_dig;
-
-                    auto slop = (coeff.coefficients.length > (ulong.sizeof * 2 / W.sizeof)) + 3 * expSign;
-                    long bitsDiff = (cast(ulong) z.opCast!(Fp!128).coefficient & mask) - half;
-                    if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
-                        goto R;
-                    if (slop == 0 && exponent <= 55 || exponent == 0)
-                        goto R;
-                    if (slop == 3 && MaxFpPow5!T >= -exponent && c.coefficient < bound)
-                    {
-                        auto e = load(-exponent);
-                        ret =  c.opCast!(T, true) / cast(T) e;
-                        goto R;
-                    }
-                }
-                ret = algoR!T(ret, coeff, cast(int) exponent);
-                goto R;
-            }
-        }
-    R:
+        auto ret = decimalTo!T(coeff.coefficients, exponent);
         if (sign)
             ret = -ret;
         return ret;
@@ -2589,7 +2292,6 @@ struct DecimalView(W)
 
 version(none)
 ///
-version(mir_bignum_test)
 unittest
 {
     {
@@ -2619,379 +2321,286 @@ unittest
     }
 }
 
-@optStrategy("minsize")
-package T algoR(T, W)(T ret, scope BigUIntView!(const W) coeff, int exponent)
-{
-    pragma(inline, false);
-
-    import mir.bignum.fixed: UInt;
-    import mir.bignum.integer: BigInt;
-    import mir.math.common: floor;
-    import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
-    import mir.utility: _expect;
-
-    BigInt!256 x = void, y = void; // max value is 2^(2^14)-1
-    if (exponent >= 0)
-    {
-        if (!x.copyFrom(coeff) && !x.mulPow5(exponent)) // if no overflow
-            ret = ldexp(cast(T) x, exponent);
-    }
-    else do
-    {
-        if (ret < ret.min_normal)
-            break;
-        int k;
-        auto m0 = frexp(ret, k);
-        k -= T.mant_dig;
-        static if (T.mant_dig <= 64)
-        {
-            enum p2 = T(2) ^^ T.mant_dig;
-            auto m = UInt!64(cast(ulong) (m0 * p2));
-        }
-        else
-        {
-            enum p2h = T(2) ^^ (T.mant_dig - 64);
-            enum p2l = T(2) ^^ 64;
-            m0 *= p2h;
-            auto mhf = floor(m0);
-            auto mh = cast(ulong) mhf;
-            m0 -= mhf;
-            m0 *= p2l;
-            auto ml = cast(ulong) m0;
-            auto m = UInt!128(mh);
-            m <<= 64;
-            m |= UInt!128(ml);
-        }
-        auto mtz = m.cttz;
-        if (mtz != m.sizeof * 8)
-        {
-            m >>= mtz;
-            k += mtz;
-        }
-
-        if (x.copyFrom(coeff)) // if overflow
-            break;
-        y.__ctor(m);
-        y.mulPow5(-exponent);
-        auto shift = k - exponent;
-        (shift >= 0  ? y : x) <<= shift >= 0 ? shift : -shift;
-        x -= y;
-        if (x.length == 0)
-            break;
-        x <<= 1;
-        x *= m;
-        auto cond = mtz == T.mant_dig - 1 && x.sign;
-        auto cmp = x.view.unsigned.opCmp(y.view.unsigned);
-        if (cmp < 0)
-        {
-            if (!cond)
-                break;
-            x <<= 1;
-            if (x.view.unsigned <= y.view.unsigned)
-                break;
-        }
-        else
-        if (!cmp && !cond && !mtz)
-            break;
-        ret = x.sign ? nextDown(ret) : nextUp(ret);
-        if (ret == 0)
-            break;
-    }
-    while (T.mant_dig >= 64 && exponent < -512);
-    return ret;
-}
-
 ///
 version(mir_bignum_test_llv)
 unittest
 {
-    alias AliasSeq(T...) = T;
-
-    foreach (T; AliasSeq!(float, double, real))
-    {{
-        T value = 3.518437208883201171875E+013;
-        
-    }}
-
-    foreach(W; AliasSeq!(ulong, uint, ushort, ubyte))
-    {{
-        auto view = DecimalView!W(false, -8, BigUIntView!W.fromHexString("BEBC2000000011E1A3"));
-
-        assert (cast(float)view == 3.518437208883201171875E+013f);
-        assert (cast(double)view == 3.518437208883201171875E+013);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 3.518437208883201171875E+013L);
-
-        view = DecimalView!W(true, -169, BigUIntView!W.fromHexString("5A174AEDA65CC"));
-        assert (cast(float)view == -0);
-        assert (cast(double)view == -0x1.1p-511);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == -0x8.80000000000019fp-514L);
-
-        view = DecimalView!W(true, 293, BigUIntView!W.fromHexString("36496F6C4ED38"));
-        assert (cast(float)view == -float.infinity);
-        assert (cast(double)view == -9.55024478104888e+307);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == -9.55024478104888e+307L);
-
-        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 1);
-        assert (cast(double)view == 1);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1L);
-
-        view = DecimalView!W(false, -5, BigUIntView!W.fromHexString("3"));
-        assert (cast(float)view == 3e-5f);
-        assert (cast(double)view == 3e-5);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 3e-5L);
-
-        view = DecimalView!W(false, -1, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0.1f);
-        assert (cast(double)view == 0.1);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0.1L);
-
-        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("3039"));
-        assert (cast(float)view == 12345.0f);
-        assert (cast(double)view == 12345.0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 12345.0L);
-
-        view = DecimalView!W(false, -7, BigUIntView!W.fromHexString("98967F"));
-        assert (cast(float)view == 0.9999999f);
-        assert (cast(double)view == 0.9999999);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0.9999999L);
-
-        view = DecimalView!W(false, -324, BigUIntView!W.fromHexString("4F0CEDC95A718E"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 2.2250738585072014e-308);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 2.2250738585072014e-308L);
-
-        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("1FFFFFFFFFFFFFFFD"));
-        assert (cast(float)view == 36893488147419103229f);
-        assert (cast(double)view == 36893488147419103229.0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x1FFFFFFFFFFFFFFFDp0L);
-
-        view = DecimalView!W(false, -33, BigUIntView!W.fromHexString("65"));
-        assert (cast(float)view == 101e-33f);
-        assert (cast(double)view == 101e-33);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 101e-33L);
-
-        view = DecimalView!W(false, 23, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 1e23f);
-        assert (cast(double)view == 1e23);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 1e23L);
-
-        view = DecimalView!W(false, 23, BigUIntView!W.fromHexString("81B"));
-        assert (cast(float)view == 2075e23f);
-        assert (cast(double)view == 0xaba3d58a1f1a98p+32);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xaba3d58a1f1a9cp+32L);
-    
-        view = DecimalView!W(false, -23, BigUIntView!W.fromHexString("2209"));
-        assert (cast(float)view == 8713e-23f);
-        assert (cast(double)view == 0x1.9b75b4e7de2b9p-64);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xc.dbada73ef15c401p-67L);
-
-        view = DecimalView!W(false, 300, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == 0x1.7e43c8800759cp+996);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xb.f21e44003acdd2dp+993L);
-
-        view = DecimalView!W(false, 245, BigUIntView!W.fromHexString("B3A73CEB227"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == 0x1.48e3735333cb6p+857);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xa.471b9a999e5b01ep+854L);
-
-        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == 0x1.117e8e90a0ff7p+239);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.8bf4748507fb99p+236L);
-
-        view = DecimalView!W(false, -324, BigUIntView!W.fromHexString("5"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0x0.0000000000001p-1022);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.18995ce7aa0e1b2p-1077L);
-
-        view = DecimalView!W(false, -324, BigUIntView!W.fromHexString("5B"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0x0.0000000000012p-1022);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x9.3594d9adeb09a55p-1073L);
-
-        view = DecimalView!W(false, -322, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0x0.0000000000014p-1022);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xa.1ebfb4219491a1fp-1073L);
-
-        view = DecimalView!W(false, -320, BigUIntView!W.fromHexString("CA1CCB"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0x0.000063df832d9p-1022);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xc.7bf065b215888c7p-1043L);
-
-        view = DecimalView!W(false, -319, BigUIntView!W.fromHexString("33CE7943FB"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0x1.000000000162p-1022);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.000000000b103b6p-1025L);
-
-        view = DecimalView!W(false, -309, BigUIntView!W.fromHexString("15"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0x0.f19c2629ccf53p-1022);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xf.19c2629ccf52fc4p-1026L);
-
-        view = DecimalView!W(false, -340, BigUIntView!W.fromHexString("AF87023B9BF0EE"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0x0.0000000000001p-1022);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xf.fffffffffffff64p-1078L);
-
-        view = DecimalView!W(false, 400, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == double.infinity);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xd.a763fc8cb9ff9e6p+1325L);
-
-        view = DecimalView!W(false, 309, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == double.infinity);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xb.201833b35d63f73p+1023L);
-
-        view = DecimalView!W(false, 308, BigUIntView!W.fromHexString("2"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == double.infinity);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
-
-        view = DecimalView!W(false, 308, BigUIntView!W.fromHexString("2"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == double.infinity);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
-
-        view = DecimalView!W(false, 295, BigUIntView!W.fromHexString("1059949B7090"));
-        assert (cast(float)view == float.infinity);
-        assert (cast(double)view == double.infinity);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.00000000006955ap+1021L);
-
-        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("0"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0L);
-
-        view = DecimalView!W(false, 0, BigUIntView!W.init);
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0L);
-
-        view = DecimalView!W(false, -325, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
-
-        view = DecimalView!W(false, -326, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
-
-        view = DecimalView!W(false, -500, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.33ada2003db9a8p-1664L);
-
-        view = DecimalView!W(false, -1000, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.68a9188a89e1467p-3325L);
-
-        view = DecimalView!W(false, -4999, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0L);
-
-        view = DecimalView!W(false, -10000, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0L);
-
-        view = DecimalView!W(false, -4969, BigUIntView!W.fromHexString("329659A941466C6B"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == real.min_normal * real.epsilon);
-
-        view = DecimalView!W(false, -15, BigUIntView!W.fromHexString("525DB0200FFAB"));
-        assert (cast(float)view == 1.448997445238699f);
-        assert (cast(double)view == 0x1.72f17f1f49aadp+0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
-
-        view = DecimalView!W(false, -15, BigUIntView!W.fromHexString("525DB0200FFAB"));
-        assert (cast(float)view == 1.448997445238699f);
-        assert (cast(double)view == 0x1.72f17f1f49aadp+0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
-
-        view = DecimalView!W(false, -325, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
-
-        view = DecimalView!W(false, -326, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0);
-        assert (cast(double)view == 0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
-
-        view = DecimalView!W(false, 0, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 1);
-        assert (cast(double)view == 0x1p+0);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0x8p-3L);
-
-        view = DecimalView!W(false, -5, BigUIntView!W.fromHexString("3"));
-        assert (cast(float)view == 3e-5f);
-        assert (cast(double)view == 0x1.f75104d551d69p-16);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xf.ba8826aa8eb4635p-19L);
-
-        view = DecimalView!W(false, -1, BigUIntView!W.fromHexString("1"));
-        assert (cast(float)view == 0.1f);
-        assert (cast(double)view == 0x1.999999999999ap-4);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xc.ccccccccccccccdp-7L);
-
-        view = DecimalView!W(false, -7, BigUIntView!W.fromHexString("98967F"));
-        assert (cast(float)view == 0.9999999f);
-        assert (cast(double)view == 0x1.fffffca501acbp-1);
-        static if (real.mant_dig >= 64)
-            assert (cast(real)view == 0xf.ffffe5280d65435p-4L);
-    }}
+    auto view = DecimalView!size_t(false, -8, BigUIntView!size_t.fromHexString("BEBC2000000011E1A3"));
+
+    assert (cast(float)view == 3.518437208883201171875E+013f);
+    assert (cast(double)view == 3.518437208883201171875E+013);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 3.518437208883201171875E+013L);
+
+    view = DecimalView!size_t(true, -169, BigUIntView!size_t.fromHexString("5A174AEDA65CC"));
+    assert (cast(float)view == -0);
+    assert (cast(double)view == -0x1.1p-511);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == -0x8.80000000000019fp-514L);
+
+    view = DecimalView!size_t(true, 293, BigUIntView!size_t.fromHexString("36496F6C4ED38"));
+    assert (cast(float)view == -float.infinity);
+    assert (cast(double)view == -9.55024478104888e+307);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == -9.55024478104888e+307L);
+
+    view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 1);
+    assert (cast(double)view == 1);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 1L);
+
+    view = DecimalView!size_t(false, -5, BigUIntView!size_t.fromHexString("3"));
+    assert (cast(float)view == 3e-5f);
+    assert (cast(double)view == 3e-5);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 3e-5L);
+
+    view = DecimalView!size_t(false, -1, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0.1f);
+    assert (cast(double)view == 0.1);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0.1L);
+
+    view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("3039"));
+    assert (cast(float)view == 12345.0f);
+    assert (cast(double)view == 12345.0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 12345.0L);
+
+    view = DecimalView!size_t(false, -7, BigUIntView!size_t.fromHexString("98967F"));
+    assert (cast(float)view == 0.9999999f);
+    assert (cast(double)view == 0.9999999);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0.9999999L);
+
+    view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("4F0CEDC95A718E"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 2.2250738585072014e-308);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 2.2250738585072014e-308L);
+
+    view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1FFFFFFFFFFFFFFFD"));
+    assert (cast(float)view == 36893488147419103229f);
+    assert (cast(double)view == 36893488147419103229.0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x1FFFFFFFFFFFFFFFDp0L);
+
+    view = DecimalView!size_t(false, -33, BigUIntView!size_t.fromHexString("65"));
+    assert (cast(float)view == 101e-33f);
+    assert (cast(double)view == 101e-33);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 101e-33L);
+
+    view = DecimalView!size_t(false, 23, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 1e23f);
+    assert (cast(double)view == 1e23);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 1e23L);
+
+    view = DecimalView!size_t(false, 23, BigUIntView!size_t.fromHexString("81B"));
+    assert (cast(float)view == 2075e23f);
+    assert (cast(double)view == 0xaba3d58a1f1a98p+32);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xaba3d58a1f1a9cp+32L);
+
+    view = DecimalView!size_t(false, -23, BigUIntView!size_t.fromHexString("2209"));
+    assert (cast(float)view == 8713e-23f);
+    assert (cast(double)view == 0x1.9b75b4e7de2b9p-64);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xc.dbada73ef15c401p-67L);
+
+    view = DecimalView!size_t(false, 300, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == 0x1.7e43c8800759cp+996);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xb.f21e44003acdd2dp+993L);
+
+    view = DecimalView!size_t(false, 245, BigUIntView!size_t.fromHexString("B3A73CEB227"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == 0x1.48e3735333cb6p+857);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xa.471b9a999e5b01ep+854L);
+
+    view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == 0x1.117e8e90a0ff7p+239);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.8bf4748507fb99p+236L);
+
+    view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("5"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0x0.0000000000001p-1022);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.18995ce7aa0e1b2p-1077L);
+
+    view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("5B"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0x0.0000000000012p-1022);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x9.3594d9adeb09a55p-1073L);
+
+    view = DecimalView!size_t(false, -322, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0x0.0000000000014p-1022);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xa.1ebfb4219491a1fp-1073L);
+
+    view = DecimalView!size_t(false, -320, BigUIntView!size_t.fromHexString("CA1CCB"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0x0.000063df832d9p-1022);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xc.7bf065b215888c7p-1043L);
+
+    view = DecimalView!size_t(false, -319, BigUIntView!size_t.fromHexString("33CE7943FB"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0x1.000000000162p-1022);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.000000000b103b6p-1025L);
+
+    view = DecimalView!size_t(false, -309, BigUIntView!size_t.fromHexString("15"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0x0.f19c2629ccf53p-1022);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xf.19c2629ccf52fc4p-1026L);
+
+    view = DecimalView!size_t(false, -340, BigUIntView!size_t.fromHexString("AF87023B9BF0EE"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0x0.0000000000001p-1022);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xf.fffffffffffff64p-1078L);
+
+    view = DecimalView!size_t(false, 400, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == double.infinity);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xd.a763fc8cb9ff9e6p+1325L);
+
+    view = DecimalView!size_t(false, 309, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == double.infinity);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xb.201833b35d63f73p+1023L);
+
+    view = DecimalView!size_t(false, 308, BigUIntView!size_t.fromHexString("2"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == double.infinity);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
+
+    view = DecimalView!size_t(false, 308, BigUIntView!size_t.fromHexString("2"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == double.infinity);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
+
+    view = DecimalView!size_t(false, 295, BigUIntView!size_t.fromHexString("1059949B7090"));
+    assert (cast(float)view == float.infinity);
+    assert (cast(double)view == double.infinity);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.00000000006955ap+1021L);
+
+    view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("0"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0L);
+
+    view = view;
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0L);
+
+    view = DecimalView!size_t(false, -325, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
+
+    view = DecimalView!size_t(false, -326, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
+
+    view = DecimalView!size_t(false, -500, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.33ada2003db9a8p-1664L);
+
+    view = DecimalView!size_t(false, -1000, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.68a9188a89e1467p-3325L);
+
+    view = DecimalView!size_t(false, -4999, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0L);
+
+    view = DecimalView!size_t(false, -10000, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0L);
+
+    view = DecimalView!size_t(false, -4969, BigUIntView!size_t.fromHexString("329659A941466C6B"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == real.min_normal * real.epsilon);
+
+    view = DecimalView!size_t(false, -15, BigUIntView!size_t.fromHexString("525DB0200FFAB"));
+    assert (cast(float)view == 1.448997445238699f);
+    assert (cast(double)view == 0x1.72f17f1f49aadp+0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
+
+    view = DecimalView!size_t(false, -15, BigUIntView!size_t.fromHexString("525DB0200FFAB"));
+    assert (cast(float)view == 1.448997445238699f);
+    assert (cast(double)view == 0x1.72f17f1f49aadp+0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
+
+    view = DecimalView!size_t(false, -325, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
+
+    view = DecimalView!size_t(false, -326, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0);
+    assert (cast(double)view == 0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
+
+    view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 1);
+    assert (cast(double)view == 0x1p+0);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0x8p-3L);
+
+    view = DecimalView!size_t(false, -5, BigUIntView!size_t.fromHexString("3"));
+    assert (cast(float)view == 3e-5f);
+    assert (cast(double)view == 0x1.f75104d551d69p-16);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xf.ba8826aa8eb4635p-19L);
+
+    view = DecimalView!size_t(false, -1, BigUIntView!size_t.fromHexString("1"));
+    assert (cast(float)view == 0.1f);
+    assert (cast(double)view == 0x1.999999999999ap-4);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xc.ccccccccccccccdp-7L);
+
+    view = DecimalView!size_t(false, -7, BigUIntView!size_t.fromHexString("98967F"));
+    assert (cast(float)view == 0.9999999f);
+    assert (cast(double)view == 0x1.fffffca501acbp-1);
+    static if (real.mant_dig >= 64)
+        assert (cast(real)view == 0xf.ffffe5280d65435p-4L);
 }
 
 /++
diff --git a/source/mir/ediff.d b/source/mir/ediff.d
index bd0397d3..83226b87 100644
--- a/source/mir/ediff.d
+++ b/source/mir/ediff.d
@@ -928,15 +928,6 @@ private template NormalCDF(T)
 /++
 Constructs the cumulative normal distribution function of the expression
 
-Params:
-    value = expression
-+/
-deprecated("normalDistribution renamed, use normalCDF instead")
-alias normalDistribution = normalCDF;
-
-/++
-Constructs the cumulative normal distribution function of the expression
-
 Params:
     value = expression
 +/
diff --git a/source/mir/format.d b/source/mir/format.d
index 538c795a..be95aec4 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -112,12 +112,13 @@ enum getData = GetData();
 
 /++
 +/
-struct _stringBuf(C)
+struct StringBuf(C, uint scopeSize = 256)
+    if (is(C == char) || is(C == wchar) || is(C == dchar))
 {
     import mir.appender: ScopedBuffer;
 
     ///
-    ScopedBuffer!(C, 256) buffer;
+    ScopedBuffer!(C, scopeSize) buffer;
 
     ///
     alias buffer this;
@@ -127,11 +128,14 @@ struct _stringBuf(C)
 }
 
 ///ditto
-alias stringBuf = _stringBuf!char;
-///ditto
-alias wstringBuf = _stringBuf!wchar;
-///ditto
-alias dstringBuf = _stringBuf!dchar;
+auto stringBuf(C = char, uint scopeSize = 256)()
+    @trusted pure nothrow @nogc @property
+    if (is(C == char) || is(C == wchar) || is(C == dchar))
+{
+    StringBuf!(C, scopeSize) buffer = void;
+    buffer.initialize;
+    return buffer;
+}
 
 /++
 +/
@@ -162,7 +166,7 @@ version (mir_test) unittest
 {
     auto name = "D";
     auto ver = 2.0;
-    assert(stringBuf() << "Hi " << name << ver << "!\n" << getData == "Hi D2.0!\n");
+    assert(stringBuf << "Hi " << name << ver << "!\n" << getData == "Hi D2.0!\n");
 }
 
 ///
@@ -171,7 +175,7 @@ version (mir_test) unittest
 {
     auto name = "D"w;
     auto ver = 2;
-    assert(wstringBuf() << "Hi "w << name << ver << "!\n"w << getData == "Hi D2!\n"w);
+    assert(stringBuf!wchar << "Hi "w << name << ver << "!\n"w << getData == "Hi D2!\n"w);
 }
 
 ///
@@ -180,13 +184,13 @@ version (mir_test) unittest
 {
     auto name = "D"d;
     auto ver = 2UL;
-    assert(dstringBuf() << "Hi "d  << name << ver << "!\n"d << getData == "Hi D2!\n");
+    assert(stringBuf!dchar << "Hi "d  << name << ver << "!\n"d << getData == "Hi D2!\n"d);
 }
 
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
-    assert(stringBuf() << -1234567890 << getData == "-1234567890");
+    assert(stringBuf << -1234567890 << getData == "-1234567890");
 }
 
 /++
@@ -421,7 +425,7 @@ ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
 version (mir_test) unittest
 {
     import mir.format: stringBuf;
-    stringBuf w;
+    auto w = stringBuf;
     assert(w.printEscaped("Hi \a\v\0\f\t\b \\\r\n" ~ `"@nogc"`).data == `Hi \a\v\0\f\t\b \\\r\n\"@nogc\"`);
     w.reset;
     assert(w.printEscaped("\x03").data == `\x03`);
@@ -766,7 +770,7 @@ version(mir_bignum_test)
 unittest
 {
     auto spec = NumericSpec.human;
-    stringBuf buffer;
+    auto buffer = stringBuf;
 
     void check(double num, string value)
     {
@@ -926,7 +930,7 @@ version (mir_test) unittest
 
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
-    assert(stringBuf() << A() << S() << D() << F() << G() << getData == "asdfg");
+    assert(stringBuf << A() << S() << D() << F() << G() << getData == "asdfg");
 }
 
 /// Prints classes and interfaces
@@ -1005,7 +1009,7 @@ version (mir_test) unittest
     static class F { const(char)[] toString()() const return { return "f"; } }
     static class G { const(char)[] s = "g"; alias s this; }
 
-    assert(stringBuf() << new A() << new S() << new D() << new F() << new G() << getData == "asdfg");
+    assert(stringBuf << new A() << new S() << new D() << new F() << new G() << getData == "asdfg");
 }
 
 ///
diff --git a/source/mir/interpolate/utility.d b/source/mir/interpolate/utility.d
index 945ae297..036dbaf7 100644
--- a/source/mir/interpolate/utility.d
+++ b/source/mir/interpolate/utility.d
@@ -110,6 +110,7 @@ Unqual!(typeof(X.init - Y.init))[3] parabolaDerivatives(X, Y)(in X x0, in X x1,
     return [d1 + d2 - d0, d0 + d2 - d1, d0 + d1 - d2];
 }
 
+version(mir_test)
 ///
 unittest
 {
@@ -128,6 +129,7 @@ unittest
 }
 
 
+version(mir_test)
 ///
 unittest
 {
@@ -219,6 +221,7 @@ struct CubicKernel(T)
     alias withTwoDerivatives = opCall!2;
 }
 
+version(mir_test)
 ///
 unittest
 {
diff --git a/source/mir/math/func/normal.d b/source/mir/math/func/normal.d
index 2a7e24bd..01cb935b 100644
--- a/source/mir/math/func/normal.d
+++ b/source/mir/math/func/normal.d
@@ -24,10 +24,6 @@ import mir.math.common;
 
 @safe pure nothrow @nogc:
 
-///
-deprecated("normalProbabilityDensity renamed, use normalPDF instead")
-alias normalProbabilityDensity = normalPDF;
-
 ///
 T normalPDF(T)(const T z)
  if (isFloatingPoint!T)
@@ -43,26 +39,6 @@ if (isFloatingPoint!T)
     return normalPDF((x - mean) / stdDev) / stdDev;
 }
 
-/++
-Computes the normal distribution function.
-The normal (or Gaussian, or bell-shaped) distribution is
-defined as:
-normalDist(x) = 1/$(SQRT) &pi; $(INTEGRAL -$(INFINITY), x) exp( - $(POWER t, 2)/2) dt
-    = 0.5 + 0.5 * erf(x/sqrt(2))
-    = 0.5 * erfc(- x/sqrt(2))
-To maintain accuracy at high values of x, use
-normalDistribution(x) = 1 - normalDistribution(-x).
-Accuracy:
-Within a few bits of machine resolution over the entire
-range.
-References:
-$(LINK http://www.netlib.org/cephes/ldoubdoc.html),
-G. Marsaglia, "Evaluating the Normal Distribution",
-Journal of Statistical Software <b>11</b>, (July 2004).
-+/
-deprecated("normalDistribution renamed, use normalCDF instead")
-alias normalDistribution = normalCDF;
-
 /++
 Computes the normal distribution cumulative distribution function (CDF).
 The normal (or Gaussian, or bell-shaped) distribution is
@@ -127,10 +103,6 @@ unittest
     assert(normalCDF(-8.0) > 0);
 }
 
-///
-deprecated("normalDistributionInverse renamed, use normalInvCDF instead")
-alias normalDistributionInverse = normalInvCDF;
-
 ///
 T normalInvCDF(T)(const T p)
 in {
diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d
index b9b6db1a..b9843538 100644
--- a/source/mir/ndslice/field.d
+++ b/source/mir/ndslice/field.d
@@ -486,6 +486,7 @@ struct BitpackField(Field, uint pack, I = typeof(cast()Field.init[size_t.init]))
 }
 
 ///
+version(mir_ndslice_test)
 unittest
 {
     import mir.ndslice.iterator: FieldIterator;
@@ -515,6 +516,7 @@ unittest
     assert(f[11] == 0);
 }
 
+version(mir_ndslice_test)
 unittest
 {
     import mir.ndslice.slice;
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index b987ca99..7da206a9 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -5641,6 +5641,7 @@ template squeeze(sizediff_t axis = 0)
 }
 
 ///
+version(mir_ndslice_test)
 unittest
 {
     import mir.ndslice.topology : iota;
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 8c21defe..9cf0bb9e 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -244,6 +244,7 @@ version(mir_test) @safe unittest
 }
 
 /// ditto
+version(mir_test)
 unittest
 {
     import core.stdc.tgmath: atan;
@@ -311,6 +312,7 @@ With adaptive bounds and single initial point.
 Reverse outer bound order controls first step direction
 in case of `f(a) == f(b)`.
 +/
+version(mir_test)
 unittest
 {
 	enum root = 1.0;
@@ -1761,6 +1763,7 @@ do {
 }
 
 ///
+version(mir_test)
 unittest
 {
     import mir.math.common;
@@ -1991,6 +1994,7 @@ do {
 }
 
 ///
+version(mir_test)
 unittest
 {
     import mir.math.common;
diff --git a/source/mir/parse.d b/source/mir/parse.d
index f5b03588..2fa2c454 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -8,7 +8,7 @@ Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry
 module mir.parse;
 
 /// `mir.conv: to` extension.
-version(mir_test)
+version(mir_bignum_test)
 @safe pure @nogc
 unittest
 {
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index b12b8d67..55c1f5d6 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -495,6 +495,7 @@ RCArray!T mininitRcarray(T)(size_t length, bool deallocate = true)
 }
 
 ///
+version(mir_test)
 @safe pure nothrow @nogc unittest
 {
     auto a = mininitRcarray!double(5);
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 27b51114..aeee7a47 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -321,6 +321,7 @@ template serdeDynamicAlgebraic(alias getAlgebraicDeserializerByAnnotation)
 }
 
 ///
+version(mir_test)
 unittest
 {
     static struct _global
@@ -652,13 +653,6 @@ version(mir_test) unittest
 +/
 alias serdeGetProxy(alias symbol) = TemplateArgsOf!(getUDA!(symbol, serdeProxy))[0];
 
-/++
-Can be applied only to fields that can be constructed from strings.
-Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation.
-Use this attributes only for strings that would not be used after the input data deallocation.
-+/
-deprecated("use @serdeScoped @serdeProxy!(const(char)[]) instead") enum serdeScopeStringProxy;
-
 /++
 Attributes to conditional ignore field during serialization.
 
diff --git a/source/mir/series.d b/source/mir/series.d
index 28deebac..773ea36c 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -154,6 +154,7 @@ See_also: $(LREF unionSeries), $(LREF troykaSeries), $(LREF troykaGalop).
 }
 
 ///
+version(mir_test)
 unittest{
 
     import mir.series;
@@ -208,10 +209,6 @@ struct mir_observation(Index, Data)
     Index index;
     /// Value or ndslice.
     Data data;
-@serdeIgnore:
-    deprecated ("use `index` instead") alias time = index;
-    deprecated ("use `index` instead") alias key = index;
-    deprecated ("use `data` instead") alias value = data;
 }
 
 /// ditto
@@ -336,14 +333,6 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     /// Data / Value type aliases
     alias Data = typeof(typeof(this).init.data.front);
 
-    deprecated ("use `Index` instead") alias Key = Index;
-    deprecated ("use `Index` instead") alias Time = Index;
-    deprecated ("use `Data` instead") alias Value = Data;
-
-    deprecated ("use `index` instead") alias time = index;
-    deprecated ("use `index` instead") alias key = index;
-    deprecated ("use `data` instead") alias value = data;
-
     private enum defaultMsg() = "Series " ~ Unqual!(this.Data).stringof ~ "[" ~ Unqual!(this.Index).stringof ~ "]: Missing";
     private static immutable defaultExc() = new Exception(defaultMsg!() ~ " required key");
 
@@ -1747,8 +1736,8 @@ then the result will contain the value of the last pair for that key in r.
 auto assocArray(IndexIterator, Iterator, size_t N, SliceKind kind)
     (Series!(IndexIterator, Iterator, N, kind) series)
 {
-    alias SK = series.Key;
-    alias SV = series.Value;
+    alias SK = series.Index;
+    alias SV = series.Data;
     alias UK = Unqual!SK;
     alias UV = Unqual!SV;
     static if (isImplicitlyConvertible!(SK, UK))
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index cb97c7fc..46c524d0 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -21,11 +21,10 @@ private static immutable errorMsg = "Cannot create SmallString: input string exc
 version(D_Exceptions)
     private static immutable exception = new Exception(errorMsg);
 
-extern(C++, "mir"):
-
 /++
 Self-contained generic Small String implementaton.
 +/
+extern(C++, "mir")
 @serdeScoped @serdeProxy!(const(char)[])
 struct SmallString(uint maxLength)
     if (maxLength)
diff --git a/source/mir/stdio.d b/source/mir/stdio.d
new file mode 100644
index 00000000..006dda9a
--- /dev/null
+++ b/source/mir/stdio.d
@@ -0,0 +1,265 @@
+/++
+A simple I/O routines around `<stdio.h>`.
+
+The implementation is CTFE-friendly.
++/
+module mir.stdio;
+
+static import core.stdc.stdio;
+
+/// Standart output
+File dout()() @trusted nothrow @nogc @property
+{
+    version(LDC)
+        pragma(inline, true);
+    return File(__ctfe ? null : core.stdc.stdio.stdout);
+}
+
+/// ditto
+File derr()() @trusted nothrow @nogc @property
+{
+    version(LDC)
+        pragma(inline, true);
+    return File(__ctfe ? null : core.stdc.stdio.stderr);
+}
+
+///
+version(mir_test)
+@safe @nogc
+unittest
+{
+    dout << "mir.stdio.dout test! - @nogc I/O" << endl;
+    derr << "mir.stdio.derr test! - @nogc I/O" << endl;
+}
+
+/++
+Nothrow standart output to use in pair with `debug` expression in nothrow
+and pure code for testing purpose.
+
+See_also: $(LREF AssumeNothrowFile)
++/
+AssumeNothrowFile tout()() @trusted nothrow @nogc @property
+{
+    version(LDC)
+        pragma(inline, true);
+    return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stdout);
+}
+
+/// ditto
+AssumeNothrowFile terr()() @trusted nothrow @nogc @property
+{
+    version(LDC)
+        pragma(inline, true);
+    return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stderr);
+}
+
+///
+version(mir_test)
+pure @safe @nogc nothrow
+unittest
+{
+    debug tout << "mir.stdio.tout test! - @nogc nothrow I/O" << endl;
+    debug terr << "mir.stdio.terr test! - @nogc nothrow I/O" << endl;
+}
+
+/++
+When used as `file << endl` it adds new line flushes the stream.
++/
+enum NewLine
+{
+    lf = "\n",
+    lf_cf = "\r\n",
+}
+
+/// ditto
+enum endl = NewLine.lf;
+
+/++
++/
+struct File
+{
+    ///
+    core.stdc.stdio.FILE* fp;
+
+    mixin FileMemembers;
+
+@trusted @nogc:
+
+    /++
+    Throws: $(LREF FileException)
+    +/
+    void rawWrite(scope const(void)[] data)
+        in (__ctfe || fp !is null)
+    {
+        if (__ctfe)
+            return;
+        core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp);
+        if (core.stdc.stdio.ferror(fp))
+            throw writeException;
+    }
+
+    /++
+    Throws: $(LREF FileException)
+    +/
+    void flush()
+        in (__ctfe || fp !is null)
+    {
+        if (__ctfe)
+            return;
+        core.stdc.stdio.fflush(fp);
+        if (core.stdc.stdio.ferror(fp))
+            throw writeException;
+    }
+}
+
+/++
+Nothrow File implementation for testing purposes.
+See_also: $(LREF tout), $(LREF terr)
++/
+struct AssumeNothrowFile
+{
+    ///
+    core.stdc.stdio.FILE* fp;
+
+    mixin FileMemembers;
+
+@trusted @nogc nothrow:
+
+    /++
+    Throws: $(LREF FileError)
+    +/
+    void rawWrite(scope const(void)[] data)
+        in (__ctfe || fp !is null)
+    {
+        if (__ctfe)
+            return;
+        core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp);
+        if (core.stdc.stdio.ferror(fp))
+            throw writeError;
+    }
+
+    /++
+    Throws: $(LREF FileError)
+    +/
+    void flush()
+        in (__ctfe || fp !is null)
+    {
+        if (__ctfe)
+            return;
+        core.stdc.stdio.fflush(fp);
+        if (core.stdc.stdio.ferror(fp))
+            throw writeError;
+    }
+}
+
+///
+mixin template FileMemembers()
+{
+    ///
+    void put(C)(const C data)
+        if (is(C == char) || is(C == wchar) | is(C == dchar))
+    {
+        C[1] array = [data];
+        this.rawWrite(array);
+    }
+
+    ///
+    void put(C)(scope const(C)[] data)
+        if (is(C == char) || is(C == wchar) | is(C == dchar))
+    {
+        this.rawWrite(data);
+    }
+
+    ///
+    template opBinary(string op : "<<")
+    {
+        ///
+        ref opBinary(T)(auto ref T value) return scope
+        {
+            import mir.format: print;
+            return print!char(this, value);
+        }
+
+        /// Prints new line and flushes the stream
+        ref opBinary(NewLine endl) return scope
+        {
+            import mir.format: print;
+            this.put(endl);
+            this.flush;
+            return this;
+        }
+    }
+}
+
+/++
+File Exception
++/
+class FileException : Exception
+{
+    ///
+    this(
+        string msg,
+        string file = __FILE__,
+        size_t line = __LINE__,
+        Throwable next = null) pure nothrow @nogc @safe 
+    {
+        super(msg, file, line, next);
+    }
+
+    ///
+    this(
+        string msg,
+        Throwable next,
+        string file = __FILE__,
+        size_t line = __LINE__,
+        ) pure nothrow @nogc @safe 
+    {
+        this(msg, file, line, next);
+    }
+
+    FileException toMutable() @trusted pure nothrow @nogc const
+    {
+        return cast() this;
+    }
+
+    alias toMutable this;
+}
+
+/++
+File Error
++/
+class FileError : Error
+{
+    ///
+    this(
+        string msg,
+        string file = __FILE__,
+        size_t line = __LINE__,
+        Throwable next = null) pure nothrow @nogc @safe 
+    {
+        super(msg, file, line, next);
+    }
+
+    ///
+    this(
+        string msg,
+        Throwable next,
+        string file = __FILE__,
+        size_t line = __LINE__,
+        ) pure nothrow @nogc @safe 
+    {
+        this(msg, file, line, next);
+    }
+
+    FileError toMutable() @trusted pure nothrow @nogc const
+    {
+        return cast() this;
+    }
+
+    alias toMutable this;
+}
+
+private static immutable writeException = new FileException("Error on file write");
+private static immutable flushException = new FileException("Error on file flush");
+private static immutable writeError = new FileError("Error on file write");
+private static immutable flushError = new FileError("Error on file flush");
diff --git a/source/mir/test.d b/source/mir/test.d
index 69958a53..6152c448 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -25,7 +25,7 @@ struct ShouldApprox(T)
         import mir.math.common: approxEqual;
         if (value.approxEqual(expected, maxRelDiff, maxAbsDiff))
             return true;
-        stringBuf buf;
+        auto buf = stringBuf;
         throw new MirException(buf
             << "expected approximately " << expected
             << ", got " << value
@@ -62,7 +62,7 @@ struct Should(T)
         import mir.format: stringBuf, getData;
         if (value == expected)
             return true;
-        stringBuf buf;
+        auto buf = stringBuf;
         throw new MirException(buf
             << "expected " << expected
             << ", got " << value
@@ -91,7 +91,7 @@ bool should(alias fun, T, R)(const T value, const R expected, string file = __FI
     import mir.format: stringBuf, getData;
     if (naryFun!fun(value, expected))
         return true;
-    stringBuf buf;
+    auto buf = stringBuf;
     buf << fun.stringof
         << " returns false"
         << " for a = " << value
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index b8046cff..7c9bdc07 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -815,6 +815,7 @@ struct Timestamp
     }
 
     ///
+    version(mir_test)
     @safe pure nothrow @nogc unittest
     {
         assert(Timestamp(1970, 1, 1).toUnixTime == 0);
@@ -846,6 +847,7 @@ struct Timestamp
     }
 
     ///
+    version(mir_test)
     @safe pure nothrow @nogc unittest
     {
         import mir.format;

From bfef61bfcdf1cf81787129682034ecaaf1e3b367 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 19:47:51 +0400
Subject: [PATCH 403/573] fixup

---
 dub.sdl                                | 8 ++++----
 source/mir/bignum/internal/dec2float.d | 5 ++---
 source/mir/bignum/low_level_view.d     | 9 ++++++---
 3 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 7ffc06fe..bbee2c1f 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -10,12 +10,12 @@ dependency "mir-core" version=">=1.1.106"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_bignum_test_llv" //"mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test" "mir_bignum_test_llv" "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test" "mir_ndslice_test" "mir_test" "mir_bignum_test_llv"
     dflags "-lowmem" "-preview=dip1008"
 }
 buildType "unittest-dip1000" {
@@ -41,8 +41,8 @@ buildType "unittest-release" {
 configuration "default" {
 }
 
-configuration "secure" {
-    versions "mir_secure_memory"
+configuration "silly" {
+    dependency "silly" version="~>1.1.1"
 }
 
 configuration "dip1008" {
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 4f0d66f0..7e4e1f30 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -159,7 +159,6 @@ private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent
     if ((is(T == float) || is(T == double) || is(T == real)))
     in (coefficients.length == 0 || coefficients[$ - 1])
 {
-    import mir.bignum.low_level_view: BigUIntView;
     import mir.bignum.fixed: UInt;
     import mir.bignum.fp: Fp, extendedMul;
     import mir.math.common: floor;
@@ -200,7 +199,7 @@ private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent
     else
     {
         if (exponent == 0)
-            return decimalTo!T(coeff);
+            return decimalTo!T(coefficients);
 
         if (coefficients.length == 0)
             return exponent == exponent.max ? T.infinity : 0;
@@ -241,7 +240,7 @@ private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent
                 enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
             else
                 enum ulong mask = ulong.max;
-            enum ulong half = (1UL << (128 - T.mant_dig - 1));
+            enum ulong half = 3;//(1UL << (128 - T.mant_dig - 1));
             enum UInt!128 bound = UInt!128(1) << T.mant_dig;
 
             auto slop = (coefficients.length > (ulong.sizeof * 2 / size_t.sizeof)) + 3 * expSign;
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 6abf0713..557bbae2 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -94,6 +94,7 @@ struct BigUIntView(W)
         return typeof(return)(this, sign);
     }
 
+    static if (W.sizeof >= size_t.sizeof)
     ///
     T opCast(T)() const
         if (isFloatingPoint!T && isMutable!T)
@@ -102,6 +103,7 @@ struct BigUIntView(W)
         return normalized.coefficients.decimalTo!T;
     }
 
+    static if (W.sizeof >= size_t.sizeof)
     ///
     @safe
     T opCast(T : Fp!coefficientSize, uint coefficientSize)() const
@@ -1120,15 +1122,15 @@ unittest
     assert(fp.exponent == 0);
     assert(fp.coefficient == UInt!128.fromHexString("afbbfae3cd0aff2714a1de7022b0029d"));
 
-    fp = cast(Fp!128) BigUIntView!uint.fromHexString("ae3cd0aff2714a1de7022b0029d");
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("ae3cd0aff2714a1de7022b0029d");
     assert(fp.exponent == -20);
     assert(fp.coefficient == UInt!128.fromHexString("ae3cd0aff2714a1de7022b0029d00000"));
 
-    fp = cast(Fp!128) BigUIntView!ushort.fromHexString("e7022b0029d");
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
     assert(fp.exponent == -84);
     assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
 
-    fp = cast(Fp!128) BigUIntView!ubyte.fromHexString("e7022b0029d");
+    fp = cast(Fp!128) BigUIntView!size_t.fromHexString("e7022b0029d");
     assert(fp.exponent == -84);
     assert(fp.coefficient == UInt!128.fromHexString("e7022b0029d000000000000000000000"));
 
@@ -2270,6 +2272,7 @@ struct DecimalView(W)
         return typeof(return)(coefficient, sign);
     }
 
+    static if (W.sizeof >= size_t.sizeof)
     /++
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.

From acb20ddaf60cd4aad1e492919dc42ec23f5ab3d5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 19:52:23 +0400
Subject: [PATCH 404/573] fixup

---
 source/mir/bignum/internal/dec2float.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 7e4e1f30..db0e05fe 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -232,7 +232,7 @@ private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent
                 while(true);
             }
         }
-        auto z = coefficients.decimalToFp!64.extendedMul(c);
+        auto z = coefficients.decimalToFp!128.extendedMul(c);
         auto ret = cast(T) z;
         if (!gotoAlgoR)
         {
@@ -240,7 +240,7 @@ private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent
                 enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
             else
                 enum ulong mask = ulong.max;
-            enum ulong half = 3;//(1UL << (128 - T.mant_dig - 1));
+            enum ulong half = (1UL << (128 - T.mant_dig - 1));
             enum UInt!128 bound = UInt!128(1) << T.mant_dig;
 
             auto slop = (coefficients.length > (ulong.sizeof * 2 / size_t.sizeof)) + 3 * expSign;

From 08128f60011dc3197c6e8ce25473e9d5be82372e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 19:54:48 +0400
Subject: [PATCH 405/573] fixup

---
 source/mir/bignum/fixed.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 660311a3..0355415c 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -42,7 +42,7 @@ struct UInt(size_t size)
 
     static if (size_t.sizeof == uint.sizeof && data.length % 2 == 0)
     ///
-    this(auto ref const ulong[data.length / 2] data)
+    this()(auto ref const ulong[data.length / 2] data)
     {
         if (!__ctfe)
         {

From c4b2c6952b8188521cca0112b92ab5afdfed5ff5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 20:03:18 +0400
Subject: [PATCH 406/573] fixup

---
 source/mir/bignum/integer.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 010ef964..36dd5162 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -987,7 +987,7 @@ unittest
         assert((a %= b) == 0xDEADBEEF);
     }
 
-    void test(const long av, const long bv)
+    void test(const int av, const int bv)
     {
         auto a = BigInt!4(av);
         const b = BigInt!4(bv);

From 5998e6fca19b28184b8c8a85e564858d2a4cf432 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 20:14:13 +0400
Subject: [PATCH 407/573] fixup

---
 source/mir/bignum/integer.d | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 36dd5162..1b570b7e 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -223,20 +223,39 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    bool opEquals()(size_t rhs, bool rhsSign = false)
+    bool opEquals(ulong rhs, bool rhsSign = false)
         const @safe pure nothrow @nogc
     {
-        return rhs == 0 && this.length == 0 || this.length == 1 && this.sign == rhsSign && this.data[0] == rhs;
+        if (rhs == 0 && this.length == 0 || this.length == 1 && this.sign == rhsSign && this.data[0] == rhs)
+            return true;
+        static if (is(size_t == ulong) || maxSize64 == 1)
+            return false;    
+        else
+            return this.length == 2 && this.data[0] == cast(uint) rhs && this.data[1] == cast(uint) (rhs >> 32);
     }
 
     ///
-    bool opEquals()(sizediff_t rhs)
+    bool opEquals(long rhs)
         const @safe pure nothrow @nogc
     {
         auto sign = rhs < 0;
         return this.opEquals(sign ? ulong(-rhs) : ulong(rhs), sign);
     }
 
+    ///
+    bool opEquals(uint rhs)
+        const @safe pure nothrow @nogc
+    {
+        return opEquals(ulong(rhs), false);
+    }
+
+    ///
+    bool opEquals(int rhs)
+        const @safe pure nothrow @nogc
+    {
+        return opEquals(long(rhs));
+    }
+
     /++
     +/
     auto opCmp()(auto ref const BigInt rhs) 
@@ -987,7 +1006,7 @@ unittest
         assert((a %= b) == 0xDEADBEEF);
     }
 
-    void test(const int av, const int bv)
+    void test(const long av, const long bv)
     {
         auto a = BigInt!4(av);
         const b = BigInt!4(bv);

From 465bab4fb85d73176871d2af86d3afb3c0280886 Mon Sep 17 00:00:00 2001
From: lempiji <lempiji@users.noreply.github.com>
Date: Mon, 30 May 2022 01:22:07 +0900
Subject: [PATCH 408/573] Fix issue #409 (#410)

Looks like you do understand my code better them me, haha)
---
 source/mir/ndslice/fuse.d | 88 ++++++++++++++++++++++++++++++++-------
 1 file changed, 72 insertions(+), 16 deletions(-)

diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index 3caad1fe..d3fd80cc 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -282,6 +282,21 @@ private template fuseDimensionCount(R)
         enum size_t fuseDimensionCount = 0;
 }
 
+///
+@safe pure version(mir_ndslice_test) unittest
+{
+    import mir.ndslice;
+
+    auto m = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].sliced(2, 3, 2);
+    static assert(fuseDimensionCount!(typeof(m)) == 3);
+
+    auto p = m.pack!1;
+    static assert(fuseDimensionCount!(typeof(p)) == 3);
+    
+    auto q = m.pack!2;
+    static assert(fuseDimensionCount!(typeof(q)) == 3);
+}
+
 private static immutable shapeExceptionMsg = "fuseShape Exception: elements have different shapes/lengths";
 
 version(D_Exceptions)
@@ -293,43 +308,84 @@ TODO docs
 size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r)
     if (hasShape!Range)
 {
-    // auto outerShape = r.shape;
     enum N = r.shape.length;
     enum RN = typeof(return).length;
-    enum M = RN - N;
-    static if (M == 0)
+    static if (RN == N)
     {
         return r.shape;
     }
     else
     {
-        import mir.ndslice.topology: repeat;
         typeof(return) ret;
         ret[0 .. N] = r.shape;
-        bool next;
-        if (!ret[0 .. N].anyEmptyShape) foreach (ref elem; r)
+        if (!ret[0 .. N].anyEmptyShape)
         {
-            auto elemShape = fuseShape(elem);
-            if (next)
+            static if (isSlice!Range)
+            {
+                ret[N .. $] = fuseShape(r.first);
+            }
+            else static if (isArray!Range)
             {
-                if (elemShape != ret[N .. $])
+                bool next;
+                foreach (ref elem; r)
                 {
-                    version (D_Exceptions)
-                        throw shapeException;
+                    const elemShape = fuseShape(elem);
+                    if (next)
+                    {
+                        if (elemShape != ret[N .. $])
+                        {
+                            version (D_Exceptions)
+                                throw shapeException;
+                            else
+                                assert(0, shapeExceptionMsg);
+                        }
+                    }
                     else
-                        assert(0, shapeExceptionMsg);
+                    {
+                        ret[N .. $] = elemShape;
+                        next = true;
+                    }
                 }
-                next = true;
             }
             else
-            {
-                ret[N .. $] = elemShape;
-            }
+                static assert(false);
         }
         return ret;
     }
 }
 
+/// basic
+@safe pure version(mir_ndslice_test) unittest
+{
+    import mir.ndslice;
+
+    auto m = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].sliced(2, 3, 2);
+    auto s = fuseShape(m);
+    assert(s == [2, 3, 2]);
+}
+
+/// pack!1
+@safe pure version(mir_ndslice_test) unittest
+{
+    import mir.ndslice;
+
+    auto m = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].sliced(2, 3, 2);
+    auto p = m.pack!1;
+    auto s = fuseShape(p);
+    assert(s == [2, 3, 2]);
+}
+
+/// pack!2
+@safe pure version(mir_ndslice_test) unittest
+{
+    import mir.ndslice;
+
+    auto m = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].sliced(2, 3, 2);
+    auto p = m.pack!2;
+    auto s = fuseShape(p);
+    assert(s == [2, 3, 2]);
+}
+
 private template FuseElementType(NDRange)
     if (fuseDimensionCount!NDRange >= 1)
 {

From 6346aab68b2b23edd9fa8902e29c5387454741e5 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 20:48:08 +0400
Subject: [PATCH 409/573] fixup

---
 bigint_benchmark/source/app.d          | 26 +++++++++++++++------
 source/mir/bignum/internal/dec2float.d |  4 ++--
 source/mir/format.d                    | 31 +++++++++++++++++++++++++-
 3 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/bigint_benchmark/source/app.d b/bigint_benchmark/source/app.d
index 119af79b..44d58a68 100644
--- a/bigint_benchmark/source/app.d
+++ b/bigint_benchmark/source/app.d
@@ -1,7 +1,7 @@
 import mir.bignum.integer: BigInt;
-import std.datetime.stopwatch;
+import mir.stdio;
 import std.bigint: StdBigInt = BigInt;
-import std.stdio: writeln;
+import std.datetime.stopwatch;
 
 immutable ps = "E5B5B1EDC8DF0F307C2220151CFCBE31F69B15659A5D6FBA1E50F55A08B341218312D707CFC16ED86A1765F5AEAFA7E6A11C4431038914C76F0F398FE6BE031E289B220D13D9E02226C691D15BC6E1186EA18222D93F52A393BE1DA1A42853512419B5E6E304FD02E962A4C2D0ECDDB8F44AC094FACA8333AE94110A5B10DA539C24A96F08530E7699E3F705165CF14B7F90A2F32ED28D21615F91D7C808AC566D6EEEF6773450AB53542CDAC337C3124530CB16319752267C3422149D41543D8742586BAB578F4E06360745AE0BD8F0E800D1920DC1F3661287367A78967458383A82465C5D966E7299EFCF58BD860185F96655E1F8D300F6B096DFE883CF15";
 immutable qs = "D9757338E9A6B363F227F3104EDEF6240C0CAF53B7D509F48870553C4A821F460469AE5616301B9CC30FBF4598A176B84284AF3A41D697A34CDC2C8D88A4C4BE82AE8DB5347511FE5B4DD915CA6A728CCFD0444CE38FC7190824059D86A9083C273581EA5AD1D5E3A8D8EC6858F291A5EADA98B0F5FD7C8E8CA6226657B8B7955796B22899B087714E293A86C78D42A7021754A6220F1D0A9588C280DD9AEC376E421D539F30A3053D95C7D70F24B471D14ECF282FA3E0B1CED2C405BA22404F3B75CD961A46097D7C098324FC47281D298734DA0DFCD8AF82E685657C926672727296147867EAEDFDEF89A79DE81FF104CF7D9157EF65A1BC333C98A7FED685";
@@ -34,7 +34,7 @@ void testStd()
     StdBigInt e = "0x" ~ es;
     StdBigInt b = e;
     b.stdPowMod(e, m);
-    // debug writeln(b);
+    debug dout << b << endl;
 }
 
 void testMir()
@@ -46,12 +46,24 @@ void testMir()
     auto e = BigInt!64.fromHexString(es);
     BigInt!64 b = e;
     b.powMod(e, m);
-    // debug writeln(b);
+    debug dout << b << endl;
 }
 
 void main()
 {
-    auto res = 10.benchmark!(testStd, testMir);
-    writeln("std = ", res[0]);
-    writeln("mir = ", res[1]);
+    version (assert)
+    {
+        testStd;
+        testMir;
+        dout << "please compile with --build=release" <<  endl;
+    }
+    else
+    {
+        auto res = 10.benchmark!(testStd, testMir);
+        dout << "std = " <<  res[0] << endl;
+        dout << "mir = " <<  res[1] << endl;
+        dout << "speedup = "
+            << (double(res[0].total!"usecs") / res[1].total!"usecs" -  1) * 100
+            << " %" << endl;
+    }
 }
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index db0e05fe..a2427b4f 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -96,7 +96,7 @@ T decimalToFloat(T)(ulong coefficient, long exponent)
     }
     else
     {
-        ulong[1] coefficients = [coefficient];
+        size_t[1] coefficients = [coefficient];
         return decimalTo!real(approx, coefficients[0 .. !!coefficient], exponent);
     }
 }
@@ -132,7 +132,7 @@ T decimalToFloat(T)(ulong coefficient, long exponent)
             auto e = load64(-exponent);
             return c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
         }
-        ulong[1] coefficients = [coefficient];
+        size_t[1] coefficients = [coefficient];
         return decimalToFloatFallback(approx, coefficients, cast(int) exponent);
     }
     return expSign ? 0 : exponent != exponent.max ? T.infinity : coefficient ? T.nan : T.infinity;
diff --git a/source/mir/format.d b/source/mir/format.d
index be95aec4..a86588bc 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -865,7 +865,36 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
         else
         static if (is(typeof(w.put(c.toString))))
             w.put(c.toString);
-        else static assert(0, T.stringof ~ ".toString definition is wrong: 'const' qualifier may be missing.");
+        else
+        {
+            import std.format: FormatSpec;
+            FormatSpec!char fmt;
+
+            static if (is(typeof(c.toString(w, fmt))))
+                c.toString(w, fmt);
+            else
+            static if (is(typeof(c.toString((scope const(C)[] s) { w.put(s); }, fmt))))
+                c.toString((scope const(C)[] s) { w.put(s); }, fmt);
+            else
+            // workaround for types with mutable toString
+            static if (is(typeof((*cast(T*)&c).toString(w, fmt))))
+                (*cast(T*)&c).toString(w, fmt);
+            else
+            static if (is(typeof((*cast(T*)&c).toString((scope const(C)[] s) { w.put(s); }, fmt))))
+                (*cast(T*)&c).toString((scope const(C)[] s) { w.put(s); }, fmt);
+            else
+            static if (is(typeof((*cast(T*)&c).toString(w))))
+                (*cast(T*)&c).toString(w);
+            else
+            static if (is(typeof((*cast(T*)&c).toString((scope const(C)[] s) { w.put(s); }))))
+                (*cast(T*)&c).toString((scope const(C)[] s) { w.put(s); });
+            else
+            static if (is(typeof(w.put((*cast(T*)&c).toString))))
+                w.put((*cast(T*)&c).toString);
+            else
+                static assert(0, T.stringof ~ ".toString definition is wrong: 'const' qualifier may be missing.");
+        }
+
         return w;
     }
     else

From e43c44d5925b657a8e159e5afaba0e2fddcf9b98 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 20:58:02 +0400
Subject: [PATCH 410/573] fixup

---
 source/mir/bignum/internal/dec2float.d | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index a2427b4f..02c7460d 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -96,8 +96,11 @@ T decimalToFloat(T)(ulong coefficient, long exponent)
     }
     else
     {
-        size_t[1] coefficients = [coefficient];
-        return decimalTo!real(approx, coefficients[0 .. !!coefficient], exponent);
+        auto coefficients = cast(size_t[ulong.sizeof / size_t.sizeof]) cast(ulong[1])[coefficient];
+        static if (coefficients.length == 1)
+            return decimalTo!real(approx, coefficients[0 .. !!coefficient], exponent);
+        else
+            return decimalTo!real(approx, coefficients[0 .. !!coefficient + (coefficient > uint.max)], exponent);
     }
 }
 
@@ -132,8 +135,11 @@ T decimalToFloat(T)(ulong coefficient, long exponent)
             auto e = load64(-exponent);
             return c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
         }
-        size_t[1] coefficients = [coefficient];
-        return decimalToFloatFallback(approx, coefficients, cast(int) exponent);
+        auto coefficients = cast(size_t[ulong.sizeof / size_t.sizeof]) cast(ulong[1])[coefficient];
+        static if (coefficients.length == 1)
+            return decimalToFloatFallback(approx, coefficients, cast(int) exponent);
+        else
+            return decimalToFloatFallback(approx, coefficients[0 .. 1 + (coefficient > uint.max)], cast(int) exponent);
     }
     return expSign ? 0 : exponent != exponent.max ? T.infinity : coefficient ? T.nan : T.infinity;
 }

From 08b2bfb54d419f719931632ab2fde7699a6815ec Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 21:05:45 +0400
Subject: [PATCH 411/573] fixup

---
 source/mir/bignum/fixed.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 0355415c..bbf260d3 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -69,6 +69,7 @@ struct UInt(size_t size)
         }
     }
 
+    static if (size >= 64)
     ///
     this(ulong data)
     {

From 0894a43fc136633da58d7f53690cbf42659b5b30 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 21:23:43 +0400
Subject: [PATCH 412/573] fixup

---
 bigint_benchmark/source/app.d | 15 ++++++++++-----
 source/mir/bignum/fixed.d     |  2 +-
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/bigint_benchmark/source/app.d b/bigint_benchmark/source/app.d
index 44d58a68..e0b67237 100644
--- a/bigint_benchmark/source/app.d
+++ b/bigint_benchmark/source/app.d
@@ -59,11 +59,16 @@ void main()
     }
     else
     {
+        import std.system: os;
         auto res = 10.benchmark!(testStd, testMir);
-        dout << "std = " <<  res[0] << endl;
-        dout << "mir = " <<  res[1] << endl;
-        dout << "speedup = "
-            << (double(res[0].total!"usecs") / res[1].total!"usecs" -  1) * 100
-            << " %" << endl;
+        auto ratio = double(res[0].total!"usecs") / res[1].total!"usecs";
+        dout
+            << "--------------------------------------------" << endl
+            << "mir speedup = " << cast(int)((ratio -  1) * 100_0) / 10.0 << "%" << endl
+            << "std = " <<  res[0] << endl
+            << "mir = " <<  res[1] << endl
+            << " ............... " << size_t.sizeof * 8 << "bit " << os << " ............... " << endl
+            << "--------------------------------------------"
+            << endl;
     }
 }
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index bbf260d3..dbcc8d53 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -88,7 +88,7 @@ struct UInt(size_t size)
     ///
     this(uint data)
     {
-        data[0] = data;
+        this.data[0] = data;
     }
 
     ///

From e6488c69a05bb138ae20bbb2d5b0563b8ef512b3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 21:26:56 +0400
Subject: [PATCH 413/573] fixup

---
 source/mir/bignum/fixed.d | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index dbcc8d53..04990a5f 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -283,11 +283,17 @@ struct UInt(size_t size)
         return 0;
     }
 
+    static if (size >= 64)
     /// ditto
     auto opCmp(ulong rhs) const scope
     {
         return opCmp(UInt!size(rhs));
     }
+    else
+    auto opCmp(uint rhs) const scope
+    {
+        return opCmp(UInt!size(rhs));
+    }
 
     /++
     +/

From 31383a9f3405154e0c1fa83b3a30b6518704a555 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 21:27:35 +0400
Subject: [PATCH 414/573] fixup

---
 source/mir/bignum/fixed.d | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 04990a5f..7135715f 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -295,6 +295,7 @@ struct UInt(size_t size)
         return opCmp(UInt!size(rhs));
     }
 
+    static if (size >= 64)
     /++
     +/
     ref UInt!size opAssign(ulong rhs) scope return
@@ -303,6 +304,14 @@ struct UInt(size_t size)
         this.data = UInt!size(rhs).data;
         return this;
     }
+    else
+    ///
+    ref UInt!size opAssign(uint rhs) scope return
+        @safe pure nothrow @nogc
+    {
+        this.data = UInt!size(rhs).data;
+        return this;
+    }
 
     /++
     +/

From e31c61c6903a438ef978fe3579647f9b0ba90405 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 21:28:28 +0400
Subject: [PATCH 415/573] fixup

---
 source/mir/bignum/fixed.d | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 7135715f..12678ea7 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -263,11 +263,17 @@ struct UInt(size_t size)
             return this.data == rhs.toSize!size.data;
     }
 
+    static if (size >= 64)
     /// ditto
     auto opEquals(ulong rhs) const
     {
         return opEquals(UInt!size(rhs));
     }
+    else
+    auto opEquals(uint rhs) const
+    {
+        return opEquals(UInt!size(rhs));
+    }
 
     /++
     +/

From 5a460e3bedd58e4e7479ac2f3f906cdeb18b89a3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 21:45:25 +0400
Subject: [PATCH 416/573] fixup

---
 source/mir/bignum/internal/phobos_kernel.d | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/source/mir/bignum/internal/phobos_kernel.d b/source/mir/bignum/internal/phobos_kernel.d
index 2c5a3425..61fbd419 100644
--- a/source/mir/bignum/internal/phobos_kernel.d
+++ b/source/mir/bignum/internal/phobos_kernel.d
@@ -39,20 +39,22 @@ Idioms:
 */
 module mir.bignum.internal.phobos_kernel;
 
-version (D_InlineAsm_X86)
-{
-    static import std.internal.math.biguintx86;
-}
+static if (__VERSION__ > 2100)
+    version (D_InlineAsm_X86)
+        static import std.internal.math.biguintx86;
+
 version (DMD)
 {
-    version (D_InlineAsm_X86)
-        version = HaveAsmVersion;
+    static if (__VERSION__ > 2100)
+        version (D_InlineAsm_X86)
+            version = HaveAsmVersion;
 }
 
 version (LDC)
 {
-    version (D_InlineAsm_X86)
-        version = HaveAsmVersion;
+    static if (__VERSION__ > 2100)
+        version (D_InlineAsm_X86)
+            version = HaveAsmVersion;
 
     version (ARM)
     {

From 24b95f58de743ccd58e6b66413eb9ca7e50c1fd7 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 21:54:32 +0400
Subject: [PATCH 417/573] fixup

---
 source/mir/bignum/internal/phobos_kernel.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/bignum/internal/phobos_kernel.d b/source/mir/bignum/internal/phobos_kernel.d
index 61fbd419..5f5caebc 100644
--- a/source/mir/bignum/internal/phobos_kernel.d
+++ b/source/mir/bignum/internal/phobos_kernel.d
@@ -1328,7 +1328,7 @@ void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v)
             {
                 // Note: On DMD, this is only ~10% faster than the non-asm code.
                 uint *p = &u[j + v.length - 1];
-                asm pure nothrow @trusted
+                asm pure nothrow @trusted @nogc
                 {
                     mov EAX, p;
                     mov EDX, [EAX+4];

From ab26fe8849318d96438c377b6db8f37dbc00cbfa Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 22:05:57 +0400
Subject: [PATCH 418/573] fixup

---
 source/mir/bignum/integer.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 1b570b7e..c3cdf8c5 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -50,6 +50,7 @@ struct BigInt(uint maxSize64)
         else
         static if (data.length == 2)
         {
+            sign = false;
             this.data[0] = data[0];
             this.data[1] = data[1];
             this.length = data[1] ? 2 : data[0] != 0;
@@ -267,7 +268,7 @@ struct BigInt(uint maxSize64)
     ///
     BigIntView!size_t view()() scope return @property
     {
-            return typeof(return)(this.data[0 .. this.length], this.sign);
+        return typeof(return)(this.data[0 .. this.length], this.sign);
     }
 
     ///

From f1a55049dcda73889b72d32cff9aa75138dfa9f2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 22:13:43 +0400
Subject: [PATCH 419/573] fixup

---
 source/mir/bignum/integer.d | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index c3cdf8c5..1b836310 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -172,19 +172,17 @@ struct BigInt(uint maxSize64)
 
     ///
     ref opAssign(uint rhsSize64)(auto ref scope const BigInt!rhsSize64 rhs) return
-        @safe pure nothrow @nogc
-        in (rhs.length <= maxSize64)
+        @trusted pure nothrow @nogc
+        in (rhs.length <= this.data.length)
     {
-        this.sign = rhs.sign;
-        this.length = rhs.length;
-        version(LittleEndian)
+        static if (maxSize64 == rhsSize64)
         {
-            data[0 .. length] = rhs.data[0 .. length];
-        }
-        else
-        {
-            data[$ - length .. $] = rhs.data[$ - length .. $];
+            if (&this is &rhs)
+                return this;
         }
+        this.sign = rhs.sign;
+        this.length = rhs.length;
+        this.data[0 .. length] = rhs.data[0 .. length];
         return this;
     }
 

From cf4bbfa87b3c90999406311634dfe5e6e0acb0e3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 22:17:33 +0400
Subject: [PATCH 420/573] fixup

---
 source/mir/bignum/integer.d | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 1b836310..86501ad3 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -75,10 +75,9 @@ struct BigInt(uint maxSize64)
         }
         else
         {
-            auto d = view.coefficients;
-            d[0] = cast(uint) data;
-            d[1] = cast(uint) (data >> 32);
-            length = data && (data >> 32);
+            this.length = data && (data >> 32);
+            this.data[0] = cast(uint) data;
+            this.data[1] = cast(uint) (data >> 32);
         }
     }
 

From daee326cc57e771f83d5167550b72adcf3e67dcf Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 22:23:35 +0400
Subject: [PATCH 421/573] fixup

---
 source/mir/bignum/integer.d | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 86501ad3..69ae4991 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -510,8 +510,10 @@ struct BigInt(uint maxSize64)
 
         pragma(inline, false);
 
-        if (divisor.length == 0 || divisor.coefficients[$ - 1] == 0)
-            assert(0, "Zero or denormalized BigInt divizor");
+        if (divisor.length == 0)
+            assert(0, "Zero BigInt divizor");
+        if (divisor.coefficients[$ - 1] == 0)
+            assert(0, "Denormalized BigInt divizor");
 
         if (this.length < divisor.length)
         {

From 86e075ed952bffe4aba78b334928335cfa77ceeb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 22:28:13 +0400
Subject: [PATCH 422/573] fixup

---
 source/mir/bignum/integer.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 69ae4991..2d70bf8c 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -75,7 +75,7 @@ struct BigInt(uint maxSize64)
         }
         else
         {
-            this.length = data && (data >> 32);
+            this.length = !!data + !!(data >> 32);
             this.data[0] = cast(uint) data;
             this.data[1] = cast(uint) (data >> 32);
         }

From 21a27e9f3260e1831f7669049f4b01d13bdd6679 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 29 May 2022 22:37:21 +0400
Subject: [PATCH 423/573] fixup

---
 source/mir/bignum/low_level_view.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 557bbae2..d8d47d92 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -115,7 +115,7 @@ struct BigUIntView(W)
         else
         static if (W.sizeof > size_t.sizeof)
         {
-            return lightConst.opCast!(BigUIntView!size_t).opCast!T;
+            return lightConst.opCast!(BigUIntView!(const size_t)).opCast!T;
         }
         else
         {

From efc54dcf593d7de28920d8488fb20e09cc290358 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 30 May 2022 04:18:59 +0400
Subject: [PATCH 424/573] rework decimal parsing

---
 source/mir/bignum/decimal.d        | 267 ++++++++++++++++-------------
 source/mir/bignum/internal/parse.d |  17 ++
 2 files changed, 168 insertions(+), 116 deletions(-)
 create mode 100644 source/mir/bignum/internal/parse.d

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 05e8cba5..48896ab0 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -186,7 +186,7 @@ struct Decimal(uint maxSize64)
             static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false)
                 pragma(inline, true);
         }
-        static if (optimize && false)
+        static if (optimize)
         {
             import mir.utility: _expect;
             static if (checkEmpty)
@@ -218,161 +218,196 @@ struct Decimal(uint maxSize64)
             exponent = 0;
 
             ulong v;
-            bool dot;
-            static if (allowUnderscores)
+
+            if (_expect(d >= 10, false))
             {
-                bool recentUnderscore;
+                static if (allowDotOnBounds)
+                {
+                    if (d == '.' - '0')
+                    {
+                        if (str.length == 0)
+                            return false;
+                        key = DecimalExponentKey.dot;
+                        d = str[0] - '0';
+                        str = str[1 .. $];
+                        if (_expect(d < 10, true))
+                            goto FI;
+                        return false;
+                    }
+                }
+                static if (allowSpecialValues)
+                    goto NI;
+                else
+                    return false;
             }
-            // Was there a recent character within the set: ['.', 'e', 'E', 'd', 'D']?
-            bool recentModifier;
+
             static if (!allowLeadingZeros)
             {
-                if (d == 0)
+                if (_expect(d == 0, false))
                 {
                     if (str.length == 0)
                         goto R;
-                    if (str[0] >= '0' && str[0] <= '9')
+                    d = str[0] - '0';
+                    str = str[1 .. $];
+                    if (d < 10)
                         return false;
-                    goto S;
+                    goto DOT;
                 }
             }
 
+            v = d;
+
+        S:
+            if (str.length == 0)
+                goto R;
+            d = str[0] - '0';
+            str = str[1 .. $];
+
             if (d < 10)
             {
+        F0:
+                import mir.checkedint: mulu, addu;
+                bool overflow;
+                v = mulu(v, cast(uint)10, overflow);
+                if (overflow)
+                    return false;
+                v = addu(v, d, overflow);
+                if (overflow)
+                    return false;
                 goto S;
             }
 
-            static if (allowDotOnBounds)
+            static if (allowUnderscores)
             {
-                if (d == '.' - '0')
+                if (d == '_' - '0')
                 {
                     if (str.length == 0)
                         return false;
-                    key = DecimalExponentKey.dot;
-                    dot = true;
-                    recentModifier = true;
-                    goto F;
+                    d = str[0] - '0';
+                    str = str[1 .. $];
+                    if (_expect(d < 10, true))
+                        goto F0;
+                    return false;
                 }
             }
-
-            static if (allowSpecialValues)
-            {
-                goto NI;
-            }
-            else
+        DOT:
+            if (d == DecimalExponentKey.dot)
             {
-                return false;
-            }
+                // The dot modifier CANNOT be preceded by any modifiers. 
+                if (key != DecimalExponentKey.none)
+                    return false;
 
-            F: for(;;)
-            {
-                d = str[0] - '0';
-                str = str[1 .. $];
+                key = DecimalExponentKey.dot;
 
-                if (_expect(d <= 10, true))
+                if (str.length == 0)
                 {
-                    static if (allowUnderscores)
-                    {
-                        recentUnderscore = false;
-                    }
-                    recentModifier = false;
-                    {
-                        import mir.checkedint: mulu;
-                        bool overflow;
-                        v = mulu(v, cast(uint)10, overflow);
-                        if (overflow)
-                            break;
-                    }
-                S:
-                    v += d;
-                    exponentShift -= dot;
-                    if (str.length)
-                        continue;
-                E:
-                    exponent += exponentShift;
-                R:
-                    coefficient.data[0] = v;
-                    coefficient.length = v != 0;
-                    static if (allowUnderscores)
-                    {
-                        return !recentUnderscore && !recentModifier;
-                    }
+                    static if (allowDotOnBounds)
+                        goto R;
                     else
-                    {
-                        return !recentModifier;
-                    }
+                        return false;
                 }
 
-                switch (d)
-                {
-                    case DecimalExponentKey.dot:
-                        // The dot modifier CANNOT be preceded by any modifiers. 
-                        if (recentModifier || key != DecimalExponentKey.none)
-                            break;
+            IF:
 
-                        static if (allowUnderscores)
-                        {
-                            // If we allow underscores, the dot also CANNOT be preceded by any underscores.
-                            // It must be preceded by a number.
-                            if (recentUnderscore)
-                                break;
-                        }
-                        key = DecimalExponentKey.dot;
-                        if (_expect(dot, false))
-                            break;
-                        dot = true;
-                        if (str.length)
-                        {
-                            recentModifier = true;
-                            continue;
-                        }
-                        static if (allowDotOnBounds)
-                        {
-                            goto R;
-                        }
-                        else
+                static if (is(C == char))
+                {
+                    import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
+                    if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
+                    {
+                        ulong multplier = 100000000;
+                        ulong value = parseEightDigits(str[0 .. 8]);
+                        str = str[8 .. $];
+                        exponentShift -= 8;
+                        if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
                         {
-                            break;
+                            multplier = 100000000 * 100000000;
+                            value *= 100000000;
+                            value += parseEightDigits(str[0 .. 8]);
+                            str = str[8 .. $];
+                            exponentShift -= 8;
                         }
-                    static if (allowExponent)
-                    {
-                        static if (allowDExponent)
+
                         {
-                            case DecimalExponentKey.d:
-                            case DecimalExponentKey.D:
-                                goto case DecimalExponentKey.e;
+                            import mir.checkedint: mulu, addu;
+                            bool overflow;
+                            v = mulu(v, multplier, overflow);
+                            if (overflow)
+                                return false;
+                            v = addu(v, value, overflow);;
+                            if (overflow)
+                                return false;
                         }
-                        case DecimalExponentKey.e:
-                        case DecimalExponentKey.E:
-                            import mir.parse: parse;
-                            // We don't really care if the e/E/d/D modifiers are preceded by a modifier,
-                            // so as long as they are a dot or a regular number.
-                            if (key != DecimalExponentKey.dot && key != DecimalExponentKey.none)
-                                break; 
-                            key = cast(DecimalExponentKey)d;
-                            if (parse(str, exponent) && str.length == 0) {
-                                recentModifier = false;
-                                goto E;
-                            }
-                            break;
                     }
-                    static if (allowUnderscores)
+                }
+
+                d = str[0] - '0';
+                str = str[1 .. $];
+                if (_expect(d >= 10, false))
+                    goto DOB;
+            FI:
+                {
+                    import mir.checkedint: mulu, addu;
+                    bool overflow;
+                    v = mulu(v, cast(uint)10, overflow);
+                    if (overflow)
+                        return false;
+                    v = addu(v, d, overflow);;
+                    if (overflow)
+                        return false;
+                }
+                exponentShift--;
+                if (str.length == 0)
+                    goto E;
+                d = str[0] - '0';
+                str = str[1 .. $];
+                if (d < 10)
+                    goto FI;
+
+                static if (allowUnderscores)
+                {
+                    if (d == '_' - '0')
                     {
-                        case '_' - '0':
-                            // A underscore cannot be preceded by an underscore or a modifier.
-                            if (recentUnderscore || recentModifier)
-                                break;
-
-                            recentUnderscore = true;
-                            if (str.length)
-                                continue;
-                            break;
+                        if (str.length == 0)
+                            return false;
+                        d = str[0] - '0';
+                        str = str[1 .. $];
+                        if (_expect(d < 10, true))
+                            goto FI;
+                        return false;
                     }
-                    default:
                 }
-                break;
+            DOB:
+                static if (!allowDotOnBounds)
+                {
+                    if (exponentShift == 0)
+                        return false;
+                }
+
+            }
+
+            static if (allowExponent)
+            {
+                if (d == DecimalExponentKey.e
+                 || d == DecimalExponentKey.E
+                 || d == DecimalExponentKey.d && allowDExponent
+                 || d == DecimalExponentKey.D && allowDExponent
+                )
+                {
+                    import mir.parse: parse;
+                    key = cast(DecimalExponentKey)d;
+                    if (parse(str, exponent) && str.length == 0)
+                        goto E;
+                }
             }
             return false;
+
+        E:
+            exponent += exponentShift;
+        R:
+            coefficient.data[0] = v;
+            coefficient.length = v != 0;
+            return true;
+
             static if (allowSpecialValues)
             {
             NI:
diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
new file mode 100644
index 00000000..01fd55ab
--- /dev/null
+++ b/source/mir/bignum/internal/parse.d
@@ -0,0 +1,17 @@
+module mir.bignum.internal.parse;
+
+bool isMadeOfEightDigits()(ref const char[8] chars)
+{
+    pragma(inline, true);
+    ulong val = (cast(ulong[1]) cast(ubyte[8]) chars)[0];
+    return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080));
+}
+
+uint parseEightDigits()(ref const char[8] chars)
+{
+    pragma(inline, true);
+    ulong val = (cast(ulong[1]) cast(ubyte[8]) chars)[0];
+    val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
+    val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
+    return uint((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
+}

From 3024b5c9d0ba794f29dfa8fa428138df86c434b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Tue, 31 May 2022 14:47:10 +0200
Subject: [PATCH 425/573] Also test using gmp-d in bigint_benchmark (#411)

---
 bigint_benchmark/dub.sdl      |  1 +
 bigint_benchmark/source/app.d | 40 +++++++++++++++++++++++++++++------
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/bigint_benchmark/dub.sdl b/bigint_benchmark/dub.sdl
index 37e74f1e..5b57848a 100644
--- a/bigint_benchmark/dub.sdl
+++ b/bigint_benchmark/dub.sdl
@@ -1,2 +1,3 @@
 name "bigint_benchmark"
 dependency "mir-algorithm" path="../"
+dependency "gmp-d" version="~master"
diff --git a/bigint_benchmark/source/app.d b/bigint_benchmark/source/app.d
index e0b67237..ed60869b 100644
--- a/bigint_benchmark/source/app.d
+++ b/bigint_benchmark/source/app.d
@@ -1,6 +1,7 @@
 import mir.bignum.integer: BigInt;
 import mir.stdio;
 import std.bigint: StdBigInt = BigInt;
+import gmp.z : GmpBigInt = MpZ, powmod;
 import std.datetime.stopwatch;
 
 immutable ps = "E5B5B1EDC8DF0F307C2220151CFCBE31F69B15659A5D6FBA1E50F55A08B341218312D707CFC16ED86A1765F5AEAFA7E6A11C4431038914C76F0F398FE6BE031E289B220D13D9E02226C691D15BC6E1186EA18222D93F52A393BE1DA1A42853512419B5E6E304FD02E962A4C2D0ECDDB8F44AC094FACA8333AE94110A5B10DA539C24A96F08530E7699E3F705165CF14B7F90A2F32ED28D21615F91D7C808AC566D6EEEF6773450AB53542CDAC337C3124530CB16319752267C3422149D41543D8742586BAB578F4E06360745AE0BD8F0E800D1920DC1F3661287367A78967458383A82465C5D966E7299EFCF58BD860185F96655E1F8D300F6B096DFE883CF15";
@@ -49,26 +50,53 @@ void testMir()
     debug dout << b << endl;
 }
 
+void testGmp()
+{
+    import std.algorithm.mutation : move;
+    auto p = GmpBigInt.fromHexString(ps);
+    auto q = GmpBigInt.fromHexString(qs);
+    GmpBigInt m = p.move();
+    m *= q;
+    auto e = GmpBigInt.fromHexString(es);
+    GmpBigInt b = e.dup;
+    b.powmod(e, m);
+    debug dout << b << endl;
+}
+
 void main()
 {
     version (assert)
     {
-        testStd;
-        testMir;
+        testStd();
+        testMir();
+        testGmp();
         dout << "please compile with --build=release" <<  endl;
     }
     else
     {
         import std.system: os;
-        auto res = 10.benchmark!(testStd, testMir);
-        auto ratio = double(res[0].total!"usecs") / res[1].total!"usecs";
-        dout
+        const res = 10.benchmark!(testStd, testMir, testGmp);
+        {
+            const mirRatio = double(res[0].total!"usecs") / res[1].total!"usecs";
+            dout
             << "--------------------------------------------" << endl
-            << "mir speedup = " << cast(int)((ratio -  1) * 100_0) / 10.0 << "%" << endl
+            << "mir speedup = " << cast(int)((mirRatio -  1) * 100_0) / 10.0 << "%" << endl
             << "std = " <<  res[0] << endl
             << "mir = " <<  res[1] << endl
             << " ............... " << size_t.sizeof * 8 << "bit " << os << " ............... " << endl
             << "--------------------------------------------"
             << endl;
+        }
+        {
+            const gmpRatio = double(res[0].total!"usecs") / res[2].total!"usecs";
+            dout
+            << "--------------------------------------------" << endl
+            << "gmp speedup = " << cast(int)((gmpRatio -  1) * 100_0) / 10.0 << "%" << endl
+            << "std = " <<  res[0] << endl
+            << "gmp = " <<  res[2] << endl
+            << " ............... " << size_t.sizeof * 8 << "bit " << os << " ............... " << endl
+            << "--------------------------------------------"
+            << endl;
+        }
     }
 }

From 4813637a5559ccf6cc65b65443bc6cfb6dfd2132 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ilyayaroshenko@gmail.com>
Date: Wed, 1 Jun 2022 03:08:07 +0400
Subject: [PATCH 426/573] Rework dec2flot (#412)

* rework decimal to FP conversion

* debug

* get rid of algorithmR

* fixup

* fixup
---
 README.md                                    |    5 +-
 bigint_benchmark/source/app.d                |   66 +-
 dub.sdl                                      |    7 +-
 source/mir/appender.d                        |    3 +-
 source/mir/bignum/decimal.d                  |   37 +-
 source/mir/bignum/fp.d                       |  154 +-
 source/mir/bignum/integer.d                  |  144 +-
 source/mir/bignum/internal/dec2float.d       |  518 +--
 source/mir/bignum/internal/dec2float_table.d | 3087 ++++++++++++------
 source/mir/bignum/internal/kernel.d          |    4 +-
 source/mir/bignum/low_level_view.d           |  292 +-
 source/mir/format.d                          |   11 +
 source/mir/parse.d                           |  153 +-
 source/mir/stdio.d                           |   55 +-
 source/mir/test.d                            |   47 +-
 15 files changed, 2923 insertions(+), 1660 deletions(-)

diff --git a/README.md b/README.md
index 948d924f..c3fa6255 100644
--- a/README.md
+++ b/README.md
@@ -41,8 +41,9 @@ void main()
     row[3] = 6;
     assert(matrix[2, 3] == 6); // D & C index order
     
-    import std.stdio;
-    matrix.writeln; // [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 6]]
+    import mir.stdio;
+    matrix.writeln;
+    // prints [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 6.0]]
 }
 ```
 
diff --git a/bigint_benchmark/source/app.d b/bigint_benchmark/source/app.d
index ed60869b..d113422f 100644
--- a/bigint_benchmark/source/app.d
+++ b/bigint_benchmark/source/app.d
@@ -1,45 +1,26 @@
-import mir.bignum.integer: BigInt;
 import mir.stdio;
-import std.bigint: StdBigInt = BigInt;
-import gmp.z : GmpBigInt = MpZ, powmod;
 import std.datetime.stopwatch;
 
 immutable ps = "E5B5B1EDC8DF0F307C2220151CFCBE31F69B15659A5D6FBA1E50F55A08B341218312D707CFC16ED86A1765F5AEAFA7E6A11C4431038914C76F0F398FE6BE031E289B220D13D9E02226C691D15BC6E1186EA18222D93F52A393BE1DA1A42853512419B5E6E304FD02E962A4C2D0ECDDB8F44AC094FACA8333AE94110A5B10DA539C24A96F08530E7699E3F705165CF14B7F90A2F32ED28D21615F91D7C808AC566D6EEEF6773450AB53542CDAC337C3124530CB16319752267C3422149D41543D8742586BAB578F4E06360745AE0BD8F0E800D1920DC1F3661287367A78967458383A82465C5D966E7299EFCF58BD860185F96655E1F8D300F6B096DFE883CF15";
 immutable qs = "D9757338E9A6B363F227F3104EDEF6240C0CAF53B7D509F48870553C4A821F460469AE5616301B9CC30FBF4598A176B84284AF3A41D697A34CDC2C8D88A4C4BE82AE8DB5347511FE5B4DD915CA6A728CCFD0444CE38FC7190824059D86A9083C273581EA5AD1D5E3A8D8EC6858F291A5EADA98B0F5FD7C8E8CA6226657B8B7955796B22899B087714E293A86C78D42A7021754A6220F1D0A9588C280DD9AEC376E421D539F30A3053D95C7D70F24B471D14ECF282FA3E0B1CED2C405BA22404F3B75CD961A46097D7C098324FC47281D298734DA0DFCD8AF82E685657C926672727296147867EAEDFDEF89A79DE81FF104CF7D9157EF65A1BC333C98A7FED685";
 immutable es = ps ~ qs;
 
-void stdPowMod(ref StdBigInt base, StdBigInt exponent, StdBigInt modulus)
-{
-    StdBigInt result = 1;
-
-    while (exponent != 0)
-    {
-        base %= modulus;
-        if (exponent & 1)
-        {
-            result *= base;
-            result %= modulus;
-        }
-        exponent >>= 1;
-        base *= base;
-    }
-    base = result;
-}
-
 void testStd()
 {
-    StdBigInt p = "0x" ~ ps;
-    StdBigInt q = "0x" ~ qs;
-    StdBigInt m = p;
+    import std.bigint;
+    BigInt p = "0x" ~ ps;
+    BigInt q = "0x" ~ qs;
+    BigInt m = p;
     m *= q;
-    StdBigInt e = "0x" ~ es;
-    StdBigInt b = e;
-    b.stdPowMod(e, m);
+    BigInt e = "0x" ~ es;
+    BigInt b = e;
+    b = powmod(b, e, m);
     debug dout << b << endl;
 }
 
 void testMir()
 {
+    import mir.bignum.integer;
     auto p = BigInt!64.fromHexString(ps);
     auto q = BigInt!64.fromHexString(qs);
     BigInt!64 m = p;
@@ -52,14 +33,15 @@ void testMir()
 
 void testGmp()
 {
+    import gmp.z : BigInt = MpZ, powmod;
     import std.algorithm.mutation : move;
-    auto p = GmpBigInt.fromHexString(ps);
-    auto q = GmpBigInt.fromHexString(qs);
-    GmpBigInt m = p.move();
+    auto p = BigInt.fromHexString(ps);
+    auto q = BigInt.fromHexString(qs);
+    BigInt m = p.move();
     m *= q;
-    auto e = GmpBigInt.fromHexString(es);
-    GmpBigInt b = e.dup;
-    b.powmod(e, m);
+    auto e = BigInt.fromHexString(es);
+    BigInt b = e.dup;
+    b = b.powmod(e, m);
     debug dout << b << endl;
 }
 
@@ -76,27 +58,17 @@ void main()
     {
         import std.system: os;
         const res = 10.benchmark!(testStd, testMir, testGmp);
-        {
-            const mirRatio = double(res[0].total!"usecs") / res[1].total!"usecs";
-            dout
+        const mirRatio = double(res[0].total!"usecs") / res[1].total!"usecs";
+        const gmpRatio = double(res[0].total!"usecs") / res[2].total!"usecs";
+        dout
             << "--------------------------------------------" << endl
+            << "gmp speedup = " << cast(int)((gmpRatio -  1) * 100_0) / 10.0 << "%" << endl
             << "mir speedup = " << cast(int)((mirRatio -  1) * 100_0) / 10.0 << "%" << endl
             << "std = " <<  res[0] << endl
             << "mir = " <<  res[1] << endl
-            << " ............... " << size_t.sizeof * 8 << "bit " << os << " ............... " << endl
-            << "--------------------------------------------"
-            << endl;
-        }
-        {
-            const gmpRatio = double(res[0].total!"usecs") / res[2].total!"usecs";
-            dout
-            << "--------------------------------------------" << endl
-            << "gmp speedup = " << cast(int)((gmpRatio -  1) * 100_0) / 10.0 << "%" << endl
-            << "std = " <<  res[0] << endl
             << "gmp = " <<  res[2] << endl
             << " ............... " << size_t.sizeof * 8 << "bit " << os << " ............... " << endl
             << "--------------------------------------------"
             << endl;
-        }
     }
 }
diff --git a/dub.sdl b/dub.sdl
index bbee2c1f..c53be93f 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -6,11 +6,12 @@ copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry
 license "Apache-2.0"
 
 dependency "mir-core" version=">=1.1.106"
-// dependency "silly" version="~>1.1.1"
+
+// versions "TeslAlgoM"
 
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_bignum_test_llv" "mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test" "mir_bignum_test_llv" // "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
@@ -54,7 +55,7 @@ configuration "dips" {
 }
 
 configuration "ci-bignum-test" {
-    versions "mir_bignum_test"
+    versions "mir_bignum_test" "mir_bignum_test_llv"
 }
 
 configuration "ci-core-test" {
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 9c54182e..f1b6b8d5 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -38,7 +38,8 @@ struct ScopedBuffer(T, size_t bytes = 4096)
         return *cast(inout(T[_bufferLength])*)&_scopeBufferPayload;
     }
 
-    private T[] prepare(size_t n) @trusted scope
+    ///
+    T[] prepare(size_t n) @trusted scope
     {
         import mir.internal.memory: realloc, malloc;
         _currentLength += n;
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 48896ab0..c9a43f2c 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -18,11 +18,11 @@ private static immutable C[9] zerosImpl(C) = "0.00000.0";
 /++
 Stack-allocated decimal type.
 Params:
-    maxSize64 = count of 64bit words in coefficient
+    size64 = count of 64bit words in coefficient
 +/
 @serdeScoped @serdeProxy!(const(char)[])
-struct Decimal(uint maxSize64)
-    if (maxSize64 && maxSize64 <= ushort.max)
+struct Decimal(uint size64)
+    if (size64 && size64 <= ushort.max)
 {
     import mir.format: NumericSpec;
     import mir.bignum.integer;
@@ -32,7 +32,7 @@ struct Decimal(uint maxSize64)
     ///
     long exponent;
     ///
-    BigInt!maxSize64 coefficient;
+    BigInt!size64 coefficient;
 
     ///
     DecimalView!size_t view()
@@ -56,11 +56,11 @@ struct Decimal(uint maxSize64)
         static if (__traits(compiles, () @nogc { throw new Exception("Can't parse Decimal."); }))
         {
             import mir.exception: MirException;
-            throw new MirException("Can't parse Decimal!" ~ maxSize64.stringof ~ " from string `", str , "`");
+            throw new MirException("Can't parse Decimal!" ~ size64.stringof ~ " from string `", str , "`");
         }
         else
         {
-            static immutable exception = new Exception("Can't parse Decimal!" ~ maxSize64.stringof ~ ".");
+            static immutable exception = new Exception("Can't parse Decimal!" ~ size64.stringof ~ ".");
             throw exception;
         }
     }
@@ -71,7 +71,7 @@ struct Decimal(uint maxSize64)
     The number is the shortest decimal representation that being converted back would result the same floating-point number.
     +/
     this(T)(const T x)
-        if (isFloatingPoint!T && maxSize64 >= 1 + (T.mant_dig >= 64))
+        if (isFloatingPoint!T && size64 >= 1 + (T.mant_dig >= 64))
     {
         import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal;
         this = genericBinaryToDecimal(x);
@@ -79,7 +79,7 @@ struct Decimal(uint maxSize64)
 
     ///
     ref opAssign(uint rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return
-        if (rhsMaxSize64 < maxSize64)
+        if (rhsMaxSize64 < size64)
     {
         this.exponent = rhs.exponent;
         this.coefficient = rhs.coefficient;
@@ -180,7 +180,7 @@ struct Decimal(uint maxSize64)
         scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
-        enum optimize = size_t.sizeof == 8 && maxSize64 == 1;
+        enum optimize = size_t.sizeof == 8 && size64 == 1;
         version(LDC)
         {
             static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false)
@@ -333,7 +333,7 @@ struct Decimal(uint maxSize64)
                             v = mulu(v, multplier, overflow);
                             if (overflow)
                                 return false;
-                            v = addu(v, value, overflow);;
+                            v = addu(v, value, overflow);
                             if (overflow)
                                 return false;
                         }
@@ -351,7 +351,7 @@ struct Decimal(uint maxSize64)
                     v = mulu(v, cast(uint)10, overflow);
                     if (overflow)
                         return false;
-                    v = addu(v, d, overflow);;
+                    v = addu(v, d, overflow);
                     if (overflow)
                         return false;
                 }
@@ -503,13 +503,13 @@ struct Decimal(uint maxSize64)
             }
             else
             {
-                BigInt!maxSize64 work = coefficient;
+                BigInt!size64 work = coefficient;
                 coefficientLength = work.view.unsigned.toStringImpl(buffer);
             }
         }
         else
         {
-            BigInt!maxSize64 work = coefficient;
+            BigInt!size64 work = coefficient;
             coefficientLength = work.view.unsigned.toStringImpl(buffer);
         }
 
@@ -675,7 +675,7 @@ struct Decimal(uint maxSize64)
         if (op == "+" || op == "-")
     {
         import mir.utility: max;
-        BigInt!(max(rhsMaxSize64, maxSize64, 256u)) rhsCopy = void;
+        BigInt!(max(rhsMaxSize64, size64, 256u)) rhsCopy = void;
         BigIntView!(const size_t) rhsView;
         auto expDiff = cast(sizediff_t) (exponent - rhs.exponent);
         if (expDiff >= 0)
@@ -702,13 +702,14 @@ version(mir_bignum_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.test: should;
     import mir.conv: to;
     Decimal!256 decimal = void;
     DecimalExponentKey key;
 
     assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
-    assert(cast(double) decimal == 0x1.596bf8ce7631ep-32);
-    assert(key == DecimalExponentKey.e);
+    decimal.to!double.should == 0x1.596bf8ce7631ep-32;
+    key.should == DecimalExponentKey.e;
 }
 
 ///
@@ -808,6 +809,8 @@ version(mir_bignum_test)
 @safe pure nothrow @nogc
 unittest
 {
+    import mir.test: should;
+
     import mir.conv: to;
     Decimal!3 decimal;
     DecimalExponentKey key;
@@ -865,7 +868,7 @@ unittest
     assert(decimal.coefficient.length == 0);
     assert(decimal.exponent == decimal.exponent.max);
     assert(key == DecimalExponentKey.infinity);
-    assert(cast(double) decimal == -double.infinity);
+    should(cast(double) decimal) == -double.infinity;
 
     assert(!decimal.fromStringImpl("3.3.4", key));
     assert(!decimal.fromStringImpl("3.4.", key));
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 61338fdc..8d5cbf49 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -378,7 +378,7 @@ struct Fp(uint size)
     ///
     Fp!(max(size, rhsSize)) opBinary(string op : "*", uint rhsSize)(Fp!rhsSize rhs) nothrow const
     {
-        return cast(Fp) .extendedMul!(size, rhsSize)(cast()this, rhs);
+        return cast(Fp) .extendedMul(cast()this, rhs);
     }
 
     static if (size == 128)
@@ -418,16 +418,19 @@ struct Fp(uint size)
     }
     
     ///
-    T opCast(T, bool noHalf = false)() nothrow const
+    T opCast(T, bool noSpecial = false, bool noHalf = false)() nothrow const
         if (is(T == float) || is(T == double) || is(T == real))
     {
         import mir.math.ieee: ldexp;
-        if (_expect(this.isSpecial, false))
+        static if (!noSpecial)
         {
-            T ret = this.coefficient ? T.nan : T.infinity;
-            if (this.sign)
-                ret = -ret;
-            return ret;
+            if (_expect(this.isSpecial, false))
+            {
+                T ret = this.coefficient ? T.nan : T.infinity;
+                if (this.sign)
+                    ret = -ret;
+                return ret;
+            }
         }
         auto exp = cast()this.exponent;
         static if (size == 32)
@@ -544,30 +547,37 @@ struct Fp(uint size)
     }
 
     ///
-    T opCast(T : Fp!newSize, size_t newSize)() nothrow const
+    T opCast(T : Fp!newSize, bool noSpecial = false, size_t newSize)() nothrow const
         if (newSize != size)
     {
         Fp!newSize ret;
         ret.sign = this.sign;
-        if (_expect(this.isSpecial, false))
-        {
-            ret.exponent = ret.exponent.max;
-            ret.coefficient = !!this.coefficient;
-            return ret;
-        }
-        if (!this)
+
+        static if (!noSpecial)
         {
-            return ret;
+            if (_expect(this.isSpecial, false))
+            {
+                ret.exponent = ret.exponent.max;
+                ret.coefficient = !!this.coefficient;
+                return ret;
+            }
+            if (!this)
+            {
+                return ret;
+            }
         }
 
         UInt!size coefficient = this.coefficient;
         int shift;
         // subnormal
 
-        if (this.exponent == this.exponent.min)
+        static if (!noSpecial)
         {
-            shift = cast(int)coefficient.ctlz;
-            coefficient <<= shift;
+            if (this.exponent == this.exponent.min)
+            {
+                shift = cast(int)coefficient.ctlz;
+                coefficient <<= shift;
+            }
         }
 
         ret = Fp!newSize(coefficient, true);
@@ -576,23 +586,31 @@ struct Fp(uint size)
 
         import mir.checkedint: adds;
         /// overflow
-        bool overflow;
-        ret.exponent = adds(ret.exponent, this.exponent, overflow);
-        if (_expect(overflow, false))
+
+        static if (!noSpecial)
         {
-            // overflow
-            if (this.exponent > 0)
+            bool overflow;
+            ret.exponent = adds(ret.exponent, this.exponent, overflow);
+            if (_expect(overflow, false))
             {
-                ret.exponent = ret.exponent.max;
-                ret.coefficient = 0u;
-            }
-            // underflow
-            else
-            {
-                ret.coefficient >>= cast(uint)(ret.exponent - exponent.min);
-                ret.exponent = ret.coefficient ? ret.exponent.min : 0;
+                // overflow
+                if (this.exponent > 0)
+                {
+                    ret.exponent = ret.exponent.max;
+                    ret.coefficient = 0u;
+                }
+                // underflow
+                else
+                {
+                    ret.coefficient >>= cast(uint)(ret.exponent - exponent.min);
+                    ret.exponent = ret.coefficient ? ret.exponent.min : 0;
+                }
             }
         }
+        else
+        {
+            ret.exponent += this.exponent;
+        }
         return ret;
     }
 
@@ -612,52 +630,64 @@ struct Fp(uint size)
 }
 
 ///
-Fp!(coefficientizeA + coefficientizeB) extendedMul(uint coefficientizeA, uint coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
+Fp!(coefficientizeA + coefficientizeB) extendedMul(bool noSpecial = false, uint coefficientizeA, uint coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
     @safe pure nothrow @nogc
 {
     import mir.bignum.fixed: extendedMul;
     import mir.checkedint: adds;
 
     typeof(return) ret = void;
-    // nan * any -> nan
-    // inf * fin -> inf
     ret.coefficient = extendedMul(a.coefficient, b.coefficient);
-    if (_expect(a.isSpecial | b.isSpecial, false))
-    {   // set nan
-        ret.exponent = ret.exponent.max;
-        // nan inf case
-        if (a.isSpecial & b.isSpecial)
-            ret.coefficient = a.coefficient | b.coefficient;
+    static if (noSpecial)
+    {
+        ret.exponent = a.exponent + b.exponent;
+        if (!ret.coefficient.signBit)
+        {
+            ret.exponent -= 1; // check overflow
+            ret.coefficient = ret.coefficient.smallLeftShift(1);
+        }
     }
     else
     {
-        bool overflow;
-        ret.exponent = adds(a.exponent, b.exponent, overflow);
-        // exponent underflow -> 0 or subnormal
-        // overflow -> inf
-        if (_expect(overflow, false))
+        // nan * any -> nan
+        // inf * fin -> inf
+        if (_expect(a.isSpecial | b.isSpecial, false))
+        {   // set nan
+            ret.exponent = ret.exponent.max;
+            // nan inf case
+            if (a.isSpecial & b.isSpecial)
+                ret.coefficient = a.coefficient | b.coefficient;
+        }
+        else
         {
-            // overflow
-            if (a.exponent > 0) //  && b.exponent > 0 is always true
+            bool overflow;
+            ret.exponent = adds(a.exponent, b.exponent, overflow);
+            // exponent underflow -> 0 or subnormal
+            // overflow -> inf
+            if (_expect(overflow, false))
             {
-                ret.exponent = ret.exponent.max;
-                ret.coefficient = 0;
+                // overflow
+                if (a.exponent > 0) //  && b.exponent > 0 is always true
+                {
+                    ret.exponent = ret.exponent.max;
+                    ret.coefficient = 0;
+                }
+                //  underflow
+                else // a.exponent < 0 and b.exponent < 0
+                {
+                    // TODO: subnormal
+                    ret.exponent = 0;
+                    ret.coefficient = 0;
+                }
             }
-            //  underflow
-            else // a.exponent < 0 and b.exponent < 0
+            else
+            if (!ret.coefficient.signBit)
             {
-                // TODO: subnormal
-                ret.exponent = 0;
-                ret.coefficient = 0;
+                auto normal = ret.exponent != ret.exponent.min;
+                ret.exponent -= normal; // check overflow
+                ret.coefficient = ret.coefficient.smallLeftShift(normal);
             }
         }
-        else
-        if (!ret.coefficient.signBit)
-        {
-            auto normal = ret.exponent != ret.exponent.min;
-            ret.exponent -= normal; // check overflow
-            ret.coefficient = ret.coefficient.smallLeftShift(normal);
-        }
     }
     ret.sign = a.sign ^ b.sign;
     return ret;
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 2d70bf8c..a1ac4609 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -12,11 +12,11 @@ import std.traits;
 /++
 Stack-allocated big signed integer.
 Params:
-    maxSize64 = count of 64bit words in coefficient
+    size64 = count of 64bit words in coefficient
 +/
 @serdeScoped @serdeProxy!(const(char)[])
-struct BigInt(uint maxSize64)
-    if (maxSize64 && maxSize64 <= ushort.max)
+struct BigInt(uint size64)
+    if (size64 && size64 <= ushort.max)
 {
     import mir.bignum.low_level_view;
     import mir.bignum.fixed;
@@ -26,7 +26,7 @@ struct BigInt(uint maxSize64)
     ///
     uint length;
     ///
-    size_t[ulong.sizeof / size_t.sizeof * maxSize64] data;// = void;
+    size_t[ulong.sizeof / size_t.sizeof * size64] data;// = void;
 
     ///
     this(uint size)(UInt!size fixedInt)
@@ -108,11 +108,11 @@ struct BigInt(uint maxSize64)
         static if (__traits(compiles, () @nogc { throw new Exception("Can't parse BigInt."); }))
         {
             import mir.exception: MirException;
-            throw new MirException("Can't parse BigInt!" ~ maxSize64.stringof ~ " from string `", str , "`.");
+            throw new MirException("Can't parse BigInt!" ~ size64.stringof ~ " from string `", str , "`.");
         }
         else
         {
-            static immutable exception = new Exception("Can't parse BigInt!" ~ maxSize64.stringof ~ ".");
+            static immutable exception = new Exception("Can't parse BigInt!" ~ size64.stringof ~ ".");
             throw exception;
         }
     }
@@ -158,7 +158,7 @@ struct BigInt(uint maxSize64)
         return this;
     }
 
-    static if (maxSize64 == 3)
+    static if (size64 == 3)
     ///
     version(mir_bignum_test) @safe pure @nogc unittest
     {
@@ -174,7 +174,7 @@ struct BigInt(uint maxSize64)
         @trusted pure nothrow @nogc
         in (rhs.length <= this.data.length)
     {
-        static if (maxSize64 == rhsSize64)
+        static if (size64 == rhsSize64)
         {
             if (&this is &rhs)
                 return this;
@@ -226,7 +226,7 @@ struct BigInt(uint maxSize64)
     {
         if (rhs == 0 && this.length == 0 || this.length == 1 && this.sign == rhsSign && this.data[0] == rhs)
             return true;
-        static if (is(size_t == ulong) || maxSize64 == 1)
+        static if (is(size_t == ulong) || size64 == 1)
             return false;    
         else
             return this.length == 2 && this.data[0] == cast(uint) rhs && this.data[1] == cast(uint) (rhs >> 32);
@@ -433,9 +433,9 @@ struct BigInt(uint maxSize64)
             return this;
         }
 
-        BigInt!(maxSize64 * 2) bas = void;
+        BigInt!(size64 * 2) bas = void;
         bas = this;
-        BigInt!(maxSize64 * 2) res = void;
+        BigInt!(size64 * 2) res = void;
         res = 1u;
 
         foreach (b; exponent.coefficients.bitwise[0 .. $ - exponent.ctlz])
@@ -454,7 +454,7 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    static if (maxSize64 == 3)
+    static if (size64 == 3)
     version (mir_bignum_test)
     unittest
     {
@@ -467,7 +467,7 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    static if (maxSize64 == 3)
+    static if (size64 == 3)
     version (mir_bignum_test)
     unittest
     {
@@ -487,7 +487,7 @@ struct BigInt(uint maxSize64)
         scope ref const BigInt!bSize64 b,
     )
         @safe pure nothrow @nogc scope return
-        if (maxSize64 >= aSize64 + bSize64)
+        if (size64 >= aSize64 + bSize64)
     {
         import mir.utility: max;
         import mir.bignum.internal.kernel : multiply, karatsubaRequiredBuffSize;
@@ -498,12 +498,26 @@ struct BigInt(uint maxSize64)
         return this;
     }
 
-    ref divMod(uint divisorSize64, uint remainderSize = maxSize64)
+    ///
+    ref divMod(uint divisorSize64, uint remainderSize = size64)
     (
         scope ref const BigInt!divisorSize64 divisor,
+        scope ref BigInt!size64 quotient,
+        scope ref BigInt!remainderSize remainder,
+    )
+        const @trusted pure nothrow @nogc scope return
+        if (remainderSize >= divisorSize64)
+    {
+        return this.divMod(divisor, quotient, &remainder);
+    }
+
+    private ref divMod(uint divisorSize64, uint remainderSize = size64)
+    (
+        scope ref const BigInt!divisorSize64 divisor,
+        scope ref BigInt!size64 quotient,
         scope BigInt!remainderSize* remainder = null,
     )
-        @trusted pure nothrow @nogc scope return
+        const @trusted pure nothrow @nogc scope return
         if (remainderSize >= divisorSize64)
     {
         import mir.bignum.internal.kernel : divMod, divisionRequiredBuffSize;
@@ -523,28 +537,30 @@ struct BigInt(uint maxSize64)
                     *remainder = this;
                 remainder.sign = 0;
             }
-            if (&this !is remainder)
-            {
-                this.sign = 0;
-                this.length = 0;
-            }
+
+            static if (size64 == remainderSize)
+                if (&quotient is remainder)
+                    return this;
+
+            quotient.sign = 0;
+            quotient.length = 0;
 
             return this;
         }
 
         enum sizeM = ulong.sizeof / size_t.sizeof;
-        enum vlen = min(divisorSize64, maxSize64);
-        size_t[divisionRequiredBuffSize(maxSize64 * sizeM, vlen * sizeM)] buffer = void;
+        enum vlen = min(divisorSize64, size64);
+        size_t[divisionRequiredBuffSize(size64 * sizeM, vlen * sizeM)] buffer = void;
 
-        this.length = cast(uint) divMod(
-            this.coefficients,
+        quotient.length = cast(uint) divMod(
+            quotient.data,
             remainder !is null ? remainder.data[] : null,
             this.coefficients,
             divisor.coefficients,
             buffer,
         );
 
-        this.sign = (this.sign ^ divisor.sign) && this.length;
+        quotient.sign = (this.sign ^ divisor.sign) && quotient.length;
 
         if (remainder !is null)
         {
@@ -568,7 +584,7 @@ struct BigInt(uint maxSize64)
         if (op == "/" || op == "%")
     {
         enum isRem = op == "%";
-        return this.divMod(rhs, isRem ? &this : null);
+        return this.divMod(rhs, this, isRem ? &this : null);
     }
 
     /++
@@ -581,14 +597,14 @@ struct BigInt(uint maxSize64)
     ref opOpAssign(string op : "*", size_t rhsSize64)(scope const ref BigInt!rhsSize64 rhs)
         @safe pure nothrow @nogc return
     {
-        BigInt!(maxSize64 + rhsSize64) c = void;
+        BigInt!(size64 + rhsSize64) c = void;
         c.multiply(this, rhs);
         this = c;
         return this;
     }
 
     ///
-    static if (maxSize64 == 3)
+    static if (size64 == 3)
     version (mir_bignum_test)
     unittest
     {
@@ -726,7 +742,7 @@ struct BigInt(uint maxSize64)
             assert(0, binaryStringErrorMsg);
     }
 
-    static if (maxSize64 == 3)
+    static if (size64 == 3)
     ///
     version(mir_bignum_test) @safe pure @nogc unittest
     {
@@ -751,7 +767,24 @@ struct BigInt(uint maxSize64)
     }
 
     ///
-    bool mulPow5(size_t degree)
+    ref pow()(ulong degree)
+    {
+        BigInt!size64 bas = void;
+        bas = this;
+        this = 1u;
+
+        while (degree)
+        {
+            if (degree & 1)
+                this *= bas;
+            bas *= bas;
+            degree >>= 1;
+        }
+        return this;
+    }
+
+    ///
+    bool mulPow5()(ulong degree)
     {
         import mir.bignum.internal.dec2float: MaxWordPow5;
         // assert(approxCanMulPow5(degree));
@@ -760,7 +793,8 @@ struct BigInt(uint maxSize64)
         enum n = MaxWordPow5!size_t;
         enum wordInit = size_t(5) ^^ n;
         size_t word = wordInit;
-        bool of;
+        size_t overflow;
+
         while(degree)
         {
             if (degree >= n)
@@ -773,14 +807,9 @@ struct BigInt(uint maxSize64)
                 do word *= 5;
                 while(--degree);
             }
-            if (auto overflow = view *= word)
-            {
-                of = length >= data.length;
-                if (!of)
-                    putCoefficient(overflow);
-            }
+            overflow |= this *= word;
         }
-        return of;
+        return overflow != 0;
     }
 
     ///
@@ -925,7 +954,7 @@ struct BigInt(uint maxSize64)
         return buffer[$ - len .. $].idup;
     }
 
-    static if (maxSize64 == 3)
+    static if (size64 == 3)
     ///
     version(mir_bignum_test) @safe pure unittest
     {
@@ -949,17 +978,28 @@ struct BigInt(uint maxSize64)
         w.put(buffer[$ - len .. $]);
     }
 
-    static if (maxSize64 == 3)
-    /// Check @nogc toString impl
-    version(mir_bignum_test) @safe pure @nogc unittest
+    ///
+    size_t bitLength()() const @property
     {
-        import mir.format;
-        auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
-        auto integer = BigInt!4(str);
-        auto buffer = stringBuf;
-        buffer << integer;
-        assert(buffer.data == str);
+        return length == 0 ? 0 : length * size_t.sizeof * 8 - data[length - 1].ctlz;
     }
+
+    ///
+    size_t ctlz()() const @property
+    {
+        return data.sizeof * 8 - bitLength;
+    }
+}
+
+/// Check @nogc toString impl
+version(mir_bignum_test) @safe pure @nogc unittest
+{
+    import mir.format;
+    auto str = "-34010447314490204552169750449563978034784726557588085989975288830070948234680";
+    auto integer = BigInt!4(str);
+    auto buffer = stringBuf;
+    buffer << integer;
+    assert(buffer.data == str);
 }
 
 ///
@@ -974,9 +1014,11 @@ unittest
         auto b = UInt!128.fromHexString("f79a222050aaeaaa417fa25a2ac93291");
 
         // ca3d7e25aebe687b 168dcef32d0bb2f0
-        assert((a /= b) == BigInt!4.fromHexString("ca3d7e25aebe687b7cc1b250b44690fb"));
-        a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
+        import mir.format;
         assert((a %= b) == BigInt!4.fromHexString("bf4c87424431d21563f23b1fc00d75ac"));
+        a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
+        a /= b;
+        assert(a == BigInt!4.fromHexString("ca3d7e25aebe687b7cc1b250b44690fb"), a.data.text);
     }
 
     {
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 02c7460d..02449299 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -1,6 +1,6 @@
 module mir.bignum.internal.dec2float;
 
-version(LDC) import ldc.attributes: optStrategy;
+version (LDC) import ldc.attributes: optStrategy;
 else struct optStrategy { string opt; }
 
 template MaxWordPow5(T)
@@ -43,316 +43,340 @@ alias decimalTo(T : float) = decimalToFloat32;
 alias decimalTo(T : double) = decimalToFloat64;
 alias decimalTo(T : real) = decimalToReal;
 
-private float decimalToFloat32(scope const size_t[] coefficients, long exponent)
+alias binaryTo(T : float) = binaryToFloat32;
+alias binaryTo(T : double) = binaryToFloat64;
+alias binaryTo(T : real) = binaryToReal;
+
+private float binaryToFloat32(scope const size_t[] coefficients, long exponent = 0)
 {
     pragma(inline, false);
-    return decimalToFloatImpl!float(coefficients, exponent);
+    return binaryToFloatImpl!float(coefficients, exponent);
 }
 
-private double decimalToFloat64(scope const size_t[] coefficients, long exponent)
+private double binaryToFloat64(scope const size_t[] coefficients, long exponent = 0)
 {
     pragma(inline, false);
-    return decimalToFloatImpl!double(coefficients, exponent);
+    return binaryToFloatImpl!double(coefficients, exponent);
 }
 
-private real decimalToReal(scope const size_t[] coefficients, long exponent)
+private real binaryToReal(scope const size_t[] coefficients, long exponent = 0)
 {
     pragma(inline, real.mant_dig == double.mant_dig);
     static if (real.mant_dig == double.mant_dig)
-        return decimalToFloat64(coefficients, exponent);
+        return binaryToFloat64(coefficients, exponent);
     else
-        return decimalToFloatImpl!real(coefficients, exponent);
+        return binaryToFloatImpl!real(coefficients, exponent);
 }
 
-private float decimalToFloat32(scope const size_t[] coefficients)
+private float decimalToFloat32(scope const ulong coefficient, long exponent)
 {
     pragma(inline, false);
-    return decimalToFloatImpl!float(coefficients);
+    return decimalToFloatImpl!float(coefficient, exponent);
 }
 
-private double decimalToFloat64(scope const size_t[] coefficients)
+private double decimalToFloat64(scope const ulong coefficient, long exponent)
 {
     pragma(inline, false);
-    return decimalToFloatImpl!double(coefficients);
+    return decimalToFloatImpl!double(coefficient, exponent);
 }
 
-private real decimalToReal(scope const size_t[] coefficients)
+private real decimalToReal(scope const ulong coefficient, long exponent)
 {
     pragma(inline, real.mant_dig == double.mant_dig);
     static if (real.mant_dig == double.mant_dig)
-        return decimalToFloat64(coefficients);
+        return decimalToFloat64(coefficient, exponent);
     else
-        return decimalToFloatImpl!real(coefficients);
+        return decimalToFloatImpl!real(coefficient, exponent);
 }
 
-T decimalToFloat(T)(ulong coefficient, long exponent)
-    if (is(T == real))
+private float decimalToFloat32(scope const size_t[] coefficients, long exponent)
 {
-    version(LDC)
-        pragma(inline, true);
-    static if (T.mant_dig < 64)
-    {
-        return decimalToFloat!double(coefficient, exponent);
-    }
+    pragma(inline, false);
+    return decimalToFloatImpl!float(coefficients, exponent);
+}
+
+private double decimalToFloat64(scope const size_t[] coefficients, long exponent)
+{
+    pragma(inline, false);
+    return decimalToFloatImpl!double(coefficients, exponent);
+}
+
+private real decimalToReal(scope const size_t[] coefficients, long exponent)
+{
+    pragma(inline, real.mant_dig == double.mant_dig);
+    static if (real.mant_dig == double.mant_dig)
+        return decimalToFloat64(coefficients, exponent);
     else
-    {
-        auto coefficients = cast(size_t[ulong.sizeof / size_t.sizeof]) cast(ulong[1])[coefficient];
-        static if (coefficients.length == 1)
-            return decimalTo!real(approx, coefficients[0 .. !!coefficient], exponent);
-        else
-            return decimalTo!real(approx, coefficients[0 .. !!coefficient + (coefficient > uint.max)], exponent);
-    }
+        return decimalToFloatImpl!real(coefficients, exponent);
 }
 
-T decimalToFloat(T)(ulong coefficient, long exponent)
-    if (is(T == float) || is(T == double))
+T decimalToFloatImpl(T)(ulong coefficient, long exponent)
+    if (is(T == float) || is(T == double) || is(T == real))
 {
+    version (LDC)
+        pragma(inline, true);
+
     import mir.bignum.fixed: UInt;
     import mir.bignum.fp: Fp, extendedMul;
     import mir.utility: _expect;
 
-    version(LDC)
-        pragma(inline, true);
-
-    enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
-    enum ulong half = (1UL << (64 - T.mant_dig - 1));
-    enum ulong bound = ulong(1) << T.mant_dig;
+    enum wordBits = T.mant_dig < 64 ? 64 : 128;
+    enum ulong half = (1UL << (wordBits - T.mant_dig - 1));
+    static if (T.mant_dig > 64)
+        enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
+    else
+    static if (T.mant_dig == 64)
+        enum ulong mask = ulong.max;
+    else
+        enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
+    
+    if (coefficient == 0)
+        return 0;
 
-    auto expSign = exponent < 0;
-    if (_expect((expSign ? -exponent : exponent) >>> dec_S == 0, true))
+    version (TeslAlgoM) {} else
+    if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
     {
-        auto c = Fp!64(UInt!64(coefficient));
-        auto z = c.extendedMul(load64(exponent));
-        auto approx = cast(T) z;
-        long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
-        if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > 3 * expSign, true))
-            return approx;
-        assert(coefficient);
-        if (!expSign && exponent <= MaxWordPow5!ulong || exponent == 0)
+        auto c = coefficient.Fp!64;
+        auto z = c.extendedMul!true(_load!T(exponent));
+        auto approx = z.opCast!(T, true);
+        long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
+        int slop = 3 * (exponent < 0);
+        if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
             return approx;
-        if (expSign && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
+
+        if (0 <= exponent)
         {
-            auto e = load64(-exponent);
-            return c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
+            if (exponent <= MaxWordPow5!ulong)
+                return approx;
         }
-        auto coefficients = cast(size_t[ulong.sizeof / size_t.sizeof]) cast(ulong[1])[coefficient];
-        static if (coefficients.length == 1)
-            return decimalToFloatFallback(approx, coefficients, cast(int) exponent);
         else
-            return decimalToFloatFallback(approx, coefficients[0 .. 1 + (coefficient > uint.max)], cast(int) exponent);
+        {
+            if (-exponent <= MaxFpPow5!T)
+            {
+                auto e = _load!T(-exponent);
+                return coefficient / e.opCast!(T, true);
+            }
+        }
     }
-    return expSign ? 0 : exponent != exponent.max ? T.infinity : coefficient ? T.nan : T.infinity;
+    size_t[ulong.sizeof / size_t.sizeof] coefficients;
+    coefficients[0] = cast(size_t) coefficient;
+    static if (coefficients.length == 2)
+        coefficients[1] = cast(size_t) (coefficient >> 32);
+    static if (coefficients.length == 1)
+        return algorithmM!T(coefficients, exponent);
+    else
+        return algorithmM!T(coefficients[0 .. 1 + (coefficient > uint.max)], exponent);
 }
 
-private T decimalToFloatImpl(T)(scope const size_t[] coefficients)
+private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent)
     @safe
     if ((is(T == float) || is(T == double) || is(T == real)))
     in (coefficients.length == 0 || coefficients[$ - 1])
 {
-    version(LDC)
+    version (LDC)
         pragma(inline, true);
 
-    enum md = T.mant_dig;
-    enum b = size_t.sizeof * 8;
-    enum n = md / b + (md % b != 0);
-    enum s = n * b;
+    import mir.bignum.fixed: UInt;
+    import mir.bignum.fp: Fp, extendedMul;
+    import mir.utility: _expect;
+
+    enum wordBits = T.mant_dig < 64 ? 64 : 128;
+    enum ulong half = (1UL << (wordBits - T.mant_dig - 1));
+    static if (T.mant_dig > 64)
+        enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
+    else
+    static if (T.mant_dig == 64)
+        enum ulong mask = ulong.max;
+    else
+        enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
 
-    return coefficients.length ? coefficients.decimalToFp!(s, s - md).opCast!(T, true) : 0;
+    if (coefficients.length < 1)
+        return 0;
+
+    if (coefficients.length == 1)
+        return decimalTo!T(coefficients[0], exponent);
+
+    static if (size_t.sizeof == uint.sizeof)
+    {
+        if (coefficients.length == 2)
+            return decimalTo!T(coefficients[0] | (ulong(coefficients[1]) << 32), exponent);
+    }
+
+    version (TeslAlgoM) {} else
+    if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
+    {
+        auto c = coefficients.binaryToFp!wordBits;
+        auto z = c.extendedMul!true(_load!T(exponent));
+        auto approx = z.opCast!(T, true);
+        auto slop = 1 + 3 * (exponent < 0);
+        long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
+
+        if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
+            return approx;
+    }
+    return algorithmM!T(coefficients, exponent);
 }
 
-private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent)
-    @safe
+private enum LOG2_10 = 0x3.5269e12f346e2bf924afdbfd36bf6p0;
+
+private template bigSize(T)
     if ((is(T == float) || is(T == double) || is(T == real)))
-    in (coefficients.length == 0 || coefficients[$ - 1])
 {
-    import mir.bignum.fixed: UInt;
-    import mir.bignum.fp: Fp, extendedMul;
-    import mir.math.common: floor;
-    import mir.utility: _expect;
-
     static if (T.mant_dig < 64)
     {
-        enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
-        enum ulong half = (1UL << (64 - T.mant_dig - 1));
-        enum ulong bound = ulong(1) << T.mant_dig;
-
-        if (coefficients.length == 1)
-            return decimalToFloat!T(coefficients[0], exponent);
-        if (coefficients.length < 1)
-            return exponent == exponent.max ? T.infinity : 0;
-        auto expSign = exponent < 0;
-        if (_expect((expSign ? -exponent : exponent) >>> dec_S == 0, true))
-        {
-            auto c = coefficients.decimalToFp!64;
-            auto z = c.extendedMul(load64(exponent));
-            auto approx = cast(T) z;
-            auto slop = (coefficients.length > (ulong.sizeof / size_t.sizeof)) + 3 * expSign;
-            long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half;
-            if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
-                return approx;
-            if (slop == 0 && exponent <= MaxWordPow5!ulong || exponent == 0)
-                return approx;
-            if (slop == 3 && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound)
-            {
-                auto e = load64(-exponent);
-                approx =  c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent);
-                return approx;
-            }
-            return decimalToFloatFallback(approx, coefficients, cast(int) exponent);
-        }
-        return expSign ? 0 : exponent != exponent.max ? T.infinity : T.nan;
+        enum size_t bigSize = 83;
     }
     else
     {
-        if (exponent == 0)
-            return decimalTo!T(coefficients);
-
-        if (coefficients.length == 0)
-            return exponent == exponent.max ? T.infinity : 0;
-
-        auto expSign = exponent < 0;
-        ulong exp = exponent;
-        exp = expSign ? -exp : exp;
-        if (exp >= 5000)
-        {
-            return expSign ? 0 : exponent != exponent.max ? T.infinity : T.nan;
-        }
-        long index = exp & 0x1FF;
-        bool gotoAlgoR;
-        auto c = load128(expSign ? -index : index);
-        {
-            exp >>= dec_S;
-            gotoAlgoR = exp != 0;
-            if (_expect(gotoAlgoR, false))
-            {
-                auto v = load128(expSign ? -dec_P : dec_P);
-                do
-                {
-                    if (exp & 1)
-                        c *= v;
-                    exp >>>= 1;
-                    if (exp == 0)
-                        break;
-                    v *= v;
-                }
-                while(true);
-            }
-        }
-        auto z = coefficients.decimalToFp!128.extendedMul(c);
-        auto ret = cast(T) z;
-        if (!gotoAlgoR)
-        {
-            static if (T.mant_dig > 64)
-                enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
-            else
-                enum ulong mask = ulong.max;
-            enum ulong half = (1UL << (128 - T.mant_dig - 1));
-            enum UInt!128 bound = UInt!128(1) << T.mant_dig;
-
-            auto slop = (coefficients.length > (ulong.sizeof * 2 / size_t.sizeof)) + 3 * expSign;
-            long bitsDiff = (cast(ulong) z.opCast!(Fp!128).coefficient & mask) - half;
-            if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
-                return ret;
-            if (slop == 0 && exponent <= 55 || exponent == 0)
-                return ret;
-            if (slop == 3 && MaxFpPow5!T >= -exponent && c.coefficient < bound)
-            {
-                auto e = load128(-exponent);
-                return c.opCast!(T, true) / cast(T) e;
-            }
-        }
-        return decimalToFloatFallback(ret, coefficients, cast(int) exponent);
+        enum size_t bits = T.max_exp - T.min_exp + T.mant_dig;
+        enum size_t bigSize = bits / 64 + bits % 64 + 1;
+        pragma(msg, "bigSize = ", bigSize);
     }
 }
 
 @optStrategy("minsize")
-private T decimalToFloatFallback(T)(T ret, scope const size_t[] coeff, int exponent)
+private T algorithmM(T)(scope const size_t[] coefficients, long exponent)
     if ((is(T == float) || is(T == double) || is(T == real)))
-    in (0 <= ret && ret <= T.infinity)
+    in (coefficients.length)
 {
     pragma(inline, false);
 
-    import mir.bignum.fixed: UInt;
+
+    import mir.bitop: ctlz;
+    import mir.bignum.fp: Fp;
     import mir.bignum.integer: BigInt;
-    import mir.math.common: floor;
-    import mir.math.ieee: ldexp, frexp, nextDown, nextUp;
-    import mir.utility: _expect;
+    import mir.math.common: log2, ceil;
+    import mir.math.ieee: ldexp, nextUp;
 
-    BigInt!256 x = void, y = void; // max value is 2^(2^14)-1
+    BigInt!(bigSize!T) u = void;
+    BigInt!(bigSize!T) v = void;
+    BigInt!(bigSize!T) q = void;
+    BigInt!(bigSize!T) r = void;
 
-    if (exponent == 0)
-        return decimalTo!T(coeff);
+    if (coefficients.length == 0)
+        return 0;
 
     // if no overflow
-    if (exponent > 0 && !x.copyFrom(coeff) && !x.mulPow5(exponent))
-        return x.coefficients.decimalTo!T.ldexp(exponent);
+    if (exponent >= 0)
+    {
+        if (3 * exponent + coefficients.length * size_t.sizeof * 8 - ctlz(coefficients[$ - 1]) - 1 > T.max_exp)
+            return T.infinity;
+        if (exponent == 0)
+            return coefficients.binaryTo!T;
+        u.copyFrom(coefficients);
+        u.mulPow5(exponent);
+        return u.coefficients.binaryTo!T(exponent);
+    }
+
+    auto log2_u = coefficients.binaryTo!T.log2;
+    auto log2_v = cast(T)(-LOG2_10) * exponent;
+    sizediff_t k = cast(sizediff_t) ceil(log2_u - log2_v);
 
-    do
+    k -= T.mant_dig;
+
+    if (k < T.min_exp - T.mant_dig)
     {
-        if (ret < ret.min_normal)
+        if (k + T.mant_dig + 1 < T.min_exp - T.mant_dig)
+            return 0;
+        k = T.min_exp - T.mant_dig;
+    }
+    else
+    if (k > T.max_exp)
+    {
+        if (k - 2 > T.max_exp)
+            return T.infinity;
+        k = T.max_exp;
+    }
+
+    if(u.copyFrom(coefficients))
+        return T.nan;
+    if (k < 0)
+    {
+        if (u.ctlz < -k)
+            return T.nan;
+        u <<= -k;
+    }
+
+    if (log2_v >= bigSize!T * 64)
+            return T.nan;
+
+    v = 1;
+    v.mulPow5(-exponent);
+    v <<= cast(int)-exponent + (k > 0 ? k : 0);
+
+    sizediff_t s;
+    for(;;)
+    {
+        u.divMod(v, q, r);
+
+        s = cast(int) q.bitLength - T.mant_dig;
+        assert(k >= T.min_exp - T.mant_dig);
+        if (s == 0)
             break;
-        int k;
-        auto m0 = frexp(ret, k);
-        k -= T.mant_dig;
-        static if (T.mant_dig <= 64)
+
+        if (s < 0)
         {
-            enum p2 = T(2) ^^ T.mant_dig;
-            auto m = UInt!64(cast(ulong) (m0 * p2));
+            if (k == T.min_exp - T.mant_dig)
+                break;
+            k--;
         }
         else
         {
-            enum p2h = T(2) ^^ (T.mant_dig - 64);
-            enum p2l = T(2) ^^ 64;
-            m0 *= p2h;
-            auto mhf = floor(m0);
-            auto mh = cast(ulong) mhf;
-            m0 -= mhf;
-            m0 *= p2l;
-            auto ml = cast(ulong) m0;
-            auto m = UInt!128(mh);
-            m <<= 64;
-            m |= UInt!128(ml);
-        }
-        auto mtz = m.cttz;
-        if (mtz != m.sizeof * 8)
-        {
-            m >>= mtz;
-            k += mtz;
+            if (k == T.max_exp)
+                return T.infinity;
+            k++;
         }
 
-        if (x.copyFrom(coeff)) // if overflow
-            break;
-        y = m;
-        y.mulPow5(-exponent);
-        auto shift = k - exponent;
-        (shift >= 0  ? y : x) <<= shift >= 0 ? shift : -shift;
-        x -= y;
-        if (x.length == 0)
-            break;
-        x <<= 1;
-        x *= m;
-        auto cond = mtz == T.mant_dig - 1 && x.sign;
-        auto cmp = x.view.unsigned.opCmp(y.view.unsigned);
-        if (cmp < 0)
-        {
-            if (!cond)
-                break;
-            x <<= 1;
-            if (x.view.unsigned <= y.view.unsigned)
-                break;
-        }
-        else
-        if (!cmp && !cond && !mtz)
-            break;
-        ret = x.sign ? nextDown(ret) : nextUp(ret);
-        if (ret == 0)
-            break;
+        if ((s < 0 ? u : v).ctlz == 0)
+            return T.nan;
+        (s < 0 ? u : v) <<= 1;
     }
-    while (T.mant_dig >= 64 && exponent < -512);
-    return ret;
+
+    sizediff_t cmp;
+    if (s <= 0)
+    {
+        u = v;
+        u -= r;
+        cmp = r.opCmp(u);
+    }
+    else
+    {
+        cmp = s - 1 - q.view.unsigned.cttz;
+        if (cmp == 0) // half
+            cmp += r != 0;
+        q >>= s;
+        k += s;
+    }
+    auto z = q.coefficients.binaryTo!T.ldexp(cast(int)k);
+    return cmp < 0 || cmp == 0 && !q.view.unsigned.bt(0) ? z : nextUp(z);
 }
 
-auto decimalToFp(uint coefficientSize, uint internalRoundLastBits = 0)
+private T binaryToFloatImpl(T)(scope const size_t[] coefficients, long exponent)
+    @safe
+    if ((is(T == float) || is(T == double) || is(T == real)))
+    in (coefficients.length == 0 || coefficients[$ - 1])
+{
+    version (LDC)
+        pragma(inline, true);
+
+    enum md = T.mant_dig;
+    enum b = size_t.sizeof * 8;
+    enum n = md / b + (md % b != 0);
+    enum s = n * b;
+
+    if (coefficients.length == 0)
+        return 0;
+    
+    if (exponent > T.max_exp)
+        return T.infinity;
+
+    auto fp = coefficients.binaryToFp!(s, s - md);
+    fp.exponent += exponent;
+    return fp.opCast!(T, true, true);
+}
+
+
+package(mir.bignum) auto binaryToFp(uint coefficientSize, uint internalRoundLastBits = 0)
     (scope const(size_t)[] coefficients)
     if (internalRoundLastBits < size_t.sizeof * 8)
     in (coefficients.length)
@@ -364,11 +388,10 @@ auto decimalToFp(uint coefficientSize, uint internalRoundLastBits = 0)
     import mir.utility: _expect;
 
     version (LDC)
-        pragma(inline, internalRoundLastBits != 0);
+        pragma(inline, true);
 
     Fp!coefficientSize ret;
 
-    assert(coefficients.length);
     enum N = ret.coefficient.data.length;
     sizediff_t size = coefficients.length * (size_t.sizeof * 8);
     sizediff_t expShift = size - coefficientSize;
@@ -416,7 +439,7 @@ auto decimalToFp(uint coefficientSize, uint internalRoundLastBits = 0)
                 coefficients = coefficients[1 .. $];
                 assert(coefficients.length);
             }
-            return coefficients.length > (N + 1);
+            return coefficients.length > N + 1;
         }
 
         static if (internalRoundLastBits)
@@ -454,40 +477,57 @@ auto decimalToFp(uint coefficientSize, uint internalRoundLastBits = 0)
     return ret;
 }
 
-private enum dec_S = 9;
-private enum dec_P = 1 << dec_S;
+private enum ExponentM = 512;
 
-private auto load64()(long e)
+private auto _load(T)(long e) @trusted
+    if (T.mant_dig < 64)
+    in (-ExponentM < e && e < ExponentM)
 {
-    version(LDC)
+    version (LDC)
         pragma(inline, true);
 
     import mir.bignum.fixed: UInt;
     import mir.bignum.fp: Fp;
     import mir.bignum.internal.dec2float_table;
 
-    auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0];
-    auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e];
+    auto idx = cast(sizediff_t)e - min_p10_e;
+    auto p10coeff = p10_coefficients_h[idx];
+    auto p10exp = p10_exponents[idx];
     return Fp!64(false, p10exp, UInt!64(p10coeff));
 }
 
-private auto load128()(long e)
+private auto _load(T)(long e) @trusted
+    if (T.mant_dig >= 64)
+    in (-ExponentM < e && e < ExponentM)
 {
-    version(LDC)
+    version (LDC)
         pragma(inline, true);
 
     import mir.bignum.fixed: UInt;
     import mir.bignum.fp: Fp;
     import mir.bignum.internal.dec2float_table;
 
-    static assert(min_p10_e <= -dec_P);
-    static assert(max_p10_e >= dec_P);
+    static assert(min_p10_e <= -ExponentM);
+    static assert(max_p10_e >= ExponentM);
     auto idx = cast(sizediff_t)e - min_p10_e;
-    ulong h = p10_coefficients[idx][0];
-    ulong l = p10_coefficients[idx][1];
+    ulong h = p10_coefficients_h[idx];
+    ulong l = p10_coefficients_l[idx];
     if (l >= cast(ulong)long.min)
         h--;
     auto p10coeff = UInt!128(cast(ulong[2])[l, h]);
     auto p10exp = p10_exponents[idx] - 64;
     return Fp!128(false, p10exp, p10coeff);
 }
+
+unittest
+{
+    import mir.bignum.fp;
+    import mir.bignum.fixed;
+    import mir.test;
+    ulong[2] data = [ulong.max - 2, 1];
+    auto coefficients = cast(size_t[])data[];
+    if (coefficients[$ - 1] == 0)
+        coefficients = coefficients[0 .. $ - 1];
+    coefficients.binaryToFp!64.should == Fp!64(false, 1, UInt!64(0xFFFFFFFFFFFFFFFE));
+    coefficients.binaryToFp!128.should == Fp!128(false, -63, UInt!128([0x8000000000000000, 0xFFFFFFFFFFFFFFFE]));
+}
diff --git a/source/mir/bignum/internal/dec2float_table.d b/source/mir/bignum/internal/dec2float_table.d
index 73691085..96200a23 100644
--- a/source/mir/bignum/internal/dec2float_table.d
+++ b/source/mir/bignum/internal/dec2float_table.d
@@ -7,1036 +7,6 @@ module mir.bignum.internal.dec2float_table;
 enum min_p10_e = -512;
 enum max_p10_e = 512;
 
-// 
-static immutable align(16) ulong[2][1025] p10_coefficients = [
-    [0x9049EE32DB23D21C, 0x7132D332E3F204D5],
-    [0xB45C69BF91ECC6A4, 0x8D7F87FF9CEE860A],
-    [0xE173842F7667F84C, 0x70DF69FF842A278D],
-    [0x8CE8329DAA00FB30, 0xC68BA23FB29A58B8],
-    [0xB0223F45148139FC, 0xB82E8ACF9F40EEE6],
-    [0xDC2ACF1659A1887B, 0xA63A2D8387112A9F],
-    [0x899AC16DF804F54D, 0xA7E45C72346ABAA4],
-    [0xAC0171C9760632A0, 0xD1DD738EC185694D],
-    [0xD701CE3BD387BF48, 0xC654D07271E6C3A0],
-    [0x866120E56434D78D, 0xDBF5024787303A44],
-    [0xA7F9691EBD420D70, 0x12F242D968FC48D5],
-    [0xD1F7C3666C9290CC, 0x17AED38FC33B5B0A],
-    [0x833ADA2003DB9A80, 0x8ECD4439DA0518E6],
-    [0xA40990A804D2811F, 0x7280954850865F20],
-    [0xCD0BF4D206072167, 0x4F20BA9A64A7F6E8],
-    [0x8027790343C474E1, 0x917474A07EE8FA51],
-    [0xA031574414B59219, 0xB5D191C89EA338E5],
-    [0xC83DAD1519E2F69F, 0xE345F63AC64C071E],
-    [0xFA4D185A605BB447, 0x9C1773C977DF08E6],
-    [0x9C702F387C3950AC, 0x218EA85DEAEB6590],
-    [0xC38C3B069B47A4D7, 0x29F2527565A63EF4],
-    [0xF46F49C842198E0D, 0xF46EE712BF0FCEB0],
-    [0x98C58E1D294FF8C8, 0x18C5506BB769E12E],
-    [0xBEF6F1A473A3F6FA, 0x1EF6A486A544597A],
-    [0xEEB4AE0D908CF4B9, 0xA6B44DA84E956FD8],
-    [0x9530ECC87A5818F3, 0x6830B089311D65E7],
-    [0xBA7D27FA98EE1F30, 0x423CDCAB7D64BF61],
-    [0xE91C71F93F29A6FC, 0x52CC13D65CBDEF39],
-    [0x91B1C73BC77A085E, 0xB3BF8C65F9F6B584],
-    [0xB61E390AB9588A75, 0x20AF6F7F787462E5],
-    [0xE3A5C74D67AEAD12, 0x68DB4B5F56917B9E],
-    [0x8E479C9060CD2C2C, 0x81890F1B961AED43],
-    [0xB1D983B479007736, 0x61EB52E27BA1A893],
-    [0xDE4FE4A197409504, 0xFA66279B1A8A12B8],
-    [0x8AF1EEE4FE885D22, 0x7C7FD8C0F0964BB3],
-    [0xADAE6A9E3E2A746B, 0x1B9FCEF12CBBDEA0],
-    [0xD91A0545CDB51186, 0xE287C2AD77EAD648],
-    [0x87B0434BA0912AF4, 0xAD94D9AC6AF2C5ED],
-    [0xA99C541E88B575B1, 0x98FA101785AF7768],
-    [0xD40369262AE2D31D, 0xBF38941D671B5542],
-    [0x848221B7DACDC3F2, 0xF7835C9260711549],
-    [0xA5A2AA25D18134EE, 0x756433B6F88D5A9C],
-    [0xCF0B54AF45E1822A, 0x12BD40A4B6B0B143],
-    [0x816714ED8BACF15A, 0x4BB64866F22E6ECA],
-    [0xA1C0DA28EE982DB1, 0xDEA3DA80AEBA0A7C],
-    [0xCA3110B32A3E391D, 0x164CD120DA688D1B],
-    [0xFCBD54DFF4CDC764, 0x5BE005691102B062],
-    [0x9DF6550BF9009C9F, 0xB96C0361AAA1AE3D],
-    [0xC573EA4EF740C3C6, 0x67C7043A154A19CC],
-    [0xF6D0E4E2B510F4B8, 0x01B8C5489A9CA040],
-    [0x9A428F0DB12A98F3, 0x01137B4D60A1E428],
-    [0xC0D332D11D753F30, 0xC1585A20B8CA5D32],
-    [0xF107FF8564D28EFC, 0xB1AE70A8E6FCF47E],
-    [0x96A4FFB35F03995D, 0x4F0D0669905E18CF],
-    [0xBC4E3FA036C47FB5, 0xA2D04803F4759F02],
-    [0xEB61CF8844759FA2, 0xCB845A04F19306C3],
-    [0x931D21B52AC983C5, 0x1F32B84316FBE43A],
-    [0xB7E46A22757BE4B6, 0x66FF6653DCBADD48],
-    [0xE5DD84AB12DADDE4, 0x00BF3FE8D3E9949A],
-    [0x8FAA72EAEBC8CAAF, 0x807787F18471FCE1],
-    [0xB3950FA5A6BAFD5A, 0x209569EDE58E7C19],
-    [0xE07A538F1069BCB1, 0xA8BAC4695EF21B1F],
-    [0x8C4C74396A4215EE, 0x6974BAC1DB5750F3],
-    [0xAF5F9147C4D29B6A, 0x03D1E972522D2530],
-    [0xDB377599B6074245, 0x84C663CEE6B86E7C],
-    [0x8902A98011C4896B, 0xD2FBFE615033450E],
-    [0xAB4353E01635ABC6, 0x87BAFDF9A4401651],
-    [0xD61428D81BC316B7, 0xE9A9BD780D501BE5],
-    [0x85CC99871159EE32, 0x520A166B0852116F],
-    [0xA73FBFE8D5B069BF, 0xE68C9C05CA6695CB],
-    [0xD10FAFE30B1C842F, 0xA02FC3073D003B3E],
-    [0x82A9CDEDE6F1D29D, 0x241DD9E486202507],
-    [0xA354416960AE4744, 0x6D25505DA7A82E48],
-    [0xCC2951C3B8D9D916, 0x886EA475119239DA],
-    [0xFF33A634A7104F5B, 0xEA8A4D9255F6C851],
-    [0x9F8047E0E86A3199, 0xD296707B75BA3D33],
-    [0xC76059D92284BDFF, 0x073C0C9A5328CC7F],
-    [0xF938704F6B25ED7F, 0xC90B0FC0E7F2FF9F],
-    [0x9BC34631A2F7B46F, 0x3DA6E9D890F7DFC3],
-    [0xC2B417BE0BB5A18B, 0x0D10A44EB535D7B4],
-    [0xF3611DAD8EA309EE, 0xD054CD6262834DA1],
-    [0x981CB28C7925E635, 0xA235005D7D921085],
-    [0xBE23DF2F976F5FC2, 0xCAC24074DCF694A6],
-    [0xEDACD6FB7D4B37B2, 0x3D72D092143439D0],
-    [0x948C065D2E4F02CF, 0x6667C25B4CA0A422],
-    [0xB9AF07F479E2C383, 0x4001B2F21FC8CD2A],
-    [0xE81AC9F1985B7464, 0x10021FAEA7BB0075],
-    [0x9110BE36FF3928BF, 0x8A0153CD28D4E049],
-    [0xB554EDC4BF0772EE, 0x2C81A8C0730A185B],
-    [0xE2AA2935EEC94FAA, 0xB7A212F08FCC9E72],
-    [0x8DAA59C1B53DD1CA, 0x12C54BD659DFE307],
-    [0xB114F032228D463D, 0x97769ECBF057DBC9],
-    [0xDD5A2C3EAB3097CC, 0xBD54467EEC6DD2BB],
-    [0x8A585BA72AFE5EDF, 0x5654AC0F53C4A3B5],
-    [0xACEE7290F5BDF697, 0x2BE9D71328B5CCA2],
-    [0xD82A0F35332D743D, 0xF6E44CD7F2E33FCB],
-    [0x871A49813FFC68A6, 0x1A4EB006F7CE07DF],
-    [0xA8E0DBE18FFB82D0, 0xA0E25C08B5C189D7],
-    [0xD31912D9F3FA6384, 0x891AF30AE331EC4C],
-    [0x83EFABC8387C7E32, 0x35B0D7E6CDFF33B0],
-    [0xA4EB96BA469B9DBF, 0xC31D0DE0817F009C],
-    [0xCE267C68D842852E, 0x73E45158A1DEC0C2],
-    [0x80D80DC18729933D, 0x086EB2D7652B387A],
-    [0xA10E1131E8F3F80C, 0x4A8A5F8D3E760698],
-    [0xC951957E6330F60F, 0x5D2CF7708E13883E],
-    [0xFBA5FADDFBFD3393, 0x3478354CB1986A4D],
-    [0x9D47BCCABD7E403C, 0x00CB214FEEFF4270],
-    [0xC499ABFD6CDDD04B, 0x00FDE9A3EABF130C],
-    [0xF5C016FCC815445E, 0xC13D640CE56ED7D0],
-    [0x99980E5DFD0D4ABB, 0x98C65E880F6546E2],
-    [0xBFFE11F57C509D69, 0x3EF7F62A133E989A],
-    [0xEFFD9672DB64C4C4, 0x8EB5F3B4980E3EC1],
-    [0x95FE7E07C91EFAFA, 0x3931B850DF08E738],
-    [0xBB7E1D89BB66B9B9, 0xC77E266516CB2106],
-    [0xEA5DA4EC2A406827, 0xF95DAFFE5C7DE948],
-    [0x927A87139A684118, 0x5BDA8DFEF9CEB1CD],
-    [0xB71928D88102515E, 0x72D1317EB8425E40],
-    [0xE4DF730EA142E5B6, 0x0F857DDE6652F5D0],
-    [0x8F0BA7E924C9CF92, 0xC9B36EAAFFF3D9A2],
-    [0xB2CE91E36DFC4376, 0x3C204A55BFF0D00B],
-    [0xDF82365C497B5454, 0xCB285CEB2FED040E],
-    [0x8BB161F9ADED14B4, 0x5EF93A12FDF42288],
-    [0xAE9DBA78196859E1, 0x76B78897BD712B2B],
-    [0xDA4529161FC2705A, 0xD4656ABDACCD75F5],
-    [0x886B39ADD3D98638, 0x24BF62B68C0069B9],
-    [0xAA86081948CFE7C6, 0x2DEF3B642F008428],
-    [0xD5278A1F9B03E1B8, 0xB96B0A3D3AC0A531],
-    [0x8538B653C0E26D13, 0xD3E2E66644B8673F],
-    [0xA686E3E8B11B0858, 0x88DB9FFFD5E6810F],
-    [0xD0289CE2DD61CA6D, 0x6B1287FFCB602152],
-    [0x8219620DCA5D1E84, 0x62EB94FFDF1C14D3],
-    [0xA29FBA913CF46625, 0x7BA67A3FD6E31A08],
-    [0xCB47A9358C317FAF, 0xDA9018CFCC9BE08A],
-    [0xFE199382EF3DDF9B, 0x91341F03BFC2D8AD],
-    [0x9ECFFC31D586ABC1, 0x9AC0936257D9C76C],
-    [0xC683FB3E4AE856B1, 0xC170B83AEDD03947],
-    [0xF824FA0DDDA26C5D, 0xF1CCE649A9444799],
-    [0x9B171C48AA8583BA, 0x17200FEE09CAACC0],
-    [0xC1DCE35AD526E4A9, 0x9CE813E98C3D57EF],
-    [0xF2541C318A709DD3, 0xC42218E3EF4CADEB],
-    [0x9774919EF68662A4, 0xBA954F8E758FECB3],
-    [0xBD51B606B427FB4D, 0xA93AA37212F3E7E0],
-    [0xECA623886131FA20, 0xD3894C4E97B0E1D8],
-    [0x93E7D6353CBF3C54, 0xE435CFB11ECE8D27],
-    [0xB8E1CBC28BEF0B69, 0xDD43439D66823071],
-    [0xE71A3EB32EEACE43, 0x14941484C022BC8D],
-    [0x9070672FFD52C0EA, 0xECDC8CD2F815B5D8],
-    [0xB48C80FBFCA77124, 0x6813B007B61B234E],
-    [0xE1AFA13AFBD14D6E, 0x82189C09A3A1EC21],
-    [0x8D0DC4C4DD62D064, 0x714F618606453395],
-    [0xB05135F614BB847E, 0x8DA339E787D6807A],
-    [0xDC65837399EA659D, 0xF10C086169CC2099],
-    [0x89BF722840327F82, 0x16A7853CE21F945F],
-    [0xAC2F4EB2503F1F63, 0x9C51668C1AA77977],
-    [0xD73B225EE44EE73B, 0x4365C02F215157D5],
-    [0x8684F57B4EB15085, 0x0A1F981D74D2D6E5],
-    [0xA82632DA225DA4A6, 0x4CA77E24D2078C9E],
-    [0xD22FBF90AAF50DD0, 0xDFD15DAE06896FC6],
-    [0x835DD7BA6AD928A2, 0xEBE2DA8CC415E5DC],
-    [0xA4354DA9058F72CA, 0x66DB912FF51B5F53],
-    [0xCD42A11346F34F7D, 0x0092757BF2623727],
-    [0x8049A4AC0C5811AE, 0x205B896D777D6279],
-    [0xA05C0DD70F6E161A, 0xA8726BC8D55CBB17],
-    [0xC873114CD3499BA0, 0x128F06BB0AB3E9DD],
-    [0xFA8FD5A0081C0288, 0x1732C869CD60E454],
-    [0x9C99E58405118195, 0x0E7FBD42205C8EB4],
-    [0xC3C05EE50655E1FA, 0x521FAC92A873B261],
-    [0xF4B0769E47EB5A79, 0xE6A797B752909EFA],
-    [0x98EE4A22ECF3188C, 0x9028BED2939A635C],
-    [0xBF29DCABA82FDEAE, 0x7432EE873880FC33],
-    [0xEEF453D6923BD65A, 0x113FAA2906A13B40],
-    [0x9558B4661B6565F8, 0x4AC7CA59A424C508],
-    [0xBAAEE17FA23EBF76, 0x5D79BCF00D2DF64A],
-    [0xE95A99DF8ACE6F54, 0xF4D82C2C107973DC],
-    [0x91D8A02BB6C10594, 0x79071B9B8A4BE86A],
-    [0xB64EC836A47146FA, 0x9748E2826CDEE284],
-    [0xE3E27A444D8D98B8, 0xFD1B1B2308169B25],
-    [0x8E6D8C6AB0787F73, 0xFE30F0F5E50E20F7],
-    [0xB208EF855C969F50, 0xBDBD2D335E51A935],
-    [0xDE8B2B66B3BC4724, 0xAD2C788035E61382],
-    [0x8B16FB203055AC76, 0x4C3BCB5021AFCC31],
-    [0xADDCB9E83C6B1794, 0xDF4ABE242A1BBF3E],
-    [0xD953E8624B85DD79, 0xD71D6DAD34A2AF0D],
-    [0x87D4713D6F33AA6C, 0x8672648C40E5AD68],
-    [0xA9C98D8CCB009506, 0x680EFDAF511F18C2],
-    [0xD43BF0EFFDC0BA48, 0x0212BD1B2566DEF3],
-    [0x84A57695FE98746D, 0x014BB630F7604B58],
-    [0xA5CED43B7E3E9188, 0x419EA3BD35385E2E],
-    [0xCF42894A5DCE35EA, 0x52064CAC828675B9],
-    [0x818995CE7AA0E1B2, 0x7343EFEBD1940994],
-    [0xA1EBFB4219491A1F, 0x1014EBE6C5F90BF9],
-    [0xCA66FA129F9B60A7, 0xD41A26E077774EF7],
-    [0xFD00B897478238D1, 0x8920B098955522B5],
-    [0x9E20735E8CB16382, 0x55B46E5F5D5535B1],
-    [0xC5A890362FDDBC63, 0xEB2189F734AA831D],
-    [0xF712B443BBD52B7C, 0xA5E9EC7501D523E4],
-    [0x9A6BB0AA55653B2D, 0x47B233C92125366F],
-    [0xC1069CD4EABE89F9, 0x999EC0BB696E840A],
-    [0xF148440A256E2C77, 0xC00670EA43CA250D],
-    [0x96CD2A865764DBCA, 0x380406926A5E5728],
-    [0xBC807527ED3E12BD, 0xC605083704F5ECF2],
-    [0xEBA09271E88D976C, 0xF7864A44C633682F],
-    [0x93445B8731587EA3, 0x7AB3EE6AFBE0211D],
-    [0xB8157268FDAE9E4C, 0x5960EA05BAD82965],
-    [0xE61ACF033D1A45DF, 0x6FB92487298E33BE],
-    [0x8FD0C16206306BAC, 0xA5D3B6D479F8E057],
-    [0xB3C4F1BA87BC8697, 0x8F48A4899877186C],
-    [0xE0B62E2929ABA83C, 0x331ACDABFE94DE87],
-    [0x8C71DCD9BA0B4926, 0x9FF0C08B7F1D0B15],
-    [0xAF8E5410288E1B6F, 0x07ECF0AE5EE44DDA],
-    [0xDB71E91432B1A24B, 0xC9E82CD9F69D6150],
-    [0x892731AC9FAF056F, 0xBE311C083A225CD2],
-    [0xAB70FE17C79AC6CA, 0x6DBD630A48AAF407],
-    [0xD64D3D9DB981787D, 0x092CBBCCDAD5B108],
-    [0x85F0468293F0EB4E, 0x25BBF56008C58EA5],
-    [0xA76C582338ED2622, 0xAF2AF2B80AF6F24E],
-    [0xD1476E2C07286FAA, 0x1AF5AF660DB4AEE2],
-    [0x82CCA4DB847945CA, 0x50D98D9FC890ED4D],
-    [0xA37FCE126597973D, 0xE50FF107BAB528A1],
-    [0xCC5FC196FEFD7D0C, 0x1E53ED49A96272C9],
-    [0xFF77B1FCBEBCDC4F, 0x25E8E89C13BB0F7B],
-    [0x9FAACF3DF73609B1, 0x77B191618C54E9AD],
-    [0xC795830D75038C1E, 0xD59DF5B9EF6A2418],
-    [0xF97AE3D0D2446F25, 0x4B0573286B44AD1E],
-    [0x9BECCE62836AC577, 0x4EE367F9430AEC33],
-    [0xC2E801FB244576D5, 0x229C41F793CDA73F],
-    [0xF3A20279ED56D48A, 0x6B43527578C1110F],
-    [0x9845418C345644D7, 0x830A13896B78AAAA],
-    [0xBE5691EF416BD60C, 0x23CC986BC656D554],
-    [0xEDEC366B11C6CB8F, 0x2CBFBE86B7EC8AA9],
-    [0x94B3A202EB1C3F39, 0x7BF7D71432F3D6AA],
-    [0xB9E08A83A5E34F08, 0xDAF5CCD93FB0CC54],
-    [0xE858AD248F5C22CA, 0xD1B3400F8F9CFF69],
-    [0x91376C36D99995BE, 0x23100809B9C21FA2],
-    [0xB58547448FFFFB2E, 0xABD40A0C2832A78A],
-    [0xE2E69915B3FFF9F9, 0x16C90C8F323F516D],
-    [0x8DD01FAD907FFC3C, 0xAE3DA7D97F6792E4],
-    [0xB1442798F49FFB4B, 0x99CD11CFDF41779D],
-    [0xDD95317F31C7FA1D, 0x40405643D711D584],
-    [0x8A7D3EEF7F1CFC52, 0x482835EA666B2572],
-    [0xAD1C8EAB5EE43B67, 0xDA3243650005EECF],
-    [0xD863B256369D4A41, 0x90BED43E40076A83],
-    [0x873E4F75E2224E68, 0x5A7744A6E804A292],
-    [0xA90DE3535AAAE202, 0x711515D0A205CB36],
-    [0xD3515C2831559A83, 0x0D5A5B44CA873E04],
-    [0x8412D9991ED58092, 0xE858790AFE9486C2],
-    [0xA5178FFF668AE0B6, 0x626E974DBE39A873],
-    [0xCE5D73FF402D98E4, 0xFB0A3D212DC81290],
-    [0x80FA687F881C7F8E, 0x7CE66634BC9D0B9A],
-    [0xA139029F6A239F72, 0x1C1FFFC1EBC44E80],
-    [0xC987434744AC874F, 0xA327FFB266B56220],
-    [0xFBE9141915D7A922, 0x4BF1FF9F0062BAA8],
-    [0x9D71AC8FADA6C9B5, 0x6F773FC3603DB4A9],
-    [0xC4CE17B399107C23, 0xCB550FB4384D21D4],
-    [0xF6019DA07F549B2B, 0x7E2A53A146606A48],
-    [0x99C102844F94E0FB, 0x2EDA7444CBFC426D],
-    [0xC0314325637A193A, 0xFA911155FEFB5309],
-    [0xF03D93EEBC589F88, 0x793555AB7EBA27CB],
-    [0x96267C7535B763B5, 0x4BC1558B2F3458DF],
-    [0xBBB01B9283253CA3, 0x9EB1AAEDFB016F16],
-    [0xEA9C227723EE8BCB, 0x465E15A979C1CADC],
-    [0x92A1958A7675175F, 0x0BFACD89EC191ECA],
-    [0xB749FAED14125D37, 0xCEF980EC671F667C],
-    [0xE51C79A85916F485, 0x82B7E12780E7401B],
-    [0x8F31CC0937AE58D3, 0xD1B2ECB8B0908811],
-    [0xB2FE3F0B8599EF08, 0x861FA7E6DCB4AA15],
-    [0xDFBDCECE67006AC9, 0x67A791E093E1D49A],
-    [0x8BD6A141006042BE, 0xE0C8BB2C5C6D24E0],
-    [0xAECC49914078536D, 0x58FAE9F773886E19],
-    [0xDA7F5BF590966849, 0xAF39A475506A899F],
-    [0x888F99797A5E012D, 0x6D8406C952429603],
-    [0xAAB37FD7D8F58179, 0xC8E5087BA6D33B84],
-    [0xD5605FCDCF32E1D7, 0xFB1E4A9A90880A65],
-    [0x855C3BE0A17FCD26, 0x5CF2EEA09A55067F],
-    [0xA6B34AD8C9DFC070, 0xF42FAA48C0EA481F],
-    [0xD0601D8EFC57B08C, 0xF13B94DAF124DA27],
-    [0x823C12795DB6CE57, 0x76C53D08D6B70858],
-    [0xA2CB1717B52481ED, 0x54768C4B0C64CA6E],
-    [0xCB7DDCDDA26DA269, 0xA9942F5DCF7DFD0A],
-    [0xFE5D54150B090B03, 0xD3F93B35435D7C4C],
-    [0x9EFA548D26E5A6E2, 0xC47BC5014A1A6DB0],
-    [0xC6B8E9B0709F109A, 0x359AB6419CA1091B],
-    [0xF867241C8CC6D4C1, 0xC30163D203C94B62],
-    [0x9B407691D7FC44F8, 0x79E0DE63425DCF1D],
-    [0xC21094364DFB5637, 0x985915FC12F542E5],
-    [0xF294B943E17A2BC4, 0x3E6F5B7B17B2939E],
-    [0x979CF3CA6CEC5B5B, 0xA705992CEECF9C43],
-    [0xBD8430BD08277231, 0x50C6FF782A838353],
-    [0xECE53CEC4A314EBE, 0xA4F8BF5635246428],
-    [0x940F4613AE5ED137, 0x871B7795E136BE99],
-    [0xB913179899F68584, 0x28E2557B59846E3F],
-    [0xE757DD7EC07426E5, 0x331AEADA2FE589CF],
-    [0x9096EA6F3848984F, 0x3FF0D2C85DEF7622],
-    [0xB4BCA50B065ABE63, 0x0FED077A756B53AA],
-    [0xE1EBCE4DC7F16DFC, 0xD3E8495912C62894],
-    [0x8D3360F09CF6E4BD, 0x64712DD7ABBBD95D],
-    [0xB080392CC4349DED, 0xBD8D794D96AACFB4],
-    [0xDCA04777F541C568, 0xECF0D7A0FC5583A1],
-    [0x89E42CAAF9491B61, 0xF41686C49DB57245],
-    [0xAC5D37D5B79B6239, 0x311C2875C522CED6],
-    [0xD77485CB25823AC7, 0x7D633293366B828B],
-    [0x86A8D39EF77164BD, 0xAE5DFF9C02033197],
-    [0xA8530886B54DBDEC, 0xD9F57F830283FDFD],
-    [0xD267CAA862A12D67, 0xD072DF63C324FD7C],
-    [0x8380DEA93DA4BC60, 0x4247CB9E59F71E6D],
-    [0xA46116538D0DEB78, 0x52D9BE85F074E609],
-    [0xCD795BE870516656, 0x67902E276C921F8B],
-    [0x806BD9714632DFF6, 0x00BA1CD8A3DB53B7],
-    [0xA086CFCD97BF97F4, 0x80E8A40ECCD228A5],
-    [0xC8A883C0FDAF7DF0, 0x6122CD128006B2CE],
-    [0xFAD2A4B13D1B5D6C, 0x796B805720085F81],
-    [0x9CC3A6EEC6311A64, 0xCBE3303674053BB1],
-    [0xC3F490AA77BD60FD, 0xBEDBFC4411068A9D],
-    [0xF4F1B4D515ACB93C, 0xEE92FB5515482D44],
-    [0x991711052D8BF3C5, 0x751BDD152D4D1C4B],
-    [0xBF5CD54678EEF0B7, 0xD262D45A78A0635D],
-    [0xEF340A98172AACE5, 0x86FB897116C87C35],
-    [0x9580869F0E7AAC0F, 0xD45D35E6AE3D4DA1],
-    [0xBAE0A846D2195713, 0x8974836059CCA109],
-    [0xE998D258869FACD7, 0x2BD1A438703FC94B],
-    [0x91FF83775423CC06, 0x7B6306A34627DDCF],
-    [0xB67F6455292CBF08, 0x1A3BC84C17B1D543],
-    [0xE41F3D6A7377EECA, 0x20CABA5F1D9E4A94],
-    [0x8E938662882AF53E, 0x547EB47B7282EE9C],
-    [0xB23867FB2A35B28E, 0xE99E619A4F23AA43],
-    [0xDEC681F9F4C31F31, 0x6405FA00E2EC94D4],
-    [0x8B3C113C38F9F37F, 0xDE83BC408DD3DD05],
-    [0xAE0B158B4738705F, 0x9624AB50B148D446],
-    [0xD98DDAEE19068C76, 0x3BADD624DD9B0957],
-    [0x87F8A8D4CFA417CA, 0xE54CA5D70A80E5D6],
-    [0xA9F6D30A038D1DBC, 0x5E9FCF4CCD211F4C],
-    [0xD47487CC8470652B, 0x7647C3200069671F],
-    [0x84C8D4DFD2C63F3B, 0x29ECD9F40041E073],
-    [0xA5FB0A17C777CF0A, 0xF468107100525890],
-    [0xCF79CC9DB955C2CC, 0x7182148D4066EEB4],
-    [0x81AC1FE293D599C0, 0xC6F14CD848405531],
-    [0xA21727DB38CB0030, 0xB8ADA00E5A506A7D],
-    [0xCA9CF1D206FDC03C, 0xA6D90811F0E4851C],
-    [0xFD442E4688BD304B, 0x908F4A166D1DA663],
-    [0x9E4A9CEC15763E2F, 0x9A598E4E043287FE],
-    [0xC5DD44271AD3CDBA, 0x40EFF1E1853F29FE],
-    [0xF7549530E188C129, 0xD12BEE59E68EF47D],
-    [0x9A94DD3E8CF578BA, 0x82BB74F8301958CE],
-    [0xC13A148E3032D6E8, 0xE36A52363C1FAF02],
-    [0xF18899B1BC3F8CA2, 0xDC44E6C3CB279AC2],
-    [0x96F5600F15A7B7E5, 0x29AB103A5EF8C0B9],
-    [0xBCB2B812DB11A5DE, 0x7415D448F6B6F0E8],
-    [0xEBDF661791D60F56, 0x111B495B3464AD21],
-    [0x936B9FCEBB25C996, 0xCAB10DD900BEEC35],
-    [0xB84687C269EF3BFB, 0x3D5D514F40EEA742],
-    [0xE65829B3046B0AFA, 0x0CB4A5A3112A5113],
-    [0x8FF71A0FE2C2E6DC, 0x47F0E785EABA72AC],
-    [0xB3F4E093DB73A093, 0x59ED216765690F57],
-    [0xE0F218B8D25088B8, 0x306869C13EC3532C],
-    [0x8C974F7383725573, 0x1E414218C73A13FC],
-    [0xAFBD2350644EEAD0, 0xE5D1929EF90898FB],
-    [0xDBAC6C247D62A584, 0xDF45F746B74ABF39],
-    [0x894BC396CE5DA772, 0x6B8BBA8C328EB784],
-    [0xAB9EB47C81F5114F, 0x066EA92F3F326565],
-    [0xD686619BA27255A3, 0xC80A537B0EFEFEBE],
-    [0x8613FD0145877586, 0xBD06742CE95F5F37],
-    [0xA798FC4196E952E7, 0x2C48113823B73704],
-    [0xD17F3B51FCA3A7A1, 0xF75A15862CA504C5],
-    [0x82EF85133DE648C5, 0x9A984D73DBE722FB],
-    [0xA3AB66580D5FDAF6, 0xC13E60D0D2E0EBBA],
-    [0xCC963FEE10B7D1B3, 0x318DF905079926A9],
-    [0xFFBBCFE994E5C620, 0xFDF17746497F7053],
-    [0x9FD561F1FD0F9BD4, 0xFEB6EA8BEDEFA634],
-    [0xC7CABA6E7C5382C9, 0xFE64A52EE96B8FC1],
-    [0xF9BD690A1B68637B, 0x3DFDCE7AA3C673B1],
-    [0x9C1661A651213E2D, 0x06BEA10CA65C084F],
-    [0xC31BFA0FE5698DB8, 0x486E494FCFF30A62],
-    [0xF3E2F893DEC3F126, 0x5A89DBA3C3EFCCFB],
-    [0x986DDB5C6B3A76B8, 0xF89629465A75E01D],
-    [0xBE89523386091466, 0xF6BBB397F1135824],
-    [0xEE2BA6C0678B597F, 0x746AA07DED582E2D],
-    [0x94DB483840B717F0, 0xA8C2A44EB4571CDC],
-    [0xBA121A4650E4DDEC, 0x92F34D62616CE413],
-    [0xE896A0D7E51E1566, 0x77B020BAF9C81D18],
-    [0x915E2486EF32CD60, 0x0ACE1474DC1D122F],
-    [0xB5B5ADA8AAFF80B8, 0x0D819992132456BB],
-    [0xE3231912D5BF60E6, 0x10E1FFF697ED6C69],
-    [0x8DF5EFABC5979C90, 0xCA8D3FFA1EF463C2],
-    [0xB1736B96B6FD83B4, 0xBD308FF8A6B17CB2],
-    [0xDDD0467C64BCE4A1, 0xAC7CB3F6D05DDBDF],
-    [0x8AA22C0DBEF60EE4, 0x6BCDF07A423AA96B],
-    [0xAD4AB7112EB3929E, 0x86C16C98D2C953C6],
-    [0xD89D64D57A607745, 0xE871C7BF077BA8B8],
-    [0x87625F056C7C4A8B, 0x11471CD764AD4973],
-    [0xA93AF6C6C79B5D2E, 0xD598E40D3DD89BCF],
-    [0xD389B47879823479, 0x4AFF1D108D4EC2C3],
-    [0x843610CB4BF160CC, 0xCEDF722A585139BA],
-    [0xA54394FE1EEDB8FF, 0xC2974EB4EE658829],
-    [0xCE947A3DA6A9273E, 0x733D226229FEEA33],
-    [0x811CCC668829B887, 0x0806357D5A3F5260],
-    [0xA163FF802A3426A9, 0xCA07C2DCB0CF26F8],
-    [0xC9BCFF6034C13053, 0xFC89B393DD02F0B6],
-    [0xFC2C3F3841F17C68, 0xBBAC2078D443ACE3],
-    [0x9D9BA7832936EDC1, 0xD54B944B84AA4C0E],
-    [0xC5029163F384A931, 0x0A9E795E65D4DF11],
-    [0xF64335BCF065D37D, 0x4D4617B5FF4A16D6],
-    [0x99EA0196163FA42E, 0x504BCED1BF8E4E46],
-    [0xC06481FB9BCF8D3A, 0xE45EC2862F71E1D7],
-    [0xF07DA27A82C37088, 0x5D767327BB4E5A4D],
-    [0x964E858C91BA2655, 0x3A6A07F8D510F870],
-    [0xBBE226EFB628AFEB, 0x890489F70A55368C],
-    [0xEADAB0ABA3B2DBE5, 0x2B45AC74CCEA842F],
-    [0x92C8AE6B464FC96F, 0x3B0B8BC90012929D],
-    [0xB77ADA0617E3BBCB, 0x09CE6EBB40173745],
-    [0xE55990879DDCAABE, 0xCC420A6A101D0516],
-    [0x8F57FA54C2A9EAB7, 0x9FA946824A12232E],
-    [0xB32DF8E9F3546564, 0x47939822DC96ABF9],
-    [0xDFF9772470297EBD, 0x59787E2B93BC56F7],
-    [0x8BFBEA76C619EF36, 0x57EB4EDB3C55B65B],
-    [0xAEFAE51477A06B04, 0xEDE622920B6B23F1],
-    [0xDAB99E59958885C5, 0xE95FAB368E45ECED],
-    [0x88B402F7FD75539B, 0x11DBCB0218EBB414],
-    [0xAAE103B5FCD2A882, 0xD652BDC29F26A11A],
-    [0xD59944A37C0752A2, 0x4BE76D3346F04960],
-    [0x857FCAE62D8493A5, 0x6F70A4400C562DDC],
-    [0xA6DFBD9FB8E5B88F, 0xCB4CCD500F6BB953],
-    [0xD097AD07A71F26B2, 0x7E2000A41346A7A8],
-    [0x825ECC24C8737830, 0x8ED400668C0C28C9],
-    [0xA2F67F2DFA90563B, 0x728900802F0F32FB],
-    [0xCBB41EF979346BCA, 0x4F2B40A03AD2FFBA],
-    [0xFEA126B7D78186BD, 0xE2F610C84987BFA8],
-    [0x9F24B832E6B0F436, 0x0DD9CA7D2DF4D7C9],
-    [0xC6EDE63FA05D3144, 0x91503D1C79720DBB],
-    [0xF8A95FCF88747D94, 0x75A44C6397CE912A],
-    [0x9B69DBE1B548CE7D, 0xC986AFBE3EE11ABA],
-    [0xC24452DA229B021C, 0xFBE85BADCE996169],
-    [0xF2D56790AB41C2A3, 0xFAE27299423FB9C3],
-    [0x97C560BA6B0919A6, 0xDCCD879FC967D41A],
-    [0xBDB6B8E905CB600F, 0x5400E987BBC1C921],
-    [0xED246723473E3813, 0x290123E9AAB23B69],
-    [0x9436C0760C86E30C, 0xF9A0B6720AAF6521],
-    [0xB94470938FA89BCF, 0xF808E40E8D5B3E6A],
-    [0xE7958CB87392C2C3, 0xB60B1D1230B20E04],
-    [0x90BD77F3483BB9BA, 0xB1C6F22B5E6F48C3],
-    [0xB4ECD5F01A4AA828, 0x1E38AEB6360B1AF3],
-    [0xE2280B6C20DD5232, 0x25C6DA63C38DE1B0],
-    [0x8D590723948A535F, 0x579C487E5A38AD0E],
-    [0xB0AF48EC79ACE837, 0x2D835A9DF0C6D852],
-    [0xDCDB1B2798182245, 0xF8E431456CF88E66],
-    [0x8A08F0F8BF0F156B, 0x1B8E9ECB641B5900],
-    [0xAC8B2D36EED2DAC6, 0xE272467E3D222F40],
-    [0xD7ADF884AA879177, 0x5B0ED81DCC6ABB10],
-    [0x86CCBB52EA94BAEB, 0x98E947129FC2B4EA],
-    [0xA87FEA27A539E9A5, 0x3F2398D747B36224],
-    [0xD29FE4B18E88640F, 0x8EEC7F0D19A03AAD],
-    [0x83A3EEEEF9153E89, 0x1953CF68300424AC],
-    [0xA48CEAAAB75A8E2B, 0x5FA8C3423C052DD7],
-    [0xCDB02555653131B6, 0x3792F412CB06794D],
-    [0x808E17555F3EBF12, 0xE2BBD88BBEE40BD0],
-    [0xA0B19D2AB70E6ED6, 0x5B6ACEAEAE9D0EC4],
-    [0xC8DE047564D20A8C, 0xF245825A5A445275],
-    [0xFB158592BE068D2F, 0xEED6E2F0F0D56713],
-    [0x9CED737BB6C4183D, 0x55464DD69685606C],
-    [0xC428D05AA4751E4D, 0xAA97E14C3C26B887],
-    [0xF53304714D9265E0, 0xD53DD99F4B3066A8],
-    [0x993FE2C6D07B7FAC, 0xE546A8038EFE4029],
-    [0xBF8FDB78849A5F97, 0xDE98520472BDD033],
-    [0xEF73D256A5C0F77D, 0x963E66858F6D4440],
-    [0x95A8637627989AAE, 0xDDE7001379A44AA8],
-    [0xBB127C53B17EC159, 0x5560C018580D5D52],
-    [0xE9D71B689DDE71B0, 0xAAB8F01E6E10B4A7],
-    [0x9226712162AB070E, 0xCAB3961304CA70E8],
-    [0xB6B00D69BB55C8D1, 0x3D607B97C5FD0D22],
-    [0xE45C10C42A2B3B06, 0x8CB89A7DB77C506B],
-    [0x8EB98A7A9A5B04E3, 0x77F3608E92ADB243],
-    [0xB267ED1940F1C61C, 0x55F038B237591ED3],
-    [0xDF01E85F912E37A3, 0x6B6C46DEC52F6688],
-    [0x8B61313BBABCE2C6, 0x2323AC4B3B3DA015],
-    [0xAE397D8AA96C1B78, 0xABEC975E0A0D081B],
-    [0xD9C7DCED53C72256, 0x96E7BD358C904A21],
-    [0x881CEA14545C7575, 0x7E50D64177DA2E55],
-    [0xAA242499697392D3, 0xDDE50BD1D5D0B9EA],
-    [0xD4AD2DBFC3D07788, 0x955E4EC64B44E864],
-    [0x84EC3C97DA624AB5, 0xBD5AF13BEF0B113F],
-    [0xA6274BBDD0FADD62, 0xECB1AD8AEACDD58E],
-    [0xCFB11EAD453994BA, 0x67DE18EDA5814AF2],
-    [0x81CEB32C4B43FCF5, 0x80EACF948770CED7],
-    [0xA2425FF75E14FC32, 0xA1258379A94D028D],
-    [0xCAD2F7F5359A3B3E, 0x096EE45813A04330],
-    [0xFD87B5F28300CA0E, 0x8BCA9D6E188853FC],
-    [0x9E74D1B791E07E48, 0x775EA264CF55347E],
-    [0xC612062576589DDB, 0x95364AFE032A819D],
-    [0xF79687AED3EEC551, 0x3A83DDBD83F52205],
-    [0x9ABE14CD44753B53, 0xC4926A9672793543],
-    [0xC16D9A0095928A27, 0x75B7053C0F178294],
-    [0xF1C90080BAF72CB1, 0x5324C68B12DD6338],
-    [0x971DA05074DA7BEF, 0xD3F6FC16EBCA5E03],
-    [0xBCE5086492111AEB, 0x88F4BB1CA6BCF584],
-    [0xEC1E4A7DB69561A5, 0x2B31E9E3D06C32E5],
-    [0x9392EE8E921D5D07, 0x3AFF322E62439FCF],
-    [0xB877AA3236A4B449, 0x09BEFEB9FAD487C3],
-    [0xE69594BEC44DE15B, 0x4C2EBE687989A9B4],
-    [0x901D7CF73AB0ACD9, 0x0F9D37014BF60A10],
-    [0xB424DC35095CD80F, 0x538484C19EF38C94],
-    [0xE12E13424BB40E13, 0x2865A5F206B06FBA],
-    [0x8CBCCC096F5088CC, 0xF93F87B7442E45D4],
-    [0xAFEBFF0BCB24AAFF, 0xF78F69A51539D749],
-    [0xDBE6FECEBDEDD5BF, 0xB573440E5A884D1B],
-    [0x89705F4136B4A597, 0x31680A88F8953031],
-    [0xABCC77118461CEFD, 0xFDC20D2B36BA7C3D],
-    [0xD6BF94D5E57A42BC, 0x3D32907604691B4D],
-    [0x8637BD05AF6C69B6, 0xA63F9A49C2C1B110],
-    [0xA7C5AC471B478423, 0x0FCF80DC33721D54],
-    [0xD1B71758E219652C, 0xD3C36113404EA4A9],
-    [0x83126E978D4FDF3B, 0x645A1CAC083126E9],
-    [0xA3D70A3D70A3D70A, 0x3D70A3D70A3D70A4],
-    [0xCCCCCCCCCCCCCCCD, 0xCCCCCCCCCCCCCCCD],
-    [0x8000000000000000, 0x0000000000000000],
-    [0xA000000000000000, 0x0000000000000000],
-    [0xC800000000000000, 0x0000000000000000],
-    [0xFA00000000000000, 0x0000000000000000],
-    [0x9C40000000000000, 0x0000000000000000],
-    [0xC350000000000000, 0x0000000000000000],
-    [0xF424000000000000, 0x0000000000000000],
-    [0x9896800000000000, 0x0000000000000000],
-    [0xBEBC200000000000, 0x0000000000000000],
-    [0xEE6B280000000000, 0x0000000000000000],
-    [0x9502F90000000000, 0x0000000000000000],
-    [0xBA43B74000000000, 0x0000000000000000],
-    [0xE8D4A51000000000, 0x0000000000000000],
-    [0x9184E72A00000000, 0x0000000000000000],
-    [0xB5E620F480000000, 0x0000000000000000],
-    [0xE35FA931A0000000, 0x0000000000000000],
-    [0x8E1BC9BF04000000, 0x0000000000000000],
-    [0xB1A2BC2EC5000000, 0x0000000000000000],
-    [0xDE0B6B3A76400000, 0x0000000000000000],
-    [0x8AC7230489E80000, 0x0000000000000000],
-    [0xAD78EBC5AC620000, 0x0000000000000000],
-    [0xD8D726B7177A8000, 0x0000000000000000],
-    [0x878678326EAC9000, 0x0000000000000000],
-    [0xA968163F0A57B400, 0x0000000000000000],
-    [0xD3C21BCECCEDA100, 0x0000000000000000],
-    [0x84595161401484A0, 0x0000000000000000],
-    [0xA56FA5B99019A5C8, 0x0000000000000000],
-    [0xCECB8F27F4200F3A, 0x0000000000000000],
-    [0x813F3978F8940984, 0x4000000000000000],
-    [0xA18F07D736B90BE5, 0x5000000000000000],
-    [0xC9F2C9CD04674EDF, 0xA400000000000000],
-    [0xFC6F7C4045812296, 0x4D00000000000000],
-    [0x9DC5ADA82B70B59E, 0xF020000000000000],
-    [0xC5371912364CE305, 0x6C28000000000000],
-    [0xF684DF56C3E01BC7, 0xC732000000000000],
-    [0x9A130B963A6C115C, 0x3C7F400000000000],
-    [0xC097CE7BC90715B3, 0x4B9F100000000000],
-    [0xF0BDC21ABB48DB20, 0x1E86D40000000000],
-    [0x96769950B50D88F4, 0x1314448000000000],
-    [0xBC143FA4E250EB31, 0x17D955A000000000],
-    [0xEB194F8E1AE525FD, 0x5DCFAB0800000000],
-    [0x92EFD1B8D0CF37BE, 0x5AA1CAE500000000],
-    [0xB7ABC627050305AE, 0xF14A3D9E40000000],
-    [0xE596B7B0C643C719, 0x6D9CCD05D0000000],
-    [0x8F7E32CE7BEA5C70, 0xE4820023A2000000],
-    [0xB35DBF821AE4F38C, 0xDDA2802C8A800000],
-    [0xE0352F62A19E306F, 0xD50B2037AD200000],
-    [0x8C213D9DA502DE45, 0x4526F422CC340000],
-    [0xAF298D050E4395D7, 0x9670B12B7F410000],
-    [0xDAF3F04651D47B4C, 0x3C0CDD765F114000],
-    [0x88D8762BF324CD10, 0xA5880A69FB6AC800],
-    [0xAB0E93B6EFEE0054, 0x8EEA0D047A457A00],
-    [0xD5D238A4ABE98068, 0x72A4904598D6D880],
-    [0x85A36366EB71F041, 0x47A6DA2B7F864750],
-    [0xA70C3C40A64E6C52, 0x999090B65F67D924],
-    [0xD0CF4B50CFE20766, 0xFFF4B4E3F741CF6D],
-    [0x82818F1281ED44A0, 0xBFF8F10E7A8921A4],
-    [0xA321F2D7226895C8, 0xAFF72D52192B6A0D],
-    [0xCBEA6F8CEB02BB3A, 0x9BF4F8A69F764490],
-    [0xFEE50B7025C36A08, 0x02F236D04753D5B5],
-    [0x9F4F2726179A2245, 0x01D762422C946591],
-    [0xC722F0EF9D80AAD6, 0x424D3AD2B7B97EF5],
-    [0xF8EBAD2B84E0D58C, 0xD2E0898765A7DEB2],
-    [0x9B934C3B330C8577, 0x63CC55F49F88EB2F],
-    [0xC2781F49FFCFA6D5, 0x3CBF6B71C76B25FB],
-    [0xF316271C7FC3908B, 0x8BEF464E3945EF7A],
-    [0x97EDD871CFDA3A57, 0x97758BF0E3CBB5AC],
-    [0xBDE94E8E43D0C8EC, 0x3D52EEED1CBEA317],
-    [0xED63A231D4C4FB27, 0x4CA7AAA863EE4BDD],
-    [0x945E455F24FB1CF9, 0x8FE8CAA93E74EF6A],
-    [0xB975D6B6EE39E437, 0xB3E2FD538E122B45],
-    [0xE7D34C64A9C85D44, 0x60DBBCA87196B616],
-    [0x90E40FBEEA1D3A4B, 0xBC8955E946FE31CE],
-    [0xB51D13AEA4A488DD, 0x6BABAB6398BDBE41],
-    [0xE264589A4DCDAB15, 0xC696963C7EED2DD2],
-    [0x8D7EB76070A08AED, 0xFC1E1DE5CF543CA3],
-    [0xB0DE65388CC8ADA8, 0x3B25A55F43294BCC],
-    [0xDD15FE86AFFAD912, 0x49EF0EB713F39EBF],
-    [0x8A2DBF142DFCC7AB, 0x6E3569326C784337],
-    [0xACB92ED9397BF996, 0x49C2C37F07965405],
-    [0xD7E77A8F87DAF7FC, 0xDC33745EC97BE906],
-    [0x86F0AC99B4E8DAFD, 0x69A028BB3DED71A4],
-    [0xA8ACD7C0222311BD, 0xC40832EA0D68CE0D],
-    [0xD2D80DB02AABD62C, 0xF50A3FA490C30190],
-    [0x83C7088E1AAB65DB, 0x792667C6DA79E0FA],
-    [0xA4B8CAB1A1563F52, 0x577001B891185939],
-    [0xCDE6FD5E09ABCF27, 0xED4C0226B55E6F87],
-    [0x80B05E5AC60B6178, 0x544F8158315B05B4],
-    [0xA0DC75F1778E39D6, 0x696361AE3DB1C721],
-    [0xC913936DD571C84C, 0x03BC3A19CD1E38EA],
-    [0xFB5878494ACE3A5F, 0x04AB48A04065C724],
-    [0x9D174B2DCEC0E47B, 0x62EB0D64283F9C76],
-    [0xC45D1DF942711D9A, 0x3BA5D0BD324F8394],
-    [0xF5746577930D6501, 0xCA8F44EC7EE36479],
-    [0x9968BF6ABBE85F20, 0x7E998B13CF4E1ECC],
-    [0xBFC2EF456AE276E9, 0x9E3FEDD8C321A67F],
-    [0xEFB3AB16C59B14A3, 0xC5CFE94EF3EA101E],
-    [0x95D04AEE3B80ECE6, 0xBBA1F1D158724A13],
-    [0xBB445DA9CA61281F, 0x2A8A6E45AE8EDC98],
-    [0xEA1575143CF97227, 0xF52D09D71A3293BE],
-    [0x924D692CA61BE758, 0x593C2626705F9C56],
-    [0xB6E0C377CFA2E12E, 0x6F8B2FB00C77836C],
-    [0xE498F455C38B997A, 0x0B6DFB9C0F956447],
-    [0x8EDF98B59A373FEC, 0x4724BD4189BD5EAC],
-    [0xB2977EE300C50FE7, 0x58EDEC91EC2CB658],
-    [0xDF3D5E9BC0F653E1, 0x2F2967B66737E3ED],
-    [0x8B865B215899F46D, 0xBD79E0D20082EE74],
-    [0xAE67F1E9AEC07188, 0xECD8590680A3AA11],
-    [0xDA01EE641A708DEA, 0xE80E6F4820CC9496],
-    [0x884134FE908658B2, 0x3109058D147FDCDE],
-    [0xAA51823E34A7EEDF, 0xBD4B46F0599FD415],
-    [0xD4E5E2CDC1D1EA96, 0x6C9E18AC7007C91A],
-    [0x850FADC09923329E, 0x03E2CF6BC604DDB0],
-    [0xA6539930BF6BFF46, 0x84DB8346B786151D],
-    [0xCFE87F7CEF46FF17, 0xE612641865679A64],
-    [0x81F14FAE158C5F6E, 0x4FCB7E8F3F60C07E],
-    [0xA26DA3999AEF774A, 0xE3BE5E330F38F09E],
-    [0xCB090C8001AB551C, 0x5CADF5BFD3072CC5],
-    [0xFDCB4FA002162A63, 0x73D9732FC7C8F7F7],
-    [0x9E9F11C4014DDA7E, 0x2867E7FDDCDD9AFA],
-    [0xC646D63501A1511E, 0xB281E1FD541501B9],
-    [0xF7D88BC24209A565, 0x1F225A7CA91A4227],
-    [0x9AE757596946075F, 0x3375788DE9B06958],
-    [0xC1A12D2FC3978937, 0x0052D6B1641C83AE],
-    [0xF209787BB47D6B85, 0xC0678C5DBD23A49A],
-    [0x9745EB4D50CE6333, 0xF840B7BA963646E0],
-    [0xBD176620A501FC00, 0xB650E5A93BC3D898],
-    [0xEC5D3FA8CE427B00, 0xA3E51F138AB4CEBE],
-    [0x93BA47C980E98CE0, 0xC66F336C36B10137],
-    [0xB8A8D9BBE123F018, 0xB80B0047445D4185],
-    [0xE6D3102AD96CEC1E, 0xA60DC059157491E6],
-    [0x9043EA1AC7E41393, 0x87C89837AD68DB30],
-    [0xB454E4A179DD1877, 0x29BABE4598C311FC],
-    [0xE16A1DC9D8545E95, 0xF4296DD6FEF3D67B],
-    [0x8CE2529E2734BB1D, 0x1899E4A65F58660D],
-    [0xB01AE745B101E9E4, 0x5EC05DCFF72E7F90],
-    [0xDC21A1171D42645D, 0x76707543F4FA1F74],
-    [0x899504AE72497EBA, 0x6A06494A791C53A8],
-    [0xABFA45DA0EDBDE69, 0x0487DB9D17636892],
-    [0xD6F8D7509292D603, 0x45A9D2845D3C42B7],
-    [0x865B86925B9BC5C2, 0x0B8A2392BA45A9B2],
-    [0xA7F26836F282B733, 0x8E6CAC7768D7141F],
-    [0xD1EF0244AF2364FF, 0x3207D795430CD927],
-    [0x8335616AED761F1F, 0x7F44E6BD49E807B8],
-    [0xA402B9C5A8D3A6E7, 0x5F16206C9C6209A6],
-    [0xCD036837130890A1, 0x36DBA887C37A8C10],
-    [0x802221226BE55A65, 0xC2494954DA2C978A],
-    [0xA02AA96B06DEB0FE, 0xF2DB9BAA10B7BD6C],
-    [0xC83553C5C8965D3D, 0x6F92829494E5ACC7],
-    [0xFA42A8B73ABBF48D, 0xCB772339BA1F17F9],
-    [0x9C69A97284B578D8, 0xFF2A760414536EFC],
-    [0xC38413CF25E2D70E, 0xFEF5138519684ABB],
-    [0xF46518C2EF5B8CD1, 0x7EB258665FC25D69],
-    [0x98BF2F79D5993803, 0xEF2F773FFBD97A62],
-    [0xBEEEFB584AFF8604, 0xAAFB550FFACFD8FA],
-    [0xEEAABA2E5DBF6785, 0x95BA2A53F983CF39],
-    [0x952AB45CFA97A0B3, 0xDD945A747BF26184],
-    [0xBA756174393D88E0, 0x94F971119AEEF9E4],
-    [0xE912B9D1478CEB17, 0x7A37CD5601AAB85E],
-    [0x91ABB422CCB812EF, 0xAC62E055C10AB33B],
-    [0xB616A12B7FE617AA, 0x577B986B314D6009],
-    [0xE39C49765FDF9D95, 0xED5A7E85FDA0B80B],
-    [0x8E41ADE9FBEBC27D, 0x14588F13BE847307],
-    [0xB1D219647AE6B31C, 0x596EB2D8AE258FC9],
-    [0xDE469FBD99A05FE3, 0x6FCA5F8ED9AEF3BB],
-    [0x8AEC23D680043BEE, 0x25DE7BB9480D5855],
-    [0xADA72CCC20054AEA, 0xAF561AA79A10AE6A],
-    [0xD910F7FF28069DA4, 0x1B2BA1518094DA05],
-    [0x87AA9AFF79042287, 0x90FB44D2F05D0843],
-    [0xA99541BF57452B28, 0x353A1607AC744A54],
-    [0xD3FA922F2D1675F2, 0x42889B8997915CE9],
-    [0x847C9B5D7C2E09B7, 0x69956135FEBADA11],
-    [0xA59BC234DB398C25, 0x43FAB9837E699096],
-    [0xCF02B2C21207EF2F, 0x94F967E45E03F4BB],
-    [0x8161AFB94B44F57D, 0x1D1BE0EEBAC278F5],
-    [0xA1BA1BA79E1632DC, 0x6462D92A69731732],
-    [0xCA28A291859BBF93, 0x7D7B8F7503CFDCFF],
-    [0xFCB2CB35E702AF78, 0x5CDA735244C3D43F],
-    [0x9DEFBF01B061ADAB, 0x3A0888136AFA64A7],
-    [0xC56BAEC21C7A1916, 0x088AAA1845B8FDD1],
-    [0xF6C69A72A3989F5C, 0x8AAD549E57273D45],
-    [0x9A3C2087A63F6399, 0x36AC54E2F678864B],
-    [0xC0CB28A98FCF3C80, 0x84576A1BB416A7DE],
-    [0xF0FDF2D3F3C30B9F, 0x656D44A2A11C51D5],
-    [0x969EB7C47859E744, 0x9F644AE5A4B1B325],
-    [0xBC4665B596706115, 0x873D5D9F0DDE1FEF],
-    [0xEB57FF22FC0C795A, 0xA90CB506D155A7EA],
-    [0x9316FF75DD87CBD8, 0x09A7F12442D588F3],
-    [0xB7DCBF5354E9BECE, 0x0C11ED6D538AEB2F],
-    [0xE5D3EF282A242E82, 0x8F1668C8A86DA5FB],
-    [0x8FA475791A569D11, 0xF96E017D694487BD],
-    [0xB38D92D760EC4455, 0x37C981DCC395A9AC],
-    [0xE070F78D3927556B, 0x85BBE253F47B1417],
-    [0x8C469AB843B89563, 0x93956D7478CCEC8E],
-    [0xAF58416654A6BABB, 0x387AC8D1970027B2],
-    [0xDB2E51BFE9D0696A, 0x06997B05FCC0319F],
-    [0x88FCF317F22241E2, 0x441FECE3BDF81F03],
-    [0xAB3C2FDDEEAAD25B, 0xD527E81CAD7626C4],
-    [0xD60B3BD56A5586F2, 0x8A71E223D8D3B075],
-    [0x85C7056562757457, 0xF6872D5667844E49],
-    [0xA738C6BEBB12D16D, 0xB428F8AC016561DB],
-    [0xD106F86E69D785C8, 0xE13336D701BEBA52],
-    [0x82A45B450226B39D, 0xECC0024661173473],
-    [0xA34D721642B06084, 0x27F002D7F95D0190],
-    [0xCC20CE9BD35C78A5, 0x31EC038DF7B441F4],
-    [0xFF290242C83396CE, 0x7E67047175A15271],
-    [0x9F79A169BD203E41, 0x0F0062C6E984D387],
-    [0xC75809C42C684DD1, 0x52C07B78A3E60868],
-    [0xF92E0C3537826146, 0xA7709A56CCDF8A83],
-    [0x9BBCC7A142B17CCC, 0x88A66076400BB692],
-    [0xC2ABF989935DDBFE, 0x6ACFF893D00EA436],
-    [0xF356F7EBF83552FE, 0x0583F6B8C4124D43],
-    [0x98165AF37B2153DF, 0xC3727A337A8B704A],
-    [0xBE1BF1B059E9A8D6, 0x744F18C0592E4C5D],
-    [0xEDA2EE1C7064130C, 0x1162DEF06F79DF74],
-    [0x9485D4D1C63E8BE8, 0x8ADDCB5645AC2BA8],
-    [0xB9A74A0637CE2EE1, 0x6D953E2BD7173693],
-    [0xE8111C87C5C1BA9A, 0xC8FA8DB6CCDD0437],
-    [0x910AB1D4DB9914A0, 0x1D9C9892400A22A2],
-    [0xB54D5E4A127F59C8, 0x2503BEB6D00CAB4B],
-    [0xE2A0B5DC971F303A, 0x2E44AE64840FD61E],
-    [0x8DA471A9DE737E24, 0x5CEAECFED289E5D3],
-    [0xB10D8E1456105DAD, 0x7425A83E872C5F47],
-    [0xDD50F1996B947519, 0xD12F124E28F77719],
-    [0x8A5296FFE33CC930, 0x82BD6B70D99AAA70],
-    [0xACE73CBFDC0BFB7B, 0x636CC64D1001550C],
-    [0xD8210BEFD30EFA5A, 0x3C47F7E05401AA4F],
-    [0x8714A775E3E95C78, 0x65ACFAEC34810A71],
-    [0xA8D9D1535CE3B396, 0x7F1839A741A14D0D],
-    [0xD31045A8341CA07C, 0x1EDE48111209A051],
-    [0x83EA2B892091E44E, 0x934AED0AAB460432],
-    [0xA4E4B66B68B65D61, 0xF81DA84D5617853F],
-    [0xCE1DE40642E3F4B9, 0x36251260AB9D668F],
-    [0x80D2AE83E9CE78F4, 0xC1D72B7C6B426019],
-    [0xA1075A24E4421731, 0xB24CF65B8612F820],
-    [0xC94930AE1D529CFD, 0xDEE033F26797B628],
-    [0xFB9B7CD9A4A7443C, 0x169840EF017DA3B1],
-    [0x9D412E0806E88AA6, 0x8E1F289560EE864F],
-    [0xC491798A08A2AD4F, 0xF1A6F2BAB92A27E3],
-    [0xF5B5D7EC8ACB58A3, 0xAE10AF696774B1DB],
-    [0x9991A6F3D6BF1766, 0xACCA6DA1E0A8EF29],
-    [0xBFF610B0CC6EDD3F, 0x17FD090A58D32AF3],
-    [0xEFF394DCFF8A948F, 0xDDFC4B4CEF07F5B0],
-    [0x95F83D0A1FB69CD9, 0x4ABDAF101564F98E],
-    [0xBB764C4CA7A44410, 0x9D6D1AD41ABE37F2],
-    [0xEA53DF5FD18D5514, 0x84C86189216DC5EE],
-    [0x92746B9BE2F8552C, 0x32FD3CF5B4E49BB5],
-    [0xB7118682DBB66A77, 0x3FBC8C33221DC2A2],
-    [0xE4D5E82392A40515, 0x0FABAF3FEAA5334A],
-    [0x8F05B1163BA6832D, 0x29CB4D87F2A7400E],
-    [0xB2C71D5BCA9023F8, 0x743E20E9EF511012],
-    [0xDF78E4B2BD342CF7, 0x914DA9246B255417],
-    [0x8BAB8EEFB6409C1A, 0x1AD089B6C2F7548E],
-    [0xAE9672ABA3D0C321, 0xA184AC2473B529B2],
-    [0xDA3C0F568CC4F3E9, 0xC9E5D72D90A2741E],
-    [0x8865899617FB1871, 0x7E2FA67C7A658893],
-    [0xAA7EEBFB9DF9DE8E, 0xDDBB901B98FEEAB8],
-    [0xD51EA6FA85785631, 0x552A74227F3EA565],
-    [0x8533285C936B35DF, 0xD53A88958F87275F],
-    [0xA67FF273B8460357, 0x8A892ABAF368F137],
-    [0xD01FEF10A657842C, 0x2D2B7569B0432D85],
-    [0x8213F56A67F6B29C, 0x9C3B29620E29FC73],
-    [0xA298F2C501F45F43, 0x8349F3BA91B47B90],
-    [0xCB3F2F7642717713, 0x241C70A936219A74],
-    [0xFE0EFB53D30DD4D8, 0xED238CD383AA0111],
-    [0x9EC95D1463E8A507, 0xF4363804324A40AB],
-    [0xC67BB4597CE2CE49, 0xB143C6053EDCD0D5],
-    [0xF81AA16FDC1B81DB, 0xDD94B7868E94050A],
-    [0x9B10A4E5E9913129, 0xCA7CF2B4191C8327],
-    [0xC1D4CE1F63F57D73, 0xFD1C2F611F63A3F0],
-    [0xF24A01A73CF2DCD0, 0xBC633B39673C8CEC],
-    [0x976E41088617CA02, 0xD5BE0503E085D814],
-    [0xBD49D14AA79DBC82, 0x4B2D8644D8A74E19],
-    [0xEC9C459D51852BA3, 0xDDF8E7D60ED1219F],
-    [0x93E1AB8252F33B46, 0xCABB90E5C942B503],
-    [0xB8DA1662E7B00A17, 0x3D6A751F3B936244],
-    [0xE7109BFBA19C0C9D, 0x0CC512670A783AD5],
-    [0x906A617D450187E2, 0x27FB2B80668B24C5],
-    [0xB484F9DC9641E9DB, 0xB1F9F660802DEDF6],
-    [0xE1A63853BBD26451, 0x5E7873F8A0396974],
-    [0x8D07E33455637EB3, 0xDB0B487B6423E1E8],
-    [0xB049DC016ABC5E60, 0x91CE1A9A3D2CDA63],
-    [0xDC5C5301C56B75F7, 0x7641A140CC7810FB],
-    [0x89B9B3E11B6329BB, 0xA9E904C87FCB0A9D],
-    [0xAC2820D9623BF429, 0x546345FA9FBDCD44],
-    [0xD732290FBACAF134, 0xA97C177947AD4095],
-    [0x867F59A9D4BED6C0, 0x49ED8EABCCCC485D],
-    [0xA81F301449EE8C70, 0x5C68F256BFFF5A75],
-    [0xD226FC195C6A2F8C, 0x73832EEC6FFF3112],
-    [0x83585D8FD9C25DB8, 0xC831FD53C5FF7EAB],
-    [0xA42E74F3D032F526, 0xBA3E7CA8B77F5E56],
-    [0xCD3A1230C43FB26F, 0x28CE1BD2E55F35EB],
-    [0x80444B5E7AA7CF85, 0x7980D163CF5B81B3],
-    [0xA0555E361951C367, 0xD7E105BCC3326220],
-    [0xC86AB5C39FA63441, 0x8DD9472BF3FEFAA8],
-    [0xFA856334878FC151, 0xB14F98F6F0FEB952],
-    [0x9C935E00D4B9D8D2, 0x6ED1BF9A569F33D3],
-    [0xC3B8358109E84F07, 0x0A862F80EC4700C8],
-    [0xF4A642E14C6262C9, 0xCD27BB612758C0FA],
-    [0x98E7E9CCCFBD7DBE, 0x8038D51CB897789C],
-    [0xBF21E44003ACDD2D, 0xE0470A63E6BD56C3],
-    [0xEEEA5D5004981478, 0x1858CCFCE06CAC74],
-    [0x95527A5202DF0CCB, 0x0F37801E0C43EBC9],
-    [0xBAA718E68396CFFE, 0xD30560258F54E6BB],
-    [0xE950DF20247C83FD, 0x47C6B82EF32A2069],
-    [0x91D28B7416CDD27E, 0x4CDC331D57FA5442],
-    [0xB6472E511C81471E, 0xE0133FE4ADF8E952],
-    [0xE3D8F9E563A198E5, 0x58180FDDD97723A7],
-    [0x8E679C2F5E44FF8F, 0x570F09EAA7EA7648],
-    [0xB201833B35D63F73, 0x2CD2CC6551E513DA],
-    [0xDE81E40A034BCF50, 0xF8077F7EA65E58D1],
-    [0x8B112E86420F6192, 0xFB04AFAF27FAF783],
-    [0xADD57A27D29339F6, 0x79C5DB9AF1F9B563],
-    [0xD94AD8B1C7380874, 0x18375281AE7822BC],
-    [0x87CEC76F1C830549, 0x8F2293910D0B15B6],
-    [0xA9C2794AE3A3C69B, 0xB2EB3875504DDB23],
-    [0xD433179D9C8CB841, 0x5FA60692A46151EC],
-    [0x849FEEC281D7F329, 0xDBC7C41BA6BCD333],
-    [0xA5C7EA73224DEFF3, 0x12B9B522906C0800],
-    [0xCF39E50FEAE16BF0, 0xD768226B34870A00],
-    [0x81842F29F2CCE376, 0xE6A1158300D46640],
-    [0xA1E53AF46F801C53, 0x60495AE3C1097FD0],
-    [0xCA5E89B18B602368, 0x385BB19CB14BDFC4],
-    [0xFCF62C1DEE382C42, 0x46729E03DD9ED7B5],
-    [0x9E19DB92B4E31BA9, 0x6C07A2C26A8346D1],
-    [0xC5A05277621BE294, 0xC7098B7305241886],
-    [0xF70867153AA2DB39, 0xB8CBEE4FC66D1EA7],
-    [0x9A65406D44A5C903, 0x737F74F1DC043328],
-    [0xC0FE908895CF3B44, 0x505F522E53053FF2],
-    [0xF13E34AABB430A15, 0x647726B9E7C68FEF],
-    [0x96C6E0EAB509E64D, 0x5ECA783430DC19F5],
-    [0xBC789925624C5FE1, 0xB67D16413D132073],
-    [0xEB96BF6EBADF77D9, 0xE41C5BD18C57E88F],
-    [0x933E37A534CBAAE8, 0x8E91B962F7B6F15A],
-    [0xB80DC58E81FE95A1, 0x723627BBB5A4ADB0],
-    [0xE61136F2227E3B0A, 0xCEC3B1AAA30DD91C],
-    [0x8FCAC257558EE4E6, 0x213A4F0AA5E8A7B2],
-    [0xB3BD72ED2AF29E20, 0xA988E2CD4F62D19E],
-    [0xE0ACCFA875AF45A8, 0x93EB1B80A33B8605],
-    [0x8C6C01C9498D8B89, 0xBC72F130660533C3],
-    [0xAF87023B9BF0EE6B, 0xEB8FAD7C7F8680B4],
-    [0xDB68C2CA82ED2A06, 0xA67398DB9F6820E1],
-    [0x892179BE91D43A44, 0x88083F8943A1148D],
-    [0xAB69D82E364948D4, 0x6A0A4F6B948959B0],
-    [0xD6444E39C3DB9B0A, 0x848CE34679ABB01C],
-    [0x85EAB0E41A6940E6, 0xF2D80E0C0C0B4E12],
-    [0xA7655D1D2103911F, 0x6F8E118F0F0E2196],
-    [0xD13EB46469447567, 0x4B7195F2D2D1A9FB],
-    [0x82C730BEC1CAC961, 0x8F26FDB7C3C30A3D],
-    [0xA378FCEE723D7BB9, 0xB2F0BD25B4B3CCCC],
-    [0xCC573C2A0ECCDAA7, 0xDFACEC6F21E0C000],
-    [0xFF6D0B3492801151, 0x9798278AEA58EFFF],
-    [0x9FA42700DB900AD2, 0x5EBF18B6D2779600],
-    [0xC78D30C112740D87, 0xF66EDEE487157B80],
-    [0xF9707CF1571110E9, 0xB40A969DA8DADA5F],
-    [0x9BE64E16D66AAA91, 0x70869E228988C87C],
-    [0xC2DFE19C8C055536, 0xCCA845AB2BEAFA9B],
-    [0xF397DA03AF06AA83, 0x3FD25715F6E5B941],
-    [0x983EE8424D642A92, 0x07E3766DBA4F93C9],
-    [0xBE4EA252E0BD3537, 0x89DC540928E378BB],
-    [0xEDE24AE798EC8284, 0x2C53690B731C56EA],
-    [0x94AD6ED0BF93D193, 0x9BB421A727F1B652],
-    [0xB9D8CA84EF78C5F7, 0x42A12A10F1EE23E7],
-    [0xE84EFD262B56F775, 0x134974952E69ACE0],
-    [0x91315E37DB165AA9, 0x2C0DE8DD3D020C0C],
-    [0xB57DB5C5D1DBF153, 0x771163148C428F0F],
-    [0xE2DD23374652EDA8, 0x54D5BBD9AF5332D3],
-    [0x8DCA36028BF3D489, 0x350595680D93FFC4],
-    [0xB13CC3832EF0C9AC, 0x8246FAC210F8FFB5],
-    [0xDD8BF463FAACFC16, 0x62D8B97295373FA2],
-    [0x8A7778BE7CAC1D8E, 0xFDC773E79D4287C5],
-    [0xAD1556EE1BD724F1, 0x7D3950E1849329B7],
-    [0xD85AACA9A2CCEE2E, 0xDC87A519E5B7F424],
-    [0x8738ABEA05C014DD, 0xA9D4C7302F92F897],
-    [0xA906D6E487301A14, 0xD449F8FC3B77B6BC],
-    [0xD3488C9DA8FC2099, 0xC95C773B4A55A46B],
-    [0x840D57E2899D945F, 0x7DD9CA850E7586C3],
-    [0xA510ADDB2C04F977, 0x5D503D265212E874],
-    [0xCE54D951F70637D5, 0x34A44C6FE697A291],
-    [0x80F507D33A63E2E5, 0x40E6AFC5F01EC59B],
-    [0xA13249C808FCDB9F, 0x91205BB76C267701],
-    [0xC97EDC3A0B3C1286, 0x356872A5473014C1],
-    [0xFBDE93488E0B1728, 0xC2C28F4E98FC19F2],
-    [0x9D6B1C0D58C6EE79, 0xD9B999911F9D9037],
-    [0xC4C5E310AEF8AA17, 0x1027FFF56784F445],
-    [0xF5F75BD4DAB6D49D, 0xD431FFF2C1663156],
-    [0x99BA996508B244E2, 0x049F3FF7B8DFDED6],
-    [0xC0293FBE4ADED61B, 0x85C70FF5A717D68B],
-    [0xF0338FADDD968BA1, 0x2738D3F310DDCC2E],
-    [0x962039CCAA7E1745, 0xB8838477EA8A9F9D],
-    [0xBBA8483FD51D9D16, 0xE6A46595E52D4784],
-    [0xEA925A4FCA65045B, 0x604D7EFB5E789965],
-    [0x929B7871DE7F22B9, 0x1C306F5D1B0B5FDF],
-    [0xB742568E561EEB67, 0x633C8B3461CE37D7],
-    [0xE512EC31EBA6A641, 0x3C0BAE017A41C5CD],
-    [0x8F2BD39F334827E9, 0xC5874CC0EC691BA0],
-    [0xB2F6C887001A31E3, 0xF6E91FF127836288],
-    [0xDFB47AA8C020BE5C, 0xB4A367ED71643B2A],
-    [0x8BD0CCA9781476F9, 0x50E620F466DEA4FA],
-    [0xAEC4FFD3D61994B8, 0xA51FA93180964E39],
-    [0xDA763FC8CB9FF9E6, 0x8E67937DE0BBE1C7],
-    [0x8889E7DD7F43FC2F, 0x7900BC2EAC756D1C],
-    [0xAAAC61D4DF14FB3B, 0x5740EB3A5792C863],
-    [0xD5577A4A16DA3A0A, 0x2D112608ED777A7C],
-    [0x8556AC6E4E486446, 0x5C2AB7C5946AAC8E],
-    [0xA6AC5789E1DA7D58, 0xF33565B6F98557B1],
-    [0xD0576D6C5A511CAE, 0xF002BF24B7E6AD9D],
-    [0x8236A463B872B1ED, 0xB601B776F2F02C82],
-    [0xA2C44D7CA68F5E68, 0xE3822554AFAC37A3],
-    [0xCB7560DBD0333602, 0xDC62AEA9DB97458C],
-    [0xFE52B912C4400382, 0x537B5A54527D16EE],
-    [0x9EF3B3ABBAA80231, 0x742D1874B38E2E55],
-    [0xC6B0A096A95202BE, 0xD1385E91E071B9EA],
-    [0xF85CC8BC53A6836D, 0x45867636588E2865],
-    [0x9B39FD75B4481224, 0x4B7409E1F758D93F],
-    [0xC2087CD3215A16AD, 0x5E510C5A752F0F8F],
-    [0xF28A9C07E9B09C59, 0xB5E54F71127AD373],
-    [0x9796A184F20E61B7, 0x71AF51A6AB8CC428],
-    [0xBD7C49E62E91FA25, 0x4E1B2610566FF531],
-    [0xECDB5C5FBA3678AF, 0xA1A1EF946C0BF27E],
-    [0x940919BBD4620B6D, 0x250535BCC387778F],
-    [0xB90B602AC97A8E48, 0x6E46832BF4695572],
-    [0xE74E38357BD931DB, 0x89D823F6F183AACF],
-    [0x9090E3216D67BF29, 0x9627167A56F24AC1],
-    [0xB4B51BE9C8C1AEF3, 0xBBB0DC18ECAEDD72],
-    [0xE1E262E43AF21AAF, 0x6A9D131F27DA94CE],
-    [0x8D2D7DCEA4D750AE, 0xA2A22BF378E89D01],
-    [0xB078DD424E0D24D9, 0x0B4AB6F05722C441],
-    [0xDC971492E1906E0F, 0x4E1D64AC6CEB7551],
-    [0x89DE6CDBCCFA44CA, 0x90D25EEBC4132953],
-    [0xAC560812C038D5FC, 0xF506F6A6B517F3A7],
-    [0xD76B8A1770470B7B, 0xF248B450625DF091],
-    [0x86A3364EA62C672D, 0xD76D70B23D7AB65B],
-    [0xA84C03E24FB780F8, 0x0D48CCDECCD963F2],
-    [0xD25F04DAE3A56136, 0x109B0016800FBCEE],
-    [0x837B6308CE475CC2, 0xCA60E00E1009D615],
-    [0xA45A3BCB01D933F2, 0x3CF91811940C4B9A],
-    [0xCD70CABDC24F80EF, 0xCC375E15F90F5E80],
-    [0x80667EB69971B095, 0x3FA29ACDBBA99B10],
-    [0xA0801E643FCE1CBB, 0x8F8B41812A9401D4],
-    [0xC8A025FD4FC1A3E9, 0x336E11E175390249],
-    [0xFAC82F7CA3B20CE4, 0x80499659D28742DC],
-    [0x9CBD1DADE64F480E, 0x302DFDF8239489C9],
-    [0xC3EC65195FE31A12, 0xBC397D762C79AC3C],
-    [0xF4E77E5FB7DBE096, 0x2B47DCD3B798174B],
-    [0x9910AEFBD2E96C5E, 0xDB0CEA0452BF0E8F],
-    [0xBF54DABAC7A3C775, 0x51D02485676ED232],
-    [0xEF2A1169798CB953, 0xA6442DA6C14A86BF],
-    [0x957A4AE1EBF7F3D4, 0xA7EA9C8838CE9437],
-    [0xBAD8DD9A66F5F0C9, 0x91E543AA47023945],
-    [0xE98F150100B36CFB, 0xB65E9494D8C2C796],
-    [0x91F96D20A070241D, 0xB1FB1CDD0779BCBE],
-    [0xB677C868C88C2D24, 0xDE79E41449582BED],
-    [0xE415BA82FAAF386D, 0xD6185D195BAE36E9],
-    [0x8E8D9491DCAD8344, 0x05CF3A2FD94CE251],
-    [0xB230F9B653D8E415, 0x074308BBCFA01AE6],
-    [0xDEBD3823E8CF1D1A, 0x4913CAEAC388219F],
-    [0x8B36431671817230, 0x6DAC5ED2BA351504],
-    [0xAE03D3DC0DE1CEBD, 0x8917768768C25A44],
-    [0xD984C8D3115A426C, 0xAB5D542942F2F0D6],
-    [0x87F2FD83EAD86983, 0x4B1A5499C9D7D685],
-    [0xA9EFBCE4E58E83E4, 0x1DE0E9C03C4DCC27],
-    [0xD46BAC1E1EF224DD, 0x255924304B613F31],
-    [0x84C34B92D357570A, 0x3757B69E2F1CC77E],
-    [0xA5F41E77882D2CCD, 0xC52DA445BAE3F95E],
-    [0xCF7126156A387800, 0xF6790D57299CF7B5],
-    [0x81A6B7CD62634B00, 0xFA0BA8567A021AD1],
-    [0xA21065C0BAFC1DC0, 0xF88E926C1882A186],
-    [0xCA947F30E9BB2530, 0xF6B237071EA349E7],
-    [0xFD399EFD2429EE7C, 0xF45EC4C8E64C1C61],
-    [0x9E44035E369A350D, 0x78BB3AFD8FEF91BD],
-    [0xC5D50435C440C251, 0xD6EA09BCF3EB762C],
-    [0xF74A45433550F2E5, 0x0CA48C2C30E653B7],
-    [0x9A8E6B4A015297CF, 0x27E6D79B9E8FF452],
-    [0xC132061C81A73DC3, 0xF1E08D828633F167],
-    [0xF17E87A3A2110D34, 0xAE58B0E327C0EDC0],
-    [0x96EF14C6454AA840, 0x4CF76E8DF8D89498],
-    [0xBCAAD9F7D69D5250, 0x60354A31770EB9BE],
-    [0xEBD59075CC44A6E4, 0x78429CBDD4D2682E],
-    [0x93657A499FAAE84F, 0xCB29A1F6A503811D],
-    [0xB83ED8DC0795A262, 0x7DF40A744E446164],
-    [0xE64E8F13097B0AFB, 0x1D710D1161D579BD],
-    [0x8FF1196BE5ECE6DD, 0xF266A82ADD256C16],
-    [0xB3ED5FC6DF682094, 0x2F005235946EC71C],
-    [0xE0E8B7B8974228B9, 0x3AC066C2F98A78E2],
-    [0x8C9172D35E895974, 0xC4B84039DBF68B8E],
-    [0xAFB5CF88362BAFD1, 0xB5E6504852F42E71],
-    [0xDBA3436A43B69BC5, 0xE35FE45A67B13A0D],
-    [0x89460A226A52215B, 0x0E1BEEB880CEC448],
-    [0xAB978CAB04E6A9B2, 0xD1A2EA66A102755A],
-    [0xD67D6FD5C620541E, 0x460BA500494312B1],
-    [0x860E65E59BD43493, 0xEBC747202DC9EBAF],
-    [0xA791FF5F02C941B8, 0xA6B918E8393C669A],
-    [0xD1767F36C37B9226, 0x90675F22478B8041],
-    [0x82EA0F823A2D3B57, 0x7A409B756CB73028],
-    [0xA3A49362C8B88A2D, 0x58D0C252C7E4FC33],
-    [0xCC8DB83B7AE6ACB9, 0xAF04F2E779DE3B3F],
-    [0xFFB1264A59A057E7, 0xDAC62FA15855CA0F],
-    [0x9FCEB7EE780436F0, 0x48BBDDC4D7359E49],
-    [0xC7C265EA160544AC, 0x5AEAD5360D0305DC],
-    [0xF9B2FF649B8695D7, 0x71A58A839043C753],
-    [0x9C0FDF9EE1341DA7, 0xA70776923A2A5C94],
-    [0xC313D78699812510, 0x50C95436C8B4F3B9],
-    [0xF3D8CD683FE16E54, 0x64FBA9447AE230A7],
-    [0x9867806127ECE4F5, 0xBF1D49CACCCD5E68],
-    [0xBE81607971E81E32, 0xEEE49C3D8000B602],
-    [0xEE21B897CE6225BE, 0x6A9DC34CE000E383],
-    [0x94D5135EE0FD5797, 0x02A29A100C008E32],
-    [0xBA0A5836993CAD7D, 0xC34B40940F00B1BE],
-    [0xE88CEE443F8BD8DC, 0xF41E10B912C0DE2E],
-    [0x915814EAA7B76789, 0x7892CA73ABB88ADD],
-    [0xB5AE1A2551A5416C, 0xD6B77D1096A6AD94],
-    [0xE319A0AEA60E91C7, 0xCC655C54BC5058F9],
-];
-
-// 
 static immutable short[1025] p10_exponents = [
     -1764,
     -1761,
@@ -2064,3 +1034,2060 @@ static immutable short[1025] p10_exponents = [
     1634,
     1637,
 ];
+
+static immutable align(16) ulong[1025] p10_coefficients_h = [
+    0x9049EE32DB23D21C,
+    0xB45C69BF91ECC6A4,
+    0xE173842F7667F84C,
+    0x8CE8329DAA00FB30,
+    0xB0223F45148139FC,
+    0xDC2ACF1659A1887B,
+    0x899AC16DF804F54D,
+    0xAC0171C9760632A0,
+    0xD701CE3BD387BF48,
+    0x866120E56434D78D,
+    0xA7F9691EBD420D70,
+    0xD1F7C3666C9290CC,
+    0x833ADA2003DB9A80,
+    0xA40990A804D2811F,
+    0xCD0BF4D206072167,
+    0x8027790343C474E1,
+    0xA031574414B59219,
+    0xC83DAD1519E2F69F,
+    0xFA4D185A605BB447,
+    0x9C702F387C3950AC,
+    0xC38C3B069B47A4D7,
+    0xF46F49C842198E0D,
+    0x98C58E1D294FF8C8,
+    0xBEF6F1A473A3F6FA,
+    0xEEB4AE0D908CF4B9,
+    0x9530ECC87A5818F3,
+    0xBA7D27FA98EE1F30,
+    0xE91C71F93F29A6FC,
+    0x91B1C73BC77A085E,
+    0xB61E390AB9588A75,
+    0xE3A5C74D67AEAD12,
+    0x8E479C9060CD2C2C,
+    0xB1D983B479007736,
+    0xDE4FE4A197409504,
+    0x8AF1EEE4FE885D22,
+    0xADAE6A9E3E2A746B,
+    0xD91A0545CDB51186,
+    0x87B0434BA0912AF4,
+    0xA99C541E88B575B1,
+    0xD40369262AE2D31D,
+    0x848221B7DACDC3F2,
+    0xA5A2AA25D18134EE,
+    0xCF0B54AF45E1822A,
+    0x816714ED8BACF15A,
+    0xA1C0DA28EE982DB1,
+    0xCA3110B32A3E391D,
+    0xFCBD54DFF4CDC764,
+    0x9DF6550BF9009C9F,
+    0xC573EA4EF740C3C6,
+    0xF6D0E4E2B510F4B8,
+    0x9A428F0DB12A98F3,
+    0xC0D332D11D753F30,
+    0xF107FF8564D28EFC,
+    0x96A4FFB35F03995D,
+    0xBC4E3FA036C47FB5,
+    0xEB61CF8844759FA2,
+    0x931D21B52AC983C5,
+    0xB7E46A22757BE4B6,
+    0xE5DD84AB12DADDE4,
+    0x8FAA72EAEBC8CAAF,
+    0xB3950FA5A6BAFD5A,
+    0xE07A538F1069BCB1,
+    0x8C4C74396A4215EE,
+    0xAF5F9147C4D29B6A,
+    0xDB377599B6074245,
+    0x8902A98011C4896B,
+    0xAB4353E01635ABC6,
+    0xD61428D81BC316B7,
+    0x85CC99871159EE32,
+    0xA73FBFE8D5B069BF,
+    0xD10FAFE30B1C842F,
+    0x82A9CDEDE6F1D29D,
+    0xA354416960AE4744,
+    0xCC2951C3B8D9D916,
+    0xFF33A634A7104F5B,
+    0x9F8047E0E86A3199,
+    0xC76059D92284BDFF,
+    0xF938704F6B25ED7F,
+    0x9BC34631A2F7B46F,
+    0xC2B417BE0BB5A18B,
+    0xF3611DAD8EA309EE,
+    0x981CB28C7925E635,
+    0xBE23DF2F976F5FC2,
+    0xEDACD6FB7D4B37B2,
+    0x948C065D2E4F02CF,
+    0xB9AF07F479E2C383,
+    0xE81AC9F1985B7464,
+    0x9110BE36FF3928BF,
+    0xB554EDC4BF0772EE,
+    0xE2AA2935EEC94FAA,
+    0x8DAA59C1B53DD1CA,
+    0xB114F032228D463D,
+    0xDD5A2C3EAB3097CC,
+    0x8A585BA72AFE5EDF,
+    0xACEE7290F5BDF697,
+    0xD82A0F35332D743D,
+    0x871A49813FFC68A6,
+    0xA8E0DBE18FFB82D0,
+    0xD31912D9F3FA6384,
+    0x83EFABC8387C7E32,
+    0xA4EB96BA469B9DBF,
+    0xCE267C68D842852E,
+    0x80D80DC18729933D,
+    0xA10E1131E8F3F80C,
+    0xC951957E6330F60F,
+    0xFBA5FADDFBFD3393,
+    0x9D47BCCABD7E403C,
+    0xC499ABFD6CDDD04B,
+    0xF5C016FCC815445E,
+    0x99980E5DFD0D4ABB,
+    0xBFFE11F57C509D69,
+    0xEFFD9672DB64C4C4,
+    0x95FE7E07C91EFAFA,
+    0xBB7E1D89BB66B9B9,
+    0xEA5DA4EC2A406827,
+    0x927A87139A684118,
+    0xB71928D88102515E,
+    0xE4DF730EA142E5B6,
+    0x8F0BA7E924C9CF92,
+    0xB2CE91E36DFC4376,
+    0xDF82365C497B5454,
+    0x8BB161F9ADED14B4,
+    0xAE9DBA78196859E1,
+    0xDA4529161FC2705A,
+    0x886B39ADD3D98638,
+    0xAA86081948CFE7C6,
+    0xD5278A1F9B03E1B8,
+    0x8538B653C0E26D13,
+    0xA686E3E8B11B0858,
+    0xD0289CE2DD61CA6D,
+    0x8219620DCA5D1E84,
+    0xA29FBA913CF46625,
+    0xCB47A9358C317FAF,
+    0xFE199382EF3DDF9B,
+    0x9ECFFC31D586ABC1,
+    0xC683FB3E4AE856B1,
+    0xF824FA0DDDA26C5D,
+    0x9B171C48AA8583BA,
+    0xC1DCE35AD526E4A9,
+    0xF2541C318A709DD3,
+    0x9774919EF68662A4,
+    0xBD51B606B427FB4D,
+    0xECA623886131FA20,
+    0x93E7D6353CBF3C54,
+    0xB8E1CBC28BEF0B69,
+    0xE71A3EB32EEACE43,
+    0x9070672FFD52C0EA,
+    0xB48C80FBFCA77124,
+    0xE1AFA13AFBD14D6E,
+    0x8D0DC4C4DD62D064,
+    0xB05135F614BB847E,
+    0xDC65837399EA659D,
+    0x89BF722840327F82,
+    0xAC2F4EB2503F1F63,
+    0xD73B225EE44EE73B,
+    0x8684F57B4EB15085,
+    0xA82632DA225DA4A6,
+    0xD22FBF90AAF50DD0,
+    0x835DD7BA6AD928A2,
+    0xA4354DA9058F72CA,
+    0xCD42A11346F34F7D,
+    0x8049A4AC0C5811AE,
+    0xA05C0DD70F6E161A,
+    0xC873114CD3499BA0,
+    0xFA8FD5A0081C0288,
+    0x9C99E58405118195,
+    0xC3C05EE50655E1FA,
+    0xF4B0769E47EB5A79,
+    0x98EE4A22ECF3188C,
+    0xBF29DCABA82FDEAE,
+    0xEEF453D6923BD65A,
+    0x9558B4661B6565F8,
+    0xBAAEE17FA23EBF76,
+    0xE95A99DF8ACE6F54,
+    0x91D8A02BB6C10594,
+    0xB64EC836A47146FA,
+    0xE3E27A444D8D98B8,
+    0x8E6D8C6AB0787F73,
+    0xB208EF855C969F50,
+    0xDE8B2B66B3BC4724,
+    0x8B16FB203055AC76,
+    0xADDCB9E83C6B1794,
+    0xD953E8624B85DD79,
+    0x87D4713D6F33AA6C,
+    0xA9C98D8CCB009506,
+    0xD43BF0EFFDC0BA48,
+    0x84A57695FE98746D,
+    0xA5CED43B7E3E9188,
+    0xCF42894A5DCE35EA,
+    0x818995CE7AA0E1B2,
+    0xA1EBFB4219491A1F,
+    0xCA66FA129F9B60A7,
+    0xFD00B897478238D1,
+    0x9E20735E8CB16382,
+    0xC5A890362FDDBC63,
+    0xF712B443BBD52B7C,
+    0x9A6BB0AA55653B2D,
+    0xC1069CD4EABE89F9,
+    0xF148440A256E2C77,
+    0x96CD2A865764DBCA,
+    0xBC807527ED3E12BD,
+    0xEBA09271E88D976C,
+    0x93445B8731587EA3,
+    0xB8157268FDAE9E4C,
+    0xE61ACF033D1A45DF,
+    0x8FD0C16206306BAC,
+    0xB3C4F1BA87BC8697,
+    0xE0B62E2929ABA83C,
+    0x8C71DCD9BA0B4926,
+    0xAF8E5410288E1B6F,
+    0xDB71E91432B1A24B,
+    0x892731AC9FAF056F,
+    0xAB70FE17C79AC6CA,
+    0xD64D3D9DB981787D,
+    0x85F0468293F0EB4E,
+    0xA76C582338ED2622,
+    0xD1476E2C07286FAA,
+    0x82CCA4DB847945CA,
+    0xA37FCE126597973D,
+    0xCC5FC196FEFD7D0C,
+    0xFF77B1FCBEBCDC4F,
+    0x9FAACF3DF73609B1,
+    0xC795830D75038C1E,
+    0xF97AE3D0D2446F25,
+    0x9BECCE62836AC577,
+    0xC2E801FB244576D5,
+    0xF3A20279ED56D48A,
+    0x9845418C345644D7,
+    0xBE5691EF416BD60C,
+    0xEDEC366B11C6CB8F,
+    0x94B3A202EB1C3F39,
+    0xB9E08A83A5E34F08,
+    0xE858AD248F5C22CA,
+    0x91376C36D99995BE,
+    0xB58547448FFFFB2E,
+    0xE2E69915B3FFF9F9,
+    0x8DD01FAD907FFC3C,
+    0xB1442798F49FFB4B,
+    0xDD95317F31C7FA1D,
+    0x8A7D3EEF7F1CFC52,
+    0xAD1C8EAB5EE43B67,
+    0xD863B256369D4A41,
+    0x873E4F75E2224E68,
+    0xA90DE3535AAAE202,
+    0xD3515C2831559A83,
+    0x8412D9991ED58092,
+    0xA5178FFF668AE0B6,
+    0xCE5D73FF402D98E4,
+    0x80FA687F881C7F8E,
+    0xA139029F6A239F72,
+    0xC987434744AC874F,
+    0xFBE9141915D7A922,
+    0x9D71AC8FADA6C9B5,
+    0xC4CE17B399107C23,
+    0xF6019DA07F549B2B,
+    0x99C102844F94E0FB,
+    0xC0314325637A193A,
+    0xF03D93EEBC589F88,
+    0x96267C7535B763B5,
+    0xBBB01B9283253CA3,
+    0xEA9C227723EE8BCB,
+    0x92A1958A7675175F,
+    0xB749FAED14125D37,
+    0xE51C79A85916F485,
+    0x8F31CC0937AE58D3,
+    0xB2FE3F0B8599EF08,
+    0xDFBDCECE67006AC9,
+    0x8BD6A141006042BE,
+    0xAECC49914078536D,
+    0xDA7F5BF590966849,
+    0x888F99797A5E012D,
+    0xAAB37FD7D8F58179,
+    0xD5605FCDCF32E1D7,
+    0x855C3BE0A17FCD26,
+    0xA6B34AD8C9DFC070,
+    0xD0601D8EFC57B08C,
+    0x823C12795DB6CE57,
+    0xA2CB1717B52481ED,
+    0xCB7DDCDDA26DA269,
+    0xFE5D54150B090B03,
+    0x9EFA548D26E5A6E2,
+    0xC6B8E9B0709F109A,
+    0xF867241C8CC6D4C1,
+    0x9B407691D7FC44F8,
+    0xC21094364DFB5637,
+    0xF294B943E17A2BC4,
+    0x979CF3CA6CEC5B5B,
+    0xBD8430BD08277231,
+    0xECE53CEC4A314EBE,
+    0x940F4613AE5ED137,
+    0xB913179899F68584,
+    0xE757DD7EC07426E5,
+    0x9096EA6F3848984F,
+    0xB4BCA50B065ABE63,
+    0xE1EBCE4DC7F16DFC,
+    0x8D3360F09CF6E4BD,
+    0xB080392CC4349DED,
+    0xDCA04777F541C568,
+    0x89E42CAAF9491B61,
+    0xAC5D37D5B79B6239,
+    0xD77485CB25823AC7,
+    0x86A8D39EF77164BD,
+    0xA8530886B54DBDEC,
+    0xD267CAA862A12D67,
+    0x8380DEA93DA4BC60,
+    0xA46116538D0DEB78,
+    0xCD795BE870516656,
+    0x806BD9714632DFF6,
+    0xA086CFCD97BF97F4,
+    0xC8A883C0FDAF7DF0,
+    0xFAD2A4B13D1B5D6C,
+    0x9CC3A6EEC6311A64,
+    0xC3F490AA77BD60FD,
+    0xF4F1B4D515ACB93C,
+    0x991711052D8BF3C5,
+    0xBF5CD54678EEF0B7,
+    0xEF340A98172AACE5,
+    0x9580869F0E7AAC0F,
+    0xBAE0A846D2195713,
+    0xE998D258869FACD7,
+    0x91FF83775423CC06,
+    0xB67F6455292CBF08,
+    0xE41F3D6A7377EECA,
+    0x8E938662882AF53E,
+    0xB23867FB2A35B28E,
+    0xDEC681F9F4C31F31,
+    0x8B3C113C38F9F37F,
+    0xAE0B158B4738705F,
+    0xD98DDAEE19068C76,
+    0x87F8A8D4CFA417CA,
+    0xA9F6D30A038D1DBC,
+    0xD47487CC8470652B,
+    0x84C8D4DFD2C63F3B,
+    0xA5FB0A17C777CF0A,
+    0xCF79CC9DB955C2CC,
+    0x81AC1FE293D599C0,
+    0xA21727DB38CB0030,
+    0xCA9CF1D206FDC03C,
+    0xFD442E4688BD304B,
+    0x9E4A9CEC15763E2F,
+    0xC5DD44271AD3CDBA,
+    0xF7549530E188C129,
+    0x9A94DD3E8CF578BA,
+    0xC13A148E3032D6E8,
+    0xF18899B1BC3F8CA2,
+    0x96F5600F15A7B7E5,
+    0xBCB2B812DB11A5DE,
+    0xEBDF661791D60F56,
+    0x936B9FCEBB25C996,
+    0xB84687C269EF3BFB,
+    0xE65829B3046B0AFA,
+    0x8FF71A0FE2C2E6DC,
+    0xB3F4E093DB73A093,
+    0xE0F218B8D25088B8,
+    0x8C974F7383725573,
+    0xAFBD2350644EEAD0,
+    0xDBAC6C247D62A584,
+    0x894BC396CE5DA772,
+    0xAB9EB47C81F5114F,
+    0xD686619BA27255A3,
+    0x8613FD0145877586,
+    0xA798FC4196E952E7,
+    0xD17F3B51FCA3A7A1,
+    0x82EF85133DE648C5,
+    0xA3AB66580D5FDAF6,
+    0xCC963FEE10B7D1B3,
+    0xFFBBCFE994E5C620,
+    0x9FD561F1FD0F9BD4,
+    0xC7CABA6E7C5382C9,
+    0xF9BD690A1B68637B,
+    0x9C1661A651213E2D,
+    0xC31BFA0FE5698DB8,
+    0xF3E2F893DEC3F126,
+    0x986DDB5C6B3A76B8,
+    0xBE89523386091466,
+    0xEE2BA6C0678B597F,
+    0x94DB483840B717F0,
+    0xBA121A4650E4DDEC,
+    0xE896A0D7E51E1566,
+    0x915E2486EF32CD60,
+    0xB5B5ADA8AAFF80B8,
+    0xE3231912D5BF60E6,
+    0x8DF5EFABC5979C90,
+    0xB1736B96B6FD83B4,
+    0xDDD0467C64BCE4A1,
+    0x8AA22C0DBEF60EE4,
+    0xAD4AB7112EB3929E,
+    0xD89D64D57A607745,
+    0x87625F056C7C4A8B,
+    0xA93AF6C6C79B5D2E,
+    0xD389B47879823479,
+    0x843610CB4BF160CC,
+    0xA54394FE1EEDB8FF,
+    0xCE947A3DA6A9273E,
+    0x811CCC668829B887,
+    0xA163FF802A3426A9,
+    0xC9BCFF6034C13053,
+    0xFC2C3F3841F17C68,
+    0x9D9BA7832936EDC1,
+    0xC5029163F384A931,
+    0xF64335BCF065D37D,
+    0x99EA0196163FA42E,
+    0xC06481FB9BCF8D3A,
+    0xF07DA27A82C37088,
+    0x964E858C91BA2655,
+    0xBBE226EFB628AFEB,
+    0xEADAB0ABA3B2DBE5,
+    0x92C8AE6B464FC96F,
+    0xB77ADA0617E3BBCB,
+    0xE55990879DDCAABE,
+    0x8F57FA54C2A9EAB7,
+    0xB32DF8E9F3546564,
+    0xDFF9772470297EBD,
+    0x8BFBEA76C619EF36,
+    0xAEFAE51477A06B04,
+    0xDAB99E59958885C5,
+    0x88B402F7FD75539B,
+    0xAAE103B5FCD2A882,
+    0xD59944A37C0752A2,
+    0x857FCAE62D8493A5,
+    0xA6DFBD9FB8E5B88F,
+    0xD097AD07A71F26B2,
+    0x825ECC24C8737830,
+    0xA2F67F2DFA90563B,
+    0xCBB41EF979346BCA,
+    0xFEA126B7D78186BD,
+    0x9F24B832E6B0F436,
+    0xC6EDE63FA05D3144,
+    0xF8A95FCF88747D94,
+    0x9B69DBE1B548CE7D,
+    0xC24452DA229B021C,
+    0xF2D56790AB41C2A3,
+    0x97C560BA6B0919A6,
+    0xBDB6B8E905CB600F,
+    0xED246723473E3813,
+    0x9436C0760C86E30C,
+    0xB94470938FA89BCF,
+    0xE7958CB87392C2C3,
+    0x90BD77F3483BB9BA,
+    0xB4ECD5F01A4AA828,
+    0xE2280B6C20DD5232,
+    0x8D590723948A535F,
+    0xB0AF48EC79ACE837,
+    0xDCDB1B2798182245,
+    0x8A08F0F8BF0F156B,
+    0xAC8B2D36EED2DAC6,
+    0xD7ADF884AA879177,
+    0x86CCBB52EA94BAEB,
+    0xA87FEA27A539E9A5,
+    0xD29FE4B18E88640F,
+    0x83A3EEEEF9153E89,
+    0xA48CEAAAB75A8E2B,
+    0xCDB02555653131B6,
+    0x808E17555F3EBF12,
+    0xA0B19D2AB70E6ED6,
+    0xC8DE047564D20A8C,
+    0xFB158592BE068D2F,
+    0x9CED737BB6C4183D,
+    0xC428D05AA4751E4D,
+    0xF53304714D9265E0,
+    0x993FE2C6D07B7FAC,
+    0xBF8FDB78849A5F97,
+    0xEF73D256A5C0F77D,
+    0x95A8637627989AAE,
+    0xBB127C53B17EC159,
+    0xE9D71B689DDE71B0,
+    0x9226712162AB070E,
+    0xB6B00D69BB55C8D1,
+    0xE45C10C42A2B3B06,
+    0x8EB98A7A9A5B04E3,
+    0xB267ED1940F1C61C,
+    0xDF01E85F912E37A3,
+    0x8B61313BBABCE2C6,
+    0xAE397D8AA96C1B78,
+    0xD9C7DCED53C72256,
+    0x881CEA14545C7575,
+    0xAA242499697392D3,
+    0xD4AD2DBFC3D07788,
+    0x84EC3C97DA624AB5,
+    0xA6274BBDD0FADD62,
+    0xCFB11EAD453994BA,
+    0x81CEB32C4B43FCF5,
+    0xA2425FF75E14FC32,
+    0xCAD2F7F5359A3B3E,
+    0xFD87B5F28300CA0E,
+    0x9E74D1B791E07E48,
+    0xC612062576589DDB,
+    0xF79687AED3EEC551,
+    0x9ABE14CD44753B53,
+    0xC16D9A0095928A27,
+    0xF1C90080BAF72CB1,
+    0x971DA05074DA7BEF,
+    0xBCE5086492111AEB,
+    0xEC1E4A7DB69561A5,
+    0x9392EE8E921D5D07,
+    0xB877AA3236A4B449,
+    0xE69594BEC44DE15B,
+    0x901D7CF73AB0ACD9,
+    0xB424DC35095CD80F,
+    0xE12E13424BB40E13,
+    0x8CBCCC096F5088CC,
+    0xAFEBFF0BCB24AAFF,
+    0xDBE6FECEBDEDD5BF,
+    0x89705F4136B4A597,
+    0xABCC77118461CEFD,
+    0xD6BF94D5E57A42BC,
+    0x8637BD05AF6C69B6,
+    0xA7C5AC471B478423,
+    0xD1B71758E219652C,
+    0x83126E978D4FDF3B,
+    0xA3D70A3D70A3D70A,
+    0xCCCCCCCCCCCCCCCD,
+    0x8000000000000000,
+    0xA000000000000000,
+    0xC800000000000000,
+    0xFA00000000000000,
+    0x9C40000000000000,
+    0xC350000000000000,
+    0xF424000000000000,
+    0x9896800000000000,
+    0xBEBC200000000000,
+    0xEE6B280000000000,
+    0x9502F90000000000,
+    0xBA43B74000000000,
+    0xE8D4A51000000000,
+    0x9184E72A00000000,
+    0xB5E620F480000000,
+    0xE35FA931A0000000,
+    0x8E1BC9BF04000000,
+    0xB1A2BC2EC5000000,
+    0xDE0B6B3A76400000,
+    0x8AC7230489E80000,
+    0xAD78EBC5AC620000,
+    0xD8D726B7177A8000,
+    0x878678326EAC9000,
+    0xA968163F0A57B400,
+    0xD3C21BCECCEDA100,
+    0x84595161401484A0,
+    0xA56FA5B99019A5C8,
+    0xCECB8F27F4200F3A,
+    0x813F3978F8940984,
+    0xA18F07D736B90BE5,
+    0xC9F2C9CD04674EDF,
+    0xFC6F7C4045812296,
+    0x9DC5ADA82B70B59E,
+    0xC5371912364CE305,
+    0xF684DF56C3E01BC7,
+    0x9A130B963A6C115C,
+    0xC097CE7BC90715B3,
+    0xF0BDC21ABB48DB20,
+    0x96769950B50D88F4,
+    0xBC143FA4E250EB31,
+    0xEB194F8E1AE525FD,
+    0x92EFD1B8D0CF37BE,
+    0xB7ABC627050305AE,
+    0xE596B7B0C643C719,
+    0x8F7E32CE7BEA5C70,
+    0xB35DBF821AE4F38C,
+    0xE0352F62A19E306F,
+    0x8C213D9DA502DE45,
+    0xAF298D050E4395D7,
+    0xDAF3F04651D47B4C,
+    0x88D8762BF324CD10,
+    0xAB0E93B6EFEE0054,
+    0xD5D238A4ABE98068,
+    0x85A36366EB71F041,
+    0xA70C3C40A64E6C52,
+    0xD0CF4B50CFE20766,
+    0x82818F1281ED44A0,
+    0xA321F2D7226895C8,
+    0xCBEA6F8CEB02BB3A,
+    0xFEE50B7025C36A08,
+    0x9F4F2726179A2245,
+    0xC722F0EF9D80AAD6,
+    0xF8EBAD2B84E0D58C,
+    0x9B934C3B330C8577,
+    0xC2781F49FFCFA6D5,
+    0xF316271C7FC3908B,
+    0x97EDD871CFDA3A57,
+    0xBDE94E8E43D0C8EC,
+    0xED63A231D4C4FB27,
+    0x945E455F24FB1CF9,
+    0xB975D6B6EE39E437,
+    0xE7D34C64A9C85D44,
+    0x90E40FBEEA1D3A4B,
+    0xB51D13AEA4A488DD,
+    0xE264589A4DCDAB15,
+    0x8D7EB76070A08AED,
+    0xB0DE65388CC8ADA8,
+    0xDD15FE86AFFAD912,
+    0x8A2DBF142DFCC7AB,
+    0xACB92ED9397BF996,
+    0xD7E77A8F87DAF7FC,
+    0x86F0AC99B4E8DAFD,
+    0xA8ACD7C0222311BD,
+    0xD2D80DB02AABD62C,
+    0x83C7088E1AAB65DB,
+    0xA4B8CAB1A1563F52,
+    0xCDE6FD5E09ABCF27,
+    0x80B05E5AC60B6178,
+    0xA0DC75F1778E39D6,
+    0xC913936DD571C84C,
+    0xFB5878494ACE3A5F,
+    0x9D174B2DCEC0E47B,
+    0xC45D1DF942711D9A,
+    0xF5746577930D6501,
+    0x9968BF6ABBE85F20,
+    0xBFC2EF456AE276E9,
+    0xEFB3AB16C59B14A3,
+    0x95D04AEE3B80ECE6,
+    0xBB445DA9CA61281F,
+    0xEA1575143CF97227,
+    0x924D692CA61BE758,
+    0xB6E0C377CFA2E12E,
+    0xE498F455C38B997A,
+    0x8EDF98B59A373FEC,
+    0xB2977EE300C50FE7,
+    0xDF3D5E9BC0F653E1,
+    0x8B865B215899F46D,
+    0xAE67F1E9AEC07188,
+    0xDA01EE641A708DEA,
+    0x884134FE908658B2,
+    0xAA51823E34A7EEDF,
+    0xD4E5E2CDC1D1EA96,
+    0x850FADC09923329E,
+    0xA6539930BF6BFF46,
+    0xCFE87F7CEF46FF17,
+    0x81F14FAE158C5F6E,
+    0xA26DA3999AEF774A,
+    0xCB090C8001AB551C,
+    0xFDCB4FA002162A63,
+    0x9E9F11C4014DDA7E,
+    0xC646D63501A1511E,
+    0xF7D88BC24209A565,
+    0x9AE757596946075F,
+    0xC1A12D2FC3978937,
+    0xF209787BB47D6B85,
+    0x9745EB4D50CE6333,
+    0xBD176620A501FC00,
+    0xEC5D3FA8CE427B00,
+    0x93BA47C980E98CE0,
+    0xB8A8D9BBE123F018,
+    0xE6D3102AD96CEC1E,
+    0x9043EA1AC7E41393,
+    0xB454E4A179DD1877,
+    0xE16A1DC9D8545E95,
+    0x8CE2529E2734BB1D,
+    0xB01AE745B101E9E4,
+    0xDC21A1171D42645D,
+    0x899504AE72497EBA,
+    0xABFA45DA0EDBDE69,
+    0xD6F8D7509292D603,
+    0x865B86925B9BC5C2,
+    0xA7F26836F282B733,
+    0xD1EF0244AF2364FF,
+    0x8335616AED761F1F,
+    0xA402B9C5A8D3A6E7,
+    0xCD036837130890A1,
+    0x802221226BE55A65,
+    0xA02AA96B06DEB0FE,
+    0xC83553C5C8965D3D,
+    0xFA42A8B73ABBF48D,
+    0x9C69A97284B578D8,
+    0xC38413CF25E2D70E,
+    0xF46518C2EF5B8CD1,
+    0x98BF2F79D5993803,
+    0xBEEEFB584AFF8604,
+    0xEEAABA2E5DBF6785,
+    0x952AB45CFA97A0B3,
+    0xBA756174393D88E0,
+    0xE912B9D1478CEB17,
+    0x91ABB422CCB812EF,
+    0xB616A12B7FE617AA,
+    0xE39C49765FDF9D95,
+    0x8E41ADE9FBEBC27D,
+    0xB1D219647AE6B31C,
+    0xDE469FBD99A05FE3,
+    0x8AEC23D680043BEE,
+    0xADA72CCC20054AEA,
+    0xD910F7FF28069DA4,
+    0x87AA9AFF79042287,
+    0xA99541BF57452B28,
+    0xD3FA922F2D1675F2,
+    0x847C9B5D7C2E09B7,
+    0xA59BC234DB398C25,
+    0xCF02B2C21207EF2F,
+    0x8161AFB94B44F57D,
+    0xA1BA1BA79E1632DC,
+    0xCA28A291859BBF93,
+    0xFCB2CB35E702AF78,
+    0x9DEFBF01B061ADAB,
+    0xC56BAEC21C7A1916,
+    0xF6C69A72A3989F5C,
+    0x9A3C2087A63F6399,
+    0xC0CB28A98FCF3C80,
+    0xF0FDF2D3F3C30B9F,
+    0x969EB7C47859E744,
+    0xBC4665B596706115,
+    0xEB57FF22FC0C795A,
+    0x9316FF75DD87CBD8,
+    0xB7DCBF5354E9BECE,
+    0xE5D3EF282A242E82,
+    0x8FA475791A569D11,
+    0xB38D92D760EC4455,
+    0xE070F78D3927556B,
+    0x8C469AB843B89563,
+    0xAF58416654A6BABB,
+    0xDB2E51BFE9D0696A,
+    0x88FCF317F22241E2,
+    0xAB3C2FDDEEAAD25B,
+    0xD60B3BD56A5586F2,
+    0x85C7056562757457,
+    0xA738C6BEBB12D16D,
+    0xD106F86E69D785C8,
+    0x82A45B450226B39D,
+    0xA34D721642B06084,
+    0xCC20CE9BD35C78A5,
+    0xFF290242C83396CE,
+    0x9F79A169BD203E41,
+    0xC75809C42C684DD1,
+    0xF92E0C3537826146,
+    0x9BBCC7A142B17CCC,
+    0xC2ABF989935DDBFE,
+    0xF356F7EBF83552FE,
+    0x98165AF37B2153DF,
+    0xBE1BF1B059E9A8D6,
+    0xEDA2EE1C7064130C,
+    0x9485D4D1C63E8BE8,
+    0xB9A74A0637CE2EE1,
+    0xE8111C87C5C1BA9A,
+    0x910AB1D4DB9914A0,
+    0xB54D5E4A127F59C8,
+    0xE2A0B5DC971F303A,
+    0x8DA471A9DE737E24,
+    0xB10D8E1456105DAD,
+    0xDD50F1996B947519,
+    0x8A5296FFE33CC930,
+    0xACE73CBFDC0BFB7B,
+    0xD8210BEFD30EFA5A,
+    0x8714A775E3E95C78,
+    0xA8D9D1535CE3B396,
+    0xD31045A8341CA07C,
+    0x83EA2B892091E44E,
+    0xA4E4B66B68B65D61,
+    0xCE1DE40642E3F4B9,
+    0x80D2AE83E9CE78F4,
+    0xA1075A24E4421731,
+    0xC94930AE1D529CFD,
+    0xFB9B7CD9A4A7443C,
+    0x9D412E0806E88AA6,
+    0xC491798A08A2AD4F,
+    0xF5B5D7EC8ACB58A3,
+    0x9991A6F3D6BF1766,
+    0xBFF610B0CC6EDD3F,
+    0xEFF394DCFF8A948F,
+    0x95F83D0A1FB69CD9,
+    0xBB764C4CA7A44410,
+    0xEA53DF5FD18D5514,
+    0x92746B9BE2F8552C,
+    0xB7118682DBB66A77,
+    0xE4D5E82392A40515,
+    0x8F05B1163BA6832D,
+    0xB2C71D5BCA9023F8,
+    0xDF78E4B2BD342CF7,
+    0x8BAB8EEFB6409C1A,
+    0xAE9672ABA3D0C321,
+    0xDA3C0F568CC4F3E9,
+    0x8865899617FB1871,
+    0xAA7EEBFB9DF9DE8E,
+    0xD51EA6FA85785631,
+    0x8533285C936B35DF,
+    0xA67FF273B8460357,
+    0xD01FEF10A657842C,
+    0x8213F56A67F6B29C,
+    0xA298F2C501F45F43,
+    0xCB3F2F7642717713,
+    0xFE0EFB53D30DD4D8,
+    0x9EC95D1463E8A507,
+    0xC67BB4597CE2CE49,
+    0xF81AA16FDC1B81DB,
+    0x9B10A4E5E9913129,
+    0xC1D4CE1F63F57D73,
+    0xF24A01A73CF2DCD0,
+    0x976E41088617CA02,
+    0xBD49D14AA79DBC82,
+    0xEC9C459D51852BA3,
+    0x93E1AB8252F33B46,
+    0xB8DA1662E7B00A17,
+    0xE7109BFBA19C0C9D,
+    0x906A617D450187E2,
+    0xB484F9DC9641E9DB,
+    0xE1A63853BBD26451,
+    0x8D07E33455637EB3,
+    0xB049DC016ABC5E60,
+    0xDC5C5301C56B75F7,
+    0x89B9B3E11B6329BB,
+    0xAC2820D9623BF429,
+    0xD732290FBACAF134,
+    0x867F59A9D4BED6C0,
+    0xA81F301449EE8C70,
+    0xD226FC195C6A2F8C,
+    0x83585D8FD9C25DB8,
+    0xA42E74F3D032F526,
+    0xCD3A1230C43FB26F,
+    0x80444B5E7AA7CF85,
+    0xA0555E361951C367,
+    0xC86AB5C39FA63441,
+    0xFA856334878FC151,
+    0x9C935E00D4B9D8D2,
+    0xC3B8358109E84F07,
+    0xF4A642E14C6262C9,
+    0x98E7E9CCCFBD7DBE,
+    0xBF21E44003ACDD2D,
+    0xEEEA5D5004981478,
+    0x95527A5202DF0CCB,
+    0xBAA718E68396CFFE,
+    0xE950DF20247C83FD,
+    0x91D28B7416CDD27E,
+    0xB6472E511C81471E,
+    0xE3D8F9E563A198E5,
+    0x8E679C2F5E44FF8F,
+    0xB201833B35D63F73,
+    0xDE81E40A034BCF50,
+    0x8B112E86420F6192,
+    0xADD57A27D29339F6,
+    0xD94AD8B1C7380874,
+    0x87CEC76F1C830549,
+    0xA9C2794AE3A3C69B,
+    0xD433179D9C8CB841,
+    0x849FEEC281D7F329,
+    0xA5C7EA73224DEFF3,
+    0xCF39E50FEAE16BF0,
+    0x81842F29F2CCE376,
+    0xA1E53AF46F801C53,
+    0xCA5E89B18B602368,
+    0xFCF62C1DEE382C42,
+    0x9E19DB92B4E31BA9,
+    0xC5A05277621BE294,
+    0xF70867153AA2DB39,
+    0x9A65406D44A5C903,
+    0xC0FE908895CF3B44,
+    0xF13E34AABB430A15,
+    0x96C6E0EAB509E64D,
+    0xBC789925624C5FE1,
+    0xEB96BF6EBADF77D9,
+    0x933E37A534CBAAE8,
+    0xB80DC58E81FE95A1,
+    0xE61136F2227E3B0A,
+    0x8FCAC257558EE4E6,
+    0xB3BD72ED2AF29E20,
+    0xE0ACCFA875AF45A8,
+    0x8C6C01C9498D8B89,
+    0xAF87023B9BF0EE6B,
+    0xDB68C2CA82ED2A06,
+    0x892179BE91D43A44,
+    0xAB69D82E364948D4,
+    0xD6444E39C3DB9B0A,
+    0x85EAB0E41A6940E6,
+    0xA7655D1D2103911F,
+    0xD13EB46469447567,
+    0x82C730BEC1CAC961,
+    0xA378FCEE723D7BB9,
+    0xCC573C2A0ECCDAA7,
+    0xFF6D0B3492801151,
+    0x9FA42700DB900AD2,
+    0xC78D30C112740D87,
+    0xF9707CF1571110E9,
+    0x9BE64E16D66AAA91,
+    0xC2DFE19C8C055536,
+    0xF397DA03AF06AA83,
+    0x983EE8424D642A92,
+    0xBE4EA252E0BD3537,
+    0xEDE24AE798EC8284,
+    0x94AD6ED0BF93D193,
+    0xB9D8CA84EF78C5F7,
+    0xE84EFD262B56F775,
+    0x91315E37DB165AA9,
+    0xB57DB5C5D1DBF153,
+    0xE2DD23374652EDA8,
+    0x8DCA36028BF3D489,
+    0xB13CC3832EF0C9AC,
+    0xDD8BF463FAACFC16,
+    0x8A7778BE7CAC1D8E,
+    0xAD1556EE1BD724F1,
+    0xD85AACA9A2CCEE2E,
+    0x8738ABEA05C014DD,
+    0xA906D6E487301A14,
+    0xD3488C9DA8FC2099,
+    0x840D57E2899D945F,
+    0xA510ADDB2C04F977,
+    0xCE54D951F70637D5,
+    0x80F507D33A63E2E5,
+    0xA13249C808FCDB9F,
+    0xC97EDC3A0B3C1286,
+    0xFBDE93488E0B1728,
+    0x9D6B1C0D58C6EE79,
+    0xC4C5E310AEF8AA17,
+    0xF5F75BD4DAB6D49D,
+    0x99BA996508B244E2,
+    0xC0293FBE4ADED61B,
+    0xF0338FADDD968BA1,
+    0x962039CCAA7E1745,
+    0xBBA8483FD51D9D16,
+    0xEA925A4FCA65045B,
+    0x929B7871DE7F22B9,
+    0xB742568E561EEB67,
+    0xE512EC31EBA6A641,
+    0x8F2BD39F334827E9,
+    0xB2F6C887001A31E3,
+    0xDFB47AA8C020BE5C,
+    0x8BD0CCA9781476F9,
+    0xAEC4FFD3D61994B8,
+    0xDA763FC8CB9FF9E6,
+    0x8889E7DD7F43FC2F,
+    0xAAAC61D4DF14FB3B,
+    0xD5577A4A16DA3A0A,
+    0x8556AC6E4E486446,
+    0xA6AC5789E1DA7D58,
+    0xD0576D6C5A511CAE,
+    0x8236A463B872B1ED,
+    0xA2C44D7CA68F5E68,
+    0xCB7560DBD0333602,
+    0xFE52B912C4400382,
+    0x9EF3B3ABBAA80231,
+    0xC6B0A096A95202BE,
+    0xF85CC8BC53A6836D,
+    0x9B39FD75B4481224,
+    0xC2087CD3215A16AD,
+    0xF28A9C07E9B09C59,
+    0x9796A184F20E61B7,
+    0xBD7C49E62E91FA25,
+    0xECDB5C5FBA3678AF,
+    0x940919BBD4620B6D,
+    0xB90B602AC97A8E48,
+    0xE74E38357BD931DB,
+    0x9090E3216D67BF29,
+    0xB4B51BE9C8C1AEF3,
+    0xE1E262E43AF21AAF,
+    0x8D2D7DCEA4D750AE,
+    0xB078DD424E0D24D9,
+    0xDC971492E1906E0F,
+    0x89DE6CDBCCFA44CA,
+    0xAC560812C038D5FC,
+    0xD76B8A1770470B7B,
+    0x86A3364EA62C672D,
+    0xA84C03E24FB780F8,
+    0xD25F04DAE3A56136,
+    0x837B6308CE475CC2,
+    0xA45A3BCB01D933F2,
+    0xCD70CABDC24F80EF,
+    0x80667EB69971B095,
+    0xA0801E643FCE1CBB,
+    0xC8A025FD4FC1A3E9,
+    0xFAC82F7CA3B20CE4,
+    0x9CBD1DADE64F480E,
+    0xC3EC65195FE31A12,
+    0xF4E77E5FB7DBE096,
+    0x9910AEFBD2E96C5E,
+    0xBF54DABAC7A3C775,
+    0xEF2A1169798CB953,
+    0x957A4AE1EBF7F3D4,
+    0xBAD8DD9A66F5F0C9,
+    0xE98F150100B36CFB,
+    0x91F96D20A070241D,
+    0xB677C868C88C2D24,
+    0xE415BA82FAAF386D,
+    0x8E8D9491DCAD8344,
+    0xB230F9B653D8E415,
+    0xDEBD3823E8CF1D1A,
+    0x8B36431671817230,
+    0xAE03D3DC0DE1CEBD,
+    0xD984C8D3115A426C,
+    0x87F2FD83EAD86983,
+    0xA9EFBCE4E58E83E4,
+    0xD46BAC1E1EF224DD,
+    0x84C34B92D357570A,
+    0xA5F41E77882D2CCD,
+    0xCF7126156A387800,
+    0x81A6B7CD62634B00,
+    0xA21065C0BAFC1DC0,
+    0xCA947F30E9BB2530,
+    0xFD399EFD2429EE7C,
+    0x9E44035E369A350D,
+    0xC5D50435C440C251,
+    0xF74A45433550F2E5,
+    0x9A8E6B4A015297CF,
+    0xC132061C81A73DC3,
+    0xF17E87A3A2110D34,
+    0x96EF14C6454AA840,
+    0xBCAAD9F7D69D5250,
+    0xEBD59075CC44A6E4,
+    0x93657A499FAAE84F,
+    0xB83ED8DC0795A262,
+    0xE64E8F13097B0AFB,
+    0x8FF1196BE5ECE6DD,
+    0xB3ED5FC6DF682094,
+    0xE0E8B7B8974228B9,
+    0x8C9172D35E895974,
+    0xAFB5CF88362BAFD1,
+    0xDBA3436A43B69BC5,
+    0x89460A226A52215B,
+    0xAB978CAB04E6A9B2,
+    0xD67D6FD5C620541E,
+    0x860E65E59BD43493,
+    0xA791FF5F02C941B8,
+    0xD1767F36C37B9226,
+    0x82EA0F823A2D3B57,
+    0xA3A49362C8B88A2D,
+    0xCC8DB83B7AE6ACB9,
+    0xFFB1264A59A057E7,
+    0x9FCEB7EE780436F0,
+    0xC7C265EA160544AC,
+    0xF9B2FF649B8695D7,
+    0x9C0FDF9EE1341DA7,
+    0xC313D78699812510,
+    0xF3D8CD683FE16E54,
+    0x9867806127ECE4F5,
+    0xBE81607971E81E32,
+    0xEE21B897CE6225BE,
+    0x94D5135EE0FD5797,
+    0xBA0A5836993CAD7D,
+    0xE88CEE443F8BD8DC,
+    0x915814EAA7B76789,
+    0xB5AE1A2551A5416C,
+    0xE319A0AEA60E91C7,
+];
+
+static if (real.mant_dig >= 64)
+static immutable align(16) ulong[1025] p10_coefficients_l = [
+    0x7132D332E3F204D5,
+    0x8D7F87FF9CEE860A,
+    0x70DF69FF842A278D,
+    0xC68BA23FB29A58B8,
+    0xB82E8ACF9F40EEE6,
+    0xA63A2D8387112A9F,
+    0xA7E45C72346ABAA4,
+    0xD1DD738EC185694D,
+    0xC654D07271E6C3A0,
+    0xDBF5024787303A44,
+    0x12F242D968FC48D5,
+    0x17AED38FC33B5B0A,
+    0x8ECD4439DA0518E6,
+    0x7280954850865F20,
+    0x4F20BA9A64A7F6E8,
+    0x917474A07EE8FA51,
+    0xB5D191C89EA338E5,
+    0xE345F63AC64C071E,
+    0x9C1773C977DF08E6,
+    0x218EA85DEAEB6590,
+    0x29F2527565A63EF4,
+    0xF46EE712BF0FCEB0,
+    0x18C5506BB769E12E,
+    0x1EF6A486A544597A,
+    0xA6B44DA84E956FD8,
+    0x6830B089311D65E7,
+    0x423CDCAB7D64BF61,
+    0x52CC13D65CBDEF39,
+    0xB3BF8C65F9F6B584,
+    0x20AF6F7F787462E5,
+    0x68DB4B5F56917B9E,
+    0x81890F1B961AED43,
+    0x61EB52E27BA1A893,
+    0xFA66279B1A8A12B8,
+    0x7C7FD8C0F0964BB3,
+    0x1B9FCEF12CBBDEA0,
+    0xE287C2AD77EAD648,
+    0xAD94D9AC6AF2C5ED,
+    0x98FA101785AF7768,
+    0xBF38941D671B5542,
+    0xF7835C9260711549,
+    0x756433B6F88D5A9C,
+    0x12BD40A4B6B0B143,
+    0x4BB64866F22E6ECA,
+    0xDEA3DA80AEBA0A7C,
+    0x164CD120DA688D1B,
+    0x5BE005691102B062,
+    0xB96C0361AAA1AE3D,
+    0x67C7043A154A19CC,
+    0x01B8C5489A9CA040,
+    0x01137B4D60A1E428,
+    0xC1585A20B8CA5D32,
+    0xB1AE70A8E6FCF47E,
+    0x4F0D0669905E18CF,
+    0xA2D04803F4759F02,
+    0xCB845A04F19306C3,
+    0x1F32B84316FBE43A,
+    0x66FF6653DCBADD48,
+    0x00BF3FE8D3E9949A,
+    0x807787F18471FCE1,
+    0x209569EDE58E7C19,
+    0xA8BAC4695EF21B1F,
+    0x6974BAC1DB5750F3,
+    0x03D1E972522D2530,
+    0x84C663CEE6B86E7C,
+    0xD2FBFE615033450E,
+    0x87BAFDF9A4401651,
+    0xE9A9BD780D501BE5,
+    0x520A166B0852116F,
+    0xE68C9C05CA6695CB,
+    0xA02FC3073D003B3E,
+    0x241DD9E486202507,
+    0x6D25505DA7A82E48,
+    0x886EA475119239DA,
+    0xEA8A4D9255F6C851,
+    0xD296707B75BA3D33,
+    0x073C0C9A5328CC7F,
+    0xC90B0FC0E7F2FF9F,
+    0x3DA6E9D890F7DFC3,
+    0x0D10A44EB535D7B4,
+    0xD054CD6262834DA1,
+    0xA235005D7D921085,
+    0xCAC24074DCF694A6,
+    0x3D72D092143439D0,
+    0x6667C25B4CA0A422,
+    0x4001B2F21FC8CD2A,
+    0x10021FAEA7BB0075,
+    0x8A0153CD28D4E049,
+    0x2C81A8C0730A185B,
+    0xB7A212F08FCC9E72,
+    0x12C54BD659DFE307,
+    0x97769ECBF057DBC9,
+    0xBD54467EEC6DD2BB,
+    0x5654AC0F53C4A3B5,
+    0x2BE9D71328B5CCA2,
+    0xF6E44CD7F2E33FCB,
+    0x1A4EB006F7CE07DF,
+    0xA0E25C08B5C189D7,
+    0x891AF30AE331EC4C,
+    0x35B0D7E6CDFF33B0,
+    0xC31D0DE0817F009C,
+    0x73E45158A1DEC0C2,
+    0x086EB2D7652B387A,
+    0x4A8A5F8D3E760698,
+    0x5D2CF7708E13883E,
+    0x3478354CB1986A4D,
+    0x00CB214FEEFF4270,
+    0x00FDE9A3EABF130C,
+    0xC13D640CE56ED7D0,
+    0x98C65E880F6546E2,
+    0x3EF7F62A133E989A,
+    0x8EB5F3B4980E3EC1,
+    0x3931B850DF08E738,
+    0xC77E266516CB2106,
+    0xF95DAFFE5C7DE948,
+    0x5BDA8DFEF9CEB1CD,
+    0x72D1317EB8425E40,
+    0x0F857DDE6652F5D0,
+    0xC9B36EAAFFF3D9A2,
+    0x3C204A55BFF0D00B,
+    0xCB285CEB2FED040E,
+    0x5EF93A12FDF42288,
+    0x76B78897BD712B2B,
+    0xD4656ABDACCD75F5,
+    0x24BF62B68C0069B9,
+    0x2DEF3B642F008428,
+    0xB96B0A3D3AC0A531,
+    0xD3E2E66644B8673F,
+    0x88DB9FFFD5E6810F,
+    0x6B1287FFCB602152,
+    0x62EB94FFDF1C14D3,
+    0x7BA67A3FD6E31A08,
+    0xDA9018CFCC9BE08A,
+    0x91341F03BFC2D8AD,
+    0x9AC0936257D9C76C,
+    0xC170B83AEDD03947,
+    0xF1CCE649A9444799,
+    0x17200FEE09CAACC0,
+    0x9CE813E98C3D57EF,
+    0xC42218E3EF4CADEB,
+    0xBA954F8E758FECB3,
+    0xA93AA37212F3E7E0,
+    0xD3894C4E97B0E1D8,
+    0xE435CFB11ECE8D27,
+    0xDD43439D66823071,
+    0x14941484C022BC8D,
+    0xECDC8CD2F815B5D8,
+    0x6813B007B61B234E,
+    0x82189C09A3A1EC21,
+    0x714F618606453395,
+    0x8DA339E787D6807A,
+    0xF10C086169CC2099,
+    0x16A7853CE21F945F,
+    0x9C51668C1AA77977,
+    0x4365C02F215157D5,
+    0x0A1F981D74D2D6E5,
+    0x4CA77E24D2078C9E,
+    0xDFD15DAE06896FC6,
+    0xEBE2DA8CC415E5DC,
+    0x66DB912FF51B5F53,
+    0x0092757BF2623727,
+    0x205B896D777D6279,
+    0xA8726BC8D55CBB17,
+    0x128F06BB0AB3E9DD,
+    0x1732C869CD60E454,
+    0x0E7FBD42205C8EB4,
+    0x521FAC92A873B261,
+    0xE6A797B752909EFA,
+    0x9028BED2939A635C,
+    0x7432EE873880FC33,
+    0x113FAA2906A13B40,
+    0x4AC7CA59A424C508,
+    0x5D79BCF00D2DF64A,
+    0xF4D82C2C107973DC,
+    0x79071B9B8A4BE86A,
+    0x9748E2826CDEE284,
+    0xFD1B1B2308169B25,
+    0xFE30F0F5E50E20F7,
+    0xBDBD2D335E51A935,
+    0xAD2C788035E61382,
+    0x4C3BCB5021AFCC31,
+    0xDF4ABE242A1BBF3E,
+    0xD71D6DAD34A2AF0D,
+    0x8672648C40E5AD68,
+    0x680EFDAF511F18C2,
+    0x0212BD1B2566DEF3,
+    0x014BB630F7604B58,
+    0x419EA3BD35385E2E,
+    0x52064CAC828675B9,
+    0x7343EFEBD1940994,
+    0x1014EBE6C5F90BF9,
+    0xD41A26E077774EF7,
+    0x8920B098955522B5,
+    0x55B46E5F5D5535B1,
+    0xEB2189F734AA831D,
+    0xA5E9EC7501D523E4,
+    0x47B233C92125366F,
+    0x999EC0BB696E840A,
+    0xC00670EA43CA250D,
+    0x380406926A5E5728,
+    0xC605083704F5ECF2,
+    0xF7864A44C633682F,
+    0x7AB3EE6AFBE0211D,
+    0x5960EA05BAD82965,
+    0x6FB92487298E33BE,
+    0xA5D3B6D479F8E057,
+    0x8F48A4899877186C,
+    0x331ACDABFE94DE87,
+    0x9FF0C08B7F1D0B15,
+    0x07ECF0AE5EE44DDA,
+    0xC9E82CD9F69D6150,
+    0xBE311C083A225CD2,
+    0x6DBD630A48AAF407,
+    0x092CBBCCDAD5B108,
+    0x25BBF56008C58EA5,
+    0xAF2AF2B80AF6F24E,
+    0x1AF5AF660DB4AEE2,
+    0x50D98D9FC890ED4D,
+    0xE50FF107BAB528A1,
+    0x1E53ED49A96272C9,
+    0x25E8E89C13BB0F7B,
+    0x77B191618C54E9AD,
+    0xD59DF5B9EF6A2418,
+    0x4B0573286B44AD1E,
+    0x4EE367F9430AEC33,
+    0x229C41F793CDA73F,
+    0x6B43527578C1110F,
+    0x830A13896B78AAAA,
+    0x23CC986BC656D554,
+    0x2CBFBE86B7EC8AA9,
+    0x7BF7D71432F3D6AA,
+    0xDAF5CCD93FB0CC54,
+    0xD1B3400F8F9CFF69,
+    0x23100809B9C21FA2,
+    0xABD40A0C2832A78A,
+    0x16C90C8F323F516D,
+    0xAE3DA7D97F6792E4,
+    0x99CD11CFDF41779D,
+    0x40405643D711D584,
+    0x482835EA666B2572,
+    0xDA3243650005EECF,
+    0x90BED43E40076A83,
+    0x5A7744A6E804A292,
+    0x711515D0A205CB36,
+    0x0D5A5B44CA873E04,
+    0xE858790AFE9486C2,
+    0x626E974DBE39A873,
+    0xFB0A3D212DC81290,
+    0x7CE66634BC9D0B9A,
+    0x1C1FFFC1EBC44E80,
+    0xA327FFB266B56220,
+    0x4BF1FF9F0062BAA8,
+    0x6F773FC3603DB4A9,
+    0xCB550FB4384D21D4,
+    0x7E2A53A146606A48,
+    0x2EDA7444CBFC426D,
+    0xFA911155FEFB5309,
+    0x793555AB7EBA27CB,
+    0x4BC1558B2F3458DF,
+    0x9EB1AAEDFB016F16,
+    0x465E15A979C1CADC,
+    0x0BFACD89EC191ECA,
+    0xCEF980EC671F667C,
+    0x82B7E12780E7401B,
+    0xD1B2ECB8B0908811,
+    0x861FA7E6DCB4AA15,
+    0x67A791E093E1D49A,
+    0xE0C8BB2C5C6D24E0,
+    0x58FAE9F773886E19,
+    0xAF39A475506A899F,
+    0x6D8406C952429603,
+    0xC8E5087BA6D33B84,
+    0xFB1E4A9A90880A65,
+    0x5CF2EEA09A55067F,
+    0xF42FAA48C0EA481F,
+    0xF13B94DAF124DA27,
+    0x76C53D08D6B70858,
+    0x54768C4B0C64CA6E,
+    0xA9942F5DCF7DFD0A,
+    0xD3F93B35435D7C4C,
+    0xC47BC5014A1A6DB0,
+    0x359AB6419CA1091B,
+    0xC30163D203C94B62,
+    0x79E0DE63425DCF1D,
+    0x985915FC12F542E5,
+    0x3E6F5B7B17B2939E,
+    0xA705992CEECF9C43,
+    0x50C6FF782A838353,
+    0xA4F8BF5635246428,
+    0x871B7795E136BE99,
+    0x28E2557B59846E3F,
+    0x331AEADA2FE589CF,
+    0x3FF0D2C85DEF7622,
+    0x0FED077A756B53AA,
+    0xD3E8495912C62894,
+    0x64712DD7ABBBD95D,
+    0xBD8D794D96AACFB4,
+    0xECF0D7A0FC5583A1,
+    0xF41686C49DB57245,
+    0x311C2875C522CED6,
+    0x7D633293366B828B,
+    0xAE5DFF9C02033197,
+    0xD9F57F830283FDFD,
+    0xD072DF63C324FD7C,
+    0x4247CB9E59F71E6D,
+    0x52D9BE85F074E609,
+    0x67902E276C921F8B,
+    0x00BA1CD8A3DB53B7,
+    0x80E8A40ECCD228A5,
+    0x6122CD128006B2CE,
+    0x796B805720085F81,
+    0xCBE3303674053BB1,
+    0xBEDBFC4411068A9D,
+    0xEE92FB5515482D44,
+    0x751BDD152D4D1C4B,
+    0xD262D45A78A0635D,
+    0x86FB897116C87C35,
+    0xD45D35E6AE3D4DA1,
+    0x8974836059CCA109,
+    0x2BD1A438703FC94B,
+    0x7B6306A34627DDCF,
+    0x1A3BC84C17B1D543,
+    0x20CABA5F1D9E4A94,
+    0x547EB47B7282EE9C,
+    0xE99E619A4F23AA43,
+    0x6405FA00E2EC94D4,
+    0xDE83BC408DD3DD05,
+    0x9624AB50B148D446,
+    0x3BADD624DD9B0957,
+    0xE54CA5D70A80E5D6,
+    0x5E9FCF4CCD211F4C,
+    0x7647C3200069671F,
+    0x29ECD9F40041E073,
+    0xF468107100525890,
+    0x7182148D4066EEB4,
+    0xC6F14CD848405531,
+    0xB8ADA00E5A506A7D,
+    0xA6D90811F0E4851C,
+    0x908F4A166D1DA663,
+    0x9A598E4E043287FE,
+    0x40EFF1E1853F29FE,
+    0xD12BEE59E68EF47D,
+    0x82BB74F8301958CE,
+    0xE36A52363C1FAF02,
+    0xDC44E6C3CB279AC2,
+    0x29AB103A5EF8C0B9,
+    0x7415D448F6B6F0E8,
+    0x111B495B3464AD21,
+    0xCAB10DD900BEEC35,
+    0x3D5D514F40EEA742,
+    0x0CB4A5A3112A5113,
+    0x47F0E785EABA72AC,
+    0x59ED216765690F57,
+    0x306869C13EC3532C,
+    0x1E414218C73A13FC,
+    0xE5D1929EF90898FB,
+    0xDF45F746B74ABF39,
+    0x6B8BBA8C328EB784,
+    0x066EA92F3F326565,
+    0xC80A537B0EFEFEBE,
+    0xBD06742CE95F5F37,
+    0x2C48113823B73704,
+    0xF75A15862CA504C5,
+    0x9A984D73DBE722FB,
+    0xC13E60D0D2E0EBBA,
+    0x318DF905079926A9,
+    0xFDF17746497F7053,
+    0xFEB6EA8BEDEFA634,
+    0xFE64A52EE96B8FC1,
+    0x3DFDCE7AA3C673B1,
+    0x06BEA10CA65C084F,
+    0x486E494FCFF30A62,
+    0x5A89DBA3C3EFCCFB,
+    0xF89629465A75E01D,
+    0xF6BBB397F1135824,
+    0x746AA07DED582E2D,
+    0xA8C2A44EB4571CDC,
+    0x92F34D62616CE413,
+    0x77B020BAF9C81D18,
+    0x0ACE1474DC1D122F,
+    0x0D819992132456BB,
+    0x10E1FFF697ED6C69,
+    0xCA8D3FFA1EF463C2,
+    0xBD308FF8A6B17CB2,
+    0xAC7CB3F6D05DDBDF,
+    0x6BCDF07A423AA96B,
+    0x86C16C98D2C953C6,
+    0xE871C7BF077BA8B8,
+    0x11471CD764AD4973,
+    0xD598E40D3DD89BCF,
+    0x4AFF1D108D4EC2C3,
+    0xCEDF722A585139BA,
+    0xC2974EB4EE658829,
+    0x733D226229FEEA33,
+    0x0806357D5A3F5260,
+    0xCA07C2DCB0CF26F8,
+    0xFC89B393DD02F0B6,
+    0xBBAC2078D443ACE3,
+    0xD54B944B84AA4C0E,
+    0x0A9E795E65D4DF11,
+    0x4D4617B5FF4A16D6,
+    0x504BCED1BF8E4E46,
+    0xE45EC2862F71E1D7,
+    0x5D767327BB4E5A4D,
+    0x3A6A07F8D510F870,
+    0x890489F70A55368C,
+    0x2B45AC74CCEA842F,
+    0x3B0B8BC90012929D,
+    0x09CE6EBB40173745,
+    0xCC420A6A101D0516,
+    0x9FA946824A12232E,
+    0x47939822DC96ABF9,
+    0x59787E2B93BC56F7,
+    0x57EB4EDB3C55B65B,
+    0xEDE622920B6B23F1,
+    0xE95FAB368E45ECED,
+    0x11DBCB0218EBB414,
+    0xD652BDC29F26A11A,
+    0x4BE76D3346F04960,
+    0x6F70A4400C562DDC,
+    0xCB4CCD500F6BB953,
+    0x7E2000A41346A7A8,
+    0x8ED400668C0C28C9,
+    0x728900802F0F32FB,
+    0x4F2B40A03AD2FFBA,
+    0xE2F610C84987BFA8,
+    0x0DD9CA7D2DF4D7C9,
+    0x91503D1C79720DBB,
+    0x75A44C6397CE912A,
+    0xC986AFBE3EE11ABA,
+    0xFBE85BADCE996169,
+    0xFAE27299423FB9C3,
+    0xDCCD879FC967D41A,
+    0x5400E987BBC1C921,
+    0x290123E9AAB23B69,
+    0xF9A0B6720AAF6521,
+    0xF808E40E8D5B3E6A,
+    0xB60B1D1230B20E04,
+    0xB1C6F22B5E6F48C3,
+    0x1E38AEB6360B1AF3,
+    0x25C6DA63C38DE1B0,
+    0x579C487E5A38AD0E,
+    0x2D835A9DF0C6D852,
+    0xF8E431456CF88E66,
+    0x1B8E9ECB641B5900,
+    0xE272467E3D222F40,
+    0x5B0ED81DCC6ABB10,
+    0x98E947129FC2B4EA,
+    0x3F2398D747B36224,
+    0x8EEC7F0D19A03AAD,
+    0x1953CF68300424AC,
+    0x5FA8C3423C052DD7,
+    0x3792F412CB06794D,
+    0xE2BBD88BBEE40BD0,
+    0x5B6ACEAEAE9D0EC4,
+    0xF245825A5A445275,
+    0xEED6E2F0F0D56713,
+    0x55464DD69685606C,
+    0xAA97E14C3C26B887,
+    0xD53DD99F4B3066A8,
+    0xE546A8038EFE4029,
+    0xDE98520472BDD033,
+    0x963E66858F6D4440,
+    0xDDE7001379A44AA8,
+    0x5560C018580D5D52,
+    0xAAB8F01E6E10B4A7,
+    0xCAB3961304CA70E8,
+    0x3D607B97C5FD0D22,
+    0x8CB89A7DB77C506B,
+    0x77F3608E92ADB243,
+    0x55F038B237591ED3,
+    0x6B6C46DEC52F6688,
+    0x2323AC4B3B3DA015,
+    0xABEC975E0A0D081B,
+    0x96E7BD358C904A21,
+    0x7E50D64177DA2E55,
+    0xDDE50BD1D5D0B9EA,
+    0x955E4EC64B44E864,
+    0xBD5AF13BEF0B113F,
+    0xECB1AD8AEACDD58E,
+    0x67DE18EDA5814AF2,
+    0x80EACF948770CED7,
+    0xA1258379A94D028D,
+    0x096EE45813A04330,
+    0x8BCA9D6E188853FC,
+    0x775EA264CF55347E,
+    0x95364AFE032A819D,
+    0x3A83DDBD83F52205,
+    0xC4926A9672793543,
+    0x75B7053C0F178294,
+    0x5324C68B12DD6338,
+    0xD3F6FC16EBCA5E03,
+    0x88F4BB1CA6BCF584,
+    0x2B31E9E3D06C32E5,
+    0x3AFF322E62439FCF,
+    0x09BEFEB9FAD487C3,
+    0x4C2EBE687989A9B4,
+    0x0F9D37014BF60A10,
+    0x538484C19EF38C94,
+    0x2865A5F206B06FBA,
+    0xF93F87B7442E45D4,
+    0xF78F69A51539D749,
+    0xB573440E5A884D1B,
+    0x31680A88F8953031,
+    0xFDC20D2B36BA7C3D,
+    0x3D32907604691B4D,
+    0xA63F9A49C2C1B110,
+    0x0FCF80DC33721D54,
+    0xD3C36113404EA4A9,
+    0x645A1CAC083126E9,
+    0x3D70A3D70A3D70A4,
+    0xCCCCCCCCCCCCCCCD,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x0000000000000000,
+    0x4000000000000000,
+    0x5000000000000000,
+    0xA400000000000000,
+    0x4D00000000000000,
+    0xF020000000000000,
+    0x6C28000000000000,
+    0xC732000000000000,
+    0x3C7F400000000000,
+    0x4B9F100000000000,
+    0x1E86D40000000000,
+    0x1314448000000000,
+    0x17D955A000000000,
+    0x5DCFAB0800000000,
+    0x5AA1CAE500000000,
+    0xF14A3D9E40000000,
+    0x6D9CCD05D0000000,
+    0xE4820023A2000000,
+    0xDDA2802C8A800000,
+    0xD50B2037AD200000,
+    0x4526F422CC340000,
+    0x9670B12B7F410000,
+    0x3C0CDD765F114000,
+    0xA5880A69FB6AC800,
+    0x8EEA0D047A457A00,
+    0x72A4904598D6D880,
+    0x47A6DA2B7F864750,
+    0x999090B65F67D924,
+    0xFFF4B4E3F741CF6D,
+    0xBFF8F10E7A8921A4,
+    0xAFF72D52192B6A0D,
+    0x9BF4F8A69F764490,
+    0x02F236D04753D5B5,
+    0x01D762422C946591,
+    0x424D3AD2B7B97EF5,
+    0xD2E0898765A7DEB2,
+    0x63CC55F49F88EB2F,
+    0x3CBF6B71C76B25FB,
+    0x8BEF464E3945EF7A,
+    0x97758BF0E3CBB5AC,
+    0x3D52EEED1CBEA317,
+    0x4CA7AAA863EE4BDD,
+    0x8FE8CAA93E74EF6A,
+    0xB3E2FD538E122B45,
+    0x60DBBCA87196B616,
+    0xBC8955E946FE31CE,
+    0x6BABAB6398BDBE41,
+    0xC696963C7EED2DD2,
+    0xFC1E1DE5CF543CA3,
+    0x3B25A55F43294BCC,
+    0x49EF0EB713F39EBF,
+    0x6E3569326C784337,
+    0x49C2C37F07965405,
+    0xDC33745EC97BE906,
+    0x69A028BB3DED71A4,
+    0xC40832EA0D68CE0D,
+    0xF50A3FA490C30190,
+    0x792667C6DA79E0FA,
+    0x577001B891185939,
+    0xED4C0226B55E6F87,
+    0x544F8158315B05B4,
+    0x696361AE3DB1C721,
+    0x03BC3A19CD1E38EA,
+    0x04AB48A04065C724,
+    0x62EB0D64283F9C76,
+    0x3BA5D0BD324F8394,
+    0xCA8F44EC7EE36479,
+    0x7E998B13CF4E1ECC,
+    0x9E3FEDD8C321A67F,
+    0xC5CFE94EF3EA101E,
+    0xBBA1F1D158724A13,
+    0x2A8A6E45AE8EDC98,
+    0xF52D09D71A3293BE,
+    0x593C2626705F9C56,
+    0x6F8B2FB00C77836C,
+    0x0B6DFB9C0F956447,
+    0x4724BD4189BD5EAC,
+    0x58EDEC91EC2CB658,
+    0x2F2967B66737E3ED,
+    0xBD79E0D20082EE74,
+    0xECD8590680A3AA11,
+    0xE80E6F4820CC9496,
+    0x3109058D147FDCDE,
+    0xBD4B46F0599FD415,
+    0x6C9E18AC7007C91A,
+    0x03E2CF6BC604DDB0,
+    0x84DB8346B786151D,
+    0xE612641865679A64,
+    0x4FCB7E8F3F60C07E,
+    0xE3BE5E330F38F09E,
+    0x5CADF5BFD3072CC5,
+    0x73D9732FC7C8F7F7,
+    0x2867E7FDDCDD9AFA,
+    0xB281E1FD541501B9,
+    0x1F225A7CA91A4227,
+    0x3375788DE9B06958,
+    0x0052D6B1641C83AE,
+    0xC0678C5DBD23A49A,
+    0xF840B7BA963646E0,
+    0xB650E5A93BC3D898,
+    0xA3E51F138AB4CEBE,
+    0xC66F336C36B10137,
+    0xB80B0047445D4185,
+    0xA60DC059157491E6,
+    0x87C89837AD68DB30,
+    0x29BABE4598C311FC,
+    0xF4296DD6FEF3D67B,
+    0x1899E4A65F58660D,
+    0x5EC05DCFF72E7F90,
+    0x76707543F4FA1F74,
+    0x6A06494A791C53A8,
+    0x0487DB9D17636892,
+    0x45A9D2845D3C42B7,
+    0x0B8A2392BA45A9B2,
+    0x8E6CAC7768D7141F,
+    0x3207D795430CD927,
+    0x7F44E6BD49E807B8,
+    0x5F16206C9C6209A6,
+    0x36DBA887C37A8C10,
+    0xC2494954DA2C978A,
+    0xF2DB9BAA10B7BD6C,
+    0x6F92829494E5ACC7,
+    0xCB772339BA1F17F9,
+    0xFF2A760414536EFC,
+    0xFEF5138519684ABB,
+    0x7EB258665FC25D69,
+    0xEF2F773FFBD97A62,
+    0xAAFB550FFACFD8FA,
+    0x95BA2A53F983CF39,
+    0xDD945A747BF26184,
+    0x94F971119AEEF9E4,
+    0x7A37CD5601AAB85E,
+    0xAC62E055C10AB33B,
+    0x577B986B314D6009,
+    0xED5A7E85FDA0B80B,
+    0x14588F13BE847307,
+    0x596EB2D8AE258FC9,
+    0x6FCA5F8ED9AEF3BB,
+    0x25DE7BB9480D5855,
+    0xAF561AA79A10AE6A,
+    0x1B2BA1518094DA05,
+    0x90FB44D2F05D0843,
+    0x353A1607AC744A54,
+    0x42889B8997915CE9,
+    0x69956135FEBADA11,
+    0x43FAB9837E699096,
+    0x94F967E45E03F4BB,
+    0x1D1BE0EEBAC278F5,
+    0x6462D92A69731732,
+    0x7D7B8F7503CFDCFF,
+    0x5CDA735244C3D43F,
+    0x3A0888136AFA64A7,
+    0x088AAA1845B8FDD1,
+    0x8AAD549E57273D45,
+    0x36AC54E2F678864B,
+    0x84576A1BB416A7DE,
+    0x656D44A2A11C51D5,
+    0x9F644AE5A4B1B325,
+    0x873D5D9F0DDE1FEF,
+    0xA90CB506D155A7EA,
+    0x09A7F12442D588F3,
+    0x0C11ED6D538AEB2F,
+    0x8F1668C8A86DA5FB,
+    0xF96E017D694487BD,
+    0x37C981DCC395A9AC,
+    0x85BBE253F47B1417,
+    0x93956D7478CCEC8E,
+    0x387AC8D1970027B2,
+    0x06997B05FCC0319F,
+    0x441FECE3BDF81F03,
+    0xD527E81CAD7626C4,
+    0x8A71E223D8D3B075,
+    0xF6872D5667844E49,
+    0xB428F8AC016561DB,
+    0xE13336D701BEBA52,
+    0xECC0024661173473,
+    0x27F002D7F95D0190,
+    0x31EC038DF7B441F4,
+    0x7E67047175A15271,
+    0x0F0062C6E984D387,
+    0x52C07B78A3E60868,
+    0xA7709A56CCDF8A83,
+    0x88A66076400BB692,
+    0x6ACFF893D00EA436,
+    0x0583F6B8C4124D43,
+    0xC3727A337A8B704A,
+    0x744F18C0592E4C5D,
+    0x1162DEF06F79DF74,
+    0x8ADDCB5645AC2BA8,
+    0x6D953E2BD7173693,
+    0xC8FA8DB6CCDD0437,
+    0x1D9C9892400A22A2,
+    0x2503BEB6D00CAB4B,
+    0x2E44AE64840FD61E,
+    0x5CEAECFED289E5D3,
+    0x7425A83E872C5F47,
+    0xD12F124E28F77719,
+    0x82BD6B70D99AAA70,
+    0x636CC64D1001550C,
+    0x3C47F7E05401AA4F,
+    0x65ACFAEC34810A71,
+    0x7F1839A741A14D0D,
+    0x1EDE48111209A051,
+    0x934AED0AAB460432,
+    0xF81DA84D5617853F,
+    0x36251260AB9D668F,
+    0xC1D72B7C6B426019,
+    0xB24CF65B8612F820,
+    0xDEE033F26797B628,
+    0x169840EF017DA3B1,
+    0x8E1F289560EE864F,
+    0xF1A6F2BAB92A27E3,
+    0xAE10AF696774B1DB,
+    0xACCA6DA1E0A8EF29,
+    0x17FD090A58D32AF3,
+    0xDDFC4B4CEF07F5B0,
+    0x4ABDAF101564F98E,
+    0x9D6D1AD41ABE37F2,
+    0x84C86189216DC5EE,
+    0x32FD3CF5B4E49BB5,
+    0x3FBC8C33221DC2A2,
+    0x0FABAF3FEAA5334A,
+    0x29CB4D87F2A7400E,
+    0x743E20E9EF511012,
+    0x914DA9246B255417,
+    0x1AD089B6C2F7548E,
+    0xA184AC2473B529B2,
+    0xC9E5D72D90A2741E,
+    0x7E2FA67C7A658893,
+    0xDDBB901B98FEEAB8,
+    0x552A74227F3EA565,
+    0xD53A88958F87275F,
+    0x8A892ABAF368F137,
+    0x2D2B7569B0432D85,
+    0x9C3B29620E29FC73,
+    0x8349F3BA91B47B90,
+    0x241C70A936219A74,
+    0xED238CD383AA0111,
+    0xF4363804324A40AB,
+    0xB143C6053EDCD0D5,
+    0xDD94B7868E94050A,
+    0xCA7CF2B4191C8327,
+    0xFD1C2F611F63A3F0,
+    0xBC633B39673C8CEC,
+    0xD5BE0503E085D814,
+    0x4B2D8644D8A74E19,
+    0xDDF8E7D60ED1219F,
+    0xCABB90E5C942B503,
+    0x3D6A751F3B936244,
+    0x0CC512670A783AD5,
+    0x27FB2B80668B24C5,
+    0xB1F9F660802DEDF6,
+    0x5E7873F8A0396974,
+    0xDB0B487B6423E1E8,
+    0x91CE1A9A3D2CDA63,
+    0x7641A140CC7810FB,
+    0xA9E904C87FCB0A9D,
+    0x546345FA9FBDCD44,
+    0xA97C177947AD4095,
+    0x49ED8EABCCCC485D,
+    0x5C68F256BFFF5A75,
+    0x73832EEC6FFF3112,
+    0xC831FD53C5FF7EAB,
+    0xBA3E7CA8B77F5E56,
+    0x28CE1BD2E55F35EB,
+    0x7980D163CF5B81B3,
+    0xD7E105BCC3326220,
+    0x8DD9472BF3FEFAA8,
+    0xB14F98F6F0FEB952,
+    0x6ED1BF9A569F33D3,
+    0x0A862F80EC4700C8,
+    0xCD27BB612758C0FA,
+    0x8038D51CB897789C,
+    0xE0470A63E6BD56C3,
+    0x1858CCFCE06CAC74,
+    0x0F37801E0C43EBC9,
+    0xD30560258F54E6BB,
+    0x47C6B82EF32A2069,
+    0x4CDC331D57FA5442,
+    0xE0133FE4ADF8E952,
+    0x58180FDDD97723A7,
+    0x570F09EAA7EA7648,
+    0x2CD2CC6551E513DA,
+    0xF8077F7EA65E58D1,
+    0xFB04AFAF27FAF783,
+    0x79C5DB9AF1F9B563,
+    0x18375281AE7822BC,
+    0x8F2293910D0B15B6,
+    0xB2EB3875504DDB23,
+    0x5FA60692A46151EC,
+    0xDBC7C41BA6BCD333,
+    0x12B9B522906C0800,
+    0xD768226B34870A00,
+    0xE6A1158300D46640,
+    0x60495AE3C1097FD0,
+    0x385BB19CB14BDFC4,
+    0x46729E03DD9ED7B5,
+    0x6C07A2C26A8346D1,
+    0xC7098B7305241886,
+    0xB8CBEE4FC66D1EA7,
+    0x737F74F1DC043328,
+    0x505F522E53053FF2,
+    0x647726B9E7C68FEF,
+    0x5ECA783430DC19F5,
+    0xB67D16413D132073,
+    0xE41C5BD18C57E88F,
+    0x8E91B962F7B6F15A,
+    0x723627BBB5A4ADB0,
+    0xCEC3B1AAA30DD91C,
+    0x213A4F0AA5E8A7B2,
+    0xA988E2CD4F62D19E,
+    0x93EB1B80A33B8605,
+    0xBC72F130660533C3,
+    0xEB8FAD7C7F8680B4,
+    0xA67398DB9F6820E1,
+    0x88083F8943A1148D,
+    0x6A0A4F6B948959B0,
+    0x848CE34679ABB01C,
+    0xF2D80E0C0C0B4E12,
+    0x6F8E118F0F0E2196,
+    0x4B7195F2D2D1A9FB,
+    0x8F26FDB7C3C30A3D,
+    0xB2F0BD25B4B3CCCC,
+    0xDFACEC6F21E0C000,
+    0x9798278AEA58EFFF,
+    0x5EBF18B6D2779600,
+    0xF66EDEE487157B80,
+    0xB40A969DA8DADA5F,
+    0x70869E228988C87C,
+    0xCCA845AB2BEAFA9B,
+    0x3FD25715F6E5B941,
+    0x07E3766DBA4F93C9,
+    0x89DC540928E378BB,
+    0x2C53690B731C56EA,
+    0x9BB421A727F1B652,
+    0x42A12A10F1EE23E7,
+    0x134974952E69ACE0,
+    0x2C0DE8DD3D020C0C,
+    0x771163148C428F0F,
+    0x54D5BBD9AF5332D3,
+    0x350595680D93FFC4,
+    0x8246FAC210F8FFB5,
+    0x62D8B97295373FA2,
+    0xFDC773E79D4287C5,
+    0x7D3950E1849329B7,
+    0xDC87A519E5B7F424,
+    0xA9D4C7302F92F897,
+    0xD449F8FC3B77B6BC,
+    0xC95C773B4A55A46B,
+    0x7DD9CA850E7586C3,
+    0x5D503D265212E874,
+    0x34A44C6FE697A291,
+    0x40E6AFC5F01EC59B,
+    0x91205BB76C267701,
+    0x356872A5473014C1,
+    0xC2C28F4E98FC19F2,
+    0xD9B999911F9D9037,
+    0x1027FFF56784F445,
+    0xD431FFF2C1663156,
+    0x049F3FF7B8DFDED6,
+    0x85C70FF5A717D68B,
+    0x2738D3F310DDCC2E,
+    0xB8838477EA8A9F9D,
+    0xE6A46595E52D4784,
+    0x604D7EFB5E789965,
+    0x1C306F5D1B0B5FDF,
+    0x633C8B3461CE37D7,
+    0x3C0BAE017A41C5CD,
+    0xC5874CC0EC691BA0,
+    0xF6E91FF127836288,
+    0xB4A367ED71643B2A,
+    0x50E620F466DEA4FA,
+    0xA51FA93180964E39,
+    0x8E67937DE0BBE1C7,
+    0x7900BC2EAC756D1C,
+    0x5740EB3A5792C863,
+    0x2D112608ED777A7C,
+    0x5C2AB7C5946AAC8E,
+    0xF33565B6F98557B1,
+    0xF002BF24B7E6AD9D,
+    0xB601B776F2F02C82,
+    0xE3822554AFAC37A3,
+    0xDC62AEA9DB97458C,
+    0x537B5A54527D16EE,
+    0x742D1874B38E2E55,
+    0xD1385E91E071B9EA,
+    0x45867636588E2865,
+    0x4B7409E1F758D93F,
+    0x5E510C5A752F0F8F,
+    0xB5E54F71127AD373,
+    0x71AF51A6AB8CC428,
+    0x4E1B2610566FF531,
+    0xA1A1EF946C0BF27E,
+    0x250535BCC387778F,
+    0x6E46832BF4695572,
+    0x89D823F6F183AACF,
+    0x9627167A56F24AC1,
+    0xBBB0DC18ECAEDD72,
+    0x6A9D131F27DA94CE,
+    0xA2A22BF378E89D01,
+    0x0B4AB6F05722C441,
+    0x4E1D64AC6CEB7551,
+    0x90D25EEBC4132953,
+    0xF506F6A6B517F3A7,
+    0xF248B450625DF091,
+    0xD76D70B23D7AB65B,
+    0x0D48CCDECCD963F2,
+    0x109B0016800FBCEE,
+    0xCA60E00E1009D615,
+    0x3CF91811940C4B9A,
+    0xCC375E15F90F5E80,
+    0x3FA29ACDBBA99B10,
+    0x8F8B41812A9401D4,
+    0x336E11E175390249,
+    0x80499659D28742DC,
+    0x302DFDF8239489C9,
+    0xBC397D762C79AC3C,
+    0x2B47DCD3B798174B,
+    0xDB0CEA0452BF0E8F,
+    0x51D02485676ED232,
+    0xA6442DA6C14A86BF,
+    0xA7EA9C8838CE9437,
+    0x91E543AA47023945,
+    0xB65E9494D8C2C796,
+    0xB1FB1CDD0779BCBE,
+    0xDE79E41449582BED,
+    0xD6185D195BAE36E9,
+    0x05CF3A2FD94CE251,
+    0x074308BBCFA01AE6,
+    0x4913CAEAC388219F,
+    0x6DAC5ED2BA351504,
+    0x8917768768C25A44,
+    0xAB5D542942F2F0D6,
+    0x4B1A5499C9D7D685,
+    0x1DE0E9C03C4DCC27,
+    0x255924304B613F31,
+    0x3757B69E2F1CC77E,
+    0xC52DA445BAE3F95E,
+    0xF6790D57299CF7B5,
+    0xFA0BA8567A021AD1,
+    0xF88E926C1882A186,
+    0xF6B237071EA349E7,
+    0xF45EC4C8E64C1C61,
+    0x78BB3AFD8FEF91BD,
+    0xD6EA09BCF3EB762C,
+    0x0CA48C2C30E653B7,
+    0x27E6D79B9E8FF452,
+    0xF1E08D828633F167,
+    0xAE58B0E327C0EDC0,
+    0x4CF76E8DF8D89498,
+    0x60354A31770EB9BE,
+    0x78429CBDD4D2682E,
+    0xCB29A1F6A503811D,
+    0x7DF40A744E446164,
+    0x1D710D1161D579BD,
+    0xF266A82ADD256C16,
+    0x2F005235946EC71C,
+    0x3AC066C2F98A78E2,
+    0xC4B84039DBF68B8E,
+    0xB5E6504852F42E71,
+    0xE35FE45A67B13A0D,
+    0x0E1BEEB880CEC448,
+    0xD1A2EA66A102755A,
+    0x460BA500494312B1,
+    0xEBC747202DC9EBAF,
+    0xA6B918E8393C669A,
+    0x90675F22478B8041,
+    0x7A409B756CB73028,
+    0x58D0C252C7E4FC33,
+    0xAF04F2E779DE3B3F,
+    0xDAC62FA15855CA0F,
+    0x48BBDDC4D7359E49,
+    0x5AEAD5360D0305DC,
+    0x71A58A839043C753,
+    0xA70776923A2A5C94,
+    0x50C95436C8B4F3B9,
+    0x64FBA9447AE230A7,
+    0xBF1D49CACCCD5E68,
+    0xEEE49C3D8000B602,
+    0x6A9DC34CE000E383,
+    0x02A29A100C008E32,
+    0xC34B40940F00B1BE,
+    0xF41E10B912C0DE2E,
+    0x7892CA73ABB88ADD,
+    0xD6B77D1096A6AD94,
+    0xCC655C54BC5058F9,
+];
diff --git a/source/mir/bignum/internal/kernel.d b/source/mir/bignum/internal/kernel.d
index f6c7f4a7..fce9df7d 100644
--- a/source/mir/bignum/internal/kernel.d
+++ b/source/mir/bignum/internal/kernel.d
@@ -27,10 +27,10 @@ size_t multiply(
     c[a.length + b.length - 1] = 0;
 
     auto length = multiply(
-        cast(uint[]) c,
+        cast(uint[]) c[],
         a.toUints,
         b.toUints,
-        cast(uint[]) buffer,
+        cast(uint[]) buffer[],
     );
 
     return length / 2 + length % 2;
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index d8d47d92..937a339b 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -99,8 +99,8 @@ struct BigUIntView(W)
     T opCast(T)() const
         if (isFloatingPoint!T && isMutable!T)
     {
-        import mir.bignum.internal.dec2float: decimalTo;
-        return normalized.coefficients.decimalTo!T;
+        import mir.bignum.internal.dec2float: binaryTo;
+        return normalized.coefficients.binaryTo!T;
     }
 
     static if (W.sizeof >= size_t.sizeof)
@@ -119,10 +119,10 @@ struct BigUIntView(W)
         }
         else
         {
-            import mir.bignum.internal.dec2float: decimalToFp;
+            import mir.bignum.internal.dec2float: binaryToFp;
             auto coefficients = normalized.coefficients;
             return coefficients.length
-                ? coefficients.decimalToFp!coefficientSize
+                ? coefficients.binaryToFp!coefficientSize
                 : Fp!coefficientSize.init;
         }
     }
@@ -850,9 +850,9 @@ struct BigUIntView(W)
     +/
     size_t cttz()() const @property
         @safe pure nothrow @nogc
+        in (coefficients.length)
     {
         import mir.bitop: cttz;
-        assert(coefficients.length);
         auto d = coefficients[];
         size_t ret;
         do
@@ -2286,7 +2286,9 @@ struct DecimalView(W)
 
         static if (!wordNormalized)
             coeff = coeff.normalized;
-        auto ret = decimalTo!T(coeff.coefficients, exponent);
+        auto ret = exponent != exponent.max ?
+            coeff.coefficients.decimalTo!T(exponent) :
+            coeff.length ? T.nan : T.infinity;
         if (sign)
             ret = -ret;
         return ret;
@@ -2328,282 +2330,284 @@ unittest
 version(mir_bignum_test_llv)
 unittest
 {
+    import mir.test;
+
     auto view = DecimalView!size_t(false, -8, BigUIntView!size_t.fromHexString("BEBC2000000011E1A3"));
 
-    assert (cast(float)view == 3.518437208883201171875E+013f);
-    assert (cast(double)view == 3.518437208883201171875E+013);
+    should(cast(float)view) == 3.518437208883201171875E+013f;
+    should(cast(double)view) == 3.518437208883201171875E+013;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 3.518437208883201171875E+013L);
+        should(cast(real)view) == 3.518437208883201171875E+013L;
 
     view = DecimalView!size_t(true, -169, BigUIntView!size_t.fromHexString("5A174AEDA65CC"));
-    assert (cast(float)view == -0);
-    assert (cast(double)view == -0x1.1p-511);
+    should(cast(float)view) == -0;
+    should(cast(double)view) == -0x1.1p-511;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == -0x8.80000000000019fp-514L);
+        should(cast(real)view) == -0x8.80000000000019fp-514L;
 
     view = DecimalView!size_t(true, 293, BigUIntView!size_t.fromHexString("36496F6C4ED38"));
-    assert (cast(float)view == -float.infinity);
-    assert (cast(double)view == -9.55024478104888e+307);
+    should(cast(float)view) == -float.infinity;
+    should(cast(double)view) == -9.55024478104888e+307;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == -9.55024478104888e+307L);
+        should(cast(real)view) == -9.55024478104888e+307L;
 
     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 1);
-    assert (cast(double)view == 1);
+    should(cast(float)view) == 1;
+    should(cast(double)view) == 1;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 1L);
+        should(cast(real)view) == 1L;
 
     view = DecimalView!size_t(false, -5, BigUIntView!size_t.fromHexString("3"));
-    assert (cast(float)view == 3e-5f);
-    assert (cast(double)view == 3e-5);
+    should(cast(float)view) == 3e-5f;
+    should(cast(double)view) == 3e-5;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 3e-5L);
+        should(cast(real)view) == 3e-5L;
 
     view = DecimalView!size_t(false, -1, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0.1f);
-    assert (cast(double)view == 0.1);
+    should(cast(float)view) == 0.1f;
+    should(cast(double)view) == 0.1;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0.1L);
+        should(cast(real)view) == 0.1L;
 
     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("3039"));
-    assert (cast(float)view == 12345.0f);
-    assert (cast(double)view == 12345.0);
+    should(cast(float)view) == 12345.0f;
+    should(cast(double)view) == 12345.0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 12345.0L);
+        should(cast(real)view) == 12345.0L;
 
     view = DecimalView!size_t(false, -7, BigUIntView!size_t.fromHexString("98967F"));
-    assert (cast(float)view == 0.9999999f);
-    assert (cast(double)view == 0.9999999);
+    should(cast(float)view) == 0.9999999f;
+    should(cast(double)view) == 0.9999999;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0.9999999L);
+        should(cast(real)view) == 0.9999999L;
 
     view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("4F0CEDC95A718E"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 2.2250738585072014e-308);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 2.2250738585072014e-308;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 2.2250738585072014e-308L);
+        should(cast(real)view) == 2.2250738585072014e-308L;
 
     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1FFFFFFFFFFFFFFFD"));
-    assert (cast(float)view == 36893488147419103229f);
-    assert (cast(double)view == 36893488147419103229.0);
+    should(cast(float)view) == 36893488147419103229f;
+    should(cast(double)view) == 36893488147419103229.0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x1FFFFFFFFFFFFFFFDp0L);
+        should(cast(real)view) == 0x1FFFFFFFFFFFFFFFDp0L;
 
     view = DecimalView!size_t(false, -33, BigUIntView!size_t.fromHexString("65"));
-    assert (cast(float)view == 101e-33f);
-    assert (cast(double)view == 101e-33);
+    should(cast(float)view) == 101e-33f;
+    should(cast(double)view) == 101e-33;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 101e-33L);
+        should(cast(real)view) == 101e-33L;
 
     view = DecimalView!size_t(false, 23, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 1e23f);
-    assert (cast(double)view == 1e23);
+    should(cast(float)view) == 1e23f;
+    should(cast(double)view) == 1e23;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 1e23L);
+        should(cast(real)view) == 1e23L;
 
     view = DecimalView!size_t(false, 23, BigUIntView!size_t.fromHexString("81B"));
-    assert (cast(float)view == 2075e23f);
-    assert (cast(double)view == 0xaba3d58a1f1a98p+32);
+    should(cast(float)view) == 2075e23f;
+    should(cast(double)view) == 0xaba3d58a1f1a98p+32;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xaba3d58a1f1a9cp+32L);
+        should(cast(real)view) == 0xaba3d58a1f1a9cp+32L;
 
     view = DecimalView!size_t(false, -23, BigUIntView!size_t.fromHexString("2209"));
-    assert (cast(float)view == 8713e-23f);
-    assert (cast(double)view == 0x1.9b75b4e7de2b9p-64);
+    should(cast(float)view) == 8713e-23f;
+    should(cast(double)view) == 0x1.9b75b4e7de2b9p-64;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xc.dbada73ef15c401p-67L);
+        should(cast(real)view) == 0xc.dbada73ef15c401p-67L;
 
     view = DecimalView!size_t(false, 300, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == 0x1.7e43c8800759cp+996);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == 0x1.7e43c8800759cp+996;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xb.f21e44003acdd2dp+993L);
+        should(cast(real)view) == 0xb.f21e44003acdd2dp+993L;
 
     view = DecimalView!size_t(false, 245, BigUIntView!size_t.fromHexString("B3A73CEB227"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == 0x1.48e3735333cb6p+857);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == 0x1.48e3735333cb6p+857;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xa.471b9a999e5b01ep+854L);
+        should(cast(real)view) == 0xa.471b9a999e5b01ep+854L;
 
     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == 0x1.117e8e90a0ff7p+239);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == 0x1.117e8e90a0ff7p+239;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.8bf4748507fb99p+236L);
+        should(cast(real)view) == 0x8.8bf4748507fb99p+236L;
 
     view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("5"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0x0.0000000000001p-1022);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0x0.0000000000001p-1022;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.18995ce7aa0e1b2p-1077L);
+        should(cast(real)view) == 0x8.18995ce7aa0e1b2p-1077L;
 
     view = DecimalView!size_t(false, -324, BigUIntView!size_t.fromHexString("5B"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0x0.0000000000012p-1022);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0x0.0000000000012p-1022;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x9.3594d9adeb09a55p-1073L);
+        should(cast(real)view) == 0x9.3594d9adeb09a55p-1073L;
 
     view = DecimalView!size_t(false, -322, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0x0.0000000000014p-1022);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0x0.0000000000014p-1022;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xa.1ebfb4219491a1fp-1073L);
+        should(cast(real)view) == 0xa.1ebfb4219491a1fp-1073L;
 
     view = DecimalView!size_t(false, -320, BigUIntView!size_t.fromHexString("CA1CCB"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0x0.000063df832d9p-1022);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0x0.000063df832d9p-1022;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xc.7bf065b215888c7p-1043L);
+        should(cast(real)view) == 0xc.7bf065b215888c7p-1043L;
 
     view = DecimalView!size_t(false, -319, BigUIntView!size_t.fromHexString("33CE7943FB"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0x1.000000000162p-1022);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0x1.000000000162p-1022;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.000000000b103b6p-1025L);
+        should(cast(real)view) == 0x8.000000000b103b6p-1025L;
 
     view = DecimalView!size_t(false, -309, BigUIntView!size_t.fromHexString("15"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0x0.f19c2629ccf53p-1022);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0x0.f19c2629ccf53p-1022;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xf.19c2629ccf52fc4p-1026L);
+        should(cast(real)view) == 0xf.19c2629ccf52fc4p-1026L;
 
     view = DecimalView!size_t(false, -340, BigUIntView!size_t.fromHexString("AF87023B9BF0EE"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0x0.0000000000001p-1022);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0x0.0000000000001p-1022;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xf.fffffffffffff64p-1078L);
+        should(cast(real)view) == 0xf.fffffffffffff64p-1078L;
 
     view = DecimalView!size_t(false, 400, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == double.infinity);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == double.infinity;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xd.a763fc8cb9ff9e6p+1325L);
+        should(cast(real)view) == 0xd.a763fc8cb9ff9e6p+1325L;
 
     view = DecimalView!size_t(false, 309, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == double.infinity);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == double.infinity;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xb.201833b35d63f73p+1023L);
+        should(cast(real)view) == 0xb.201833b35d63f73p+1023L;
 
     view = DecimalView!size_t(false, 308, BigUIntView!size_t.fromHexString("2"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == double.infinity);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == double.infinity;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
+        should(cast(real)view) == 0x8.e679c2f5e44ff8fp+1021L;
 
     view = DecimalView!size_t(false, 308, BigUIntView!size_t.fromHexString("2"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == double.infinity);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == double.infinity;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.e679c2f5e44ff8fp+1021L);
+        should(cast(real)view) == 0x8.e679c2f5e44ff8fp+1021L;
 
     view = DecimalView!size_t(false, 295, BigUIntView!size_t.fromHexString("1059949B7090"));
-    assert (cast(float)view == float.infinity);
-    assert (cast(double)view == double.infinity);
+    should(cast(float)view) == float.infinity;
+    should(cast(double)view) == double.infinity;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.00000000006955ap+1021L);
+        should(cast(real)view) == 0x8.00000000006955ap+1021L;
 
     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("0"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0L);
+        should(cast(real)view) == 0L;
 
     view = view;
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0L);
+        should(cast(real)view) == 0L;
 
     view = DecimalView!size_t(false, -325, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
+        should(cast(real)view) == 0xa.5ced43b7e3e9188p-1083L;
 
     view = DecimalView!size_t(false, -326, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
+        should(cast(real)view) == 0x8.4a57695fe98746dp-1086L;
 
     view = DecimalView!size_t(false, -500, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.33ada2003db9a8p-1664L);
+        should(cast(real)view) == 0x8.33ada2003db9a8p-1664L;
 
     view = DecimalView!size_t(false, -1000, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.68a9188a89e1467p-3325L);
+        should(cast(real)view) == 0x8.68a9188a89e1467p-3325L;
 
     view = DecimalView!size_t(false, -4999, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0L);
+        should(cast(real)view) == 0L;
 
     view = DecimalView!size_t(false, -10000, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0L);
+        should(cast(real)view) == 0L;
 
     view = DecimalView!size_t(false, -4969, BigUIntView!size_t.fromHexString("329659A941466C6B"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == real.min_normal * real.epsilon);
+        should(cast(real)view) == real.min_normal * real.epsilon;
 
     view = DecimalView!size_t(false, -15, BigUIntView!size_t.fromHexString("525DB0200FFAB"));
-    assert (cast(float)view == 1.448997445238699f);
-    assert (cast(double)view == 0x1.72f17f1f49aadp+0);
+    should(cast(float)view) == 1.448997445238699f;
+    should(cast(double)view) == 0x1.72f17f1f49aadp+0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
+        should(cast(real)view) == 0xb.978bf8fa4d56cp-3L;
 
     view = DecimalView!size_t(false, -15, BigUIntView!size_t.fromHexString("525DB0200FFAB"));
-    assert (cast(float)view == 1.448997445238699f);
-    assert (cast(double)view == 0x1.72f17f1f49aadp+0);
+    should(cast(float)view) == 1.448997445238699f;
+    should(cast(double)view) == 0x1.72f17f1f49aadp+0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xb.978bf8fa4d56cp-3L);
+        should(cast(real)view) == 0xb.978bf8fa4d56cp-3L;
 
     view = DecimalView!size_t(false, -325, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xa.5ced43b7e3e9188p-1083L);
+        should(cast(real)view) == 0xa.5ced43b7e3e9188p-1083L;
 
     view = DecimalView!size_t(false, -326, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0);
-    assert (cast(double)view == 0);
+    should(cast(float)view) == 0;
+    should(cast(double)view) == 0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8.4a57695fe98746dp-1086L);
+        should(cast(real)view) == 0x8.4a57695fe98746dp-1086L;
 
     view = DecimalView!size_t(false, 0, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 1);
-    assert (cast(double)view == 0x1p+0);
+    should(cast(float)view) == 1;
+    should(cast(double)view) == 0x1p+0;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0x8p-3L);
+        should(cast(real)view) == 0x8p-3L;
 
     view = DecimalView!size_t(false, -5, BigUIntView!size_t.fromHexString("3"));
-    assert (cast(float)view == 3e-5f);
-    assert (cast(double)view == 0x1.f75104d551d69p-16);
+    should(cast(float)view) == 3e-5f;
+    should(cast(double)view) == 0x1.f75104d551d69p-16;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xf.ba8826aa8eb4635p-19L);
+        should(cast(real)view) == 0xf.ba8826aa8eb4635p-19L;
 
     view = DecimalView!size_t(false, -1, BigUIntView!size_t.fromHexString("1"));
-    assert (cast(float)view == 0.1f);
-    assert (cast(double)view == 0x1.999999999999ap-4);
+    should(cast(float)view) == 0.1f;
+    should(cast(double)view) == 0x1.999999999999ap-4;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xc.ccccccccccccccdp-7L);
+        should(cast(real)view) == 0xc.ccccccccccccccdp-7L;
 
     view = DecimalView!size_t(false, -7, BigUIntView!size_t.fromHexString("98967F"));
-    assert (cast(float)view == 0.9999999f);
-    assert (cast(double)view == 0x1.fffffca501acbp-1);
+    should(cast(float)view) == 0.9999999f;
+    should(cast(double)view) == 0x1.fffffca501acbp-1;
     static if (real.mant_dig >= 64)
-        assert (cast(real)view == 0xf.ffffe5280d65435p-4L);
+        should(cast(real)view) == 0xf.ffffe5280d65435p-4L;
 }
 
 /++
diff --git a/source/mir/format.d b/source/mir/format.d
index a86588bc..92acd274 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -1160,3 +1160,14 @@ size_t printBoolean(C)(bool c, ref C[5] buf)
         return 5;
     }
 }
+
+
+/// Prints pointers
+ref W print(C = char, W, T)(scope return ref W w, scope const T* c)
+{
+    import mir.enums: getEnumIndex, enumStrings;
+    import mir.utility: _expect;
+    if (c is null)
+        return w.print!C(null);
+    return w.print!C(HexAddress!size_t((()@trusted=>cast(size_t)cast(const void*)c)()));
+}
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 2fa2c454..ecce011c 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -12,18 +12,16 @@ version(mir_bignum_test)
 @safe pure @nogc
 unittest
 {
+    import mir.test: should;
     import mir.conv: to;
 
-    assert("123.0".to!double == 123);
-    assert("123".to!int == 123);
-    assert("123".to!byte == 123);
+    "123.0".to!double.should == 123;
+    "123".to!int.should == 123;
+    "123".to!byte.should == 123;
 
     import mir.small_string;
     alias S = SmallString!32;
-    assert(S("123.0").to!double == 123);
-    assert(S("123.").to!double == 123.);
-    assert(S(".123").to!double == .123);
-    assert(S("123").to!(immutable int) == 123);
+    "123.0".SmallString!32.to!double.should == 123;
 }
 
 import mir.primitives;
@@ -81,42 +79,123 @@ T fromString(T, C)(scope const(C)[] str)
     }
 }
 
+version(unittest)
+{
+    import core.stdc.stdlib: strtof, strtod, strtold;
+    private auto _assumePure(T)(scope return T t) {
+        import std.traits;
+        enum attrs = functionAttributes!T | FunctionAttribute.pure_;
+        return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
+    }
+
+    private static @trusted float _stdc_parse(T : float)(string str){ auto endPtr = str.ptr + str.length; return _assumePure(&strtof)(str.ptr, &endPtr);  }
+    private static @trusted double _stdc_parse(T : double)(string str){ auto endPtr = str.ptr + str.length; return _assumePure(&strtod)(str.ptr, &endPtr);  }
+    private static @trusted real _stdc_parse(T : real)(string str){ auto endPtr = str.ptr + str.length; return _assumePure(&strtold)(str.ptr, &endPtr);  }
+}
+
 ///
 version(mir_bignum_test)
 @safe pure @nogc unittest
 {
-    assert("123".fromString!int == 123);
-    static assert("-123".fromString!int == -123);
+    import mir.test;
+    "123".fromString!int.should == 123;
 
-    assert(".5".fromString!float == .5);
-    assert("12.3".fromString!double == 12.3);
-    assert("12.3".fromString!float == 12.3f);
-    assert("12.3".fromString!real == 12.3L);
-    assert("-12.3e-30".fromString!double == -12.3e-30);
-    assert("2.9802322387695312E-8".fromString!double == 2.9802322387695312E-8);
+    ".5".fromString!float.should == .5;
+    "12.3".fromString!double.should == 12.3;
+    "12.3".fromString!float.should == 12.3f;
+    "12.3".fromString!real.should == 12.3L;
+    "-12.3e-30".fromString!double.should == -12.3e-30;
+    "2.9802322387695312E-8".fromString!double.should == 2.9802322387695312E-8;
 
     // default support of underscores
-    assert("123_456.789_012".fromString!double == 123_456.789_012);
-    assert("12_34_56_78_90_12e-6".fromString!double == 123_456.789_012);
+    "123_456.789_012".fromString!double.should == 123_456.789_012;
+    "12_34_56_78_90_12e-6".fromString!double.should == 123_456.789_012;
 
     // default support of leading zeros
-    assert("010".fromString!double == 10.0);
-    assert("000010".fromString!double == 10.0);
-    assert("0000.10".fromString!double == 0.1);
-    assert("0000e10".fromString!double == 0);
+    "010".fromString!double.should == 10.0;
+    "000010".fromString!double.should == 10.0;
+    "0000.10".fromString!double.should == 0.1;
+    "0000e10".fromString!double.should == 0;
 
-    /// Test CTFE support  
-    static assert("-12.3e-30".fromString!double == -0x1.f2f280b2414d5p-97);
-    static assert("+12.3e+30".fromString!double == 0x1.367ee3119d2bap+103);
+    version(all) {} else
+    version (TeslAlgoM) {} else
+    {
+        /// Test CTFE support  
+        static assert("-123".fromString!int == -123);
 
-    static assert("1.448997445238699".fromString!double == 0x1.72f17f1f49aadp0);
-    static if (real.mant_dig >= 64)
-        static assert("1.448997445238699".fromString!real == 1.448997445238699L);
+        static assert("-12.3e-30".fromString!double == -0x1.f2f280b2414d5p-97);
+        static assert("+12.3e+30".fromString!double == 0x1.367ee3119d2bap+103);
 
-    static assert("3.518437208883201171875".fromString!float == 0x1.c25c26p+1);
-    static assert("3.518437208883201171875".fromString!double == 0x1.c25c268497684p+1);
-    static if (real.mant_dig >= 64)
-        static assert("3.518437208883201171875".fromString!real == 0xe.12e13424bb4232fp-2L);    
+        static assert("1.448997445238699".fromString!double == 0x1.72f17f1f49aadp0);
+        static if (real.mant_dig >= 64)
+            static assert("1.448997445238699".fromString!real == 1.448997445238699L);
+
+        static assert("3.518437208883201171875".fromString!float == 0x1.c25c26p+1);
+        static assert("3.518437208883201171875".fromString!double == 0x1.c25c268497684p+1);
+        static if (real.mant_dig >= 64)
+            static assert("3.518437208883201171875".fromString!real == 0xe.12e13424bb4232fp-2L);    
+    }
+
+    void test(string str)
+    {
+        version(CRuntime_DigitalMars) // No precise parsing at all
+        {
+        }
+        else
+        {
+            str.fromString!float.should == str._stdc_parse!float;
+            str.fromString!double.should == str._stdc_parse!double;
+            version (Windows) // No precise real parsing on windows
+            {
+            }
+            else
+                str.fromString!real.should == str._stdc_parse!real;
+        }
+    }
+
+    test("2.5e-324");
+
+    // large
+    test("1e300");
+    test("123456789.34567e250");
+    test("943794359898089732078308743689303290943794359843568973207830874368930329.");
+
+    // min normal
+    test("2.2250738585072014e-308");
+
+    // subnormals
+    test("5e-324");
+    test("91e-324");
+    test("1e-322");
+    test("13245643e-320");
+    test("2.22507385851e-308");
+    test("2.1e-308");
+    test("4.9406564584124654e-324");
+
+    // infinity
+    test("1e400");
+    test("1e309");
+    test("2e308");
+    test("1.7976931348624e308");
+
+    // zero
+    test("0.0");
+    test("1e-325");
+    test("1e-326");
+    test("1e-500");
+
+    // Triggers the tricky underflow case in AlgorithmM (for f32)
+    test("101e-33");
+    // Triggers AlgorithmR
+    test("1e23");
+    // Triggers another path through AlgorithmR
+    test("2075e23");
+    // ... and yet another.
+    test("8713e-23");
+
+    // 2^65 - 3, triggers half-to-even with even significand
+    test("36893488147419103229.0");
+    test("36893488147419103229");
 
 //  Related DMD Issues:
 // https://issues.dlang.org/show_bug.cgi?id=20951
@@ -125,18 +204,6 @@ version(mir_bignum_test)
 // https://issues.dlang.org/show_bug.cgi?id=20967
 }
 
-// unittest
-// {
-//     import std.stdio;
-//     import mir.conv;
-//     import mir.format;
-//     text(double.min_normal).writeln;
-//     text(double.min_normal * double.epsilon).writeln;
-//     assert("2.2250738585072014e-308".fromString!double == double.min_normal);
-//     assert("5e-324".fromString!double == double.min_normal * double.epsilon);
-//     assert("2.5e-324".fromString!double == double.min_normal * double.epsilon);
-// }
-
 version(mir_bignum_test)
 @safe pure unittest
 {
diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index 006dda9a..2902342d 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -7,6 +7,29 @@ module mir.stdio;
 
 static import core.stdc.stdio;
 
+/// Writes values in a text form
+void writeln(string separator = "", Args...)(auto ref const Args args)
+    if (Args.length > 0)
+{
+    dout.write!separator(args);
+    dout << endl;
+}
+
+/// ditto
+void write(string separator = "", Args...)(auto ref const Args args)
+    if (Args.length > 0)
+{
+    dout.write!separator(args);
+}
+
+/// Writes values in a text form using nothrow $(LREF tout)
+void dump(string separator = " ", Args...)(auto ref const Args args)
+    if (Args.length > 0)
+{
+    tout.write!separator(args);
+    tout << endl;
+}
+
 /// Standart output
 File dout()() @trusted nothrow @nogc @property
 {
@@ -174,8 +197,10 @@ mixin template FileMemembers()
     template opBinary(string op : "<<")
     {
         ///
-        ref opBinary(T)(auto ref T value) return scope
+        ref opBinary(T)(auto ref const T value) return scope
         {
+            if (__ctfe)
+                return this;
             import mir.format: print;
             return print!char(this, value);
         }
@@ -183,12 +208,40 @@ mixin template FileMemembers()
         /// Prints new line and flushes the stream
         ref opBinary(NewLine endl) return scope
         {
+            if (__ctfe)
+                return this;
             import mir.format: print;
             this.put(endl);
             this.flush;
             return this;
         }
     }
+
+    /// Writes values in a text form
+    void writeln(string separator = "", Args...)(auto ref const Args args)
+        if (Args.length > 0)
+    {
+        write(args);
+        this << endl;
+    }
+
+    /// ditto
+    void write(string separator = "", Args...)(auto ref const Args args)
+        if (Args.length > 0)
+    {
+        pragma(inline, false);
+        if (__ctfe)
+            return;
+        import mir.format: print, printStaticString;
+        foreach (i, ref arg; args)
+        {
+            print!char(this, arg);
+            static if (separator.length && i + 1 < args.length)
+            {
+                printStaticString!char(this, separator);
+            }
+        }
+    }
 }
 
 /++
diff --git a/source/mir/test.d b/source/mir/test.d
index 6152c448..217af0c6 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -5,7 +5,13 @@ Authors: Ilya Yaroshenko
 +/
 module mir.test;
 
-import mir.exception: MirException;
+import mir.exception: MirError;
+
+private noreturn assumeAllAttrAndCall(scope const void delegate() t)
+    @nogc pure nothrow @trusted {
+    (cast(const void delegate() @safe pure nothrow @nogc) t)();
+    assert(0);
+}
 
 ///
 struct ShouldApprox(T)
@@ -19,19 +25,22 @@ struct ShouldApprox(T)
     T maxAbsDiff = 0x1p-20f;
 
     ///
-    bool opEquals(T expected, string file = __FILE__, int line = __LINE__) const
+    void opEquals(T expected, string file = __FILE__, int line = __LINE__) @safe pure nothrow @nogc
     {
         import mir.format: stringBuf, getData;
         import mir.math.common: approxEqual;
         if (value.approxEqual(expected, maxRelDiff, maxAbsDiff))
-            return true;
+            return;
         auto buf = stringBuf;
-        throw new MirException(buf
-            << "expected approximately " << expected
-            << ", got " << value
-            << ", maxRelDiff = " << maxRelDiff
-            << ", maxAbsDiff = " << maxAbsDiff
-            << getData, file, line);
+        assumeAllAttrAndCall({
+            throw new MirError(buf
+                << "expected approximately " << expected
+                << ", got " << value
+                << ", maxRelDiff = " << maxRelDiff
+                << ", maxAbsDiff = " << maxAbsDiff
+                << getData, file, line);
+        });
+        assert(0);
     }
 }
 
@@ -57,16 +66,18 @@ struct Should(T)
     T value;
 
     ///
-    bool opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__) const
+    void opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__)
     {
         import mir.format: stringBuf, getData;
         if (value == expected)
-            return true;
+            return;
         auto buf = stringBuf;
-        throw new MirException(buf
-            << "expected " << expected
-            << ", got " << value
-            << getData, file, line);
+        assumeAllAttrAndCall({
+            throw new MirError(buf
+                << "expected " << expected
+                << ", got " << value
+                << getData, file, line);
+        });
     }
 }
 
@@ -85,18 +96,18 @@ unittest
 }
 
 ///
-bool should(alias fun, T, R)(const T value, const R expected, string file = __FILE__, int line = __LINE__)
+void should(alias fun, T, R)(const T value, const R expected, string file = __FILE__, int line = __LINE__)
 {
     import mir.functional;
     import mir.format: stringBuf, getData;
     if (naryFun!fun(value, expected))
-        return true;
+        return;
     auto buf = stringBuf;
     buf << fun.stringof
         << " returns false"
         << " for a = " << value
         << ", b = " << expected;
-    throw new MirException(buf << getData, file, line);
+    throw new MirError(buf << getData, file, line);
 }
 
 ///

From 8d8c8beacd46c0c7426dd6c683a910ffa7a3e942 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ilyayaroshenko@gmail.com>
Date: Wed, 1 Jun 2022 04:22:59 +0400
Subject: [PATCH 427/573] optimize (#413)

---
 source/mir/bignum/internal/dec2float.d       | 64 +++++++++++++-------
 source/mir/bignum/internal/dec2float_table.d |  1 -
 2 files changed, 41 insertions(+), 24 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 02449299..3ec1c4f9 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -122,13 +122,16 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
 
     enum wordBits = T.mant_dig < 64 ? 64 : 128;
     enum ulong half = (1UL << (wordBits - T.mant_dig - 1));
+    enum bigHalf = UInt!128([0UL, half]);
+    enum bigMask = (1UL << (64 - T.mant_dig)) - 1;
+
     static if (T.mant_dig > 64)
         enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
     else
     static if (T.mant_dig == 64)
         enum ulong mask = ulong.max;
     else
-        enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
+        enum ulong mask = bigMask;
     
     if (coefficient == 0)
         return 0;
@@ -136,26 +139,41 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
     version (TeslAlgoM) {} else
     if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
     {
-        auto c = coefficient.Fp!64;
-        auto z = c.extendedMul!true(_load!T(exponent));
-        auto approx = z.opCast!(T, true);
-        long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
-        int slop = 3 * (exponent < 0);
-        if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
-            return approx;
-
-        if (0 <= exponent)
-        {
-            if (exponent <= MaxWordPow5!ulong)
+        version (all)
+        {{
+            auto c = coefficient.Fp!64;
+            auto z = c.extendedMul!true(_load!wordBits(exponent));
+            auto approx = z.opCast!(T, true);
+            long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
+            uint slop = 3 * (exponent < 0);
+            if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
                 return approx;
-        }
-        else
-        {
-            if (-exponent <= MaxFpPow5!T)
+
+            if (0 <= exponent)
             {
-                auto e = _load!T(-exponent);
-                return coefficient / e.opCast!(T, true);
+                if (exponent <= MaxWordPow5!ulong)
+                    return approx;
             }
+            else
+            {
+                if (-exponent <= MaxFpPow5!T)
+                {
+                    auto e = _load!wordBits(-exponent);
+                    return coefficient / e.opCast!(T, true);
+                }
+            }
+        }}
+        static if (T.mant_dig < 64)
+        {
+            auto c = coefficient.Fp!64;
+            auto z = c.extendedMul!true(_load!128(exponent));
+            auto approx = z.opCast!(T, true);
+            auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
+            if (bitsDiff.signBit)
+                bitsDiff = UInt!128.init - bitsDiff;
+            uint slop = 3 * (exponent < 0);
+            if (_expect(approx > T.min_normal && bitsDiff > slop, true))
+                return approx;
         }
     }
     size_t[ulong.sizeof / size_t.sizeof] coefficients;
@@ -206,7 +224,7 @@ private T decimalToFloatImpl(T)(scope const size_t[] coefficients, long exponent
     if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
     {
         auto c = coefficients.binaryToFp!wordBits;
-        auto z = c.extendedMul!true(_load!T(exponent));
+        auto z = c.extendedMul!true(_load!wordBits(exponent));
         auto approx = z.opCast!(T, true);
         auto slop = 1 + 3 * (exponent < 0);
         long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
@@ -241,6 +259,8 @@ private T algorithmM(T)(scope const size_t[] coefficients, long exponent)
 {
     pragma(inline, false);
 
+    // import mir.stdio;
+    // debug dump("algorithmM", coefficients, exponent);
 
     import mir.bitop: ctlz;
     import mir.bignum.fp: Fp;
@@ -479,8 +499,7 @@ package(mir.bignum) auto binaryToFp(uint coefficientSize, uint internalRoundLast
 
 private enum ExponentM = 512;
 
-private auto _load(T)(long e) @trusted
-    if (T.mant_dig < 64)
+private auto _load(uint size : 64)(long e) @trusted
     in (-ExponentM < e && e < ExponentM)
 {
     version (LDC)
@@ -496,8 +515,7 @@ private auto _load(T)(long e) @trusted
     return Fp!64(false, p10exp, UInt!64(p10coeff));
 }
 
-private auto _load(T)(long e) @trusted
-    if (T.mant_dig >= 64)
+private auto _load(uint size : 128)(long e) @trusted
     in (-ExponentM < e && e < ExponentM)
 {
     version (LDC)
diff --git a/source/mir/bignum/internal/dec2float_table.d b/source/mir/bignum/internal/dec2float_table.d
index 96200a23..dc7a6187 100644
--- a/source/mir/bignum/internal/dec2float_table.d
+++ b/source/mir/bignum/internal/dec2float_table.d
@@ -2063,7 +2063,6 @@ static immutable align(16) ulong[1025] p10_coefficients_h = [
     0xE319A0AEA60E91C7,
 ];
 
-static if (real.mant_dig >= 64)
 static immutable align(16) ulong[1025] p10_coefficients_l = [
     0x7132D332E3F204D5,
     0x8D7F87FF9CEE860A,

From 2dd726f5f02bf9479d0aff55dfc96d14bed44691 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Jun 2022 04:51:39 +0400
Subject: [PATCH 428/573] rework

---
 source/mir/bignum/internal/dec2float.d | 31 +++++++++++++-------------
 1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 3ec1c4f9..deb37836 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -139,33 +139,18 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
     version (TeslAlgoM) {} else
     if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
     {
+        auto c = coefficient.Fp!64;
         version (all)
         {{
-            auto c = coefficient.Fp!64;
             auto z = c.extendedMul!true(_load!wordBits(exponent));
             auto approx = z.opCast!(T, true);
             long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
             uint slop = 3 * (exponent < 0);
             if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
                 return approx;
-
-            if (0 <= exponent)
-            {
-                if (exponent <= MaxWordPow5!ulong)
-                    return approx;
-            }
-            else
-            {
-                if (-exponent <= MaxFpPow5!T)
-                {
-                    auto e = _load!wordBits(-exponent);
-                    return coefficient / e.opCast!(T, true);
-                }
-            }
         }}
         static if (T.mant_dig < 64)
         {
-            auto c = coefficient.Fp!64;
             auto z = c.extendedMul!true(_load!128(exponent));
             auto approx = z.opCast!(T, true);
             auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
@@ -175,6 +160,20 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
             if (_expect(approx > T.min_normal && bitsDiff > slop, true))
                 return approx;
         }
+
+        if (0 <= exponent)
+        {
+            if (exponent <= 55) // exact exponent
+                return approx;
+        }
+        else
+        {
+            if (-exponent <= MaxFpPow5!T)
+            {
+                auto e = _load!wordBits(-exponent);
+                return coefficient / e.opCast!(T, true);
+            }
+        }
     }
     size_t[ulong.sizeof / size_t.sizeof] coefficients;
     coefficients[0] = cast(size_t) coefficient;

From 2a0fd33ef8e1b37b8d705652972445cfa62ade33 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Jun 2022 15:05:06 +0400
Subject: [PATCH 429/573] improve bignum parsing

---
 dub.sdl                                |   5 +-
 source/mir/bignum/decimal.d            | 122 +++++++-----
 source/mir/bignum/internal/dec2float.d |   3 -
 source/mir/bignum/internal/parse.d     |   6 +
 source/mir/parse.d                     | 259 ++++++++++---------------
 source/mir/test.d                      |   5 +
 6 files changed, 195 insertions(+), 205 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index c53be93f..938f1be3 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,9 +9,12 @@ dependency "mir-core" version=">=1.1.106"
 
 // versions "TeslAlgoM"
 
+
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_bignum_test_llv" // "mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test" "mir_bignum_test_llv"
+    versions "mir_ndslice_test"
+    versions "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index c9a43f2c..856e795f 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -213,11 +213,11 @@ struct Decimal(uint size64)
                 }
             }
 
-            uint d = str[0] - '0';
+            ulong d = str[0] - C('0');
             str = str[1 .. $];
             exponent = 0;
 
-            ulong v;
+            ulong v, multplier = void;
 
             if (_expect(d >= 10, false))
             {
@@ -228,7 +228,7 @@ struct Decimal(uint size64)
                         if (str.length == 0)
                             return false;
                         key = DecimalExponentKey.dot;
-                        d = str[0] - '0';
+                        d = str[0] - C('0');
                         str = str[1 .. $];
                         if (_expect(d < 10, true))
                             goto FI;
@@ -247,7 +247,7 @@ struct Decimal(uint size64)
                 {
                     if (str.length == 0)
                         goto R;
-                    d = str[0] - '0';
+                    d = str[0] - C('0');
                     str = str[1 .. $];
                     if (d < 10)
                         return false;
@@ -260,7 +260,7 @@ struct Decimal(uint size64)
         S:
             if (str.length == 0)
                 goto R;
-            d = str[0] - '0';
+            d = str[0] - C('0');
             str = str[1 .. $];
 
             if (d < 10)
@@ -268,7 +268,7 @@ struct Decimal(uint size64)
         F0:
                 import mir.checkedint: mulu, addu;
                 bool overflow;
-                v = mulu(v, cast(uint)10, overflow);
+                v = mulu(v, 10u, overflow);
                 if (overflow)
                     return false;
                 v = addu(v, d, overflow);
@@ -283,7 +283,7 @@ struct Decimal(uint size64)
                 {
                     if (str.length == 0)
                         return false;
-                    d = str[0] - '0';
+                    d = str[0] - C('0');
                     str = str[1 .. $];
                     if (_expect(d < 10, true))
                         goto F0;
@@ -314,51 +314,76 @@ struct Decimal(uint size64)
                     import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
                     if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
                     {
-                        ulong multplier = 100000000;
-                        ulong value = parseEightDigits(str[0 .. 8]);
+                        multplier = 100000000;
+                        d = parseEightDigits(str[0 .. 8]);
                         str = str[8 .. $];
                         exponentShift -= 8;
-                        if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
+                        if (str.length >= 7)
                         {
-                            multplier = 100000000 * 100000000;
-                            value *= 100000000;
-                            value += parseEightDigits(str[0 .. 8]);
-                            str = str[8 .. $];
-                            exponentShift -= 8;
-                        }
-
-                        {
-                            import mir.checkedint: mulu, addu;
-                            bool overflow;
-                            v = mulu(v, multplier, overflow);
-                            if (overflow)
-                                return false;
-                            v = addu(v, value, overflow);
-                            if (overflow)
-                                return false;
+                            if (isMadeOfEightDigits((str.ptr - 1)[0 .. 8]))
+                            {
+                                multplier = 100000000 * 10000000;
+                                d -= str.ptr[-1] - '0';
+                                d *= 10000000;
+                                d += parseEightDigits((str.ptr - 1)[0 .. 8]);
+                                str = str[7 .. $];
+                                exponentShift -= 7;
+                                if (str.length)
+                                {
+                                    auto md = str[0] - C('0');
+                                    if (md < 10)
+                                    {
+                                        d *= 10;
+                                        multplier = 100000000 * 100000000;
+                                        d += md;
+                                        str = str[1 .. $];
+                                    }
+                                }
+                            }
+                            else
+                            {
+                            TrySix:
+                                if (isMadeOfEightDigits((str.ptr - 2)[0 .. 8]))
+                                {
+                                    multplier = 100000000 * 1000000;
+                                    d -= str.ptr[-1] - '0';
+                                    d -= (str.ptr[-2] - '0') * 10;
+                                    d *= 1000000;
+                                    d += parseEightDigits((str.ptr - 2)[0 .. 8]);
+                                    str = str[6 .. $];
+                                    exponentShift -= 6;
+                                }
+                            }
+      
                         }
+                        else
+                        if (str.length == 6)
+                            goto TrySix;
+                        goto FIL;
                     }
                 }
 
-                d = str[0] - '0';
+                d = str[0] - C('0');
                 str = str[1 .. $];
                 if (_expect(d >= 10, false))
                     goto DOB;
             FI:
+                exponentShift--;
+                multplier = 10;
+            FIL:
                 {
                     import mir.checkedint: mulu, addu;
                     bool overflow;
-                    v = mulu(v, cast(uint)10, overflow);
+                    v = mulu(v, multplier, overflow);
                     if (overflow)
                         return false;
                     v = addu(v, d, overflow);
                     if (overflow)
                         return false;
                 }
-                exponentShift--;
                 if (str.length == 0)
                     goto E;
-                d = str[0] - '0';
+                d = str[0] - C('0');
                 str = str[1 .. $];
                 if (d < 10)
                     goto FI;
@@ -369,7 +394,7 @@ struct Decimal(uint size64)
                     {
                         if (str.length == 0)
                             return false;
-                        d = str[0] - '0';
+                        d = str[0] - C('0');
                         str = str[1 .. $];
                         if (_expect(d < 10, true))
                             goto FI;
@@ -817,33 +842,33 @@ unittest
 
     // Check precise percentate parsing
     assert(decimal.fromStringImpl("71.7", key, -2));
-    assert(key == DecimalExponentKey.dot);
+    key.should == DecimalExponentKey.dot;
     // The result is exact value instead of 0.7170000000000001 = 71.7 / 100
-    assert(cast(double) decimal == 0.717);
+    (cast(double) decimal).should == 0.717;
 
     assert(decimal.fromStringImpl("+0.334e-5"w, key));
-    assert(key == DecimalExponentKey.e);
-    assert(cast(double) decimal == 0.334e-5);
+    key.should == DecimalExponentKey.e;
+    (cast(double) decimal).should == 0.334e-5;
 
     assert(decimal.fromStringImpl("100_000_000"w, key));
-    assert(key == DecimalExponentKey.none);
-    assert(cast(double) decimal == 1e8);
+    key.should == DecimalExponentKey.none;
+    (cast(double) decimal).should == 1e8;
 
     assert(decimal.fromStringImpl("-334D-5"d, key));
-    assert(key == DecimalExponentKey.D);
-    assert(cast(double) decimal == -334e-5);
+    key.should == DecimalExponentKey.D;
+    (cast(double) decimal).should == -334e-5;
 
     assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key));
-    assert(key == DecimalExponentKey.none);
-    assert(cast(double) decimal == 2482734692817364218734682973648217364981273648923423.0);
+    key.should == DecimalExponentKey.none;
+    (cast(double) decimal).should == 2482734692817364218734682973648217364981273648923423.0;
 
     assert(decimal.fromStringImpl(".023", key));
-    assert(key == DecimalExponentKey.dot);
-    assert(cast(double) decimal == .023);
+    key.should == DecimalExponentKey.dot;
+    (cast(double) decimal).should == .023;
 
     assert(decimal.fromStringImpl("0E100", key));
-    assert(key == DecimalExponentKey.E);
-    assert(cast(double) decimal == 0);
+    key.should == DecimalExponentKey.E;
+    (cast(double) decimal).should == 0;
 
     foreach (str; ["-nan", "-NaN", "-NAN"])
     {
@@ -851,8 +876,9 @@ unittest
         assert(decimal.coefficient.length > 0);
         assert(decimal.exponent == decimal.exponent.max);
         assert(decimal.coefficient.sign);
-        assert(key == DecimalExponentKey.nan);
-        assert(cast(double) decimal != cast(double) decimal);
+        key.should == DecimalExponentKey.nan;
+        auto nan = cast(double) decimal;
+        (cast(double) decimal).should == double.nan;
     }
 
     foreach (str; ["inf", "Inf", "INF"])
@@ -861,7 +887,7 @@ unittest
         assert(decimal.coefficient.length == 0);
         assert(decimal.exponent == decimal.exponent.max);
         assert(key == DecimalExponentKey.infinity);
-        assert(cast(double) decimal == double.infinity);
+        (cast(double) decimal).should == double.infinity;
     }
 
     assert(decimal.fromStringImpl("-inf", key));
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index deb37836..f8650b05 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -258,9 +258,6 @@ private T algorithmM(T)(scope const size_t[] coefficients, long exponent)
 {
     pragma(inline, false);
 
-    // import mir.stdio;
-    // debug dump("algorithmM", coefficients, exponent);
-
     import mir.bitop: ctlz;
     import mir.bignum.fp: Fp;
     import mir.bignum.integer: BigInt;
diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
index 01fd55ab..c0634d4f 100644
--- a/source/mir/bignum/internal/parse.d
+++ b/source/mir/bignum/internal/parse.d
@@ -1,5 +1,10 @@
 module mir.bignum.internal.parse;
 
+/+
+https://arxiv.org/abs/2101.11408
+Number Parsing at a Gigabyte per Second
+Daniel Lemire
++/
 bool isMadeOfEightDigits()(ref const char[8] chars)
 {
     pragma(inline, true);
@@ -7,6 +12,7 @@ bool isMadeOfEightDigits()(ref const char[8] chars)
     return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080));
 }
 
+// ditto
 uint parseEightDigits()(ref const char[8] chars)
 {
     pragma(inline, true);
diff --git a/source/mir/parse.d b/source/mir/parse.d
index ecce011c..ed9a6fb0 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -24,8 +24,7 @@ unittest
     "123.0".SmallString!32.to!double.should == 123;
 }
 
-import mir.primitives;
-import std.traits: isMutable, isFloatingPoint, isSomeChar;
+import std.traits: isMutable, isFloatingPoint, isSomeChar, isSigned, isUnsigned, Unsigned;
 
 /++
 Performs `nothrow` and `@nogc` string to native type conversion.
@@ -39,42 +38,47 @@ Floating_point:
     Mir parsing supports up-to quadruple precision.
 The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.+/
-T fromString(T, C)(scope const(C)[] str)
+template fromString(T)
     if (isMutable!T)
 {
-    import mir.utility: _expect;
-    static immutable excfp = new Exception("fromString failed to parse " ~ T.stringof);
-
-    static if (isFloatingPoint!T)
-    {
-        T value;
-        if (_expect(fromString(str, value), true))
-            return value;
-        version (D_Exceptions)
-            throw excfp;
-        else
-            assert(0);
-    }
-    else
+    ///
+    T fromString(C)(scope const(C)[] str)
+        if (isSomeChar!C)
     {
-        static immutable excne = new Exception("fromString: remaining input is not empty after parsing " ~ T.stringof);
+        import mir.utility: _expect;
+        static immutable excfp = new Exception("fromString failed to parse " ~ T.stringof);
 
-        T value;
-        if (_expect(parse!T(str, value), true))
+        static if (isFloatingPoint!T)
         {
-            if (_expect(str.empty, true))
+            T value;
+            if (_expect(.fromString(str, value), true))
                 return value;
             version (D_Exceptions)
-                throw excne;
+                throw excfp;
             else
                 assert(0);
         }
         else
         {
-            version (D_Exceptions)
-                throw excfp;
+            static immutable excne = new Exception("fromString: remaining input is not empty after parsing " ~ T.stringof);
+
+            T value;
+            if (_expect(parse!T(str, value), true))
+            {
+                if (_expect(str.length == 0, true))
+                    return value;
+                version (D_Exceptions)
+                    throw excne;
+                else
+                    assert(0);
+            }
             else
-                assert(0);
+            {
+                version (D_Exceptions)
+                    throw excfp;
+                else
+                    assert(0);
+            }
         }
     }
 }
@@ -223,7 +227,7 @@ version(mir_bignum_test)
 /++
 Performs `nothrow` and `@nogc` string to native type conversion.
 
-Returns: true if success and false otherwise.
+Rseturns: true if success and false otherwise.
 +/
 bool fromString(T, C)(scope const(C)[] str, ref T value)
     if (isSomeChar!C)
@@ -249,7 +253,7 @@ bool fromString(T, C)(scope const(C)[] str, ref T value)
     }
     else
     {
-        return parse!T(str, value) && str.empty;
+        return parse!T(str, value) && str.length == 0;
     }
 }
 
@@ -266,13 +270,13 @@ Single character parsing utilities.
 
 Returns: true if success and false otherwise.
 +/
-bool parse(T, Range)(scope ref Range r, scope ref T value)
-    if (isInputRange!Range && isSomeChar!T)
+bool parse(T, C)(ref scope inout(C)[] str, ref scope T value)
+    if (isSomeChar!C && isSomeChar!T && T.sizeof == C.sizeof)
 {
-    if (r.empty)
+    if (str.length == 0)
         return false;
-    value = r.front;
-    r.popFront;
+    value = str[0];
+    str = str[1 .. $];
     return true;
 }
 
@@ -292,188 +296,137 @@ Integer parsing utilities.
 
 Returns: true if success and false otherwise.
 +/
-bool parse(T, Range)(scope ref Range r, scope ref T value)
-    if ((is(T == byte) || is(T == short)) && isInputRange!Range && !__traits(isUnsigned, T))
+bool parse(T, C)(ref scope inout(C)[] str, out scope T value)
+    if ((is(T == byte) || is(T == short)) && isSomeChar!C)
 {
     int lvalue;
-    auto ret = parse!(int, Range)(r, lvalue);
+    auto ret = .parse!(int, C)(str, lvalue);
     value = cast(T) lvalue;
     return ret && value == lvalue;
 }
 
-/// ditto
-bool parse(T, Range)(scope ref Range r, scope ref T value)
-    if (is(T == int) && isInputRange!Range && !__traits(isUnsigned, T))
-{
-    version(LDC) pragma(inline, true);
-    return parseSignedImpl!(int, Range)(r, value);
-}
-
-/// ditto
-bool parse(T, Range)(scope ref Range r, scope ref T value)
-    if (is(T == long) && isInputRange!Range && !__traits(isUnsigned, T))
-{
-    version(LDC) pragma(inline, true);
-    return parseSignedImpl!(long, Range)(r, value);
-}
-
-/// ditto
-bool parse(T, Range)(scope ref Range r, scope ref T value)
-    if ((is(T == ubyte) || is(T == ushort)) && isInputRange!Range && __traits(isUnsigned, T))
+bool parse(T, C)(ref scope inout(C)[] str, out scope T value)
+    if ((is(T == ubyte) || is(T == ushort)) && isSomeChar!C)
 {
     uint lvalue;
-    auto ret = parse!(uint, Range)(r, lvalue);
+    auto ret = .parse!(uint, C)(str, lvalue);
     value = cast(T) lvalue;
     return ret && value == lvalue;
 }
 
-/// ditto
-bool parse(T, Range)(scope ref Range r, scope ref T value)
-    if (is(T == uint) && isInputRange!Range && __traits(isUnsigned, T))
-{
-    version(LDC) pragma(inline, true);
-    return parseUnsignedImpl!(uint, Range)(r, value);
-}
-
-/// ditto
-bool parse(T, Range)(scope ref Range r, scope ref T value)
-    if (is(T == ulong) && isInputRange!Range && __traits(isUnsigned, T))
-{
-    version(LDC) pragma(inline, true);
-    return parseUnsignedImpl!(ulong, Range)(r, value);
-}
-
-
 ///
 version (mir_test) unittest
 {
+    import mir.test: should;
     import std.meta: AliasSeq;
-    foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+    foreach (T; AliasSeq!(
+        byte, ubyte, short, ushort,
+        int, uint, long, ulong))
     {
         auto str = "123";
         T val;
         assert(parse(str, val));
-        assert(val == 123);
+        val.should == 123;
         str = "0";
         assert(parse(str, val));
-        assert(val == 0);
+        val.should == 0;
         str = "9";
         assert(parse(str, val));
-        assert(val == 9);
+        val.should == 9;
         str = "";
         assert(!parse(str, val));
-        assert(val == 0);
+        val.should == 0;
         str = "text";
         assert(!parse(str, val));
-        assert(val == 0);
+        val.should == 0;
     }
 }
 
 ///
 version (mir_test) unittest
 {
+    import mir.test: should;
     import std.meta: AliasSeq;
     foreach (T; AliasSeq!(byte, short, int, long))
     {
         auto str = "-123";
         T val;
         assert(parse(str, val));
-        assert(val == -123);
+        val.should == -123;
         str = "-0";
         assert(parse(str, val));
-        assert(val == 0);
+        val.should == 0;
         str = "-9text";
         assert(parse(str, val));
-        assert(val == -9);
+        val.should == -9;
         assert(str == "text");
         enum m = T.min + 0;
         str = m.stringof;
         assert(parse(str, val));
-        assert(val == T.min);
+        val.should == T.min;
     }
 }
 
-private bool parseUnsignedImpl(T, Range)(scope ref Range r, scope ref T value)
-    if(__traits(isUnsigned, T))
+bool parse(T, C)(ref scope inout(C)[] str, scope out T value)
+    if ((isSigned!T || isUnsigned!T) && T.sizeof >= uint.sizeof && isSomeChar!C)
 {
     version(LDC) pragma(inline, true);
     import mir.checkedint: addu, mulu;
 
-    bool sign;
-B:
-    if (!r.empty)
+    if (str.length == 0)
+        return false;
+
+    Unsigned!T x = str[0] - C('0');
+
+    static if (isSigned!T)
+        bool sign;
+
+    if (x >= 10)
     {
-        auto f = r.front + 0u;
-        if (!sign && f == '+')
-        {
-            r.popFront;
-            sign = true;
-            goto B;
-        }
-        uint c = f - '0';
-        if (c >= 10)
-            goto F;
-        T x = c;
-        for(;;)
+        static if (isSigned!T)
         {
-            r.popFront;
-            if (r.empty)
-                break;
-            c = r.front - '0';
-            if (c >= 10)
-                break;
-            bool overflow;
-            T y = mulu(x, cast(uint)10, overflow);
-            if (overflow)
-                goto R;
-            x = y;
-            T z = addu(x, cast(uint)c, overflow);
-            if (overflow)
-                goto R;
-            x = z;
+            if (x == C('-') - C('0'))
+            {
+                sign = true;
+                goto S;
+            }
         }
-        value = x;
-        return true;
+        
+        if (x != C('+') - C('0'))
+            return false;
+    S:
+        str = str[1 .. $];
+        if (str.length == 0)
+            return false;
+        x = str[0] - C('0');
+        if (x >= 10)
+            return false;
     }
-F:  value = 0;
-R:  return false;
-}
 
-private bool parseSignedImpl(T, Range)(scope ref Range r, scope ref T value)
-    if(!__traits(isUnsigned, T))
-{
-    version(LDC) pragma(inline, true);
-    import core.checkedint: negs;
-    import std.traits: Unsigned;
+    str = str[1 .. $];
 
-    bool sign;
-B:
-    if (!r.empty)
+    while (str.length)
     {
-        auto f = r.front + 0u;
-        if (!sign && f == '-')
-        {
-            r.popFront;
-            sign = true;
-            goto B;
-        }
-        auto retu = (()@trusted=>parse(r, *cast(Unsigned!T*) &value))();
-        // auto retu = false;
-        if (!retu)
-            goto R;
-        if (!sign)
-        {
-            if (value < 0)
-                goto R;
-        }
-        else
-        {
-            if (value < 0 && value != T.min)
-                goto R;
-            value = -value;
-        }
-        return true;
+        uint c = str[0] - C('0');
+        if (c >= 10)
+            break;
+        str = str[1 .. $];
+        bool overflow;
+        x = x.mulu(10u, overflow);
+        if (overflow)
+            return false;
+        x = x.addu(c, overflow);
+        if (overflow)
+            return false;
     }
-F:  value = 0;
-R:  return false;
+
+    static if (isSigned!T)
+    {
+        if (x > Unsigned!T(T.max + sign))
+            return false;
+        x = sign ? -x : x;
+    }
+
+    value = x;
+    return true;
 }
diff --git a/source/mir/test.d b/source/mir/test.d
index 217af0c6..ca6c7b8b 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -69,6 +69,11 @@ struct Should(T)
     void opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__)
     {
         import mir.format: stringBuf, getData;
+        static if (__traits(isFloating, R))
+        {
+            if (expected != expected && value != value)
+                return;
+        }
         if (value == expected)
             return;
         auto buf = stringBuf;

From be77e72468b686bc5134000dfb8685b45c26179b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Jun 2022 18:21:23 +0400
Subject: [PATCH 430/573] new JSON number parsing API

---
 dub.sdl                                |   4 +-
 source/mir/bignum/decimal.d            | 284 +-------------------
 source/mir/bignum/internal/dec2float.d |   3 +-
 source/mir/bignum/internal/parse.d     | 342 +++++++++++++++++++++++++
 source/mir/bignum/low_level_view.d     | 311 ++++------------------
 source/mir/math/sum.d                  |   2 +-
 source/mir/parse.d                     |  32 ++-
 7 files changed, 427 insertions(+), 551 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 938f1be3..89e205f9 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -13,8 +13,8 @@ dependency "mir-core" version=">=1.1.106"
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
-    versions "mir_ndslice_test"
-    versions "mir_test"
+    // versions "mir_ndslice_test"
+    // versions "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 856e795f..adfe4914 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -9,7 +9,7 @@ module mir.bignum.decimal;
 import mir.serde: serdeProxy, serdeScoped;
 import std.traits: isSomeChar;
 ///
-public import mir.bignum.low_level_view: DecimalExponentKey;
+public import mir.parse: DecimalExponentKey;
 import mir.bignum.low_level_view: ceilLog10Exp2;
 
 private enum expBufferLength = 2 + ceilLog10Exp2(ulong.sizeof * 8);
@@ -180,283 +180,6 @@ struct Decimal(uint size64)
         scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
     {
-        enum optimize = size_t.sizeof == 8 && size64 == 1;
-        version(LDC)
-        {
-            static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false)
-                pragma(inline, true);
-        }
-        static if (optimize)
-        {
-            import mir.utility: _expect;
-            static if (checkEmpty)
-            {
-                if (_expect(str.length == 0, false))
-                    return false;
-            }
-
-            coefficient.sign = str[0] == '-';
-            if (coefficient.sign)
-            {
-                str = str[1 .. $];
-                if (_expect(str.length == 0, false))
-                    return false;
-            }
-            else
-            static if (allowStartingPlus)
-            {
-                if (_expect(str[0] == '+', false))
-                {
-                    str = str[1 .. $];
-                    if (_expect(str.length == 0, false))
-                        return false;
-                }
-            }
-
-            ulong d = str[0] - C('0');
-            str = str[1 .. $];
-            exponent = 0;
-
-            ulong v, multplier = void;
-
-            if (_expect(d >= 10, false))
-            {
-                static if (allowDotOnBounds)
-                {
-                    if (d == '.' - '0')
-                    {
-                        if (str.length == 0)
-                            return false;
-                        key = DecimalExponentKey.dot;
-                        d = str[0] - C('0');
-                        str = str[1 .. $];
-                        if (_expect(d < 10, true))
-                            goto FI;
-                        return false;
-                    }
-                }
-                static if (allowSpecialValues)
-                    goto NI;
-                else
-                    return false;
-            }
-
-            static if (!allowLeadingZeros)
-            {
-                if (_expect(d == 0, false))
-                {
-                    if (str.length == 0)
-                        goto R;
-                    d = str[0] - C('0');
-                    str = str[1 .. $];
-                    if (d < 10)
-                        return false;
-                    goto DOT;
-                }
-            }
-
-            v = d;
-
-        S:
-            if (str.length == 0)
-                goto R;
-            d = str[0] - C('0');
-            str = str[1 .. $];
-
-            if (d < 10)
-            {
-        F0:
-                import mir.checkedint: mulu, addu;
-                bool overflow;
-                v = mulu(v, 10u, overflow);
-                if (overflow)
-                    return false;
-                v = addu(v, d, overflow);
-                if (overflow)
-                    return false;
-                goto S;
-            }
-
-            static if (allowUnderscores)
-            {
-                if (d == '_' - '0')
-                {
-                    if (str.length == 0)
-                        return false;
-                    d = str[0] - C('0');
-                    str = str[1 .. $];
-                    if (_expect(d < 10, true))
-                        goto F0;
-                    return false;
-                }
-            }
-        DOT:
-            if (d == DecimalExponentKey.dot)
-            {
-                // The dot modifier CANNOT be preceded by any modifiers. 
-                if (key != DecimalExponentKey.none)
-                    return false;
-
-                key = DecimalExponentKey.dot;
-
-                if (str.length == 0)
-                {
-                    static if (allowDotOnBounds)
-                        goto R;
-                    else
-                        return false;
-                }
-
-            IF:
-
-                static if (is(C == char))
-                {
-                    import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
-                    if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
-                    {
-                        multplier = 100000000;
-                        d = parseEightDigits(str[0 .. 8]);
-                        str = str[8 .. $];
-                        exponentShift -= 8;
-                        if (str.length >= 7)
-                        {
-                            if (isMadeOfEightDigits((str.ptr - 1)[0 .. 8]))
-                            {
-                                multplier = 100000000 * 10000000;
-                                d -= str.ptr[-1] - '0';
-                                d *= 10000000;
-                                d += parseEightDigits((str.ptr - 1)[0 .. 8]);
-                                str = str[7 .. $];
-                                exponentShift -= 7;
-                                if (str.length)
-                                {
-                                    auto md = str[0] - C('0');
-                                    if (md < 10)
-                                    {
-                                        d *= 10;
-                                        multplier = 100000000 * 100000000;
-                                        d += md;
-                                        str = str[1 .. $];
-                                    }
-                                }
-                            }
-                            else
-                            {
-                            TrySix:
-                                if (isMadeOfEightDigits((str.ptr - 2)[0 .. 8]))
-                                {
-                                    multplier = 100000000 * 1000000;
-                                    d -= str.ptr[-1] - '0';
-                                    d -= (str.ptr[-2] - '0') * 10;
-                                    d *= 1000000;
-                                    d += parseEightDigits((str.ptr - 2)[0 .. 8]);
-                                    str = str[6 .. $];
-                                    exponentShift -= 6;
-                                }
-                            }
-      
-                        }
-                        else
-                        if (str.length == 6)
-                            goto TrySix;
-                        goto FIL;
-                    }
-                }
-
-                d = str[0] - C('0');
-                str = str[1 .. $];
-                if (_expect(d >= 10, false))
-                    goto DOB;
-            FI:
-                exponentShift--;
-                multplier = 10;
-            FIL:
-                {
-                    import mir.checkedint: mulu, addu;
-                    bool overflow;
-                    v = mulu(v, multplier, overflow);
-                    if (overflow)
-                        return false;
-                    v = addu(v, d, overflow);
-                    if (overflow)
-                        return false;
-                }
-                if (str.length == 0)
-                    goto E;
-                d = str[0] - C('0');
-                str = str[1 .. $];
-                if (d < 10)
-                    goto FI;
-
-                static if (allowUnderscores)
-                {
-                    if (d == '_' - '0')
-                    {
-                        if (str.length == 0)
-                            return false;
-                        d = str[0] - C('0');
-                        str = str[1 .. $];
-                        if (_expect(d < 10, true))
-                            goto FI;
-                        return false;
-                    }
-                }
-            DOB:
-                static if (!allowDotOnBounds)
-                {
-                    if (exponentShift == 0)
-                        return false;
-                }
-
-            }
-
-            static if (allowExponent)
-            {
-                if (d == DecimalExponentKey.e
-                 || d == DecimalExponentKey.E
-                 || d == DecimalExponentKey.d && allowDExponent
-                 || d == DecimalExponentKey.D && allowDExponent
-                )
-                {
-                    import mir.parse: parse;
-                    key = cast(DecimalExponentKey)d;
-                    if (parse(str, exponent) && str.length == 0)
-                        goto E;
-                }
-            }
-            return false;
-
-        E:
-            exponent += exponentShift;
-        R:
-            coefficient.data[0] = v;
-            coefficient.length = v != 0;
-            return true;
-
-            static if (allowSpecialValues)
-            {
-            NI:
-                exponent = exponent.max;
-                if (str.length == 2)
-                {
-                    auto stail = cast(C[2])str[0 .. 2];
-                    if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
-                    {
-                        key = DecimalExponentKey.infinity;
-                        goto R;
-                    }
-                    if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
-                    {
-                        v = 1;
-                        key = DecimalExponentKey.nan;
-                        goto R;
-                    }
-                }
-                return false;
-            }
-        }
-        else
-        {
             import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10;
             auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data));
             auto ret = work.fromStringImpl!(C,
@@ -473,7 +196,6 @@ struct Decimal(uint size64)
             coefficient.sign = work.sign;
             exponent = work.exponent;
             return ret;
-        }
     }
 
     private enum coefficientBufferLength = 2 + ceilLog10Exp2(coefficient.data.length * (size_t.sizeof * 8)); // including dot and sign
@@ -723,13 +445,13 @@ struct Decimal(uint size64)
 }
 
 ///
-version(mir_bignum_test) 
+version(mir_bignum_test)
 @safe pure nothrow @nogc
 unittest
 {
     import mir.test: should;
     import mir.conv: to;
-    Decimal!256 decimal = void;
+    Decimal!128 decimal = void;
     DecimalExponentKey key;
 
     assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index f8650b05..282fe687 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -241,7 +241,7 @@ private template bigSize(T)
 {
     static if (T.mant_dig < 64)
     {
-        enum size_t bigSize = 83;
+        enum size_t bigSize = 128;
     }
     else
     {
@@ -533,6 +533,7 @@ private auto _load(uint size : 128)(long e) @trusted
     return Fp!128(false, p10exp, p10coeff);
 }
 
+version(mir_test)
 unittest
 {
     import mir.bignum.fp;
diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
index c0634d4f..0663ce5a 100644
--- a/source/mir/bignum/internal/parse.d
+++ b/source/mir/bignum/internal/parse.d
@@ -1,5 +1,8 @@
 module mir.bignum.internal.parse;
 
+import std.traits: isSomeChar;
+import mir.parse: DecimalExponentKey;
+
 /+
 https://arxiv.org/abs/2101.11408
 Number Parsing at a Gigabyte per Second
@@ -21,3 +24,342 @@ uint parseEightDigits()(ref const char[8] chars)
     val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
     return uint((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
 }
+
+/++
++/
+struct SmallDecimalParsingResult
+{
+    ///
+    bool success;
+    ///
+    bool sign;
+    ///
+    DecimalExponentKey key;
+    ///
+    long exponent;
+    ///
+    ulong coefficient;
+}
+
+/++
++/
+SmallDecimalParsingResult parseJsonNumberImpl(scope const(char)[] str)
+    in (str.length)
+{
+    pragma(inline, false);
+
+    alias W = ulong;
+    enum bool allowSpecialValues = false;
+    enum bool allowDotOnBounds = false;
+    enum bool allowDExponent = false;
+    enum bool allowStartingPlus = false;
+    enum bool allowUnderscores = false;
+    enum bool allowLeadingZeros = false;
+    enum bool allowExponent = true;
+    enum bool checkEmpty = false;
+
+    typeof(return) result;
+
+    bool mullAdd(ulong multplier, ulong carry)
+    {
+        import mir.checkedint: mulu, addu;
+        bool overflow;
+        result.coefficient = mulu(result.coefficient, multplier, overflow);
+        if (overflow)
+            return overflow;
+        result.coefficient = addu(result.coefficient, carry, overflow);
+        return overflow;
+    }
+
+    alias impl = decimalFromStringImpl!(mullAdd, ulong);
+    alias specialization = impl!(char,
+        allowSpecialValues,
+        allowDotOnBounds,
+        allowDExponent,
+        allowStartingPlus,
+        allowUnderscores,
+        allowLeadingZeros,
+        allowExponent,
+        checkEmpty,
+    );
+
+    with(result)
+        success = specialization(str, key, exponent, sign);
+    return result;
+}
+
+/++
+Returns: false in case of overflow or incorrect string.
++/
+template decimalFromStringImpl(alias mullAdd, W = size_t)
+{
+    bool decimalFromStringImpl(C,
+        bool allowSpecialValues,
+        bool allowDotOnBounds,
+        bool allowDExponent,
+        bool allowStartingPlus,
+        bool allowUnderscores,
+        bool allowLeadingZeros,
+        bool allowExponent,
+        bool checkEmpty,
+    )
+        (scope const(C)[] str, out DecimalExponentKey key, scope ref long exponent, scope ref bool sign)
+        scope @trusted pure @nogc nothrow
+        if (isSomeChar!C)
+    {
+        version(LDC)
+            pragma(inline, true);
+
+        import mir.utility: _expect;
+        static if (checkEmpty)
+        {
+            if (_expect(str.length == 0, false))
+                return false;
+        }
+
+        sign = str[0] == '-';
+        if (sign)
+        {
+            str = str[1 .. $];
+            if (_expect(str.length == 0, false))
+                return false;
+        }
+        else
+        static if (allowStartingPlus)
+        {
+            if (_expect(str[0] == '+', false))
+            {
+                str = str[1 .. $];
+                if (_expect(str.length == 0, false))
+                    return false;
+            }
+        }
+
+        ulong d = str[0] - C('0');
+        str = str[1 .. $];
+
+        ulong v;
+
+        if (_expect(d >= 10, false))
+        {
+            static if (allowDotOnBounds)
+            {
+                if (d == '.' - '0')
+                {
+                    if (str.length == 0)
+                        return false;
+                    key = DecimalExponentKey.dot;
+                    d = str[0] - C('0');
+                    str = str[1 .. $];
+                    if (_expect(d < 10, true))
+                        goto FI;
+                    return false;
+                }
+            }
+            static if (allowSpecialValues)
+                goto NI;
+            else
+                return false;
+        }
+
+        static if (!allowLeadingZeros)
+        {
+            if (_expect(d == 0, false))
+            {
+                if (str.length == 0)
+                    return true;
+                d = str[0] - C('0');
+                str = str[1 .. $];
+                if (d < 10)
+                    return false;
+                goto DOT;
+            }
+        }
+
+        goto F0;
+
+    S:
+        if (str.length == 0)
+            return true;
+        d = str[0] - C('0');
+        str = str[1 .. $];
+
+        if (d < 10)
+        {
+    F0:
+            if (_expect(mullAdd(10u, cast(uint)d), false))
+                return false;
+            goto S;
+        }
+
+        static if (allowUnderscores)
+        {
+            if (d == '_' - '0')
+            {
+                if (str.length == 0)
+                    return false;
+                d = str[0] - C('0');
+                str = str[1 .. $];
+                if (_expect(d < 10, true))
+                    goto F0;
+                return false;
+            }
+        }
+    DOT:
+        if (d == DecimalExponentKey.dot)
+        {
+            // The dot modifier CANNOT be preceded by any modifiers. 
+            if (key != DecimalExponentKey.none)
+                return false;
+
+            key = DecimalExponentKey.dot;
+
+            if (str.length == 0)
+            {
+                return allowDotOnBounds;
+            }
+
+        IF:
+            ulong multplier = 10;
+            static if (is(C == char) && is(W == ulong))
+            {
+                import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
+                if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
+                {
+                    multplier = 100000000;
+                    d = parseEightDigits(str[0 .. 8]);
+                    str = str[8 .. $];
+                    exponent -= 8;
+                    if (str.length >= 7)
+                    {
+                        if (isMadeOfEightDigits((str.ptr - 1)[0 .. 8]))
+                        {
+                            multplier = 100000000UL * 10000000;
+                            d -= str.ptr[-1] - '0';
+                            d *= 10000000;
+                            d += parseEightDigits((str.ptr - 1)[0 .. 8]);
+                            str = str[7 .. $];
+                            exponent -= 7;
+                            if (str.length)
+                            {
+                                auto md = str[0] - C('0');
+                                if (md < 10)
+                                {
+                                    d *= 10;
+                                    multplier = 100000000UL * 100000000;
+                                    d += md;
+                                    str = str[1 .. $];
+                                    exponent -= 1;
+                                }
+                            }
+                        }
+                        else
+                        {
+                        TrySix:
+                            if (isMadeOfEightDigits((str.ptr - 2)[0 .. 8]))
+                            {
+                                multplier = 100000000UL * 1000000;
+                                d -= str.ptr[-1] - '0';
+                                d -= (str.ptr[-2] - '0') * 10;
+                                d *= 1000000;
+                                d += parseEightDigits((str.ptr - 2)[0 .. 8]);
+                                str = str[6 .. $];
+                                exponent -= 6;
+                            }
+                        }
+
+                    }
+                    else
+                    if (str.length == 6)
+                        goto TrySix;
+                    goto FIL;
+                }
+            }
+
+            d = str[0] - C('0');
+            str = str[1 .. $];
+            if (_expect(d >= 10, false))
+                goto DOB;
+        FI:
+            exponent--;
+            multplier = 10;
+        FIL:
+            if (_expect(mullAdd(multplier, d), false))
+                return false;
+            import mir.stdio;
+            // debug dump("str = ", str);
+            if (str.length == 0)
+                return true;
+            d = str[0] - C('0');
+            str = str[1 .. $];
+            if (d < 10)
+                goto FI;
+
+            static if (allowUnderscores)
+            {
+                if (d == '_' - '0')
+                {
+                    if (str.length == 0)
+                        return false;
+                    d = str[0] - C('0');
+                    str = str[1 .. $];
+                    if (_expect(d < 10, true))
+                        goto FI;
+                    return false;
+                }
+            }
+        DOB:
+            static if (!allowDotOnBounds)
+            {
+                if (exponent == 0)
+                    return false;
+            }
+        }
+
+        static if (allowExponent)
+        {
+            if (d == DecimalExponentKey.e
+                || d == DecimalExponentKey.E
+                || d == DecimalExponentKey.d && allowDExponent
+                || d == DecimalExponentKey.D && allowDExponent
+            )
+            {
+                import mir.parse: parse;
+                key = cast(DecimalExponentKey)d;
+                long exponentPlus;
+                if (parse(str, exponentPlus) && str.length == 0)
+                {
+                    import mir.stdio;
+                    // debug dump("exponentPlus ", exponentPlus);
+                    // debug dump("exponent ", exponent);
+                    import mir.checkedint: adds;
+                    bool overflow;
+                    exponent = exponent.adds(exponentPlus, overflow);
+                    // debug dump("overflow ", overflow);
+                    return !overflow;
+                }
+            }
+        }
+        return false;
+
+        static if (allowSpecialValues)
+        {
+        NI:
+            if (str.length == 2)
+            {
+                auto stail = cast(C[2])str[0 .. 2];
+                if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
+                {
+                    key = DecimalExponentKey.infinity;
+                    return true;
+                }
+                if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
+                {
+                    key = DecimalExponentKey.nan;
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 937a339b..dac48e44 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -653,9 +653,8 @@ struct BigUIntView(W)
     W opOpAssign(string op : "*")(W rhs, W overflow = 0u)
         @safe pure nothrow @nogc scope
     {
-        assert(coefficients.length);
         auto ns = this.coefficients;
-        do
+        while (ns.length)
         {
             import mir.utility: extMul;
             auto ext = ns[0].extMul(rhs);
@@ -664,7 +663,6 @@ struct BigUIntView(W)
             overflow = ext.high + overflowM;
             ns = ns[1 .. $];
         }
-        while (ns.length);
         return overflow;
     }
 
@@ -1949,32 +1947,13 @@ unittest
     assert(bigView == BigUIntView!size_t.fromHexString("c73fd2b26f2514c103c324943b6c90a05d2732118d5f0099c36a69a8051bb0573adc825b5c9295896c70280faa4c4d369df8e92f82bfffafe078b52ae695d316"));
 }
 
-///
-enum DecimalExponentKey
-{
-    ///
-    none = 0,
-    ///
-    infinity = 1,
-    ///
-    nan = 2,
-    ///
-    dot = '.' - '0',
-    ///
-    d = 'd' - '0',
-    ///
-    e = 'e' - '0',
-    ///
-    D = 'D' - '0',
-    ///
-    E = 'E' - '0',
-}
-
 /++
 +/
 struct DecimalView(W)
     if (is(Unqual!W == size_t))
 {
+    import mir.parse: DecimalExponentKey;
+
     ///
     bool sign;
     ///
@@ -1982,7 +1961,7 @@ struct DecimalView(W)
     ///
     BigUIntView!W coefficient;
 
-    static if (isMutable!W && W.sizeof >= 4)
+    static if (is(W == size_t))
     /++
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients
@@ -2000,259 +1979,61 @@ struct DecimalView(W)
         (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0)
         scope @trusted pure @nogc nothrow
         if (isSomeChar!C)
+        in (coefficient.length)
     {
-        import mir.utility: _expect;
-
-        version(LDC)
-        {
-            static if ((allowSpecialValues && allowDExponent && allowStartingPlus && allowDotOnBounds && checkEmpty) == false)
-                pragma(inline, true);
-        }
-
-        assert(coefficient.coefficients.length);
-
-        coefficient.coefficients[0] = 0;
-        auto work = coefficient.topLeastSignificantPart(1);
-
-        static if (checkEmpty)
-        {
-            if (_expect(str.length == 0, false))
-                return false;
-        }
-
-        if (str[0] == '-')
-        {
-            sign = true;
-            str = str[1 .. $];
-            if (_expect(str.length == 0, false))
-                return false;
-        }
-        else
-        static if (allowStartingPlus)
-        {
-            if (_expect(str[0] == '+', false))
-            {
-                str = str[1 .. $];
-                if (_expect(str.length == 0, false))
-                    return false;
-            }
-        }
-
-        uint d = str[0] - '0';
-        str = str[1 .. $];
-
-        W v;
-        W t = 1;
-        bool dot;
+        pragma(inline, false);
 
-        static if (allowUnderscores)
-        {
-            bool recentUnderscore;
-        }
+        import mir.utility: _expect;
 
-        // Was there a recent character within the set: ['.', 'e', 'E', 'd', 'D']?
-        bool recentModifier;
+        BigUIntView!W work;
 
-        static if (!allowLeadingZeros)
+        bool mullAdd(W rhs, W overflow)
         {
-            if (d == 0)
+            import mir.stdio;
+            // debug dump(rhs, overflow, work);
+            if ((overflow = work.opOpAssign!"*"(rhs, overflow)) != 0)
             {
-                if (str.length == 0)
+                if (_expect(work.coefficients.length < coefficient.coefficients.length, true))
                 {
-                    coefficient = coefficient.init;
-                    return true;
-                }
-                if (str[0] >= '0' && str[0] <= '9')
-                    return false;
-                goto S;
-            }
-        }
-
-        if (d < 10)
-        {
-            goto S;
-        }
-
-        static if (allowDotOnBounds)
-        {
-            if (d == '.' - '0')
-            {
-                if (str.length == 0)
+                    work = coefficient.topLeastSignificantPart(work.coefficients.length + 1);
+                    work.coefficients[$ - 1] = overflow;
+                // debug dump(overflow, work);
                     return false;
-                key = DecimalExponentKey.dot;
-                dot = true;
-                recentModifier = true;
-                goto F;
-            }
-        }
-
-        static if (allowSpecialValues)
-        {
-            goto NI;
-        }
-        else
-        {
-            return false;
-        }
-
-        F: for(;;)
-        {
-            enum mp10 = W(10) ^^ MaxWordPow10!W;
-            d = str[0] - '0';
-            str = str[1 .. $];
-
-            if (_expect(d <= 10, true))
-            {
-                static if (allowUnderscores)
-                {
-                    recentUnderscore = false;
-                }
-                recentModifier = false;
-                v *= 10;
-        S:
-                t *= 10;
-                v += d;
-                exponentShift -= dot;
-
-                if (_expect(t == mp10 || str.length == 0, false))
-                {
-                L:
-                    if (auto overflow = work.opOpAssign!"*"(t, v))
-                    {
-                        if (_expect(work.coefficients.length < coefficient.coefficients.length, true))
-                        {
-                            work = coefficient.topLeastSignificantPart(work.coefficients.length + 1);
-                            work.coefficients[$ - 1] = overflow;
-                        }
-                        else
-                        {
-                            return false;
-                        }
-                    }
-
-                    v = 0;
-                    t = 1;
-                    if (str.length == 0)
-                    {
-                    M:
-                        exponent += exponentShift;
-                        coefficient = work.coefficients[$ - 1] == 0 ? coefficient.init : work;
-                        static if (allowUnderscores)
-                        {
-                            // If we have no characters, then we should return true IF
-                            // the last character was NOT a underscore OR a modifier
-                            return !recentUnderscore && !recentModifier;
-                        }
-                        else
-                        {
-                            // If we don't allow underscores, and we have no characters,
-                            // then we should return true IF the character was NOT a modifier.
-                            return !recentModifier;
-                        }
-                    }
-                }
-
-                continue;
-            }
-
-            switch (d)
-            {
-                case DecimalExponentKey.dot:
-                    // The dot modifier CANNOT be preceded by any modifiers. 
-                    if (recentModifier || key != DecimalExponentKey.none)
-                        return false;
-
-                    static if (allowUnderscores)
-                    {
-                        // If we allow underscores, the dot also CANNOT be preceded by any underscores.
-                        // It must be preceded by a number.
-                        if (recentUnderscore)
-                            return false;
-                    }
-
-                    key = DecimalExponentKey.dot;
-                    if (_expect(dot, false))
-                        break;
-                    dot = true;
-                    if (str.length)
-                    {
-                        recentModifier = true;
-                        continue;
-                    }
-                    static if (allowDotOnBounds)
-                    {
-                        goto L;
-                    }
-                    else
-                    {
-                        return false;
-                    }
-                static if (allowExponent)
-                {
-                    static if (allowDExponent)
-                    {
-                        case DecimalExponentKey.d:
-                        case DecimalExponentKey.D:
-                            goto case DecimalExponentKey.e;
-                    }
-                    case DecimalExponentKey.e:
-                    case DecimalExponentKey.E:
-                        import mir.parse: parse;
-                        // We don't really care if the e/E/d/D modifiers are preceded by a modifier,
-                        // so as long as they are a dot or a regular number.
-                        if (key != DecimalExponentKey.dot && key != DecimalExponentKey.none)
-                            return false;
-                        key = cast(DecimalExponentKey)d;
-                        if (parse(str, exponent) && str.length == 0)
-                        {
-                            recentModifier = false;
-                            if (t != 1)
-                                goto L;
-                            goto M;
-                        }
-                        break;
                 }
-                static if (allowUnderscores)
-                {
-                    case '_' - '0':
-                        // A underscore cannot be preceded by an underscore or a modifier.
-                        if (recentUnderscore || recentModifier)
-                            return false;
-                        
-                        recentUnderscore = true;
-                        if (str.length)
-                            continue;
-                        break;
-                }
-                default:
+                return true;
             }
-            break;
+                // debug dump(overflow, work);
+            return false;
         }
 
-        return false;
-
-        static if (allowSpecialValues)
+        import mir.bignum.internal.parse: decimalFromStringImpl;
+        alias impl = decimalFromStringImpl!(mullAdd, W);
+        alias specification = impl!(C,
+            allowSpecialValues,
+            allowDotOnBounds,
+            allowDExponent,
+            allowStartingPlus,
+            allowUnderscores,
+            allowLeadingZeros,
+            allowExponent,
+            checkEmpty,
+        );
+        exponent += exponentShift;
+        auto ret = specification(str, key, exponent, sign);
+        switch (key)
         {
-        NI:
-            exponent = exponent.max;
-            if (str.length == 2)
-            {
-                auto stail = cast(C[2])str[0 .. 2];
-                if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF"))
-                {
-                    coefficient = coefficient.init;
-                    key = DecimalExponentKey.infinity;
-                    return true;
-                }
-                if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN"))
-                {
-                    coefficient.coefficients[0] = 1;
-                    coefficient = coefficient.topLeastSignificantPart(1);
-                    key = DecimalExponentKey.nan;
-                    return true;
-                }
-            }
-            return false;
+            case DecimalExponentKey.infinity:
+                exponent = exponent.max;
+                coefficient = coefficient.topLeastSignificantPart(0);
+                return ret;
+            case DecimalExponentKey.nan:
+                exponent = exponent.max;
+                coefficient = coefficient.topLeastSignificantPart(1);
+                coefficient.coefficients[0] = 1;
+                return ret;
+            default:
+                coefficient = work;
+                return ret;
         }
     }
 
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 5121b622..daae990a 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -651,7 +651,7 @@ struct Summator(T, Summation summation)
     static if (summation == Summation.decimal)
     {
         import mir.bignum.decimal;
-        Decimal!256 s;
+        Decimal!128 s;
         T ss = 0;
     }
     else
diff --git a/source/mir/parse.d b/source/mir/parse.d
index ed9a6fb0..2a82775e 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -7,6 +7,36 @@ Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry
 +/
 module mir.parse;
 
+///
+enum DecimalExponentKey
+{
+    ///
+    none = 0,
+    ///
+    infinity = 1,
+    ///
+    nan = 2,
+    ///
+    dot = '.' - '0',
+    ///
+    d = 'd' - '0',
+    ///
+    e = 'e' - '0',
+    ///
+    D = 'D' - '0',
+    ///
+    E = 'E' - '0',
+}
+
+///
+struct DecimalExponentInfo
+{
+    ///
+    long exponent;
+    ///
+    DecimalExponentKey key;
+}
+
 /// `mir.conv: to` extension.
 version(mir_bignum_test)
 @safe pure @nogc
@@ -237,7 +267,7 @@ bool fromString(T, C)(scope const(C)[] str, ref T value)
         import mir.bignum.decimal: Decimal, DecimalExponentKey;
         import mir.utility: _expect;
 
-        Decimal!256 decimal = void;
+        Decimal!128 decimal = void;
         DecimalExponentKey key;
         auto ret = decimal.fromStringImpl(str, key);
         if (_expect(ret, true))

From c4724cbdea087b46f5a4e6bb18d0f98afa62572f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Jun 2022 21:37:54 +0400
Subject: [PATCH 431/573] add fromBigEndian

---
 source/mir/bignum/integer.d        | 53 ++++++++++++++++++++++++++++++
 source/mir/bignum/internal/parse.d |  1 +
 2 files changed, 54 insertions(+)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index a1ac4609..94626536 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -185,6 +185,59 @@ struct BigInt(uint size64)
         return this;
     }
 
+    ///
+    static BigInt fromBigEndian()(scope const(ubyte)[] data, bool sign = false)
+        @trusted pure @nogc
+    {
+        BigInt ret = void;
+        if (!ret.copyFromBigEndian(data, sign))
+            static immutable bigIntOverflowException = new Exception("BigInt!" ~ size64.stringof ~ ".fromBigEndian: data overflow");
+        return ret;
+    }
+
+    ///
+    bool copyFromBigEndian()(scope const(ubyte)[] data, bool sign = false)
+        @trusted pure @nogc
+    {
+        while(data.length && data[0] == 0)
+            data = data[1 .. $];
+        if (data.length == 0)
+        {
+            this.length = 0;
+            this.sign = false;
+        }
+        else
+        {
+            if (data.length > this.data.sizeof)
+                return false;
+            this.sign = sign;
+            this.length = cast(uint) (data.length / size_t.sizeof);
+            foreach_reverse (ref c; this.coefficients)
+            {
+                size_t value;
+                foreach (j; 0 .. size_t.sizeof)
+                {
+                    value <<= 8;
+                    value |= data[0];
+                    data = data[1 .. $];
+                }
+                c = value;
+            }
+            if (data.length)
+            {
+                this.length++; 
+                size_t value;
+                foreach (b; data)
+                {
+                    value <<= 8;
+                    value |= b;
+                }
+                this.data[length - 1] = value;
+            }
+        }
+        return true;
+    }
+
     /++
     Returns: false in case of overflow or incorrect string.
     Precondition: non-empty coefficients.
diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
index 0663ce5a..bd45dbe1 100644
--- a/source/mir/bignum/internal/parse.d
+++ b/source/mir/bignum/internal/parse.d
@@ -222,6 +222,7 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
         IF:
             ulong multplier = 10;
             static if (is(C == char) && is(W == ulong))
+            if (!__ctfe)
             {
                 import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
                 if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))

From dee91b2f074486fb3a1afd4c1c2b43f778c884f8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Jun 2022 23:59:24 +0400
Subject: [PATCH 432/573] inline parseJsonNumberImpl

---
 source/mir/bignum/internal/parse.d | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
index bd45dbe1..e07feeff 100644
--- a/source/mir/bignum/internal/parse.d
+++ b/source/mir/bignum/internal/parse.d
@@ -43,10 +43,11 @@ struct SmallDecimalParsingResult
 
 /++
 +/
-SmallDecimalParsingResult parseJsonNumberImpl(scope const(char)[] str)
+SmallDecimalParsingResult parseJsonNumberImpl()(scope const(char)[] str)
+    @trusted pure nothrow @nogc
     in (str.length)
 {
-    pragma(inline, false);
+    pragma(inline, true);
 
     alias W = ulong;
     enum bool allowSpecialValues = false;

From 9091b8f58112f805fe1a0e5ce213ba87860c84eb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 2 Jun 2022 00:10:19 +0400
Subject: [PATCH 433/573] fixup

---
 source/mir/bignum/internal/dec2float.d | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 282fe687..2ae8cb60 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -140,26 +140,27 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
     if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
     {
         auto c = coefficient.Fp!64;
+        T approx = void;
         version (all)
         {{
             auto z = c.extendedMul!true(_load!wordBits(exponent));
-            auto approx = z.opCast!(T, true);
+            approx = z.opCast!(T, true);
             long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
             uint slop = 3 * (exponent < 0);
             if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
                 return approx;
         }}
         static if (T.mant_dig < 64)
-        {
+        {{
             auto z = c.extendedMul!true(_load!128(exponent));
-            auto approx = z.opCast!(T, true);
+            approx = z.opCast!(T, true);
             auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
             if (bitsDiff.signBit)
                 bitsDiff = UInt!128.init - bitsDiff;
             uint slop = 3 * (exponent < 0);
             if (_expect(approx > T.min_normal && bitsDiff > slop, true))
                 return approx;
-        }
+        }}
 
         if (0 <= exponent)
         {

From 3ed0e568966b07aa9b40014e91921e1df65c2552 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 2 Jun 2022 00:15:57 +0400
Subject: [PATCH 434/573] fixup

---
 source/mir/bignum/internal/parse.d | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
index e07feeff..d2d6e652 100644
--- a/source/mir/bignum/internal/parse.d
+++ b/source/mir/bignum/internal/parse.d
@@ -136,11 +136,9 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
             }
         }
 
-        ulong d = str[0] - C('0');
+        W d = str[0] - C('0');
         str = str[1 .. $];
 
-        ulong v;
-
         if (_expect(d >= 10, false))
         {
             static if (allowDotOnBounds)
@@ -221,7 +219,7 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
             }
 
         IF:
-            ulong multplier = 10;
+            W multplier = 10;
             static if (is(C == char) && is(W == ulong))
             if (!__ctfe)
             {

From 8e03797e3dc6020d43b36b961afbc3a7e4574dc9 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 2 Jun 2022 01:01:04 +0400
Subject: [PATCH 435/573] fix copyFromBigEndian

---
 source/mir/bignum/integer.d | 7 +++++--
 source/mir/parse.d          | 5 +++++
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 94626536..2526a372 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -212,6 +212,8 @@ struct BigInt(uint size64)
                 return false;
             this.sign = sign;
             this.length = cast(uint) (data.length / size_t.sizeof);
+            auto tail = data[0 .. data.length % size_t.sizeof];
+            data = data[data.length % size_t.sizeof .. $];
             foreach_reverse (ref c; this.coefficients)
             {
                 size_t value;
@@ -223,11 +225,12 @@ struct BigInt(uint size64)
                 }
                 c = value;
             }
-            if (data.length)
+            assert(data.length == 0);
+            if (tail.length)
             {
                 this.length++; 
                 size_t value;
-                foreach (b; data)
+                foreach (b; tail)
                 {
                     value <<= 8;
                     value |= b;
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 2a82775e..046dfbd7 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -231,6 +231,11 @@ version(mir_bignum_test)
     test("36893488147419103229.0");
     test("36893488147419103229");
 
+    test("18446744073709551615.");
+    test("-18446744073709551615.");
+    test("18446744073709551616.");
+    test("-18446744073709551616.");
+
 //  Related DMD Issues:
 // https://issues.dlang.org/show_bug.cgi?id=20951
 // https://issues.dlang.org/show_bug.cgi?id=20952

From f6685b7e7225d1a46843df54ad086ca5c1f0581e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 2 Jun 2022 16:16:22 +0400
Subject: [PATCH 436/573] update parse

---
 source/mir/bignum/internal/parse.d | 39 ++++++++++++++++++++++--------
 1 file changed, 29 insertions(+), 10 deletions(-)

diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
index d2d6e652..6d039f7e 100644
--- a/source/mir/bignum/internal/parse.d
+++ b/source/mir/bignum/internal/parse.d
@@ -61,11 +61,11 @@ SmallDecimalParsingResult parseJsonNumberImpl()(scope const(char)[] str)
 
     typeof(return) result;
 
-    bool mullAdd(ulong multplier, ulong carry)
+    bool mullAdd(ulong multiplier, ulong carry)
     {
         import mir.checkedint: mulu, addu;
         bool overflow;
-        result.coefficient = mulu(result.coefficient, multplier, overflow);
+        result.coefficient = mulu(result.coefficient, multiplier, overflow);
         if (overflow)
             return overflow;
         result.coefficient = addu(result.coefficient, carry, overflow);
@@ -89,6 +89,26 @@ SmallDecimalParsingResult parseJsonNumberImpl()(scope const(char)[] str)
     return result;
 }
 
+unittest
+{
+    import mir.test;
+    auto res = "-0.1234567890e-30".parseJsonNumberImpl;
+    res.key.should == DecimalExponentKey.e;
+    res.sign.should == true;
+    res.exponent.should == -40;
+    res.coefficient.should == 1234567890;
+}
+
+unittest
+{
+    import mir.test;
+    auto res = "2.9802322387695312E-8".parseJsonNumberImpl;
+    res.key.should == DecimalExponentKey.E;
+    res.sign.should == false;
+    res.exponent.should == -24;
+    res.coefficient.should == 29802322387695312;
+}
+
 /++
 Returns: false in case of overflow or incorrect string.
 +/
@@ -219,14 +239,13 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
             }
 
         IF:
-            W multplier = 10;
+            W multiplier = 10;
             static if (is(C == char) && is(W == ulong))
             if (!__ctfe)
             {
-                import mir.bignum.internal.parse: isMadeOfEightDigits, parseEightDigits;
                 if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
                 {
-                    multplier = 100000000;
+                    multiplier = 100000000;
                     d = parseEightDigits(str[0 .. 8]);
                     str = str[8 .. $];
                     exponent -= 8;
@@ -234,7 +253,7 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
                     {
                         if (isMadeOfEightDigits((str.ptr - 1)[0 .. 8]))
                         {
-                            multplier = 100000000UL * 10000000;
+                            multiplier = 100000000UL * 10000000;
                             d -= str.ptr[-1] - '0';
                             d *= 10000000;
                             d += parseEightDigits((str.ptr - 1)[0 .. 8]);
@@ -246,7 +265,7 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
                                 if (md < 10)
                                 {
                                     d *= 10;
-                                    multplier = 100000000UL * 100000000;
+                                    multiplier = 100000000UL * 100000000;
                                     d += md;
                                     str = str[1 .. $];
                                     exponent -= 1;
@@ -258,7 +277,7 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
                         TrySix:
                             if (isMadeOfEightDigits((str.ptr - 2)[0 .. 8]))
                             {
-                                multplier = 100000000UL * 1000000;
+                                multiplier = 100000000UL * 1000000;
                                 d -= str.ptr[-1] - '0';
                                 d -= (str.ptr[-2] - '0') * 10;
                                 d *= 1000000;
@@ -282,9 +301,9 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
                 goto DOB;
         FI:
             exponent--;
-            multplier = 10;
+            multiplier = 10;
         FIL:
-            if (_expect(mullAdd(multplier, d), false))
+            if (_expect(mullAdd(multiplier, d), false))
                 return false;
             import mir.stdio;
             // debug dump("str = ", str);

From b6bdb63a18ab2df551cbc5d87ddfc4898f8d1866 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 2 Jun 2022 17:39:39 +0400
Subject: [PATCH 437/573] optimize dec2float

---
 source/mir/bignum/internal/dec2float.d | 90 ++++++++++++++++++--------
 1 file changed, 63 insertions(+), 27 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 2ae8cb60..2183b6a0 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -140,40 +140,76 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
     if (_expect(-ExponentM <= exponent && exponent <= ExponentM, true))
     {
         auto c = coefficient.Fp!64;
+
         T approx = void;
-        version (all)
-        {{
-            auto z = c.extendedMul!true(_load!wordBits(exponent));
-            approx = z.opCast!(T, true);
-            long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
-            uint slop = 3 * (exponent < 0);
-            if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
-                return approx;
-        }}
-        static if (T.mant_dig < 64)
-        {{
-            auto z = c.extendedMul!true(_load!128(exponent));
-            approx = z.opCast!(T, true);
-            auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
-            if (bitsDiff.signBit)
-                bitsDiff = UInt!128.init - bitsDiff;
-            uint slop = 3 * (exponent < 0);
-            if (_expect(approx > T.min_normal && bitsDiff > slop, true))
-                return approx;
-        }}
-
-        if (0 <= exponent)
-        {
-            if (exponent <= 55) // exact exponent
-                return approx;
-        }
-        else
+
+        if (exponent < 0)
         {
+            version (all)
+            {{
+                auto z = c.extendedMul!true(_load!wordBits(exponent));
+                approx = z.opCast!(T, true);
+                long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
+                uint slop = 3;
+                if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
+                    return approx;
+            }}
+
             if (-exponent <= MaxFpPow5!T)
             {
                 auto e = _load!wordBits(-exponent);
                 return coefficient / e.opCast!(T, true);
             }
+
+            static if (T.mant_dig < 64)
+            {{
+                auto z = c.extendedMul!true(_load!128(exponent));
+                approx = z.opCast!(T, true);
+                auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
+                if (bitsDiff.signBit)
+                    bitsDiff = UInt!128.init - bitsDiff;
+                uint slop = 3;
+                if (_expect(approx > T.min_normal && bitsDiff > slop, true))
+                    return approx;
+            }}
+
+        }
+        else
+        {
+            version (all)
+            {{
+                auto z = c.extendedMul!true(_load!wordBits(exponent));
+                approx = z.opCast!(T, true);
+
+                if (exponent <= 27) // exact exponent
+                {
+                    return approx;
+                }
+
+                long bitsDiff = (cast(ulong) z.opCast!(Fp!wordBits).coefficient & mask) - half;
+                uint slop;
+                if (_expect(approx > T.min_normal && (bitsDiff < 0 ? -bitsDiff : bitsDiff) > slop, true))
+                    return approx;
+            }}
+
+            static if (T.mant_dig < 64)
+            {{
+                auto z = c.extendedMul!true(_load!128(exponent));
+                approx = z.opCast!(T, true);
+
+                if (exponent <= 55) // exact exponent
+                {
+                    return approx;
+                }
+
+                auto bitsDiff = (z.opCast!(Fp!128).coefficient & bigMask) - bigHalf;
+                if (bitsDiff.signBit)
+                    bitsDiff = UInt!128.init - bitsDiff;
+                uint slop;
+                if (_expect(approx > T.min_normal && bitsDiff > slop, true))
+                    return approx;
+            }}
+
         }
     }
     size_t[ulong.sizeof / size_t.sizeof] coefficients;

From 1da19a9ae0e78052e3efccdea80804b0129f7d54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Fri, 3 Jun 2022 14:36:13 +0200
Subject: [PATCH 438/573] Various improvements to mir.stdio (#414)

* Use scope return inplace of return scope in FileMemembers

* Correct naming of FileMemembers to FileMembers
---
 source/mir/stdio.d | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index 2902342d..20ecdfed 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -104,7 +104,7 @@ struct File
     ///
     core.stdc.stdio.FILE* fp;
 
-    mixin FileMemembers;
+    mixin FileMembers;
 
 @trusted @nogc:
 
@@ -144,7 +144,7 @@ struct AssumeNothrowFile
     ///
     core.stdc.stdio.FILE* fp;
 
-    mixin FileMemembers;
+    mixin FileMembers;
 
 @trusted @nogc nothrow:
 
@@ -176,7 +176,7 @@ struct AssumeNothrowFile
 }
 
 ///
-mixin template FileMemembers()
+mixin template FileMembers()
 {
     ///
     void put(C)(const C data)
@@ -197,7 +197,7 @@ mixin template FileMemembers()
     template opBinary(string op : "<<")
     {
         ///
-        ref opBinary(T)(auto ref const T value) return scope
+        ref opBinary(T)(auto ref const T value) scope return
         {
             if (__ctfe)
                 return this;
@@ -206,7 +206,7 @@ mixin template FileMemembers()
         }
 
         /// Prints new line and flushes the stream
-        ref opBinary(NewLine endl) return scope
+        ref opBinary(NewLine endl) scope return
         {
             if (__ctfe)
                 return this;
@@ -254,7 +254,7 @@ class FileException : Exception
         string msg,
         string file = __FILE__,
         size_t line = __LINE__,
-        Throwable next = null) pure nothrow @nogc @safe 
+        Throwable next = null) pure nothrow @nogc @safe
     {
         super(msg, file, line, next);
     }
@@ -265,7 +265,7 @@ class FileException : Exception
         Throwable next,
         string file = __FILE__,
         size_t line = __LINE__,
-        ) pure nothrow @nogc @safe 
+        ) pure nothrow @nogc @safe
     {
         this(msg, file, line, next);
     }
@@ -288,7 +288,7 @@ class FileError : Error
         string msg,
         string file = __FILE__,
         size_t line = __LINE__,
-        Throwable next = null) pure nothrow @nogc @safe 
+        Throwable next = null) pure nothrow @nogc @safe
     {
         super(msg, file, line, next);
     }
@@ -299,7 +299,7 @@ class FileError : Error
         Throwable next,
         string file = __FILE__,
         size_t line = __LINE__,
-        ) pure nothrow @nogc @safe 
+        ) pure nothrow @nogc @safe
     {
         this(msg, file, line, next);
     }

From 055b25c41729850387cdcce4a02005055df943c3 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 4 Jun 2022 02:00:05 -0500
Subject: [PATCH 439/573] Support const for binomial (#415)

---
 source/mir/combinatorics/package.d | 35 ++++++++++++++++++++----------
 1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/source/mir/combinatorics/package.d b/source/mir/combinatorics/package.d
index 3f9e9895..8c8a3201 100644
--- a/source/mir/combinatorics/package.d
+++ b/source/mir/combinatorics/package.d
@@ -85,45 +85,49 @@ Params:
 Returns:
     Binomial coefficient
 */
-R binomial(R = ulong, T)(T n, T k)
+R binomial(R = ulong, T)(const T n, const T k)
     if (isArithmetic!(R, T) &&
         ((is(typeof(T.min < 0)) && is(typeof(T.init & 1))) || !is(typeof(T.min < 0))) )
 {
     R result = 1;
 
     enum hasMinProperty = is(typeof(T.min < 0));
+    
+    T n2 = n;
+    T k2 = k;
+
     // only add negative support if possible
     static if ((hasMinProperty && T.min < 0) || !hasMinProperty)
     {
-        if (n < 0)
+        if (n2 < 0)
         {
-            if (k >= 0)
+            if (k2 >= 0)
             {
-                return (k & 1 ? -1 : 1) * binomial!(R, T)(-n + k-1, k);
+                return (k2 & 1 ? -1 : 1) * binomial!(R, T)(-n2 + k2 - 1, k2);
             }
-            else if (k <= n)
+            else if (k2 <= n2)
             {
-                return ((n-k) & 1 ? -1 : 1) * binomial!(R, T)(-k-1, n-k);
+                return ((n2 - k2) & 1 ? -1 : 1) * binomial!(R, T)(-k2 - 1, n2 - k2);
             }
         }
-        if (k < 0)
+        if (k2 < 0)
         {
             result = 0;
             return result;
         }
     }
 
-    if (k > n)
+    if (k2 > n2)
     {
         result = 0;
         return result;
     }
-    if (k > n - k)
+    if (k2 > n2 - k2)
     {
-        k = n - k;
+        k2 = n2 - k2;
     }
     // make a copy of n (could be a custom type)
-    for (T i = 1, m = n; i <= k; i++, m--)
+    for (T i = 1, m = n2; i <= k2; i++, m--)
     {
         // check whether an overflow can happen
         // hasMember!(Result, "max") doesn't work with dmd2.068 and ldc 0.17
@@ -189,6 +193,15 @@ version(mir_test) unittest
     assert(binomial!BigInt(-5, -7) == 15);
 }
 
+version(mir_test)
+@safe pure nothrow @nogc 
+unittest
+{
+    const size_t n = 5;
+    const size_t k = 2;
+    assert(binomial(n, k) == 10);
+}
+
 /**
 Creates a projection of a generalized `Collection` range for the numeric case
 case starting from `0` onto a custom `range` of any type.

From 441100d1979b2c66f5e8c4f08e3022b4a8820410 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 6 Jun 2022 16:59:39 +0400
Subject: [PATCH 440/573] build with DIP1000

---
 dub.sdl                             |  1 -
 source/mir/appender.d               |  2 +-
 source/mir/bignum/decimal.d         | 10 +++---
 source/mir/bignum/fixed.d           |  6 ++--
 source/mir/bignum/integer.d         |  2 ++
 source/mir/bignum/internal/kernel.d |  2 +-
 source/mir/bignum/low_level_view.d  | 56 ++++++++++++++++-------------
 7 files changed, 45 insertions(+), 34 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 89e205f9..4a44d519 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -9,7 +9,6 @@ dependency "mir-core" version=">=1.1.106"
 
 // versions "TeslAlgoM"
 
-
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
diff --git a/source/mir/appender.d b/source/mir/appender.d
index f1b6b8d5..6b35d162 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -360,7 +360,7 @@ unittest
     Algebraic!(int, string, double) x;
     x = 42;
     auto s = x.to!string;
-    assert(s == "42", s);
+    assert(s == "42");
     x = "abc";
     assert(x.to!string == "abc");
     x = 42.0;
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index adfe4914..0d0d1862 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -34,14 +34,16 @@ struct Decimal(uint size64)
     ///
     BigInt!size64 coefficient;
 
+@safe:
+
     ///
-    DecimalView!size_t view()
+    DecimalView!size_t view() return scope
     {
         return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
     }
 
     /// ditto
-    DecimalView!(const size_t) view() const
+    DecimalView!(const size_t) view() const return scope
     {
         return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned);
     }
@@ -393,7 +395,7 @@ struct Decimal(uint size64)
     /++
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/
-    T opCast(T, bool wordNormalized = true)() const
+    T opCast(T, bool wordNormalized = true)() scope const
         if (isFloatingPoint!T && isMutable!T)
     {
         return view.opCast!(T, wordNormalized);
@@ -455,7 +457,7 @@ unittest
     DecimalExponentKey key;
 
     assert(decimal.fromStringImpl("3.141592653589793378e-10", key));
-    decimal.to!double.should == 0x1.596bf8ce7631ep-32;
+    decimal.opCast!double.should == 0x1.596bf8ce7631ep-32;
     key.should == DecimalExponentKey.e;
 }
 
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index 12678ea7..a1d74bcc 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -185,13 +185,13 @@ struct UInt(size_t size)
     import mir.bignum.low_level_view: BigUIntView;
 
     ///
-    BigUIntView!size_t view() @property pure nothrow @nogc scope @safe
+    BigUIntView!size_t view() @property pure nothrow @nogc scope return @safe
     {
         return BigUIntView!size_t(data);
     }
 
     ///
-    BigUIntView!(const size_t) view() const @property pure nothrow @nogc scope @safe
+    BigUIntView!(const size_t) view() const @property pure nothrow @nogc scope return @safe
     {
         return BigUIntView!(const size_t)(data);
     }
@@ -865,7 +865,7 @@ struct UInt(size_t size)
 
     /++
     +/
-    size_t ctlz() const @property
+    size_t ctlz() const scope @property
         @safe pure nothrow @nogc
     {
         return view.ctlz;
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 2526a372..a2b7f90c 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -28,6 +28,8 @@ struct BigInt(uint size64)
     ///
     size_t[ulong.sizeof / size_t.sizeof * size64] data;// = void;
 
+@safe:
+
     ///
     this(uint size)(UInt!size fixedInt)
     {
diff --git a/source/mir/bignum/internal/kernel.d b/source/mir/bignum/internal/kernel.d
index fce9df7d..34438d03 100644
--- a/source/mir/bignum/internal/kernel.d
+++ b/source/mir/bignum/internal/kernel.d
@@ -3,7 +3,7 @@ module mir.bignum.internal.kernel;
 import mir.bignum.internal.phobos_kernel;
 public import mir.bignum.internal.phobos_kernel: karatsubaRequiredBuffSize, divisionRequiredBuffSize;
 
-private inout(uint)[] toUints()(inout ulong[] data)
+private inout(uint)[] toUints(return scope inout ulong[] data)
     @trusted pure nothrow @nogc
 {
     auto ret = cast(typeof(return)) data;
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index dac48e44..47acfd44 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -78,6 +78,8 @@ struct BigUIntView(W)
     +/
     W[] coefficients;
 
+@safe:
+
     /++
     Retrurns: signed integer view using the same data payload
     +/
@@ -89,14 +91,14 @@ struct BigUIntView(W)
     /++
     Retrurns: signed integer view using the same data payload
     +/
-    BigIntView!W signed()(bool sign = false) @safe pure nothrow @nogc scope @property
+    BigIntView!W signed()(bool sign = false) @safe pure nothrow @nogc return @property
     {
         return typeof(return)(this, sign);
     }
 
     static if (W.sizeof >= size_t.sizeof)
     ///
-    T opCast(T)() const
+    T opCast(T)() const scope
         if (isFloatingPoint!T && isMutable!T)
     {
         import mir.bignum.internal.dec2float: binaryTo;
@@ -106,7 +108,7 @@ struct BigUIntView(W)
     static if (W.sizeof >= size_t.sizeof)
     ///
     @safe
-    T opCast(T : Fp!coefficientSize, uint coefficientSize)() const
+    T opCast(T : Fp!coefficientSize, uint coefficientSize)() const scope
     {
         static if (isMutable!W)
         {
@@ -159,21 +161,21 @@ struct BigUIntView(W)
 
     ///
     pure nothrow @nogc
-    BigUIntView!V opCast(T : BigUIntView!V, V)()
+    BigUIntView!V opCast(T : BigUIntView!V, V)() return scope
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(cast(V[])this.coefficients);
     }
 
     pure nothrow @nogc
-    BigUIntView!V opCast(T : BigUIntView!V, V)() const
+    BigUIntView!V opCast(T : BigUIntView!V, V)() const return scope
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(cast(V[])this.coefficients);
     }
 
     ///
-    BigUIntView!(const W) lightConst()()
+    BigUIntView!(const W) lightConst()() return scope
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(coefficients);
@@ -183,7 +185,7 @@ struct BigUIntView(W)
 
     /++
     +/
-    sizediff_t opCmp(BigUIntView!(const W) rhs)
+    sizediff_t opCmp(scope BigUIntView!(const W) rhs)
         const @safe pure nothrow @nogc scope
     {
         import mir.algorithm.iteration: cmp;
@@ -567,7 +569,7 @@ struct BigUIntView(W)
     Returns:
         true in case of unsigned overflow
     +/
-    bool opOpAssign(string op)(BigUIntView!(const W) rhs, bool overflow = false)
+    bool opOpAssign(string op)(scope BigUIntView!(const W) rhs, bool overflow = false)
     @safe pure nothrow @nogc scope
         if (op == "+" || op == "-")
     {
@@ -793,7 +795,7 @@ struct BigUIntView(W)
     /++
     Strips most significant zero coefficients.
     +/
-    BigUIntView normalized()
+    BigUIntView normalized() return scope
     {
         auto number = this;
         if (number.coefficients.length) do
@@ -807,7 +809,7 @@ struct BigUIntView(W)
     }
 
     ///ditto
-    BigUIntView!(const W) normalized() const
+    BigUIntView!(const W) normalized() const return scope
     {
         return lightConst.normalized;
     }
@@ -823,7 +825,7 @@ struct BigUIntView(W)
 
     /++
     +/
-    size_t ctlz()() const @property
+    size_t ctlz()() scope const @property
         @safe pure nothrow @nogc
     {
         import mir.bitop: ctlz;
@@ -846,7 +848,7 @@ struct BigUIntView(W)
 
     /++
     +/
-    size_t cttz()() const @property
+    size_t cttz()() scope const @property
         @safe pure nothrow @nogc
         in (coefficients.length)
     {
@@ -868,7 +870,7 @@ struct BigUIntView(W)
     }
 
     ///
-    BigIntView!W withSign()(bool sign)
+    BigIntView!W withSign()(bool sign) return scope
     {
         return typeof(return)(this, sign);
     }
@@ -1267,6 +1269,8 @@ struct BigIntView(W)
     +/
     bool sign;
 
+@safe:
+
     ///
     inout(W)[] coefficients() inout @property
     {
@@ -1412,7 +1416,7 @@ struct BigIntView(W)
     }
 
     ///
-    T opCast(T)() const
+    T opCast(T)() const scope
         if (isFloatingPoint!T && isMutable!T)
     {
         auto ret = this.unsigned.opCast!T;
@@ -1422,7 +1426,7 @@ struct BigIntView(W)
     }
 
     ///
-    T opCast(T, bool nonZero = false)() const
+    T opCast(T, bool nonZero = false)() const scope
         if (is(T == long) || is(T == int))
     {
         auto ret = this.unsigned.opCast!(Unsigned!T, nonZero);
@@ -1555,7 +1559,7 @@ struct BigIntView(W)
 
     /++
     +/
-    T opCast(T : Fp!coefficientSize, uint coefficientSize)() const
+    T opCast(T : Fp!coefficientSize, uint coefficientSize)() const scope
     {
         auto ret = unsigned.opCast!(Fp!coefficientSize);
         ret.sign = sign;
@@ -1563,21 +1567,21 @@ struct BigIntView(W)
     }
 
     ///
-    BigIntView!V opCast(T : BigIntView!V, V)()
+    BigIntView!V opCast(T : BigIntView!V, V)() return scope
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
     }
 
     ///
-    BigIntView!V opCast(T : BigIntView!V, V)() const
+    BigIntView!V opCast(T : BigIntView!V, V)() const return scope
         if (V.sizeof <= W.sizeof)
     {
         return typeof(return)(this.unsigned.opCast!(BigUIntView!V), sign);
     }
 
     ///
-    BigIntView!(const W) lightConst()()
+    BigIntView!(const W) lightConst()() return scope
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(unsigned.lightConst, sign);
@@ -1783,7 +1787,7 @@ struct BigIntView(W)
     Strips most significant zero coefficients.
     Sets sign to zero if no coefficients were left.
     +/
-    BigIntView normalized()
+    BigIntView normalized() return scope
     {
         auto number = this;
         number.unsigned = number.unsigned.normalized;
@@ -1792,7 +1796,7 @@ struct BigIntView(W)
     }
 
     ///ditto
-    BigIntView!(const W) normalized() const
+    BigIntView!(const W) normalized() const return scope
     {
         return lightConst.normalized;
     }
@@ -1961,6 +1965,8 @@ struct DecimalView(W)
     ///
     BigUIntView!W coefficient;
 
+@safe:
+
     static if (is(W == size_t))
     /++
     Returns: false in case of overflow or incorrect string.
@@ -2038,7 +2044,7 @@ struct DecimalView(W)
     }
 
     ///
-    DecimalView!(const W) lightConst()()
+    DecimalView!(const W) lightConst()() return scope
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(sign, exponent, coefficient.lightConst);
@@ -2058,7 +2064,7 @@ struct DecimalView(W)
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.
     +/
-    T opCast(T, bool wordNormalized = false)() const
+    T opCast(T, bool wordNormalized = false)() const scope
         if (isFloatingPoint!T && isMutable!T)
     {
         import mir.bignum.internal.dec2float;
@@ -2402,8 +2408,10 @@ struct BinaryView(W)
     ///
     BigUIntView!W coefficient;
 
+@safe:
+
     ///
-    DecimalView!(const W) lightConst()()
+    DecimalView!(const W) lightConst()() return scope
         const @safe pure nothrow @nogc @property
     {
         return typeof(return)(sign, exponent, coefficient.lightConst);

From f48b7a2661395bb218261a00d6a3acfc5bf77ccc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 6 Jun 2022 17:29:47 +0400
Subject: [PATCH 441/573] minor DIP1000 fixup

---
 source/mir/ndslice/slice.d | 6 +++---
 source/mir/serde.d         | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 384a21dd..f7361320 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -954,7 +954,7 @@ public:
     }
 
     /// ditto
-    auto lightScope()() return const scope @property
+    auto lightScope()() return scope const @property
     {
         auto ret = Slice!(LightConstOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightConstOfLightScopeOf, Labels))
             (_structure, .lightScope(_iterator));
@@ -1203,7 +1203,7 @@ public:
     }
 
     /// ditto
-    auto field()() return const scope @trusted @property
+    auto field()() return scope const @trusted @property
     {
         return this.lightConst.field;
     }
@@ -2488,7 +2488,7 @@ public:
     See_also: $(LREF .Slice.idup)
     +/
     Slice!(Unqual!DeepElement*, N)
-    dup()() scope @property
+    dup()() scope @property @safe
     {
         if (__ctfe)
         {
diff --git a/source/mir/serde.d b/source/mir/serde.d
index aeee7a47..bcd5c7f0 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -2322,7 +2322,7 @@ auto fastToUpper()(char a)
 }
 
 @safe pure nothrow @nogc
-char[] fastToUpperInPlace()(scope return char[] a)
+char[] fastToUpperInPlace()(return scope char[] a)
 {
     foreach(ref char e; a)
         e = e.fastToUpper;

From 59dadff65b4d37ff5a75a9307ceba0f671273e16 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 6 Jun 2022 17:30:04 +0400
Subject: [PATCH 442/573] resurect dub

---
 dub.sdl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 4a44d519..e35c8ddd 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -12,8 +12,8 @@ dependency "mir-core" version=">=1.1.106"
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
-    // versions "mir_ndslice_test"
-    // versions "mir_test"
+    versions "mir_ndslice_test"
+    versions "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {

From 3ad27dd61a45e40e28e9a9e4f0d1de6d08054084 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 6 Jun 2022 17:49:48 +0400
Subject: [PATCH 443/573] update dub with dip1000

---
 dub.sdl | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index e35c8ddd..9b0a93b4 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -12,8 +12,8 @@ dependency "mir-core" version=">=1.1.106"
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
-    versions "mir_ndslice_test"
-    versions "mir_test"
+    // versions "mir_ndslice_test"
+    // versions "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
@@ -28,7 +28,7 @@ buildType "unittest-dip1000" {
 }
 buildType "unittest-cov" {
     buildOptions "unittests" "coverage" "debugMode" "debugInfo"
-    versions "mir_bignum_test" "mir_ndslice_test" "mir_test"
+    versions "mir_bignum_test" "mir_bignum_test_llv" "mir_ndslice_test" "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-ci" {
@@ -58,6 +58,7 @@ configuration "dips" {
 
 configuration "ci-bignum-test" {
     versions "mir_bignum_test" "mir_bignum_test_llv"
+    dflags "-lowmem" "-preview=dip1000"
 }
 
 configuration "ci-core-test" {

From e6b3cb287d601a2b80e4ed6e9962ddfad8568f1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Thu, 9 Jun 2022 14:01:41 +0200
Subject: [PATCH 444/573] Add support for const values in StringMap (#418)

---
 source/mir/string_map.d | 40 +++++++++++++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 7 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 8819b1fb..de1a2b52 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -30,7 +30,7 @@ Params:
     U = an unsigned type that can hold an index of keys. `U.max` must be less then the maximum possible number of struct members.
 +/
 struct StringMap(T, U = uint)
-    if (isMutable!T && !is(typeof(T.opPostMove)) && __traits(isUnsigned, U))
+    if (!is(typeof(T.opPostMove)) && __traits(isUnsigned, U))
 {
     import mir.utility: _expect;
     import core.lifetime: move;
@@ -632,7 +632,7 @@ struct StringMap(T, U = uint)
                     assert (index < length);
                     index = implementation._indices[index];
                     assert (index < length);
-                    implementation._values[index] = move(value);
+                    move(cast()value, cast()(implementation._values[index]));
                     return implementation._values[index];
                 }
                 assert (index <= length);
@@ -643,7 +643,7 @@ struct StringMap(T, U = uint)
             }
         }
         assert (index <= length);
-        implementation.insertAt(key, move(value), index);
+        implementation.insertAt(key, move(cast()value), index);
         index = implementation._indices[index];
         return implementation._values[index];
     }
@@ -843,7 +843,7 @@ struct StringMap(T, U = uint)
             }
             {
                 auto a = values;
-                a ~= move(value);
+                a ~= move(cast()value);
                 _values = a.ptr;
             }
             {
@@ -1019,7 +1019,33 @@ struct StringMap(T, U = uint)
 
 version(mir_test)
 ///
-unittest
+@safe unittest
+{
+    class C
+    {
+        this(int x) { this.x = x; }
+        int x;
+        bool opEquals(scope const C rhs) const scope @safe pure nothrow @nogc
+        {
+            return x == rhs.x;
+        }
+    }
+    StringMap!(const C) table;
+    const v0 = new C(42);
+    const v1 = new C(43);
+    table["0"] = v0;
+    table["1"] = v1;
+    assert(table.keys == ["0", "1"]);
+    static if (__VERSION__ > 2098) // See https://github.com/libmir/mir-algorithm/runs/6809888795?check_suite_focus=true#step:5:17
+    {
+        assert(table.values == [v0, v1]); // TODO: qualify unittest as `pure` when this is inferred `pure`
+    }
+    static assert(is(typeof(table.values) == const(C)[]));
+}
+
+version(mir_test)
+///
+@safe pure unittest
 {
     StringMap!int table;
     table["L"] = 3;
@@ -1054,7 +1080,7 @@ unittest
 
 version(mir_test)
 ///
-@safe unittest
+@safe pure unittest
 {
     static void testEquals(X, Y)()
     {
@@ -1089,7 +1115,7 @@ version(mir_test)
 }
 
 version(mir_test)
-@safe unittest
+@safe pure unittest
 {
     import mir.algebraic_alias.json: JsonAlgebraic;
     import mir.string_map: StringMap;

From eb8e9604c221a78f56637998f03b26921f5a71f9 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 10 Jun 2022 01:16:37 +0400
Subject: [PATCH 445/573] update formatting

---
 source/mir/bignum/internal/dec2float.d |  1 -
 source/mir/format.d                    | 66 +++++++++++++++++++++++---
 source/mir/test.d                      | 35 +++++++++++---
 3 files changed, 87 insertions(+), 15 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 2183b6a0..7671c05a 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -284,7 +284,6 @@ private template bigSize(T)
     {
         enum size_t bits = T.max_exp - T.min_exp + T.mant_dig;
         enum size_t bigSize = bits / 64 + bits % 64 + 1;
-        pragma(msg, "bigSize = ", bigSize);
     }
 }
 
diff --git a/source/mir/format.d b/source/mir/format.d
index 92acd274..b206e206 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -334,6 +334,13 @@ struct HexAddress(T)
     }
 }
 
+///ditto
+HexAddress!T hexAddress(T)(const T value, SwitchLU switchLU = SwitchLU.upper)
+    if (isUnsigned!T && !is(T == enum))
+{
+    return typeof(return)(value, switchLU);
+}
+
 /++
 Escaped string formats
 +/
@@ -640,25 +647,37 @@ version (mir_test) unittest
 
 /// Prints array
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, scope const(T)[] c)
+ref W printArray(C = char, W, T)(scope return ref W w,
+    scope const(T)[] c,
+    scope const(C)[] lb = "[",
+    scope const(C)[] rb = "]",
+    scope const(C)[] sep = ", ",
+)
     if (isSomeChar!C && !isSomeChar!T)
 {
-    enum C left = '[';
-    enum C right = ']';
-    enum C[2] sep = ", ";
-    w.put(left);
+    w.put(lb);
     bool first = true;
     foreach (ref e; c)
     {
         if (!first)
-            w.printStaticString!C(sep);
+            w.put(sep);
         first = false;
         printElement!C(w, e);
     }
-    w.put(right);
+    w.put(rb);
     return w;
 }
 
+/// ditto
+pragma(inline, false)
+ref W print(C = char, W, T)(scope return ref W w,
+    scope const(T)[] c,
+)
+    if (isSomeChar!C && !isSomeChar!T)
+{
+    return printArray(w, c);
+}
+
 ///
 @safe pure nothrow @nogc
 version (mir_test) unittest
@@ -669,6 +688,39 @@ version (mir_test) unittest
     assert(w.print(array[]).data == `["a\na", "b"]`);
 }
 
+/// Prints array as hex values
+pragma(inline, false)
+ref W printHexArray(C = char, W, T)(scope return ref W w,
+    scope const(T)[] c,
+    scope const(C)[] lb = "",
+    scope const(C)[] rb = "",
+    scope const(C)[] sep = " ",
+)
+    if (isSomeChar!C && !isSomeChar!T && isUnsigned!T)
+{
+    w.put(lb);
+    bool first = true;
+    foreach (ref e; c)
+    {
+        if (!first)
+            w.put(sep);
+        first = false;
+        printElement!C(w, e.hexAddress);
+    }
+    w.put(rb);
+    return w;
+}
+
+///
+@safe pure nothrow @nogc
+version (mir_test) unittest
+{
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
+    ubyte[2] array = [0x34, 0x32];
+    assert(w.print(array[]).data == `34 32`);
+}
+
 /// Prints escaped character in the form `'c'`.
 pragma(inline, false)
 ref W print(C = char, W)(scope return ref W w, char c)
diff --git a/source/mir/test.d b/source/mir/test.d
index ca6c7b8b..5f4258f3 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -65,7 +65,8 @@ struct Should(T)
     ///
     T value;
 
-    ///
+    static if(!is(immutable T == immutable ubyte[]))
+        ///
     void opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__)
     {
         import mir.format: stringBuf, getData;
@@ -77,12 +78,29 @@ struct Should(T)
         if (value == expected)
             return;
         auto buf = stringBuf;
-        assumeAllAttrAndCall({
-            throw new MirError(buf
-                << "expected " << expected
-                << ", got " << value
-                << getData, file, line);
-        });
+        buf << "mir.test.should:\n";
+            buf << "expected " << expected << "\n"
+                << "     got " << value;
+        assumeAllAttrAndCall({ throw new MirError(buf << getData, file, line); });
+    }
+    else
+    /// ditto
+    void opEquals(scope const ubyte[] expected, string file = __FILE__, int line = __LINE__)
+        @safe pure nothrow @nogc
+    {
+        import mir.format: stringBuf, getData;
+        if (value == expected)
+            return;
+        auto buf = stringBuf;
+        import mir.format: printHexArray;
+        import mir.ndslice.topology: map;
+        buf << "mir.test.should:\n";
+        buf << "expected ";
+        buf.printHexArray(expected);
+        buf << "\n";
+        buf << "     got ";
+        buf.printHexArray(   value);
+        assumeAllAttrAndCall({ throw new MirError(buf << getData, file, line); });
     }
 }
 
@@ -98,6 +116,9 @@ unittest
 {
     1.0.should == 1;
     should(1) == 1;
+
+    ubyte[] val = [0, 2, 3];
+    val.should = [0, 2, 3];
 }
 
 ///

From c6c821fc4f131a45b9a30d13afa6d345a5d06174 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 10 Jun 2022 01:18:51 +0400
Subject: [PATCH 446/573] fixup

---
 source/mir/test.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/test.d b/source/mir/test.d
index 5f4258f3..751ded7e 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -118,7 +118,7 @@ unittest
     should(1) == 1;
 
     ubyte[] val = [0, 2, 3];
-    val.should = [0, 2, 3];
+    val.should == [0, 2, 3];
 }
 
 ///

From fba267fdc3aff5d74cf469b3a8289245684c1148 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 10 Jun 2022 01:21:19 +0400
Subject: [PATCH 447/573] fixup

---
 dub.sdl             | 2 +-
 source/mir/format.d | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 9b0a93b4..893708c4 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -13,7 +13,7 @@ buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
     // versions "mir_ndslice_test"
-    // versions "mir_test"
+    versions "mir_test"
     dflags "-lowmem"
 }
 buildType "unittest-dip1008" {
diff --git a/source/mir/format.d b/source/mir/format.d
index b206e206..65e009dc 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -715,10 +715,11 @@ ref W printHexArray(C = char, W, T)(scope return ref W w,
 @safe pure nothrow @nogc
 version (mir_test) unittest
 {
+    import mir.test;
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
     ubyte[2] array = [0x34, 0x32];
-    assert(w.print(array[]).data == `34 32`);
+    w.printHexArray(array[]).data.should == `34 32`;
 }
 
 /// Prints escaped character in the form `'c'`.

From 69868ac096204c3735af2b1574cf9bbfd43e268c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 14 Jun 2022 12:30:39 +0400
Subject: [PATCH 448/573] optimise bigint parsing

---
 source/mir/bignum/internal/parse.d | 43 ++++++++++++++++++++++++------
 1 file changed, 35 insertions(+), 8 deletions(-)

diff --git a/source/mir/bignum/internal/parse.d b/source/mir/bignum/internal/parse.d
index 6d039f7e..9cd77d38 100644
--- a/source/mir/bignum/internal/parse.d
+++ b/source/mir/bignum/internal/parse.d
@@ -89,6 +89,7 @@ SmallDecimalParsingResult parseJsonNumberImpl()(scope const(char)[] str)
     return result;
 }
 
+version(mir_test)
 unittest
 {
     import mir.test;
@@ -99,6 +100,19 @@ unittest
     res.coefficient.should == 1234567890;
 }
 
+version(mir_test)
+unittest
+{
+    import mir.test;
+    auto res = "-12345.67890e-10".parseJsonNumberImpl;
+    res.key.should == DecimalExponentKey.e;
+    res.sign.should == true;
+    res.exponent.should == -15;
+    res.coefficient.should == 1234567890;
+}
+
+
+version(mir_test)
 unittest
 {
     import mir.test;
@@ -156,6 +170,7 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
             }
         }
 
+        W multiplier = 10;
         W d = str[0] - C('0');
         str = str[1 .. $];
 
@@ -205,8 +220,26 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
 
         if (d < 10)
         {
+            multiplier = 10;
+            static if (is(C == char) && is(W == ulong))
+            if (!__ctfe)
+            if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
+            {
+                multiplier = 1000000000UL;
+                d *= 100000000;
+                d += parseEightDigits(str[0 .. 8]);
+                str = str[8 .. $];
+                if (str.length >= 8 && isMadeOfEightDigits(str[0 .. 8]))
+                {
+                    multiplier = 1000000000UL * 100000000;
+                    d *= 100000000;
+                    d += parseEightDigits(str[0 .. 8]);
+                    str = str[8 .. $];
+                }
+            }
+
     F0:
-            if (_expect(mullAdd(10u, cast(uint)d), false))
+            if (_expect(mullAdd(multiplier, d), false))
                 return false;
             goto S;
         }
@@ -239,7 +272,7 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
             }
 
         IF:
-            W multiplier = 10;
+            multiplier = 10;
             static if (is(C == char) && is(W == ulong))
             if (!__ctfe)
             {
@@ -305,8 +338,6 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
         FIL:
             if (_expect(mullAdd(multiplier, d), false))
                 return false;
-            import mir.stdio;
-            // debug dump("str = ", str);
             if (str.length == 0)
                 return true;
             d = str[0] - C('0');
@@ -348,13 +379,9 @@ template decimalFromStringImpl(alias mullAdd, W = size_t)
                 long exponentPlus;
                 if (parse(str, exponentPlus) && str.length == 0)
                 {
-                    import mir.stdio;
-                    // debug dump("exponentPlus ", exponentPlus);
-                    // debug dump("exponent ", exponent);
                     import mir.checkedint: adds;
                     bool overflow;
                     exponent = exponent.adds(exponentPlus, overflow);
-                    // debug dump("overflow ", overflow);
                     return !overflow;
                 }
             }

From 71d5a55fdebd214f2cac19d934540b37cf9b7dd4 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 18 Jun 2022 06:15:20 -0500
Subject: [PATCH 449/573] Add additional filter UT examples (#419)

---
 source/mir/algorithm/iteration.d | 58 ++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 3d988c61..86bfa4d6 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -4095,6 +4095,64 @@ version(mir_test)
     assert(equal!equal(rc, [ [3, 100], [], [400, 102] ]));
 }
 
+/// Filter out NaNs
+version(mir_test)
+@safe pure nothrow
+unittest {
+    import mir.algorithm.iteration: equal, filter;
+    import mir.ndslice.slice: sliced;
+    import std.math.traits: isNaN;
+   
+    static immutable result1 = [1.0, 2];
+   
+    double x;
+    auto y = [1.0, 2, x].sliced;
+    auto z = y.filter!(a => !isNaN(a));
+    assert(z.equal(result1));
+}
+
+/// Filter out NaNs by row and by column
+version(mir_test)
+@safe pure
+unittest {
+    import mir.algorithm.iteration: equal, filter;
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.topology: byDim, map;
+    import std.math.traits: isNaN;
+   
+    static immutable result1 = [[1.0, 2], [3.0, 4, 5]];
+    static immutable result2 = [[1.0, 3], [2.0, 4], [5.0]];
+   
+    double x;
+    auto y = [[1.0, 2, x], [3.0, 4, 5]].fuse;
+
+    auto z1 = y.byDim!0.map!(filter!(a => !isNaN(a)));
+    assert(z1.equal!equal(result1));
+    auto z2 = y.byDim!1.map!(filter!(a => !isNaN(a)));
+    assert(z2.equal!equal(result2));
+}
+
+/// Filter entire rows/columns that have NaNs
+version(mir_test)
+@safe pure
+unittest {
+    import mir.algorithm.iteration: equal, filter;
+    import mir.ndslice.fuse: fuse;
+    import mir.ndslice.topology: byDim, map;
+    import std.math.traits: isNaN;
+   
+    static immutable result1 = [[3.0, 4, 5]];
+    static immutable result2 = [[1.0, 3], [2.0, 4]];
+   
+    double x;
+    auto y = [[1.0, 2, x], [3.0, 4, 5]].fuse;
+
+    auto z1 = y.byDim!0.filter!(a => a.all!(b => !isNaN(b)));
+    assert(z1.equal!equal(result1));
+    auto z2 = y.byDim!1.filter!(a => a.all!(b => !isNaN(b)));
+    assert(z2.equal!equal(result2));
+}
+
 /++
 Implements the higher order filter and map function. The predicate and map functions are passed to
 `mir.functional.naryFun`, and can either accept a string, or any callable

From ee68b09d78d4a1d5dd973c6cbc9f2ea953be00fc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 18 Jun 2022 15:17:56 +0400
Subject: [PATCH 450/573] fixup scope

---
 source/mir/ndslice/slice.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index f7361320..9f4252dd 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1622,7 +1622,7 @@ public:
     }
 
     ///ditto
-    void popFront(size_t dimension = 0)() @trusted
+    void popFront(size_t dimension = 0)() @trusted scope
         if (dimension < N && (dimension == 0 || kind != Contiguous))
     {
         assert(_lengths[dimension], __FUNCTION__ ~ ": length!" ~ dimension.stringof ~ " should be greater than 0.");
@@ -2488,7 +2488,7 @@ public:
     See_also: $(LREF .Slice.idup)
     +/
     Slice!(Unqual!DeepElement*, N)
-    dup()() scope @property @safe
+    dup()() scope @property @trusted
     {
         if (__ctfe)
         {

From 89b84442f65023891012692381f565b815b4da39 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 18 Jun 2022 17:02:45 +0400
Subject: [PATCH 451/573] adopt for DIP1000

---
 source/mir/algorithm/iteration.d    | 29 ++++++-----
 source/mir/interpolate/polynomial.d |  2 +-
 source/mir/ndslice/concatenation.d  |  6 +--
 source/mir/ndslice/internal.d       | 18 +++----
 source/mir/ndslice/slice.d          |  2 +-
 source/mir/series.d                 | 80 ++++++++++++++++++++++++-----
 source/mir/stdio.d                  | 16 +++---
 7 files changed, 104 insertions(+), 49 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 86bfa4d6..2358b095 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -703,9 +703,9 @@ S reduceImpl(alias fun, S, Slices...)(S seed, Slices slices)
     do
     {
         static if (DimensionCount!(Slices[0]) == 1)
-            seed = fun(seed, frontOf!slices);
+            mixin(`seed = fun(seed,` ~ frontOf!(Slices.length) ~ `);`);
         else
-            seed = .reduceImpl!fun(seed, frontOf!slices);
+            mixin(`seed = .reduceImpl!fun(seed,` ~ frontOf!(Slices.length) ~ `);`);
         foreach_reverse(ref slice; slices)
             slice.popFront;
     }
@@ -960,7 +960,7 @@ void eachImpl(alias fun, Slices...)(Slices slices)
     do
     {
         static if (DimensionCount!(Slices[0]) == 1)
-            fun(frontOf!slices);
+            mixin(`fun(`~ frontOf!(Slices.length) ~ `);`);
         else
             .eachImpl!fun(frontOf2!slices);
         foreach_reverse(i; Iota!(Slices.length))
@@ -988,7 +988,7 @@ void chequerEachImpl(alias fun, Slices...)(Chequer color, Slices slices)
     {
         do
         {
-            .chequerEachImpl!fun(color, frontOf!slices);
+            mixin(`.chequerEachImpl!fun(color,` ~ frontOf!(Slices.length) ~ `);`);
             color = cast(Chequer)!color;
             foreach_reverse(i; Iota!(Slices.length))
                 slices[i].popFront;
@@ -1029,7 +1029,7 @@ template eachOnBorder(alias fun)
             alias N = DimensionCount!(Slices[0]);
             static if (N == 1)
             {
-                fun(frontOf!slices);
+                mixin(`fun(`~ frontOf!(Slices.length) ~ `);`);
                 if (slices[0].length > 1)
                     fun(backOf!slices);
             }
@@ -1874,7 +1874,7 @@ bool findImpl(alias fun, size_t N, Slices...)(scope ref size_t[N] backwardIndex,
         {
             static if (DimensionCount!(Slices[0]) == 1)
             {
-                if (fun(frontOf!slices))
+                if (mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
                 {
                     backwardIndex[0] = slices[0].length;
                     return true;
@@ -1882,7 +1882,7 @@ bool findImpl(alias fun, size_t N, Slices...)(scope ref size_t[N] backwardIndex,
             }
             else
             {
-                if (findImpl!fun(backwardIndex[1 .. $], frontOf!slices))
+                if (mixin(`findImpl!fun(backwardIndex[1 .. $],` ~ frontOf!(Slices.length) ~ `)`))
                 {
                     backwardIndex[0] = slices[0].length;
                     return true;
@@ -2018,7 +2018,7 @@ template find(alias pred)
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) find(Slices...)(auto ref Slices slices)
+    @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) find(Slices...)(Slices slices)
         if (Slices.length && allSatisfy!(hasShape, Slices))
     {
         static if (Slices.length > 1)
@@ -2125,9 +2125,10 @@ version(mir_test) unittest
     assert(bi == [1, 1]);
     assert(sl.backward(bi) == 5);
 
+    import mir.test;
     // sl was changed
-    assert(sl == [[8, 8, 8],
-                  [8, 8, 5]]);
+    sl.should == [[8, 8, 8],
+                  [8, 8, 5]];
 }
 
 @safe pure nothrow
@@ -2160,7 +2161,7 @@ size_t anyImpl(alias fun, Slices...)(Slices slices)
         {
             static if (DimensionCount!(Slices[0]) == 1)
             {
-                if (fun(frontOf!slices))
+                if (mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
                     return true;
             }
             else
@@ -2320,7 +2321,7 @@ size_t allImpl(alias fun, Slices...)(Slices slices)
         {
             static if (DimensionCount!(Slices[0]) == 1)
             {
-                if (!fun(frontOf!slices))
+                if (!mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
                     return false;
             }
             else
@@ -2634,7 +2635,7 @@ template equal(alias pred = "a == b")
                     }
                     if (empty)
                         return true;
-                    if (!pred(frontOf!slices))
+                    if (!mixin(`pred(`~ frontOf!(Slices.length) ~ `)`))
                         goto False;
                     foreach (ref slice; slices)
                         slice.popFront;
@@ -2848,7 +2849,7 @@ size_t countImpl(alias fun, Slices...)(Slices slices)
     {
         static if (DimensionCount!(Slices[0]) == 1)
         {
-            if(fun(frontOf!slices))
+            if(mixin(`fun(`~ frontOf!(Slices.length) ~ `)`))
                 ret++;
         }
         else
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index aa7753f8..68e73502 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -70,7 +70,7 @@ version(mir_test)
 /++
 Constructs barycentric lagrange interpolant.
 +/
-Lagrange!(T, maxDerivative) lagrange(uint maxDerivative = 0, T, X)(scope const X[] x, scope const T[] y)
+Lagrange!(T, maxDerivative) lagrange(uint maxDerivative = 0, T, X)(scope const X[] x, scope const T[] y) @trusted
     if (maxDerivative < 16)
 {
     import mir.ndslice.allocation: rcslice;
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index 043302fd..0af813ab 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -286,7 +286,7 @@ struct Concatenation(size_t dim, Slices...)
     }
 
     /// Shape of the concatenation.
-    size_t[N] shape()() const @property
+    size_t[N] shape()() scope const @property
     {
         typeof(return) ret;
         foreach(i; Iota!N)
@@ -295,7 +295,7 @@ struct Concatenation(size_t dim, Slices...)
     }
 
     /// Multidimensional input range primitives
-    bool empty(size_t d = 0)() const @property
+    bool empty(size_t d = 0)() scope const @property
     {
         static if (d == dim)
         {
@@ -311,7 +311,7 @@ struct Concatenation(size_t dim, Slices...)
     }
 
     /// ditto
-    void popFront(size_t d = 0)()
+    void popFront(size_t d = 0)() scope
     {
         static if (d == dim)
         {
diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index 8f996d05..c30f89f9 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -359,18 +359,18 @@ package(mir) template strideOf(args...)
     }
 }
 
-package(mir) template frontOf(args...)
+package(mir) template frontOf(size_t n)
 {
-    static if (args.length == 0)
-        enum frontOf = args;
-    else
-    {
-        @optmath @property auto ref frontOfMod()()
+    enum frontOf = () {
+        string ret;
+        static foreach (i; 0 .. n)
         {
-            return args[0].front;
+            if (i)
+                ret ~= `, `;
+            ret ~= "slices[" ~ i.stringof ~ `].front`;
         }
-        alias frontOf = AliasSeq!(frontOfMod, frontOf!(args[1..$]));
-    }
+        return ret;
+    } ();
 }
 
 package(mir) template frontOf2(args...)
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 9f4252dd..eb8f1846 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -2375,7 +2375,7 @@ public:
     Note:
         Does not allocate neither new slice nor a closure.
     +/
-    auto opUnary(string op)() return scope
+    auto opUnary(string op)()
         if (op == "*" || op == "~" || op == "-" || op == "+")
     {
         import mir.ndslice.topology: map;
diff --git a/source/mir/series.d b/source/mir/series.d
index 773ea36c..23348324 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -2188,18 +2188,45 @@ Merges multiple (time) series into one.
 Makes exactly one memory allocation for two series union
 and two memory allocation for three and more series union.
 
-Params:
-    seriesTuple = variadic static array of composed of series, each series must be sorted.
 Returns: sorted GC-allocated series.
 See_also $(LREF Series.opBinary) $(LREF makeUnionSeries)
 */
-auto unionSeries(IndexIterator, Iterator, size_t N, SliceKind kind, size_t C)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple...)
-    if (C > 1)
+auto unionSeries(IndexIterator, Iterator, size_t N, SliceKind kind)(
+    Series!(IndexIterator, Iterator, N, kind) a,
+    Series!(IndexIterator, Iterator, N, kind) b,
+    ) @safe
+{
+    import core.lifetime: move;
+    Series!(IndexIterator, Iterator, N, kind)[2] ar = [move(a), move(b)];
+    return unionSeriesImplPrivate!false(move(ar));
+}
+
+/// ditto
+auto unionSeries(IndexIterator, Iterator, size_t N, SliceKind kind)(
+    Series!(IndexIterator, Iterator, N, kind) a,
+    Series!(IndexIterator, Iterator, N, kind) b,
+    Series!(IndexIterator, Iterator, N, kind) c,
+    ) @safe
 {
     import core.lifetime: move;
-    return unionSeriesImplPrivate!false(move(seriesTuple));
+    Series!(IndexIterator, Iterator, N, kind)[3] ar = [move(a), move(b), move(c)];
+    return unionSeriesImplPrivate!false(move(ar));
 }
 
+/// ditto
+auto unionSeries(IndexIterator, Iterator, size_t N, SliceKind kind)(
+    Series!(IndexIterator, Iterator, N, kind) a,
+    Series!(IndexIterator, Iterator, N, kind) b,
+    Series!(IndexIterator, Iterator, N, kind) c,
+    Series!(IndexIterator, Iterator, N, kind) d,
+    ) @safe
+{
+    import core.lifetime: move;
+    Series!(IndexIterator, Iterator, N, kind)[4] ar = [move(a), move(b), move(c), move(d)];
+    return unionSeriesImplPrivate!false(move(ar));
+}
+
+
 ///
 @safe pure nothrow version(mir_test) unittest
 {
@@ -2322,15 +2349,42 @@ auto makeUnionSeries(IndexIterator, Iterator, size_t N, SliceKind kind, size_t C
 /**
 Merges multiple (time) series into one.
 
-Params:
-    seriesTuple = variadic static array of composed of series.
 Returns: sorted manually allocated series.
 See_also $(LREF unionSeries)
 */
-auto rcUnionSeries(IndexIterator, Iterator, size_t N, SliceKind kind, size_t C)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple...)
-    if (C > 1)
+auto rcUnionSeries(IndexIterator, Iterator, size_t N, SliceKind kind)(
+    Series!(IndexIterator, Iterator, N, kind) a,
+    Series!(IndexIterator, Iterator, N, kind) b,
+    ) @safe
 {
-    return unionSeriesImplPrivate!true(seriesTuple);
+    import core.lifetime: move;
+    Series!(IndexIterator, Iterator, N, kind)[2] ar = [move(a), move(b)];
+    return unionSeriesImplPrivate!true(move(ar));
+}
+
+///ditto
+auto rcUnionSeries(IndexIterator, Iterator, size_t N, SliceKind kind)(
+    Series!(IndexIterator, Iterator, N, kind) a,
+    Series!(IndexIterator, Iterator, N, kind) b,
+    Series!(IndexIterator, Iterator, N, kind) c,
+    ) @safe
+{
+    import core.lifetime: move;
+    Series!(IndexIterator, Iterator, N, kind)[3] ar = [move(a), move(b), move(c)];
+    return unionSeriesImplPrivate!true(move(ar));
+}
+
+///ditto
+auto rcUnionSeries(IndexIterator, Iterator, size_t N, SliceKind kind)(
+    Series!(IndexIterator, Iterator, N, kind) a,
+    Series!(IndexIterator, Iterator, N, kind) b,
+    Series!(IndexIterator, Iterator, N, kind) c,
+    Series!(IndexIterator, Iterator, N, kind) d,
+    ) @safe
+{
+    import core.lifetime: move;
+    Series!(IndexIterator, Iterator, N, kind)[4] ar = [move(a), move(b), move(c), move(d)];
+    return unionSeriesImplPrivate!true(move(ar));
 }
 
 ///
@@ -2374,9 +2428,9 @@ Params:
 pragma(inline, false)
 auto unionSeriesImpl(I, E,
     IndexIterator, Iterator, size_t N, SliceKind kind, UI, UE)(
-    Series!(IndexIterator, Iterator, N, kind)[] seriesTuple,
+    scope Series!(IndexIterator, Iterator, N, kind)[] seriesTuple,
     Series!(UI*, UE*, N) uninitSeries,
-    )
+    ) @trusted
 {
     import mir.conv: emplaceRef;
     import mir.algorithm.setops: multiwayUnion;
@@ -2403,7 +2457,7 @@ auto unionSeriesImpl(I, E,
     }
 }
 
-private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator...)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple, ref Allocator allocator)
+private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator...)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple, ref Allocator allocator) @safe
     if (C > 1 && Allocator.length <= 1)
 {
     import mir.algorithm.setops: unionLength;
diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index 20ecdfed..a8a1069c 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -111,7 +111,7 @@ struct File
     /++
     Throws: $(LREF FileException)
     +/
-    void rawWrite(scope const(void)[] data)
+    void rawWrite(scope const(void)[] data) scope
         in (__ctfe || fp !is null)
     {
         if (__ctfe)
@@ -124,7 +124,7 @@ struct File
     /++
     Throws: $(LREF FileException)
     +/
-    void flush()
+    void flush() scope
         in (__ctfe || fp !is null)
     {
         if (__ctfe)
@@ -151,7 +151,7 @@ struct AssumeNothrowFile
     /++
     Throws: $(LREF FileError)
     +/
-    void rawWrite(scope const(void)[] data)
+    void rawWrite(scope const(void)[] data) scope
         in (__ctfe || fp !is null)
     {
         if (__ctfe)
@@ -164,7 +164,7 @@ struct AssumeNothrowFile
     /++
     Throws: $(LREF FileError)
     +/
-    void flush()
+    void flush() scope
         in (__ctfe || fp !is null)
     {
         if (__ctfe)
@@ -179,7 +179,7 @@ struct AssumeNothrowFile
 mixin template FileMembers()
 {
     ///
-    void put(C)(const C data)
+    void put(C)(const C data) scope
         if (is(C == char) || is(C == wchar) | is(C == dchar))
     {
         C[1] array = [data];
@@ -187,7 +187,7 @@ mixin template FileMembers()
     }
 
     ///
-    void put(C)(scope const(C)[] data)
+    void put(C)(scope const(C)[] data) scope
         if (is(C == char) || is(C == wchar) | is(C == dchar))
     {
         this.rawWrite(data);
@@ -218,7 +218,7 @@ mixin template FileMembers()
     }
 
     /// Writes values in a text form
-    void writeln(string separator = "", Args...)(auto ref const Args args)
+    void writeln(string separator = "", Args...)(auto ref const Args args) scope
         if (Args.length > 0)
     {
         write(args);
@@ -226,7 +226,7 @@ mixin template FileMembers()
     }
 
     /// ditto
-    void write(string separator = "", Args...)(auto ref const Args args)
+    void write(string separator = "", Args...)(auto ref const Args args) scope
         if (Args.length > 0)
     {
         pragma(inline, false);

From 4f77045e8f9a30f9fc1337a17408e78f3240dba3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 18 Jun 2022 18:28:01 +0400
Subject: [PATCH 452/573] fixup formatting

---
 source/mir/format.d |  2 +-
 source/mir/series.d | 28 +++++++++++++++++-----------
 source/mir/stdio.d  | 23 +++++++++++++++++++++++
 3 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 65e009dc..05277a11 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -994,7 +994,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
 /// ditto
 // FUTURE: remove it
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, scope const T c)
+ref W print(C = char, W, T)(scope return ref W w, const T c)
     if (isSomeChar!C && is(T == struct) || is(T == union))
 {
     return print!(C, W, T)(w, c);
diff --git a/source/mir/series.d b/source/mir/series.d
index 23348324..2f580edf 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -1316,21 +1316,16 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     {
         import mir.format: print;
         auto ls = lightScope;
-        print(w, "{ index: ", ls.index, ", data: ", ls.data, " }");
+        print(w, "{ index: ");
+        print(w, ls.index);
+        print(w, ", data: ");
+        print(w, ls.data);
+        print(w, " }");
     }
 
-    version(mir_test)
-    ///
-    unittest
-    {
-        import mir.series: series, sort;
-        auto s = ["b", "a"].series([9, 8]).sort;
-
-        import mir.format : text;
-        assert(s.text == `{ index: [a, b], data: [8, 9] }`);
-    }
 }
 
+
 /// ditto
 alias Series = mir_series;
 
@@ -1441,6 +1436,17 @@ alias Series = mir_series;
     }
 }
 
+ version(mir_test)
+///
+@safe unittest
+{
+    import mir.series: series, sort;
+    auto s = ["b", "a"].series([9, 8]).sort;
+
+    import mir.format : text;
+    assert(s.text == `{ index: [a, b], data: [8, 9] }`);
+}
+
 /++
 Convenient function for $(LREF Series) construction.
 See_also: $(LREF assocArray)
diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index a8a1069c..7a2939c9 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -316,3 +316,26 @@ private static immutable writeException = new FileException("Error on file write
 private static immutable flushException = new FileException("Error on file flush");
 private static immutable writeError = new FileError("Error on file write");
 private static immutable flushError = new FileError("Error on file flush");
+
+@safe unittest
+{
+    static struct ParameterSpec
+    {
+        string name;
+        string type;
+        string default_;
+    }
+
+    static struct FunctionOverloadSpec
+    {
+        ParameterSpec[] parameters;
+        string doc;
+        string test;
+        size_t minArgs;
+    }
+
+    import mir.algebraic;
+    import mir.stdio;
+    if (false)
+        Algebraic!(FunctionOverloadSpec, ParameterSpec).init.writeln; // error
+}

From b9706da6e9d9659b4b158922db51403d7c3505db Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 20 Jun 2022 16:03:27 +0400
Subject: [PATCH 453/573] fixup unitest version

---
 source/mir/stdio.d | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index 7a2939c9..64ae2283 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -317,6 +317,7 @@ private static immutable flushException = new FileException("Error on file flush
 private static immutable writeError = new FileError("Error on file write");
 private static immutable flushError = new FileError("Error on file flush");
 
+version(mir_test)
 @safe unittest
 {
     static struct ParameterSpec

From 9d4a2596b25af6714b3fe13b075d5a946c2a2bb3 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 20 Jun 2022 16:40:08 +0400
Subject: [PATCH 454/573] add scope qualifier for structs

---
 .gitignore          | 1 +
 source/mir/format.d | 7 ++++---
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index 53b25574..98af4e28 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@ mir-algorithm-test-secure
 bigint_benchmark/bigint_benchmark
 bigint_benchmark/temp.d
 mir-algorithm-test-silly
+mir-algorithm-test-ci-bignum-test
diff --git a/source/mir/format.d b/source/mir/format.d
index 05277a11..346ebcda 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -902,7 +902,7 @@ unittest
 
 /// Prints structs and unions
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, ref const T c)
+ref W print(C = char, W, T)(scope return ref W w, ref scope const T c)
     if (isSomeChar!C && is(T == struct) || is(T == union) && !is(T : NumericSpec))
 {
     static if (__traits(hasMember, T, "toString"))
@@ -945,7 +945,8 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
             static if (is(typeof(w.put((*cast(T*)&c).toString))))
                 w.put((*cast(T*)&c).toString);
             else
-                static assert(0, T.stringof ~ ".toString definition is wrong: 'const' qualifier may be missing.");
+                c.toString(w);
+                // static assert(0, T.stringof ~ ".toString definition is wrong: 'const' qualifier may be missing.");
         }
 
         return w;
@@ -994,7 +995,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref const T c)
 /// ditto
 // FUTURE: remove it
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, const T c)
+ref W print(C = char, W, T)(scope return ref W w, scope const T c)
     if (isSomeChar!C && is(T == struct) || is(T == union))
 {
     return print!(C, W, T)(w, c);

From 1050b3c0374f1faf6a4da1cfd4fa4e3661fbb529 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 21 Jun 2022 19:20:32 +0400
Subject: [PATCH 455/573] fixup decimal toString

---
 source/mir/bignum/decimal.d | 356 ++++++++++++++++++------------------
 1 file changed, 178 insertions(+), 178 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 0d0d1862..9cf91db3 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -34,6 +34,184 @@ struct Decimal(uint size64)
     ///
     BigInt!size64 coefficient;
 
+    ///
+    void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
+        if(isSomeChar!C && isMutable!C)
+    {
+        assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human);
+        import mir.utility: _expect;
+        // handle special values
+        if (_expect(exponent == exponent.max, false))
+        {
+            static immutable C[3] nan = "nan";
+            static immutable C[4] ninf = "-inf";
+            static immutable C[4] pinf = "+inf";
+            w.put(coefficient.length == 0 ? coefficient.sign ? ninf[] : pinf[] : nan[]);
+            return;
+        }
+
+        C[coefficientBufferLength + 16] buffer0 = void;
+        auto buffer = buffer0[0 .. $ - 16];
+
+        size_t coefficientLength;
+        static if (size_t.sizeof == 8)
+        {
+            if (__ctfe)
+            {
+                uint[coefficient.data.length * 2] data;
+                foreach (i; 0 .. coefficient.length)
+                {
+                    auto l = cast(uint)coefficient.data[i];
+                    auto h = cast(uint)(coefficient.data[i] >> 32);
+                    data[i * 2 + 0] = l;
+                    data[i * 2 + 1] = h;
+                }
+                auto work = BigUIntView!uint(data);
+                work = work.topLeastSignificantPart(coefficient.length * 2).normalized;
+                coefficientLength = work.toStringImpl(buffer);
+            }
+            else
+            {
+                BigInt!size64 work = coefficient;
+                coefficientLength = work.view.unsigned.toStringImpl(buffer);
+            }
+        }
+        else
+        {
+            BigInt!size64 work = coefficient;
+            coefficientLength = work.view.unsigned.toStringImpl(buffer);
+        }
+
+        C[1] sign = coefficient.sign ? "-" : "+";
+        bool addSign = coefficient.sign || spec.plus;
+        long s = this.exponent + coefficientLength;
+
+        alias zeros = zerosImpl!C;
+
+        if (spec.format == NumericSpec.Format.human)
+        {
+            if (!spec.separatorCount)
+                spec.separatorCount = 3;
+            void putL(scope const(C)[] b)
+            {
+                assert(b.length);
+
+                if (addSign)
+                    w.put(sign[]);
+
+                auto r = b.length % spec.separatorCount;
+                if (r == 0)
+                    r = spec.separatorCount;
+                C[1] sep = spec.separatorChar;
+                goto LS;
+                do
+                {
+                    w.put(sep[]);
+                LS:
+                    w.put(b[0 .. r]);
+                    b = b[r .. $];
+                    r = spec.separatorCount;
+                }
+                while(b.length);
+            }
+
+            // try print decimal form without exponent
+            // up to 6 digits exluding leading 0. or final .0
+            if (s <= 0)
+            {
+                //0.001....
+                //0.0001
+                //0.00001
+                //0.000001
+                //If separatorChar is defined lets be less greed for space.
+                if (this.exponent >= -6 || s >= -2 - (spec.separatorChar != 0) * 3)
+                {
+                    if (addSign)
+                        w.put(sign[]);
+                    w.put(zeros[0 .. cast(sizediff_t)(-s + 2)]);
+                    w.put(buffer[$ - coefficientLength .. $]);
+                    return;
+                }
+            }
+            else
+            if (this.exponent >= 0)
+            {
+                ///dddddd.0
+                if (!spec.separatorChar)
+                {
+                    if (s <= 6)
+                    {
+                        buffer[$ - coefficientLength - 1] = sign[0];
+                        w.put(buffer[$ - coefficientLength - addSign .. $]);
+                        w.put(zeros[($ - (cast(sizediff_t)this.exponent + 2)) .. $]);
+                        return;
+                    }
+                }
+                else
+                {
+                    if (s <= 12)
+                    {
+                        buffer0[$ - 16 .. $] = '0';
+                        putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + cast(sizediff_t)this.exponent]);
+                        w.put(zeros[$ - 2 .. $]);
+                        return;
+                    }
+                }
+            }
+            else
+            {
+                ///dddddd.0
+                if (!spec.separatorChar)
+                {
+                    ///dddddd.d....
+                    if (s <= 6 || coefficientLength <= 6)
+                    {
+                        buffer[$ - coefficientLength - 1] = sign[0];
+                        w.put(buffer[$ - coefficientLength  - addSign .. $ - coefficientLength + cast(sizediff_t)s]);
+                    T2:
+                        buffer[$ - coefficientLength + cast(sizediff_t)s - 1] = '.';
+                        w.put(buffer[$ - coefficientLength + cast(sizediff_t)s - 1 .. $]);
+                        return;
+                    }
+                }
+                else
+                {
+                    if (s <= 12 || coefficientLength <= 12)
+                    {
+                        putL(buffer[$ - coefficientLength .. $ - coefficientLength + cast(sizediff_t)s]);
+                        goto T2;
+                    }
+                }
+            }
+        }
+
+        assert(coefficientLength);
+
+        long exponent = s - 1;
+
+        if (coefficientLength > 1)
+        {
+            auto c = buffer[$ - coefficientLength];
+            buffer[$ - coefficientLength] = '.';
+            buffer[$ - ++coefficientLength] = c;
+        }
+
+        buffer[$ - coefficientLength - 1] = sign[0];
+        w.put(buffer[$ - coefficientLength - addSign .. $]);
+
+        import mir.format_impl: printSignedToTail;
+
+        static if (exponent.sizeof == 8)
+            enum N = 21;
+        else
+            enum N = 11;
+
+        // prints e+/-exponent
+        auto expLength = printSignedToTail(exponent, buffer0[$ - N - 16 .. $ - 16], '+');
+        buffer[$ - ++expLength] = spec.exponentChar;
+        w.put(buffer[$ - expLength .. $]);
+    }
+
 @safe:
 
     ///
@@ -214,184 +392,6 @@ struct Decimal(uint size64)
         return buffer.data.idup;
     }
 
-    ///
-    void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
-        if(isSomeChar!C && isMutable!C)
-    {
-        assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human);
-        import mir.utility: _expect;
-        // handle special values
-        if (_expect(exponent == exponent.max, false))
-        {
-            static immutable C[3] nan = "nan";
-            static immutable C[4] ninf = "-inf";
-            static immutable C[4] pinf = "+inf";
-            w.put(coefficient.length == 0 ? coefficient.sign ? ninf[] : pinf[] : nan[]);
-            return;
-        }
-
-        C[coefficientBufferLength + 16] buffer0 = void;
-        auto buffer = buffer0[0 .. $ - 16];
-
-        size_t coefficientLength;
-        static if (size_t.sizeof == 8)
-        {
-            if (__ctfe)
-            {
-                uint[coefficient.data.length * 2] data;
-                foreach (i; 0 .. coefficient.length)
-                {
-                    auto l = cast(uint)coefficient.data[i];
-                    auto h = cast(uint)(coefficient.data[i] >> 32);
-                    data[i * 2 + 0] = l;
-                    data[i * 2 + 1] = h;
-                }
-                auto work = BigUIntView!uint(data);
-                work = work.topLeastSignificantPart(coefficient.length * 2).normalized;
-                coefficientLength = work.toStringImpl(buffer);
-            }
-            else
-            {
-                BigInt!size64 work = coefficient;
-                coefficientLength = work.view.unsigned.toStringImpl(buffer);
-            }
-        }
-        else
-        {
-            BigInt!size64 work = coefficient;
-            coefficientLength = work.view.unsigned.toStringImpl(buffer);
-        }
-
-        C[1] sign = coefficient.sign ? "-" : "+";
-        bool addSign = coefficient.sign || spec.plus;
-        long s = this.exponent + coefficientLength;
-
-        alias zeros = zerosImpl!C;
-
-        if (spec.format == NumericSpec.Format.human)
-        {
-            if (!spec.separatorCount)
-                spec.separatorCount = 3;
-            void putL(scope const(C)[] b)
-            {
-                assert(b.length);
-
-                if (addSign)
-                    w.put(sign[]);
-
-                auto r = b.length % spec.separatorCount;
-                if (r == 0)
-                    r = spec.separatorCount;
-                C[1] sep = spec.separatorChar;
-                goto LS;
-                do
-                {
-                    w.put(sep[]);
-                LS:
-                    w.put(b[0 .. r]);
-                    b = b[r .. $];
-                    r = spec.separatorCount;
-                }
-                while(b.length);
-            }
-
-            // try print decimal form without exponent
-            // up to 6 digits exluding leading 0. or final .0
-            if (s <= 0)
-            {
-                //0.001....
-                //0.0001
-                //0.00001
-                //0.000001
-                //If separatorChar is defined lets be less greed for space.
-                if (this.exponent >= -6 || s >= -2 - (spec.separatorChar != 0) * 3)
-                {
-                    if (addSign)
-                        w.put(sign[]);
-                    w.put(zeros[0 .. cast(sizediff_t)(-s + 2)]);
-                    w.put(buffer[$ - coefficientLength .. $]);
-                    return;
-                }
-            }
-            else
-            if (this.exponent >= 0)
-            {
-                ///dddddd.0
-                if (!spec.separatorChar)
-                {
-                    if (s <= 6)
-                    {
-                        buffer[$ - coefficientLength - 1] = sign[0];
-                        w.put(buffer[$ - coefficientLength - addSign .. $]);
-                        w.put(zeros[($ - (cast(sizediff_t)this.exponent + 2)) .. $]);
-                        return;
-                    }
-                }
-                else
-                {
-                    if (s <= 12)
-                    {
-                        buffer0[$ - 16 .. $] = '0';
-                        putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + cast(sizediff_t)this.exponent]);
-                        w.put(zeros[$ - 2 .. $]);
-                        return;
-                    }
-                }
-            }
-            else
-            {
-                ///dddddd.0
-                if (!spec.separatorChar)
-                {
-                    ///dddddd.d....
-                    if (s <= 6 || coefficientLength <= 6)
-                    {
-                        buffer[$ - coefficientLength - 1] = sign[0];
-                        w.put(buffer[$ - coefficientLength  - addSign .. $ - coefficientLength + cast(sizediff_t)s]);
-                    T2:
-                        buffer[$ - coefficientLength + cast(sizediff_t)s - 1] = '.';
-                        w.put(buffer[$ - coefficientLength + cast(sizediff_t)s - 1 .. $]);
-                        return;
-                    }
-                }
-                else
-                {
-                    if (s <= 12 || coefficientLength <= 12)
-                    {
-                        putL(buffer[$ - coefficientLength .. $ - coefficientLength + cast(sizediff_t)s]);
-                        goto T2;
-                    }
-                }
-            }
-        }
-
-        assert(coefficientLength);
-
-        long exponent = s - 1;
-
-        if (coefficientLength > 1)
-        {
-            auto c = buffer[$ - coefficientLength];
-            buffer[$ - coefficientLength] = '.';
-            buffer[$ - ++coefficientLength] = c;
-        }
-
-        buffer[$ - coefficientLength - 1] = sign[0];
-        w.put(buffer[$ - coefficientLength - addSign .. $]);
-
-        import mir.format_impl: printSignedToTail;
-
-        static if (exponent.sizeof == 8)
-            enum N = 21;
-        else
-            enum N = 11;
-
-        // prints e+/-exponent
-        auto expLength = printSignedToTail(exponent, buffer0[$ - N - 16 .. $ - 16], '+');
-        buffer[$ - ++expLength] = spec.exponentChar;
-        w.put(buffer[$ - expLength .. $]);
-    }
-
     /++
     Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. 
     Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP.    +/

From 5818b5afd45a40804c0c9d683cfd6de44cbb2a86 Mon Sep 17 00:00:00 2001
From: Jan Jurzitza <gh@webfreak.org>
Date: Fri, 24 Jun 2022 14:31:31 +0200
Subject: [PATCH 456/573] add `@serdeProxyCast`, `@serdeEnumProxy` UDAs (#408)

Implementation is needed on the mir-ion side for use though
---
 source/mir/serde.d | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index bcd5c7f0..f7cd58c1 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -13,6 +13,7 @@ module mir.serde;
 
 import mir.functional: naryFun;
 import mir.reflection;
+import std.meta: AliasSeq;
 import std.traits: TemplateArgsOf, EnumMembers, hasUDA, isAggregateType;
 
 version (D_Exceptions)
@@ -653,6 +654,13 @@ version(mir_test) unittest
 +/
 alias serdeGetProxy(alias symbol) = TemplateArgsOf!(getUDA!(symbol, serdeProxy))[0];
 
+/// Can be applied to @serdeProxy types to make (de)serialization use
+/// underlying type through casting. Useful for enums.
+enum serdeProxyCast;
+
+/// Equivalent to @serdeProxy!T @serdeProxyCast
+alias serdeEnumProxy(T) = AliasSeq!(serdeProxy!T, serdeProxyCast);
+
 /++
 Attributes to conditional ignore field during serialization.
 

From 53b56522c57835700082cb5634c63c66b46ef2f9 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Wed, 29 Jun 2022 07:42:17 -0500
Subject: [PATCH 457/573] Add additional filter UT example (#421)

* Update filter UTs with comments

* Add additional filter UT example
---
 source/mir/algorithm/iteration.d | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 2358b095..ec00ae58 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -4096,6 +4096,27 @@ version(mir_test)
     assert(equal!equal(rc, [ [3, 100], [], [400, 102] ]));
 }
 
+/// N-dimensional filtering based on value in specific row/column
+version(mir_test)
+@safe pure
+unittest
+{
+    import mir.ndslice.fuse;
+    import mir.ndslice.topology: byDim;
+
+    auto matrix =
+    [[   3,   2, 400 ],
+     [ 100, -101, 102 ]].fuse;
+
+    // filter row based on value in index 1
+    auto r1 = matrix.byDim!0.filter!("a[1] > 0");
+    assert(equal!equal(r1, [ [3, 2, 400] ]));
+
+    // filter column based on value in index 1
+    auto r2 = matrix.byDim!1.filter!("a[1] > 0");
+    assert(equal!equal(r2, [ [3, 100], [400, 102] ]));
+}
+
 /// Filter out NaNs
 version(mir_test)
 @safe pure nothrow
@@ -4127,8 +4148,10 @@ unittest {
     double x;
     auto y = [[1.0, 2, x], [3.0, 4, 5]].fuse;
 
+    // by row
     auto z1 = y.byDim!0.map!(filter!(a => !isNaN(a)));
     assert(z1.equal!equal(result1));
+    // by column
     auto z2 = y.byDim!1.map!(filter!(a => !isNaN(a)));
     assert(z2.equal!equal(result2));
 }
@@ -4148,8 +4171,10 @@ unittest {
     double x;
     auto y = [[1.0, 2, x], [3.0, 4, 5]].fuse;
 
+    // by row
     auto z1 = y.byDim!0.filter!(a => a.all!(b => !isNaN(b)));
     assert(z1.equal!equal(result1));
+    // by column
     auto z2 = y.byDim!1.filter!(a => a.all!(b => !isNaN(b)));
     assert(z2.equal!equal(result2));
 }

From ed862a7d42fc636cf8338946a74440117970f2af Mon Sep 17 00:00:00 2001
From: Dennis Korpel <dkorpel@gmail.com>
Date: Sun, 3 Jul 2022 14:02:15 +0200
Subject: [PATCH 458/573] Don't depend on `stringof` in `mir.parse` test

---
 source/mir/parse.d | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source/mir/parse.d b/source/mir/parse.d
index 046dfbd7..b8c19f3d 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -381,6 +381,7 @@ version (mir_test) unittest
 version (mir_test) unittest
 {
     import mir.test: should;
+    import mir.conv: to;
     import std.meta: AliasSeq;
     foreach (T; AliasSeq!(byte, short, int, long))
     {
@@ -396,7 +397,7 @@ version (mir_test) unittest
         val.should == -9;
         assert(str == "text");
         enum m = T.min + 0;
-        str = m.stringof;
+        str = m.to!string;
         assert(parse(str, val));
         val.should == T.min;
     }

From 494908a203327e47ce721f25993f6e9058de1b1b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 8 Jul 2022 16:08:53 +0400
Subject: [PATCH 459/573] simplify mir.math.stat

---
 source/mir/math/stat.d | 23 +++++------------------
 1 file changed, 5 insertions(+), 18 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 95c8c66e..b6f53bda 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -2086,12 +2086,8 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        if (isPopulation == false)
-            return cast(F) sumOfSquares.sum / cast(F) (count - 1) - 
-                (cast(F) meanAccumulator.mean) ^^ 2 * (cast(F) count / cast(F) (count - 1));
-        else
-            return cast(F) sumOfSquares.sum / cast(F) count - 
-                (cast(F) meanAccumulator.mean) ^^ 2;
+        return cast(F) sumOfSquares.sum / cast(F) (count + isPopulation - 1) - 
+            (cast(F) meanAccumulator.mean) ^^ 2 * (cast(F) count / cast(F) (count + isPopulation - 1));
     }
 }
 
@@ -2198,10 +2194,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        if (isPopulation == false)
-            return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
-        else
-            return cast(F) centeredSumOfSquares.sum / cast(F) count;
+        return cast(F) centeredSumOfSquares.sum / cast(F) (count + isPopulation - 1);
     }
 }
 
@@ -2412,10 +2405,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        if (isPopulation == false)
-            return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
-        else
-            return cast(F) centeredSumOfSquares.sum / cast(F) count;
+        return cast(F) centeredSumOfSquares.sum / cast(F) (count + isPopulation - 1);
     }
 }
 
@@ -2539,10 +2529,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        if (isPopulation == false)
-            return cast(F) centeredSumOfSquares.sum / cast(F) (count - 1);
-        else
-            return cast(F) centeredSumOfSquares.sum / cast(F) count;
+        return cast(F) centeredSumOfSquares.sum / cast(F) (count + isPopulation - 1);
     }
 }
 

From db45e1b05862783e331136545be36b8db689ebcd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 8 Jul 2022 16:27:53 +0400
Subject: [PATCH 460/573] simplify variance

---
 source/mir/math/stat.d | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index b6f53bda..f1f8a5c3 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -2086,8 +2086,8 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        return cast(F) sumOfSquares.sum / cast(F) (count + isPopulation - 1) - 
-            (cast(F) meanAccumulator.mean) ^^ 2 * (cast(F) count / cast(F) (count + isPopulation - 1));
+        return cast(F) sumOfSquares.sum / (count + isPopulation - 1) - 
+            (cast(F) meanAccumulator.mean) ^^ 2 * (cast(F) count / (count + isPopulation - 1));
     }
 }
 
@@ -2194,7 +2194,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        return cast(F) centeredSumOfSquares.sum / cast(F) (count + isPopulation - 1);
+        return cast(F) centeredSumOfSquares.sum / (count + isPopulation - 1);
     }
 }
 
@@ -2405,7 +2405,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        return cast(F) centeredSumOfSquares.sum / cast(F) (count + isPopulation - 1);
+        return cast(F) centeredSumOfSquares.sum / (count + isPopulation - 1);
     }
 }
 
@@ -2529,7 +2529,7 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     F variance(F = T)(bool isPopulation) @property
     {
-        return cast(F) centeredSumOfSquares.sum / cast(F) (count + isPopulation - 1);
+        return cast(F) centeredSumOfSquares.sum / (count + isPopulation - 1);
     }
 }
 

From 35f39ad0b5ef4091243310874e7e6cc50e1fb3f9 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 8 Jul 2022 17:18:15 +0400
Subject: [PATCH 461/573] fix VarianceAccumulator

---
 source/mir/math/stat.d | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index f1f8a5c3..d6fa903b 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -2143,18 +2143,6 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     MeanAccumulator!(T, summation) meanAccumulator;
 
-    ///
-    size_t count() @property
-    {
-        return meanAccumulator.count;
-    }
-
-    ///
-    F mean(F = T)() const @property
-    {
-        return meanAccumulator.mean;
-    }
-
     ///
     Summator!(T, summation) centeredSumOfSquares;
 
@@ -2191,6 +2179,20 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
         centeredSumOfSquares.put(v.centeredSumOfSquares.sum + delta * delta * v.count * oldCount / count);
     }
 
+const:
+
+    ///
+    size_t count() @property
+    {
+        return meanAccumulator.count;
+    }
+
+    ///
+    F mean(F = T)() const @property
+    {
+        return meanAccumulator.mean;
+    }
+
     ///
     F variance(F = T)(bool isPopulation) @property
     {

From e157488f63ba0cbb9a79483f87436d77407998e8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 8 Jul 2022 17:23:25 +0400
Subject: [PATCH 462/573] fix VarianceAccumulator

---
 source/mir/math/stat.d | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index d6fa903b..64b9b692 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -2051,18 +2051,6 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     MeanAccumulator!(T, summation) meanAccumulator;
 
-    ///
-    size_t count() @property
-    {
-        return meanAccumulator.count;
-    }
-
-    ///
-    F mean(F = T)() const @property
-    {
-        return meanAccumulator.mean;
-    }
-
     ///
     Summator!(T, summation) sumOfSquares;
 
@@ -2083,6 +2071,20 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
         sumOfSquares.put(x * x);
     }
 
+const:
+
+    ///
+    size_t count() @property
+    {
+        return meanAccumulator.count;
+    }
+
+    ///
+    F mean(F = T)() const @property
+    {
+        return meanAccumulator.mean;
+    }
+
     ///
     F variance(F = T)(bool isPopulation) @property
     {

From 261604bbd77d90ee9b46858676f1c4abe0c62272 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 8 Jul 2022 17:25:42 +0400
Subject: [PATCH 463/573] ditto

---
 source/mir/math/stat.d | 52 +++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 24 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 64b9b692..78a9f9fb 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -2358,18 +2358,6 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     ///
     MeanAccumulator!(T, summation) meanAccumulator;
 
-    ///
-    size_t count() @property
-    {
-        return meanAccumulator.count;
-    }
-
-    ///
-    F mean(F = T)() const @property
-    {
-        return meanAccumulator.mean;
-    }
-
     ///
     Summator!(T, summation) centeredSumOfSquares;
 
@@ -2406,6 +2394,20 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
         centeredSumOfSquares.put(cast(T) 0);
     }
 
+const:
+
+    ///
+    size_t count() @property
+    {
+        return meanAccumulator.count;
+    }
+
+    ///
+    F mean(F = T)() const @property
+    {
+        return meanAccumulator.mean;
+    }
+
     ///
     F variance(F = T)(bool isPopulation) @property
     {
@@ -2477,18 +2479,6 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
     import mir.ndslice.slice: Slice, SliceKind, hasAsSlice;
 
     private size_t _count;
-    
-    ///
-    size_t count() @property
-    {
-        return _count;
-    }
-    
-    ///
-    F mean(F = T)() const @property
-    {
-        return cast(F) 0;
-    }
 
     ///
     Summator!(T, summation) centeredSumOfSquares;
@@ -2530,6 +2520,20 @@ struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation)
         centeredSumOfSquares.put(v.centeredSumOfSquares.sum);
     }
 
+const:
+
+    ///
+    size_t count() @property
+    {
+        return _count;
+    }
+    
+    ///
+    F mean(F = T)() const @property
+    {
+        return cast(F) 0;
+    }
+
     ///
     F variance(F = T)(bool isPopulation) @property
     {

From 1dd5b1a338bcb4ea550de4465dfad876d95d3379 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Mon, 11 Jul 2022 12:41:18 -0400
Subject: [PATCH 464/573] Add support for YearMonth dates (#427)

* Add support for YearMonth dates

* Make YearMonth non-generic

* Fixup YearMonth issues
---
 source/mir/date.d | 424 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 420 insertions(+), 4 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index 72e3578b..b49378c0 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -259,6 +259,15 @@ struct YearMonthDay
 
     ///
     alias opCast(T : Timestamp) = timestamp;
+    
+    ///
+    YearMonth yearMonth() @safe pure nothrow @nogc @property
+    {
+        return YearMonth(year, cast(ubyte)month);
+    }
+
+    ///
+    alias opCast(T : YearMonth) = yearMonth;
 
     ///
     version(mir_test)
@@ -282,6 +291,12 @@ struct YearMonthDay
         this = date.yearMonthDay;
     }
 
+    ///
+    this(YearMonth yearMonth, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure nothrow @nogc
+    {
+        with(yearMonth) this(year, month, day(assumePeriod));
+    }
+
     version(D_Exceptions)
     ///
     this(Timestamp timestamp) @safe pure nothrow @nogc
@@ -435,6 +450,221 @@ struct YearMonthDay
     }
 }
 
+///
+enum AssumePeriod {
+    begin,
+    end
+}
+
+///
+@serdeProxy!Timestamp
+struct YearMonth
+{
+    short year  = 1;
+    Month month = Month.jan;
+    
+    @property ubyte day(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
+    {
+        final switch (assumePeriod)
+        {
+            case AssumePeriod.begin:
+                return 1;
+            case AssumePeriod.end:
+                return daysInMonth;
+        }
+    }
+
+    ///
+    Timestamp timestamp(bool includeDay = false) @safe pure nothrow @nogc @property
+    {
+        if (!includeDay)
+            return Timestamp(year, cast(ubyte)month);
+        else
+            return Timestamp(year, cast(ubyte)month, this.day);  
+    }
+
+    ///
+    alias opCast(T : Timestamp) = timestamp;
+
+    ///
+    version(mir_test)
+    unittest
+    {
+        import mir.timestamp;
+        auto ym0 = YearMonth(2020, Month.may);
+        auto timestamp1 = cast(Timestamp) ym0;
+        auto timestamp2 = ym0.timestamp(true);
+        auto ym1 = YearMonth(timestamp1);
+        auto ym2 = YearMonth(timestamp2);
+    }
+
+    ///
+    this(short year, Month month) @safe pure nothrow @nogc
+    {
+        this.year = year;
+        this.month = month;
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(2000, Month.dec);
+    }
+
+    ///
+    this(Date date) @safe pure nothrow @nogc
+    {
+        this = date.yearMonth;
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(Date(2000, Month.dec, 31));
+    }
+
+    ///
+    this(YearMonthDay yearMonthDay) @safe pure nothrow @nogc
+    {
+        with(yearMonthDay) this(year, month);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(YearMonthDay(2000, Month.dec, 31));
+    }
+
+    version(D_Exceptions)
+    ///
+    this(Timestamp timestamp) @safe pure nothrow @nogc
+    {
+        if (!(timestamp.precision == Timestamp.Precision.day) ||
+             (timestamp.precision == Timestamp.Precision.month))
+        {
+            static immutable exc = new Exception("YearMonth: invalid timestamp precision");
+        }
+        with(timestamp) this(year, cast(Month)month);
+    }
+
+    // Shares documentation with "years" version.
+    @safe pure nothrow @nogc
+    ref YearMonth add(string units)(long months)
+        if (units == "months")
+    {
+        auto years = months / 12;
+        months %= 12;
+        auto newMonth = month + months;
+
+        if (months < 0)
+        {
+            if (newMonth < 1)
+            {
+                newMonth += 12;
+                --years;
+            }
+        }
+        else if (newMonth > 12)
+        {
+            newMonth -= 12;
+            ++years;
+        }
+
+        year += years;
+        month = cast(Month) newMonth;
+
+        return this;
+    }
+
+    // Shares documentation with "years" version.
+    @safe pure nothrow @nogc
+    ref YearMonth add(string units)(long years)
+        if (units == "years")
+    {
+        year += years;
+        return this;
+    }
+
+    private void setMonthOfYear(bool useExceptions = false)(int days)
+    {
+        immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
+
+        bool dayOutOfRange = days <= 0 || days > (isLeapYear ? daysInLeapYear : daysInYear);
+
+        static if (useExceptions)
+        {
+            if (dayOutOfRange) throw InvalidDay;
+        }
+        else
+        {
+            assert(!dayOutOfRange, "Invalid Day");
+        }
+
+        foreach (i; 1 .. lastDay.length)
+        {
+            if (days <= lastDay[i])
+            {
+                month = cast(Month)(cast(int) Month.jan + i - 1);
+                return;
+            }
+        }
+        assert(0, "Invalid day of the year.");
+    }
+
+    /++
+        Day of the year this $(LREF Date) is on.
+      +/
+    @property int dayOfYear(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
+    {
+        if (month >= Month.jan && month <= Month.dec)
+        {
+            immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
+            auto monthIndex = month - Month.jan;
+
+            return lastDay[monthIndex] + day(assumePeriod);
+        }
+        assert(0, "Invalid month.");
+    }
+
+    /++
+        Whether this $(LREF Date) is in a leap year.
+     +/
+    @property bool isLeapYear() const @safe pure nothrow @nogc
+    {
+        return yearIsLeapYear(year);
+    }
+
+    /++
+        The last day in the month that this $(LREF Date) is in.
+      +/
+    @property ubyte daysInMonth() const @safe pure nothrow @nogc
+    {
+        return maxDay(year, month);
+    }
+
+    /++
+        Whether the current year is a date in A.D.
+      +/
+    @property bool isAD() const @safe pure nothrow @nogc
+    {
+        return year > 0;
+    }
+}
+
+///
+version (mir_test)
+@safe unittest
+{
+    assert(YearMonth(1999, cast(Month) 1).day(AssumePeriod.begin) == 1);
+    assert(YearMonth(1999, cast(Month) 12).day(AssumePeriod.end) == 31);
+    assert(YearMonth(1999, cast(Month) 1).dayOfYear(AssumePeriod.begin) == 1);
+    assert(YearMonth(1999, cast(Month) 12).dayOfYear(AssumePeriod.end) == 365);
+    assert(YearMonth(2000, cast(Month) 12).dayOfYear(AssumePeriod.end) == 366);
+}
+
 /++
     Represents a date in the
     $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
@@ -580,6 +810,28 @@ public:
         with(ymd) this(year, month, day);
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = date(YearMonthDay(2020, Month.may, 31));
+    }
+
+    version(D_Exceptions)
+    ///
+    this(YearMonth ym, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure @nogc
+    {
+        with(ym) this(year, month, day(assumePeriod));
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d1 = date(YearMonth(2020, Month.may));
+        auto d2 = date(YearMonth(2020, Month.may), AssumePeriod.end);
+    }
+
     version(D_Exceptions)
     ///
     this(int _year, int _month, int _day) @safe pure @nogc
@@ -886,7 +1138,7 @@ public:
 
     private enum uint _startDict = Date(1900, 1, 1)._julianDay; // [
     private enum uint _endDict = Date(2040, 1, 1)._julianDay; // )
-    static immutable _dict = ()
+    static immutable _dictYMD = ()
     {
         YearMonthDay[Date._endDict - Date._startDict] dict;
         foreach (uint i; 0 .. dict.length)
@@ -904,21 +1156,52 @@ public:
             bool overflow;
             auto index = subu(day, _startDict, overflow);
             if (!overflow)
-                return _dict[index];
+                return _dictYMD[index];
         }
         return yearMonthDayImpl;
     }
 
+    ///
+    YearMonth yearMonth() const @safe pure nothrow @nogc @property
+    {
+        uint day = _julianDay;
+        if (day < _endDict)
+        {
+            return yearMonthDay().YearMonth;
+        }
+        return yearMonthImpl;
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date(2020, Month.may, 31);
+        auto ym = d.yearMonth;
+        assert(ym.year == 2020);
+        assert(ym.month == Month.may);
+    }
+
+    //
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date(2050, Month.dec, 31);
+        auto ym = d.yearMonth;
+        assert(ym.year == 2050);
+        assert(ym.month == Month.dec);
+    }
+
     ///
     short year() const @safe pure nothrow @nogc @property
     {
-        return yearMonthDay.year;
+        return yearMonth.year;
     }
 
     ///
     Month month() const @safe pure nothrow @nogc @property
     {
-        return yearMonthDay.month;
+        return yearMonth.month;
     }
 
     ///
@@ -927,6 +1210,16 @@ public:
         return yearMonthDay.day;
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date(2020, Month.may, 31);
+        assert(d.year == 2020);
+        assert(d.month == Month.may);
+        assert(d.day == 31);
+    }
+
     pragma(inline, false)
     YearMonthDay yearMonthDayImpl() const @safe pure nothrow @nogc @property
     {
@@ -1046,6 +1339,129 @@ public:
         return ymd;
     }
 
+    pragma(inline, false)
+    YearMonth yearMonthImpl() const @safe pure nothrow @nogc @property
+    {
+        YearMonth ym;
+        int days = dayOfGregorianCal;
+        with(ym)
+        if (days > 0)
+        {
+            int years = (days / daysIn400Years) * 400 + 1;
+            days %= daysIn400Years;
+
+            {
+                immutable tempYears = days / daysIn100Years;
+
+                if (tempYears == 4)
+                {
+                    years += 300;
+                    days -= daysIn100Years * 3;
+                }
+                else
+                {
+                    years += tempYears * 100;
+                    days %= daysIn100Years;
+                }
+            }
+
+            years += (days / daysIn4Years) * 4;
+            days %= daysIn4Years;
+
+            {
+                immutable tempYears = days / daysInYear;
+
+                if (tempYears == 4)
+                {
+                    years += 3;
+                    days -= daysInYear * 3;
+                }
+                else
+                {
+                    years += tempYears;
+                    days %= daysInYear;
+                }
+            }
+
+            if (days == 0)
+            {
+                year = cast(short)(years - 1);
+                month = Month.dec;
+            }
+            else
+            {
+                year = cast(short) years;
+                setMonthOfYear(days);
+            }
+        }
+        else if (days <= 0 && -days < daysInLeapYear)
+        {
+            year = 0;
+
+            setMonthOfYear(daysInLeapYear + days);
+        }
+        else
+        {
+            days += daysInLeapYear - 1;
+            int years = (days / daysIn400Years) * 400 - 1;
+            days %= daysIn400Years;
+
+            {
+                immutable tempYears = days / daysIn100Years;
+
+                if (tempYears == -4)
+                {
+                    years -= 300;
+                    days += daysIn100Years * 3;
+                }
+                else
+                {
+                    years += tempYears * 100;
+                    days %= daysIn100Years;
+                }
+            }
+
+            years += (days / daysIn4Years) * 4;
+            days %= daysIn4Years;
+
+            {
+                immutable tempYears = days / daysInYear;
+
+                if (tempYears == -4)
+                {
+                    years -= 3;
+                    days += daysInYear * 3;
+                }
+                else
+                {
+                    years += tempYears;
+                    days %= daysInYear;
+                }
+            }
+
+            if (days == 0)
+            {
+                year = cast(short)(years + 1);
+                month = Month.jan;
+            }
+            else
+            {
+                year = cast(short) years;
+                immutable newDoY = (yearIsLeapYear(year) ? daysInLeapYear : daysInYear) + days + 1;
+
+                setMonthOfYear(newDoY);
+            }
+        }
+        return ym;
+    }
+
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = date(2020, Month.may, 31);
+        auto ym = d.yearMonthImpl;
+    }
+
     /++
      $(LREF Date) for the last day in the quarter that this $(LREF Date) is in.
     +/

From 27f787713d3b529730a1bb94328d5c21f53da1d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Christian=20K=C3=B6stlin?= <christian.koestlin@gmail.com>
Date: Mon, 11 Jul 2022 18:41:55 +0200
Subject: [PATCH 465/573] Fix `dub build --build=docs` or `dub build
 --build=ddox` (#428)

This is a mixed bag commit:
- phobos_kernel.d: ddoc syntax
- mir/math/numeric.d: just removed DivisionType as its also not documented in
  factorial
- mir/numeric.d: just added empty docs for lowerBound, upperBound,
  steps. Some of those are not used at all in the function.
---
 source/mir/bignum/internal/phobos_kernel.d | 14 +++++++-------
 source/mir/math/numeric.d                  |  2 --
 source/mir/numeric.d                       |  3 +++
 3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/source/mir/bignum/internal/phobos_kernel.d b/source/mir/bignum/internal/phobos_kernel.d
index 5f5caebc..3457fc4f 100644
--- a/source/mir/bignum/internal/phobos_kernel.d
+++ b/source/mir/bignum/internal/phobos_kernel.d
@@ -751,10 +751,10 @@ size_t biguintToOctal(char[] buff, const(BigDigit)[] data)
 /** Convert a big uint into a decimal string.
  *
  * Params:
- *  data    The biguint to be converted. Will be destroyed.
- *  buff    The destination buffer for the decimal string. Must be
- *          large enough to store the result, including leading zeros.
- *          Will be filled backwards, starting from buff[$-1].
+ *  buff = The destination buffer for the decimal string. Must be
+ *      large enough to store the result, including leading zeros.
+ *      Will be filled backwards, starting from buff[$-1].
+ *  data = The biguint to be converted. Will be destroyed.
  *
  * buff.length must be >= (data.length*32)/log2(10) = 9.63296 * data.length.
  * Returns:
@@ -787,9 +787,9 @@ size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow @safe
 /** Convert a decimal string into a big uint.
  *
  * Params:
- *  data    The biguint to be receive the result. Must be large enough to
- *          store the result.
- *  s       The decimal string. May contain _ or 0 .. 9
+ *  data = The biguint to be receive the result. Must be large enough to
+ *      store the result.
+ *  s = The decimal string. May contain _ or 0 .. 9
  *
  * The required length for the destination buffer is slightly less than
  *  1 + s.length/log2(10) = 1 + s.length/3.3219.
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index ff378d2f..a784a7c3 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -606,8 +606,6 @@ Quickly computes binomial coefficient using extended
 precision floating point type $(MREF mir,bignum,fp).
 
 Params:
-    DivisionType = the type used for a single division at the final.
-        By defualt, uses `real` precision. TODO: full `Fp` precision.
     n = number elements in the set 
     k = number elements in the subset 
 Returns: n choose k
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 9cf0bb9e..22cf8fac 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1561,7 +1561,10 @@ Params:
     fbx = Value of `f(bx)` (optional).
     relTolerance = Relative tolerance used by $(LREF findLocalMin).
     absTolerance = Absolute tolerance used by $(LREF findLocalMin).
+    lowerBound = 
+    upperBound =
     maxIterations = Appr. maximum allowed number of function calls for each $(LREF findRoot) call.
+    steps = 
 
 Returns: $(LREF FindSmileRootsResult)
 +/

From 84176195cd2efcfc4b3938c6c161c40e4a9ca861 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 11 Jul 2022 20:48:58 +0400
Subject: [PATCH 466/573] fixup sumSlices for 2D slices

---
 source/mir/math/sum.d | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index daae990a..8b72cf95 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1903,7 +1903,7 @@ template sumSlices()
         alias T = Unqual!(DeepElementType!(DeepElementType!(Slice!(Iterator, 1, kind))));
         import mir.ndslice: slice;
         if (sliceOfSlices.length == 0)
-            return Slice!(T*).init;
+            return typeof(slice(as!T(sliceOfSlices.front))).init;
         auto ret = slice(as!T(sliceOfSlices.front));
         sliceOfSlices.popFront;
         foreach (sl; sliceOfSlices)
@@ -1925,11 +1925,16 @@ template sumSlices()
 version(mir_test)
 unittest
 {
-    import mir.ndslice.topology: map;
+    import mir.ndslice.topology: map, byDim;
     import mir.ndslice.slice: sliced;
 
     auto ar = [[1, 2, 3], [10, 20, 30]];
     assert(ar.map!sliced.sumSlices == [11, 22, 33]);
+
+    import mir.ndslice.fuse: fuse;
+    auto a = [[[1.2], [2.1]], [[4.1], [5.2]]].fuse;
+    auto s = a.byDim!0.sumSlices;
+    assert(s == [[5.3], [7.300000000000001]]);
 }
 
 version(mir_test)

From 35b3287da4386af7c0b0c8938ebecf903dfb71f2 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 12 Jul 2022 02:24:43 -0400
Subject: [PATCH 467/573] Add YearQuarter (#429)

---
 source/mir/date.d | 658 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 657 insertions(+), 1 deletion(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index b49378c0..2a2cd151 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -251,6 +251,12 @@ struct YearMonthDay
     Month month = Month.jan;
     ubyte day   = 1;
 
+    ///
+    Quarter quarter() @safe pure nothrow @nogc @property
+    {
+        return month.quarter;
+    }
+
     ///
     Timestamp timestamp() @safe pure nothrow @nogc @property
     {
@@ -268,6 +274,15 @@ struct YearMonthDay
 
     ///
     alias opCast(T : YearMonth) = yearMonth;
+    
+    ///
+    YearQuarter yearQuarter() @safe pure nothrow @nogc @property
+    {
+        return YearQuarter(year, this.quarter);
+    }
+
+    ///
+    alias opCast(T : YearQuarter) = yearQuarter;
 
     ///
     version(mir_test)
@@ -297,6 +312,12 @@ struct YearMonthDay
         with(yearMonth) this(year, month, day(assumePeriod));
     }
 
+    ///
+    this(YearQuarter yearQuarter, AssumePeriod assumePeriodMonth = AssumePeriod.begin, AssumePeriod assumePeriodDay = AssumePeriod.begin) @safe pure nothrow @nogc
+    {
+        with(yearQuarter) this(year, month(assumePeriodMonth), day(assumePeriodDay));
+    }
+
     version(D_Exceptions)
     ///
     this(Timestamp timestamp) @safe pure nothrow @nogc
@@ -351,6 +372,50 @@ struct YearMonthDay
         return this;
     }
 
+
+    // Shares documentation with "years" version.
+    @safe pure nothrow @nogc
+    ref YearMonthDay add(string units)(long quarters)
+        if (units == "quarters")
+    {
+        auto years = quarters / 4;
+        quarters %= 4;
+        auto newQuarter = this.quarter + quarters;
+
+        if (quarters < 0)
+        {
+            if (newQuarter < 1)
+            {
+                newQuarter += 4;
+                --years;
+            }
+        }
+        else if (newQuarter > 4)
+        {
+            newQuarter -= 4;
+            ++years;
+        }
+
+        year += years;
+        month = cast(Month) (cast(ubyte) month + newQuarter * 3);
+
+        immutable currMaxDay = maxDay(year, month);
+        immutable overflow = day - currMaxDay;
+
+        if (overflow > 0)
+        {
+            if (allowOverflow == AllowDayOverflow.yes)
+            {
+                ++month;
+                day = cast(ubyte) overflow;
+            }
+            else
+                day = cast(ubyte) currMaxDay;
+        }
+
+        return this;
+    }
+
     // Shares documentation with "years" version.
     @safe pure nothrow @nogc
     ref YearMonthDay add(string units)(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
@@ -452,7 +517,9 @@ struct YearMonthDay
 
 ///
 enum AssumePeriod {
+    ///
     begin,
+    ///
     end
 }
 
@@ -474,6 +541,12 @@ struct YearMonth
         }
     }
 
+    ///
+    Quarter quarter() @safe pure nothrow @nogc @property
+    {
+        return month.quarter;
+    }
+
     ///
     Timestamp timestamp(bool includeDay = false) @safe pure nothrow @nogc @property
     {
@@ -512,6 +585,21 @@ struct YearMonth
         auto ym = YearMonth(2000, Month.dec);
     }
 
+    ///
+    this(short year, Quarter quarter, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure nothrow @nogc
+    {
+        this.year = year;
+        this.month = monthInQuarter(quarter, assumePeriod);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym1 = YearMonth(2000, Quarter.q1);
+        auto ym2 = YearMonth(2000, Quarter.q1, AssumePeriod.end);
+    }
+
     ///
     this(Date date) @safe pure nothrow @nogc
     {
@@ -538,6 +626,20 @@ struct YearMonth
         auto ym = YearMonth(YearMonthDay(2000, Month.dec, 31));
     }
 
+    ///
+    this(YearQuarter yearQuarter, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure nothrow @nogc
+    {
+        with(yearQuarter) this(year, month(assumePeriod));
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym1 = YearMonth(YearQuarter(2000, Quarter.q1));
+        auto ym2 = YearMonth(YearQuarter(2000, Quarter.q1), AssumePeriod.end);
+    }
+
     version(D_Exceptions)
     ///
     this(Timestamp timestamp) @safe pure nothrow @nogc
@@ -579,6 +681,35 @@ struct YearMonth
         return this;
     }
 
+    // Shares documentation with "years" version.
+    @safe pure nothrow @nogc
+    ref YearMonth add(string units)(long quarters)
+        if (units == "quarters")
+    {
+        auto years = quarters / 4;
+        quarters %= 4;
+        auto newQuarter = this.quarter + quarters;
+
+        if (quarters < 0)
+        {
+            if (newQuarter < 1)
+            {
+                newQuarter += 4;
+                --years;
+            }
+        }
+        else if (newQuarter > 4)
+        {
+            newQuarter -= 4;
+            ++years;
+        }
+
+        year += years;
+        month = cast(Month) (cast(ubyte) month + newQuarter * 3);
+
+        return this;
+    }
+
     // Shares documentation with "years" version.
     @safe pure nothrow @nogc
     ref YearMonth add(string units)(long years)
@@ -661,10 +792,343 @@ version (mir_test)
     assert(YearMonth(1999, cast(Month) 1).day(AssumePeriod.begin) == 1);
     assert(YearMonth(1999, cast(Month) 12).day(AssumePeriod.end) == 31);
     assert(YearMonth(1999, cast(Month) 1).dayOfYear(AssumePeriod.begin) == 1);
+    assert(YearMonth(1999, cast(Month) 12).dayOfYear(AssumePeriod.begin) == 335);
     assert(YearMonth(1999, cast(Month) 12).dayOfYear(AssumePeriod.end) == 365);
+    assert(YearMonth(2000, cast(Month) 12).dayOfYear(AssumePeriod.begin) == 336);
     assert(YearMonth(2000, cast(Month) 12).dayOfYear(AssumePeriod.end) == 366);
 }
 
+///
+enum Quarter : ubyte
+{
+    ///
+    q1 = 1,
+    ///
+    q2,
+    ///
+    q3,
+    ///
+    q4,
+}
+
+///
+@safe pure @nogc nothrow
+Quarter quarter(Month month)
+{
+    return cast(Quarter)((cast(ubyte)month - 1) / 3 + 1);
+}
+
+///
+version(mir_test)
+@safe pure @nogc nothrow
+unittest {
+    assert(Month.jan.quarter == Quarter.q1);
+    assert(Month.feb.quarter == Quarter.q1);
+    assert(Month.mar.quarter == Quarter.q1);
+    assert(Month.apr.quarter == Quarter.q2);
+    assert(Month.may.quarter == Quarter.q2);
+    assert(Month.jun.quarter == Quarter.q2);
+    assert(Month.jul.quarter == Quarter.q3);
+    assert(Month.aug.quarter == Quarter.q3);
+    assert(Month.sep.quarter == Quarter.q3);
+    assert(Month.oct.quarter == Quarter.q4);
+    assert(Month.nov.quarter == Quarter.q4);
+    assert(Month.dec.quarter == Quarter.q4);
+}
+
+private
+@safe pure @nogc nothrow
+Month monthInQuarter(Quarter quarter, AssumePeriod assumePeriod = AssumePeriod.begin)
+{
+    assert (assumePeriod == AssumePeriod.begin || assumePeriod == AssumePeriod.end);
+    return cast(Month) ((cast(byte)quarter - 1) * 3 + 1 + 2 * assumePeriod);
+}
+
+version(mir_test)
+@safe pure @nogc nothrow
+unittest {
+    assert(Quarter.q1.monthInQuarter == Month.jan);
+    assert(Quarter.q1.monthInQuarter(AssumePeriod.end) == Month.mar);
+    assert(Quarter.q2.monthInQuarter == Month.apr);
+    assert(Quarter.q2.monthInQuarter(AssumePeriod.end) == Month.jun);
+    assert(Quarter.q3.monthInQuarter == Month.jul);
+    assert(Quarter.q3.monthInQuarter(AssumePeriod.end) == Month.sep);
+    assert(Quarter.q4.monthInQuarter == Month.oct);
+    assert(Quarter.q4.monthInQuarter(AssumePeriod.end) == Month.dec);
+}
+
+///
+@serdeProxy!Timestamp
+struct YearQuarter
+{
+    short year  = 1;
+    Quarter quarter = Quarter.q1;
+    
+    @property Month month(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
+    {
+        return quarter.monthInQuarter(assumePeriod);
+    }
+
+    @property ubyte day(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
+    {
+        final switch (assumePeriod)
+        {
+            case AssumePeriod.begin:
+                return 1;
+            case AssumePeriod.end:
+                return daysInMonth;
+        }
+    }
+
+    ///
+    Timestamp timestamp(bool includeDay = false) @safe pure nothrow @nogc @property
+    {
+        if (!includeDay)
+            return Timestamp(year, cast(ubyte)month);
+        else
+            return Timestamp(year, cast(ubyte)month, this.day);  
+    }
+
+    ///
+    alias opCast(T : Timestamp) = timestamp;
+
+    ///
+    version(mir_test)
+    unittest
+    {
+        import mir.timestamp;
+        auto yq0 = YearQuarter(2020, Quarter.q2);
+        auto timestamp1 = cast(Timestamp) yq0;
+        auto timestamp2 = yq0.timestamp(true);
+        auto yq1 = YearQuarter(timestamp1);
+        auto yq2 = YearQuarter(timestamp2);
+    }
+
+    ///
+    this(short year, Quarter quarter) @safe pure nothrow @nogc
+    {
+        this.year = year;
+        this.quarter = quarter;
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(2000, Quarter.q4);
+    }
+
+    ///
+    this(short year, Month month) @safe pure nothrow @nogc
+    {
+        this.year = year;
+        this.quarter = month.quarter;
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(2000, Month.dec);
+    }
+
+    ///
+    this(Date date) @safe pure nothrow @nogc
+    {
+        this = date.yearQuarter;
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(Date(2000, Month.dec, 31));
+    }
+
+    ///
+    this(YearMonthDay yearMonthDay) @safe pure nothrow @nogc
+    {
+        with(yearMonthDay) this(year, quarter);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearQuarter(YearMonthDay(2000, Month.dec, 31));
+    }
+
+    ///
+    this(YearMonth yearMonth) @safe pure nothrow @nogc
+    {
+        with(yearMonth) this(year, quarter);
+    }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(YearMonth(2000, Month.dec));
+    }
+
+    version(D_Exceptions)
+    ///
+    this(Timestamp timestamp) @safe pure nothrow @nogc
+    {
+        if (!(timestamp.precision == Timestamp.Precision.day) ||
+             (timestamp.precision == Timestamp.Precision.month))
+        {
+            static immutable exc = new Exception("YearMonth: invalid timestamp precision");
+        }
+        with(timestamp) this(year, cast(Month)month);
+    }
+
+    // Shares documentation with "years" version.
+    @safe pure nothrow @nogc
+    ref YearQuarter add(string units)(long quarters)
+        if (units == "quarters")
+    {
+        auto years = quarters / 4;
+        quarters %= 4;
+        auto newQuarter = quarter + quarters;
+
+        if (quarters < 0)
+        {
+            if (newQuarter < 1)
+            {
+                newQuarter += 4;
+                --years;
+            }
+        }
+        else if (newQuarter > 4)
+        {
+            newQuarter -= 4;
+            ++years;
+        }
+
+        year += years;
+        quarter = cast(Quarter) newQuarter;
+
+        return this;
+    }
+
+    // Shares documentation with "years" version.
+    @safe pure nothrow @nogc
+    ref YearQuarter add(string units)(long years)
+        if (units == "years")
+    {
+        year += years;
+        return this;
+    }
+
+    private void setQuarterOfYear(bool useExceptions = false)(int days)
+    {
+        immutable int[] lastDay = isLeapYear ? lastDayQuarterLeap : lastDayQuarterNonLeap;
+
+        bool dayOutOfRange = days <= 0 || days > (isLeapYear ? daysInLeapYear : daysInYear);
+
+        static if (useExceptions)
+        {
+            if (dayOutOfRange) throw InvalidDay;
+        }
+        else
+        {
+            assert(!dayOutOfRange, "Invalid Day");
+        }
+
+        foreach (i; 1 .. lastDay.length)
+        {
+            if (days <= lastDay[i])
+            {
+                quarter = cast(Quarter)(cast(int) Quarter.q1 + i - 1);
+                return;
+            }
+        }
+        assert(0, "Invalid day of the year.");
+    }
+
+    /++
+        Day of the quarter this $(LREF Date) is on.
+      +/
+    @property int dayOfQuarter(AssumePeriod assumePeriodMonth, AssumePeriod assumePeriodDay) const @safe pure nothrow @nogc
+    {
+        if (quarter >= Quarter.q1 && quarter <= Quarter.q4)
+        {
+            immutable int[] lastDayQuarter = isLeapYear ? lastDayQuarterLeap : lastDayQuarterNonLeap;
+            auto quarterIndex = quarter - Quarter.q1;
+            immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
+            auto monthIndex = month(assumePeriodMonth) - Month.jan;
+
+            return lastDay[monthIndex] - lastDayQuarter[quarterIndex] + day(assumePeriodDay);
+        }
+        assert(0, "Invalid quarter.");
+    }
+
+    /// ditto
+    @property int dayOfQuarter(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
+    {
+        return dayOfQuarter(assumePeriod, assumePeriod);
+    }
+
+    /++
+        Day of the year this $(LREF Date) is on.
+      +/
+    @property int dayOfYear(AssumePeriod assumePeriodMonth, AssumePeriod assumePeriodDay) const @safe pure nothrow @nogc
+    {
+        if (quarter >= Quarter.q1 && quarter <= Quarter.q4)
+        {
+            immutable int[] lastDayQuarter = isLeapYear ? lastDayQuarterLeap : lastDayQuarterNonLeap;
+            auto quarterIndex = quarter - Quarter.q1;
+
+            return lastDayQuarter[quarterIndex] + dayOfQuarter(assumePeriodMonth, assumePeriodDay);
+        }
+        assert(0, "Invalid quarter.");
+    }
+    
+    /// ditto
+    @property int dayOfYear(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
+    {
+        return dayOfYear(assumePeriod, assumePeriod);
+    }
+
+    /++
+        Whether this $(LREF Date) is in a leap year.
+     +/
+    @property bool isLeapYear() const @safe pure nothrow @nogc
+    {
+        return yearIsLeapYear(year);
+    }
+
+    /++
+        The last day in the month that this $(LREF Date) is in.
+      +/
+    @property ubyte daysInMonth() const @safe pure nothrow @nogc
+    {
+        return maxDay(year, month);
+    }
+
+    /++
+        Whether the current year is a date in A.D.
+      +/
+    @property bool isAD() const @safe pure nothrow @nogc
+    {
+        return year > 0;
+    }
+}
+
+///
+version (mir_test)
+@safe unittest
+{
+    assert(YearQuarter(1999, cast(Quarter) 1).day(AssumePeriod.begin) == 1);
+    assert(YearQuarter(1999, cast(Quarter) 4).day(AssumePeriod.end) == 31);
+    assert(YearQuarter(1999, cast(Quarter) 1).dayOfYear == 1);
+    assert(YearQuarter(1999, cast(Quarter) 4).dayOfYear == 274);
+    assert(YearQuarter(1999, cast(Quarter) 4).dayOfYear(AssumePeriod.end) == 365);
+    assert(YearQuarter(2000, cast(Quarter) 4).dayOfYear == 275);
+    assert(YearQuarter(2000, cast(Quarter) 4).dayOfYear(AssumePeriod.end) == 366);
+}
+
 /++
     Represents a date in the
     $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
@@ -817,6 +1281,28 @@ public:
         auto d = date(YearMonthDay(2020, Month.may, 31));
     }
 
+    version(D_Exceptions)
+    ///
+    this(YearQuarter yq, AssumePeriod assumePeriodMonth, AssumePeriod assumePeriodDay) @safe pure @nogc
+    {
+        with(yq) this(year, month(assumePeriodMonth), day(assumePeriodDay));
+    }
+
+    version(D_Exceptions)
+    ///
+    this(YearQuarter yq, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure @nogc
+    {
+        this(yq, assumePeriod, assumePeriod);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d1 = date(YearQuarter(2020, Quarter.q2));
+        auto d2 = date(YearQuarter(2020, Quarter.q2), AssumePeriod.end);
+    }
+
     version(D_Exceptions)
     ///
     this(YearMonth ym, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure @nogc
@@ -1192,10 +1678,47 @@ public:
         assert(ym.month == Month.dec);
     }
 
+    ///
+    YearQuarter yearQuarter() const @safe pure nothrow @nogc @property
+    {
+        uint day = _julianDay;
+        if (day < _endDict)
+        {
+            return yearMonthDay().YearQuarter;
+        }
+        return yearQuarterImpl;
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date(2020, Month.may, 31);
+        auto yq = d.yearQuarter;
+        assert(yq.year == 2020);
+        assert(yq.quarter == Quarter.q2);
+    }
+
+    //
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date(2050, Month.dec, 31);
+        auto yq = d.yearQuarter;
+        assert(yq.year == 2050);
+        assert(yq.quarter == Quarter.q4);
+    }
+
     ///
     short year() const @safe pure nothrow @nogc @property
     {
-        return yearMonth.year;
+        return yearQuarter.year;
+    }
+
+    ///
+    Quarter quarter() const @safe pure nothrow @nogc @property
+    {
+        return yearQuarter.quarter;
     }
 
     ///
@@ -1216,6 +1739,7 @@ public:
     {
         auto d = Date(2020, Month.may, 31);
         assert(d.year == 2020);
+        assert(d.quarter == Quarter.q2);
         assert(d.month == Month.may);
         assert(d.day == 31);
     }
@@ -1462,6 +1986,129 @@ public:
         auto ym = d.yearMonthImpl;
     }
 
+    pragma(inline, false)
+    YearQuarter yearQuarterImpl() const @safe pure nothrow @nogc @property
+    {
+        YearQuarter yq;
+        int days = dayOfGregorianCal;
+        with(yq)
+        if (days > 0)
+        {
+            int years = (days / daysIn400Years) * 400 + 1;
+            days %= daysIn400Years;
+
+            {
+                immutable tempYears = days / daysIn100Years;
+
+                if (tempYears == 4)
+                {
+                    years += 300;
+                    days -= daysIn100Years * 3;
+                }
+                else
+                {
+                    years += tempYears * 100;
+                    days %= daysIn100Years;
+                }
+            }
+
+            years += (days / daysIn4Years) * 4;
+            days %= daysIn4Years;
+
+            {
+                immutable tempYears = days / daysInYear;
+
+                if (tempYears == 4)
+                {
+                    years += 3;
+                    days -= daysInYear * 3;
+                }
+                else
+                {
+                    years += tempYears;
+                    days %= daysInYear;
+                }
+            }
+
+            if (days == 0)
+            {
+                year = cast(short)(years - 1);
+                quarter = Quarter.q4;
+            }
+            else
+            {
+                year = cast(short) years;
+                setQuarterOfYear(days);
+            }
+        }
+        else if (days <= 0 && -days < daysInLeapYear)
+        {
+            year = 0;
+
+            setQuarterOfYear(daysInLeapYear + days);
+        }
+        else
+        {
+            days += daysInLeapYear - 1;
+            int years = (days / daysIn400Years) * 400 - 1;
+            days %= daysIn400Years;
+
+            {
+                immutable tempYears = days / daysIn100Years;
+
+                if (tempYears == -4)
+                {
+                    years -= 300;
+                    days += daysIn100Years * 3;
+                }
+                else
+                {
+                    years += tempYears * 100;
+                    days %= daysIn100Years;
+                }
+            }
+
+            years += (days / daysIn4Years) * 4;
+            days %= daysIn4Years;
+
+            {
+                immutable tempYears = days / daysInYear;
+
+                if (tempYears == -4)
+                {
+                    years -= 3;
+                    days += daysInYear * 3;
+                }
+                else
+                {
+                    years += tempYears;
+                    days %= daysInYear;
+                }
+            }
+
+            if (days == 0)
+            {
+                year = cast(short)(years + 1);
+                quarter = Quarter.q2;
+            }
+            else
+            {
+                year = cast(short) years;
+                immutable newDoY = (yearIsLeapYear(year) ? daysInLeapYear : daysInYear) + days + 1;
+
+                setQuarterOfYear(newDoY);
+            }
+        }
+        return yq;
+    }
+
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = date(2020, Month.may, 31);
+        auto yq = d.yearQuarterImpl;
+    }
+
     /++
      $(LREF Date) for the last day in the quarter that this $(LREF Date) is in.
     +/
@@ -2818,6 +3465,15 @@ immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273,
   +/
 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
 
+/+
+    Array of integers representing the last days of each quarter in a year.
+  +/
+immutable int[5] lastDayQuarterNonLeap = [0, 90, 181, 273, 365];
+
+/+
+    Array of integers representing the last days of each quarter in a leap year.
+  +/
+immutable int[5] lastDayQuarterLeap = [0, 91, 182, 274, 366];
 
 /+
     Returns the string representation of the given month.

From bafd3bc887ca5ac142f887d0d0ab92fc5f907420 Mon Sep 17 00:00:00 2001
From: Iain Buclaw <ibuclaw@gdcproject.org>
Date: Tue, 12 Jul 2022 12:43:55 +0200
Subject: [PATCH 468/573] Adjust test for pow() computed at double precision

---
 source/mir/ediff.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/ediff.d b/source/mir/ediff.d
index 83226b87..06f6d633 100644
--- a/source/mir/ediff.d
+++ b/source/mir/ediff.d
@@ -139,7 +139,7 @@ unittest
     auto f1 = exp(3.Const * spot.powi!(-2));
     static assert(Dependencies!(typeof(f1)) == ["spot"].DependsOn);
     assert(f1.getFunctionValue == mir.math.exp(3 * 7.0 ^^ -2));
-    assert(f1.getDerivative!(["spot"]) == 3 * -2 * 7.0 ^^ -3 * mir.math.exp(3 * 7.0 ^^ -2));
+    assert(f1.getDerivative!(["spot"]).approxEqual(3 * -2 * 7.0 ^^ -3 * mir.math.exp(3 * 7.0 ^^ -2)));
     // Test DerivativeOf
     assert(f1.derivativeOf!"spot".getFunctionValue == f1.getDerivative!(["spot"]));
     // Test product and sum

From f70cf9e6cb50f1ca22a529c2f2767ad4903ccf04 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 12 Jul 2022 20:47:03 +0400
Subject: [PATCH 469/573] extend YearMonth (#431)

---
 source/mir/date.d | 284 +++++++++++++++++++++++++++++-----------------
 1 file changed, 180 insertions(+), 104 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index 2a2cd151..2abaafe6 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -258,19 +258,16 @@ struct YearMonthDay
     }
 
     ///
-    Timestamp timestamp() @safe pure nothrow @nogc @property
+    Timestamp timestamp() @safe pure nothrow @nogc const @property
     {
         return Timestamp(year, cast(ubyte)month, day);
     }
 
     ///
     alias opCast(T : Timestamp) = timestamp;
-    
-    ///
-    YearMonth yearMonth() @safe pure nothrow @nogc @property
-    {
-        return YearMonth(year, cast(ubyte)month);
-    }
+
+@safe pure @nogc:
+
 
     ///
     alias opCast(T : YearMonth) = yearMonth;
@@ -320,19 +317,19 @@ struct YearMonthDay
 
     version(D_Exceptions)
     ///
-    this(Timestamp timestamp) @safe pure nothrow @nogc
+    this(Timestamp timestamp) @safe pure @nogc
     {
         if (timestamp.precision != Timestamp.Precision.day)
         {
             static immutable exc = new Exception("YearMonthDay: invalid timestamp precision");
+            throw exc;
         }
         with(timestamp) this(year, cast(Month)month, day);
     }
 
-    // Shares documentation with "years" version.
+    ///
     @safe pure nothrow @nogc
-    ref YearMonthDay add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
-        if (units == "months")
+    ref YearMonthDay add(string units : "months")(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
     {
         auto years = months / 12;
         months %= 12;
@@ -372,54 +369,16 @@ struct YearMonthDay
         return this;
     }
 
-
-    // Shares documentation with "years" version.
+    ///
     @safe pure nothrow @nogc
-    ref YearMonthDay add(string units)(long quarters)
-        if (units == "quarters")
+    ref YearMonthDay add(string units : "quarters")(long quarters)
     {
-        auto years = quarters / 4;
-        quarters %= 4;
-        auto newQuarter = this.quarter + quarters;
-
-        if (quarters < 0)
-        {
-            if (newQuarter < 1)
-            {
-                newQuarter += 4;
-                --years;
-            }
-        }
-        else if (newQuarter > 4)
-        {
-            newQuarter -= 4;
-            ++years;
-        }
-
-        year += years;
-        month = cast(Month) (cast(ubyte) month + newQuarter * 3);
-
-        immutable currMaxDay = maxDay(year, month);
-        immutable overflow = day - currMaxDay;
-
-        if (overflow > 0)
-        {
-            if (allowOverflow == AllowDayOverflow.yes)
-            {
-                ++month;
-                day = cast(ubyte) overflow;
-            }
-            else
-                day = cast(ubyte) currMaxDay;
-        }
-
-        return this;
+        return add!"months"(quarters * 4);
     }
 
     // Shares documentation with "years" version.
     @safe pure nothrow @nogc
-    ref YearMonthDay add(string units)(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
-        if (units == "years")
+    ref YearMonthDay add(string units : "years")(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
     {
         year += years;
 
@@ -529,7 +488,40 @@ struct YearMonth
 {
     short year  = 1;
     Month month = Month.jan;
-    
+
+    version(D_BetterC){} else
+    {
+        private string toStringImpl(alias fun)() const @safe pure nothrow
+        {
+            import mir.small_string : SmallString;
+            SmallString!16 w;
+            try
+                fun(w);
+            catch (Exception e)
+                assert(0, __traits(identifier, fun) ~ " threw.");
+            return w[].idup;
+        }
+
+        string toISOExtString() const @safe pure nothrow
+        {
+            return toStringImpl!toISOExtString;
+        }
+
+        alias toString = toISOExtString;
+    }
+
+    ///
+    void toISOExtString(W)(scope ref W w) const scope
+        if (isOutputRange!(W, char))
+    {
+        import mir.format: printZeroPad;
+        if (year >= 10_000)
+            w.put('+');
+        w.printZeroPad(year, year >= 0 ? year < 10_000 ? 4 : 5 : year > -10_000 ? 5 : 6);
+        w.put('-');
+        w.printZeroPad(cast(uint)month, 2);
+    }
+
     @property ubyte day(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
     {
         final switch (assumePeriod)
@@ -548,12 +540,9 @@ struct YearMonth
     }
 
     ///
-    Timestamp timestamp(bool includeDay = false) @safe pure nothrow @nogc @property
+    Timestamp timestamp() @safe pure nothrow @nogc const @property
     {
-        if (!includeDay)
-            return Timestamp(year, cast(ubyte)month);
-        else
-            return Timestamp(year, cast(ubyte)month, this.day);  
+        return Timestamp(year, cast(ubyte)month);
     }
 
     ///
@@ -566,9 +555,7 @@ struct YearMonth
         import mir.timestamp;
         auto ym0 = YearMonth(2020, Month.may);
         auto timestamp1 = cast(Timestamp) ym0;
-        auto timestamp2 = ym0.timestamp(true);
         auto ym1 = YearMonth(timestamp1);
-        auto ym2 = YearMonth(timestamp2);
     }
 
     ///
@@ -586,18 +573,13 @@ struct YearMonth
     }
 
     ///
-    this(short year, Quarter quarter, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure nothrow @nogc
+    this(short year, ushort month) @safe pure @nogc
     {
+        static immutable exc = new Exception("Month out of bounds [1, 12]");
+        if (1 > month || month > 12)
+            throw exc;
         this.year = year;
-        this.month = monthInQuarter(quarter, assumePeriod);
-    }
-
-    ///
-    version (mir_test)
-    @safe unittest
-    {
-        auto ym1 = YearMonth(2000, Quarter.q1);
-        auto ym2 = YearMonth(2000, Quarter.q1, AssumePeriod.end);
+        this.month = cast(Month)month;
     }
 
     ///
@@ -642,20 +624,100 @@ struct YearMonth
 
     version(D_Exceptions)
     ///
-    this(Timestamp timestamp) @safe pure nothrow @nogc
+    this(Timestamp timestamp) @safe pure @nogc
     {
-        if (!(timestamp.precision == Timestamp.Precision.day) ||
-             (timestamp.precision == Timestamp.Precision.month))
+        if (timestamp.precision != Timestamp.Precision.month)
         {
             static immutable exc = new Exception("YearMonth: invalid timestamp precision");
+            throw exc;
         }
         with(timestamp) this(year, cast(Month)month);
     }
 
-    // Shares documentation with "years" version.
+    Date nthWeekday(int n, DayOfWeek dow) const @safe pure nothrow @nogc
+    {
+        auto d = trustedWithDayOfMonth(1);
+        auto dc = d.dayOfWeek.daysToDayOfWeek(dow) + (n - 1) * 7;
+        d = d + dc;
+        return d;
+    }
+
+    ///
+    Date trustedWithDayOfMonth(int days) const @safe pure nothrow @nogc
+    {
+        assert(days <= lengthOfMonth);
+        return Date.trustedCreate(year, month, days);
+    }
+
+    ///
+    int opCmp(YearMonth rhs) const pure nothrow @nogc @safe
+    {
+        if (auto d = this.year - rhs.year)
+            return d;
+        return this.month - rhs.month;
+    }
+
+    ///
+    size_t toHash() const pure nothrow @nogc @safe
+    {
+        return year * 16 + month;
+    }
+
+    ///
+    Date endOfMonth() const nothrow @property @nogc @safe pure
+    {
+        return Date.trustedCreate(year, month, lengthOfMonth);
+    }
+
+    ///
+    ushort lengthOfMonth() const pure nothrow @property @nogc @safe
+    {
+        return maxDay(year, month);
+    }
+    ///
+    this(scope const(char)[] str) @safe pure @nogc
+    {
+        this = fromISOExtString(str);
+    }
+
+    static bool fromISOExtString(C)(scope const(C)[] str, out YearMonth value) @safe pure @nogc
+        if (isSomeChar!C)
+    {
+        import mir.parse: fromString;
+        if (str.length < 7 || str[$-3] != '-')
+            return false;
+
+        auto yearStr = str[0 .. $ - 3];
+
+        if ((yearStr[0] == '+' || yearStr[0] == '-') != (yearStr.length > 4))
+            return false;
+
+        short year;
+        ushort month;
+
+        const ret =
+         fromString(str[$ - 2 .. $], month)
+         && fromString(yearStr, year);
+
+        value = YearMonth(year, month);
+        return ret;
+    }
+
+    static YearMonth fromISOExtString(C)(scope const(C)[] str) @safe pure
+        if (isSomeChar!C)
+    {
+        YearMonth ret;
+        if (fromISOExtString(str, ret))
+            return ret;
+        static immutable exc = new Exception("Invalid YearMonth string");
+        throw exc;
+    }
+
+nothrow:
+
+    ///
     @safe pure nothrow @nogc
-    ref YearMonth add(string units)(long months)
-        if (units == "months")
+    ref YearMonth add(string units : "months")(long months)
     {
         auto years = months / 12;
         months %= 12;
@@ -681,10 +743,9 @@ struct YearMonth
         return this;
     }
 
-    // Shares documentation with "years" version.
+    ///
     @safe pure nothrow @nogc
-    ref YearMonth add(string units)(long quarters)
-        if (units == "quarters")
+    ref YearMonth add(string units : "quarters")(long quarters)
     {
         auto years = quarters / 4;
         quarters %= 4;
@@ -710,10 +771,9 @@ struct YearMonth
         return this;
     }
 
-    // Shares documentation with "years" version.
+    ///
     @safe pure nothrow @nogc
-    ref YearMonth add(string units)(long years)
-        if (units == "years")
+    ref YearMonth add(string units : "years")(long years)
     {
         year += years;
         return this;
@@ -745,6 +805,27 @@ struct YearMonth
         assert(0, "Invalid day of the year.");
     }
 
+    ///
+    int opBinary(string op : "-")(YearMonth rhs) const
+    {
+        alias a = this;
+        alias b = rhs;
+        return (a.year - b.year) * 12 + a.month - b.month;
+    }
+
+    ///
+    YearMonth opBinary(string op)(int rhs) const
+        if (op == "+" || op == "-")
+    {
+        static if (op == "+")
+           return addMonths(rhs);
+        else
+           return addMonths(-rhs);
+    }
+
+    ///
+    alias opBinaryRight(string op : "+") = opBinary!"+";
+
     /++
         Day of the year this $(LREF Date) is on.
       +/
@@ -799,7 +880,7 @@ version (mir_test)
 }
 
 ///
-enum Quarter : ubyte
+enum Quarter : short
 {
     ///
     q1 = 1,
@@ -881,12 +962,9 @@ struct YearQuarter
     }
 
     ///
-    Timestamp timestamp(bool includeDay = false) @safe pure nothrow @nogc @property
+    Timestamp timestamp() @safe pure nothrow @nogc const @property
     {
-        if (!includeDay)
-            return Timestamp(year, cast(ubyte)month);
-        else
-            return Timestamp(year, cast(ubyte)month, this.day);  
+        return Timestamp(year, cast(ubyte)month);
     }
 
     ///
@@ -898,10 +976,8 @@ struct YearQuarter
     {
         import mir.timestamp;
         auto yq0 = YearQuarter(2020, Quarter.q2);
-        auto timestamp1 = cast(Timestamp) yq0;
-        auto timestamp2 = yq0.timestamp(true);
-        auto yq1 = YearQuarter(timestamp1);
-        auto yq2 = YearQuarter(timestamp2);
+        auto timestamp = cast(Timestamp) yq0;
+        auto yq = YearQuarter(timestamp);
     }
 
     ///
@@ -973,20 +1049,19 @@ struct YearQuarter
 
     version(D_Exceptions)
     ///
-    this(Timestamp timestamp) @safe pure nothrow @nogc
+    this(Timestamp timestamp) @safe pure @nogc
     {
-        if (!(timestamp.precision == Timestamp.Precision.day) ||
-             (timestamp.precision == Timestamp.Precision.month))
+        if (timestamp.precision != Timestamp.Precision.month)
         {
             static immutable exc = new Exception("YearMonth: invalid timestamp precision");
+            throw exc;
         }
         with(timestamp) this(year, cast(Month)month);
     }
 
-    // Shares documentation with "years" version.
+    ///
     @safe pure nothrow @nogc
-    ref YearQuarter add(string units)(long quarters)
-        if (units == "quarters")
+    ref YearQuarter add(string units : "quarters")(long quarters)
     {
         auto years = quarters / 4;
         quarters %= 4;
@@ -1012,10 +1087,9 @@ struct YearQuarter
         return this;
     }
 
-    // Shares documentation with "years" version.
+    ///
     @safe pure nothrow @nogc
-    ref YearQuarter add(string units)(long years)
-        if (units == "years")
+    ref YearQuarter add(string units : "years")(long years)
     {
         year += years;
         return this;
@@ -1257,7 +1331,9 @@ public:
         if (timestamp.precision != Timestamp.Precision.day)
         {
             static immutable exc = new Exception("Date: invalid timestamp precision");
+            throw exc;
         }
+        with(timestamp) this(year, cast(Month)month, day);
     }
 
     version(D_Exceptions)
@@ -1305,9 +1381,9 @@ public:
 
     version(D_Exceptions)
     ///
-    this(YearMonth ym, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure @nogc
+    this(YearMonth ym, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure @nogc nothrow
     {
-        with(ym) this(year, month, day(assumePeriod));
+        with(ym) this = trustedCreate(year, month, day(assumePeriod));
     }
 
     ///

From 72f3178d07a2172295c9aa59b6275e6a021369ec Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 12 Jul 2022 21:12:28 +0400
Subject: [PATCH 470/573] resolve conflicts

---
 source/mir/date.d | 200 ++++++++++------------------------------------
 1 file changed, 42 insertions(+), 158 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index 2abaafe6..d45b8fe6 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -268,10 +268,6 @@ struct YearMonthDay
 
 @safe pure @nogc:
 
-
-    ///
-    alias opCast(T : YearMonth) = yearMonth;
-    
     ///
     YearQuarter yearQuarter() @safe pure nothrow @nogc @property
     {
@@ -303,6 +299,26 @@ struct YearMonthDay
         this = date.yearMonthDay;
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = YearMonthDay(2020, Month.may, 31);
+        auto ym = d.YearMonth;
+        assert(ym.year == 2020);
+        assert(ym.month == Month.may);
+    }
+
+    //
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = YearMonthDay(2050, Month.dec, 31);
+        auto ym = d.YearMonth;
+        assert(ym.year == 2050);
+        assert(ym.month == Month.dec);
+    }
+
     ///
     this(YearMonth yearMonth, AssumePeriod assumePeriod = AssumePeriod.begin) @safe pure nothrow @nogc
     {
@@ -585,7 +601,7 @@ struct YearMonth
     ///
     this(Date date) @safe pure nothrow @nogc
     {
-        this = date.yearMonth;
+        this(date.YearMonthDay);
     }
 
     ///
@@ -595,6 +611,26 @@ struct YearMonth
         auto ym = YearMonth(Date(2000, Month.dec, 31));
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date(2020, Month.may, 31);
+        auto ym = d.YearMonth;
+        assert(ym.year == 2020);
+        assert(ym.month == Month.may);
+    }
+
+    //
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date(2050, Month.dec, 31);
+        auto ym = d.YearMonth;
+        assert(ym.year == 2050);
+        assert(ym.month == Month.dec);
+    }
+
     ///
     this(YearMonthDay yearMonthDay) @safe pure nothrow @nogc
     {
@@ -1723,37 +1759,6 @@ public:
         return yearMonthDayImpl;
     }
 
-    ///
-    YearMonth yearMonth() const @safe pure nothrow @nogc @property
-    {
-        uint day = _julianDay;
-        if (day < _endDict)
-        {
-            return yearMonthDay().YearMonth;
-        }
-        return yearMonthImpl;
-    }
-
-    ///
-    version(mir_test)
-    @safe unittest
-    {
-        auto d = Date(2020, Month.may, 31);
-        auto ym = d.yearMonth;
-        assert(ym.year == 2020);
-        assert(ym.month == Month.may);
-    }
-
-    //
-    version(mir_test)
-    @safe unittest
-    {
-        auto d = Date(2050, Month.dec, 31);
-        auto ym = d.yearMonth;
-        assert(ym.year == 2050);
-        assert(ym.month == Month.dec);
-    }
-
     ///
     YearQuarter yearQuarter() const @safe pure nothrow @nogc @property
     {
@@ -1800,7 +1805,7 @@ public:
     ///
     Month month() const @safe pure nothrow @nogc @property
     {
-        return yearMonth.month;
+        return yearMonthDay.month;
     }
 
     ///
@@ -1939,128 +1944,7 @@ public:
         return ymd;
     }
 
-    pragma(inline, false)
-    YearMonth yearMonthImpl() const @safe pure nothrow @nogc @property
-    {
-        YearMonth ym;
-        int days = dayOfGregorianCal;
-        with(ym)
-        if (days > 0)
-        {
-            int years = (days / daysIn400Years) * 400 + 1;
-            days %= daysIn400Years;
-
-            {
-                immutable tempYears = days / daysIn100Years;
-
-                if (tempYears == 4)
-                {
-                    years += 300;
-                    days -= daysIn100Years * 3;
-                }
-                else
-                {
-                    years += tempYears * 100;
-                    days %= daysIn100Years;
-                }
-            }
-
-            years += (days / daysIn4Years) * 4;
-            days %= daysIn4Years;
-
-            {
-                immutable tempYears = days / daysInYear;
-
-                if (tempYears == 4)
-                {
-                    years += 3;
-                    days -= daysInYear * 3;
-                }
-                else
-                {
-                    years += tempYears;
-                    days %= daysInYear;
-                }
-            }
-
-            if (days == 0)
-            {
-                year = cast(short)(years - 1);
-                month = Month.dec;
-            }
-            else
-            {
-                year = cast(short) years;
-                setMonthOfYear(days);
-            }
-        }
-        else if (days <= 0 && -days < daysInLeapYear)
-        {
-            year = 0;
-
-            setMonthOfYear(daysInLeapYear + days);
-        }
-        else
-        {
-            days += daysInLeapYear - 1;
-            int years = (days / daysIn400Years) * 400 - 1;
-            days %= daysIn400Years;
-
-            {
-                immutable tempYears = days / daysIn100Years;
-
-                if (tempYears == -4)
-                {
-                    years -= 300;
-                    days += daysIn100Years * 3;
-                }
-                else
-                {
-                    years += tempYears * 100;
-                    days %= daysIn100Years;
-                }
-            }
-
-            years += (days / daysIn4Years) * 4;
-            days %= daysIn4Years;
-
-            {
-                immutable tempYears = days / daysInYear;
-
-                if (tempYears == -4)
-                {
-                    years -= 3;
-                    days += daysInYear * 3;
-                }
-                else
-                {
-                    years += tempYears;
-                    days %= daysInYear;
-                }
-            }
-
-            if (days == 0)
-            {
-                year = cast(short)(years + 1);
-                month = Month.jan;
-            }
-            else
-            {
-                year = cast(short) years;
-                immutable newDoY = (yearIsLeapYear(year) ? daysInLeapYear : daysInYear) + days + 1;
-
-                setMonthOfYear(newDoY);
-            }
-        }
-        return ym;
-    }
 
-    version(mir_test)
-    @safe unittest
-    {
-        auto d = date(2020, Month.may, 31);
-        auto ym = d.yearMonthImpl;
-    }
 
     pragma(inline, false)
     YearQuarter yearQuarterImpl() const @safe pure nothrow @nogc @property

From 31caffd765c95b2069b3233b70097c9636749b63 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 14 Jul 2022 17:15:40 +0400
Subject: [PATCH 471/573] fix decimalToFloatImpl

---
 source/mir/bignum/internal/dec2float.d | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 7671c05a..d48ba6e5 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -123,7 +123,7 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
     enum wordBits = T.mant_dig < 64 ? 64 : 128;
     enum ulong half = (1UL << (wordBits - T.mant_dig - 1));
     enum bigHalf = UInt!128([0UL, half]);
-    enum bigMask = (1UL << (64 - T.mant_dig)) - 1;
+    enum bigMask = (UInt!128(1UL) << (64 - T.mant_dig)) - 1;
 
     static if (T.mant_dig > 64)
         enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;
@@ -131,7 +131,7 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
     static if (T.mant_dig == 64)
         enum ulong mask = ulong.max;
     else
-        enum ulong mask = bigMask;
+        enum ulong mask = (1UL << (64 - T.mant_dig)) - 1;
     
     if (coefficient == 0)
         return 0;

From 39742368d31ae67ad1b1aa248e2174f47bf0328e Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 16 Jul 2022 11:06:31 -0400
Subject: [PATCH 472/573] Increase date documentation, coverage and
 functionality (#434)

* Improve date documentation

* Update coverage for YearQuarter

* Add addMonths/Quarters/Years
---
 dub.sdl           |   2 +-
 source/mir/date.d | 657 ++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 603 insertions(+), 56 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 893708c4..0270bdd2 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -12,7 +12,7 @@ dependency "mir-core" version=">=1.1.106"
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
-    // versions "mir_ndslice_test"
+    // versions "mir_ndslice_test"
     versions "mir_test"
     dflags "-lowmem"
 }
diff --git a/source/mir/date.d b/source/mir/date.d
index d45b8fe6..ec5e4ac5 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -7,9 +7,12 @@ $(BOOKTABLE,
 $(TR $(TH Category) $(TH Functions))
 $(TR $(TD Main date types) $(TD
     $(LREF Date)
+    $(LREF YearMonth)
+    $(LREF YearQuarter)
 ))
 $(TR $(TD Other date types) $(TD
     $(LREF Month)
+    $(LREF Quarter)
     $(LREF DayOfWeek)
 ))
 $(TR $(TD Date checking) $(TD
@@ -18,10 +21,12 @@ $(TR $(TD Date checking) $(TD
 ))
 $(TR $(TD Date conversion) $(TD
     $(LREF daysToDayOfWeek)
+    $(LREF quarter)
 ))
 $(TR $(TD Other) $(TD
     $(LREF AllowDayOverflow)
     $(LREF DateTimeException)
+    $(LREF AssumePeriod)
 ))
 ))
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
@@ -490,7 +495,10 @@ struct YearMonthDay
     }
 }
 
-///
+/++
+Controls the assumed start period of days for `YearMonth` or days and quarters
+for `YearQuarter`
++/
 enum AssumePeriod {
     ///
     begin,
@@ -498,7 +506,7 @@ enum AssumePeriod {
     end
 }
 
-///
+/// Represents a date as a pair of years and months. 
 @serdeProxy!Timestamp
 struct YearMonth
 {
@@ -538,6 +546,22 @@ struct YearMonth
         w.printZeroPad(cast(uint)month, 2);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(1999, Month.jan);
+        assert(ym.toISOExtString == "1999-01");
+    }
+
+    //
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(10_001, Month.jan);
+        assert(ym.toISOExtString == "+10001-01");
+    }
+
     @property ubyte day(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
     {
         final switch (assumePeriod)
@@ -549,12 +573,27 @@ struct YearMonth
         }
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(1999, cast(Month) 1).day(AssumePeriod.begin) == 1);
+        assert(YearMonth(1999, cast(Month) 12).day(AssumePeriod.end) == 31);
+    }
+
     ///
     Quarter quarter() @safe pure nothrow @nogc @property
     {
         return month.quarter;
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(1999, Month.jan).quarter == 1);
+    }
+
     ///
     Timestamp timestamp() @safe pure nothrow @nogc const @property
     {
@@ -598,6 +637,13 @@ struct YearMonth
         this.month = cast(Month)month;
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(2000, 12);
+    }
+
     ///
     this(Date date) @safe pure nothrow @nogc
     {
@@ -678,6 +724,15 @@ struct YearMonth
         return d;
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(2000, Month.nov);
+        assert(ym.nthWeekday(1, DayOfWeek.mon) == Date(2000, 11, 6));
+        assert(ym.nthWeekday(5, DayOfWeek.mon) == Date(2000, 12, 4));
+    }
+
     ///
     Date trustedWithDayOfMonth(int days) const @safe pure nothrow @nogc
     {
@@ -685,6 +740,14 @@ struct YearMonth
         return Date.trustedCreate(year, month, days);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(2000, Month.nov);
+        assert(ym.trustedWithDayOfMonth(6) == Date(2000, 11, 6));
+    }
+
     ///
     int opCmp(YearMonth rhs) const pure nothrow @nogc @safe
     {
@@ -693,29 +756,71 @@ struct YearMonth
         return this.month - rhs.month;
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(2000, Month.nov);
+        assert(ym.opCmp(YearMonth(2000, Month.nov)) == 0);
+        assert(ym.opCmp(YearMonth(2000, Month.oct)) == 1);
+        assert(ym.opCmp(YearMonth(2000, Month.dec)) == -1);
+        assert(ym.opCmp(YearMonth(2001, Month.nov)) == -1);
+    }
+
     ///
     size_t toHash() const pure nothrow @nogc @safe
     {
         return year * 16 + month;
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(2000, Month.dec).toHash == 32012);
+    }
+
     ///
     Date endOfMonth() const nothrow @property @nogc @safe pure
     {
         return Date.trustedCreate(year, month, lengthOfMonth);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(2000, Month.dec).endOfMonth == Date(2000, Month.dec, 31));
+    }
+
     ///
     ushort lengthOfMonth() const pure nothrow @property @nogc @safe
     {
         return maxDay(year, month);
     }
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(2000, Month.dec).lengthOfMonth == 31);
+    }
+
     ///
     this(scope const(char)[] str) @safe pure @nogc
     {
         this = fromISOExtString(str);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth("1999-01");
+        assert(ym.year == 1999);
+        assert(ym.month == 1);
+    }
+
     static bool fromISOExtString(C)(scope const(C)[] str, out YearMonth value) @safe pure @nogc
         if (isSomeChar!C)
     {
@@ -779,32 +884,65 @@ nothrow:
         return this;
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        ym0.add!"months"(1);
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.feb);
+
+        auto ym1 = ym0.add!"months"(1);
+        assert(ym1.year == 2020);
+        assert(ym1.month == Month.mar);
+
+        // also changes ym0
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.mar);
+
+        ym1.add!"months"(10);
+        assert(ym1.year == 2021);
+        assert(ym1.month == Month.jan);
+
+        ym1.add!"months"(-13);
+        assert(ym1.year == 2019);
+        assert(ym1.month == Month.dec);
+    }
+
     ///
     @safe pure nothrow @nogc
     ref YearMonth add(string units : "quarters")(long quarters)
     {
-        auto years = quarters / 4;
-        quarters %= 4;
-        auto newQuarter = this.quarter + quarters;
+        return add!"months"(quarters * 3);
+    }
 
-        if (quarters < 0)
-        {
-            if (newQuarter < 1)
-            {
-                newQuarter += 4;
-                --years;
-            }
-        }
-        else if (newQuarter > 4)
-        {
-            newQuarter -= 4;
-            ++years;
-        }
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq0 = YearMonth(2020, Month.jan);
 
-        year += years;
-        month = cast(Month) (cast(ubyte) month + newQuarter * 3);
+        yq0.add!"quarters"(1);
+        assert(yq0.year == 2020);
+        assert(yq0.month == Month.apr);
 
-        return this;
+        auto yq1 = yq0.add!"quarters"(1);
+        assert(yq1.year == 2020);
+        assert(yq1.month == Month.jul);
+
+        // also changes yq0
+        assert(yq0.year == 2020);
+        assert(yq0.month == Month.jul);
+
+        yq1.add!"quarters"(2);
+        assert(yq1.year == 2021);
+        assert(yq1.month == Month.jan);
+
+        yq1.add!"quarters"(-5);
+        assert(yq1.year == 2019);
+        assert(yq1.month == Month.oct);
     }
 
     ///
@@ -815,6 +953,129 @@ nothrow:
         return this;
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        ym0.add!"years"(1);
+        assert(ym0.year == 2021);
+        assert(ym0.month == Month.jan);
+
+        auto ym1 = ym0.add!"years"(1);
+        assert(ym1.year == 2022);
+        assert(ym1.month == Month.jan);
+
+        // also changes ym0
+        assert(ym0.year == 2022);
+        assert(ym0.month == Month.jan);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearMonth addMonths(long months)
+    {
+        auto newYear = year;
+        newYear += months / 12;
+        months %= 12;
+        auto newMonth = month;
+        newMonth += months;
+
+        if (months < 0)
+        {
+            if (newMonth < 1)
+            {
+                newMonth += 12;
+                --newYear;
+            }
+        }
+        else if (newMonth > 12)
+        {
+            newMonth -= 12;
+            ++newYear;
+        }
+
+        return YearMonth(newYear, newMonth);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        auto ym1 = ym0.addMonths(15);
+        assert(ym1.year == 2021);
+        assert(ym1.month == Month.apr);
+
+        auto ym2 = ym1.addMonths(-6);
+        assert(ym2.year == 2020);
+        assert(ym2.month == Month.oct);
+
+        auto ym3 = YearMonth(2020, Month.dec).addMonths(3);
+        assert(ym3.year == 2021);
+        assert(ym3.month == Month.mar);
+
+        // ym0 is left unchagned
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.jan);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearMonth addQuarters(long quarters)
+    {
+        return addMonths(quarters * 3);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        auto ym1 = ym0.addQuarters(5);
+        assert(ym1.year == 2021);
+        assert(ym1.month == Month.apr);
+
+        auto ym2 = ym1.addQuarters(-2);
+        assert(ym2.year == 2020);
+        assert(ym2.month == Month.oct);
+
+        auto ym3 = YearMonth(2020, Month.dec).addQuarters(1);
+        assert(ym3.year == 2021);
+        assert(ym3.month == Month.mar);
+
+        // ym0 is left unchagned
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.jan);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearMonth addYears(long years)
+    {
+        auto newYear = this.year;
+        newYear += years;
+        return YearMonth(newYear, month);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        auto ym1 = ym0.addYears(1);
+        assert(ym1.year == 2021);
+        assert(ym1.month == Month.jan);
+ 
+        // leaves ym0 unchanged
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.jan);
+    }
+
     private void setMonthOfYear(bool useExceptions = false)(int days)
     {
         immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
@@ -841,6 +1102,25 @@ nothrow:
         assert(0, "Invalid day of the year.");
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym = YearMonth(2020, Month.feb);
+        ym.setMonthOfYear(10);
+        assert(ym.year == 2020);
+        assert(ym.month == Month.jan);
+        ym.setMonthOfYear(100);
+        assert(ym.year == 2020);
+        assert(ym.month == Month.apr);
+        ym.setMonthOfYear(200);
+        assert(ym.year == 2020);
+        assert(ym.month == Month.jul);
+        ym.setMonthOfYear(300);
+        assert(ym.year == 2020);
+        assert(ym.month == Month.oct);
+    }
+
     ///
     int opBinary(string op : "-")(YearMonth rhs) const
     {
@@ -854,9 +1134,9 @@ nothrow:
         if (op == "+" || op == "-")
     {
         static if (op == "+")
-           return addMonths(rhs);
+           return add!"months"(rhs);
         else
-           return addMonths(-rhs);
+           return add!"months"(-rhs);
     }
 
     ///
@@ -877,6 +1157,17 @@ nothrow:
         assert(0, "Invalid month.");
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(1999, cast(Month) 1).dayOfYear == 1);
+        assert(YearMonth(1999, cast(Month) 12).dayOfYear(AssumePeriod.begin) == 335);
+        assert(YearMonth(1999, cast(Month) 12).dayOfYear(AssumePeriod.end) == 365);
+        assert(YearMonth(2000, cast(Month) 12).dayOfYear(AssumePeriod.begin) == 336);
+        assert(YearMonth(2000, cast(Month) 12).dayOfYear(AssumePeriod.end) == 366);
+    }
+
     /++
         Whether this $(LREF Date) is in a leap year.
      +/
@@ -885,6 +1176,14 @@ nothrow:
         return yearIsLeapYear(year);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(1999, cast(Month) 12).isLeapYear == false);
+        assert(YearMonth(2000, cast(Month) 12).isLeapYear == true);
+    }
+
     /++
         The last day in the month that this $(LREF Date) is in.
       +/
@@ -893,6 +1192,13 @@ nothrow:
         return maxDay(year, month);
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(2020, Month.dec).daysInMonth == 31);
+    }
+
     /++
         Whether the current year is a date in A.D.
       +/
@@ -900,19 +1206,13 @@ nothrow:
     {
         return year > 0;
     }
-}
-
-///
-version (mir_test)
-@safe unittest
-{
-    assert(YearMonth(1999, cast(Month) 1).day(AssumePeriod.begin) == 1);
-    assert(YearMonth(1999, cast(Month) 12).day(AssumePeriod.end) == 31);
-    assert(YearMonth(1999, cast(Month) 1).dayOfYear(AssumePeriod.begin) == 1);
-    assert(YearMonth(1999, cast(Month) 12).dayOfYear(AssumePeriod.begin) == 335);
-    assert(YearMonth(1999, cast(Month) 12).dayOfYear(AssumePeriod.end) == 365);
-    assert(YearMonth(2000, cast(Month) 12).dayOfYear(AssumePeriod.begin) == 336);
-    assert(YearMonth(2000, cast(Month) 12).dayOfYear(AssumePeriod.end) == 366);
+    
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        assert(YearMonth(2020, Month.jan).isAD == true);
+    }
 }
 
 ///
@@ -928,7 +1228,13 @@ enum Quarter : short
     q4,
 }
 
-///
+/++
+Returns the quarter for a given month.
+
+Params:
+    month
+
++/
 @safe pure @nogc nothrow
 Quarter quarter(Month month)
 {
@@ -974,18 +1280,29 @@ unittest {
     assert(Quarter.q4.monthInQuarter(AssumePeriod.end) == Month.dec);
 }
 
-///
+/// Represents a date as a pair of years and quarters. 
 @serdeProxy!Timestamp
 struct YearQuarter
 {
     short year  = 1;
     Quarter quarter = Quarter.q1;
     
+    ///
     @property Month month(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
     {
         return quarter.monthInQuarter(assumePeriod);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(2000, Quarter.q4);
+        assert(yq.month == 10);
+        assert(yq.month(AssumePeriod.end) == 12);
+    }
+
+    ///
     @property ubyte day(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
     {
         final switch (assumePeriod)
@@ -997,12 +1314,30 @@ struct YearQuarter
         }
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(2000, Quarter.q4);
+        assert(yq.day == 1);
+        assert(yq.day(AssumePeriod.end) == 31);
+    }
+
     ///
     Timestamp timestamp() @safe pure nothrow @nogc const @property
     {
         return Timestamp(year, cast(ubyte)month);
     }
 
+    ///
+    version(mir_test)
+    unittest
+    {
+        import mir.timestamp;
+        auto yq = YearQuarter(2020, Quarter.q2);
+        auto ts = yq.timestamp;
+    }
+
     ///
     alias opCast(T : Timestamp) = timestamp;
 
@@ -1011,9 +1346,8 @@ struct YearQuarter
     unittest
     {
         import mir.timestamp;
-        auto yq0 = YearQuarter(2020, Quarter.q2);
-        auto timestamp = cast(Timestamp) yq0;
-        auto yq = YearQuarter(timestamp);
+        auto yq = YearQuarter(2020, Quarter.q2);
+        auto timestamp = cast(Timestamp) yq;
     }
 
     ///
@@ -1095,6 +1429,15 @@ struct YearQuarter
         with(timestamp) this(year, cast(Month)month);
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        import mir.timestamp;
+        auto ts = Timestamp(2020, 4);
+        auto yq = YearQuarter(ts);
+    }
+
     ///
     @safe pure nothrow @nogc
     ref YearQuarter add(string units : "quarters")(long quarters)
@@ -1123,6 +1466,33 @@ struct YearQuarter
         return this;
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq0 = YearQuarter(2020, Quarter.q1);
+
+        yq0.add!"quarters"(1);
+        assert(yq0.year == 2020);
+        assert(yq0.quarter == Quarter.q2);
+
+        auto yq1 = yq0.add!"quarters"(1);
+        assert(yq1.year == 2020);
+        assert(yq1.quarter == Quarter.q3);
+
+        // also changes yq0
+        assert(yq0.year == 2020);
+        assert(yq0.quarter == Quarter.q3);
+
+        yq1.add!"quarters"(2);
+        assert(yq1.year == 2021);
+        assert(yq1.quarter == Quarter.q1);
+
+        yq1.add!"quarters"(-5);
+        assert(yq1.year == 2019);
+        assert(yq1.quarter == Quarter.q4);
+    }
+
     ///
     @safe pure nothrow @nogc
     ref YearQuarter add(string units : "years")(long years)
@@ -1131,6 +1501,99 @@ struct YearQuarter
         return this;
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq0 = YearQuarter(2020, Quarter.q1);
+
+        yq0.add!"years"(1);
+        assert(yq0.year == 2021);
+        assert(yq0.quarter == Quarter.q1);
+
+        auto yq1 = yq0.add!"years"(1);
+        assert(yq1.year == 2022);
+        assert(yq1.quarter == Quarter.q1);
+
+        // also changes yq0
+        assert(yq0.year == 2022);
+        assert(yq0.quarter == Quarter.q1);
+    }
+
+
+    ///
+    @safe pure nothrow @nogc
+    YearQuarter addQuarters(long quarters)
+    {
+        auto years = quarters / 4;
+        auto newYear = year;
+        newYear += years;
+        quarters %= 4;
+        auto newQuarter = quarter + quarters;
+
+        if (quarters < 0)
+        {
+            if (newQuarter < 1)
+            {
+                newQuarter += 4;
+                --newYear;
+            }
+        }
+        else if (newQuarter > 4)
+        {
+            newQuarter -= 4;
+            ++newYear;
+        }
+
+        return YearQuarter(newYear, cast(Quarter) newQuarter);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq0 = YearQuarter(2020, Quarter.q1);
+
+        auto yq1 = yq0.addQuarters(5);
+        assert(yq1.year == 2021);
+        assert(yq1.quarter == Quarter.q2);
+
+        auto yq2 = yq1.addQuarters(-2);
+        assert(yq2.year == 2020);
+        assert(yq2.quarter == Quarter.q4);
+
+        auto yq3 = YearQuarter(2020, Quarter.q4).addQuarters(1);
+        assert(yq3.year == 2021);
+        assert(yq3.quarter == Quarter.q1);
+
+        // yq0 is left unchagned
+        assert(yq0.year == 2020);
+        assert(yq0.quarter == Quarter.q1);
+    }
+    ///
+    @safe pure nothrow @nogc
+    YearQuarter addYears(long years)
+    {
+        auto newYear = this.year;
+        newYear += years;
+        return YearQuarter(newYear, quarter);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq0 = YearQuarter(2020, Quarter.q1);
+
+        auto yq1 = yq0.addYears(1);
+        assert(yq1.year == 2021);
+        assert(yq1.quarter == Quarter.q1);
+ 
+        // leaves yq0 unchanged
+        assert(yq0.year == 2020);
+        assert(yq0.quarter == Quarter.q1);
+    }
+
     private void setQuarterOfYear(bool useExceptions = false)(int days)
     {
         immutable int[] lastDay = isLeapYear ? lastDayQuarterLeap : lastDayQuarterNonLeap;
@@ -1157,6 +1620,25 @@ struct YearQuarter
         assert(0, "Invalid day of the year.");
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(2020, Quarter.q3);
+        yq.setQuarterOfYear(10);
+        assert(yq.year == 2020);
+        assert(yq.quarter == Quarter.q1);
+        yq.setQuarterOfYear(100);
+        assert(yq.year == 2020);
+        assert(yq.quarter == Quarter.q2);
+        yq.setQuarterOfYear(200);
+        assert(yq.year == 2020);
+        assert(yq.quarter == Quarter.q3);
+        yq.setQuarterOfYear(300);
+        assert(yq.year == 2020);
+        assert(yq.quarter == Quarter.q4);
+    }
+
     /++
         Day of the quarter this $(LREF Date) is on.
       +/
@@ -1180,6 +1662,22 @@ struct YearQuarter
         return dayOfQuarter(assumePeriod, assumePeriod);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearQuarter(1999, cast(Quarter) 1).dayOfQuarter == 1);
+        assert(YearQuarter(1999, cast(Quarter) 1).dayOfQuarter(AssumePeriod.begin, AssumePeriod.end) == 31);
+        assert(YearQuarter(1999, cast(Quarter) 1).dayOfQuarter(AssumePeriod.end) == 90);
+
+        assert(YearQuarter(2000, cast(Quarter) 1).dayOfQuarter(AssumePeriod.begin, AssumePeriod.end) == 31);
+        assert(YearQuarter(2000, cast(Quarter) 1).dayOfQuarter(AssumePeriod.end) == 91);
+
+        assert(YearQuarter(2000, cast(Quarter) 4).dayOfQuarter == 1);
+        assert(YearQuarter(2000, cast(Quarter) 4).dayOfQuarter(AssumePeriod.begin, AssumePeriod.end) == 31);
+        assert(YearQuarter(2000, cast(Quarter) 4).dayOfQuarter(AssumePeriod.end) == 92);
+    }
+
     /++
         Day of the year this $(LREF Date) is on.
       +/
@@ -1201,6 +1699,19 @@ struct YearQuarter
         return dayOfYear(assumePeriod, assumePeriod);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearQuarter(1999, cast(Quarter) 1).dayOfYear == 1);
+        assert(YearQuarter(1999, cast(Quarter) 4).dayOfYear == 274);
+        assert(YearQuarter(1999, cast(Quarter) 4).dayOfYear(AssumePeriod.begin, AssumePeriod.end) == 304);
+        assert(YearQuarter(1999, cast(Quarter) 4).dayOfYear(AssumePeriod.end) == 365);
+        assert(YearQuarter(2000, cast(Quarter) 4).dayOfYear == 275);
+        assert(YearQuarter(2000, cast(Quarter) 4).dayOfYear(AssumePeriod.begin, AssumePeriod.end) == 305);
+        assert(YearQuarter(2000, cast(Quarter) 4).dayOfYear(AssumePeriod.end) == 366);
+    }
+
     /++
         Whether this $(LREF Date) is in a leap year.
      +/
@@ -1209,12 +1720,29 @@ struct YearQuarter
         return yearIsLeapYear(year);
     }
 
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        assert(YearQuarter(1999, cast(Quarter) 4).isLeapYear == false);
+        assert(YearQuarter(2000, cast(Quarter) 4).isLeapYear == true);
+    }
+
     /++
         The last day in the month that this $(LREF Date) is in.
       +/
-    @property ubyte daysInMonth() const @safe pure nothrow @nogc
+    @property ubyte daysInMonth(AssumePeriod assumePeriod = AssumePeriod.begin) const @safe pure nothrow @nogc
     {
-        return maxDay(year, month);
+        return maxDay(year, month(assumePeriod));
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq = YearQuarter(2020, Quarter.q3);
+        assert(yq.daysInMonth == 31);
+        assert(yq.daysInMonth(AssumePeriod.end) == 30);
     }
 
     /++
@@ -1224,19 +1752,13 @@ struct YearQuarter
     {
         return year > 0;
     }
-}
 
-///
-version (mir_test)
-@safe unittest
-{
-    assert(YearQuarter(1999, cast(Quarter) 1).day(AssumePeriod.begin) == 1);
-    assert(YearQuarter(1999, cast(Quarter) 4).day(AssumePeriod.end) == 31);
-    assert(YearQuarter(1999, cast(Quarter) 1).dayOfYear == 1);
-    assert(YearQuarter(1999, cast(Quarter) 4).dayOfYear == 274);
-    assert(YearQuarter(1999, cast(Quarter) 4).dayOfYear(AssumePeriod.end) == 365);
-    assert(YearQuarter(2000, cast(Quarter) 4).dayOfYear == 275);
-    assert(YearQuarter(2000, cast(Quarter) 4).dayOfYear(AssumePeriod.end) == 366);
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        assert(YearQuarter(2020, Quarter.q1).isAD == true);
+    }
 }
 
 /++
@@ -1360,6 +1882,15 @@ public:
         return yearMonthDay.timestamp;
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        import mir.timestamp;
+        auto d1 = Date(2020, Month.may, 15);
+        auto ts2 = d1.timestamp;
+    }
+
     version(D_Exceptions)
     ///
     this(Timestamp timestamp) @safe pure @nogc
@@ -1372,6 +1903,15 @@ public:
         with(timestamp) this(year, cast(Month)month, day);
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        import mir.timestamp;
+        auto ts = Date(2020, Month.may, 15).timestamp;
+        auto d2 = Date(ts);
+    }
+
     version(D_Exceptions)
     ///
     this(scope const(char)[] str) @safe pure @nogc
@@ -1379,6 +1919,13 @@ public:
         this = fromString(str);
     }
 
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto d = Date("2020-12-31");
+    }
+
     version(D_Exceptions)
     ///
     this(YearMonthDay ymd) @safe pure @nogc

From 35c9edb626fd88c585369c9a36e3fb55a237bb01 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 26 Jul 2022 12:59:35 -0400
Subject: [PATCH 473/573] Add mir.date.d operator overloading, UTs (#438)

* Add mir.date.d operator overloading, UTs

* Update Date UT

* Address ddoc CI failure

* Remove date mixins

* Rename some lhs variables rhs

* Restore and deprecate date add functions
---
 source/mir/date.d | 237 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 215 insertions(+), 22 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index ec5e4ac5..c343417e 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -854,9 +854,10 @@ struct YearMonth
         throw exc;
     }
 
-nothrow:
+nothrow:
 
     ///
+    deprecated("please use addMonths instead")
     @safe pure nothrow @nogc
     ref YearMonth add(string units : "months")(long months)
     {
@@ -885,7 +886,7 @@ nothrow:
     }
 
     ///
-    version(mir_test)
+    version(mir_test_deprecated)
     @safe unittest
     {
         auto ym0 = YearMonth(2020, Month.jan);
@@ -912,6 +913,7 @@ nothrow:
     }
 
     ///
+    deprecated("please use addQuarters instead")
     @safe pure nothrow @nogc
     ref YearMonth add(string units : "quarters")(long quarters)
     {
@@ -919,7 +921,7 @@ nothrow:
     }
 
     ///
-    version(mir_test)
+    version(mir_test_deprecated)
     @safe unittest
     {
         auto yq0 = YearMonth(2020, Month.jan);
@@ -946,6 +948,7 @@ nothrow:
     }
 
     ///
+    deprecated("please use addYears instead")
     @safe pure nothrow @nogc
     ref YearMonth add(string units : "years")(long years)
     {
@@ -954,7 +957,7 @@ nothrow:
     }
 
     ///
-    version(mir_test)
+    version(mir_test_deprecated)
     @safe unittest
     {
         auto ym0 = YearMonth(2020, Month.jan);
@@ -970,7 +973,7 @@ nothrow:
         // also changes ym0
         assert(ym0.year == 2022);
         assert(ym0.month == Month.jan);
-    }
+    }
 
     ///
     @safe pure nothrow @nogc
@@ -1122,7 +1125,7 @@ nothrow:
     }
 
     ///
-    int opBinary(string op : "-")(YearMonth rhs) const
+    int opBinary(string op : "-")(YearMonth rhs)
     {
         alias a = this;
         alias b = rhs;
@@ -1130,18 +1133,73 @@ nothrow:
     }
 
     ///
-    YearMonth opBinary(string op)(int rhs) const
+    YearMonth opBinary(string op)(int rhs)
         if (op == "+" || op == "-")
     {
         static if (op == "+")
-           return add!"months"(rhs);
+           return addMonths(rhs);
         else
-           return add!"months"(-rhs);
+           return addMonths(-rhs);
     }
 
     ///
     alias opBinaryRight(string op : "+") = opBinary!"+";
 
+    ///
+    ref YearMonth opOpAssign(string op)(int rhs) return @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        static if (op == "+")
+           this = addMonths(rhs);
+        else
+           this = addMonths(-rhs);
+        return this;
+    }
+
+    ///
+    @safe pure @nogc nothrow
+    version(mir_test)
+    unittest
+    {
+        auto x = YearMonth(2020, Month.mar);
+        auto x1 = x + 1;
+        assert(x1 == YearMonth(2020, Month.apr));
+        auto x2 = x + 2;
+        assert(x2 == YearMonth(2020, Month.may));
+        auto x3 = x + 3;
+        assert(x3 == YearMonth(2020, Month.jun));
+    }
+
+    ///
+    @safe pure @nogc nothrow
+    version(mir_test)
+    unittest {
+        auto ym = YearMonth(2020, Month.mar);
+        ym += 2;
+        assert(ym == YearMonth(2020, Month.may));
+        ym -= 1;
+        assert(ym == YearMonth(2020, Month.apr));
+    }
+
+    /// Get a slice of YearMonths
+    @safe pure @nogc nothrow
+    version(mir_test)
+    unittest {
+        import mir.ndslice.topology: iota;
+
+        static immutable result1 = [YearMonth(2020, Month.mar), YearMonth(2020, Month.apr), YearMonth(2020, Month.may), YearMonth(2020, Month.jun)];
+        static immutable result2 = [YearMonth(2020, Month.mar), YearMonth(2020, Month.may), YearMonth(2020, Month.jul), YearMonth(2020, Month.sep)];
+
+        auto ym = YearMonth(2020, Month.mar);
+
+        auto x = ym + 4.iota!uint;
+        assert(x == result1);
+
+        // every other month
+        auto y = ym + iota!uint([4], 0, 2);
+        assert(y == result2);
+    }
+
     /++
         Day of the year this $(LREF Date) is on.
       +/
@@ -1232,7 +1290,7 @@ enum Quarter : short
 Returns the quarter for a given month.
 
 Params:
-    month
+    month = month
 
 +/
 @safe pure @nogc nothrow
@@ -1439,6 +1497,7 @@ struct YearQuarter
     }
 
     ///
+    deprecated("please use addQuarters instead")
     @safe pure nothrow @nogc
     ref YearQuarter add(string units : "quarters")(long quarters)
     {
@@ -1467,7 +1526,7 @@ struct YearQuarter
     }
 
     ///
-    version(mir_test)
+    version(mir_test_deprecated)
     @safe unittest
     {
         auto yq0 = YearQuarter(2020, Quarter.q1);
@@ -1494,6 +1553,7 @@ struct YearQuarter
     }
 
     ///
+    deprecated("please use addYears instead")
     @safe pure nothrow @nogc
     ref YearQuarter add(string units : "years")(long years)
     {
@@ -1502,7 +1562,7 @@ struct YearQuarter
     }
 
     ///
-    version(mir_test)
+    version(mir_test_deprecated)
     @safe unittest
     {
         auto yq0 = YearQuarter(2020, Quarter.q1);
@@ -1520,7 +1580,6 @@ struct YearQuarter
         assert(yq0.quarter == Quarter.q1);
     }
 
-
     ///
     @safe pure nothrow @nogc
     YearQuarter addQuarters(long quarters)
@@ -1569,7 +1628,8 @@ struct YearQuarter
         // yq0 is left unchagned
         assert(yq0.year == 2020);
         assert(yq0.quarter == Quarter.q1);
-    }
+    }
+
     ///
     @safe pure nothrow @nogc
     YearQuarter addYears(long years)
@@ -1639,6 +1699,82 @@ struct YearQuarter
         assert(yq.quarter == Quarter.q4);
     }
 
+    ///
+    int opBinary(string op : "-")(YearQuarter rhs)
+    {
+        alias a = this;
+        alias b = rhs;
+        return (a.year - b.year) * 4 + a.quarter - b.quarter;
+    }
+
+    ///
+    YearQuarter opBinary(string op)(int rhs)
+        if (op == "+" || op == "-")
+    {
+        static if (op == "+")
+           return addQuarters(rhs);
+        else
+           return addQuarters(-rhs);
+    }
+
+    ///
+    alias opBinaryRight(string op : "+") = opBinary!"+";
+
+    ///
+    ref YearQuarter opOpAssign(string op)(int rhs) return @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
+    {
+        static if (op == "+")
+           this = addQuarters(rhs);
+        else
+           this = addQuarters(-rhs);
+        return this;
+    }
+
+    ///
+    @safe pure @nogc nothrow
+    version(mir_test)
+    unittest
+    {
+        auto x = YearQuarter(2020, Quarter.q1);
+        auto x1 = x + 1;
+        assert(x1 == YearQuarter(2020, Quarter.q2));
+        auto x2 = x + 2;
+        assert(x2 == YearQuarter(2020, Quarter.q3));
+        auto x3 = x + 3;
+        assert(x3 == YearQuarter(2020, Quarter.q4));
+    }
+
+    ///
+    @safe pure @nogc nothrow
+    version(mir_test)
+    unittest {
+        auto yq = YearQuarter(2020, Quarter.q1);
+        yq += 2;
+        assert(yq == YearQuarter(2020, Quarter.q3));
+        yq -= 1;
+        assert(yq == YearQuarter(2020, Quarter.q2));
+    }
+
+    /// Get a slice of YearQuarters
+    @safe pure @nogc nothrow
+    version(mir_test)
+    unittest {
+        import mir.ndslice.topology: iota;
+
+        static immutable result1 = [YearQuarter(2020, Quarter.q1), YearQuarter(2020, Quarter.q2), YearQuarter(2020, Quarter.q3), YearQuarter(2020, Quarter.q4)];
+        static immutable result2 = [YearQuarter(2020, Quarter.q1), YearQuarter(2020, Quarter.q3), YearQuarter(2021, Quarter.q1), YearQuarter(2021, Quarter.q3)];
+
+        auto yq = YearQuarter(2020, Quarter.q1);
+
+        auto x = yq + 4.iota!uint;
+        assert(x == result1);
+
+        // every other quarter
+        auto y = yq + iota!uint([4], 0, 2);
+        assert(y == result2);
+    }
+
     /++
         Day of the quarter this $(LREF Date) is on.
       +/
@@ -2715,27 +2851,84 @@ public:
     }
 
     ///
-    int opBinary(string op : "-")(Date lhs) const
+    int opBinary(string op : "-")(Date rhs) const
     {
-        return _julianDay - lhs._julianDay;
+        return _julianDay - rhs._julianDay;
     }
 
     ///
-    Date opBinary(string op : "+")(int lhs) const
+    Date opBinary(string op : "+")(int rhs) const
     {
-        return Date(_julianDay + lhs);
+        return Date(_julianDay + rhs);
     }
 
     ///
-    Date opBinaryRight(string op : "+")(int lhs) const
+    Date opBinaryRight(string op : "+")(int rhs) const
     {
-        return Date(_julianDay + lhs);
+        return Date(_julianDay + rhs);
     }
 
     ///
-    Date opBinary(string op : "-")(int lhs) const
+    Date opBinary(string op : "-")(int rhs) const
+    {
+        return Date(_julianDay - rhs);
+    }
+
+    ///
+    ref Date opOpAssign(string op)(int rhs) return @safe pure nothrow @nogc
+        if (op == "+" || op == "-")
     {
-        return Date(_julianDay - lhs);
+        static if (op == "+")
+           this._addDays(rhs);
+        else
+           this._addDays(-rhs);
+        return this;
+    }
+    
+    ///
+    @safe pure @nogc
+    version(mir_test)
+    unittest {
+        auto d = Date(2020, 1, 1);
+        d += 2;
+        assert(d == Date(2020, 1, 3));
+        d -= 1;
+        assert(d == Date(2020, 1, 2));
+    }
+
+
+    /// Get a slice of Dates
+    @safe pure @nogc
+    version(mir_test)
+    unittest {
+        import mir.ndslice.topology: iota, map;
+
+        static immutable result1 = [Date(2020, Month.mar, 1), Date(2020, Month.mar, 2), Date(2020, Month.mar, 3), Date(2020, Month.mar, 4)];
+        static immutable result2 = [Date(2020, Month.mar, 1), Date(2020, Month.mar, 3), Date(2020, Month.mar, 5), Date(2020, Month.mar, 7)];
+        static immutable result3 = [Date(2020, Month.mar, 1), Date(2020, Month.apr, 1), Date(2020, Month.may, 1), Date(2020, Month.jun, 1)];
+        static immutable result4 = [Date(2020, Month.mar, 1), Date(2020, Month.jun, 1), Date(2020, Month.sep, 1), Date(2020, Month.dec, 1)];
+        static immutable result5 = [Date(2020, Month.mar, 1), Date(2021, Month.mar, 1), Date(2022, Month.mar, 1), Date(2023, Month.mar, 1)];
+
+        auto d = Date(2020, Month.mar, 1);
+
+        auto x = d + 4.iota!uint;
+        assert(x == result1);
+
+        // every other date
+        auto y = d + iota!uint([4], 0, 2);
+        assert(y == result2);
+
+        // every month
+        auto z = (d.YearMonth + 4.iota!uint).map!Date;
+        assert(z == result3);
+
+        // every quarter
+        auto a = (d.YearQuarter + 4.iota!uint).map!(a => a.Date(AssumePeriod.end, AssumePeriod.begin));
+        assert(a == result4);
+
+        // every year
+        auto b = (d.year + 4.iota!uint).map!(a => YearMonthDay(cast(short) a, Month.mar, 1).Date);
+        assert(b == result5);
     }
 
     const nothrow @nogc pure @safe

From 2bc7dc75c9287e4ddda881dfc87da2fbd3caf2e6 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 31 Jul 2022 18:30:51 +0400
Subject: [PATCH 474/573] add convexity member for Spline

---
 source/mir/interpolate/spline.d | 69 ++++++++++++++++++++++++++++++---
 1 file changed, 64 insertions(+), 5 deletions(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 646359e2..b4be8494 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -332,6 +332,7 @@ unittest
     auto grid = cartesian(x0, x1, x2);
 
     auto interpolant = spline!(double, 3)(x0.rcslice, x1.rcslice, x2.rcslice, grid.map!f);
+    assert(interpolant.convexity == [SplineConvexity.none, SplineConvexity.none, SplineConvexity.convex]);
 
     ///// compute test data ////
     auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23, x2.sliced - 3);
@@ -472,6 +473,19 @@ enum SplineType
     akima,
 }
 
+/++
+Spline convexity type
++/
+enum SplineConvexity
+{
+    /// Neither convex nor concave spline
+    none = 0,
+    /// Concave spline
+    concave = -1,
+    /// Convex spline
+    convex = 1,
+}
+
 /++
 Constructs multivariate cubic spline in symmetrical form with nodes on rectilinear grid.
 Result has continues second derivatives throughout the curve / nd-surface.
@@ -726,6 +740,8 @@ struct Spline(F, size_t N = 1, X = F)
     Slice!(RCI!(F[2 ^^ N]), N) _data;
     /// Grid iterators. $(RED For internal use.)
     Repeat!(N, RCI!(immutable X)) _grid;
+    ///
+    SplineConvexity[N] convexity;
 
     enum dimensionCount = N;
 
@@ -809,7 +825,7 @@ struct Spline(F, size_t N = 1, X = F)
 
         static if (N == 1)
         {
-            splineSlopes!(F, F)(_grid[0].lightConst.sliced(_data._lengths[0]), pickDataSubslice(_data.lightScope, 0), pickDataSubslice(_data.lightScope, 1), temp[0 .. _data._lengths[0]], kind, param, lBoundary, rBoundary);
+            convexity[0] = splineSlopes!(F, F)(_grid[0].lightConst.sliced(_data._lengths[0]), pickDataSubslice(_data.lightScope, 0), pickDataSubslice(_data.lightScope, 1), temp[0 .. _data._lengths[0]], kind, param, lBoundary, rBoundary);
         }
         else
         foreach_reverse(i; Iota!N)
@@ -826,7 +842,16 @@ struct Spline(F, size_t N = 1, X = F)
                         auto y = pickDataSubslice(d, l);
                         auto s = pickDataSubslice(d, L + l);
                         // debug printf("ptr = %ld, stride = %ld, stride = %ld, d = %ld i = %ld l = %ld\n", d.iterator, d._stride!0, y._stride!0, d.length, i, l);
-                        splineSlopes!(F, F)(_grid[i].sliced(_data._lengths[i]), y, s, temp[0 .. _data._lengths[i]], kind, param, lBoundary, rBoundary);
+                        auto c = splineSlopes!(F, F)(_grid[i].sliced(_data._lengths[i]), y, s, temp[0 .. _data._lengths[i]], kind, param, lBoundary, rBoundary);
+                        static if (l)
+                        {
+                            if (convexity[i] != c)
+                                convexity[i] = SplineConvexity.none;
+                        }
+                        else
+                        {
+                            convexity[i] = c;
+                        }
                         // debug{
                         //     (cast(void delegate() @nogc)(){
                         //     writeln("y = ", y);
@@ -1055,8 +1080,10 @@ Params:
 Constraints:
     `points`, `values`, and `slopes`, must have the same length > 3;
     `temp` must have length greater or equal to points less minus one.
+Returs:
+    Spline convexity type
 +/
-void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind skind)(
+SplineConvexity splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind skind)(
     Slice!(IP, 1, gkind) points,
     Slice!(IV, 1, vkind) values,
     Slice!(IS, 1, skind) slopes,
@@ -1146,7 +1173,7 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
                 assert(slopes.length == 2);
                 slopes.back = slopes.front = yd.front / xd.front;
             }
-            return;
+            return slopes.back >= slopes.front ? SplineConvexity.convex : SplineConvexity.concave;
         }
     }
 
@@ -1360,12 +1387,30 @@ void splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind
 
     slopes.back = temp.back / slopes.back;
 
+    size_t convexCount;
+    size_t concaveCount;
     foreach_reverse (i; 0 .. n - 1)
     {
         auto c = i ==     0 ? first : kind == SplineType.c2 ? xd[i - 1] : 0;
         auto v = temp[i] - c * slopes[i + 1];
         slopes[i] = slopes[i]  == 1 ? v : v / slopes[i];
+
+        auto xdiff = points[i + 1] - points[i];
+        auto ydiff = values[i + 1] - values[i];
+
+        auto convex1  = xdiff * (2 * slopes[i] + slopes[i + 1]) <= 3 * ydiff;
+        auto concave1 = xdiff * (2 * slopes[i] + slopes[i + 1]) >= 3 * ydiff;
+        auto convex2  = xdiff * (slopes[i] + 2 * slopes[i + 1]) >= 3 * ydiff;
+        auto concave2 = xdiff * (slopes[i] + 2 * slopes[i + 1]) <= 3 * ydiff;
+        convexCount += convex1 & convex2;
+        convexCount += concave1 & concave2;
     }
+
+    return
+        // convex kind has priority for the linear spline
+        convexCount  == n - 1 ? SplineConvexity.convex  :
+        concaveCount == n - 1 ? SplineConvexity.concave :
+        SplineConvexity.none;
 }
 
 private F akimaTail(F)(in F d2, in F d3)
@@ -1813,7 +1858,7 @@ unittest
         .map!f
         .rcslice
         .lightConst;
-    
+
     auto int0 = spline!double(x1.rcslice!(immutable double), grid[0]);
     auto int1 = spline!double(x1.rcslice!(immutable double), grid[1]);
     auto int2 = spline!double(x1.rcslice!(immutable double), grid[2]);
@@ -1839,3 +1884,17 @@ unittest
     assert(appreq(d[0][1], y_x1 + y_x0x1 * z0));
     assert(appreq(d[1][1], y_x0x1));
 }
+
+version(mir_test)
+unittest
+{
+    static immutable x = [0.05000161436323281, 0.10000322872646562, 0.15000484308969841, 0.250008071816164, 0.35001130054262963, 0.5, 0.6499886994573704, 0.749991928183836, 0.8499951569103016, 0.8999967712735344, 0.9499983856367672];
+    static immutable y = [0.09758, 0.097825, 0.09795000000000001, 0.0991, 0.10115, 0.10545, 0.11115000000000001, 0.1164, 0.12425, 0.129675, 0.13632];
+    import mir.ndslice, mir.stdio;
+    auto spline = spline!double(x.rcslice, y.rcslice, SplineType.c2);
+    foreach (e; x)
+    {
+        auto v = spline.opCall!2(e);
+        writeln(e, ",\t", v[0], ",\t", v[1], ",\t", v[2]);
+    }
+}

From 64e331a80e54a48598626d011adcb7d47bcc86a8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Aug 2022 16:57:40 +0400
Subject: [PATCH 475/573] change the author name

---
 LICENSE                                      |   2 +-
 dub.sdl                                      |   6 +-
 include/mir/ndslice.h                        |   4 +-
 index.d                                      |   2 +-
 source/mir/algebraic_alias/ion.d             |   2 +-
 source/mir/algebraic_alias/json.d            |   2 +-
 source/mir/algebraic_alias/transform.d       |   2 +-
 source/mir/algorithm/iteration.d             |  10 +-
 source/mir/algorithm/setops.d                |   4 +-
 source/mir/annotated.d                       |   2 +-
 source/mir/appender.d                        |   2 +-
 source/mir/array/allocation.d                |   2 +-
 source/mir/bignum/internal/ryu/generic_128.d |   2 +-
 source/mir/combinatorics/package.d           |   2 +-
 source/mir/container/binaryheap.d            |   4 +-
 source/mir/date.d                            | 360 +++++++++----------
 source/mir/ediff.d                           |   2 +-
 source/mir/format.d                          |   2 +-
 source/mir/graph/package.d                   |   4 +-
 source/mir/graph/tarjan.d                    |   4 +-
 source/mir/interpolate/constant.d            |   4 +-
 source/mir/interpolate/generic.d             |   4 +-
 source/mir/interpolate/linear.d              |   4 +-
 source/mir/interpolate/package.d             |   4 +-
 source/mir/interpolate/polynomial.d          |   4 +-
 source/mir/interpolate/spline.d              |   4 +-
 source/mir/math/func/expdigamma.d            |   2 +-
 source/mir/math/func/normal.d                |   2 +-
 source/mir/math/numeric.d                    |   4 +-
 source/mir/math/stat.d                       |   4 +-
 source/mir/math/sum.d                        |   4 +-
 source/mir/ndslice/allocation.d              |   4 +-
 source/mir/ndslice/chunks.d                  |   4 +-
 source/mir/ndslice/concatenation.d           |   4 +-
 source/mir/ndslice/connect/cpython.d         |   4 +-
 source/mir/ndslice/dynamic.d                 |   4 +-
 source/mir/ndslice/field.d                   |   4 +-
 source/mir/ndslice/filling.d                 |   4 +-
 source/mir/ndslice/fuse.d                    |   4 +-
 source/mir/ndslice/iterator.d                |   4 +-
 source/mir/ndslice/mutation.d                |   4 +-
 source/mir/ndslice/ndfield.d                 |   4 +-
 source/mir/ndslice/package.d                 |   4 +-
 source/mir/ndslice/slice.d                   |   4 +-
 source/mir/ndslice/sorting.d                 |   4 +-
 source/mir/ndslice/topology.d                |   4 +-
 source/mir/ndslice/traits.d                  |   2 +-
 source/mir/numeric.d                         |   4 +-
 source/mir/parse.d                           |   4 +-
 source/mir/polynomial.d                      |   2 +-
 source/mir/range.d                           |   4 +-
 source/mir/serde.d                           |   4 +-
 source/mir/series.d                          |   4 +-
 source/mir/small_array.d                     |   4 +-
 source/mir/small_string.d                    |   4 +-
 source/mir/type_info.d                       |   4 +-
 56 files changed, 278 insertions(+), 278 deletions(-)

diff --git a/LICENSE b/LICENSE
index 6ff8988b..5e20e311 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-   Copyright 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+   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.
diff --git a/dub.sdl b/dub.sdl
index 0270bdd2..0b4bf179 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -1,8 +1,8 @@
 name "mir-algorithm"
 description "Dlang Core Library"
 
-authors "Ilya Yaroshenko" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And others" "mir.date and a bit of other code is based on Phobos"
-copyright "2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments"
+authors "Ilia Ki" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And others" "mir.date and a bit of other code is based on Phobos"
+copyright "2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
 dependency "mir-core" version=">=1.1.106"
@@ -12,7 +12,7 @@ dependency "mir-core" version=">=1.1.106"
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
-    // versions "mir_ndslice_test"
+    // versions "mir_ndslice_test"
     versions "mir_test"
     dflags "-lowmem"
 }
diff --git a/include/mir/ndslice.h b/include/mir/ndslice.h
index 1e2a5d24..c5f23713 100644
--- a/include/mir/ndslice.h
+++ b/include/mir/ndslice.h
@@ -9,8 +9,8 @@ The module provides wrappers for $(SUBREF slice, Slice) that
 can be used as arguments for C++ functions.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 */
 #include <cstddef>
 #include <cstdint>
diff --git a/index.d b/index.d
index fa1c7123..0b863783 100644
--- a/index.d
+++ b/index.d
@@ -67,7 +67,7 @@ $(BOOKTABLE ,
     $(TR $(TDNW $(MREF mir,ndslice,connect,cpython)) $(TD Utilities for $(HTTPS docs.python.org/3/c-api/buffer.html, Python Buffer Protocol)))
 )
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 
 Macros:
         TITLE=Mir Algorithm
diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
index 69a97d15..6a4a442b 100644
--- a/source/mir/algebraic_alias/ion.d
+++ b/source/mir/algebraic_alias/ion.d
@@ -6,7 +6,7 @@ This module contains a single alias definition and doesn't provide Ion serializa
 See_also: Ion library $(MIR_PACKAGE mir-ion)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko 
+Authors: Ilia Ki 
 Macros:
 +/
 module mir.algebraic_alias.ion;
diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index cf24a231..36901959 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -6,7 +6,7 @@ This module contains a single alias definition and doesn't provide JSON serializ
 See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf);
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko 
+Authors: Ilia Ki 
 Macros:
 +/
 module mir.algebraic_alias.json;
diff --git a/source/mir/algebraic_alias/transform.d b/source/mir/algebraic_alias/transform.d
index 4ffe5ee5..80c8f487 100644
--- a/source/mir/algebraic_alias/transform.d
+++ b/source/mir/algebraic_alias/transform.d
@@ -4,7 +4,7 @@ $(H1 Transformation utilities for JSON-like values)
 See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf);
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko 
+Authors: Ilia Ki 
 Macros:
 +/
 module mir.algebraic_alias.transform;
diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index ec00ae58..e9b62fd0 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -38,13 +38,13 @@ All operators are suitable to change slices using `ref` argument qualification i
 Note, that string lambdas in Mir are `auto ref` functions.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko, John Michael Hall, Andrei Alexandrescu (original Phobos code)
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki, John Michael Hall, Andrei Alexandrescu (original Phobos code)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-Authors: , Ilya Yaroshenko (Mir & BetterC rework).
+Authors: , Ilia Ki (Mir & BetterC rework).
 Source: $(PHOBOSSRC std/algorithm/_iteration.d)
 Macros:
     NDSLICEREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
@@ -3831,7 +3831,7 @@ version(mir_test)
 }
 
 /++
-Authros: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (betterC rework)
+Authros: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilia Ki (betterC rework)
 +/
 struct Uniq(alias pred, Range)
 {
diff --git a/source/mir/algorithm/setops.d b/source/mir/algorithm/setops.d
index 3671571a..155a45bd 100644
--- a/source/mir/algorithm/setops.d
+++ b/source/mir/algorithm/setops.d
@@ -3,9 +3,9 @@
 This is a submodule of $(MREF mir, algorithm). It contains `nothrow` `@nogc` BetterC alternative to `MultiwayMerge` from `std.algorithm.setops`.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (Mir & BetterC rework, optimization).
+Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilia Ki (Mir & BetterC rework, optimization).
  */
 module mir.algorithm.setops;
 
diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 2245ae1d..f45cbbc4 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -2,7 +2,7 @@
 $(H1 Annotated value)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko 
+Authors: Ilia Ki 
 Macros:
 +/
 module mir.annotated;
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 6b35d162..9da76dc5 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -2,7 +2,7 @@
 $(H1 Scoped Buffer)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko
+Authors: Ilia Ki
 +/
 module mir.appender;
 
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index 6fc6e330..2c44ca16 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -12,7 +12,7 @@ $(TR $(TH Function Name) $(TH Description)
     ))
 )
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
diff --git a/source/mir/bignum/internal/ryu/generic_128.d b/source/mir/bignum/internal/ryu/generic_128.d
index a9cf6fab..2dc9168c 100644
--- a/source/mir/bignum/internal/ryu/generic_128.d
+++ b/source/mir/bignum/internal/ryu/generic_128.d
@@ -1,6 +1,6 @@
 // Converted and then optimised from generic_128.h and generic_128.c
 // Copyright 2018 Ulf Adams (original code https://github.com/ulfjack/ryu)
-// Copyright 2020 Ilya Yaroshenko (2020 D conversion and optimisation)
+// Copyright 2020 Ilia Ki (2020 D conversion and optimisation)
 // License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
 // This is a generic 128-bit implementation of float to shortest conversion
diff --git a/source/mir/combinatorics/package.d b/source/mir/combinatorics/package.d
index 8c8a3201..6684f1bc 100644
--- a/source/mir/combinatorics/package.d
+++ b/source/mir/combinatorics/package.d
@@ -1,7 +1,7 @@
 /**
 This module contains various combinatorics algorithms.
 
-Authors: Sebastian Wilzbach, Ilya Yaroshenko
+Authors: Sebastian Wilzbach, Ilia Ki
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 */
diff --git a/source/mir/container/binaryheap.d b/source/mir/container/binaryheap.d
index 81d866ac..9dbc7a0f 100644
--- a/source/mir/container/binaryheap.d
+++ b/source/mir/container/binaryheap.d
@@ -6,11 +6,11 @@ Current implementation is suitable for Mir/BetterC concepts.
 
 This module is a submodule of $(MREF mir, container).
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
-Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (Mir & BetterC rework).
+Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilia Ki (Mir & BetterC rework).
 */
 module mir.container.binaryheap;
 
diff --git a/source/mir/date.d b/source/mir/date.d
index c343417e..80446cde 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -30,7 +30,7 @@ $(TR $(TD Other) $(TD
 ))
 ))
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like and BetterC rework)
+Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilia Ki (boost-like and BetterC rework)
 +/
 module mir.date;
 
@@ -854,7 +854,7 @@ struct YearMonth
         throw exc;
     }
 
-nothrow:
+nothrow:
 
     ///
     deprecated("please use addMonths instead")
@@ -974,110 +974,110 @@ nothrow:
         assert(ym0.year == 2022);
         assert(ym0.month == Month.jan);
     }
-
-    ///
-    @safe pure nothrow @nogc
-    YearMonth addMonths(long months)
-    {
-        auto newYear = year;
-        newYear += months / 12;
-        months %= 12;
-        auto newMonth = month;
-        newMonth += months;
-
-        if (months < 0)
-        {
-            if (newMonth < 1)
-            {
-                newMonth += 12;
-                --newYear;
-            }
-        }
-        else if (newMonth > 12)
-        {
-            newMonth -= 12;
-            ++newYear;
-        }
-
-        return YearMonth(newYear, newMonth);
-    }
-
-    ///
-    version(mir_test)
-    @safe unittest
-    {
-        auto ym0 = YearMonth(2020, Month.jan);
-
-        auto ym1 = ym0.addMonths(15);
-        assert(ym1.year == 2021);
-        assert(ym1.month == Month.apr);
-
-        auto ym2 = ym1.addMonths(-6);
-        assert(ym2.year == 2020);
-        assert(ym2.month == Month.oct);
-
-        auto ym3 = YearMonth(2020, Month.dec).addMonths(3);
-        assert(ym3.year == 2021);
-        assert(ym3.month == Month.mar);
-
-        // ym0 is left unchagned
-        assert(ym0.year == 2020);
-        assert(ym0.month == Month.jan);
-    }
-
-    ///
-    @safe pure nothrow @nogc
-    YearMonth addQuarters(long quarters)
-    {
-        return addMonths(quarters * 3);
-    }
-
-    ///
-    version(mir_test)
-    @safe unittest
-    {
-        auto ym0 = YearMonth(2020, Month.jan);
-
-        auto ym1 = ym0.addQuarters(5);
-        assert(ym1.year == 2021);
-        assert(ym1.month == Month.apr);
-
-        auto ym2 = ym1.addQuarters(-2);
-        assert(ym2.year == 2020);
-        assert(ym2.month == Month.oct);
-
-        auto ym3 = YearMonth(2020, Month.dec).addQuarters(1);
-        assert(ym3.year == 2021);
-        assert(ym3.month == Month.mar);
-
-        // ym0 is left unchagned
-        assert(ym0.year == 2020);
-        assert(ym0.month == Month.jan);
-    }
-
-    ///
-    @safe pure nothrow @nogc
-    YearMonth addYears(long years)
-    {
-        auto newYear = this.year;
-        newYear += years;
-        return YearMonth(newYear, month);
-    }
-
-    ///
-    version(mir_test)
-    @safe unittest
-    {
-        auto ym0 = YearMonth(2020, Month.jan);
-
-        auto ym1 = ym0.addYears(1);
-        assert(ym1.year == 2021);
-        assert(ym1.month == Month.jan);
- 
-        // leaves ym0 unchanged
-        assert(ym0.year == 2020);
-        assert(ym0.month == Month.jan);
-    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearMonth addMonths(long months)
+    {
+        auto newYear = year;
+        newYear += months / 12;
+        months %= 12;
+        auto newMonth = month;
+        newMonth += months;
+
+        if (months < 0)
+        {
+            if (newMonth < 1)
+            {
+                newMonth += 12;
+                --newYear;
+            }
+        }
+        else if (newMonth > 12)
+        {
+            newMonth -= 12;
+            ++newYear;
+        }
+
+        return YearMonth(newYear, newMonth);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        auto ym1 = ym0.addMonths(15);
+        assert(ym1.year == 2021);
+        assert(ym1.month == Month.apr);
+
+        auto ym2 = ym1.addMonths(-6);
+        assert(ym2.year == 2020);
+        assert(ym2.month == Month.oct);
+
+        auto ym3 = YearMonth(2020, Month.dec).addMonths(3);
+        assert(ym3.year == 2021);
+        assert(ym3.month == Month.mar);
+
+        // ym0 is left unchagned
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.jan);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearMonth addQuarters(long quarters)
+    {
+        return addMonths(quarters * 3);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        auto ym1 = ym0.addQuarters(5);
+        assert(ym1.year == 2021);
+        assert(ym1.month == Month.apr);
+
+        auto ym2 = ym1.addQuarters(-2);
+        assert(ym2.year == 2020);
+        assert(ym2.month == Month.oct);
+
+        auto ym3 = YearMonth(2020, Month.dec).addQuarters(1);
+        assert(ym3.year == 2021);
+        assert(ym3.month == Month.mar);
+
+        // ym0 is left unchagned
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.jan);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearMonth addYears(long years)
+    {
+        auto newYear = this.year;
+        newYear += years;
+        return YearMonth(newYear, month);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto ym0 = YearMonth(2020, Month.jan);
+
+        auto ym1 = ym0.addYears(1);
+        assert(ym1.year == 2021);
+        assert(ym1.month == Month.jan);
+ 
+        // leaves ym0 unchanged
+        assert(ym0.year == 2020);
+        assert(ym0.month == Month.jan);
+    }
 
     private void setMonthOfYear(bool useExceptions = false)(int days)
     {
@@ -1579,80 +1579,80 @@ struct YearQuarter
         assert(yq0.year == 2022);
         assert(yq0.quarter == Quarter.q1);
     }
-
-    ///
-    @safe pure nothrow @nogc
-    YearQuarter addQuarters(long quarters)
-    {
-        auto years = quarters / 4;
-        auto newYear = year;
-        newYear += years;
-        quarters %= 4;
-        auto newQuarter = quarter + quarters;
-
-        if (quarters < 0)
-        {
-            if (newQuarter < 1)
-            {
-                newQuarter += 4;
-                --newYear;
-            }
-        }
-        else if (newQuarter > 4)
-        {
-            newQuarter -= 4;
-            ++newYear;
-        }
-
-        return YearQuarter(newYear, cast(Quarter) newQuarter);
-    }
-
-    ///
-    version(mir_test)
-    @safe unittest
-    {
-        auto yq0 = YearQuarter(2020, Quarter.q1);
-
-        auto yq1 = yq0.addQuarters(5);
-        assert(yq1.year == 2021);
-        assert(yq1.quarter == Quarter.q2);
-
-        auto yq2 = yq1.addQuarters(-2);
-        assert(yq2.year == 2020);
-        assert(yq2.quarter == Quarter.q4);
-
-        auto yq3 = YearQuarter(2020, Quarter.q4).addQuarters(1);
-        assert(yq3.year == 2021);
-        assert(yq3.quarter == Quarter.q1);
-
-        // yq0 is left unchagned
-        assert(yq0.year == 2020);
-        assert(yq0.quarter == Quarter.q1);
-    }
-
-    ///
-    @safe pure nothrow @nogc
-    YearQuarter addYears(long years)
-    {
-        auto newYear = this.year;
-        newYear += years;
-        return YearQuarter(newYear, quarter);
-    }
-
-    ///
-    version(mir_test)
-    @safe unittest
-    {
-        auto yq0 = YearQuarter(2020, Quarter.q1);
-
-        auto yq1 = yq0.addYears(1);
-        assert(yq1.year == 2021);
-        assert(yq1.quarter == Quarter.q1);
- 
-        // leaves yq0 unchanged
-        assert(yq0.year == 2020);
-        assert(yq0.quarter == Quarter.q1);
-    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearQuarter addQuarters(long quarters)
+    {
+        auto years = quarters / 4;
+        auto newYear = year;
+        newYear += years;
+        quarters %= 4;
+        auto newQuarter = quarter + quarters;
+
+        if (quarters < 0)
+        {
+            if (newQuarter < 1)
+            {
+                newQuarter += 4;
+                --newYear;
+            }
+        }
+        else if (newQuarter > 4)
+        {
+            newQuarter -= 4;
+            ++newYear;
+        }
+
+        return YearQuarter(newYear, cast(Quarter) newQuarter);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq0 = YearQuarter(2020, Quarter.q1);
+
+        auto yq1 = yq0.addQuarters(5);
+        assert(yq1.year == 2021);
+        assert(yq1.quarter == Quarter.q2);
+
+        auto yq2 = yq1.addQuarters(-2);
+        assert(yq2.year == 2020);
+        assert(yq2.quarter == Quarter.q4);
+
+        auto yq3 = YearQuarter(2020, Quarter.q4).addQuarters(1);
+        assert(yq3.year == 2021);
+        assert(yq3.quarter == Quarter.q1);
+
+        // yq0 is left unchagned
+        assert(yq0.year == 2020);
+        assert(yq0.quarter == Quarter.q1);
+    }
+
+    ///
+    @safe pure nothrow @nogc
+    YearQuarter addYears(long years)
+    {
+        auto newYear = this.year;
+        newYear += years;
+        return YearQuarter(newYear, quarter);
+    }
+
+    ///
+    version(mir_test)
+    @safe unittest
+    {
+        auto yq0 = YearQuarter(2020, Quarter.q1);
+
+        auto yq1 = yq0.addYears(1);
+        assert(yq1.year == 2021);
+        assert(yq1.quarter == Quarter.q1);
+ 
+        // leaves yq0 unchanged
+        assert(yq0.year == 2020);
+        assert(yq0.quarter == Quarter.q1);
+    }
 
     private void setQuarterOfYear(bool useExceptions = false)(int days)
     {
diff --git a/source/mir/ediff.d b/source/mir/ediff.d
index 06f6d633..934b3656 100644
--- a/source/mir/ediff.d
+++ b/source/mir/ediff.d
@@ -68,7 +68,7 @@ On the other hand, it can't eliminate identical calls of complex functions.
 It is highly recommended to evaluate a set of partial derivatives immediately after constructing
 a complex expression such as $(LREF normalDistribution).
 
-Authors: Ilya Yaroshenko
+Authors: Ilia Ki
 +/
 module mir.ediff;
 
diff --git a/source/mir/format.d b/source/mir/format.d
index 346ebcda..308d9108 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -2,7 +2,7 @@
 $(H1 @nogc Formatting Utilities)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko
+Authors: Ilia Ki
 +/
 module mir.format;
 
diff --git a/source/mir/graph/package.d b/source/mir/graph/package.d
index 37587137..44001f50 100644
--- a/source/mir/graph/package.d
+++ b/source/mir/graph/package.d
@@ -2,8 +2,8 @@
 Basic routines to work with graphs.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, graph, $1)$(NBSP)
diff --git a/source/mir/graph/tarjan.d b/source/mir/graph/tarjan.d
index 3e1000ba..b63b8262 100644
--- a/source/mir/graph/tarjan.d
+++ b/source/mir/graph/tarjan.d
@@ -4,8 +4,8 @@ This is a submodule of $(MREF mir,graph).
 Tarjan's strongly connected components algorithm.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 29f5e632..707e9d9d 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -4,8 +4,8 @@ $(H2 Constant Interpolation)
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index 1d1c1703..5c7c22a4 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -4,8 +4,8 @@ $(H2 Generic Piecewise Interpolant)
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index c9f4e081..169795eb 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -4,8 +4,8 @@ $(H2 Linear Interpolation)
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/package.d b/source/mir/interpolate/package.d
index b55d2f21..83af1c5a 100644
--- a/source/mir/interpolate/package.d
+++ b/source/mir/interpolate/package.d
@@ -10,8 +10,8 @@ $(T2M polynomial, Lagrange Barycentric Interpolant)
 $(T2M spline, Piecewise Cubic Hermite Interpolant Spline: C2 (with contiguous second derivative), cardinal, monotone (aka PCHIP), double-quadratic, Akima)
 )
 ]
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir,interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index 68e73502..215db54c 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -4,8 +4,8 @@ $(H2 Lagrange Barycentric Interpolation)
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index b4be8494..ae232a06 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -6,8 +6,8 @@ The module provides common C2 splines, monotone (PCHIP) splines, Akima splines a
 See_also: $(LREF SplineType), $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
diff --git a/source/mir/math/func/expdigamma.d b/source/mir/math/func/expdigamma.d
index d4724b46..64d38c1f 100644
--- a/source/mir/math/func/expdigamma.d
+++ b/source/mir/math/func/expdigamma.d
@@ -1,7 +1,7 @@
 /**
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
-Authors: Ilya Yaroshenko
+Authors: Ilia Ki
 */
 module mir.math.func.expdigamma;
 
diff --git a/source/mir/math/func/normal.d b/source/mir/math/func/normal.d
index 01cb935b..5a5e6e8a 100644
--- a/source/mir/math/func/normal.d
+++ b/source/mir/math/func/normal.d
@@ -6,7 +6,7 @@ License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
  *
  * Copyright: Based on the CEPHES math library, which is
  *            Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
- * Authors:   Stephen L. Moshier, ported to D by Don Clugston and David Nadlinger. Adopted to Mir by Ilya Yaroshenko.
+ * Authors:   Stephen L. Moshier, ported to D by Don Clugston and David Nadlinger. Adopted to Mir by Ilia Ki.
  */
 /**
  * Macros:
diff --git a/source/mir/math/numeric.d b/source/mir/math/numeric.d
index a784a7c3..2a14cc56 100644
--- a/source/mir/math/numeric.d
+++ b/source/mir/math/numeric.d
@@ -1,8 +1,8 @@
 /++
 This module contains simple numeric algorithms.
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 Sponsors: This work has been sponsored by $(SUBREF http://symmetryinvestments.com, Symmetry Investments) and Kaleidic Associates.
 +/
 module mir.math.numeric;
diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index 78a9f9fb..a2ce2936 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -7,9 +7,9 @@ at expense of precision, one can use $(REF_ALTTEXT $(TT Summation.fast), Summati
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
-Authors: Shigeki Karita (original numir code), Ilya Yaroshenko, John Michael Hall
+Authors: Shigeki Karita (original numir code), Ilia Ki, John Michael Hall
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, math, $1)$(NBSP)
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 8b72cf95..01fd1a21 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -3,9 +3,9 @@ This module contains summation algorithms.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
-Authors: Ilya Yaroshenko
+Authors: Ilia Ki
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 +/
 module mir.math.sum;
 
diff --git a/source/mir/ndslice/allocation.d b/source/mir/ndslice/allocation.d
index 70fce888..f791f979 100644
--- a/source/mir/ndslice/allocation.d
+++ b/source/mir/ndslice/allocation.d
@@ -44,8 +44,8 @@ $(T2 stdcFreeAlignedSlice, Frees memory using CRuntime)
 )
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/chunks.d b/source/mir/ndslice/chunks.d
index 16acc47e..e5ff246e 100644
--- a/source/mir/ndslice/chunks.d
+++ b/source/mir/ndslice/chunks.d
@@ -7,8 +7,8 @@ $(LREF Chunks) structure is multidimensional random access range with slicing.
 $(SUBREF slice, slicedField), $(SUBREF slice, slicedNdField) can be used to construct ndslice view on top of $(LREF Chunks).
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index 0af813ab..38a403a9 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -20,8 +20,8 @@ $(T2 padWrap, Pads with the wrap of the slice along the axis. The first values a
 
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 See_also: $(SUBMODULE fuse) submodule.
 
diff --git a/source/mir/ndslice/connect/cpython.d b/source/mir/ndslice/connect/cpython.d
index a3b8a9cd..8c943141 100644
--- a/source/mir/ndslice/connect/cpython.d
+++ b/source/mir/ndslice/connect/cpython.d
@@ -2,8 +2,8 @@
 Utilities for $(LINK2 https://docs.python.org/3/c-api/buffer.html, Python Buffer Protocol).
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/dynamic.d b/source/mir/ndslice/dynamic.d
index 9cbcebf3..bab8a638 100644
--- a/source/mir/ndslice/dynamic.d
+++ b/source/mir/ndslice/dynamic.d
@@ -66,9 +66,9 @@ is identical to that of $(LREF reversed).
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 
-Authors: Ilya Yaroshenko
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d
index b9843538..9f36768e 100644
--- a/source/mir/ndslice/field.d
+++ b/source/mir/ndslice/field.d
@@ -22,8 +22,8 @@ $(T2 SparseField, Used for mutable DOK sparse matrixes )
 
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/filling.d b/source/mir/ndslice/filling.d
index 554f3073..77e9a67a 100644
--- a/source/mir/ndslice/filling.d
+++ b/source/mir/ndslice/filling.d
@@ -4,8 +4,8 @@ This is a submodule of $(MREF mir,ndslice).
 Initialisation routines.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index d3fd80cc..3621c031 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -4,8 +4,8 @@ This is a submodule of $(MREF mir,ndslice).
 Allocation routines that construct ndslices from ndranges.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 See_also: $(SUBMODULE concatenation) submodule.
 
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index eb5732f2..c0c5752e 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -27,8 +27,8 @@ $(T2 ZipIterator, $(SUBREF topology, zip))
 )
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/mutation.d b/source/mir/ndslice/mutation.d
index 6d0becd4..0f77f738 100644
--- a/source/mir/ndslice/mutation.d
+++ b/source/mir/ndslice/mutation.d
@@ -8,8 +8,8 @@ $(TR $(TH Function Name) $(TH Description))
 )
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/ndfield.d b/source/mir/ndslice/ndfield.d
index 90fb32e7..6bca052c 100644
--- a/source/mir/ndslice/ndfield.d
+++ b/source/mir/ndslice/ndfield.d
@@ -13,8 +13,8 @@ $(T2 Kronecker, $(SUBREF topology, kronecker))
 See_also: $(SUBREF concatenation, concatenation).
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index 347819c1..76d21c0b 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -483,8 +483,8 @@ efficient as if they were written in C.
 
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 Acknowledgements: John Loughran Colvin
 
 Macros:
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index eb8f1846..f18ad164 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -6,8 +6,8 @@ Safety_note:
     Bounds are checked in ndslice code.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 $(BOOKTABLE $(H2 Definitions),
 $(TR $(TH Name) $(TH Description))
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index 1c869b81..e29b2a13 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -15,8 +15,8 @@ See_also: $(SUBREF topology, flattened)
 `isSorted` and `isStrictlyMonotonic`
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Andrei Alexandrescu (Phobos), Ilya Yaroshenko (API, rework, Mir adoptation)
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Andrei Alexandrescu (Phobos), Ilia Ki (API, rework, Mir adoptation)
 
 Macros:
     SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 7da206a9..889a47d5 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -101,8 +101,8 @@ Examples of use of subspace selectors are available for selectors,
 $(SUBREF slice, Slice.shape), and $(SUBREF slice, Slice.elementCount).
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko, John Michael Hall, Shigeki Karita (original numir code)
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki, John Michael Hall, Shigeki Karita (original numir code)
 
 Sponsors: Part of this work has been sponsored by $(LINK2 http://symmetryinvestments.com, Symmetry Investments) and Kaleidic Associates.
 
diff --git a/source/mir/ndslice/traits.d b/source/mir/ndslice/traits.d
index 61d20b50..087efb6f 100644
--- a/source/mir/ndslice/traits.d
+++ b/source/mir/ndslice/traits.d
@@ -20,7 +20,7 @@ $(T2 isIterator, Test if type is a random access iterator.)
 )
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 Authors: John Hall
 
 
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 22cf8fac..86ad7faa 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -4,7 +4,7 @@ Base numeric algorithms.
 Reworked part of `std.numeric`.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot), Lars Tandle Kyllingstad (diff)
+Authors: Ilia Ki (API, findLocalMin, findRoot extension), Don Clugston (findRoot), Lars Tandle Kyllingstad (diff)
 +/
 module mir.numeric;
 
@@ -415,7 +415,7 @@ private @fmamath FindRootResult!T findRootImplGen(T)(
     // (www.netlib.org).  The changes to improve the worst-cast performance are
     // entirely original.
     
-    // Author: Ilya Yaroshenko (Bounds extension logic,
+    // Author: Ilia Ki (Bounds extension logic,
     // API improvements, infinity and huge numbers handing, compiled code size reduction)
 
     T d;  // [a .. b] is our current bracket. d is the third best guess.
diff --git a/source/mir/parse.d b/source/mir/parse.d
index b8c19f3d..c57f24cb 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -2,8 +2,8 @@
 $(H1 @nogc and nothrow Parsing Utilities)
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
 +/
 module mir.parse;
 
diff --git a/source/mir/polynomial.d b/source/mir/polynomial.d
index 641d3db6..3e244663 100644
--- a/source/mir/polynomial.d
+++ b/source/mir/polynomial.d
@@ -2,7 +2,7 @@
 Polynomial ref-counted structure.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Authors: Ilya Yaroshenko
+Authors: Ilia Ki
 +/
 module mir.polynomial;
 
diff --git a/source/mir/range.d b/source/mir/range.d
index 6f88f9dc..c96526ee 100644
--- a/source/mir/range.d
+++ b/source/mir/range.d
@@ -4,8 +4,8 @@ Ranges.
 See_also: $(MREF mir,_primitives).
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko, Phobos Authors
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki, Phobos Authors
 +/
 module mir.range;
 
diff --git a/source/mir/serde.d b/source/mir/serde.d
index f7cd58c1..9df6cffb 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -2,8 +2,8 @@
 This implements common de/serialization routines.
 
 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
diff --git a/source/mir/series.d b/source/mir/series.d
index 2f580edf..2e1f9389 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -6,8 +6,8 @@ It is aimed to construct index or time-series using Mir and Phobos algorithms.
 
 Public_imports: $(MREF mir,ndslice,slice).
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 315045c2..d9cd0693 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -5,8 +5,8 @@ The module contains self-contained generic small array implementaton.
 
 $(LREF SmallArray) supports ASDF - Json Serialisation Library.
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 +/
 module mir.small_array;
 
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 46c524d0..f2e844ae 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -8,8 +8,8 @@ $(LREF SmallString) supports ASDF - Json Serialisation Library.
 See also `include/mir/small_series.h` for a C++ version of this type.
 Both C++ and D implementations have the same ABI and name mangling.
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 +/
 module mir.small_string;
 
diff --git a/source/mir/type_info.d b/source/mir/type_info.d
index c8673dcd..7983d0bc 100644
--- a/source/mir/type_info.d
+++ b/source/mir/type_info.d
@@ -3,8 +3,8 @@ $(H1 Type Information)
 
 Type Information implementation compatible with BetterC mode.
 
-Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
-Authors: Ilya Yaroshenko
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
 
 Macros:
 NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)

From 16c631859f703b1259b9a36630077ea85b45188d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Aug 2022 17:16:23 +0400
Subject: [PATCH 476/573] get rid of unused unittest

---
 source/mir/interpolate/spline.d | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index ae232a06..cc8cc542 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -1884,17 +1884,3 @@ unittest
     assert(appreq(d[0][1], y_x1 + y_x0x1 * z0));
     assert(appreq(d[1][1], y_x0x1));
 }
-
-version(mir_test)
-unittest
-{
-    static immutable x = [0.05000161436323281, 0.10000322872646562, 0.15000484308969841, 0.250008071816164, 0.35001130054262963, 0.5, 0.6499886994573704, 0.749991928183836, 0.8499951569103016, 0.8999967712735344, 0.9499983856367672];
-    static immutable y = [0.09758, 0.097825, 0.09795000000000001, 0.0991, 0.10115, 0.10545, 0.11115000000000001, 0.1164, 0.12425, 0.129675, 0.13632];
-    import mir.ndslice, mir.stdio;
-    auto spline = spline!double(x.rcslice, y.rcslice, SplineType.c2);
-    foreach (e; x)
-    {
-        auto v = spline.opCall!2(e);
-        writeln(e, ",\t", v[0], ",\t", v[1], ",\t", v[2]);
-    }
-}

From 7853e9c61dc6df11d1e3c813a9f7de3efec9ef3b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Aug 2022 17:16:45 +0400
Subject: [PATCH 477/573] allow zero derivatives for constant spline

---
 source/mir/interpolate/constant.d | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 707e9d9d..76afbf41 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -161,9 +161,12 @@ extern(D):
     ///
     enum uint derivativeOrder = 0;
 
+    ///
+    enum dimensionCount = N;
+
     ///
     template opCall(uint derivative = 0)
-        if (derivative <= derivativeOrder)
+        // if (derivative <= derivativeOrder)
     {
         /++
         `(x)` operator.
@@ -181,7 +184,16 @@ extern(D):
                 else
                     indices[i] = _data._lengths[i] > 1 ? this.findInterval!i(xs[i]) : 0;
             }
-            return _data[indices];
+            static if (derivative == 0)
+            {
+                return _data[indices];
+            }
+            else
+            {
+                F[derivative + 1] ret = 0;
+                ret[0] = _data[indices];
+                return ret;
+            }
         }
     }
 }

From 83a03a8e61a9d64463b79dc5fc1c197039c76f33 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Aug 2022 17:17:00 +0400
Subject: [PATCH 478/573] add dimensionCount to Lagrange

---
 source/mir/interpolate/polynomial.d | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index 215db54c..aaddbbf7 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -104,6 +104,8 @@ struct Lagrange(T, uint maxAdditionalFunctions = 0, X = T)
 
     ///
     enum uint derivativeOrder = maxAdditionalFunctions;
+    ///
+    enum uint dimensionCount = 1;
 
     /++
     Complexity: `O(N)`

From 1de60213601d8644f6b26645a3ea1f1aec24aea8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 7 Aug 2022 18:07:02 +0400
Subject: [PATCH 479/573] add singleConstant interpolation

---
 source/mir/interpolate/constant.d | 62 ++++++++++++++++++++++++++++++-
 source/mir/interpolate/linear.d   |  4 +-
 source/mir/interpolate/spline.d   |  4 +-
 3 files changed, 65 insertions(+), 5 deletions(-)

diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 76afbf41..22f52ab7 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -162,7 +162,7 @@ extern(D):
     enum uint derivativeOrder = 0;
 
     ///
-    enum dimensionCount = N;
+    enum uint dimensionCount = N;
 
     ///
     template opCall(uint derivative = 0)
@@ -197,3 +197,63 @@ extern(D):
         }
     }
 }
+
+/++
+Single value interpolation
++/
+SingleConstant!F singleConstant(F)(const F value)
+{
+    return typeof(return)(value);
+}
+
+/// ditto
+struct SingleConstant(F)
+{
+    ///
+    enum uint derivativeOrder = 0;
+
+    ///
+    enum uint dimensionCount = 1;
+
+    ///
+    F value;
+
+    ///
+    this(F value)
+    {
+        this.value = value;
+    }
+
+    ///
+    template opCall(uint derivative = 0)
+    {
+        /++
+        `(x)` operator.
+        Complexity:
+            `O(1)`
+        +/
+        auto opCall(X...)(in X xs) scope const @trusted
+        {
+            static if (derivative == 0)
+            {
+                return value;
+            }
+            else
+            {
+                F[derivative + 1] ret = 0;
+                ret[0] = value;
+                return ret;
+            }
+        }
+    }
+}
+
+///
+@safe pure nothrow
+version (mir_test)
+unittest
+{
+    auto sc = singleConstant(34.1);
+    assert(sc(100) == 34.1);
+    assert(sc.opCall!2(100) == [34.1, 0, 0]);
+}
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 169795eb..17b1d697 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -268,7 +268,7 @@ struct Linear(F, size_t N = 1, X = F)
     enum uint derivativeOrder = 1;
 
     ///
-    enum dimensionCount = N;
+    enum uint dimensionCount = N;
 
     ///
     template opCall(uint derivative = 0)
@@ -498,7 +498,7 @@ struct MetaLinear(T, X)
     enum uint derivativeOrder = 1;
 
     ///
-    enum dimensionCount = T.dimensionCount + 1;
+    enum uint dimensionCount = T.dimensionCount + 1;
 
     ///
     template opCall(uint derivative = 0)
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index cc8cc542..0ecabe21 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -743,7 +743,7 @@ struct Spline(F, size_t N = 1, X = F)
     ///
     SplineConvexity[N] convexity;
 
-    enum dimensionCount = N;
+    enum uint dimensionCount = N;
 
 @fmamath extern(D):
 
@@ -1733,7 +1733,7 @@ struct MetaSpline(T, X)
     enum uint derivativeOrder = 3;
 
     ///
-    enum dimensionCount = T.dimensionCount + 1;
+    enum uint dimensionCount = T.dimensionCount + 1;
 
     ///
     template opCall(uint derivative = 0)

From 34c7f241637be069fd74f8b6549cd1c7219e6159 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 8 Aug 2022 00:09:13 +0400
Subject: [PATCH 480/573] extend interpolators

---
 source/mir/interpolate/constant.d    | 104 +++++++++++++++++-
 source/mir/interpolate/extrapolate.d | 154 +++++++++++++++++++++++++++
 source/mir/interpolate/linear.d      |   5 +-
 source/mir/interpolate/spline.d      |  40 ++++---
 4 files changed, 285 insertions(+), 18 deletions(-)
 create mode 100644 source/mir/interpolate/extrapolate.d

diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 22f52ab7..154c3171 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -207,7 +207,7 @@ SingleConstant!F singleConstant(F)(const F value)
 }
 
 /// ditto
-struct SingleConstant(F)
+struct SingleConstant(F, X = F)
 {
     ///
     enum uint derivativeOrder = 0;
@@ -224,6 +224,14 @@ struct SingleConstant(F)
         this.value = value;
     }
 
+
+    ///
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
+        if (dimension == 0)
+    {
+        return null;
+    }
+
     ///
     template opCall(uint derivative = 0)
     {
@@ -236,7 +244,7 @@ struct SingleConstant(F)
         {
             static if (derivative == 0)
             {
-                return value;
+                return F(value);
             }
             else
             {
@@ -257,3 +265,95 @@ unittest
     assert(sc(100) == 34.1);
     assert(sc.opCall!2(100) == [34.1, 0, 0]);
 }
+
+/++
+Interpolator used for non-rectiliner trapezoid-like greeds.
+Params:
+    grid = rc-array of interpolation grid
+    data = rc-array of interpolator-like structures
++/
+auto metaSingleConstant(T)(T data)
+{
+    import core.lifetime: move;
+    return MetaSingleConstant!T(data.move);
+}
+
+/// ditto
+struct MetaSingleConstant(T, X = double)
+    // if (T.derivativeOrder >= 1)
+{
+    import mir.interpolate.utility: DeepType;
+
+    ///
+    T data;
+
+    ///
+    this(T data)
+    {
+        import core.lifetime: move;
+        this.data = data.move;
+    }
+
+    ///
+    MetaSingleConstant lightConst()() const @property { return *cast(MetaSingleConstant*)&this; }
+
+    ///
+    immutable(X)[] gridScopeView(size_t dimension = 0)() return scope const @property @trusted
+        if (dimension == 0)
+    {
+        return null;
+    }
+
+    ///
+    enum uint derivativeOrder = 1;
+
+    static if (__traits(compiles, {enum N = T.dimensionCount;}))
+    ///
+    enum uint dimensionCount = T.dimensionCount + 1;
+
+    ///
+    template opCall(uint derivative = 0)
+    {
+        /++
+        `(x)` operator.
+        Complexity:
+            `O(log(grid.length))`
+        +/
+        auto opCall(X...)(const X xs) scope const @trusted
+            if (xs.length >= 1)
+        {
+            static if (derivative == 0)
+            {
+                return data(xs[1.. $]);
+            }
+            else
+            {
+                auto iv = data.opCall!derivative(xs[1.. $]);
+                typeof(iv)[derivative + 1] ret = 0;
+                ret[0] = iv;
+                return ret;
+            }
+        }
+    }
+}
+
+/// Ignores the first dimension parameter
+version(mir_test)
+unittest
+{
+    import mir.interpolate.linear;
+
+    auto x = [0.0, 1, 2, 3, 5];
+    auto y = [4.0, 0, 9, 23, 40];
+
+    auto g = [7.0, 10, 15];
+
+    import mir.ndslice.allocation: rcslice;
+
+    auto d = linear!double(x.rcslice!(immutable double), y.rcslice!(const double));
+
+    auto ignoresFirstDim = d.metaSingleConstant;
+
+    assert(ignoresFirstDim(9.0, 1.8) == d(1.8));
+    assert(ignoresFirstDim.opCall!1(9.0, 1.8) == [d.opCall!1(1.8), [0.0, 0.0]]);
+}
diff --git a/source/mir/interpolate/extrapolate.d b/source/mir/interpolate/extrapolate.d
new file mode 100644
index 00000000..a79f8c3d
--- /dev/null
+++ b/source/mir/interpolate/extrapolate.d
@@ -0,0 +1,154 @@
+/++
+$(H2 Constant Interpolation)
+
+See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
+Authors: Ilia Ki
+
+Macros:
+SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
++/
+module mir.interpolate.extrapolate;
+
+/++
+Constant extrapolator
++/
+ConstantExtrapolator!T constantExtrapolator(T)(T interpolator)
+{
+    import core.lifetime: move;
+    return typeof(return)(interpolator.move);
+}
+
+
+/// ditto
+struct ConstantExtrapolator(T)
+    if (__traits(hasMember, T, "gridScopeView"))
+{
+    ///
+    T data;
+
+    ///
+    this(T data)
+    {
+        import core.lifetime: move;
+        this.data = data.move;
+    }
+
+    ///
+    ConstantExtrapolator lightConst()() const @property { return *cast(ConstantExtrapolator*)&this; }
+
+    ///
+    auto gridScopeView(size_t dimension = 0)() return scope const @property @trusted
+    {
+        return data.gridScopeView!dimension;
+    }
+
+    ///
+    enum uint derivativeOrder = 1;
+
+    static if (__traits(compiles, {enum N = T.dimensionCount;}))
+    ///
+    enum uint dimensionCount = T.dimensionCount + 1;
+
+    ///
+    template opCall(uint derivative = 0)
+    {
+        /++
+        `(x)` operator.
+        Complexity:
+            `O(log(grid.length))`
+        +/
+        auto opCall(X...)(const X xs) scope const @trusted
+            if (xs.length >= 1)
+        {
+            import mir.internal.utility: Iota;
+            import mir.math.common: fmin, fmax;
+            import std.meta: staticMap;
+
+            static if (derivative)
+                bool[X.length] extrpolated;
+
+            auto mod(size_t i)()
+            {
+                static if (__traits(compiles, gridScopeView!i))
+                {
+                    auto grid = gridScopeView!i;
+                    static if (derivative)
+                        extrpolated[i] = grid.length != 0 && (xs[i] < grid[0] || grid[$ - 1] < xs[i]);
+                    return grid.length ? xs[i].fmax(grid[0]).fmin(grid[$ - 1]) : xs[i];
+                }
+                else
+                {
+                    return xs[i];
+                }
+            }
+
+            alias xm = staticMap!(mod, Iota!(X.length));
+
+            static if (derivative == 0)
+                return data(xm);
+            else
+            {
+                static assert (xs.length <= 2, "multidimensional constant exrapolation with derivatives isn't implemented");
+                auto ret = data.opCall!derivative(xm);
+                static if (xs.length == 1)
+                {
+                    if (extrpolated[0])
+                    {
+                        foreach (ref e; ret[1 .. $])
+                        {
+                            e = 0;
+                        }
+                    }
+                }
+                else
+                static if (xs.length == 2)
+                {
+                    if (extrpolated[0])
+                    {
+                        foreach (ref e; ret[1 .. $])
+                        {
+                            e = 0;
+                        }
+                    }
+
+                    if (extrpolated[1])
+                    {
+                        foreach (ref e; ret)
+                        {
+                            e[1 .. $] = 0;
+                        }
+                    }
+                }
+                return ret;
+            }
+        }
+    }
+}
+
+
+///
+version(mir_test)
+unittest
+{
+    import mir.interpolate.linear;
+
+    auto x = [0.0, 1, 2, 3, 5];
+    auto y = [4.0, 0, 9, 23, 40];
+
+    auto g = [7.0, 10, 15];
+
+    import mir.ndslice.allocation: rcslice;
+
+    auto linear = linear!double(
+        x.rcslice!(immutable double),
+        y.rcslice!(const double),
+    ).constantExtrapolator;
+
+    assert(linear(2) == 9);
+    assert(linear(-1) == 4);
+    assert(linear(100) == 40);
+}
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 17b1d697..7fe1c165 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -444,7 +444,7 @@ auto metaLinear(X, T)(RCArray!(immutable X) grid, RCArray!(const T) data)
 
 /// ditto
 struct MetaLinear(T, X)
-    if (T.derivativeOrder >= 1)
+    // if (T.derivativeOrder >= 1)
 {
     import mir.interpolate.utility: DeepType;
     // alias ElementInterpolator = Linear!(F, N, X);
@@ -497,6 +497,7 @@ struct MetaLinear(T, X)
     ///
     enum uint derivativeOrder = 1;
 
+    static if (__traits(compiles, {enum N = T.dimensionCount;}))
     ///
     enum uint dimensionCount = T.dimensionCount + 1;
 
@@ -509,7 +510,7 @@ struct MetaLinear(T, X)
             `O(log(grid.length))`
         +/
         auto opCall(X...)(const X xs) scope const @trusted
-            if (X.length == dimensionCount)
+            // if (X.length == dimensionCount)
         {
             static if (isInterval!(typeof(xs[0])))
             {
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 0ecabe21..59be1f5b 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -41,7 +41,13 @@ version(D_Exceptions)
     static immutable splineConfigurationException = new Exception(splineConfigurationMsg);
 }
 
-private alias ValueType(T, X) = typeof(T.init.opCall(Repeat!(T.dimensionCount, X.init)));
+private template ValueType(T, X)
+{
+    static if (__traits(compiles, {enum N = T.dimensionCount;}))
+        alias ValueType = typeof(T.init.opCall(Repeat!(T.dimensionCount, X.init)));
+    else
+        alias ValueType = typeof(T.init.opCall(X.init));
+}
 
 @fmamath:
 
@@ -510,7 +516,8 @@ template spline(T, size_t N = 1, X = T)
         in T valueOfBoundaryConditions = 0,
         )
     {
-        return spline(grid, values, SplineType.c2, 0, typeOfBoundaries, valueOfBoundaryConditions);
+        import core.lifetime: forward;
+        return spline(forward!grid, forward!values, SplineType.c2, 0, typeOfBoundaries, valueOfBoundaryConditions);
     }
 
     Spline!(T, N, X) spline(yIterator, SliceKind ykind)(
@@ -522,7 +529,8 @@ template spline(T, size_t N = 1, X = T)
         in T valueOfBoundaryConditions = 0,
         )
     {
-        return spline(grid, values, SplineBoundaryCondition!T(typeOfBoundaries, valueOfBoundaryConditions), kind, param);
+        import core.lifetime: forward;
+        return spline(forward!grid, forward!values, SplineBoundaryCondition!T(typeOfBoundaries, valueOfBoundaryConditions), kind, param);
     }
 
     /++
@@ -545,7 +553,8 @@ template spline(T, size_t N = 1, X = T)
         in T param = 0,
         )
     {
-        return spline(grid, values, boundaries, boundaries, kind, param);
+        import core.lifetime: forward;
+        return spline(forward!grid, forward!values, boundaries, boundaries, kind, param);
     }
 
     /++
@@ -825,7 +834,7 @@ struct Spline(F, size_t N = 1, X = F)
 
         static if (N == 1)
         {
-            convexity[0] = splineSlopes!(F, F)(_grid[0].lightConst.sliced(_data._lengths[0]), pickDataSubslice(_data.lightScope, 0), pickDataSubslice(_data.lightScope, 1), temp[0 .. _data._lengths[0]], kind, param, lBoundary, rBoundary);
+            convexity[0] = splineSlopes!(F, F)(_grid[0]._iterator.sliced(_data._lengths[0]), pickDataSubslice(_data.lightScope, 0), pickDataSubslice(_data.lightScope, 1), temp[0 .. _data._lengths[0]], kind, param, lBoundary, rBoundary);
         }
         else
         foreach_reverse(i; Iota!N)
@@ -842,7 +851,7 @@ struct Spline(F, size_t N = 1, X = F)
                         auto y = pickDataSubslice(d, l);
                         auto s = pickDataSubslice(d, L + l);
                         // debug printf("ptr = %ld, stride = %ld, stride = %ld, d = %ld i = %ld l = %ld\n", d.iterator, d._stride!0, y._stride!0, d.length, i, l);
-                        auto c = splineSlopes!(F, F)(_grid[i].sliced(_data._lengths[i]), y, s, temp[0 .. _data._lengths[i]], kind, param, lBoundary, rBoundary);
+                        auto c = splineSlopes!(F, F)(_grid[i]._iterator.sliced(_data._lengths[i]), y, s, temp[0 .. _data._lengths[i]], kind, param, lBoundary, rBoundary);
                         static if (l)
                         {
                             if (convexity[i] != c)
@@ -1083,8 +1092,8 @@ Constraints:
 Returs:
     Spline convexity type
 +/
-SplineConvexity splineSlopes(F, T, IP, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind skind)(
-    Slice!(IP, 1, gkind) points,
+SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind, SliceKind skind)(
+    Slice!(P*, 1, gkind) points,
     Slice!(IV, 1, vkind) values,
     Slice!(IS, 1, skind) slopes,
     Slice!(T*) temp,
@@ -1556,7 +1565,8 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
     const F valueOfBoundaryConditions = 0,
     )
 {
-    return metaSpline!(F, X, T)(grid, data, SplineType.c2, 0, typeOfBoundaries, valueOfBoundaryConditions);
+    import core.lifetime: move;
+    return metaSpline!(F, X, T)(grid.move, data.move, SplineType.c2, 0, typeOfBoundaries, valueOfBoundaryConditions);
 }
 
 /++
@@ -1582,7 +1592,8 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
     const F valueOfBoundaryConditions = 0,
     )
 {
-    return metaSpline!(F, X, T)(grid, data, SplineBoundaryCondition!F(typeOfBoundaries, valueOfBoundaryConditions), kind, param);
+    import core.lifetime: move;
+    return metaSpline!(F, X, T)(grid.move, data.move, SplineBoundaryCondition!F(typeOfBoundaries, valueOfBoundaryConditions), kind, param);
 }
 
 /++
@@ -1606,7 +1617,8 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
     const F param = 0,
     )
 {
-    return metaSpline!(F, X, T)(grid, data, SplineConfiguration!F(kind, boundaries, boundaries, param));
+    import core.lifetime: move;
+    return metaSpline!(F, X, T)(grid.move, data.move, SplineConfiguration!F(kind, boundaries, boundaries, param));
 }
 
 /++
@@ -1658,7 +1670,6 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
 
 /// ditto
 struct MetaSpline(T, X)
-    if (T.derivativeOrder >= 3)
 {
     import mir.interpolate.utility: DeepType;
     // alias ElementInterpolator = Linear!(F, N, X);
@@ -1732,12 +1743,13 @@ struct MetaSpline(T, X)
     ///
     enum uint derivativeOrder = 3;
 
+    static if (__traits(compiles, {enum N = T.dimensionCount;}))
     ///
     enum uint dimensionCount = T.dimensionCount + 1;
 
     ///
     template opCall(uint derivative = 0)
-        if (derivative <= derivativeOrder)
+        // if (derivative <= derivativeOrder)
     {
         /++
         `(x)` operator.
@@ -1745,7 +1757,7 @@ struct MetaSpline(T, X)
             `O(log(grid.length))`
         +/
         auto opCall(X...)(const X xs) scope const @trusted
-            if (X.length == dimensionCount)
+            // if (X.length == dimensionCount)
         {
             F[2][][derivative + 1] mutable;
 

From 1f005b8efe8f6264c5d979a637aa1017ecf66461 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 8 Aug 2022 00:10:04 +0400
Subject: [PATCH 481/573] fixup docs

---
 source/mir/interpolate/extrapolate.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/interpolate/extrapolate.d b/source/mir/interpolate/extrapolate.d
index a79f8c3d..9722dd40 100644
--- a/source/mir/interpolate/extrapolate.d
+++ b/source/mir/interpolate/extrapolate.d
@@ -1,5 +1,5 @@
 /++
-$(H2 Constant Interpolation)
+$(H2 Extrapolators)
 
 See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate)
 

From 3e310762cd36098cdff24f3477b510db2739e747 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 8 Aug 2022 12:35:54 +0400
Subject: [PATCH 482/573] fixup and extend constant extrapolator

---
 source/mir/interpolate/extrapolate.d | 54 +++++++++++++++-------------
 1 file changed, 30 insertions(+), 24 deletions(-)

diff --git a/source/mir/interpolate/extrapolate.d b/source/mir/interpolate/extrapolate.d
index 9722dd40..c100ac2d 100644
--- a/source/mir/interpolate/extrapolate.d
+++ b/source/mir/interpolate/extrapolate.d
@@ -92,37 +92,43 @@ struct ConstantExtrapolator(T)
                 return data(xm);
             else
             {
-                static assert (xs.length <= 2, "multidimensional constant exrapolation with derivatives isn't implemented");
+                static assert (xs.length <= 4, "multidimensional constant exrapolation with derivatives isn't implemented");
                 auto ret = data.opCall!derivative(xm);
-                static if (xs.length == 1)
+ 
+                static if (xs.length <= 1)
                 {
                     if (extrpolated[0])
-                    {
-                        foreach (ref e; ret[1 .. $])
-                        {
-                            e = 0;
-                        }
-                    }
+                    foreach (ref a; ret[1 .. $])
+                        a = 0;
                 }
-                else
-                static if (xs.length == 2)
-                {
-                    if (extrpolated[0])
-                    {
-                        foreach (ref e; ret[1 .. $])
-                        {
-                            e = 0;
-                        }
-                    }
 
+                static if (xs.length <= 2)
+                {
                     if (extrpolated[1])
-                    {
-                        foreach (ref e; ret)
-                        {
-                            e[1 .. $] = 0;
-                        }
-                    }
+                    foreach (ref a; ret)
+                    foreach (ref b; a[1 .. $])
+                        b = 0;
                 }
+
+                static if (xs.length <= 3)
+                {
+                    if (extrpolated[2])
+                    foreach (ref a; ret)
+                    foreach (ref b; a)
+                    foreach (ref c; b[1 .. $])
+                        c = 0;
+                }
+
+                static if (xs.length <= 4)
+                {
+                    if (extrpolated[2])
+                    foreach (ref a; ret)
+                    foreach (ref b; a)
+                    foreach (ref c; b)
+                    foreach (ref d; c[1 .. $])
+                        d = 0;
+                }
+
                 return ret;
             }
         }

From 1e9b1ea204726319ce70734351c1e93253bb68b8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 8 Aug 2022 20:10:16 +0400
Subject: [PATCH 483/573] fixup interpolate.extrapolate

---
 source/mir/interpolate/extrapolate.d | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/source/mir/interpolate/extrapolate.d b/source/mir/interpolate/extrapolate.d
index c100ac2d..b1d49a2e 100644
--- a/source/mir/interpolate/extrapolate.d
+++ b/source/mir/interpolate/extrapolate.d
@@ -62,7 +62,7 @@ struct ConstantExtrapolator(T)
             `O(log(grid.length))`
         +/
         auto opCall(X...)(const X xs) scope const @trusted
-            if (xs.length >= 1)
+            if (X.length >= 1)
         {
             import mir.internal.utility: Iota;
             import mir.math.common: fmin, fmax;
@@ -92,17 +92,17 @@ struct ConstantExtrapolator(T)
                 return data(xm);
             else
             {
-                static assert (xs.length <= 4, "multidimensional constant exrapolation with derivatives isn't implemented");
+                static assert (X.length <= 4, "multidimensional constant exrapolation with derivatives isn't implemented");
                 auto ret = data.opCall!derivative(xm);
  
-                static if (xs.length <= 1)
+                static if (X.length >= 1)
                 {
                     if (extrpolated[0])
                     foreach (ref a; ret[1 .. $])
                         a = 0;
                 }
 
-                static if (xs.length <= 2)
+                static if (X.length >= 2)
                 {
                     if (extrpolated[1])
                     foreach (ref a; ret)
@@ -110,7 +110,7 @@ struct ConstantExtrapolator(T)
                         b = 0;
                 }
 
-                static if (xs.length <= 3)
+                static if (X.length >= 3)
                 {
                     if (extrpolated[2])
                     foreach (ref a; ret)
@@ -119,7 +119,7 @@ struct ConstantExtrapolator(T)
                         c = 0;
                 }
 
-                static if (xs.length <= 4)
+                static if (X.length >= 4)
                 {
                     if (extrpolated[2])
                     foreach (ref a; ret)

From 6e38072efae666d3a0761c4c6bad9ae161cae14e Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 8 Aug 2022 20:10:38 +0400
Subject: [PATCH 484/573] add mir.interpolate.mod

---
 source/mir/interpolate/mod.d | 209 +++++++++++++++++++++++++++++++++++
 1 file changed, 209 insertions(+)
 create mode 100644 source/mir/interpolate/mod.d

diff --git a/source/mir/interpolate/mod.d b/source/mir/interpolate/mod.d
new file mode 100644
index 00000000..093cb9fe
--- /dev/null
+++ b/source/mir/interpolate/mod.d
@@ -0,0 +1,209 @@
+/++
+$(H2 Interpolation Modifier)
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Copyright: 2022 Ilia, Symmetry Investments
+Authors: Ilia Ki
+
+Macros:
+SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP)
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
++/
+module mir.interpolate.mod;
+
+import mir.math.common;
+
+/++
+Applies function to the interpolated value.
+
+Params:
+    fun = two arguments `(x, derivativeOrder)` function
++/
+template interpolationMap(alias fun)
+{
+    ///
+    auto interpolationMap(T)(T interpolator)
+    {
+        import core.lifetime: move;
+        alias S = InterpolationMap!fun;
+        return S!T(interpolator.move);
+    }
+}
+
+/// ditto
+template InterpolationMap(alias fun)
+{
+    ///
+    struct InterpolationMap(T)
+    {
+        static if (__traits(hasMember, T, "derivativeOrder"))
+            enum derivativeOrder = T.derivativeOrder;
+
+        static if (__traits(hasMember, T, "dimensionCount"))
+            enum uint dimensionCount = T.dimensionCount;
+
+        ///
+        T interpolator;
+
+        ///
+        this(T interpolator)
+        {
+            import core.lifetime: move;
+            this.interpolator = interpolator.move;
+        }
+
+        ///
+        template opCall(uint derivative = 0)
+            // if (derivative <= derivativeOrder)
+        {
+            /++
+            `(x)` operator.
+            Complexity:
+                `O(log(grid.length))`
+            +/
+            auto opCall(X...)(const X xs) scope const @trusted
+                // if (X.length == dimensionCount)
+            {
+                auto g = interpolator.opCall!derivative(xs);
+
+                static if (derivative == 0)
+                {
+                    typeof(g)[1] ret;
+                    fun(g, ret);
+                    return ret[0];
+                }
+                else
+                {
+                    static if (X.length == 1)
+                        auto g0 = g[0];
+                    else
+                    static if (X.length == 2)
+                        auto g0 = g[0][0];
+                    else
+                    static if (X.length == 3)
+                        auto g0 = g[0][0][0];
+                    else
+                    static assert(0, "Not implemented");
+
+                    typeof(g0)[derivative + 1] f;
+
+                    fun(g0, f);
+
+                    static if (X.length == 1)
+                    {
+                        typeof(g) r;
+                        r[0] = f[0];
+                        r[1] = f[1] * g[1];
+
+                        static if (derivative >= 2)
+                        {
+                            r[2] = f[2] * (g[1] * g[1]) + f[1] * g[2];
+                        }
+                        static if (derivative >= 3)
+                        {
+                            r[3] = f[3] * (g[1] * g[1] * g[1]) + f[1] * g[3] + 3 * (f[2] * g[1] * g[2]);
+                        }
+                        static if (derivative >= 4)
+                        {
+                            static assert(0, "Not implemented");
+                        }
+
+                        return r;
+                    } else static assert(0, "Not implemented");
+                }
+            }
+        }
+    }
+}
+
+///
+version (mir_test)
+unittest
+{
+    import mir.interpolate.spline;
+    import mir.math.common: log;
+    import mir.ndslice.allocation: rcslice;
+    import mir.test;
+
+    alias g = (double x, uint d = 0) =>
+        d == 0 ? 3 * x ^^ 3 + 5 * x ^^ 2 + 0.23 * x + 2 :
+        d == 1 ? 9 * x ^^ 2 + 10 * x + 0.23 :
+        double.nan;
+    
+    alias f = (double x, ref scope y)
+    {
+        y[0] = log(x);
+        static if (y.length >= 2)
+            y[1] = 1 / x;
+        static if (y.length >= 3)
+            y[2] = -y[1] * y[1];
+        static if (y.length >= 4)
+            y[3] = -2 * y[1] * y[2];
+        static if (y.length >= 5)
+            static assert(0, "Not implemented");
+    };
+
+    auto s = spline!double(
+        [0.1, 0.4, 0.5, 1.0].rcslice!(immutable double),
+        [g(0.1), g(0.4), g(0.5), g(1.0)].rcslice!(const double)
+    );
+
+    auto m = s.interpolationMap!f;
+
+    m(0.7).shouldApprox == log(g(0.7));
+    auto d = m.opCall!3(0.7);
+    d[0].shouldApprox == log(g(0.7));
+    d[1].shouldApprox == 1 / g(0.7) * g(0.7, 1);
+    d[2].shouldApprox == -0.252301;
+    d[3].shouldApprox == -4.03705;
+}
+
+private alias implSqrt = (x, ref scope y)
+{
+    import mir.math.common: sqrt;
+    y[0] = sqrt(x);
+    static if (y.length >= 2)
+        y[1] = 0.5f / y[0];
+    static if (y.length >= 3)
+        y[2] = -0.5f * y[1] / x;
+    static if (y.length >= 4)
+        y[3] = -1.5f * y[2] / x;
+    static if (y.length >= 5)
+        static assert(0, "Not implemented");
+};
+
+/++
+Applies square root function to the interpolated value.
++/
+alias interpolationSqrt = interpolationMap!implSqrt;
+/// ditto
+alias InterpolationSqrt = InterpolationMap!implSqrt;
+
+///
+version (mir_test)
+unittest
+{
+    import mir.interpolate.spline;
+    import mir.math.common: sqrt;
+    import mir.ndslice.allocation: rcslice;
+    import mir.test;
+
+    alias g = (double x, uint d = 0) =>
+        d == 0 ? 3 * x ^^ 3 + 5 * x ^^ 2 + 0.23 * x + 2 :
+        d == 1 ? 9 * x ^^ 2 + 10 * x + 0.23 :
+        double.nan;
+    
+    auto s = spline!double(
+        [0.1, 0.4, 0.5, 1.0].rcslice!(immutable double),
+        [g(0.1), g(0.4), g(0.5), g(1.0)].rcslice!(const double)
+    );
+
+    auto m = s.interpolationSqrt;
+
+    m(0.7).shouldApprox == sqrt(g(0.7));
+    auto d = m.opCall!3(0.7);
+    d[0].shouldApprox == sqrt(g(0.7));
+    d[1].shouldApprox == 0.5 / sqrt(g(0.7)) * g(0.7, 1);
+    d[2].shouldApprox == 2.2292836438189414;
+    d[3].shouldApprox == -3.11161;
+}

From 84e56b83001d055192b5f455cb33a0457c0070fd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 8 Aug 2022 20:10:42 +0400
Subject: [PATCH 485/573] update meson

---
 meson.build | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/meson.build b/meson.build
index b1090242..284d258b 100644
--- a/meson.build
+++ b/meson.build
@@ -38,8 +38,10 @@ sources_list = [
     'mir/graph/package',
     'mir/graph/tarjan',
     'mir/interpolate/constant',
+    'mir/interpolate/extrapolate',
     'mir/interpolate/generic',
     'mir/interpolate/linear',
+    'mir/interpolate/mod',
     'mir/interpolate/package',
     'mir/interpolate/polynomial',
     'mir/interpolate/spline',

From 76b43538ae58e55bc6ac5f14839f85729f709223 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 15 Aug 2022 16:09:45 +0700
Subject: [PATCH 486/573] add LinearExtrapolator

---
 source/mir/interpolate/extrapolate.d | 127 +++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/source/mir/interpolate/extrapolate.d b/source/mir/interpolate/extrapolate.d
index b1d49a2e..d7c6d442 100644
--- a/source/mir/interpolate/extrapolate.d
+++ b/source/mir/interpolate/extrapolate.d
@@ -157,4 +157,131 @@ unittest
     assert(linear(2) == 9);
     assert(linear(-1) == 4);
     assert(linear(100) == 40);
+
+    assert(linear.opCall!1(-1) == [4, 0]);
+
+}
+
+/++
+Linear extrapolator.
++/
+LinearExtrapolator!T linearExtrapolator(T)(T interpolator)
+{
+    import core.lifetime: move;
+    return typeof(return)(interpolator.move);
+}
+
+
+/// ditto
+struct LinearExtrapolator(T)
+    if (__traits(hasMember, T, "gridScopeView"))
+{
+    ///
+    T data;
+
+    ///
+    this(T data)
+    {
+        import core.lifetime: move;
+        this.data = data.move;
+    }
+
+    ///
+    LinearExtrapolator lightConst()() const @property { return *cast(LinearExtrapolator*)&this; }
+
+    ///
+    auto gridScopeView(size_t dimension = 0)() return scope const @property @trusted
+    {
+        return data.gridScopeView!dimension;
+    }
+
+    ///
+    enum uint derivativeOrder = 1;
+
+    static if (__traits(compiles, {enum N = T.dimensionCount;}))
+    ///
+    enum uint dimensionCount = T.dimensionCount + 1;
+
+    ///
+    template opCall(uint derivative = 0)
+    {
+        /++
+        `(x)` operator.
+        Complexity:
+            `O(log(grid.length))`
+        +/
+        auto opCall(X...)(const X xs) scope const @trusted
+            if (X.length >= 1)
+        {
+            import mir.internal.utility: Iota;
+            import mir.math.common: fmin, fmax;
+            import std.meta: staticMap;
+
+            bool[X.length] extrpolated;
+
+            auto mod(size_t i)()
+            {
+                static if (__traits(compiles, gridScopeView!i))
+                {
+                    auto grid = gridScopeView!i;
+                    extrpolated[i] = grid.length != 0 && (xs[i] < grid[0] || grid[$ - 1] < xs[i]);
+                    return grid.length ? xs[i].fmax(grid[0]).fmin(grid[$ - 1]) : xs[i];
+                }
+                else
+                {
+                    return xs[i];
+                }
+            }
+
+            alias xm = staticMap!(mod, Iota!(X.length));
+
+            import mir.utility: max;
+
+            static assert (X.length <= 2, "multidimensional linear exrapolation with derivatives isn't implemented");
+            auto ret = data.opCall!(derivative.max(1u))(xm);
+
+            static if (X.length >= 1)
+            {
+                if (extrpolated[0])
+                {
+                    ret[0] += ret[1] * (xs[0] - xm[0]);
+                    foreach (ref a; ret[2 .. $])
+                        a = 0;
+                }
+            }
+
+            static if (derivative == 0)
+                return ret[0];
+            else
+                return ret;
+        }
+    }
+}
+
+
+///
+version(mir_test)
+unittest
+{
+    import mir.test;
+    import mir.interpolate.linear;
+
+    auto x = [0.0, 1, 2, 3, 5];
+    auto y = [4.0, 0, 9, 23, 40];
+
+    auto g = [7.0, 10, 15];
+
+    import mir.ndslice.allocation: rcslice;
+
+    auto linear = linear!double(
+        x.rcslice!(immutable double),
+        y.rcslice!(const double),
+    );
+
+    auto linearLinear = linear.linearExtrapolator;
+
+    linearLinear(2).should == linear(2);
+    linearLinear(-1).should == linear(-1);
+    linearLinear.opCall!1(-1).should == [8, -4];
+    linearLinear(100).shouldApprox == linear(100);
 }

From 5a465360f7c16033b3136473089b1dfe0646dd14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Fri, 19 Aug 2022 05:49:06 +0200
Subject: [PATCH 487/573] =?UTF-8?q?In=20ScopedBuffer=20add=20reserve()=20a?=
 =?UTF-8?q?nd=20make=20=5FcurrentLength=20private=20and=20add=E2=80=A6=20(?=
 =?UTF-8?q?#440)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* In ScopedBuffer add reserve() and make _currentLength private and add check to prepare()

* Improve test

* Make _currentLength public again

* Express reserve() using prepare() and comment both
---
 source/mir/appender.d | 43 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 4 deletions(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index 9da76dc5..49ed7ced 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -38,7 +38,14 @@ struct ScopedBuffer(T, size_t bytes = 4096)
         return *cast(inout(T[_bufferLength])*)&_scopeBufferPayload;
     }
 
-    ///
+    /// Reserve `n` more elements.
+    void reserve(size_t n) @safe scope
+    {
+        prepare(n);
+        _currentLength -= n;
+    }
+
+    /// Return a slice to `n` more elements.
     T[] prepare(size_t n) @trusted scope
     {
         import mir.internal.memory: realloc, malloc;
@@ -51,7 +58,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
             }
             else
             {
-                auto newLen = _currentLength << 1;
+                const newLen = _currentLength << 1;
                 if (auto p = malloc(T.sizeof * newLen))
                 {
                     _buffer = (cast(T*)p)[0 .. newLen];
@@ -67,8 +74,12 @@ struct ScopedBuffer(T, size_t bytes = 4096)
         else
         if (_currentLength > _buffer.length)
         {
-            auto newLen = _currentLength << 1;
-            _buffer = (cast(T*)realloc(cast(void*)_buffer.ptr, T.sizeof * newLen))[0 .. newLen];
+            const newLen = _currentLength << 1;
+            if (auto p = realloc(cast(void*)_buffer.ptr, T.sizeof * newLen))
+            {
+                _buffer = (cast(T*)p)[0 .. newLen];
+            }
+            else assert(0);
             version (mir_secure_memory)
             {
                 (cast(ubyte[])_buffer[_currentLength .. $])[] = 0;
@@ -199,6 +210,9 @@ struct ScopedBuffer(T, size_t bytes = 4096)
         }
     }
 
+    ///
+    alias opOpAssign(string op : "~") = put;
+
     ///
     void reset() scope nothrow
     {
@@ -280,6 +294,27 @@ version (mir_test) unittest
     assert(buf.data == "cs");
 }
 
+@safe pure nothrow @nogc
+version (mir_test) unittest
+{
+    alias T = char;
+    const n = 3;
+
+    auto buf = scopedBuffer!(T, n * T.sizeof);
+    assert(buf._scopeBuffer.length == n); // stack
+    assert(buf._buffer.length == 0);      // unset
+
+    buf.reserve(n + 1);                   // transition to heap
+    assert(buf._buffer.length >= n + 1); // heap
+
+    buf ~= 'c';
+    buf ~= "str";
+    assert(buf.data == "cstr");
+
+    buf.popBackN(2);
+    assert(buf.data == "cs");
+}
+
 ///
 struct UnsafeArrayBuffer(T)
 {

From e1d32a7a4730d2b4824d060e488a2219c2727ea8 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 21 Aug 2022 21:28:49 +0700
Subject: [PATCH 488/573] extend findLocalMin

---
 source/mir/numeric.d | 62 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 55 insertions(+), 7 deletions(-)

diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 86ad7faa..10f1d9d4 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1162,6 +1162,11 @@ Params:
     bx = Right bound of initial range of f known to contain the minimum.
     relTolerance = Relative tolerance.
     absTolerance = Absolute tolerance.
+    N = number of addition inner points of uniform grid.
+        The algorithm computes function value for the N points.
+        Then reassing ax to the point that preceeds the grid's argmin,
+        reassing bx to the point that follows the the grid's argmin.
+        The search interval reduces (N + 1) / 2 times.
 Preconditions:
     `ax` and `bx` shall be finite reals. $(BR)
     `relTolerance` shall be normal positive real. $(BR)
@@ -1179,7 +1184,9 @@ FindLocalMinResult!T findLocalMin(alias f, T)(
     const T ax,
     const T bx,
     const T relTolerance = sqrt(T.epsilon),
-    const T absTolerance = sqrt(T.epsilon))
+    const T absTolerance = sqrt(T.epsilon),
+    size_t N = 0,
+)
     if (isFloatingPoint!T && __traits(compiles, T(f(T.init))))
 {
     if (false) // break attributes
@@ -1191,7 +1198,7 @@ FindLocalMinResult!T findLocalMin(alias f, T)(
     };
     scope fun = funInst.trustedAllAttr;
 
-    return findLocalMinImpl(ax, bx, relTolerance, absTolerance, fun);
+    return findLocalMinImpl(ax, bx, relTolerance, absTolerance, fun, N);
 }
 
 @fmamath
@@ -1201,10 +1208,11 @@ private FindLocalMinResult!float findLocalMinImpl(
     const float relTolerance,
     const float absTolerance,
     scope const float delegate(float) @safe pure nothrow @nogc f,
+    size_t N,
     ) @safe pure nothrow @nogc
 {
     pragma(inline, false);
-    return findLocalMinImplGen!float(ax, bx, relTolerance, absTolerance, f);
+    return findLocalMinImplGen!float(ax, bx, relTolerance, absTolerance, f, N);
 }
 
 @fmamath
@@ -1214,10 +1222,11 @@ private FindLocalMinResult!double findLocalMinImpl(
     const double relTolerance,
     const double absTolerance,
     scope const double delegate(double) @safe pure nothrow @nogc f,
+    size_t N,
     ) @safe pure nothrow @nogc
 {
     pragma(inline, false);
-    return findLocalMinImplGen!double(ax, bx, relTolerance, absTolerance, f);
+    return findLocalMinImplGen!double(ax, bx, relTolerance, absTolerance, f, N);
 }
 
 @fmamath
@@ -1227,19 +1236,21 @@ private FindLocalMinResult!real findLocalMinImpl(
     const real relTolerance,
     const real absTolerance,
     scope const real delegate(real) @safe pure nothrow @nogc f,
+    size_t N,
     ) @safe pure nothrow @nogc
 {
     pragma(inline, false);
-    return findLocalMinImplGen!real(ax, bx, relTolerance, absTolerance, f);
+    return findLocalMinImplGen!real(ax, bx, relTolerance, absTolerance, f, N);
 }
 
 @fmamath
 private FindLocalMinResult!T findLocalMinImplGen(T)(
-    const T ax,
-    const T bx,
+    T ax,
+    T bx,
     const T relTolerance,
     const T absTolerance,
     scope const T delegate(T) @safe pure nothrow @nogc f,
+    size_t N,
     )
     if (isFloatingPoint!T && __traits(compiles, {T _ = f(T.init);}))
 in
@@ -1251,6 +1262,26 @@ in
 }
 do
 {
+    if (N > 1)
+    {
+        import mir.ndslice.topology: linspace;
+
+        auto xPoints = linspace!T([N + 2], [ax, bx]);
+        size_t idx;
+        double value = double.infinity;
+        foreach (i; 0 .. N)
+        {
+            auto y = f(xPoints[i + 1]);
+            if (y < value)
+            {
+                value = y;
+                idx = i;
+            }
+        }
+        ax = xPoints[idx + 0];
+        bx = xPoints[idx + 2];
+    }
+
     version(LDC) pragma(inline, true);
     // c is the squared inverse of the golden ratio
     // (3 - sqrt(5))/2
@@ -1417,6 +1448,23 @@ version(mir_test) @safe unittest
     }
 }
 
+/// Tries to find a global minimum using uniform grid to reduce the search interval
+version(mir_test)
+unittest
+{
+    import mir.math.common: cos;
+    import mir.test;
+
+    alias f = x => cos(x)  + x / 10;
+
+    findLocalMin!f(-4.0, 6.0, double.epsilon, 2 * double.epsilon)
+        .validate.x.shouldApprox == 3.041425233955413;
+    // Use 10 inner points on uniform grid to reduce the interval
+    // reduces the interval of search 5.5 = (10 + 1) / 2  times
+    findLocalMin!f(-4.0, 6.0, double.epsilon, 2 * double.epsilon, 10)
+        .validate.x.shouldApprox == -3.2417600767368255;
+}
+
 /++
 A set of one or two smile roots.
 

From 11d29402a6dab2d232da69cf731783179e8152cc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 22 Aug 2022 00:06:32 +0700
Subject: [PATCH 489/573] add Spline.argmin

---
 source/mir/interpolate/spline.d | 113 +++++++++++++++++++++++++++++++-
 1 file changed, 112 insertions(+), 1 deletion(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 59be1f5b..20c1a008 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -237,6 +237,7 @@ private template ValueType(T, X)
 version(mir_test)
 @safe unittest
 {
+    import mir.test;
     import mir.algorithm.iteration: all;
     import mir.math.common: approxEqual;
     import mir.ndslice;
@@ -386,6 +387,7 @@ unittest
 version(mir_test)
 @safe unittest
 {
+    import mir.test;
     import mir.math.common: approxEqual;
     import mir.algorithm.iteration: all;
     import mir.ndslice.allocation: rcslice;
@@ -395,6 +397,8 @@ version(mir_test)
     static immutable x = [1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22];
     static immutable y = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
     auto interpolant = spline!double(x.rcslice, y.sliced, SplineType.monotone);
+    interpolant.argmin.should == 2;
+    interpolant.argmin!"a > b".should == 12;
 
     auto xs = x[0 .. $ - 1].sliced + 0.5;
 
@@ -446,6 +450,29 @@ version(mir_test)
     assert(vmap(points[0 .. $ - 1].sliced +  0.5, interpolant) == vmap(pointsR.retro[0 .. $ - 1] - 0.5, interpolantR));
 }
 
+/// argmin test
+version(mir_test)
+unittest
+{
+    import mir.test;
+    import mir.ndslice.slice: sliced;
+    import mir.ndslice.allocation: rcslice;
+
+    static immutable points = [1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22];
+    static immutable values = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
+    auto interpolant = spline!double(points.rcslice, values.sliced);
+
+    auto argmin = 5.898317667706634;
+    interpolant.argmin.shouldApprox == argmin;
+    interpolant(argmin).shouldApprox == -0.8684781710737299;
+    interpolant.opCall!2(argmin)[1].shouldApprox == 0;
+
+    auto argmax = 19.8816211020945;
+    interpolant.argmin!"a > b".shouldApprox == argmax;
+    interpolant.opCall!1(argmax)[1].shouldApprox == 0;
+    interpolant(argmax).shouldApprox == 19.54377309088673;
+}
+
 /++
 Cubic Spline types.
 
@@ -794,6 +821,90 @@ struct Spline(F, size_t N = 1, X = F)
         return Slice!(F*, strides.length, Universal)(data.shape, strides, data._iterator.ptr + index);
     }
 
+    static if (N == 1)
+    /++
+    Returns: spline argmin on the interpolation interval
+    Note: defined only for 1D splines
+    +/
+    F argmin(string pred = "a < b")() @trusted const @property
+        if (pred == "a < b" || pred == "a > b")
+    {
+        import mir.functional: naryFun;
+
+        static if (pred == "a < b")
+            auto min = F.max;
+        else
+            auto min = -F.max;
+
+        F argmin;
+
+        auto grid = gridScopeView;
+        foreach (i, ref y; _data.lightScope.field)
+        {
+            if (naryFun!pred(y[0], min))
+            {
+                min = y[0];
+                argmin = grid[i];
+            }
+        }
+
+        foreach (i; 0 .. grid.length - 1)
+        {
+            auto x = grid[i + 0];
+
+            auto y = SplineKernel!F(
+                grid[i + 0],
+                grid[i + 1],
+                x, // any point between
+            ).opCall!3(
+                _data[i + 0][0],
+                _data[i + 1][0],
+                _data[i + 0][1],
+                _data[i + 1][1],
+            );
+
+            // 3 ax^2 + 2 bx + c = y[1]
+            // 6 ax + 2 b= y[2]
+            // 6 a = y[3]
+
+            auto a3 = y[3] * 0.5;
+            auto b2 = y[2] - y[3] * x;
+            auto c = y[1] - (b2 + a3 * x) * x;
+            auto d = b2 * b2 - 4 * a3 * c;
+            if (d < 0)
+                continue;
+
+            import mir.math.common: sqrt;
+            F[2] x12 = [
+                (-b2 - sqrt(d)) / (2 * a3),
+                (-b2 + sqrt(d)) / (2 * a3),
+            ];
+
+            foreach (xi; x12)
+            if (grid[i + 0] < xi && xi < grid[i + 1])
+            {
+                auto yi = SplineKernel!F(
+                    grid[i + 0],
+                    grid[i + 1],
+                    xi,
+                )(
+                    _data[i + 0][0],
+                    _data[i + 1][0],
+                    _data[i + 0][1],
+                    _data[i + 1][1],
+                );
+
+                if (naryFun!pred(yi, min))
+                {
+                    min = yi;
+                    argmin = xi;
+                }
+            }
+        }
+
+        return argmin;
+    }
+
     /++
     Assigns function values to the internal memory.
     $(RED For internal use.)
@@ -1400,7 +1511,7 @@ SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind,
     size_t concaveCount;
     foreach_reverse (i; 0 .. n - 1)
     {
-        auto c = i ==     0 ? first : kind == SplineType.c2 ? xd[i - 1] : 0;
+        auto c = i == 0 ? first : kind == SplineType.c2 ? xd[i - 1] : 0;
         auto v = temp[i] - c * slopes[i + 1];
         slopes[i] = slopes[i]  == 1 ? v : v / slopes[i];
 

From 8c2ecfc90c6576a4aaa38a8dc4d5c4665b2e2637 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 22 Aug 2022 01:14:47 +0700
Subject: [PATCH 490/573] add spline binary ops

---
 source/mir/interpolate/spline.d | 74 ++++++++++++++++++++++++++++++++-
 1 file changed, 73 insertions(+), 1 deletion(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 20c1a008..b5c40f55 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -450,7 +450,7 @@ version(mir_test)
     assert(vmap(points[0 .. $ - 1].sliced +  0.5, interpolant) == vmap(pointsR.retro[0 .. $ - 1] - 0.5, interpolantR));
 }
 
-/// argmin test
+/// argmin, +, - tests
 version(mir_test)
 unittest
 {
@@ -471,6 +471,18 @@ unittest
     interpolant.argmin!"a > b".shouldApprox == argmax;
     interpolant.opCall!1(argmax)[1].shouldApprox == 0;
     interpolant(argmax).shouldApprox == 19.54377309088673;
+
+    auto zeroInterpolant = interpolant - interpolant;
+    zeroInterpolant.opCall!3(13.3).should == [0.0, 0.0, 0.0, 0.0];
+
+    static immutable pointsR = [1.0, 3, 4,];
+    static immutable valuesR = [13.0, 12, 10];
+    auto interpolantR = spline!double(pointsR.rcslice, valuesR.sliced);
+
+    auto sumInterpolant = interpolant + interpolantR;
+
+    sumInterpolant(2.3).shouldApprox == interpolant(2.3) + interpolantR(2.3);
+    sumInterpolant(3.3).shouldApprox == interpolant(3.3) + interpolantR(3.3);
 }
 
 /++
@@ -821,6 +833,66 @@ struct Spline(F, size_t N = 1, X = F)
         return Slice!(F*, strides.length, Universal)(data.shape, strides, data._iterator.ptr + index);
     }
 
+    static if (N == 1)
+    /++
+    Note: defined only for 1D splines
+    +/
+    Spline opBinary(string op)(const Spline rhs) @trusted const
+        if (op == "+" || op == "-")
+    {
+        import mir.ndslice.allocation: rcslice;
+        import core.lifetime: move;
+        import mir.algorithm.setops: unionLength, multiwayUnion;
+
+        auto lgrid = this.gridScopeView;
+        auto rgrid = rhs.gridScopeView;
+        scope const(F)[][2] grids = [lgrid, rgrid];
+        auto length = grids[].unionLength;
+        grids = [lgrid, rgrid];
+
+        size_t j;
+        auto grid = RCArray!X(length);
+        auto data = length.rcslice!(F[2]);
+        auto un = grids[].multiwayUnion;
+
+        while (!un.empty)
+        {
+            auto x = un.front;
+            un.popFront;
+            auto ly = this.opCall!1(x);
+            auto ry = rhs.opCall!1(x);
+            data[j] = mixin(`[ly[0] ` ~ op ~ ` ry[0], ly[1] ` ~ op ~ ` ry[1]]`);
+            grid[j++] = x;
+        }
+
+        size_t convexCount;
+        size_t concaveCount;
+        foreach_reverse (i; 0 .. length - 1)
+        {
+            auto xdiff = grid[i + 1] - grid[i];
+            auto ydiff = data[i + 1][0] - data[i][0];
+
+            auto convex1  = xdiff * (2 * data[i][1] + data[i + 1][1]) <= 3 * ydiff;
+            auto concave1 = xdiff * (2 * data[i][1] + data[i + 1][1]) >= 3 * ydiff;
+            auto convex2  = xdiff * (data[i][1] + 2 * data[i + 1][1]) >= 3 * ydiff;
+            auto concave2 = xdiff * (data[i][1] + 2 * data[i + 1][1]) <= 3 * ydiff;
+            convexCount += convex1 & convex2;
+            convexCount += concave1 & concave2;
+        }
+
+        Spline ret;
+        ret._data = data.move;
+        ret._grid[0] = RCI!(immutable X)(cast(RCArray!(immutable X))grid) ;
+
+        ret.convexity =
+            // convex kind has priority for the linear spline
+            convexCount  == length - 1 ? SplineConvexity.convex  :
+            concaveCount == length - 1 ? SplineConvexity.concave :
+            SplineConvexity.none;
+
+        return ret;
+    }
+
     static if (N == 1)
     /++
     Returns: spline argmin on the interpolation interval

From f48deddc2497e36e681632630742d88b3ac4db30 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 22 Aug 2022 14:19:47 +0700
Subject: [PATCH 491/573] improve spline API

---
 source/mir/interpolate/spline.d | 141 ++++++++++++++++++--------------
 1 file changed, 78 insertions(+), 63 deletions(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index b5c40f55..0c2f0f80 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -398,7 +398,9 @@ version(mir_test)
     static immutable y = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6];
     auto interpolant = spline!double(x.rcslice, y.sliced, SplineType.monotone);
     interpolant.argmin.should == 2;
-    interpolant.argmin!"a > b".should == 12;
+    interpolant.withDerivative(2)[1].should == 0;
+    interpolant.argmax.should == 12;
+    interpolant.withDerivative(12)[1].should == 0;
 
     auto xs = x[0 .. $ - 1].sliced + 0.5;
 
@@ -468,8 +470,8 @@ unittest
     interpolant.opCall!2(argmin)[1].shouldApprox == 0;
 
     auto argmax = 19.8816211020945;
-    interpolant.argmin!"a > b".shouldApprox == argmax;
-    interpolant.opCall!1(argmax)[1].shouldApprox == 0;
+    interpolant.argmax.shouldApprox == argmax;
+    interpolant.withDerivative(argmax)[1].shouldApprox == 0;
     interpolant(argmax).shouldApprox == 19.54377309088673;
 
     auto zeroInterpolant = interpolant - interpolant;
@@ -894,89 +896,102 @@ struct Spline(F, size_t N = 1, X = F)
     }
 
     static if (N == 1)
-    /++
-    Returns: spline argmin on the interpolation interval
-    Note: defined only for 1D splines
-    +/
-    F argmin(string pred = "a < b")() @trusted const @property
+    private template argminImpl(string pred)
         if (pred == "a < b" || pred == "a > b")
     {
-        import mir.functional: naryFun;
+        F argminImpl()() @trusted const @property
+        {
+            import mir.functional: naryFun;
 
-        static if (pred == "a < b")
-            auto min = F.max;
-        else
-            auto min = -F.max;
+            static if (pred == "a < b")
+                auto min = F.max;
+            else
+                auto min = -F.max;
 
-        F argmin;
+            F argmin;
 
-        auto grid = gridScopeView;
-        foreach (i, ref y; _data.lightScope.field)
-        {
-            if (naryFun!pred(y[0], min))
+            auto grid = gridScopeView;
+            foreach (i, ref y; _data.lightScope.field)
             {
-                min = y[0];
-                argmin = grid[i];
+                if (naryFun!pred(y[0], min))
+                {
+                    min = y[0];
+                    argmin = grid[i];
+                }
             }
-        }
 
-        foreach (i; 0 .. grid.length - 1)
-        {
-            auto x = grid[i + 0];
-
-            auto y = SplineKernel!F(
-                grid[i + 0],
-                grid[i + 1],
-                x, // any point between
-            ).opCall!3(
-                _data[i + 0][0],
-                _data[i + 1][0],
-                _data[i + 0][1],
-                _data[i + 1][1],
-            );
-
-            // 3 ax^2 + 2 bx + c = y[1]
-            // 6 ax + 2 b= y[2]
-            // 6 a = y[3]
-
-            auto a3 = y[3] * 0.5;
-            auto b2 = y[2] - y[3] * x;
-            auto c = y[1] - (b2 + a3 * x) * x;
-            auto d = b2 * b2 - 4 * a3 * c;
-            if (d < 0)
-                continue;
-
-            import mir.math.common: sqrt;
-            F[2] x12 = [
-                (-b2 - sqrt(d)) / (2 * a3),
-                (-b2 + sqrt(d)) / (2 * a3),
-            ];
-
-            foreach (xi; x12)
-            if (grid[i + 0] < xi && xi < grid[i + 1])
+            foreach (i; 0 .. grid.length - 1)
             {
-                auto yi = SplineKernel!F(
+                auto x = grid[i + 0];
+
+                auto y = SplineKernel!F(
                     grid[i + 0],
                     grid[i + 1],
-                    xi,
-                )(
+                    x, // any point between
+                ).opCall!3(
                     _data[i + 0][0],
                     _data[i + 1][0],
                     _data[i + 0][1],
                     _data[i + 1][1],
                 );
 
-                if (naryFun!pred(yi, min))
+                // 3 ax^2 + 2 bx + c = y[1]
+                // 6 ax + 2 b= y[2]
+                // 6 a = y[3]
+
+                auto a3 = y[3] * 0.5;
+                auto b2 = y[2] - y[3] * x;
+                auto c = y[1] - (b2 + a3 * x) * x;
+                auto d = b2 * b2 - 4 * a3 * c;
+                if (d < 0)
+                    continue;
+
+                import mir.math.common: sqrt;
+                F[2] x12 = [
+                    (-b2 - sqrt(d)) / (2 * a3),
+                    (-b2 + sqrt(d)) / (2 * a3),
+                ];
+
+                foreach (xi; x12)
+                if (grid[i + 0] < xi && xi < grid[i + 1])
                 {
-                    min = yi;
-                    argmin = xi;
+                    auto yi = SplineKernel!F(
+                        grid[i + 0],
+                        grid[i + 1],
+                        xi,
+                    )(
+                        _data[i + 0][0],
+                        _data[i + 1][0],
+                        _data[i + 0][1],
+                        _data[i + 1][1],
+                    );
+
+                    if (naryFun!pred(yi, min))
+                    {
+                        min = yi;
+                        argmin = xi;
+                    }
                 }
             }
-        }
 
-        return argmin;
+            return argmin;
+        }
     }
 
+    static if (N == 1)
+    /++
+    Returns: spline argmin on the interpolation interval
+    Note: defined only for 1D splines
+    +/
+    alias argmin = argminImpl!"a < b";
+
+    static if (N == 1)
+    /++
+    Returns: spline argmax on the interpolation interval
+    Note: defined only for 1D splines
+    +/
+    alias argmax = argminImpl!"a > b";
+
     /++
     Assigns function values to the internal memory.
     $(RED For internal use.)

From e83d4ed622cdfdcd4ef16f3446072dc7e372c9c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Per=20Nordl=C3=B6w?= <nordlow@users.noreply.github.com>
Date: Mon, 22 Aug 2022 10:31:21 +0200
Subject: [PATCH 492/573] Add new dub configuration unittest-verbose appending
 compiler dflags "-checkaction=context" "-allinst" (#417)

---
 dub.sdl | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/dub.sdl b/dub.sdl
index 0b4bf179..394ba94f 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -16,6 +16,14 @@ buildType "unittest" {
     versions "mir_test"
     dflags "-lowmem"
 }
+
+buildType "unittest-verbose" {
+    buildOptions "unittests" "debugMode" "debugInfo"
+    versions "mir_bignum_test" "mir_bignum_test_llv"
+    // versions "mir_ndslice_test"
+    versions "mir_test"
+    dflags "-lowmem" "-checkaction=context" "-allinst"
+}
 buildType "unittest-dip1008" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_ndslice_test" "mir_test" "mir_bignum_test_llv"

From 555ea1f8dc5f8377fe0333472826db01643b7040 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 22 Aug 2022 15:36:28 +0700
Subject: [PATCH 493/573] fixup private alias

---
 source/mir/interpolate/spline.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 0c2f0f80..9692b12f 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -896,7 +896,7 @@ struct Spline(F, size_t N = 1, X = F)
     }
 
     static if (N == 1)
-    private template argminImpl(string pred)
+    template argminImpl(string pred)
         if (pred == "a < b" || pred == "a > b")
     {
         F argminImpl()() @trusted const @property

From bbfcbd83d9cc52fb1198135dd0467b889dd606ad Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 23 Aug 2022 07:45:04 -0400
Subject: [PATCH 494/573] Add hermite polynomial coefficients (#439)

* Add hermite polynomials

* Fixup hermite documentation

* Change Hermitian to recurrence

* Fixup Hermite naming

* Fixup

* Rework hermite templates as functions, ensure works with @nogc CTFE
---
 meson.build                    |   1 +
 source/mir/math/func/hermite.d | 182 +++++++++++++++++++++++++++++++++
 2 files changed, 183 insertions(+)
 create mode 100644 source/mir/math/func/hermite.d

diff --git a/meson.build b/meson.build
index 284d258b..263170b8 100644
--- a/meson.build
+++ b/meson.build
@@ -48,6 +48,7 @@ sources_list = [
     'mir/interpolate/utility',
     'mir/lob',
     'mir/math/func/expdigamma',
+    'mir/math/func/hermite',
     'mir/math/func/normal',
     'mir/math/numeric',
     'mir/math/stat',
diff --git a/source/mir/math/func/hermite.d b/source/mir/math/func/hermite.d
new file mode 100644
index 00000000..ce5763ba
--- /dev/null
+++ b/source/mir/math/func/hermite.d
@@ -0,0 +1,182 @@
+/++
+Hermite Polynomial Coefficients
+
+License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
+Authors: John Hall
+
++/
+
+module mir.math.func.hermite;
+
+/++
+Normalized (probabilist's) Hermite polynomial coefficients
+
+Params:
+    N = Degree of polynomial
+
+See_also:
+    $(LINK2 https://en.wikipedia.org/wiki/Hermite_polynomials, Hermite polynomials)
++/
+@safe pure nothrow
+long[] hermiteCoefficientsNorm(size_t N)
+{
+    if (N == 0) {
+      return [1];  
+    } else {
+        typeof(return) output = [0, 1];
+        if (N > 1) {
+            output.length = N + 1;
+            int K;
+            typeof(return) h_N_minus_1 = hermiteCoefficientsNorm(0); // to be copied to h_N_minus_2 in loop
+            h_N_minus_1.length = N;
+            typeof(return) h_N_minus_2; //value replaced later
+            h_N_minus_2.length = N - 1;
+
+            foreach (size_t j; 2..(N + 1)) {
+                h_N_minus_2[0..(j - 1)] = h_N_minus_1[0..(j - 1)];
+                h_N_minus_1[0..j] = output[0..j];
+                K = -(cast(int) j - 1);
+                output[0] = K * h_N_minus_2[0];
+                foreach (size_t i; 1..(j - 1)) {
+                    output[i] = h_N_minus_1[i - 1] + K * h_N_minus_2[i];
+                }
+                foreach (size_t i; (j - 1)..(j + 1)) {
+                    output[i] = h_N_minus_1[i - 1];
+                }
+            }
+        }
+        return output;
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.polynomial: polynomial;
+    import mir.rc.array: rcarray;
+    import mir.test: should;
+
+    auto h0 = hermiteCoefficientsNorm(0).rcarray!(const double).polynomial;
+    auto h1 = hermiteCoefficientsNorm(1).rcarray!(const double).polynomial;
+    auto h2 = hermiteCoefficientsNorm(2).rcarray!(const double).polynomial;
+    auto h3 = hermiteCoefficientsNorm(3).rcarray!(const double).polynomial;
+    auto h4 = hermiteCoefficientsNorm(4).rcarray!(const double).polynomial;
+    auto h5 = hermiteCoefficientsNorm(5).rcarray!(const double).polynomial;
+    auto h6 = hermiteCoefficientsNorm(6).rcarray!(const double).polynomial;
+    auto h7 = hermiteCoefficientsNorm(7).rcarray!(const double).polynomial;
+    auto h8 = hermiteCoefficientsNorm(8).rcarray!(const double).polynomial;
+    auto h9 = hermiteCoefficientsNorm(9).rcarray!(const double).polynomial;
+    auto h10 = hermiteCoefficientsNorm(10).rcarray!(const double).polynomial;
+
+    h0(3).should == 1;
+    h1(3).should == 3;
+    h2(3).should == 8;
+    h3(3).should == 18;
+    h4(3).should == 30;
+    h5(3).should == 18;
+    h6(3).should == -96;
+    h7(3).should == -396;
+    h8(3).should == -516;
+    h9(3).should == 1_620;
+    h10(3).should == 9_504;
+}
+
+/// Also works with @nogc CTFE
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.test: should;
+
+    static immutable result = [-1, 0, 1];
+
+    static immutable hc2 = hermiteCoefficientsNorm(2);
+    hc2.sliced.should == result;
+}
+
+/++
+Physicist's Hermite polynomial coefficients
+
+Params:
+    N = Degree of polynomial
+
+See_also:
+    $(LINK2 https://en.wikipedia.org/wiki/Hermite_polynomials, Hermite polynomials)
++/
+@safe pure nothrow
+long[] hermiteCoefficients(size_t N)
+{
+    if (N == 0) {
+      return [1];  
+    } else {
+        typeof(return) output = [0, 2];
+        if (N > 1) {
+            output.length = N + 1;
+            typeof(return) h_N_minus_1 = hermiteCoefficients(0);
+            h_N_minus_1.length = N;
+
+            foreach (size_t j; 2..(N + 1)) {
+                h_N_minus_1[0..j] = output[0..j];
+                output[0] = -h_N_minus_1[1];
+                foreach (size_t i; 1..(j - 1)) {
+                    output[i] = 2 * h_N_minus_1[i - 1] - (cast(int) i + 1) * h_N_minus_1[i + 1];
+                }
+                foreach (size_t i; (j - 1)..(j + 1)) {
+                    output[i] = 2 * h_N_minus_1[i - 1];
+                }
+            }
+        }
+        return output;
+    }
+}
+
+///
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.polynomial: polynomial;
+    import mir.rc.array: rcarray;
+    import mir.test: should;
+
+    auto h0 = hermiteCoefficients(0).rcarray!(const double).polynomial;
+    auto h1 = hermiteCoefficients(1).rcarray!(const double).polynomial;
+    auto h2 = hermiteCoefficients(2).rcarray!(const double).polynomial;
+    auto h3 = hermiteCoefficients(3).rcarray!(const double).polynomial;
+    auto h4 = hermiteCoefficients(4).rcarray!(const double).polynomial;
+    auto h5 = hermiteCoefficients(5).rcarray!(const double).polynomial;
+    auto h6 = hermiteCoefficients(6).rcarray!(const double).polynomial;
+    auto h7 = hermiteCoefficients(7).rcarray!(const double).polynomial;
+    auto h8 = hermiteCoefficients(8).rcarray!(const double).polynomial;
+    auto h9 = hermiteCoefficients(9).rcarray!(const double).polynomial;
+    auto h10 = hermiteCoefficients(10).rcarray!(const double).polynomial;
+
+    h0(3).should == 1;
+    h1(3).should == 6;
+    h2(3).should == 34;
+    h3(3).should == 180;
+    h4(3).should == 876;
+    h5(3).should == 3_816;
+    h6(3).should == 14_136;
+    h7(3).should == 39_024;
+    h8(3).should == 36_240;
+    h9(3).should == -406_944;
+    h10(3).should == -3_093_984;
+}
+
+/// Also works with @nogc CTFE
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    import mir.test: should;
+
+    static immutable result = [-2, 0, 4];
+
+    static immutable hc2 = hermiteCoefficients(2);
+    hc2.sliced.should == result;
+}

From b14ab4d09553be831279f2866b773205af1e98de Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 26 Aug 2022 18:53:16 +0700
Subject: [PATCH 495/573] fixup splineSlopes

---
 source/mir/interpolate/spline.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 9692b12f..43e28225 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -1496,7 +1496,7 @@ SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind,
 
     }
 
-    with(SplineType) final switch(kind)
+    if (n > 2) with(SplineType) final switch(kind)
     {
         case c2:
 

From bdf43b089f5ce089ab1aabcfa203709701b65acd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 13 Sep 2022 13:02:33 +0700
Subject: [PATCH 496/573] add makima spline

---
 source/mir/interpolate/spline.d | 75 +++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 43e28225..b586e1be 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -126,6 +126,14 @@ private template ValueType(T, X)
     interpolant = spline!double(x, y, SplineType.akima);
     assert(xs.vmap(interpolant).all!approxEqual(test_data6));
 
+    static immutable test_data7 = [
+        12.363463972190182, 3.066421497605127, 9.848385331143952, 4.429544487015751, 11.244211106655975,
+        16.255750063889600, 18.140374440374440, 5.291585909796099, 17.722477222271888, 3.181558328118484,
+    ];
+    /// Modified Akima spline
+    interpolant = spline!double(x, y, SplineType.makima);
+    assert(xs.vmap(interpolant).all!approxEqual(test_data7));
+
     /// Double Quadratic spline
     interpolant = spline!double(x, y, SplineType.doubleQuadratic);
     import mir.interpolate.utility: ParabolaKernel;
@@ -518,6 +526,10 @@ enum SplineType
     $(HTTPS en.wikipedia.org/wiki/Akima_spline, Akima spline).
     +/
     akima,
+    /++
+    $(HTTPS https://www.mathworks.com/help/matlab/ref/makima.html, Modified Akima spline).
+    +/
+    makima,
 }
 
 /++
@@ -685,6 +697,10 @@ enum SplineBoundaryType
     +/
     akima,
     /++
+    Default for Modified Akima splines.
+    +/
+    makima,
+    /++
     Not implemented.
     +/
     periodic = -1,
@@ -1345,6 +1361,12 @@ SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind,
             if (rBoundary.type == SplineBoundaryType.notAKnot)
                 rBoundary.type = SplineBoundaryType.akima;
             break;
+        case makima:
+            if (lBoundary.type == SplineBoundaryType.notAKnot)
+                lBoundary.type = SplineBoundaryType.makima;
+            if (rBoundary.type == SplineBoundaryType.notAKnot)
+                rBoundary.type = SplineBoundaryType.makima;
+            break;
     }
 
     if (n <= 3)
@@ -1357,9 +1379,11 @@ SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind,
         if (n == 2)
         {
             if (lBoundary.type == SplineBoundaryType.monotone
+             || lBoundary.type == SplineBoundaryType.makima
              || lBoundary.type == SplineBoundaryType.akima)
                 lBoundary.type = SplineBoundaryType.parabolic;
             if (rBoundary.type == SplineBoundaryType.monotone
+             || rBoundary.type == SplineBoundaryType.makima
              || rBoundary.type == SplineBoundaryType.akima)
                 rBoundary.type = SplineBoundaryType.parabolic;
         }
@@ -1439,6 +1463,13 @@ SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind,
         temp.front = akimaTail(dd[0], dd[1]);
         break;
 
+    case makima:
+
+        slopes.front = 1;
+        first = 0;
+        temp.front = makimaTail(dd[0], dd[1]);
+        break;
+
     }
 
     with(SplineBoundaryType) final switch(rBoundary.type)
@@ -1494,6 +1525,13 @@ SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind,
         temp.back = akimaTail(dd[$ - 1], dd[$ - 2]);
         break;
 
+    case makima:
+
+        slopes.back = 1;
+        last = 0;
+        temp.back = makimaTail(dd[$ - 1], dd[$ - 2]);
+        break;
+
     }
 
     if (n > 2) with(SplineType) final switch(kind)
@@ -1581,6 +1619,24 @@ SplineConvexity splineSlopes(F, T, P, IV, IS, SliceKind gkind, SliceKind vkind,
                 }
                 break;
             }
+
+        case makima:
+            {
+                auto d3 = dd[1];
+                auto d2 = dd[0];
+                auto d1 = 2 * d2 - d3;
+                auto d0 = d1;
+                foreach (i; 1 .. n - 1)
+                {
+                    d0 = d1;
+                    d1 = d2;
+                    d2 = d3;
+                    d3 = i == n - 2 ? 2 * d2 - d1 : dd[i + 1];
+                    slopes[i] = 1;
+                    temp[i] = makimaSlope(d0, d1, d2, d3);
+                }
+                break;
+            }
     }
 
     foreach (i; 0 .. n - 1)
@@ -1645,6 +1701,25 @@ private F akimaSlope(F)(in F d0, in F d1, in F d2, in F d3)
     return w0 * d2 + w1 * d1;
 }
 
+private F makimaTail(F)(in F d2, in F d3)
+{
+    auto d1 = 2 * d2 - d3;
+    auto d0 = 2 * d1 - d2;
+    return makimaSlope(d0, d1, d2, d3);
+}
+
+private F makimaSlope(F)(in F d0, in F d1, in F d2, in F d3)
+{
+    if (d1 == d2)
+        return d1;
+    auto w0 = fabs(d1 - d0) + fabs(d1 + d0) * 0.5f;
+    auto w1 = fabs(d3 - d2) + fabs(d3 + d2) * 0.5f;
+    auto ws = w0 + w1;
+    w0 /= ws;
+    w1 /= ws;
+    return w0 * d2 + w1 * d1;
+}
+
 ///
 struct SplineKernel(X)
 {

From 6451f671a16792f12095991d54065797f695cfb2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 13 Sep 2022 15:14:50 +0700
Subject: [PATCH 497/573] Fix aarch64-linux-gnu

---
 dub.sdl                                |  2 ++
 source/mir/bignum/fp.d                 | 18 ++++++++++++------
 source/mir/bignum/internal/dec2float.d |  3 ++-
 3 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 394ba94f..b56b3980 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -7,6 +7,8 @@ license "Apache-2.0"
 
 dependency "mir-core" version=">=1.1.106"
 
+// dflags "-mtriple=aarch64-linux-gnu"
+
 // versions "TeslAlgoM"
 
 buildType "unittest" {
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index 8d5cbf49..cbd77061 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -110,9 +110,9 @@ struct Fp(uint size)
             auto most = ulong(high);
             auto least = cast(ulong)x;
             version(LittleEndian)
-                ulong[2] pair = [most, least];
-            else
                 ulong[2] pair = [least, most];
+            else
+                ulong[2] pair = [most, least];
 
             if (normalize)
             {
@@ -167,8 +167,11 @@ struct Fp(uint size)
         static assert(cast(double) Fp!64(double.min_normal / 2) == double.min_normal / 2);
         static assert(cast(double) Fp!64(double.min_normal * double.epsilon) == double.min_normal * double.epsilon);
         // subnormals
-        static assert(cast(real) Fp!64(real.min_normal / 2) == real.min_normal / 2);
-        static assert(cast(real) Fp!64(real.min_normal * real.epsilon) == real.min_normal * real.epsilon);
+        static if (real.mant_dig <= 64)
+        {
+            static assert(cast(real) Fp!128(real.min_normal / 2) == real.min_normal / 2);
+            static assert(cast(real) Fp!128(real.min_normal * real.epsilon) == real.min_normal * real.epsilon);
+        }
 
         enum d = cast(float) Fp!64(float.min_normal / 2, false);
 
@@ -179,8 +182,11 @@ struct Fp(uint size)
         static assert(cast(double) Fp!64(double.min_normal / 2, false) == double.min_normal / 2);
         static assert(cast(double) Fp!64(double.min_normal * double.epsilon, false) == double.min_normal * double.epsilon);
         // subnormals
-        static assert(cast(real) Fp!64(real.min_normal / 2, false) == real.min_normal / 2);
-        static assert(cast(real) Fp!64(real.min_normal * real.epsilon, false) == real.min_normal * real.epsilon);
+        static if (real.mant_dig <= 64)
+        {
+            static assert(cast(real) Fp!64(real.min_normal / 2, false) == real.min_normal / 2);
+            static assert(cast(real) Fp!64(real.min_normal * real.epsilon, false) == real.min_normal * real.epsilon);
+        }
 
         import mir.bignum.fixed: UInt;
 
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index d48ba6e5..01b0be74 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -123,7 +123,8 @@ T decimalToFloatImpl(T)(ulong coefficient, long exponent)
     enum wordBits = T.mant_dig < 64 ? 64 : 128;
     enum ulong half = (1UL << (wordBits - T.mant_dig - 1));
     enum bigHalf = UInt!128([0UL, half]);
-    enum bigMask = (UInt!128(1UL) << (64 - T.mant_dig)) - 1;
+    static if (T.mant_dig < 64)
+        enum bigMask = (UInt!128(1UL) << (64 - T.mant_dig)) - 1;
 
     static if (T.mant_dig > 64)
         enum ulong mask = (1UL << (128 - T.mant_dig)) - 1;

From 8bd1ea73059244387e32adec56b987f6ac49238c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Sep 2022 06:17:25 +0700
Subject: [PATCH 498/573] adopt for DIP1000

---
 source/mir/algebraic_alias/ion.d   |  45 +++--
 source/mir/algebraic_alias/json.d  |  44 +++--
 source/mir/algorithm/iteration.d   |  34 ++--
 source/mir/annotated.d             | 168 +++++++++++++++++-
 source/mir/appender.d              |   2 +-
 source/mir/array/allocation.d      |   2 +-
 source/mir/base64.d                |  10 +-
 source/mir/bignum/decimal.d        |   4 +-
 source/mir/bignum/fixed.d          |   4 +-
 source/mir/bignum/integer.d        |   4 +-
 source/mir/combinatorics/package.d |   6 +-
 source/mir/format.d                | 274 ++++++++++++++++++++---------
 source/mir/format_impl.d           |   2 +-
 source/mir/interpolate/package.d   |   2 +-
 source/mir/lob.d                   |  14 ++
 source/mir/ndslice/iterator.d      |   6 +-
 source/mir/ndslice/slice.d         |   4 +-
 source/mir/parse.d                 |  31 ++++
 source/mir/rc/ptr.d                |  10 +-
 source/mir/rc/slim_ptr.d           |   4 +-
 source/mir/serde.d                 |   7 -
 source/mir/series.d                |  30 ++--
 source/mir/small_array.d           |  12 +-
 source/mir/small_string.d          |  16 +-
 source/mir/stdio.d                 |   3 +-
 source/mir/string_map.d            |  85 ++++++++-
 source/mir/test.d                  |   8 +-
 source/mir/timestamp.d             |   2 +-
 28 files changed, 631 insertions(+), 202 deletions(-)

diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
index 6a4a442b..a9088155 100644
--- a/source/mir/algebraic_alias/ion.d
+++ b/source/mir/algebraic_alias/ion.d
@@ -11,7 +11,7 @@ Macros:
 +/
 module mir.algebraic_alias.ion;
 
-import mir.algebraic: TaggedVariant, This;
+import mir.algebraic: Algebraic, This;
 ///
 public import mir.annotated: Annotated;
 ///
@@ -25,7 +25,7 @@ public import mir.timestamp: Timestamp;
 /++
 Definition union for $(LREF IonAlgebraic).
 +/
-union IonAlgebraicUnion
+union Ion_
 {
     ///
     typeof(null) null_;
@@ -48,7 +48,7 @@ union IonAlgebraicUnion
     /// Self alias in $(MREF mir,string_map).
     StringMap!This object;
     /// Self alias in $(MREF mir,annotated).
-    Annotated!This annotations;
+    Annotated!This annotated;
 }
 
 /++
@@ -57,7 +57,7 @@ Ion tagged algebraic alias.
 The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic).
 See also $(MREF mir,string_map) - ordered string-value associative array.
 +/
-alias IonAlgebraic = TaggedVariant!IonAlgebraicUnion;
+alias IonAlgebraic = Algebraic!Ion_;
 
 ///
 version(mir_test)
@@ -79,14 +79,28 @@ unittest
     assert(!value.isNull);
     assert(value == true);
     assert(value.kind == IonAlgebraic.Kind.boolean);
+    // access
+    assert(value.boolean == true);
     assert(value.get!bool == true);
+    assert(value.get!"boolean" == true);
     assert(value.get!(IonAlgebraic.Kind.boolean) == true);
+    // nothrow access
+    assert(value.trustedGet!bool == true);
+    assert(value.trustedGet!"boolean" == true);
+    assert(value.trustedGet!(IonAlgebraic.Kind.boolean) == true);
+    // checks
+    assert(!value._is!string);
+    assert(value._is!bool);
+    assert(value._is!"boolean");
+    assert(value._is!(IonAlgebraic.Kind.boolean));
 
     // Null
     value = object["null"] = null;
     assert(value.isNull);
     assert(value == null);
     assert(value.kind == IonAlgebraic.Kind.null_);
+    // access
+    assert(value.null_ == null);
     assert(value.get!(typeof(null)) == null);
     assert(value.get!(IonAlgebraic.Kind.null_) == null);
 
@@ -94,7 +108,13 @@ unittest
     value = object["string"] = "s";
     assert(value.kind == IonAlgebraic.Kind.string);
     assert(value == "s");
+    // access
+    // Yep, `string` here is an alias to `get!(immutable(char)[])` method
+    assert(value.string == "s");
+    // `string` here is an alias of type `immutable(char)[]`
     assert(value.get!string == "s");
+    assert(value.get!"string" == "s");
+    // finally, `string` here is an enum meber
     assert(value.get!(IonAlgebraic.Kind.string) == "s");
 
     // Integer
@@ -102,16 +122,14 @@ unittest
     assert(value.kind == IonAlgebraic.Kind.integer);
     assert(value == 4);
     assert(value != 4.0);
-    assert(value.get!long == 4);
-    assert(value.get!(IonAlgebraic.Kind.integer) == 4);
+    assert(value.integer == 4);
 
     // Float
     value = object["float"] = 3.0;
     assert(value.kind == IonAlgebraic.Kind.float_);
     assert(value != 3);
     assert(value == 3.0);
-    assert(value.get!double == 3.0);
-    assert(value.get!(IonAlgebraic.Kind.float_) == 3.0);
+    assert(value.float_ == 3.0);
 
     // Array
     IonAlgebraic[] arr = [0, 1, 2, 3, 4].map!IonAlgebraic.array;
@@ -119,7 +137,7 @@ unittest
     value = object["array"] = arr;
     assert(value.kind == IonAlgebraic.Kind.array);
     assert(value == arr);
-    assert(value.get!(IonAlgebraic[])[3] == 3);
+    assert(value.array[3] == 3);
 
     // Object
     assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]);
@@ -129,15 +147,14 @@ unittest
 
     value = object["array"] = object;
     assert(value.kind == IonAlgebraic.Kind.object);
-    assert(value.get!(StringMap!IonAlgebraic).keys is object.keys);
-    assert(value.get!(StringMap!IonAlgebraic).values is object.values);
+    assert(value.object.keys is object.keys);
 
     IonAlgebraic[string] aa = object.toAA;
-    object = StringMap!IonAlgebraic(aa);
+    object = aa.StringMap!IonAlgebraic;
 
     IonAlgebraic fromAA = ["a" : IonAlgebraic(3), "b" : IonAlgebraic("b")];
-    assert(fromAA.get!(StringMap!IonAlgebraic)["a"] == 3);
-    assert(fromAA.get!(StringMap!IonAlgebraic)["b"] == "b");
+    assert(fromAA.object["a"] == 3);
+    assert(fromAA.object["b"] == "b");
 
     auto annotated = Annotated!IonAlgebraic(["birthday"], Timestamp("2001-01-01"));
     value = annotated;
diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index 36901959..fb8e97ed 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -11,14 +11,14 @@ Macros:
 +/
 module mir.algebraic_alias.json;
 
-import mir.algebraic: TaggedVariant, This;
+import mir.algebraic: Algebraic, This;
 ///
 public import mir.string_map: StringMap;
 
 /++
 Definition union for $(LREF JsonAlgebraic).
 +/
-union JsonAlgebraicUnion
+union Json_
 {
     ///
     typeof(null) null_;
@@ -42,10 +42,11 @@ JSON tagged algebraic alias.
 The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic).
 See also $(MREF mir,string_map) - ordered string-value associative array.
 +/
-alias JsonAlgebraic = TaggedVariant!JsonAlgebraicUnion;
+alias JsonAlgebraic = Algebraic!Json_;
 
 ///
 version(mir_test)
+@safe pure
 unittest
 {
     import mir.ndslice.topology: map;
@@ -64,14 +65,28 @@ unittest
     assert(!value.isNull);
     assert(value == true);
     assert(value.kind == JsonAlgebraic.Kind.boolean);
+    // access
+    assert(value.boolean == true);
     assert(value.get!bool == true);
+    assert(value.get!"boolean" == true);
     assert(value.get!(JsonAlgebraic.Kind.boolean) == true);
+    // nothrow access
+    assert(value.trustedGet!bool == true);
+    assert(value.trustedGet!"boolean" == true);
+    assert(value.trustedGet!(JsonAlgebraic.Kind.boolean) == true);
+    // checks
+    assert(!value._is!string);
+    assert(value._is!bool);
+    assert(value._is!"boolean");
+    assert(value._is!(JsonAlgebraic.Kind.boolean));
 
     // Null
     value = object["null"] = null;
     assert(value.isNull);
     assert(value == null);
     assert(value.kind == JsonAlgebraic.Kind.null_);
+    // access
+    assert(value.null_ == null);
     assert(value.get!(typeof(null)) == null);
     assert(value.get!(JsonAlgebraic.Kind.null_) == null);
 
@@ -79,7 +94,13 @@ unittest
     value = object["string"] = "s";
     assert(value.kind == JsonAlgebraic.Kind.string);
     assert(value == "s");
+    // access
+    // Yep, `string` here is an alias to `get!(immutable(char)[])` method
+    assert(value.string == "s");
+    // `string` here is an alias of type `immutable(char)[]`
     assert(value.get!string == "s");
+    assert(value.get!"string" == "s");
+    // finally, `string` here is an enum meber
     assert(value.get!(JsonAlgebraic.Kind.string) == "s");
 
     // Integer
@@ -87,16 +108,14 @@ unittest
     assert(value.kind == JsonAlgebraic.Kind.integer);
     assert(value == 4);
     assert(value != 4.0);
-    assert(value.get!long == 4);
-    assert(value.get!(JsonAlgebraic.Kind.integer) == 4);
+    assert(value.integer == 4);
 
     // Float
     value = object["float"] = 3.0;
     assert(value.kind == JsonAlgebraic.Kind.float_);
     assert(value != 3);
     assert(value == 3.0);
-    assert(value.get!double == 3.0);
-    assert(value.get!(JsonAlgebraic.Kind.float_) == 3.0);
+    assert(value.float_ == 3.0);
 
     // Array
     JsonAlgebraic[] arr = [0, 1, 2, 3, 4].map!JsonAlgebraic.array;
@@ -104,7 +123,7 @@ unittest
     value = object["array"] = arr;
     assert(value.kind == JsonAlgebraic.Kind.array);
     assert(value == arr);
-    assert(value.get!(JsonAlgebraic[])[3] == 3);
+    assert(value.array[3] == 3);
 
     // Object
     assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]);
@@ -114,13 +133,12 @@ unittest
 
     value = object["array"] = object;
     assert(value.kind == JsonAlgebraic.Kind.object);
-    assert(value.get!(StringMap!JsonAlgebraic).keys is object.keys);
-    assert(value.get!(StringMap!JsonAlgebraic).values is object.values);
+    assert(value.object.keys is object.keys);
 
     JsonAlgebraic[string] aa = object.toAA;
-    object = StringMap!JsonAlgebraic(aa);
+    object = aa.StringMap!JsonAlgebraic;
 
     JsonAlgebraic fromAA = ["a" : JsonAlgebraic(3), "b" : JsonAlgebraic("b")];
-    assert(fromAA.get!(StringMap!JsonAlgebraic)["a"] == 3);
-    assert(fromAA.get!(StringMap!JsonAlgebraic)["b"] == "b");
+    assert(fromAA.object["a"] == 3);
+    assert(fromAA.object["b"] == "b");
 }
diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index e9b62fd0..b834b351 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -3837,11 +3837,6 @@ struct Uniq(alias pred, Range)
 {
     Range _input;
 
-    ref opSlice() inout
-    {
-        return this;
-    }
-
     void popFront() scope
     {
         assert(!empty, "Attempting to popFront an empty uniq.");
@@ -3890,6 +3885,16 @@ struct Uniq(alias pred, Range)
         @property bool empty() const { return _input.empty; }
     }
 
+    ref opIndex()() scope return
+    {
+        return this;
+    }
+
+    auto opIndex()() const return scope
+    {
+        return Filter!(typeof(_input[]))(_input[]);
+    }
+
     import std.range.primitives: isForwardRange;
 
     static if (isForwardRange!Range)
@@ -3995,11 +4000,6 @@ struct Filter(alias pred, Range)
     Range _input;
     version(assert) bool _freshEmpty;
 
-    ref opSlice() inout
-    {
-        return this;
-    }
-
     void popFront() scope
     {
         assert(!_input.empty, "Attempting to popFront an empty Filter.");
@@ -4008,14 +4008,14 @@ struct Filter(alias pred, Range)
         _input.popFront;
     }
 
-    auto ref front() @property
+    auto ref front() @property return scope
     {
         assert(!_input.empty, "Attempting to fetch the front of an empty Filter.");
         version(assert) assert(_freshEmpty, "Attempting to fetch the front of a Filter without calling '.empty' method ahead.");
         return _input.front;
     }
 
-    bool empty() @property
+    bool empty() scope @property
     {
         version(assert) _freshEmpty = true;
         for (;;)
@@ -4036,6 +4036,16 @@ struct Filter(alias pred, Range)
             return typeof(this)(_input.save);
         }
     }
+
+    ref opIndex()() scope return
+    {
+        return this;
+    }
+
+    auto opIndex()() const return scope
+    {
+        return Filter!(pred, typeof(_input[]))(_input[]);
+    }
 }
 
 ///
diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index f45cbbc4..46d69c68 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -7,6 +7,7 @@ Macros:
 +/
 module mir.annotated;
 
+import mir.internal.meta: basicElementType;
 import mir.serde: serdeRegister, serdeAnnotation, serdeIsDynamicAlgebraic;
 
 static immutable excMsg = "At least one annotation is required to create an annotated value.";
@@ -62,7 +63,13 @@ struct Annotated(T) {
         }
 
         ///
-        bool opEquals(const Annotated rhs) const
+        bool opEquals(scope const Annotated rhs) scope const
+        {
+            return annotations == rhs.annotations && value == rhs.value;
+        }
+
+        /// ditto
+        bool opEquals(ref scope const Annotated rhs) scope const
         {
             return annotations == rhs.annotations && value == rhs.value;
         }
@@ -100,6 +107,25 @@ struct Annotated(T) {
         else
             this.value = T(forward!args);
     }
+
+    private alias E = .basicElementType!T;
+
+    import std.traits: isAssociativeArray, isAggregateType;
+    static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
+    ///
+    int opCmp()(ref scope const typeof(this) rhs) scope const // pure nothrow @nogc
+    {
+        if (auto d = __cmp(annotations, rhs.annotations))
+            return d;
+
+        static if (__traits(compiles, __cmp(value, rhs.value)))
+            return __cmp(value, rhs.value);
+        else
+        static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U))
+            return value.opCmp(rhs.value);
+        else
+            return value < rhs.value ? -1 : value > rhs.value ? +1 : 0;
+    }
 }
 
 ///
@@ -134,3 +160,143 @@ unittest
     assert(ac.annotations == annotations);
     assert(ac.value.x == 5);
 }
+
+/++
+A convenience definition of an annotated value.
+
+A structure that behaves like a recursive algebraic type should define `enum _serdeRecursiveAlgebraic;` member.
++/
+@serdeRegister
+@serdeAnnotation
+struct AnnotatedOnce(T) {
+    ///
+    @serdeAnnotation
+    string annotation;
+
+    static if (!(is(T == union) || is(T == struct)))
+        private enum _alloc_ = false;
+    else
+    static if (__traits(hasMember, T, "_serdeRecursiveAlgebraic"))
+        private enum _alloc_ = true;
+    else
+    {
+        import mir.algebraic: isVariant;
+        static if (isVariant!T)
+            private enum _alloc_ = true;
+        else
+            private enum _alloc_ = false;
+    }
+
+    static if (_alloc_)
+    {
+        ///
+        private T* _value;
+        ///
+        ref inout(T) value() inout @property
+        {
+            return *_value;
+        }
+
+        ///
+        ref T value(T value) @property
+        {
+            if (_value is null)
+            {
+                _value = new T;
+                import core.lifetime: move;
+                *_value = move(value);
+            }
+            return *_value;
+        }
+
+        ///
+        bool opEquals(scope const AnnotatedOnce rhs) scope const
+        {
+            return annotation == rhs.annotation && value == rhs.value;
+        }
+
+        ///
+        bool opEquals(ref scope const AnnotatedOnce rhs) scope const
+        {
+            return annotation == rhs.annotation && value == rhs.value;
+        }
+    }
+    else
+    {
+        ///
+        T value;
+    }
+
+
+    /++
+    Params:
+        annotation = non-empty array of annotation
+        args = arguments to construct value with
+    +/
+    this(Args...)(string annotation, Args args) @safe pure {
+        import core.lifetime: forward;
+        this.annotation = annotation;
+        static if (_alloc_)
+            this._value = new T(forward!args);
+        else
+        static if (__traits(compiles, value = args))
+            this.value = args;
+        else
+        static if (is(T == class))
+            this.value = new T(forward!args);
+        else
+            this.value = T(forward!args);
+    }
+
+    private alias E = .basicElementType!T;
+
+    import std.traits: isAssociativeArray, isAggregateType;
+    static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
+    ///
+    int opCmp()(ref scope const typeof(this) rhs) scope const @safe pure nothrow @nogc
+    {
+        if (auto d = __cmp(annotation, rhs.annotation))
+            return d;
+
+        static if (__traits(compiles, __cmp(value, rhs.value)))
+            return __cmp(value, rhs.value);
+        else
+        static if (__traits(hasMember, value, "opCmp") && !is(T[i] == U*, U))
+            return value.opCmp(rhs.value);
+        else
+            return value < rhs.value ? -1 : value > rhs.value ? +1 : 0;
+    }
+}
+
+///
+version(mir_test)
+unittest
+{
+    auto annotation = "annotation";
+    static struct S {double x;}
+    auto as = AnnotatedOnce!S(annotation, 5);
+    assert(as.annotation == annotation);
+    assert(as.value.x == 5);
+
+    static struct C {double x;}
+    auto ac = AnnotatedOnce!S(annotation, 5);
+    assert(ac.annotation == annotation);
+    assert(ac.value.x == 5);
+}
+
+///
+version(mir_test)
+unittest
+{
+    import mir.algebraic;
+    auto annotation = "annotation";
+    static struct S {double x;}
+    auto as = AnnotatedOnce!(Variant!S)(annotation, 5);
+    assert(as.annotation == annotation);
+    assert(as.value.x == 5);
+
+    static struct C {double x;}
+    auto ac = AnnotatedOnce!(Variant!S)(annotation, 5);
+    assert(ac.annotation == annotation);
+    assert(ac.value.x == 5);
+}
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 49ed7ced..27fc4f0d 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -387,7 +387,7 @@ unittest
     static struct S
     {
     @safe pure nothrow @nogc:
-        @property string toString() const
+        @property string toString() scope const
         {
             return "_";
         }
diff --git a/source/mir/array/allocation.d b/source/mir/array/allocation.d
index 2c44ca16..f1ef2b23 100644
--- a/source/mir/array/allocation.d
+++ b/source/mir/array/allocation.d
@@ -220,7 +220,7 @@ if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !__traits(i
     // @system due to array!string
     import std.conv : to;
 
-    static struct TestArray { int x; string toString() @safe { return to!string(x); } }
+    static struct TestArray { int x; string toString() scope const @safe { return to!string(x); } }
 
     static struct OpAssign
     {
diff --git a/source/mir/base64.d b/source/mir/base64.d
index d958f6ed..8170e2ee 100644
--- a/source/mir/base64.d
+++ b/source/mir/base64.d
@@ -7,7 +7,7 @@ Copyright: 2021 Harrison Ford, Symmetry Investments
 +/
 module mir.base64;
 
-package static immutable base64DecodeInvalidCharMsg = "Invalid character encountered.";
+package static immutable base64DecodeInvalidCharMsg = "base64: Invalid character encountered.";
 package static immutable base64DecodeInvalidLenMsg = "Cannot decode a buffer with given length (not a multiple of 4, missing padding?)";
 version(D_Exceptions) {
     package static immutable base64DecodeInvalidCharException = new Exception(base64DecodeInvalidCharMsg);
@@ -101,7 +101,7 @@ ubyte[] decodeBase64(scope const(char)[] data, char plusChar = '+', char slashCh
 Decode a Base64 encoded value, placing the result onto an Appender.
 +/
 void decodeBase64(Appender)(scope const(char)[] data,
-                            scope return ref Appender appender,
+                            scope ref Appender appender,
                             char plusChar = '+',
                             char slashChar = '/') @safe pure
 {
@@ -289,19 +289,19 @@ version(mir_test)
 /++
 Encode a ubyte array as Base64, returning the encoded value.
 +/
-const(char)[] encodeBase64(scope const(ubyte)[] buf, char plusChar = '+', char slashChar = '/') @safe pure
+string encodeBase64(scope const(ubyte)[] buf, char plusChar = '+', char slashChar = '/') @safe pure
 {
     import mir.appender : scopedBuffer;
     auto app = scopedBuffer!char;
     encodeBase64(buf, app, plusChar, slashChar);
-    return app.data.dup;
+    return app.data.idup;
 }
 
 /++
 Encode a ubyte array as Base64, placing the result onto an Appender.
 +/
 void encodeBase64(Appender)(scope const(ubyte)[] input,
-                            scope return ref Appender appender,
+                            scope ref Appender appender,
                             char plusChar = '+',
                             char slashChar = '/') @safe pure
 {
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 9cf91db3..60ddd488 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -35,7 +35,7 @@ struct Decimal(uint size64)
     BigInt!size64 coefficient;
 
     ///
-    void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const
+    void toString(C = char, W)(ref scope W w, NumericSpec spec = NumericSpec.init) const scope
         if(isSomeChar!C && isMutable!C)
     {
         assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human);
@@ -382,7 +382,7 @@ struct Decimal(uint size64)
     private enum eDecimalLength = coefficientBufferLength + expBufferLength;
 
     ///
-    immutable(C)[] toString(C = char)(NumericSpec spec = NumericSpec.init) const @safe pure nothrow
+    immutable(C)[] toString(C = char)(NumericSpec spec = NumericSpec.init) const scope @safe pure nothrow
         if(isSomeChar!C && isMutable!C)
     {
         import mir.appender: UnsafeArrayBuffer;
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index a1d74bcc..e23a3355 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -131,7 +131,7 @@ struct UInt(size_t size)
     }
 
     ///
-    immutable(C)[] toString(C = char)() const @safe pure nothrow
+    immutable(C)[] toString(C = char)() scope const @safe pure nothrow
         if(isSomeChar!C && isMutable!C)
     {
         UInt!size copy = this;
@@ -154,7 +154,7 @@ struct UInt(size_t size)
     }
 
     ///
-    void toString(C = char, W)(scope ref W w) const
+    void toString(C = char, W)(ref scope W w) scope const
         if(isSomeChar!C && isMutable!C)
     {
         UInt!size copy = this;
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index a2b7f90c..d435aa5b 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -1001,7 +1001,7 @@ struct BigInt(uint size64)
     }
 
     ///
-    immutable(C)[] toString(C = char)() const @safe pure nothrow
+    immutable(C)[] toString(C = char)() scope const @safe pure nothrow
         if(isSomeChar!C && isMutable!C)
     {
         C[ceilLog10Exp2(data.length * (size_t.sizeof * 8)) + 1] buffer = void;
@@ -1025,7 +1025,7 @@ struct BigInt(uint size64)
     }
 
     ///
-    void toString(C = char, W)(scope ref W w) const
+    void toString(C = char, W)(ref scope W w) scope const
         if(isSomeChar!C && isMutable!C)
     {
         C[ceilLog10Exp2(data.length * (size_t.sizeof * 8)) + 1] buffer = void;
diff --git a/source/mir/combinatorics/package.d b/source/mir/combinatorics/package.d
index 6684f1bc..e6660b45 100644
--- a/source/mir/combinatorics/package.d
+++ b/source/mir/combinatorics/package.d
@@ -241,19 +241,19 @@ struct IndexedRoR(Collection, Range)
     }
 
     ///
-    auto lightScope()()
+    auto lightScope()() return scope
     {
         return IndexedRoR!(LightScopeOf!Collection, LightScopeOf!Range)(.lightScope(c), .lightScope(r));
     }
 
     ///
-    auto lightScope()() const
+    auto lightScope()() return scope const
     {
         return IndexedRoR!(LightConstOf!(LightScopeOf!Collection), LightConstOf!(LightScopeOf!Range))(.lightScope(c), .lightScope(r));
     }
 
     ///
-    auto lightConst()() const
+    auto lightConst()() return scope const
     {
         return IndexedRoR!(LightConstOf!Collection, LightConstOf!Range)(.lightConst(c), .lightConst(r));
     }
diff --git a/source/mir/format.d b/source/mir/format.d
index 308d9108..3e5c3035 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -9,6 +9,34 @@ module mir.format;
 import std.traits;
 import mir.primitives: isOutputRange;
 
+///Scalar styles.
+enum StringStyle
+{
+    /// Uninitialized style
+    none,
+    /// Literal block style, `|`
+    longMultiLine,
+    /// Folded block style, `>`
+    longSingleLine,
+    /// Plain scalar
+    plain,
+    /// Single quoted scalar
+    asSingleQuoted,
+    /// Double quoted scalar
+    asEscapedString,
+}
+
+/// Collection styles
+enum CollectionStyle
+{
+    /// Uninitialized style
+    none,
+    /// Block style
+    block,
+    /// Flow style
+    flow,
+}
+
 /// `mir.conv: to` extension.
 version(mir_test)
 @safe pure @nogc
@@ -60,9 +88,11 @@ unittest
 }
 
 /// Concatenated string results
-string text(string separator = "", Args...)(auto ref Args args)
+string text(string separator = "", Args...)(auto scope ref const Args args)
     if (Args.length > 0)
 {
+    import mir.utility: _expect;
+
     static if (Args.length == 1)
     {
         static if (is(immutable Args[0] == immutable typeof(null)))
@@ -70,10 +100,30 @@ string text(string separator = "", Args...)(auto ref Args args)
             return "null";
         }
         else
+        static if (is(Args[0] == enum))
+        {
+            import mir.enums: getEnumIndex, enumStrings;
+            uint id = void;
+            if (getEnumIndex(args[0], id)._expect(true))
+                return enumStrings!(Args[0])[id];
+            assert(0);
+        }
+        else
+        static if (is(Unqual!(Args[0]) == bool))
+        {
+            return args[0] ? "true" : "false";
+        }
+        else
+        static if (is(args[0].toString : string))
+        {
+            return args[0].toString;
+        }
+        else
         {
-            import mir.functional: forward;
-            import mir.conv: to;
-            return to!string(forward!args);
+            import mir.appender: scopedBuffer;
+            auto buffer = scopedBuffer!char;
+            buffer.print(args[0]);
+            return buffer.data.idup;
         }
     }
     else
@@ -142,19 +192,21 @@ auto stringBuf(C = char, uint scopeSize = 256)()
 mixin template StreamFormatOp(C)
 {
     ///
-    ref typeof(this) opBinary(string op : "<<", T)(ref const T c) scope
+    ref typeof(this) opBinary(string op : "<<", T)(scope ref const T c) scope
     {
-        return print!C(this, c);
+        print!C(this, c);
+        return this;
     }
 
     ///
-    ref typeof(this) opBinary(string op : "<<", T)(const T c) scope
+    ref typeof(this) opBinary(string op : "<<", T)(scope const T c) scope
     {
-        return print!C(this, c);
+        print!C(this, c);
+        return this;
     }
 
     /// ditto
-    const(C)[] opBinary(string op : "<<", T : GetData)(const T c) scope
+    const(C)[] opBinary(string op : "<<", T : GetData)(scope const T c) scope
     {
         return buffer.data;
     }
@@ -360,7 +412,7 @@ enum escapeFormatQuote(EscapeFormat escapeFormat) = escapeFormat == EscapeFormat
 
 /++
 +/
-ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope return ref W w, scope const(C)[] str)
+void printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ref W w, scope const(C)[] str)
     if (isOutputRange!(W, C))
 {
     import mir.utility: _expect;
@@ -424,7 +476,7 @@ ref W printEscaped(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ret
                     put_xXX!C(w, cast(char)c);
         }
     }
-    return w;
+    return;
 }
 
 ///
@@ -433,15 +485,17 @@ version (mir_test) unittest
 {
     import mir.format: stringBuf;
     auto w = stringBuf;
-    assert(w.printEscaped("Hi \a\v\0\f\t\b \\\r\n" ~ `"@nogc"`).data == `Hi \a\v\0\f\t\b \\\r\n\"@nogc\"`);
+    w.printEscaped("Hi \a\v\0\f\t\b \\\r\n" ~ `"@nogc"`);
+    assert(w.data == `Hi \a\v\0\f\t\b \\\r\n\"@nogc\"`);
     w.reset;
-    assert(w.printEscaped("\x03").data == `\x03`);
+    w.printEscaped("\x03");
+    assert(w.data == `\x03`);
 }
 
 /++
 Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
 +/
-ref W put_xXX(C = char, W)(scope return ref W w, char c)
+void put_xXX(C = char, W)(scope ref W w, char c)
     if (isSomeChar!C)
 {
     ubyte[2] spl;
@@ -456,9 +510,9 @@ ref W put_xXX(C = char, W)(scope return ref W w, char c)
 }
 
 /++
-Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
+Decodes `char` `c` to the form `\u00XX`, where `XX` is 2 hexadecimal characters.
 +/
-ref W put_uXXXX(C = char, W)(scope return ref W w, char c)
+void put_uXXXX(C = char, W)(scope ref W w, char c)
     if (isSomeChar!C)
 {
     ubyte[2] spl;
@@ -475,9 +529,9 @@ ref W put_uXXXX(C = char, W)(scope return ref W w, char c)
 }
 
 /++
-Decodes ushort `c` to the form `uXXXX`, where `XXXX` is 2 hexadecimal characters.
+Decodes ushort `c` to the form `\uXXXX`, where `XXXX` is 2 hexadecimal characters.
 +/
-ref W put_uXXXX(C = char, W)(scope return ref W w, ushort c)
+void put_uXXXX(C = char, W)(scope ref W w, ushort c)
     if (isSomeChar!C)
 {
     ubyte[4] spl;
@@ -495,19 +549,28 @@ ref W put_uXXXX(C = char, W)(scope return ref W w, ushort c)
     return w.printStaticString(buffer);
 }
 
+/++
+Decodes uint `c` to the form `\UXXXXXXXX`, where `XXXXXXXX` is 2 hexadecimal characters.
++/
+void put_UXXXXXXXX(C = char, W)(scope ref W w, uint c)
+    if (isSomeChar!C)
+{
+    w.printStaticString!C(`\U`);
+    w.print!C(HexAddress!uint(cast(uint)c));
+}
+
 ///
-ref W printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope return ref W w, scope const(C)[] c)
+void printElement(C, EscapeFormat escapeFormat = EscapeFormat.ion, W)(scope ref W w, scope const(C)[] c)
     if (isSomeChar!C)
 {
     static immutable C[1] quote = '\"';
-    return w
-        .printStaticString!C(quote)
-        .printEscaped!(C, escapeFormat)(c)
-        .printStaticString!C(quote);
+    w.printStaticString!C(quote);
+    w.printEscaped!(C, escapeFormat)(c);
+    w.printStaticString!C(quote);
 }
 
 ///
-ref W printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)(scope return ref W w, scope auto ref const T c)
+void printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)(scope ref W w, scope auto ref const T c)
     if (!isSomeString!T)
 {
     return w.print!C(c);
@@ -516,7 +579,7 @@ ref W printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)
 /++
 Multiargument overload.
 +/
-ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Args args)
+void print(C = char, W, Args...)(scope ref W w, auto scope ref const Args args)
     if (isSomeChar!C && Args.length > 1)
 {
     foreach(i, ref c; args)
@@ -527,7 +590,7 @@ ref W print(C = char, W, Args...)(scope return ref W w, scope auto ref const Arg
 }
 
 /// Prints enums
-ref W print(C = char, W, T)(scope return ref W w, const T c)
+void print(C = char, W, T)(scope ref W w, scope const T c) @nogc
     if (isSomeChar!C && is(T == enum))
 {
     import mir.enums: getEnumIndex, enumStrings;
@@ -538,13 +601,13 @@ ref W print(C = char, W, T)(scope return ref W w, const T c)
     if (getEnumIndex(c, index)._expect(true))
     {
         w.put(enumStrings!T[index]);
-        return w;
+        return;
     }
     static immutable C[] str = T.stringof ~ "(";
     w.put(str[]);
     print!C(w, cast(OriginalType!T) c);
     w.put(')');
-    return w;
+    return;
 }
 
 ///
@@ -564,7 +627,7 @@ version (mir_test) unittest
 }
 
 /// Prints boolean
-ref W print(C = char, W)(scope return ref W w, bool c)
+void print(C = char, W)(scope ref W w, bool c)
     if (isSomeChar!C)
 {
     enum N = 5;
@@ -578,7 +641,7 @@ ref W print(C = char, W)(scope return ref W w, bool c)
         auto n = printBoolean(c, buf);
         w.put(buf[0 .. n]);
     }
-    return w;
+    return;
 }
 
 ///
@@ -587,14 +650,16 @@ version (mir_test) unittest
 {
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
-    assert(w.print(true).data == `true`);
+    w.print(true);
+    assert(w.data == `true`);
     w.reset;
-    assert(w.print(false).data == `false`);
+    w.print(false);
+    assert(w.data == `false`);
 }
 
 /// Prints associative array
 pragma(inline, false)
-ref W print(C = char, W, V, K)(scope return ref W w, scope const V[K] c)
+void print(C = char, W, V, K)(scope ref W w, scope const V[K] c)
     if (isSomeChar!C)
 {
     enum C left = '[';
@@ -608,13 +673,12 @@ ref W print(C = char, W, V, K)(scope return ref W w, scope const V[K] c)
         if (!first)
             w.printStaticString!C(sep);
         first = false;
-        w
-            .printElement!C(key)
-            .printStaticString!C(mid)
-            .printElement!C(value);
+        w.printElement!C(key);
+        w.printStaticString!C(mid);
+        w.printElement!C(value);
     }
     w.put(right);
-    return w;
+    return;
 }
 
 ///
@@ -628,7 +692,7 @@ version (mir_test) unittest
 }
 
 /// Prints null
-ref W print(C = char, W, V)(scope return ref W w, const V c)
+void print(C = char, W, V)(scope ref W w, const V c)
     if (is(V == typeof(null)))
 {
     enum C[4] Null = "null";
@@ -647,7 +711,7 @@ version (mir_test) unittest
 
 /// Prints array
 pragma(inline, false)
-ref W printArray(C = char, W, T)(scope return ref W w,
+void printArray(C = char, W, T)(scope ref W w,
     scope const(T)[] c,
     scope const(C)[] lb = "[",
     scope const(C)[] rb = "]",
@@ -665,12 +729,12 @@ ref W printArray(C = char, W, T)(scope return ref W w,
         printElement!C(w, e);
     }
     w.put(rb);
-    return w;
+    return;
 }
 
 /// ditto
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w,
+void print(C = char, W, T)(scope ref W w,
     scope const(T)[] c,
 )
     if (isSomeChar!C && !isSomeChar!T)
@@ -685,12 +749,13 @@ version (mir_test) unittest
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
     string[2] array = ["a\na", "b"];
-    assert(w.print(array[]).data == `["a\na", "b"]`);
+    w.print(array[]);
+    assert(w.data == `["a\na", "b"]`);
 }
 
 /// Prints array as hex values
 pragma(inline, false)
-ref W printHexArray(C = char, W, T)(scope return ref W w,
+void printHexArray(C = char, W, T)(scope ref W w,
     scope const(T)[] c,
     scope const(C)[] lb = "",
     scope const(C)[] rb = "",
@@ -708,7 +773,7 @@ ref W printHexArray(C = char, W, T)(scope return ref W w,
         printElement!C(w, e.hexAddress);
     }
     w.put(rb);
-    return w;
+    return;
 }
 
 ///
@@ -719,15 +784,22 @@ version (mir_test) unittest
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
     ubyte[2] array = [0x34, 0x32];
-    w.printHexArray(array[]).data.should == `34 32`;
+    w.printHexArray(array[]);
+    w.data.should == `34 32`;
 }
 
 /// Prints escaped character in the form `'c'`.
 pragma(inline, false)
-ref W print(C = char, W)(scope return ref W w, char c)
+void print(C = char, W)(scope ref W w, char c)
     if (isSomeChar!C)
 {
     w.put('\'');
+    if (c >= ubyte.max)
+    {
+        w.printStaticString!C(`\u`);
+        w.print!C(HexAddress!ubyte(cast(ubyte)c));
+    }
+    else
     if (c >= 0x20)
     {
         if (c < 0x7F)
@@ -762,7 +834,7 @@ ref W print(C = char, W)(scope return ref W w, char c)
         }
     }
     w.put('\'');
-    return w;
+    return;
 }
 
 ///
@@ -771,24 +843,62 @@ version (mir_test) unittest
 {
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
-    assert(w
-        .print('\n')
-        .print('\'')
-        .print('a')
-        .print('\xF4')
-        .data == `'\n''\'''a''\xF4'`);
+    w.print('\n');
+    w.print('\'');
+    w.print('a');
+    w.print('\xF4');
+    assert(w.data == `'\n''\'''a''\xF4'`);
+}
+
+/// Prints escaped character in the form `'c'`.
+pragma(inline, false)
+void print(C = char, W)(scope ref W w, dchar c)
+    if (isSomeChar!C)
+{
+    import std.uni: isGraphical;
+    if (c <= ubyte.max)
+        return print(w, cast(char) c);
+    w.put('\'');
+    if (c.isGraphical)
+    {
+        import std.utf: encode;
+        C[dchar.sizeof / C.sizeof] buf;
+        print!C(w, buf[0 .. encode(buf, c)]);
+    }
+    else
+    if (c <= ushort.max)
+    {
+        w.put_uXXXX!C(cast(ushort)c);
+    }
+    else
+    {
+        w.put_UXXXXXXXX!C(c);
+    }
+    w.put('\'');
+}
+
+///
+@safe pure
+version (mir_test) unittest
+{
+    import mir.appender: scopedBuffer;
+    auto w = scopedBuffer!char;
+    w.print('\u23F4');
+    w.print('щ');
+    w.print('\U0010FFFE');
+    assert(w.data == `'\u23F4''щ''\U0010FFFE'`);
 }
 
 /// Prints some string
-ref W print(C = char, W)(scope return ref W w, scope const(C)[] c)
+void print(C = char, W)(scope ref W w, scope const(C)[] c)
     if (isSomeChar!C)
 {
     w.put(c);
-    return w;
+    return;
 }
 
 /// Prints integers
-ref W print(C = char, W, I)(scope return ref W w, const I c)
+void print(C = char, W, I)(scope ref W w, const I c)
     if (isSomeChar!C && isIntegral!I && !is(I == enum))
 {
     static if (I.sizeof == 16)
@@ -804,17 +914,17 @@ ref W print(C = char, W, I)(scope return ref W w, const I c)
     else
         auto n = printSignedToTail(c, buf);
     w.put(buf[$ - n ..  $]);
-    return w;
+    return;
 }
 
 /// Prints floating point numbers
-ref W print(C = char, W, T)(scope return ref W w, const T c, NumericSpec spec = NumericSpec.init)
+void print(C = char, W, T)(scope ref W w, const T c, NumericSpec spec = NumericSpec.init)
     if(isSomeChar!C && is(T == float) || is(T == double) || is(T == real))
 {
     import mir.bignum.decimal;
     auto decimal = Decimal!(T.mant_dig < 64 ? 1 : 2)(c);
     decimal.toString(w, spec);
-    return w;
+    return;
 }
 
 /// Human friendly precise output (default)
@@ -827,7 +937,8 @@ unittest
 
     void check(double num, string value)
     {
-        assert(buffer.print(num, spec).data == value, value);
+        buffer.print(num, spec);
+        assert(buffer.data == value, value);
         buffer.reset;
     }
 
@@ -902,15 +1013,16 @@ unittest
 
 /// Prints structs and unions
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, ref scope const T c)
+void print(C = char, W, T)(scope ref W w, scope ref const T c)
     if (isSomeChar!C && is(T == struct) || is(T == union) && !is(T : NumericSpec))
 {
+    import mir.algebraic: isVariant;
     static if (__traits(hasMember, T, "toString"))
     {
         static if (is(typeof(c.toString!C(w))))
             c.toString!C(w);
         else
-        static if (is(typeof(c.toString(w))))
+        static if (isVariant!T || is(typeof(c.toString(w))))
             c.toString(w);
         else
         static if (is(typeof(c.toString((scope const(C)[] s) { w.put(s); }))))
@@ -949,7 +1061,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref scope const T c)
                 // static assert(0, T.stringof ~ ".toString definition is wrong: 'const' qualifier may be missing.");
         }
 
-        return w;
+        return;
     }
     else
     static if (__traits(compiles, { scope const(C)[] string_of_c = c; }))
@@ -973,7 +1085,7 @@ ref W print(C = char, W, T)(scope return ref W w, ref scope const T c)
             print!C(w, e);
         }
         w.put(right);
-        return w;
+        return;
     }
     else
     {
@@ -988,14 +1100,14 @@ ref W print(C = char, W, T)(scope return ref W w, ref scope const T c)
             print!C(w, e);
         }
         w.put(right);
-        return w;
+        return;
     }
 }
 
 /// ditto
 // FUTURE: remove it
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, scope const T c)
+void print(C = char, W, T)(scope ref W w, scope const T c)
     if (isSomeChar!C && is(T == struct) || is(T == union))
 {
     return print!(C, W, T)(w, c);
@@ -1018,7 +1130,7 @@ version (mir_test) unittest
 
 /// Prints classes and interfaces
 pragma(inline, false)
-ref W print(C = char, W, T)(scope return ref W w, scope const T c)
+void print(C = char, W, T)(scope ref W w, scope const T c)
     if (isSomeChar!C && is(T == class) || is(T == interface))
 {
     static if (__traits(hasMember, T, "toString") || __traits(compiles, { scope const(C)[] string_of_c = c; }))
@@ -1029,25 +1141,25 @@ ref W print(C = char, W, T)(scope return ref W w, scope const T c)
         static if (is(typeof(c.toString!C(w))))
         {
             c.toString!C(w);
-            return w;
+            return;
         }
         else
         static if (is(typeof(c.toString(w))))
         {
             c.toString(w);
-            return w;
+            return;
         }
         else
         static if (is(typeof(c.toString((scope const(C)[] s) { w.put(s); }))))
         {
             c.toString((scope const(C)[] s) { w.put(s); });
-            return w;
+            return;
         }
         else
         static if (is(typeof(w.put(c.toString))))
         {
             w.put(c.toString);
-            return w;
+            return;
         }
         else
         static if (__traits(compiles, { scope const(C)[] string_of_c = c; }))
@@ -1073,12 +1185,12 @@ ref W print(C = char, W, T)(scope return ref W w, scope const T c)
             print!C(w, e);
         }
         w.put(right);
-        return w;
+        return;
     }
     else
     {
         w.put(T.stringof);
-        return w;
+        return;
     }
 }
 
@@ -1096,7 +1208,7 @@ version (mir_test) unittest
 }
 
 ///
-ref W printStaticString(C, size_t N, W)(scope return ref W w, ref scope const C[N] c)
+void printStaticString(C, size_t N, W)(scope ref W w, scope ref const C[N] c)
     if (isSomeChar!C && is(C == char) || is(C == wchar) || is(C == dchar))
 {
     static if (isFastBuffer!W)
@@ -1109,7 +1221,7 @@ ref W printStaticString(C, size_t N, W)(scope return ref W w, ref scope const C[
     {
         w.put(c[]);
     }
-    return w;
+    return;
 }
 
 private template hasIterableLightConst(T)
@@ -1124,7 +1236,7 @@ private template hasIterableLightConst(T)
     }
 }
 
-private ref C[N] getStaticBuf(size_t N, C, W)(scope return ref W w)
+private ref C[N] getStaticBuf(size_t N, C, W)(scope ref W w)
     if (isFastBuffer!W)
 {
     auto buf = w.getBuffer(N);
@@ -1132,7 +1244,7 @@ private ref C[N] getStaticBuf(size_t N, C, W)(scope return ref W w)
     return buf.ptr[0 .. N];
 }
 
-private @trusted ref C[N] getStaticBuf(size_t N, C)(scope return ref C[] buf)
+private @trusted ref C[N] getStaticBuf(size_t N, C)(return scope ref C[] buf)
 {
     assert(buf.length >= N);
     return buf.ptr[0 .. N];
@@ -1144,7 +1256,7 @@ template isFastBuffer(W)
 }
 
 ///
-ref W printZeroPad(C = char, W, I)(scope return ref W w, const I c, size_t minimalLength)
+void printZeroPad(C = char, W, I)(scope ref W w, const I c, size_t minimalLength)
     if (isSomeChar!C && isIntegral!I && !is(I == enum))
 {
     static if (I.sizeof == 16)
@@ -1175,7 +1287,7 @@ ref W printZeroPad(C = char, W, I)(scope return ref W w, const I c, size_t minim
         while(--zeros);
     }
     w.put(buf[$ - n ..  $]);
-    return w;
+    return;
 }
 
 ///
@@ -1217,7 +1329,7 @@ size_t printBoolean(C)(bool c, ref C[5] buf)
 
 
 /// Prints pointers
-ref W print(C = char, W, T)(scope return ref W w, scope const T* c)
+void print(C = char, W, T)(scope ref W w, scope const T* c)
 {
     import mir.enums: getEnumIndex, enumStrings;
     import mir.utility: _expect;
diff --git a/source/mir/format_impl.d b/source/mir/format_impl.d
index d5c148be..548df598 100644
--- a/source/mir/format_impl.d
+++ b/source/mir/format_impl.d
@@ -410,7 +410,7 @@ version (mir_test) unittest
     static assert (stringBuf() << 123 << getData == "123");
 }
 
-ref W printIntegralZeroImpl(C, size_t N, W, I)(scope return ref W w, I c, size_t zeroLen)
+void printIntegralZeroImpl(C, size_t N, W, I)(ref scope W w, I c, size_t zeroLen)
 {
     static if (__traits(isUnsigned, I))
         alias impl = printUnsignedToTail;
diff --git a/source/mir/interpolate/package.d b/source/mir/interpolate/package.d
index 83af1c5a..111082b6 100644
--- a/source/mir/interpolate/package.d
+++ b/source/mir/interpolate/package.d
@@ -120,7 +120,7 @@ struct Interp1(Range, Interpolant)
     private size_t _interval;
 
     ///
-    auto lightScope()
+    auto lightScope() scope
     {
         return Interp1!(LightScopeOf!Range, LightScopeOf!Interpolant)(.lightScope(_range), .lightScope(_interpolant), _interval);
     }
diff --git a/source/mir/lob.d b/source/mir/lob.d
index 8c4036e7..4fa0ad90 100644
--- a/source/mir/lob.d
+++ b/source/mir/lob.d
@@ -13,6 +13,13 @@ struct Clob
 {
     ///
     const(char)[] data;
+
+    ///
+    int opCmp(scope const typeof(this) rhs)
+    @safe pure nothrow @nogc scope const
+    {
+        return __cmp(data, rhs.data);
+    }
 }
 
 /++
@@ -23,4 +30,11 @@ struct Blob
 {
     ///
     const(ubyte)[] data;
+
+    ///
+    int opCmp(scope const typeof(this) rhs)
+    @safe pure nothrow @nogc scope const
+    {
+        return __cmp(data, rhs.data);
+    }
 }
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index c0c5752e..e5c1c899 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -749,7 +749,7 @@ struct CachedIterator(Iterator, CacheIterator, FlagIterator)
 @optmath:
 
     ///
-    auto lightScope()() scope @property
+    auto lightScope()() return scope @property
     {
         return CachedIterator!(LightScopeOf!Iterator, LightScopeOf!CacheIterator, LightScopeOf!FlagIterator)(
             .lightScope(_iterator),
@@ -759,13 +759,13 @@ struct CachedIterator(Iterator, CacheIterator, FlagIterator)
     }
 
     ///
-    auto lightScope()() scope const @property
+    auto lightScope()() return scope const @property
     {
         return lightConst.lightScope;
     }
 
     ///
-    auto lightScope()() scope immutable @property
+    auto lightScope()() return scope immutable @property
     {
         return lightImmutable.lightScope;
     }
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index f18ad164..f1b4bcf7 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -239,7 +239,7 @@ Params:
 Returns:
     n-dimensional slice
 +/
-auto sliced(size_t N, Iterator)(Iterator iterator, size_t[N] lengths...)
+auto sliced(size_t N, Iterator)(return scope Iterator iterator, size_t[N] lengths...)
     if (!__traits(isStaticArray, Iterator) && N
         && !is(Iterator : Slice!(_Iterator, _N, kind), _Iterator, size_t _N, SliceKind kind))
 {
@@ -964,7 +964,7 @@ public:
     }
 
     /// ditto
-    auto lightScope()() return immutable scope @property
+    auto lightScope()() return scope immutable @property
     {
         auto ret =  Slice!(LightImmutableOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightImmutableOfLightConstOf(Labels)))
             (_structure, .lightScope(_iterator));
diff --git a/source/mir/parse.d b/source/mir/parse.d
index c57f24cb..b96981e2 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -7,6 +7,37 @@ Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investme
 +/
 module mir.parse;
 
+/++
+Parsing position
++/
+struct ParsePosition
+{
+    ///
+    string file;
+    /// 0 is for unknown
+    uint line;
+    /// 0 is for unknown
+    uint column;
+
+    ///
+    void toString(W)(scope ref W w) scope const
+    {
+        w.put(file);
+        if (line)
+        {
+            import mir.format: print;
+            w.put("(");
+            w.print(line);
+            if (column)
+            {
+                w.put(",");
+                w.print(column);
+            }
+            w.put(")");
+        }
+    }
+}
+
 ///
 enum DecimalExponentKey
 {
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index f3975cc1..42bb8f5b 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -65,7 +65,7 @@ struct mir_rcptr(T)
     static if (is(T == class) || is(T == interface))
         ///
         pragma(inline, true)
-        inout(T) _get_value() scope inout @property
+        inout(T) _get_value() return scope inout @property
         {
             assert(this, getExcMsg);
             return _value;
@@ -73,7 +73,7 @@ struct mir_rcptr(T)
     else
         ///
         pragma(inline, true)
-        ref inout(T) _get_value() scope inout @property
+        ref inout(T) _get_value() return scope inout @property
         {
             assert(this, getExcMsg);
             return *_value;
@@ -223,7 +223,7 @@ alias RCPtr = mir_rcptr;
 /++
 Returns: shared pointer of the member and the context from the current pointer. 
 +/
-auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref Args args)
+auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref Args args) @safe
 {
     import core.lifetime: move;
     void foo(A)(auto ref A) {}
@@ -231,13 +231,13 @@ auto shareMember(string member, T, Args...)(return mir_rcptr!T context, auto ref
     static if (args.length)
     {
         // breaks safaty
-        if (false) foo(__traits(getMember, context._get_value, member)(forward!args));
+        if (false) foo(__traits(getMember, T.init, member)(forward!args));
         return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member)(forward!args), context.move))();
     }
     else
     {
         // breaks safaty
-        if (false) foo(__traits(getMember, context._get_value, member));
+        if (false) foo(__traits(getMember, T.init, member));
         return (()@trusted => createRCWithContext(__traits(getMember, context._get_value, member), context.move))();
     }
 }
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index 0ef3aa14..b2d584d9 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -62,7 +62,7 @@ struct mir_slim_rcptr(T)
     static if (is(T == class) || is(T == interface))
         ///
         pragma(inline, true)
-        inout(T) _get_value() scope inout @property
+        inout(T) _get_value() return scope inout @property
         {
             assert(this, getExcMsg);
             return _value;
@@ -70,7 +70,7 @@ struct mir_slim_rcptr(T)
     else
         ///
         pragma(inline, true)
-        ref inout(T) _get_value() scope inout @property
+        ref inout(T) _get_value() return scope inout @property
         {
             assert(this, getExcMsg);
             return *_value;
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 9df6cffb..9bfdac11 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -278,13 +278,6 @@ template serdeHasAlgebraicAnnotation(T)
             enum serdeHasAlgebraicAnnotation = true;
         }
         else
-        static if (__traits(getAliasThis, T).length)
-        {
-            T* aggregate;
-            alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
-            enum serdeHasAlgebraicAnnotation = .serdeHasAlgebraicAnnotation!A;
-        }
-        else
         {
             enum serdeHasAlgebraicAnnotation = false;
         }
diff --git a/source/mir/series.d b/source/mir/series.d
index 2e1f9389..7747d5be 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -373,17 +373,17 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         return this.lightScopeIndex == rhs.lightScopeIndex && this.data.lightScope == rhs.data.lightScope;
     }
 
-    private auto lightScopeIndex()() @property @trusted
+    private auto lightScopeIndex()() return scope @trusted
     {
         return .lightScope(_index).sliced(this.data._lengths[0]);
     }
 
-    private auto lightScopeIndex()() @property @trusted const
+    private auto lightScopeIndex()() return scope @trusted const
     {
         return .lightScope(_index).sliced(this.data._lengths[0]);
     }
 
-    private auto lightScopeIndex()() @property @trusted immutable
+    private auto lightScopeIndex()() return scope @trusted immutable
     {
         return .lightScope(_index).sliced(this.data._lengths[0]);
     }
@@ -391,14 +391,14 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     ///
     typeof(this) opBinary(string op : "~")(typeof(this) rhs)
     {
-        typeof(this.lightScope)[2] lhsAndRhs = [this.lightScope, rhs.lightScope];
+        scope typeof(this.lightScope)[2] lhsAndRhs = [this.lightScope, rhs.lightScope];
         return unionSeriesImplPrivate!false(lhsAndRhs);
     }
 
     /// ditto
     auto opBinary(string op : "~")(const typeof(this) rhs) const @trusted
     {
-        typeof(this.lightScope)[2] lhsAndRhs = [this.lightScope, rhs.lightScope];
+        scope typeof(this.lightScope)[2] lhsAndRhs = [this.lightScope, rhs.lightScope];
         return unionSeriesImplPrivate!false(lhsAndRhs);
     }
 
@@ -505,8 +505,8 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     void opIndexOpAssign(string op, IndexIterator_, Iterator_, size_t N_, SliceKind kind_)
         (auto ref Series!(IndexIterator_, Iterator_, N_, kind_) rSeries)
     {
-        auto l = this.lightScope;
-        auto r = rSeries.lightScope;
+        scope l = this.lightScope;
+        scope r = rSeries.lightScope;
         if (r.empty)
             return;
         if (l.empty)
@@ -1276,19 +1276,19 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     }
 
     ///
-    Series!(LightScopeOf!IndexIterator, LightScopeOf!Iterator, N, kind) lightScope()() @trusted @property
+    Series!(LightScopeOf!IndexIterator, LightScopeOf!Iterator, N, kind) lightScope()() return scope @trusted @property
     {
         return typeof(return)(lightScopeIndex, this.data.lightScope);
     }
 
     /// ditto
-    Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() @trusted const @property
+    Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() return scope @trusted const @property
     {
         return typeof(return)(lightScopeIndex, this.data.lightScope);
     }
 
     /// ditto
-    Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() @trusted immutable @property
+    Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() return scope @trusted immutable @property
     {
         return typeof(return)(lightScopeIndex, this.data.lightScope);
     }
@@ -1312,10 +1312,10 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     }
 
     ///
-    void toString(Writer)(scope ref Writer w) const
+    void toString(Writer)(scope ref Writer w) scope const @safe
     {
         import mir.format: print;
-        auto ls = lightScope;
+        scope ls = lightScope;
         print(w, "{ index: ");
         print(w, ls.index);
         print(w, ", data: ");
@@ -2463,7 +2463,7 @@ auto unionSeriesImpl(I, E,
     }
 }
 
-private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator...)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple, ref Allocator allocator) @safe
+private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator...)(scope Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple, ref Allocator allocator) @safe
     if (C > 1 && Allocator.length <= 1)
 {
     import mir.algorithm.setops: unionLength;
@@ -2477,7 +2477,7 @@ private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N,
     foreach (i; Iota!C)
         indeces[i] = seriesTuple[i].index;
 
-    immutable len = indeces[].unionLength;
+    immutable len = (()@trusted => indeces[].unionLength)();
 
     alias I = typeof(seriesTuple[0].index.front);
     alias E = typeof(seriesTuple[0].data.front);
@@ -2539,7 +2539,7 @@ private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N,
     }
     else
     {
-        unionSeriesImpl!(I, E)(seriesTuple, ret.lightScope);
+        unionSeriesImpl!(I, E)((()@trusted => seriesTuple[])(), ret.lightScope);
     }
 
     return () @trusted {return *cast(R*) &ret; }();
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index d9cd0693..c2a3f12e 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -243,7 +243,7 @@ template SmallArray(T, uint maxLength)
             return opIndex[index];
         }
 
-    const:
+    scope const:
 
         ///
         bool empty() @property
@@ -264,7 +264,7 @@ template SmallArray(T, uint maxLength)
         }
 
         /// Comparisons operator overloads
-        bool opEquals(ref const SmallArray rhs)
+        bool opEquals(scope ref const SmallArray rhs)
         {
             return opIndex == rhs.opIndex;
         }
@@ -276,25 +276,25 @@ template SmallArray(T, uint maxLength)
         }
 
         /// ditto
-        bool opEquals()(V[] array)
+        bool opEquals()(const scope V[] array)
         {
             return opIndex == array;
         }
 
         /// ditto
-        bool opEquals(uint rhsMaxLength)(auto ref SmallArray!(T, rhsMaxLength) array)
+        bool opEquals(uint rhsMaxLength)(auto ref scope SmallArray!(T, rhsMaxLength) array)
         {
             return opIndex == array.opIndex;
         }
 
         /// ditto
-        auto opCmp()(V[] array)
+        auto opCmp()(const scope V[] array)
         {
             return __cmp(opIndex, array);
         }
 
         /// ditto
-        auto opCmp(uint rhsMaxLength)(auto ref SmallArray!(T, rhsMaxLength) array)
+        auto opCmp(uint rhsMaxLength)(auto ref scope const SmallArray!(T, rhsMaxLength) array)
         {
             return __cmp(opIndex, array.opIndex);
         }
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index f2e844ae..38cf3ddc 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -234,10 +234,10 @@ scope nothrow:
         return opIndex()[range[0] .. range[1]];
     }
 
-const:
+scope const:
 
     /// ditto
-    size_t[2] opSlice(size_t pos : 0)(size_t i, size_t j) scope {
+    size_t[2] opSlice(size_t pos : 0)(size_t i, size_t j) {
         return [i, j];
     }
 
@@ -266,38 +266,38 @@ const:
     }
 
     /// Comparisons operator overloads
-    bool opEquals(ref scope const SmallString rhs) scope
+    bool opEquals(ref scope const SmallString rhs)
     {
         return _data == rhs._data;
     }
 
     /// ditto
-    bool opEquals(SmallString rhs) scope
+    bool opEquals(SmallString rhs)
     {
         return _data == rhs._data;
     }
 
     /// ditto
-    bool opEquals(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
+    bool opEquals(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs)
         if (rhsMaxLength != maxLength)
     {
         return opIndex == rhs.opIndex;
     }
 
     /// ditto
-    bool opEquals()(scope const(char)[] str) scope
+    bool opEquals()(scope const(char)[] str)
     {
         return opIndex == str;
     }
 
     /// ditto
-    int opCmp(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs) scope
+    int opCmp(uint rhsMaxLength)(auto ref scope const SmallString!rhsMaxLength rhs)
     {
         return __cmp(opIndex, rhs.opIndex);
     }
 
     /// ditto
-    int opCmp()(scope const(char)[] str) scope
+    int opCmp()(scope const(char)[] str)
     {
         return __cmp(opIndex, str);
     }
diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index 64ae2283..6c9af852 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -202,7 +202,8 @@ mixin template FileMembers()
             if (__ctfe)
                 return this;
             import mir.format: print;
-            return print!char(this, value);
+            print!char(this, value);
+            return this;
         }
 
         /// Prints new line and flushes the stream
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index de1a2b52..5d01b4da 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -7,6 +7,7 @@ AlgebraicREF = $(GREF_ALTTEXT mir-core, $(TT $1), $1, mir, algebraic)$(NBSP)
 module mir.string_map;
 
 import std.traits;
+import mir.internal.meta: basicElementType;
 
 /++
 Checks if the type is instance of $(LREF StringMap).
@@ -29,13 +30,15 @@ Params:
     T = mutable value type, can be instance of $(AlgebraicREF Algebraic) for example.
     U = an unsigned type that can hold an index of keys. `U.max` must be less then the maximum possible number of struct members.
 +/
-struct StringMap(T, U = uint)
-    if (!is(typeof(T.opPostMove)) && __traits(isUnsigned, U))
+struct StringMap(T)
+    if (!is(typeof(T.opPostMove)))
 {
     import mir.utility: _expect;
     import core.lifetime: move;
     import mir.conv: emplaceRef;
 
+    private alias U = uint;
+
     ///
     static struct KeyValue
     {
@@ -48,13 +51,35 @@ struct StringMap(T, U = uint)
     ///
     alias serdeKeysProxy = Unqual!T;
 
-    ///
+    /// `hashOf` Implementation. Doesn't depend on order
+    size_t toHash() scope const //@trusted pure nothrow @nogc
+    {
+        if (implementation is null)
+            return 0;
+        size_t hash;
+        foreach (i, index; implementation.indices)
+        {
+            hash = hashOf(implementation._keys[index], hash);
+            hash = hashOf(implementation._values[index], hash);
+        }
+        return hash;
+    }
+
+    /// `==` implementation. Doesn't depend on order
     // current implementation is workaround for linking bugs when used in self referencing algebraic types
-    bool opEquals(V)(scope const StringMap!(V, U) rhs) const @trusted
+    bool opEquals(V)(scope const StringMap!V rhs) scope const @trusted
     {
+        import std.traits: isAggregateType;
         // NOTE: moving this to template restriction fails with recursive template instanation
-        static assert(is(typeof(T.init == V.init) : bool),
-                      "Unsupported rhs of type " ~ typeof(rhs).stringof);
+        static if (isAggregateType!T)
+        {
+            if (false)
+            {
+                T a;
+                V b;
+                auto c = a.opEquals(b);
+            }
+        }
         if (implementation is null)
             return rhs.length == 0;
         if (rhs.implementation is null)
@@ -67,8 +92,9 @@ struct StringMap(T, U = uint)
                 return false;
         return true;
     }
+
     /// ditto
-    bool opEquals(K, V)(scope const const(V)[const(K)] rhs) const
+    bool opEquals(K, V)(scope const const(V)[const(K)] rhs) scope const
     if (is(typeof(K.init == string.init) : bool) &&
         is(typeof(V.init == T.init) : bool))
     {
@@ -162,7 +188,7 @@ struct StringMap(T, U = uint)
     }
 
     ///
-    string toString()() const
+    string toString()() const scope
     {
         import mir.format: stringBuf;
         stringBuf buffer;
@@ -171,7 +197,7 @@ struct StringMap(T, U = uint)
     }
 
     ///ditto
-    void toString(W)(scope ref W w) const
+    void toString(W)(ref scope W w) const scope
     {
         bool next;
         w.put('[');
@@ -1014,6 +1040,42 @@ struct StringMap(T, U = uint)
         return this;
     }
 
+    private alias E = .basicElementType!T;
+
+    import std.traits: isAssociativeArray, isAggregateType;
+    static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
+    /// `opCmp` Implementation. Doesn't depend on order
+    int opCmp()(ref scope const typeof(this) rhs) scope const @trusted // pure nothrow @nogc
+    {
+        if (sizediff_t d = length - rhs.length)
+            return d < 0 ? -1 : 1;
+        if (length == 0)
+            return 0;
+
+        foreach (i, index; implementation.indices)
+            if (auto d = __cmp(implementation._keys[index], rhs.implementation._keys[rhs.implementation._indices[i]]))
+                return d;
+        foreach (i, index; implementation.indices)
+            static if (__traits(compiles, __cmp(implementation._values[index], rhs.implementation._values[rhs.implementation._indices[i]])))
+            {
+                if (auto d = __cmp(implementation._values[index], rhs.implementation._values[rhs.implementation._indices[i]]))
+                    return d;
+            }
+            else
+            static if (__traits(hasMember, T, "opCmp"))
+            {
+                if (auto d = implementation._values[index].opCmp(rhs.implementation._values[rhs.implementation._indices[i]]))
+                    return d;
+            }
+            else
+            {
+                return
+                    implementation._values[index] < rhs.implementation._values[rhs.implementation._indices[i]] ? -1 :
+                    implementation._values[index] > rhs.implementation._values[rhs.implementation._indices[i]] ? +1 : 0;
+            }
+        return false;
+    }
+
     private Impl* implementation;
 }
 
@@ -1029,6 +1091,11 @@ version(mir_test)
         {
             return x == rhs.x;
         }
+
+        override size_t toHash() @safe const scope pure nothrow @nogc
+        {
+            return x;
+        }
     }
     StringMap!(const C) table;
     const v0 = new C(42);
diff --git a/source/mir/test.d b/source/mir/test.d
index 751ded7e..850cc2ed 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -67,7 +67,7 @@ struct Should(T)
 
     static if(!is(immutable T == immutable ubyte[]))
         ///
-    void opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__)
+    void opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__) @trusted
     {
         import mir.format: stringBuf, getData;
         static if (__traits(isFloating, R))
@@ -85,8 +85,8 @@ struct Should(T)
     }
     else
     /// ditto
-    void opEquals(scope const ubyte[] expected, string file = __FILE__, int line = __LINE__)
-        @safe pure nothrow @nogc
+    void opEquals(scope const ubyte[] expected, string file = __FILE__, int line = __LINE__) @trusted
+        pure nothrow @nogc
     {
         import mir.format: stringBuf, getData;
         if (value == expected)
@@ -122,7 +122,7 @@ unittest
 }
 
 ///
-void should(alias fun, T, R)(const T value, const R expected, string file = __FILE__, int line = __LINE__)
+void should(alias fun = "a == b", T, R)(const T value, const R expected, string file = __FILE__, int line = __LINE__)
 {
     import mir.functional;
     import mir.format: stringBuf, getData;
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 7c9bdc07..fce6cec2 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -898,7 +898,7 @@ struct Timestamp
         }
 
         /// ditto
-        void toISOStringImp(W)(scope ref W w) const scope
+        void toISOStringImp(W)(ref scope W w) const scope
             // if (isOutputRange!(W, char))
         {
             import mir.format: printZeroPad;

From bfed0091d0a4d9f47019103e8e08837ed637a997 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Sep 2022 07:27:31 +0700
Subject: [PATCH 499/573] more dip1000

---
 source/mir/algorithm/iteration.d   | 6 +++---
 source/mir/ndslice/concatenation.d | 6 +++---
 source/mir/small_array.d           | 4 ++--
 source/mir/small_string.d          | 6 +++---
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index b834b351..e0db8cb4 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -2608,7 +2608,7 @@ template equal(alias pred = "a == b")
         Returns:
             `true` any of the elements verify `pred` and `false` otherwise.
         +/
-        bool equal(Slices...)(Slices slices)
+        bool equal(Slices...)(Slices slices) @safe
             if (Slices.length >= 2)
         {
             import mir.internal.utility;
@@ -4008,14 +4008,14 @@ struct Filter(alias pred, Range)
         _input.popFront;
     }
 
-    auto ref front() @property return scope
+    auto ref front() @safe @property return scope
     {
         assert(!_input.empty, "Attempting to fetch the front of an empty Filter.");
         version(assert) assert(_freshEmpty, "Attempting to fetch the front of a Filter without calling '.empty' method ahead.");
         return _input.front;
     }
 
-    bool empty() scope @property
+    bool empty() @safe scope @property
     {
         version(assert) _freshEmpty = true;
         for (;;)
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index 38a403a9..670e3aa5 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -261,7 +261,7 @@ struct Concatenation(size_t dim, Slices...)
     }
 
     /// Length primitive
-    size_t length(size_t d = 0)() const @property
+    size_t length(size_t d = 0)() scope const @property
     {
         static if (d == dim)
         {
@@ -277,7 +277,7 @@ struct Concatenation(size_t dim, Slices...)
     }
 
     /// Total elements count in the concatenation.
-    size_t elementCount()() const @property
+    size_t elementCount()() scope const @property
     {
         size_t count = 1;
         foreach(i; Iota!N)
@@ -331,7 +331,7 @@ struct Concatenation(size_t dim, Slices...)
     }
 
     /// ditto
-    auto front(size_t d = 0)()
+    auto ref front(size_t d = 0)() return scope
     {
         static if (d == dim)
         {
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index c2a3f12e..3e8d9705 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -232,13 +232,13 @@ template SmallArray(T, uint maxLength)
 
         The alias helps with `[]`, `[i]`, `[i .. j]`, `==`, and `!=` operations and implicit conversion to strings.
         +/
-        inout(T)[] opIndex() inout @trusted return scope
+        inout(T)[] opIndex() inout @trusted scope return
         {
             return _data[0 .. _length];
         }
 
         ///
-        ref inout(T) opIndex(size_t index) inout return scope
+        ref inout(T) opIndex(size_t index) inout scope return
         {
             return opIndex[index];
         }
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 38cf3ddc..54b2455a 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -206,7 +206,7 @@ scope nothrow:
 
     The method implement with `[]` operation.
     +/
-    inout(char)[] opIndex() inout @trusted return scope
+    inout(char)[] opIndex() inout @trusted scope return
     {
         size_t i;
         if (__ctfe)
@@ -217,7 +217,7 @@ scope nothrow:
     }
 
     ///
-    ref inout(char) opIndex(size_t index) inout return scope
+    ref inout(char) opIndex(size_t index) inout scope return
     {
         return opIndex[index];
     }
@@ -227,7 +227,7 @@ scope nothrow:
 
     The method implement with `[i .. j]` operation.
     +/
-    inout(char)[] opIndex(size_t[2] range) inout @trusted return scope
+    inout(char)[] opIndex(size_t[2] range) inout @trusted scope return
     in (range[0] <= range[1])
     in (range[1] <= this.length)
     {

From f0dc3b3038918ef8381866664e64e8db922a13d9 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Sep 2022 18:38:21 +0700
Subject: [PATCH 500/573] improve annotated. opCmp

---
 source/mir/annotated.d | 4 ++--
 source/mir/serde.d     | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 46d69c68..340cd1b3 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -111,9 +111,9 @@ struct Annotated(T) {
     private alias E = .basicElementType!T;
 
     import std.traits: isAssociativeArray, isAggregateType;
-    static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
     ///
-    int opCmp()(ref scope const typeof(this) rhs) scope const // pure nothrow @nogc
+    int opCmp()(ref scope const typeof(this) rhs) scope const pure nothrow @nogc
+        if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
     {
         if (auto d = __cmp(annotations, rhs.annotations))
             return d;
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 9bfdac11..b9ecede0 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -2210,7 +2210,7 @@ struct serdeDiscriminatedField
 template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped)
 {
     ///
-    SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, ref T value, ref SerdeFlags!T requiredFlags, ref Context context)
+    SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, scope ref T value, scope ref SerdeFlags!T requiredFlags, scope ref Context context)
     {
         import core.lifetime: move;
         import mir.conv: to;

From 63b18c5aaa9e39a8aa79c902ca72271003168102 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 21 Sep 2022 21:35:45 +0700
Subject: [PATCH 501/573] add yaml timestamp parsing

---
 source/mir/date.d      |  12 ++---
 source/mir/timestamp.d | 101 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 91 insertions(+), 22 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index 80446cde..08f408d8 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -219,12 +219,12 @@ class DateTimeException : Exception
 
 version(D_Exceptions)
 {
-    private static immutable InvalidMonth = new DateTimeException("Invalid Month");
-    private static immutable InvalidDay = new DateTimeException("Invalid Day");
-    private static immutable InvalidISOString = new DateTimeException("Invalid ISO String");
-    private static immutable InvalidISOExtendedString = new DateTimeException("Invalid ISO Extended String");
-    private static immutable InvalidSimpleString = new DateTimeException("Invalid Simple String");
-    private static immutable InvalidString = new DateTimeException("Invalid String");
+    private static immutable InvalidMonth = new DateTimeException("Date: Invalid Month");
+    private static immutable InvalidDay = new DateTimeException("Date: Invalid Day");
+    private static immutable InvalidISOString = new DateTimeException("Date: Invalid ISO String");
+    private static immutable InvalidISOExtendedString = new DateTimeException("Date: Invalid ISO Extended String");
+    private static immutable InvalidSimpleString = new DateTimeException("Date: Invalid Simple String");
+    private static immutable InvalidString = new DateTimeException("Date: Invalid String");
 }
 
 version (mir_test)
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index fce6cec2..354bd93c 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -25,12 +25,13 @@ class DateTimeException : Exception
 
 version(D_Exceptions)
 {
-    private static immutable InvalidMonth = new DateTimeException("Invalid Month");
-    private static immutable InvalidDay = new DateTimeException("Invalid Day");
-    private static immutable InvalidISOString = new DateTimeException("Invalid ISO String");
-    private static immutable InvalidISOExtendedString = new DateTimeException("Invalid ISO Extended String");
-    private static immutable InvalidString = new DateTimeException("Invalid String");
-    private static immutable ExpectedDuration = new DateTimeException("Expected Duration");
+    private static immutable InvalidMonth = new DateTimeException("Timestamp: Invalid Month");
+    private static immutable InvalidDay = new DateTimeException("Timestamp: Invalid Day");
+    private static immutable InvalidISOString = new DateTimeException("Timestamp: Invalid ISO String");
+    private static immutable InvalidISOExtendedString = new DateTimeException("Timestamp: Invalid ISO Extended String");
+    private static immutable InvalidYamlString = new DateTimeException("Timestamp: Invalid YAML String");
+    private static immutable InvalidString = new DateTimeException("Timestamp: Invalid String");
+    private static immutable ExpectedDuration = new DateTimeException("Timestamp: Expected Duration");
 }
 
 /++
@@ -1135,6 +1136,38 @@ struct Timestamp
         static assert(Timestamp(2021, 01, 29,  4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30"));
     }
 
+    /++
+    Creates a $(LREF Timestamp) from a YAML string format
+    or its leading part allowed by the standard.
+
+    Params:
+        str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates.
+        value = (optional) result value.
+
+    Throws:
+        $(LREF DateTimeException) if the given string is
+        not in the correct format. Two arguments overload is `nothrow`.
+    Returns:
+        `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload.
+    +/
+    alias fromYamlString = fromISOStringImpl!(true, true);
+
+    ///
+    version (mir_test)
+    @safe unittest
+    {
+        //canonical:        
+        assert(Timestamp.fromYamlString("2001-12-15T02:59:43.1Z") == Timestamp("2001-12-15T02:59:43.1Z"));
+        //valid iso8601:    
+        assert(Timestamp.fromYamlString("2001-12-14t21:59:43.1-05:30") == Timestamp("2001-12-14T21:59:43.1-05:30"));
+        //yamle separated:  
+        assert(Timestamp.fromYamlString("2001-12-14 21:59:43.1 -5") == Timestamp("2001-12-14T21:59:43.1-05"));
+        //no time zone (Z): 
+        assert(Timestamp.fromYamlString("2001-12-15 2:59:43.10") == Timestamp("2001-12-15T02:59:43.10"));
+        //date (00:00:00Z): 
+        assert(Timestamp.fromYamlString("2002-12-14") == Timestamp("2002-12-14"));
+    }
+
     version (mir_test)
     @safe unittest
     {
@@ -1246,7 +1279,7 @@ struct Timestamp
         throw InvalidString;
     }
 
-    template fromISOStringImpl(bool ext)
+    template fromISOStringImpl(bool ext, bool yaml = false)
     {
         static Timestamp fromISOStringImpl(C)(scope const(C)[] str) @safe pure
             if (isSomeChar!C)
@@ -1254,7 +1287,13 @@ struct Timestamp
             Timestamp ret;
             if (fromISOStringImpl(str, ret))
                 return ret;
-            throw InvalidISOExtendedString;
+            static if (yaml)
+                throw InvalidYamlString;
+            else
+            static if (ext)
+                throw InvalidISOExtendedString;
+            else
+                throw InvalidISOString;
         }
 
         static bool fromISOStringImpl(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc
@@ -1263,9 +1302,9 @@ struct Timestamp
             import mir.parse: fromString, parse;
 
             static if (ext)
-                auto isOnlyTime = str.length >= 3 && (str[0] == 'T' || str[2] == ':');
+                auto isOnlyTime = str.length >= 3 && ((str[0] == 'T' || (yaml && (str[0] == 't' || str[0] == ' ' || str[0] == '\t'))) || str[2] == ':');
             else
-                auto isOnlyTime = str.length >= 3 && str[0] == 'T';
+                auto isOnlyTime = str.length >= 3 && (str[0] == 'T' || (yaml && (str[0] == 't' || str[0] == ' ' || str[0] == '\t')));
 
             if (!isOnlyTime)
             {
@@ -1303,7 +1342,7 @@ struct Timestamp
                     return false;
                 str = str[2 .. $];
                 value.precision = Precision.month;
-                if (str.length == 0 || str == "T")
+                if (str.length == 0 || str.length == 1 && (str[0] == 'T' || (yaml && (str[0] == 't' || str[0] == ' ' || str[0] == '\t'))))
                     return ext;
 
                 static if (ext)
@@ -1324,7 +1363,7 @@ struct Timestamp
 
             // str isn't empty here
             // T
-            if (str[0] == 'T')
+            if ((str[0] == 'T' || (yaml && (str[0] == 't' || str[0] == ' ' || str[0] == '\t'))))
             {
                 str = str[1 .. $];
                 // OK, onlyTime requires length >= 3
@@ -1341,8 +1380,20 @@ struct Timestamp
 
             // hh
             if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.hour))
-                return false;
-            str = str[2 .. $];
+            {
+                static if (yaml)
+                {
+                    if (str.length < 1 || !str[0].isDigit || !fromString(str[0 .. 1], value.hour))
+                        return false;
+                    else
+                        str = str[1 .. $];
+                }
+                else
+                    return false;
+            }
+            else
+                str = str[2 .. $];
+
             if (str.length == 0)
                 return true;
 
@@ -1408,14 +1459,32 @@ struct Timestamp
 
         TZ:
 
+            static if (yaml)
+            {
+                if (str.length && (str[0] == ' ' || str[0] == '\t'))
+                    str = str[1 .. $];
+            }
+
             if (str == "Z")
                 return true;
 
             int hour;
             int minute;
             if (str.length < 3 || str[0].isDigit || !fromString(str[0 .. 3], hour))
-                return false;
-            str = str[3 .. $];
+            {
+                static if (yaml)
+                {
+                    if (str.length < 2 || str[0].isDigit || !fromString(str[0 .. 2], hour))
+                        return false;
+                    str = str[2 .. $];
+                }
+                else
+                    return false;
+            }
+            else
+            {
+                str = str[3 .. $];
+            }
 
             if (str.length)
             {

From 4f7b0903d693b665132af849f335ce91ee6d947f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 22 Sep 2022 13:05:36 +0700
Subject: [PATCH 502/573] minor fixups

---
 source/mir/annotated.d     | 3 ++-
 source/mir/ndslice/slice.d | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 340cd1b3..406687b7 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -46,12 +46,13 @@ struct Annotated(T) {
         private T* _value;
         ///
         ref inout(T) value() inout @property
+            in(_value)
         {
             return *_value;
         }
 
         ///
-        ref T value(T value) @property
+        ref T value(T value) @property return scope
         {
             if (_value is null)
             {
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index f1b4bcf7..cd89cb0d 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -1832,7 +1832,7 @@ public:
     /+
     Returns: `true` if for any dimension of completely unpacked slice the length equals to `0`, and `false` otherwise.
     +/
-    private bool anyRUEmpty()() @trusted @property scope const
+    private bool anyRUEmpty()() @trusted scope const
     {
         static if (isInstanceOf!(SliceIterator, Iterator))
         {

From 7e39bf345c992bae17ef0285054147d11d87e03d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 22 Sep 2022 18:06:08 +0700
Subject: [PATCH 503/573] fixup hash functions

---
 source/mir/annotated.d  | 11 +++++++++++
 source/mir/string_map.d |  5 ++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 406687b7..2b19e050 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -74,6 +74,17 @@ struct Annotated(T) {
         {
             return annotations == rhs.annotations && value == rhs.value;
         }
+
+        size_t toHash() scope @trusted const pure nothrow @nogc
+        {
+            static if (__traits(compiles, hashOf(value)))
+                return hashOf(value);
+            else
+            {
+                debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof);
+                return cast(size_t)_value;
+            }
+        }
     }
     else
     {
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 5d01b4da..e8a5a588 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -60,7 +60,10 @@ struct StringMap(T)
         foreach (i, index; implementation.indices)
         {
             hash = hashOf(implementation._keys[index], hash);
-            hash = hashOf(implementation._values[index], hash);
+            static if (__traits(hasMember, T, "toHash"))
+               hash = hashOf(implementation._values[index].toHash, hash);
+            else
+               hash = hashOf(implementation._values[index], hash);
         }
         return hash;
     }

From ae834483654de4598834fe1dd87ac5d10791f208 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 22 Sep 2022 19:31:43 +0700
Subject: [PATCH 504/573] update toHash for stirng map

---
 source/mir/string_map.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index e8a5a588..65efe0f6 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -52,7 +52,7 @@ struct StringMap(T)
     alias serdeKeysProxy = Unqual!T;
 
     /// `hashOf` Implementation. Doesn't depend on order
-    size_t toHash() scope const //@trusted pure nothrow @nogc
+    size_t toHash() scope const pure @trusted  nothrow @nogc
     {
         if (implementation is null)
             return 0;

From 7ff0145f88dc4e11a23252f532e45ff0ebd66734 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 22 Sep 2022 20:19:07 +0700
Subject: [PATCH 505/573] update deps

---
 dub.sdl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index b56b3980..06247155 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilia Ki" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And
 copyright "2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.1.106"
+dependency "mir-core" version=">=1.3.0"
 
 // dflags "-mtriple=aarch64-linux-gnu"
 

From 4c70674357d4a2015d7b390e3f40e246cadeec3c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 22 Sep 2022 21:08:52 +0700
Subject: [PATCH 506/573] update compilers

---
 .github/workflows/compilers.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/compilers.json b/.github/workflows/compilers.json
index dc811c76..a737cdb0 100644
--- a/.github/workflows/compilers.json
+++ b/.github/workflows/compilers.json
@@ -2,9 +2,9 @@
     "dmd-master",
     "dmd-latest",
     "dmd-beta",
-    "dmd-2.098.1",
+    "dmd-2.100.2",
     "ldc-master",
     "ldc-latest",
     "ldc-beta",
-    "ldc-1.28.1"
+    "ldc-1.30.1"
 ]
\ No newline at end of file

From e2a27b0f6188db49be531883ec63d5aa2c1ba2a0 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 22 Sep 2022 22:42:36 +0700
Subject: [PATCH 507/573] update stringmap

---
 source/mir/string_map.d | 34 +++++++++++++++-------------------
 1 file changed, 15 insertions(+), 19 deletions(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 65efe0f6..5a854096 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -23,6 +23,8 @@ unittest
     static assert(!isStringMap!int);
 }
 
+private alias U = uint;
+
 /++
 Ordered string-value associative array with extremely fast lookup.
 
@@ -31,14 +33,12 @@ Params:
     U = an unsigned type that can hold an index of keys. `U.max` must be less then the maximum possible number of struct members.
 +/
 struct StringMap(T)
-    if (!is(typeof(T.opPostMove)))
+    // if (!is(typeof(T.opPostMove)))
 {
     import mir.utility: _expect;
     import core.lifetime: move;
     import mir.conv: emplaceRef;
 
-    private alias U = uint;
-
     ///
     static struct KeyValue
     {
@@ -48,9 +48,6 @@ struct StringMap(T)
         T value;
     }
 
-    ///
-    alias serdeKeysProxy = Unqual!T;
-
     /// `hashOf` Implementation. Doesn't depend on order
     size_t toHash() scope const pure @trusted  nothrow @nogc
     {
@@ -70,19 +67,10 @@ struct StringMap(T)
 
     /// `==` implementation. Doesn't depend on order
     // current implementation is workaround for linking bugs when used in self referencing algebraic types
-    bool opEquals(V)(scope const StringMap!V rhs) scope const @trusted
+    bool opEquals(V)(scope const StringMap!V rhs) scope const @trusted pure @nogc nothrow
     {
         import std.traits: isAggregateType;
         // NOTE: moving this to template restriction fails with recursive template instanation
-        static if (isAggregateType!T)
-        {
-            if (false)
-            {
-                T a;
-                V b;
-                auto c = a.opEquals(b);
-            }
-        }
         if (implementation is null)
             return rhs.length == 0;
         if (rhs.implementation is null)
@@ -1043,10 +1031,8 @@ struct StringMap(T)
         return this;
     }
 
-    private alias E = .basicElementType!T;
-
     import std.traits: isAssociativeArray, isAggregateType;
-    static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
+    static if (!isAssociativeArray!(.basicElementType!T) && (!isAggregateType!(.basicElementType!T) || __traits(hasMember, .basicElementType!T, "opCmp")))
     /// `opCmp` Implementation. Doesn't depend on order
     int opCmp()(ref scope const typeof(this) rhs) scope const @trusted // pure nothrow @nogc
     {
@@ -1080,6 +1066,16 @@ struct StringMap(T)
     }
 
     private Impl* implementation;
+
+    ///
+    static if (is(T == const) || is(T == immutable))
+    {
+        alias serdeKeysProxy = Unqual!T;
+    }
+    else
+    {
+        alias serdeKeysProxy = T;
+    }
 }
 
 version(mir_test)

From 318b2d24ebdbc1727aa53cb347b390a4e6d7f923 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 23 Sep 2022 17:22:47 +0700
Subject: [PATCH 508/573] improve Timestamp.fromString

---
 source/mir/timestamp.d | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 354bd93c..36a46246 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -1243,10 +1243,10 @@ struct Timestamp
     }
 
     /++
-    Creates a $(LREF Timestamp) from a string with the format YYYY-MM-DD, YYYYMMDD, or YYYY-Mon-DD.
+    Creates a $(LREF Timestamp) from a string.
 
     Params:
-        str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) and $(LREF .Timestamp.toISOString) format dates. The function is case sensetive.
+        str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) and $(LREF .Timestamp.toISOString) format dates, also YAML like spaces seprated strings are accepted. The function is case sensetive.
         value = (optional) result value.
 
     Throws:
@@ -1257,7 +1257,7 @@ struct Timestamp
     +/
     static bool fromString(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc
     {
-        return fromISOExtString(str, value)
+        return fromYamlString(str, value)
             || fromISOString(str, value);
     }
 

From 34e123fa532e8627fba65c688b958dd27feae0a0 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 23 Sep 2022 18:50:39 +0700
Subject: [PATCH 509/573] update mir-core

---
 dub.sdl                             |  2 +-
 source/mir/interpolate/generic.d    |  4 +-
 source/mir/interpolate/package.d    |  8 +--
 source/mir/interpolate/polynomial.d |  6 +-
 source/mir/math/sum.d               | 90 ++++++++++++++---------------
 source/mir/ndslice/field.d          | 14 ++---
 source/mir/ndslice/iterator.d       | 64 ++++++++++----------
 source/mir/ndslice/ndfield.d        |  4 +-
 source/mir/ndslice/topology.d       | 20 +++----
 9 files changed, 106 insertions(+), 106 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 06247155..b9bc321e 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilia Ki" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And
 copyright "2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.3.0"
+dependency "mir-core" version=">=1.3.5"
 
 // dflags "-mtriple=aarch64-linux-gnu"
 
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index 5c7c22a4..37a9deab 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -192,11 +192,11 @@ extern(D):
         +/
         auto opCall(X)(in X x) const
         {
-            return opCall!(X)(RefTuple!(size_t, X)(this.findInterval(x), x));
+            return opCall!(X)(Tuple!(size_t, X)(this.findInterval(x), x));
         }
 
         ///
-        auto opCall(X)(RefTuple!(size_t, X) tuple) const
+        auto opCall(X)(Tuple!(size_t, X) tuple) const
         {
             X x = tuple[1];
             size_t idx = tuple[0];
diff --git a/source/mir/interpolate/package.d b/source/mir/interpolate/package.d
index 111082b6..1227d075 100644
--- a/source/mir/interpolate/package.d
+++ b/source/mir/interpolate/package.d
@@ -20,7 +20,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 +/
 module mir.interpolate;
 
-import mir.functional: RefTuple;
+import mir.functional: Tuple;
 import mir.math.common: optmath;
 import mir.ndslice.slice: Slice, Contiguous;
 import mir.primitives;
@@ -32,8 +32,8 @@ import std.traits: isInstanceOf;
 package ref iter(alias s)() { return s._iterator; };
 package alias GridVector(It) = Slice!It;
 
-package enum bool isInterval(T) = isInstanceOf!(RefTuple, T);
-package enum bool isInterval(alias T) = isInstanceOf!(RefTuple, T);
+package enum bool isInterval(T) = isInstanceOf!(Tuple, T);
+package enum bool isInterval(alias T) = isInstanceOf!(Tuple, T);
 
 package template Repeat(ulong n, L...)
 {
@@ -204,7 +204,7 @@ By default interpolants uses binary search to find appropriate interval,
 it has `O(log(.gridScopeView.length))` complexity.
 If an interval is given, interpolant uses it instead of binary search.
 +/
-RefTuple!(T, size_t) atInterval(T)(in T value, size_t intervalIndex)
+Tuple!(T, size_t) atInterval(T)(in T value, size_t intervalIndex)
 {
     return typeof(return)(value, intervalIndex);
 }
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index aaddbbf7..3cd0fd4b 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -16,7 +16,7 @@ module mir.interpolate.polynomial;
 ///
 public import mir.interpolate: atInterval;
 import core.lifetime: move;
-import mir.functional: RefTuple;
+import mir.functional: Tuple;
 import mir.internal.utility : isFloatingPoint, Iota;
 import mir.interpolate: findInterval;
 import mir.math.common;
@@ -185,12 +185,12 @@ scope const:
         pragma(inline, false)
         auto opCall(T x)
         {
-            return opCall!derivative(RefTuple!(size_t, T)(this.findInterval(x), x));
+            return opCall!derivative(Tuple!(size_t, T)(this.findInterval(x), x));
         }
 
         ///
         pragma(inline, false)
-        auto opCall(RefTuple!(size_t, T) tuple)
+        auto opCall(Tuple!(size_t, T) tuple)
         {
 
             const x = tuple[1];
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 01fd1a21..4e8e48c8 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1658,7 +1658,7 @@ public:
 version(mir_test)
 unittest
 {
-    import mir.functional: RefTuple, refTuple;
+    import mir.functional: Tuple, tuple;
     import mir.ndslice.topology: map, iota, retro;
     import mir.array.allocation: array;
     import std.math: isInfinity, isFinite, isNaN;
@@ -1666,50 +1666,50 @@ unittest
     Summator!(double, Summation.precise) summator = 0.0;
 
     enum double M = (cast(double)2) ^^ (double.max_exp - 1);
-    RefTuple!(double[], double)[] tests = [
-        refTuple(new double[0], 0.0),
-        refTuple([0.0], 0.0),
-        refTuple([1e100, 1.0, -1e100, 1e-100, 1e50, -1, -1e50], 1e-100),
-        refTuple([1e308, 1e308, -1e308], 1e308),
-        refTuple([-1e308, 1e308, 1e308], 1e308),
-        refTuple([1e308, -1e308, 1e308], 1e308),
-        refTuple([M, M, -2.0^^1000], 1.7976930277114552e+308),
-        refTuple([M, M, M, M, -M, -M, -M], 8.9884656743115795e+307),
-        refTuple([2.0^^53, -0.5, -2.0^^-54], 2.0^^53-1.0),
-        refTuple([2.0^^53, 1.0, 2.0^^-100], 2.0^^53+2.0),
-        refTuple([2.0^^53+10.0, 1.0, 2.0^^-100], 2.0^^53+12.0),
-        refTuple([2.0^^53-4.0, 0.5, 2.0^^-54], 2.0^^53-3.0),
-        refTuple([M-2.0^^970, -1, M], 1.7976931348623157e+308),
-        refTuple([double.max, double.max*2.^^-54], double.max),
-        refTuple([double.max, double.max*2.^^-53], double.infinity),
-        refTuple(iota([1000], 1).map!(a => 1.0/a).array , 7.4854708605503451),
-        refTuple(iota([1000], 1).map!(a => (-1.0)^^a/a).array, -0.69264743055982025), //0.693147180559945309417232121458176568075500134360255254120680...
-        refTuple(iota([1000], 1).map!(a => 1.0/a).retro.array , 7.4854708605503451),
-        refTuple(iota([1000], 1).map!(a => (-1.0)^^a/a).retro.array, -0.69264743055982025),
-        refTuple([double.infinity, -double.infinity, double.nan], double.nan),
-        refTuple([double.nan, double.infinity, -double.infinity], double.nan),
-        refTuple([double.infinity, double.nan, double.infinity], double.nan),
-        refTuple([double.infinity, double.infinity], double.infinity),
-        refTuple([double.infinity, -double.infinity], double.nan),
-        refTuple([-double.infinity, 1e308, 1e308, -double.infinity], -double.infinity),
-        refTuple([M-2.0^^970, 0.0, M], double.infinity),
-        refTuple([M-2.0^^970, 1.0, M], double.infinity),
-        refTuple([M, M], double.infinity),
-        refTuple([M, M, -1], double.infinity),
-        refTuple([M, M, M, M, -M, -M], double.infinity),
-        refTuple([M, M, M, M, -M, M], double.infinity),
-        refTuple([-M, -M, -M, -M], -double.infinity),
-        refTuple([M, M, -2.^^971], double.max),
-        refTuple([M, M, -2.^^970], double.infinity),
-        refTuple([-2.^^970, M, M, -0X0.0000000000001P-0 * 2.^^-1022], double.max),
-        refTuple([M, M, -2.^^970, 0X0.0000000000001P-0 * 2.^^-1022], double.infinity),
-        refTuple([-M, 2.^^971, -M], -double.max),
-        refTuple([-M, -M, 2.^^970], -double.infinity),
-        refTuple([-M, -M, 2.^^970, 0X0.0000000000001P-0 * 2.^^-1022], -double.max),
-        refTuple([-0X0.0000000000001P-0 * 2.^^-1022, -M, -M, 2.^^970], -double.infinity),
-        refTuple([2.^^930, -2.^^980, M, M, M, -M], 1.7976931348622137e+308),
-        refTuple([M, M, -1e307], 1.6976931348623159e+308),
-        refTuple([1e16, 1., 1e-16], 10_000_000_000_000_002.0),
+    Tuple!(double[], double)[] tests = [
+        tuple(new double[0], 0.0),
+        tuple([0.0], 0.0),
+        tuple([1e100, 1.0, -1e100, 1e-100, 1e50, -1, -1e50], 1e-100),
+        tuple([1e308, 1e308, -1e308], 1e308),
+        tuple([-1e308, 1e308, 1e308], 1e308),
+        tuple([1e308, -1e308, 1e308], 1e308),
+        tuple([M, M, -2.0^^1000], 1.7976930277114552e+308),
+        tuple([M, M, M, M, -M, -M, -M], 8.9884656743115795e+307),
+        tuple([2.0^^53, -0.5, -2.0^^-54], 2.0^^53-1.0),
+        tuple([2.0^^53, 1.0, 2.0^^-100], 2.0^^53+2.0),
+        tuple([2.0^^53+10.0, 1.0, 2.0^^-100], 2.0^^53+12.0),
+        tuple([2.0^^53-4.0, 0.5, 2.0^^-54], 2.0^^53-3.0),
+        tuple([M-2.0^^970, -1, M], 1.7976931348623157e+308),
+        tuple([double.max, double.max*2.^^-54], double.max),
+        tuple([double.max, double.max*2.^^-53], double.infinity),
+        tuple(iota([1000], 1).map!(a => 1.0/a).array , 7.4854708605503451),
+        tuple(iota([1000], 1).map!(a => (-1.0)^^a/a).array, -0.69264743055982025), //0.693147180559945309417232121458176568075500134360255254120680...
+        tuple(iota([1000], 1).map!(a => 1.0/a).retro.array , 7.4854708605503451),
+        tuple(iota([1000], 1).map!(a => (-1.0)^^a/a).retro.array, -0.69264743055982025),
+        tuple([double.infinity, -double.infinity, double.nan], double.nan),
+        tuple([double.nan, double.infinity, -double.infinity], double.nan),
+        tuple([double.infinity, double.nan, double.infinity], double.nan),
+        tuple([double.infinity, double.infinity], double.infinity),
+        tuple([double.infinity, -double.infinity], double.nan),
+        tuple([-double.infinity, 1e308, 1e308, -double.infinity], -double.infinity),
+        tuple([M-2.0^^970, 0.0, M], double.infinity),
+        tuple([M-2.0^^970, 1.0, M], double.infinity),
+        tuple([M, M], double.infinity),
+        tuple([M, M, -1], double.infinity),
+        tuple([M, M, M, M, -M, -M], double.infinity),
+        tuple([M, M, M, M, -M, M], double.infinity),
+        tuple([-M, -M, -M, -M], -double.infinity),
+        tuple([M, M, -2.^^971], double.max),
+        tuple([M, M, -2.^^970], double.infinity),
+        tuple([-2.^^970, M, M, -0X0.0000000000001P-0 * 2.^^-1022], double.max),
+        tuple([M, M, -2.^^970, 0X0.0000000000001P-0 * 2.^^-1022], double.infinity),
+        tuple([-M, 2.^^971, -M], -double.max),
+        tuple([-M, -M, 2.^^970], -double.infinity),
+        tuple([-M, -M, 2.^^970, 0X0.0000000000001P-0 * 2.^^-1022], -double.max),
+        tuple([-0X0.0000000000001P-0 * 2.^^-1022, -M, -M, 2.^^970], -double.infinity),
+        tuple([2.^^930, -2.^^980, M, M, M, -M], 1.7976931348622137e+308),
+        tuple([M, M, -1e307], 1.6976931348623159e+308),
+        tuple([1e16, 1., 1e-16], 10_000_000_000_000_002.0),
     ];
     foreach (i, test; tests)
     {
diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d
index 9f36768e..fd61c374 100644
--- a/source/mir/ndslice/field.d
+++ b/source/mir/ndslice/field.d
@@ -96,8 +96,8 @@ struct MapField(Field, alias _fun)
 
     auto ref opIndex(T...)(auto ref T index)
     {
-        import mir.functional: RefTuple, unref;
-        static if (is(typeof(_field[index]) : RefTuple!K, K...))
+        import mir.functional: Tuple, unref;
+        static if (is(typeof(_field[index]) : Tuple!K, K...))
         {
             auto t = _field[index];
             return mixin("_fun(" ~ _iotaArgs!(K.length, "t.expand[", "].unref, ") ~ ")");
@@ -157,8 +157,8 @@ struct VmapField(Field, Fun)
 
     auto ref opIndex(T...)(auto ref T index)
     {
-        import mir.functional: RefTuple, unref;
-        static if (is(typeof(_field[index]) : RefTuple!K, K...))
+        import mir.functional: Tuple, unref;
+        static if (is(typeof(_field[index]) : Tuple!K, K...))
         {
             auto t = _field[index];
             return mixin("_fun(" ~ _iotaArgs!(K.length, "t.expand[", "].unref, ") ~ ")");
@@ -239,7 +239,7 @@ struct ZipField(Fields...)
     if (Fields.length > 1)
 {
 @optmath:
-    import mir.functional: RefTuple, Ref, _ref;
+    import mir.functional: Tuple, Ref, _ref;
     import std.meta: anySatisfy;
 
     ///
@@ -268,10 +268,10 @@ struct ZipField(Fields...)
         alias Iterators = Fields;
         alias _iterators = _fields;
         import mir.ndslice.iterator: _zip_types, _zip_index;
-        return mixin("RefTuple!(_zip_types!Fields)(" ~ _zip_index!Fields ~ ")");
+        return mixin("Tuple!(_zip_types!Fields)(" ~ _zip_index!Fields ~ ")");
     }
 
-    auto opIndexAssign(Types...)(RefTuple!(Types) value, ptrdiff_t index)
+    auto opIndexAssign(Types...)(Tuple!(Types) value, ptrdiff_t index)
         if (Types.length == Fields.length)
     {
         foreach(i, ref val; value.expand)
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index e5c1c899..bbeeb6ed 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -599,7 +599,7 @@ struct ZipIterator(Iterators...)
 @optmath:
     import std.traits: ConstOf, ImmutableOf;
     import std.meta: staticMap;
-    import mir.functional: RefTuple, Ref, _ref;
+    import mir.functional: Tuple, Ref, _ref;
     ///
     Iterators _iterators;
 
@@ -626,14 +626,14 @@ struct ZipIterator(Iterators...)
     }
 
     auto opUnary(string op : "*")()
-    { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); }
+    { return mixin("Tuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); }
 
 
     auto opUnary(string op : "*")() const
-    { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); }
+    { return mixin("Tuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); }
 
     auto opUnary(string op : "*")() immutable
-    { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); }
+    { return mixin("Tuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); }
 
     void opUnary(string op)() scope
         if (op == "++" || op == "--")
@@ -643,9 +643,9 @@ struct ZipIterator(Iterators...)
     }
 
     auto opIndex()(ptrdiff_t index)
-    { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_index!Iterators ~ ")"); }
+    { return mixin("Tuple!(_zip_types!Iterators)(" ~ _zip_index!Iterators ~ ")"); }
 
-    auto opIndexAssign(Types...)(RefTuple!(Types) value, ptrdiff_t index)
+    auto opIndexAssign(Types...)(Tuple!(Types) value, ptrdiff_t index)
         if (Types.length == Iterators.length)
     {
         foreach(i, ref val; value.expand)
@@ -860,11 +860,11 @@ struct CachedIterator(Iterator, CacheIterator, FlagIterator)
 
 private enum map_primitives = q{
 
-    import mir.functional: RefTuple, autoExpandAndForward;
+    import mir.functional: Tuple, autoExpandAndForward;
 
     auto ref opUnary(string op : "*")()
     {
-        static if (is(typeof(*_iterator) : RefTuple!T, T...))
+        static if (is(typeof(*_iterator) : Tuple!T, T...))
         {
             auto t = *_iterator;
             return _fun(autoExpandAndForward!t);
@@ -875,7 +875,7 @@ private enum map_primitives = q{
 
     auto ref opIndex(ptrdiff_t index) scope
     {
-        static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+        static if (is(typeof(_iterator[0]) : Tuple!T, T...))
         {
             auto t = _iterator[index];
             return _fun(autoExpandAndForward!t);
@@ -888,7 +888,7 @@ private enum map_primitives = q{
     {
         auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return _fun(autoExpandAndForward!t) = value;
@@ -899,7 +899,7 @@ private enum map_primitives = q{
 
         auto ref opIndexUnary(string op)(ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return _fun(autoExpandAndForward!t);
@@ -910,7 +910,7 @@ private enum map_primitives = q{
 
         auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return mixin("_fun(autoExpandAndForward!t)" ~ op ~ "= value");
@@ -945,11 +945,11 @@ struct VmapIterator(Iterator, Fun)
         return VmapIterator!(LightImmutableOf!Iterator, LightImmutableOf!Fun)(.lightImmutable(_iterator), .lightImmutable(_fun));
     }
 
-    import mir.functional: RefTuple, autoExpandAndForward;
+    import mir.functional: Tuple, autoExpandAndForward;
 
     auto ref opUnary(string op : "*")()
     {
-        static if (is(typeof(*_iterator) : RefTuple!T, T...))
+        static if (is(typeof(*_iterator) : Tuple!T, T...))
         {
             auto t = *_iterator;
             return _fun(autoExpandAndForward!t);
@@ -960,7 +960,7 @@ struct VmapIterator(Iterator, Fun)
 
     auto ref opIndex(ptrdiff_t index) scope
     {
-        static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+        static if (is(typeof(_iterator[0]) : Tuple!T, T...))
         {
             auto t = _iterator[index];
             return _fun(autoExpandAndForward!t);
@@ -973,7 +973,7 @@ struct VmapIterator(Iterator, Fun)
     {
         auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return _fun(autoExpandAndForward!t) = value;
@@ -984,7 +984,7 @@ struct VmapIterator(Iterator, Fun)
 
         auto ref opIndexUnary(string op)(ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return mixin(op ~ "_fun(autoExpandAndForward!t)");
@@ -995,7 +995,7 @@ struct VmapIterator(Iterator, Fun)
 
         auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return mixin("_fun(autoExpandAndForward!t)" ~ op ~ "= value");
@@ -1045,11 +1045,11 @@ struct MapIterator(Iterator, alias _fun)
     ///
     static alias __map(alias fun1) = MapIterator__map!(Iterator, _fun, pipe!(_fun, fun1));
 
-    import mir.functional: RefTuple, autoExpandAndForward;
+    import mir.functional: Tuple, autoExpandAndForward;
 
     auto ref opUnary(string op : "*")()
     {
-        static if (is(typeof(*_iterator) : RefTuple!T, T...))
+        static if (is(typeof(*_iterator) : Tuple!T, T...))
         {
             auto t = *_iterator;
             return _fun(autoExpandAndForward!t);
@@ -1060,7 +1060,7 @@ struct MapIterator(Iterator, alias _fun)
 
     auto ref opIndex(ptrdiff_t index) scope
     {
-        static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+        static if (is(typeof(_iterator[0]) : Tuple!T, T...))
         {
             auto t = _iterator[index];
             return _fun(autoExpandAndForward!t);
@@ -1073,7 +1073,7 @@ struct MapIterator(Iterator, alias _fun)
     {
         auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return _fun(autoExpandAndForward!t) = value;
@@ -1084,7 +1084,7 @@ struct MapIterator(Iterator, alias _fun)
 
         auto ref opIndexUnary(string op)(ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return mixin(op ~ "_fun(autoExpandAndForward!t)");
@@ -1095,7 +1095,7 @@ struct MapIterator(Iterator, alias _fun)
 
         auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return mixin("_fun(autoExpandAndForward!t)" ~ op ~ "= value");
@@ -1212,10 +1212,10 @@ struct NeighboursIterator(Iterator, size_t N, alias _fun, bool around)
         return NeighboursIterator!(LightImmutableOf!Iterator, N, _fun, around)(.lightImmutable(_iterator), neighbours);
     }
 
-    import mir.functional: RefTuple, _ref;
+    import mir.functional: Tuple, _ref;
 
     private alias RA = Unqual!(typeof(_fun(_iterator[-1], _iterator[+1])));
-    private alias Result = RefTuple!(_zip_types!Iterator, RA);
+    private alias Result = Tuple!(_zip_types!Iterator, RA);
 
     auto ref opUnary(string op : "*")()
     {
@@ -1516,7 +1516,7 @@ Iterates a field using an iterator.
 +/
 struct IndexIterator(Iterator, Field)
 {
-    import mir.functional: RefTuple, autoExpandAndForward;
+    import mir.functional: Tuple, autoExpandAndForward;
 
 @optmath:
     ///
@@ -1541,7 +1541,7 @@ struct IndexIterator(Iterator, Field)
 
     auto ref opUnary(string op : "*")()
     {
-        static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+        static if (is(typeof(_iterator[0]) : Tuple!T, T...))
         {
             auto t = *_iterator;
             return _field[autoExpandAndForward!t];
@@ -1552,7 +1552,7 @@ struct IndexIterator(Iterator, Field)
 
     auto ref opIndex()(ptrdiff_t index)
     {
-        static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+        static if (is(typeof(_iterator[0]) : Tuple!T, T...))
         {
             auto t = _iterator[index];
             return _field[autoExpandAndForward!t];
@@ -1565,7 +1565,7 @@ struct IndexIterator(Iterator, Field)
     {
         auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return _field[autoExpandAndForward!t] = value;
@@ -1576,7 +1576,7 @@ struct IndexIterator(Iterator, Field)
 
         auto ref opIndexUnary(string op)(ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return mixin(op ~ "_field[autoExpandAndForward!t]");
@@ -1587,7 +1587,7 @@ struct IndexIterator(Iterator, Field)
 
         auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index)
         {
-            static if (is(typeof(_iterator[0]) : RefTuple!T, T...))
+            static if (is(typeof(_iterator[0]) : Tuple!T, T...))
             {
                 auto t = _iterator[index];
                 return mixin("_field[autoExpandAndForward!t]" ~ op ~ "= value");
diff --git a/source/mir/ndslice/ndfield.d b/source/mir/ndslice/ndfield.d
index 6bca052c..8e0e86cd 100644
--- a/source/mir/ndslice/ndfield.d
+++ b/source/mir/ndslice/ndfield.d
@@ -126,8 +126,8 @@ struct Cartesian(NdFields...)
     ///
     auto opIndex(size_t[N] indices...)
     {
-        import mir.functional : refTuple;
-        return mixin("refTuple(" ~ _indices!(NdFields) ~ ")");
+        import mir.functional : tuple;
+        return mixin("tuple(" ~ _indices!(NdFields) ~ ")");
     }
 }
 
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 889a47d5..91c94729 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -2000,16 +2000,16 @@ version(mir_ndslice_test) unittest
 @safe pure nothrow
 version(mir_ndslice_test) unittest
 {
-    import mir.functional: refTuple;
+    import mir.functional: tuple;
 
     auto s = linspace!double([5, 3], [1.0, 2.0], [0.0, 1.0]);
 
     assert(s == [
-        [refTuple(1.00, 0.00), refTuple(1.00, 0.5), refTuple(1.00, 1.0)],
-        [refTuple(1.25, 0.00), refTuple(1.25, 0.5), refTuple(1.25, 1.0)],
-        [refTuple(1.50, 0.00), refTuple(1.50, 0.5), refTuple(1.50, 1.0)],
-        [refTuple(1.75, 0.00), refTuple(1.75, 0.5), refTuple(1.75, 1.0)],
-        [refTuple(2.00, 0.00), refTuple(2.00, 0.5), refTuple(2.00, 1.0)],
+        [tuple(1.00, 0.00), tuple(1.00, 0.5), tuple(1.00, 1.0)],
+        [tuple(1.25, 0.00), tuple(1.25, 0.5), tuple(1.25, 1.0)],
+        [tuple(1.50, 0.00), tuple(1.50, 0.5), tuple(1.50, 1.0)],
+        [tuple(1.75, 0.00), tuple(1.75, 0.5), tuple(1.75, 1.0)],
+        [tuple(2.00, 0.00), tuple(2.00, 0.5), tuple(2.00, 1.0)],
         ]);
 
     assert(s.map!"a * b" == [
@@ -2917,7 +2917,7 @@ version(mir_ndslice_test) unittest
 
 /++
 Multiple functions can be passed to `map`.
-In that case, the element type of `map` is a refTuple containing
+In that case, the element type of `map` is a tuple containing
 one element for each function.
 +/
 @safe pure nothrow
@@ -3159,7 +3159,7 @@ version(mir_ndslice_test) unittest
 // TODO
 /+
 Multiple functions can be passed to `vmap`.
-In that case, the element type of `vmap` is a refTuple containing
+In that case, the element type of `vmap` is a tuple containing
 one element for each function.
 +/
 @safe pure nothrow
@@ -3919,7 +3919,7 @@ Params:
     sameStrides = if `true` assumes that all slices has the same strides.
     slices = list of slices
 Returns:
-    n-dimensional slice of elements refTuple
+    n-dimensional slice of elements tuple
 See_also: $(SUBREF slice, Slice.strides).
 +/
 template zip(bool sameStrides = false)
@@ -3929,7 +3929,7 @@ template zip(bool sameStrides = false)
     Params:
         slices = list of slices
     Returns:
-        n-dimensional slice of elements refTuple
+        n-dimensional slice of elements tuple
     See_also: $(SUBREF slice, Slice.strides).
     +/
     @optmath

From 7778489f7d0817a76f320f1cb32e5feb92121467 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 23 Sep 2022 20:12:38 +0700
Subject: [PATCH 510/573] fix mir.serde

---
 source/mir/serde.d | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index b9ecede0..8aea8b24 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -1865,11 +1865,14 @@ package template isAlgebraicAliasThis(T)
     {
         import mir.algebraic: isVariant;
         T* aggregate;
-        alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
-        static if (isVariant!A)
+        alias A = AliasSeq!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))));
+        static if (A.length != 1)
+            enum isAlgebraicAliasThis = false;
+        else
+        static if (isVariant!(A[0]))
             enum isAlgebraicAliasThis = true;
         else
-            enum isAlgebraicAliasThis = serdeIsDynamicAlgebraic!A;
+            enum isAlgebraicAliasThis = serdeIsDynamicAlgebraic!(A[0]);
     }
     else
     {

From dc8e3bd921805fba64626d19ae4a8e2cb34b93ff Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 24 Sep 2022 13:45:47 +0700
Subject: [PATCH 511/573] fix docs

---
 source/mir/timestamp.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 36a46246..8ba6b04c 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -1160,7 +1160,7 @@ struct Timestamp
         assert(Timestamp.fromYamlString("2001-12-15T02:59:43.1Z") == Timestamp("2001-12-15T02:59:43.1Z"));
         //valid iso8601:    
         assert(Timestamp.fromYamlString("2001-12-14t21:59:43.1-05:30") == Timestamp("2001-12-14T21:59:43.1-05:30"));
-        //yamle separated:  
+        //yaml space separated:  
         assert(Timestamp.fromYamlString("2001-12-14 21:59:43.1 -5") == Timestamp("2001-12-14T21:59:43.1-05"));
         //no time zone (Z): 
         assert(Timestamp.fromYamlString("2001-12-15 2:59:43.10") == Timestamp("2001-12-15T02:59:43.10"));

From 705132ecf2ee61d634b359ead6036a24a5a23d14 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 24 Sep 2022 13:46:53 +0700
Subject: [PATCH 512/573] ditto

---
 source/mir/timestamp.d | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 8ba6b04c..5cc5152e 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -1156,15 +1156,15 @@ struct Timestamp
     version (mir_test)
     @safe unittest
     {
-        //canonical:        
+        // canonical
         assert(Timestamp.fromYamlString("2001-12-15T02:59:43.1Z") == Timestamp("2001-12-15T02:59:43.1Z"));
-        //valid iso8601:    
+        // with lower 't' separator
         assert(Timestamp.fromYamlString("2001-12-14t21:59:43.1-05:30") == Timestamp("2001-12-14T21:59:43.1-05:30"));
-        //yaml space separated:  
+        // yaml space separated
         assert(Timestamp.fromYamlString("2001-12-14 21:59:43.1 -5") == Timestamp("2001-12-14T21:59:43.1-05"));
-        //no time zone (Z): 
+        // no time zone (Z)
         assert(Timestamp.fromYamlString("2001-12-15 2:59:43.10") == Timestamp("2001-12-15T02:59:43.10"));
-        //date (00:00:00Z): 
+        // date 00:00:00Z
         assert(Timestamp.fromYamlString("2002-12-14") == Timestamp("2002-12-14"));
     }
 

From a1749e98e72d7bfab1dd35ee631f25057b9cb525 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 25 Sep 2022 13:08:01 +0700
Subject: [PATCH 513/573] string are scalars for fuse

---
 dub.sdl                            |  2 +-
 source/mir/combinatorics/package.d | 43 ++++++++++++++++++------------
 source/mir/ndslice/fuse.d          | 42 +++++++++++++++++++++++++----
 3 files changed, 64 insertions(+), 23 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index b9bc321e..b1b7d87c 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -14,7 +14,7 @@ dependency "mir-core" version=">=1.3.5"
 buildType "unittest" {
     buildOptions "unittests" "debugMode" "debugInfo"
     versions "mir_bignum_test" "mir_bignum_test_llv"
-    // versions "mir_ndslice_test"
+    versions "mir_ndslice_test"
     versions "mir_test"
     dflags "-lowmem"
 }
diff --git a/source/mir/combinatorics/package.d b/source/mir/combinatorics/package.d
index e6660b45..226cbc70 100644
--- a/source/mir/combinatorics/package.d
+++ b/source/mir/combinatorics/package.d
@@ -15,11 +15,12 @@ import std.traits;
 version(mir_test) unittest
 {
     import mir.ndslice.fuse;
+    import std.string: representation;
 
-    assert(['a', 'b'].permutations.fuse == [['a', 'b'], ['b', 'a']]);
-    assert(['a', 'b'].cartesianPower(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'a'], ['b', 'b']]);
-    assert(['a', 'b'].combinations(2).fuse == [['a', 'b']]);
-    assert(['a', 'b'].combinationsRepeat(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'b']]);
+    assert(['a', 'b'].representation.permutations.fuse == [['a', 'b'], ['b', 'a']]);
+    assert(['a', 'b'].representation.cartesianPower(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'a'], ['b', 'b']]);
+    assert(['a', 'b'].representation.combinations(2).fuse == [['a', 'b']]);
+    assert(['a', 'b'].representation.combinationsRepeat(2).fuse == [['a', 'a'], ['a', 'b'], ['b', 'b']]);
 
     assert(permutations!ushort(2).fuse == [[0, 1], [1, 0]]);
     assert(cartesianPower!ushort(2, 2).fuse == [[0, 0], [0, 1], [1, 0], [1, 1]]);
@@ -323,9 +324,10 @@ struct IndexedRoR(Collection, Range)
 version(mir_test) unittest
 {
     import mir.ndslice.fuse;
+    import std.string: representation;
     // import mir.ndslice.topology: only;
 
-    auto projectionD = 2.permutations.indexedRoR("ab"d);
+    auto projectionD = 2.permutations.indexedRoR("ab"d.representation);
     assert(projectionD.fuse == [['a', 'b'], ['b', 'a']]);
 
     // auto projectionC = 2.permutations.indexedRoR(only('a', 'b'));
@@ -745,13 +747,15 @@ pure nothrow @safe version(mir_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.topology: iota;
+
     assert(iota(2).cartesianPower.fuse == [[0], [1]]);
     assert(iota(2).cartesianPower(2).fuse == [[0, 0], [0, 1], [1, 0], [1, 1]]);
 
     auto three_nums_two_bins = [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]];
     assert(iota(3).cartesianPower(2).fuse == three_nums_two_bins);
 
-    assert("AB"d.cartesianPower(2).fuse == ["AA"d, "AB"d, "BA"d, "BB"d]);
+    import std.string: representation;
+    assert("AB"d.representation.cartesianPower(2).fuse == ["AA"d, "AB"d, "BA"d, "BB"d]);
 }
 
 ///
@@ -781,16 +785,17 @@ pure nothrow @safe version(mir_test) unittest
     import mir.array.allocation: array;
     import mir.ndslice.topology: iota;
     import std.range: dropOne;
+    import std.string: representation;
 
     assert(iota(0).cartesianPower.length == 0);
-    assert("AB"d.cartesianPower(3).fuse == ["AAA"d, "AAB"d, "ABA"d, "ABB"d, "BAA"d, "BAB"d, "BBA"d, "BBB"d]);
+    assert("AB"d.representation.cartesianPower(3).fuse == ["AAA"d, "AAB"d, "ABA"d, "ABB"d, "BAA"d, "BAB"d, "BBA"d, "BBB"d]);
     auto expected = ["AA"d, "AB"d, "AC"d, "AD"d,
                      "BA"d, "BB"d, "BC"d, "BD"d,
                      "CA"d, "CB"d, "CC"d, "CD"d,
                      "DA"d, "DB"d, "DC"d, "DD"d];
-    assert("ABCD"d.cartesianPower(2).fuse == expected);
+    assert("ABCD"d.representation.cartesianPower(2).fuse == expected);
     // verify with array too
-    assert("ABCD"d.cartesianPower(2).fuse == expected);
+    assert("ABCD"d.representation.cartesianPower(2).fuse == expected);
 
     assert(iota(2).cartesianPower.front == [0]);
 
@@ -1023,9 +1028,10 @@ pure nothrow @safe version(mir_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.topology: iota;
+    import std.string: representation;
     assert(iota(3).combinations(2).fuse == [[0, 1], [0, 2], [1, 2]]);
-    assert("AB"d.combinations(2).fuse == ["AB"d]);
-    assert("ABC"d.combinations(2).fuse == ["AB"d, "AC"d, "BC"d]);
+    assert("AB"d.representation.combinations(2).fuse == ["AB"d]);
+    assert("ABC"d.representation.combinations(2).fuse == ["AB"d, "AC"d, "BC"d]);
 }
 
 ///
@@ -1054,14 +1060,15 @@ pure nothrow @safe version(mir_test) unittest
     import mir.array.allocation: array;
     import mir.ndslice.topology: iota;
     import std.range: dropOne;
+    import std.string: representation;
 
     assert(iota(0).combinations.length == 0);
     assert(iota(2).combinations.fuse == [[0], [1]]);
 
     auto expected = ["AB"d, "AC"d, "AD"d, "BC"d, "BD"d, "CD"d];
-    assert("ABCD"d.combinations(2).fuse == expected);
+    assert("ABCD"d.representation.combinations(2).fuse == expected);
     // verify with array too
-    assert("ABCD"d.combinations(2).fuse == expected);
+    assert("ABCD"d.representation.combinations(2).fuse == expected);
     assert(iota(2).combinations.front == [0]);
 
     // is copyable?
@@ -1329,11 +1336,12 @@ pure nothrow @safe version(mir_test) unittest
 {
     import mir.ndslice.fuse;
     import mir.ndslice.topology: iota;
+    import std.string: representation;
 
     assert(iota(2).combinationsRepeat.fuse == [[0], [1]]);
     assert(iota(2).combinationsRepeat(2).fuse == [[0, 0], [0, 1], [1, 1]]);
     assert(iota(3).combinationsRepeat(2).fuse == [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]);
-    assert("AB"d.combinationsRepeat(2).fuse == ["AA"d, "AB"d,  "BB"d]);
+    assert("AB"d.representation.combinationsRepeat(2).fuse == ["AA"d, "AB"d,  "BB"d]);
 }
 
 ///
@@ -1362,14 +1370,15 @@ version(mir_test) unittest
     import mir.array.allocation: array;
     import mir.ndslice.topology: iota;
     import std.range: dropOne;
+    import std.string: representation;
 
     assert(iota(0).combinationsRepeat.length == 0);
-    assert("AB"d.combinationsRepeat(3).fuse == ["AAA"d, "AAB"d, "ABB"d,"BBB"d]);
+    assert("AB"d.representation.combinationsRepeat(3).fuse == ["AAA"d, "AAB"d, "ABB"d,"BBB"d]);
 
     auto expected = ["AA"d, "AB"d, "AC"d, "AD"d, "BB"d, "BC"d, "BD"d, "CC"d, "CD"d, "DD"d];
-    assert("ABCD"d.combinationsRepeat(2).fuse == expected);
+    assert("ABCD"d.representation.combinationsRepeat(2).fuse == expected);
     // verify with array too
-    assert("ABCD"d.combinationsRepeat(2).fuse == expected);
+    assert("ABCD"d.representation.combinationsRepeat(2).fuse == expected);
 
     assert(iota(2).combinationsRepeat.front == [0]);
 
diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index 3621c031..aa232000 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -41,7 +41,7 @@ alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions);
 {
     import mir.ndslice.fuse;
     import mir.ndslice.slice : Contiguous, Slice;
-    import mir.ndslice.topology: iota;
+    import mir.ndslice.topology: iota, map, as;
     import mir.rc.array: RCI;
 
     enum ror = [
@@ -63,6 +63,11 @@ alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions);
     // matrix is contiguos
     static assert(is(typeof(matrix) == Slice!(int*, 2)));
     static assert(is(typeof(rcmatrix) == Slice!(RCI!int, 2)));
+
+    /// also works with strings
+    auto strMatrix = ror.map!(as!string).fuse;
+    static assert(is(typeof(strMatrix) == Slice!(string*, 2)));
+    assert(strMatrix.as!int == matrix);
 }
 
 /// Transposed
@@ -181,7 +186,7 @@ template fuseImpl(bool RC, T_, Dimensions...)
         r = parallelotope (ndrange) with length/shape and input range primitives.
     +/
     auto fuseImpl(NDRange)(NDRange r)
-        if (hasShape!NDRange)
+        if (isFusable!NDRange)
     {
         import mir.conv: emplaceRef;
         import mir.algorithm.iteration: each;
@@ -276,8 +281,19 @@ template fuseImpl(bool RC, T_, Dimensions...)
 
 private template fuseDimensionCount(R)
 {
+    static if (!isFusable!R)
+        enum size_t fuseDimensionCount = 0;
+    else
     static if (is(typeof(R.init.shape) : size_t[N], size_t N) && (isDynamicArray!R || __traits(hasMember, R, "front")))
-        enum size_t fuseDimensionCount = N + fuseDimensionCount!(DeepElementType!R);
+    {
+        static if (N == 1)
+            static if (isSomeChar!(typeof(R.init.front)))
+                enum size_t fuseDimensionCount = 0;
+            else
+                enum size_t fuseDimensionCount = N + fuseDimensionCount!(DeepElementType!R);
+        else
+            enum size_t fuseDimensionCount = N + fuseDimensionCount!(DeepElementType!R);
+    }
     else
         enum size_t fuseDimensionCount = 0;
 }
@@ -302,11 +318,23 @@ private static immutable shapeExceptionMsg = "fuseShape Exception: elements have
 version(D_Exceptions)
     static immutable shapeException = new Exception(shapeExceptionMsg);
 
+private template isFusable(Range)
+{
+    static if (hasShape!Range)
+    {
+        enum isFusable = !isSomeChar!(typeof(Range.init.front));
+    }
+    else
+    {
+        enum isFusable = false;
+    }
+}
+
 /+
 TODO docs
 +/
 size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r)
-    if (hasShape!Range)
+    if (isFusable!Range)
 {
     enum N = r.shape.length;
     enum RN = typeof(return).length;
@@ -387,12 +415,16 @@ size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r)
 }
 
 private template FuseElementType(NDRange)
-    if (fuseDimensionCount!NDRange >= 1)
+    if (isFusable!NDRange)
 {
+    static assert (fuseDimensionCount!NDRange);
     static if (fuseDimensionCount!NDRange == 1)
         alias FuseElementType = typeof(NDRange.init.front);
     else
+    static if (isFusable!(typeof(NDRange.init.front)))
         alias FuseElementType = FuseElementType!(typeof(NDRange.init.front));
+    else
+        alias FuseElementType = typeof(NDRange.init.front);
 }
 
 /++

From 08da19adb02cc467879a3dd1b1f93db28f0eb660 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 25 Sep 2022 16:20:25 +0700
Subject: [PATCH 514/573] simplify parsing

---
 source/mir/parse.d | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/source/mir/parse.d b/source/mir/parse.d
index b96981e2..9425c33f 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -308,12 +308,7 @@ bool fromString(T, C)(scope const(C)[] str, ref T value)
         auto ret = decimal.fromStringImpl(str, key);
         if (_expect(ret, true))
         {
-            switch(key) with(DecimalExponentKey)
-            {
-                case nan: value = decimal.coefficient.sign ? -T.nan : T.nan; break;
-                case infinity: value = decimal.coefficient.sign ? -T.infinity : T.infinity; break;
-                default: value =  cast(T) decimal; break;
-            }
+            value = cast(T) decimal;
         }
         return ret;
     }
@@ -331,6 +326,15 @@ version(mir_test)
     assert("123".fromString(value) && value == 123);
 }
 
+///
+version(mir_test)
+@safe pure nothrow @nogc unittest
+{
+    double value = 0;
+    assert("+Inf".fromString(value) && value == double.infinity);
+    assert("-nan".fromString(value) && value != value);
+}
+
 /++
 Single character parsing utilities.
 

From 4b8bb1d4e6f64fbb2a2cc051d8b40eb5c36d188a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sun, 25 Sep 2022 17:50:59 +0700
Subject: [PATCH 515/573] improve timestamp parsing

---
 source/mir/timestamp.d | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 5cc5152e..64d3cf38 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -1301,10 +1301,13 @@ struct Timestamp
         {
             import mir.parse: fromString, parse;
 
+            if (str.length < 4)
+                return false;
+
             static if (ext)
-                auto isOnlyTime = str.length >= 3 && ((str[0] == 'T' || (yaml && (str[0] == 't' || str[0] == ' ' || str[0] == '\t'))) || str[2] == ':');
+                auto isOnlyTime = (str[0] == 'T' || yaml && (str[0] == 't')) || str[2] == ':';
             else
-                auto isOnlyTime = str.length >= 3 && (str[0] == 'T' || (yaml && (str[0] == 't' || str[0] == ' ' || str[0] == '\t')));
+                auto isOnlyTime = str[0] == 'T' || yaml && (str[0] == 't');
 
             if (!isOnlyTime)
             {

From 38b9fb3a695b0a9344b80573a69bd892ac1bc1b2 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 26 Sep 2022 15:44:53 +0700
Subject: [PATCH 516/573] add mir.string

---
 source/mir/string.d | 314 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 314 insertions(+)
 create mode 100644 source/mir/string.d

diff --git a/source/mir/string.d b/source/mir/string.d
new file mode 100644
index 00000000..44ac691d
--- /dev/null
+++ b/source/mir/string.d
@@ -0,0 +1,314 @@
+/++
+$(H1 String routines)
+
+The module contains SIMD-accelerated string routines.
+
+Copyright: 2022 Ilia Ki, Symmetry Investments
+
+Authors: Ilia Ki
++/
+module mir.string;
+
+import std.traits: isSomeChar;
+
+private alias Representation(T :  char) = byte;
+private alias Representation(T : wchar) = short;
+private alias Representation(T : dchar) = int;
+
+private enum size_t ScanVecSize = 16;
+
+///
+bool containsAny(C, size_t L)
+    (scope const(C)[] str, const C[L] chars...)
+    @trusted pure nothrow @nogc
+    if (isSomeChar!C && L)
+{
+    enum size_t N = ScanVecSize / C.sizeof;
+
+    alias U = Representation!C;
+
+    version(none)
+    version (LittleEndian)
+    version (LDC)
+    static if (N <= 8)
+    static if (is(__vector(U[N])))
+    {
+        alias V = __vector(U[N]);
+        pragma(msg, V);
+        V[L] charsv;
+        static foreach (i; 0 .. L)
+            charsv[i] = chars[i];
+
+        while (str.length >= N)
+        {
+            auto a = cast(V) *cast(const U[N]*) str.ptr;
+
+            import ldc.simd: mask = equalMask;
+
+            V[L] masked;
+            static foreach (i; 0 .. L)
+                masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
+
+            static foreach (i; 0 .. L)
+                static if (i == 0)
+                    V m = masked[i];
+                else
+                    m |= masked[i];
+
+            if (m != V.init)
+                return true;
+
+            str = str[N .. $];
+        }
+    }
+
+    foreach (C c; str)
+        static foreach (i; 0 .. L)
+            if (c == chars[i])
+                return true;
+    return false;
+}
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.test: should;
+
+    assert("     hello world     ".containsAny('w'));
+    assert(!"     hello world     ".containsAny('W'));
+    assert("     hello world     ".containsAny('W', 'e'));
+    assert("     hello world     ".containsAny("We"));
+}
+
+///
+template scanLeftAny(string op = "==")
+    if (op == "==" || op == "!=")
+{
+    ///
+    inout(C)[]
+        scanLeftAny(C, size_t L)
+        (return scope inout(C)[] str, const C[L] chars...)
+        @trusted pure nothrow @nogc
+        if (isSomeChar!C && L)
+    {
+        enum size_t N = ScanVecSize / C.sizeof;
+
+        alias U = Representation!C;
+
+        // version(none)
+        version (LittleEndian)
+        version (LDC)
+        static if (N <= 8)
+        static if (is(__vector(U[N])))
+        {
+            import mir.bitop: cttz;
+
+            alias V = __vector(U[N]);
+            pragma(msg, V);
+            V[L] charsv;
+            static foreach (i; 0 .. L)
+                charsv[i] = chars[i];
+
+            while (str.length >= N)
+            {
+                auto a = cast(V) *cast(const U[N]*) str.ptr;
+
+                import ldc.simd: mask = equalMask;
+
+                V[L] masked;
+                static foreach (i; 0 .. L)
+                    masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
+
+                static foreach (i; 0 .. L)
+                    static if (i == 0)
+                        V m = masked[i];
+                    else
+                        m |= masked[i];
+
+                static if (op == "!=")
+                    m = ~m;
+
+                auto words = (cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array;
+
+                static foreach (i; 0 .. words.length)
+                {
+                    if (words[i])
+                    {
+                        enum p = i * size_t.sizeof / U.sizeof;
+                        return str[p + (cttz(words[i]) / (U.sizeof * 8)) .. $];
+                    }
+                }
+                str = str[N .. $];
+            }
+        }
+
+        Loop: for (; str.length; str = str[1 .. $])
+        {
+            auto c = str[0];
+            static foreach (i; 0 .. L)
+            {
+                if (c == chars[i])
+                {
+                    static if (op == "==")
+                        break Loop;
+                    else
+                        continue Loop;
+                }
+            }
+            static if (op == "==")
+                continue Loop;
+            else
+                break Loop;
+        }
+        return str;
+    }
+}
+
+///
+alias stripLeft = scanLeftAny!"!=";
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.test: should;
+
+    "     hello world     ".stripLeft(' ').should == "hello world     ";
+    "     hello world     ".scanLeftAny('w').should == "world     ";
+    "     hello world     ".scanLeftAny('!').should == "";
+    "\t\n\thello world\n\t___".stripLeft('\n', '\t').should == "hello world\n\t___";
+    "hello world".stripLeft(' ').should == "hello world";
+    "hello world           ".stripLeft(' ').should == "hello world           ";
+
+    "        _____________              hello world     "
+        .stripLeft(' ', '_').should == "hello world     ";
+}
+
+///
+template scanRightAny(string op = "==")
+    if (op == "==" || op == "!=")
+{
+    ///
+    inout(C)[]
+        scanRightAny(C, size_t L)
+        (return scope inout(C)[] str, const C[L] chars...)
+        @trusted pure nothrow @nogc
+        if (isSomeChar!C && L)
+    {
+        enum size_t N = ScanVecSize / C.sizeof;
+
+        alias U = Representation!C;
+
+        // version(none)
+        version (LittleEndian)
+        version (LDC)
+        static if (N <= 8)
+        static if (is(__vector(U[N])))
+        {
+            import mir.bitop: ctlz;
+
+            alias V = __vector(U[N]);
+            pragma(msg, V);
+            V[L] charsv;
+            static foreach (i; 0 .. L)
+                charsv[i] = chars[i];
+
+            while (str.length >= N)
+            {
+                auto a = cast(V) *cast(const U[N]*) (str.ptr + str.length - N);
+
+                import ldc.simd: mask = equalMask;
+
+                V[L] masked;
+                static foreach (i; 0 .. L)
+                    masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
+
+                static foreach (i; 0 .. L)
+                    static if (i == 0)
+                        V m = masked[i];
+                    else
+                        m |= masked[i];
+
+                static if (op == "!=")
+                    m = ~m;
+
+                auto words = (cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array;
+
+                static foreach (i; 0 .. words.length)
+                {
+                    if (words[$ - 1 - i])
+                    {
+                        enum p = i * size_t.sizeof / U.sizeof;
+                        return str[0 .. $ - (p + (ctlz(words[$ - 1 - i]) / (U.sizeof * 8)))];
+                    }
+                }
+                str = str[0 .. $ - N];
+            }
+        }
+
+        Loop: for (; str.length; str = str[0 .. $ - 1])
+        {
+            auto c = str[$ - 1];
+            static foreach (i; 0 .. L)
+            {
+                if (c == chars[i])
+                {
+                    static if (op == "==")
+                        break Loop;
+                    else
+                        continue Loop;
+                }
+            }
+            static if (op == "==")
+                continue Loop;
+            else
+                break Loop;
+        }
+        return str;
+    }
+}
+
+///
+alias stripRight = scanRightAny!"!=";
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.test: should;
+
+    "     hello world     ".stripRight(' ').should == "     hello world";
+    "     hello world     ".scanRightAny('w').should == "     hello w";
+    "     hello world     ".scanRightAny('!').should == "";
+    "___\t\n\thello world\n\t".stripRight('\n', '\t').should == "___\t\n\thello world";
+    "hello world".stripRight(' ').should == "hello world";
+    "           hello world".stripRight(' ').should == "           hello world";
+
+    "     hello world        _____________              "
+        .stripRight(' ', '_').should == "     hello world";
+}
+
+///
+inout(C)[]
+    strip(C, size_t L)
+    (return scope inout(C)[] str, const C[L] chars...)
+    @trusted pure nothrow @nogc
+    if (isSomeChar!C && L)
+{
+    return str.stripLeft(chars).stripRight(chars);
+}
+
+///
+version(mir_test)
+@safe pure nothrow @nogc
+unittest
+{
+    import mir.test: should;
+
+    "     hello world!     ".strip(' ')     .should == "hello world!";
+    "     hello world!!!   ".strip(" !").should == "hello world";
+}

From 330a6822038fa3f57ded97a934086e179a16a74f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 26 Sep 2022 17:05:37 +0700
Subject: [PATCH 517/573] fixup containsAny

---
 source/mir/string.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string.d b/source/mir/string.d
index 44ac691d..8d38b6d3 100644
--- a/source/mir/string.d
+++ b/source/mir/string.d
@@ -27,7 +27,7 @@ bool containsAny(C, size_t L)
 
     alias U = Representation!C;
 
-    version(none)
+    // version(none)
     version (LittleEndian)
     version (LDC)
     static if (N <= 8)

From e0acce4313a26385d2e461f58ec5a1a7a6a3acc1 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 26 Sep 2022 18:06:54 +0700
Subject: [PATCH 518/573] add mir.format printReplaced

---
 source/mir/format.d | 28 ++++++++++++++++++++++++++++
 source/mir/string.d |  5 ++++-
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 3e5c3035..70e73083 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -492,6 +492,34 @@ version (mir_test) unittest
     assert(w.data == `\x03`);
 }
 
+///
+void printReplaced(C, W)(scope ref W w, scope const(C)[] str, C c, scope const(C)[] to)
+{
+    import mir.string: scanLeftAny;
+
+    while (str.length)
+    {
+        auto tailLen = str.scanLeftAny(c).length;
+        print(w, str[0 .. $ - tailLen]);
+        if (tailLen == 0)
+            break;
+        print(w, to);
+        str = str[$ - tailLen + 1 .. $];
+    }
+}
+
+///
+@safe pure nothrow
+unittest
+{
+    import mir.test: should;
+    auto csv = stringBuf;
+    csv.put('"');
+    csv.printReplaced(`some string with " double quotes "!`, '"', `""`);
+    csv.put('"');
+    csv.data.should == `"some string with "" double quotes ""!"`;
+}
+
 /++
 Decodes `char` `c` to the form `u00XX`, where `XX` is 2 hexadecimal characters.
 +/
diff --git a/source/mir/string.d b/source/mir/string.d
index 8d38b6d3..b4362607 100644
--- a/source/mir/string.d
+++ b/source/mir/string.d
@@ -32,6 +32,7 @@ bool containsAny(C, size_t L)
     version (LDC)
     static if (N <= 8)
     static if (is(__vector(U[N])))
+    if (!__ctfe)
     {
         alias V = __vector(U[N]);
         pragma(msg, V);
@@ -102,6 +103,7 @@ template scanLeftAny(string op = "==")
         version (LDC)
         static if (N <= 8)
         static if (is(__vector(U[N])))
+        if (!__ctfe)
         {
             import mir.bitop: cttz;
 
@@ -207,6 +209,7 @@ template scanRightAny(string op = "==")
         version (LDC)
         static if (N <= 8)
         static if (is(__vector(U[N])))
+        if (!__ctfe)
         {
             import mir.bitop: ctlz;
 
@@ -296,7 +299,7 @@ unittest
 inout(C)[]
     strip(C, size_t L)
     (return scope inout(C)[] str, const C[L] chars...)
-    @trusted pure nothrow @nogc
+    @safe pure nothrow @nogc
     if (isSomeChar!C && L)
 {
     return str.stripLeft(chars).stripRight(chars);

From a3df437cf0685bfc2179c1e482917750f5c67a98 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 26 Sep 2022 22:34:36 +0700
Subject: [PATCH 519/573] fixup decimal and integer

---
 source/mir/bignum/decimal.d | 6 ++++--
 source/mir/bignum/integer.d | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 60ddd488..e5c5bdd6 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -72,13 +72,15 @@ struct Decimal(uint size64)
             }
             else
             {
-                BigInt!size64 work = coefficient;
+                BigInt!size64 work = void;
+                work = coefficient;
                 coefficientLength = work.view.unsigned.toStringImpl(buffer);
             }
         }
         else
         {
-            BigInt!size64 work = coefficient;
+            BigInt!size64 work = void;
+            work = coefficient;
             coefficientLength = work.view.unsigned.toStringImpl(buffer);
         }
 
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index d435aa5b..428b9d68 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -73,7 +73,7 @@ struct BigInt(uint size64)
         static if (size_t.sizeof == ulong.sizeof)
         {
             length = data != 0;
-            view.coefficients[0] = data;
+            this.data[0] = data;
         }
         else
         {

From 5e2637e0b558b6068796bbca404fd7b81bc7fa1b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 28 Sep 2022 14:37:35 +0700
Subject: [PATCH 520/573] optimize string operations

---
 source/mir/small_string.d |  22 +---
 source/mir/string.d       | 240 ++++++++++++++++++++++++--------------
 2 files changed, 160 insertions(+), 102 deletions(-)

diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 54b2455a..025be0ff 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -15,8 +15,6 @@ module mir.small_string;
 
 import mir.serde: serdeScoped, serdeProxy;
 
-private extern (C) @system nothrow @nogc pure size_t strnlen_s(scope const char* s, size_t n);
-
 private static immutable errorMsg = "Cannot create SmallString: input string exceeds maximum allowed length.";
 version(D_Exceptions)
     private static immutable exception = new Exception(errorMsg);
@@ -30,7 +28,7 @@ struct SmallString(uint maxLength)
     if (maxLength)
 {
 
-    import core.stdc.string: memcmp, memcpy, strlen;
+    import core.stdc.string: memcmp, memcpy;
     import std.traits: Unqual, isIterable;
 
     // maxLength bytes
@@ -81,6 +79,7 @@ extern(D) @safe pure @nogc:
     /// ditto
     ref typeof(this) opAssign(scope const(char)[] str) return @trusted
     {
+        _data = '\0';
         if (str.length > _data.sizeof)
         {
             version(D_Exceptions) throw exception;
@@ -90,7 +89,6 @@ extern(D) @safe pure @nogc:
             _data[0 .. str.length] =  str;
         else
             memcpy(_data.ptr, str.ptr, str.length);
-        _data[str.length .. $] = '\0';
         return this;
     }
 
@@ -100,11 +98,11 @@ extern(D) @safe pure @nogc:
     {
         static if (n < maxLength)
         {
+            _data = '\0';
             version (LDC)
                 cast(char[n])(_data[0 .. n]) = rhs._data;
             else
                 _data[0 .. n] = rhs._data;
-            _data[n .. maxLength] = '\0';
         }
         else
         {
@@ -208,12 +206,8 @@ scope nothrow:
     +/
     inout(char)[] opIndex() inout @trusted scope return
     {
-        size_t i;
-        if (__ctfe)
-            while (i < maxLength && _data[i]) i++;
-        else
-            i = _data[$ - 1] ? _data.length : strlen(_data.ptr);
-        return _data[0 .. i];
+        import mir.string: scanLeftAny;
+        return _data[0 .. $ - _data.scanLeftAny('\0').length];
     }
 
     ///
@@ -259,12 +253,6 @@ scope const:
     ///
     alias toString = opIndex;
 
-    /// Hash implementation
-    size_t toHash()
-    {
-        return hashOf(opIndex);
-    }
-
     /// Comparisons operator overloads
     bool opEquals(ref scope const SmallString rhs)
     {
diff --git a/source/mir/string.d b/source/mir/string.d
index b4362607..8f5a361e 100644
--- a/source/mir/string.d
+++ b/source/mir/string.d
@@ -23,44 +23,64 @@ bool containsAny(C, size_t L)
     @trusted pure nothrow @nogc
     if (isSomeChar!C && L)
 {
-    enum size_t N = ScanVecSize / C.sizeof;
+    enum size_t NF = ScanVecSize / C.sizeof;
 
     alias U = Representation!C;
 
     // version(none)
     version (LittleEndian)
     version (LDC)
-    static if (N <= 8)
-    static if (is(__vector(U[N])))
+    static if (L <= 8)
+    static if (is(__vector(U[NF])))
     if (!__ctfe)
     {
-        alias V = __vector(U[N]);
-        pragma(msg, V);
-        V[L] charsv;
-        static foreach (i; 0 .. L)
-            charsv[i] = chars[i];
-
-        while (str.length >= N)
-        {
-            auto a = cast(V) *cast(const U[N]*) str.ptr;
+        import mir.bitop: cttzp;
 
-            import ldc.simd: mask = equalMask;
+        static foreach (F; 1 .. 2 + (C.sizeof == 1))
+        {{
+            enum N = NF / F;
+            alias V = __vector(U[N]);
 
-            V[L] masked;
+            V[L] charsv;
             static foreach (i; 0 .. L)
-                masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
+                charsv[i] = chars[i];
 
-            static foreach (i; 0 .. L)
-                static if (i == 0)
-                    V m = masked[i];
+            while (str.length >= N)
+            {
+                auto a = cast(V) *cast(const U[N]*) str.ptr;
+
+                import ldc.simd: mask = equalMask;
+
+                V[L] masked;
+                static foreach (i; 0 .. L)
+                    masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
+
+                static foreach (i; 0 .. L)
+                    static if (i == 0)
+                        V m = masked[i];
+                    else
+                        m |= masked[i];
+
+                static if (U[N].sizeof == size_t.sizeof)
+                {
+                    size_t[U[N].sizeof / size_t.sizeof] words = [(cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array[0]];
+                }
                 else
-                    m |= masked[i];
+                {
+                    auto words = (cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array;
+                }
 
-            if (m != V.init)
-                return true;
+                foreach (word; words)
+                    if (word)
+                        return true;
 
-            str = str[N .. $];
-        }
+                str = str[N .. $];
+
+                static if (F != 1)
+                    break;
+            }
+
+        }}
     }
 
     foreach (C c; str)
@@ -94,56 +114,81 @@ template scanLeftAny(string op = "==")
         @trusted pure nothrow @nogc
         if (isSomeChar!C && L)
     {
-        enum size_t N = ScanVecSize / C.sizeof;
+        enum size_t NF = ScanVecSize / C.sizeof;
 
         alias U = Representation!C;
 
         // version(none)
         version (LittleEndian)
         version (LDC)
-        static if (N <= 8)
-        static if (is(__vector(U[N])))
+        static if (L <= 8)
+        static if (is(__vector(U[NF])))
         if (!__ctfe)
         {
-            import mir.bitop: cttz;
+            import mir.bitop: cttzp;
 
-            alias V = __vector(U[N]);
-            pragma(msg, V);
-            V[L] charsv;
-            static foreach (i; 0 .. L)
-                charsv[i] = chars[i];
+            static foreach (F; 1 .. 2 + (C.sizeof == 1))
+            {{
+                enum N = NF / F;
+                alias V = __vector(U[N]);
+                V[L] charsv;
+                static foreach (i; 0 .. L)
+                    charsv[i] = chars[i];
 
-            while (str.length >= N)
-            {
-                auto a = cast(V) *cast(const U[N]*) str.ptr;
+                while (str.length >= N)
+                {
+                    auto a = cast(V) *cast(const U[N]*) str.ptr;
 
-                import ldc.simd: mask = equalMask;
+                    import ldc.simd: mask = equalMask;
 
-                V[L] masked;
-                static foreach (i; 0 .. L)
-                    masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
+                    V[L] masked;
+                    static foreach (i; 0 .. L)
+                        masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
 
-                static foreach (i; 0 .. L)
-                    static if (i == 0)
-                        V m = masked[i];
-                    else
-                        m |= masked[i];
+                    static foreach (i; 0 .. L)
+                        static if (i == 0)
+                            V m = masked[i];
+                        else
+                            m |= masked[i];
+
+                    static if (op == "!=")
+                        m = ~m;
 
-                static if (op == "!=")
-                    m = ~m;
+                    static if (U[N].sizeof == size_t.sizeof)
+                    {
+                        size_t[U[N].sizeof / size_t.sizeof] words = [(cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array[0]];
+                    }
+                    else
+                    {
+                        auto words = (cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array;
+                    }
 
-                auto words = (cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array;
+                    size_t p;
 
-                static foreach (i; 0 .. words.length)
-                {
-                    if (words[i])
+                    static foreach (i; 0 .. words.length)
                     {
-                        enum p = i * size_t.sizeof / U.sizeof;
-                        return str[p + (cttz(words[i]) / (U.sizeof * 8)) .. $];
+                        p += cttzp(words[i]);
+                        if (words[i])
+                        {
+                            static if (F == 1)
+                                goto L;
+                            else
+                                goto M;
+                        }
                     }
+                    str = str[N .. $];
+                    static if (F == 1)
+                        continue;
+                    else
+                        break;
+
+                    static if (F == 1)
+                {L:}
+                    else
+                {M:}
+                    return str[p / (U.sizeof * 8) .. $];
                 }
-                str = str[N .. $];
-            }
+            }}
         }
 
         Loop: for (; str.length; str = str[1 .. $])
@@ -200,56 +245,81 @@ template scanRightAny(string op = "==")
         @trusted pure nothrow @nogc
         if (isSomeChar!C && L)
     {
-        enum size_t N = ScanVecSize / C.sizeof;
+        enum size_t NF = ScanVecSize / C.sizeof;
 
         alias U = Representation!C;
 
         // version(none)
         version (LittleEndian)
         version (LDC)
-        static if (N <= 8)
-        static if (is(__vector(U[N])))
+        static if (L <= 8)
+        static if (is(__vector(U[NF])))
         if (!__ctfe)
         {
-            import mir.bitop: ctlz;
+            import mir.bitop: ctlzp;
 
-            alias V = __vector(U[N]);
-            pragma(msg, V);
-            V[L] charsv;
-            static foreach (i; 0 .. L)
-                charsv[i] = chars[i];
+            static foreach (F; 1 .. 2 + (C.sizeof == 1))
+            {{
+                enum N = NF / F;
 
-            while (str.length >= N)
-            {
-                auto a = cast(V) *cast(const U[N]*) (str.ptr + str.length - N);
+                alias V = __vector(U[N]);
+                V[L] charsv;
+                static foreach (i; 0 .. L)
+                    charsv[i] = chars[i];
 
-                import ldc.simd: mask = equalMask;
+                while (str.length >= N)
+                {
+                    auto a = cast(V) *cast(const U[N]*) (str.ptr + str.length - N);
 
-                V[L] masked;
-                static foreach (i; 0 .. L)
-                    masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
+                    import ldc.simd: mask = equalMask;
 
-                static foreach (i; 0 .. L)
-                    static if (i == 0)
-                        V m = masked[i];
-                    else
-                        m |= masked[i];
+                    V[L] masked;
+                    static foreach (i; 0 .. L)
+                        masked[i] = mask!(__vector(U[N]))(a, charsv[i]);
 
-                static if (op == "!=")
-                    m = ~m;
+                    static foreach (i; 0 .. L)
+                        static if (i == 0)
+                            V m = masked[i];
+                        else
+                            m |= masked[i];
 
-                auto words = (cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array;
+                    static if (op == "!=")
+                        m = ~m;
 
-                static foreach (i; 0 .. words.length)
-                {
-                    if (words[$ - 1 - i])
+                    static if (U[N].sizeof == size_t.sizeof)
+                    {
+                        size_t[U[N].sizeof / size_t.sizeof] words = [(cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array[0]];
+                    }
+                    else
                     {
-                        enum p = i * size_t.sizeof / U.sizeof;
-                        return str[0 .. $ - (p + (ctlz(words[$ - 1 - i]) / (U.sizeof * 8)))];
+                        auto words = (cast(__vector(size_t[U[N].sizeof / size_t.sizeof])) m).array;
                     }
+                    size_t p;
+
+                    static foreach (i; 0 .. words.length)
+                    {
+                        p += ctlzp(words[$ - 1 - i]);
+                        if (words[$ - 1 - i])
+                        {
+                            static if (F == 1)
+                                goto L;
+                            else
+                                goto M;
+                        }
+                    }
+                    str = str[0 .. $ - N];
+                    static if (F == 1)
+                        continue;
+                    else
+                        break;
+
+                    static if (F == 1)
+                {L:}
+                    else
+                {M:}
+                    return str[0 .. $ - p / (U.sizeof * 8)];
                 }
-                str = str[0 .. $ - N];
-            }
+            }}
         }
 
         Loop: for (; str.length; str = str[0 .. $ - 1])

From f7ffb3e76c08699905e34d06dcb82740b615e617 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 3 Oct 2022 18:59:10 +0400
Subject: [PATCH 521/573] adopt for the recent mir-core

---
 source/mir/string_map.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 5a854096..9f282be5 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -49,7 +49,7 @@ struct StringMap(T)
     }
 
     /// `hashOf` Implementation. Doesn't depend on order
-    size_t toHash() scope const pure @trusted  nothrow @nogc
+    size_t toHash() scope @trusted const // pure nothrow @nogc
     {
         if (implementation is null)
             return 0;

From 3edd91272d96471092c1845c1e7ebc7ea1a50e40 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 6 Oct 2022 19:58:49 +0400
Subject: [PATCH 522/573] fix StringMap.toHash

---
 source/mir/string_map.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 9f282be5..db44dc7d 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -49,7 +49,7 @@ struct StringMap(T)
     }
 
     /// `hashOf` Implementation. Doesn't depend on order
-    size_t toHash() scope @trusted const // pure nothrow @nogc
+    size_t toHash() scope @trusted const nothrow // pure  @nogc
     {
         if (implementation is null)
             return 0;

From 4cdb26d7a66f00e2389469678c3bff86fa20c98c Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 6 Oct 2022 20:20:03 +0400
Subject: [PATCH 523/573] fix StringMap.toHash

---
 source/mir/string_map.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index db44dc7d..50a9fd1b 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -49,7 +49,7 @@ struct StringMap(T)
     }
 
     /// `hashOf` Implementation. Doesn't depend on order
-    size_t toHash() scope @trusted const nothrow // pure  @nogc
+    size_t toHash() scope @trusted const nothrow pure @nogc
     {
         if (implementation is null)
             return 0;

From 2bd3a49e4a6c0ae5cf50d283061d399ded20203f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 11 Oct 2022 00:05:09 +0700
Subject: [PATCH 524/573] Add workaround for StringMap.toHash

---
 source/mir/string_map.d | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 50a9fd1b..bff5b62c 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -38,6 +38,7 @@ struct StringMap(T)
     import mir.utility: _expect;
     import core.lifetime: move;
     import mir.conv: emplaceRef;
+    import mir.algebraic: Algebraic;
 
     ///
     static struct KeyValue
@@ -49,6 +50,7 @@ struct StringMap(T)
     }
 
     /// `hashOf` Implementation. Doesn't depend on order
+    static if (is(T == Algebraic!Union, Union) && is(Union == union))
     size_t toHash() scope @trusted const nothrow pure @nogc
     {
         if (implementation is null)
@@ -64,6 +66,22 @@ struct StringMap(T)
         }
         return hash;
     }
+    else
+    size_t toHash() scope @trusted const nothrow // pure @nogc
+    {
+        if (implementation is null)
+            return 0;
+        size_t hash;
+        foreach (i, index; implementation.indices)
+        {
+            hash = hashOf(implementation._keys[index], hash);
+            static if (__traits(hasMember, T, "toHash"))
+               hash = hashOf(implementation._values[index].toHash, hash);
+            else
+               hash = hashOf(implementation._values[index], hash);
+        }
+        return hash;
+    }
 
     /// `==` implementation. Doesn't depend on order
     // current implementation is workaround for linking bugs when used in self referencing algebraic types

From 85e7c512030cdb1d002d416985f4c25384817c5a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Tue, 11 Oct 2022 16:08:06 +0700
Subject: [PATCH 525/573] add version MirNoSIMD

---
 dub.sdl             | 1 +
 source/mir/string.d | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/dub.sdl b/dub.sdl
index b1b7d87c..4758f61a 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -7,6 +7,7 @@ license "Apache-2.0"
 
 dependency "mir-core" version=">=1.3.5"
 
+// dflags "-version=MirNoSIMD"
 // dflags "-mtriple=aarch64-linux-gnu"
 
 // versions "TeslAlgoM"
diff --git a/source/mir/string.d b/source/mir/string.d
index 8f5a361e..f7e0e916 100644
--- a/source/mir/string.d
+++ b/source/mir/string.d
@@ -28,6 +28,8 @@ bool containsAny(C, size_t L)
     alias U = Representation!C;
 
     // version(none)
+    version (MirNoSIMD) {}
+    else
     version (LittleEndian)
     version (LDC)
     static if (L <= 8)
@@ -119,6 +121,8 @@ template scanLeftAny(string op = "==")
         alias U = Representation!C;
 
         // version(none)
+        version (MirNoSIMD) {}
+        else
         version (LittleEndian)
         version (LDC)
         static if (L <= 8)
@@ -250,6 +254,8 @@ template scanRightAny(string op = "==")
         alias U = Representation!C;
 
         // version(none)
+        version (MirNoSIMD) {}
+        else
         version (LittleEndian)
         version (LDC)
         static if (L <= 8)

From b30a1baa6928e6a80faa47590da9c2d7c05c9608 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 13 Oct 2022 11:51:48 +0700
Subject: [PATCH 526/573] introduce ldc_simd to workaround linking bugs

---
 source/mir/internal/ldc_simd.d   | 308 +++++++++++++++++++++++++++++++
 source/mir/interpolate/package.d |   6 +-
 source/mir/string.d              |   6 +-
 3 files changed, 314 insertions(+), 6 deletions(-)
 create mode 100644 source/mir/internal/ldc_simd.d

diff --git a/source/mir/internal/ldc_simd.d b/source/mir/internal/ldc_simd.d
new file mode 100644
index 00000000..d7fb9987
--- /dev/null
+++ b/source/mir/internal/ldc_simd.d
@@ -0,0 +1,308 @@
+// module ldc.simd;
+// compilers has permanent issues with .di files
+module mir.internal.ldc_simd;
+
+version(LDC):
+
+import core.simd;
+import ldc.llvmasm;
+
+pure:
+nothrow:
+@nogc:
+@trusted:
+
+private template isFloatingPoint(T)
+{
+    enum isFloatingPoint =
+        is(T == float) ||
+        is(T == double) ||
+        is(T == real);
+}
+
+private template isIntegral(T)
+{
+    enum isIntegral =
+        is(T == byte) ||
+        is(T == ubyte) ||
+        is(T == short) ||
+        is(T == ushort) ||
+        is(T == int) ||
+        is(T == uint) ||
+        is(T == long) ||
+        is(T == ulong);
+}
+
+private template isSigned(T)
+{
+    enum isSigned =
+        is(T == byte) ||
+        is(T == short) ||
+        is(T == int) ||
+        is(T == long);
+}
+
+private template IntOf(T)
+if(isIntegral!T || isFloatingPoint!T)
+{
+    enum n = T.sizeof;
+    static if(n == 1)
+        alias byte IntOf;
+    else static if(n == 2)
+        alias short IntOf;
+    else static if(n == 4)
+        alias int IntOf;
+    else static if(n == 8)
+        alias long IntOf;
+    else
+        static assert(0, "Type not supported");
+}
+
+private template BaseType(V)
+{
+    alias typeof(V.array[0]) BaseType;
+}
+
+private template numElements(V)
+{
+    enum numElements = V.sizeof / BaseType!(V).sizeof;
+}
+
+private template llvmType(T)
+{
+    static if(is(T == float))
+        enum llvmType = "float";
+    else static if(is(T == double))
+        enum llvmType = "double";
+    else static if(is(T == byte) || is(T == ubyte) || is(T == void))
+        enum llvmType = "i8";
+    else static if(is(T == short) || is(T == ushort))
+        enum llvmType = "i16";
+    else static if(is(T == int) || is(T == uint))
+        enum llvmType = "i32";
+    else static if(is(T == long) || is(T == ulong))
+        enum llvmType = "i64";
+    else
+        static assert(0,
+            "Can't determine llvm type for D type " ~ T.stringof);
+}
+
+private template llvmVecType(V)
+{
+    static if(is(V == void16))
+        enum llvmVecType =  "<16 x i8>";
+    else static if(is(V == void32))
+        enum llvmVecType =  "<32 x i8>";
+    else
+    {
+        alias BaseType!V T;
+        enum int n = numElements!V;
+        enum llvmT = llvmType!T;
+        enum llvmVecType = "<"~n.stringof~" x "~llvmT~">";
+    }
+}
+
+/**
+This template provides access to
+$(LINK2 http://llvm.org/docs/LangRef.html#i_shufflevector,
+LLVM's shufflevector instruction).
+
+Example:
+---
+int4 a = [0, 10, 20, 30];
+int4 b = [40, 50, 60, 70];
+int4 c = shufflevector!(int4, 0, 2, 4, 6)(a, b);
+assert(c.array == [0, 20, 40, 60]);
+---
+*/
+
+template shufflevector(V, mask...)
+if(is(typeof(llvmVecType!V)) && mask.length == numElements!V)
+{
+    enum int n = mask.length;
+    enum llvmV = llvmVecType!V;
+
+    template genMaskIr(string ir, m...)
+    {
+        static if(m.length == 0)
+            enum genMaskIr = ir;
+        else
+        {
+            enum int mfront = m[0];
+
+            enum genMaskIr =
+                genMaskIr!(ir ~ ", i32 " ~ mfront.stringof, m[1 .. $]);
+        }
+    }
+    enum maskIr = genMaskIr!("", mask)[2 .. $];
+    enum ir = `
+        %r = shufflevector `~llvmV~` %0, `~llvmV~` %1, <`~n.stringof~` x i32> <`~maskIr~`>
+        ret `~llvmV~` %r`;
+
+    alias __ir_pure!(ir, V, V, V) shufflevector;
+}
+
+/**
+This template provides access to
+$(LINK2 http://llvm.org/docs/LangRef.html#i_extractelement,
+LLVM's extractelement instruction).
+
+Example:
+---
+int4 a = [0, 10, 20, 30];
+int k = extractelement!(int4, 2)(a);
+assert(k == 20);
+---
+*/
+
+template extractelement(V, int i)
+if(is(typeof(llvmVecType!V)) && i < numElements!V)
+{
+    enum llvmT = llvmType!(BaseType!V);
+    enum llvmV = llvmVecType!V;
+    enum ir = `
+        %r = extractelement `~llvmV~` %0, i32 `~i.stringof~`
+        ret `~llvmT~` %r`;
+
+    alias __ir_pure!(ir, BaseType!V, V) extractelement;
+}
+
+/**
+This template provides access to
+$(LINK2 http://llvm.org/docs/LangRef.html#i_insertelement,
+LLVM's insertelement instruction).
+
+Example:
+---
+int4 a = [0, 10, 20, 30];
+int b = insertelement!(int4, 2)(a, 50);
+assert(b.array == [0, 10, 50, 30]);
+---
+*/
+
+template insertelement(V, int i)
+if(is(typeof(llvmVecType!V)) && i < numElements!V)
+{
+    enum llvmT = llvmType!(BaseType!V);
+    enum llvmV = llvmVecType!V;
+    enum ir = `
+        %r = insertelement `~llvmV~` %0, `~llvmT~` %1, i32 `~i.stringof~`
+        ret `~llvmV~` %r`;
+
+    alias __ir_pure!(ir, V, V, BaseType!V) insertelement;
+}
+
+/**
+loadUnaligned: Loads a vector from an unaligned pointer.
+Example:
+---
+int[4] a = [0, 10, 20, 30];
+int4 v = loadUnaligned!int4(a.ptr);
+assert(v.array == a);
+---
+*/
+template loadUnaligned(V)
+if(is(typeof(llvmVecType!V)))
+{
+    enum llvmElementType = llvmType!(BaseType!V);
+    enum llvmV = llvmVecType!V;
+    enum ir = `
+        %p = bitcast `~llvmElementType~`* %0 to `~llvmV~`*
+        %r = load `~llvmV~`, `~llvmV~`* %p, align 1
+        ret `~llvmV~` %r`;
+    private alias impl = __ir_pure!(ir, V, const(BaseType!V)*);
+
+    pragma(inline, true):
+
+    V loadUnaligned(const(BaseType!V)* p)
+    {
+        return impl(p);
+    }
+}
+
+/**
+storeUnaligned: Stores a vector to an unaligned pointer.
+Example:
+---
+int[4] a;
+int4 v = [0, 10, 20, 30];
+storeUnaligned!int4(v, a.ptr);
+assert(v.array == a);
+---
+*/
+template storeUnaligned(V)
+if(is(typeof(llvmVecType!V)))
+{
+    enum llvmElementType = llvmType!(BaseType!V);
+    enum llvmV = llvmVecType!V;
+    enum ir = `
+        %p = bitcast `~llvmElementType~`* %1 to `~llvmV~`*
+        store `~llvmV~` %0, `~llvmV~`* %p, align 1`;
+    private alias impl = __ir_pure!(ir, void, V, BaseType!V*);
+
+    pragma(inline, true):
+
+    void storeUnaligned(V value, BaseType!V* p)
+    {
+        impl(value, p);
+    }
+}
+
+private enum Cond{ eq, ne, gt, ge }
+
+private template cmpMask(Cond cond)
+{
+    template cmpMask(V)
+    if(is(IntOf!(BaseType!V)))
+    {
+        alias BaseType!V T;
+        enum llvmT = llvmType!T;
+
+        alias IntOf!T Relem;
+
+        enum int n = numElements!V;
+        alias __vector(Relem[n]) R;
+
+        enum llvmV = llvmVecType!V;
+        enum llvmR = llvmVecType!R;
+        enum sign =
+            (cond == Cond.eq || cond == Cond.ne) ? "" :
+            isSigned!T ? "s" : "u";
+        enum condStr =
+            cond == Cond.eq ? "eq" :
+            cond == Cond.ne ? "ne" :
+            cond == Cond.ge ? "ge" : "gt";
+        enum op =
+            isFloatingPoint!T ? "fcmp o"~condStr : "icmp "~sign~condStr;
+
+        enum ir = `
+            %cmp = `~op~` `~llvmV~` %0, %1
+            %r = sext <`~n.stringof~` x i1> %cmp to `~llvmR~`
+            ret `~llvmR~` %r`;
+
+        alias __ir_pure!(ir, R, V, V) cmpMask;
+    }
+}
+
+/**
+equalMask, notEqualMask, greaterMask and greaterOrEqualMask perform an
+element-wise comparison between two vectors and return a vector with
+signed integral elements. The number of elements in the returned vector
+and their size is the same as in parameter vectors. If the condition in
+the name of the function holds for elements of the parameter vectors at
+a given index, all bits of the element of the result at that index are
+set to 1, otherwise the element of the result is zero.
+
+Example:
+---
+float4 a = [1, 3, 5, 7];
+float4 b = [2, 3, 4, 5];
+int4 c = greaterMask!float4(a, b);
+writeln(c.array);
+assert(c.array == [0, 0, 0xffff_ffff, 0xffff_ffff]);
+---
+*/
+alias cmpMask!(Cond.eq) equalMask;
+alias cmpMask!(Cond.ne) notEqualMask; /// Ditto
+alias cmpMask!(Cond.gt) greaterMask; /// Ditto
+alias cmpMask!(Cond.ge) greaterOrEqualMask; /// Ditto
diff --git a/source/mir/interpolate/package.d b/source/mir/interpolate/package.d
index 1227d075..57475475 100644
--- a/source/mir/interpolate/package.d
+++ b/source/mir/interpolate/package.d
@@ -307,7 +307,7 @@ void shuffle3(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N
     {
         enum masks = generateShuffles3!(N, P);
         import std.meta: aliasSeqOf;
-        import ldc.simd: shufflevector;
+        import mir.internal.ldc_simd: shufflevector;
         alias V = __vector(F[N]); // @FUTURE@ vector support
         auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
         auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
@@ -347,7 +347,7 @@ void shuffle2(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N
     {
         enum masks = generateShuffles2!(N, P);
         import std.meta: aliasSeqOf;
-        import ldc.simd: shufflevector;
+        import mir.internal.ldc_simd: shufflevector;
         alias V = __vector(F[N]); // @FUTURE@ vector support
         auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
         auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
@@ -388,7 +388,7 @@ void shuffle1(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N
     {
         enum masks = generateShuffles1!(N, P);
         import std.meta: aliasSeqOf;
-        import ldc.simd: shufflevector;
+        import mir.internal.ldc_simd: shufflevector;
         alias V = __vector(F[N]); // @FUTURE@ vector support
         auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
         auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr);
diff --git a/source/mir/string.d b/source/mir/string.d
index f7e0e916..3fd8e5f6 100644
--- a/source/mir/string.d
+++ b/source/mir/string.d
@@ -51,7 +51,7 @@ bool containsAny(C, size_t L)
             {
                 auto a = cast(V) *cast(const U[N]*) str.ptr;
 
-                import ldc.simd: mask = equalMask;
+                import mir.internal.ldc_simd: mask = equalMask;
 
                 V[L] masked;
                 static foreach (i; 0 .. L)
@@ -143,7 +143,7 @@ template scanLeftAny(string op = "==")
                 {
                     auto a = cast(V) *cast(const U[N]*) str.ptr;
 
-                    import ldc.simd: mask = equalMask;
+                    import mir.internal.ldc_simd: mask = equalMask;
 
                     V[L] masked;
                     static foreach (i; 0 .. L)
@@ -277,7 +277,7 @@ template scanRightAny(string op = "==")
                 {
                     auto a = cast(V) *cast(const U[N]*) (str.ptr + str.length - N);
 
-                    import ldc.simd: mask = equalMask;
+                    import mir.internal.ldc_simd: mask = equalMask;
 
                     V[L] masked;
                     static foreach (i; 0 .. L)

From bd61c40c4ff3559f0f5770046a7fa36f539a2cba Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 13 Oct 2022 13:01:00 +0700
Subject: [PATCH 527/573] update algebraic unittest

---
 source/mir/algebraic_alias/ion.d | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
index a9088155..b11b18d3 100644
--- a/source/mir/algebraic_alias/ion.d
+++ b/source/mir/algebraic_alias/ion.d
@@ -60,6 +60,7 @@ See also $(MREF mir,string_map) - ordered string-value associative array.
 alias IonAlgebraic = Algebraic!Ion_;
 
 ///
+@safe pure
 version(mir_test)
 unittest
 {
@@ -137,6 +138,7 @@ unittest
     value = object["array"] = arr;
     assert(value.kind == IonAlgebraic.Kind.array);
     assert(value == arr);
+    assert(value == [0, 1, 2, 3, 4].map!IonAlgebraic.array);// by value
     assert(value.array[3] == 3);
 
     // Object

From a483d6a3716e0ca6377ca83857baa926dae2bddd Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 14 Oct 2022 14:13:19 +0700
Subject: [PATCH 528/573] extend string-map API

---
 source/mir/algebraic_alias/ion.d  |  9 +++
 source/mir/algebraic_alias/json.d |  8 +++
 source/mir/string_map.d           | 95 ++++++++++++++++++++++++++++---
 3 files changed, 105 insertions(+), 7 deletions(-)

diff --git a/source/mir/algebraic_alias/ion.d b/source/mir/algebraic_alias/ion.d
index b11b18d3..c98e8971 100644
--- a/source/mir/algebraic_alias/ion.d
+++ b/source/mir/algebraic_alias/ion.d
@@ -64,6 +64,7 @@ alias IonAlgebraic = Algebraic!Ion_;
 version(mir_test)
 unittest
 {
+    import mir.test: should;
     import mir.ndslice.topology: map;
     import mir.array.allocation: array;
 
@@ -158,6 +159,14 @@ unittest
     assert(fromAA.object["a"] == 3);
     assert(fromAA.object["b"] == "b");
 
+    // object foreach iteration
+    long sum;
+    foreach (ref key, ref val; fromAA.object)
+        if (key == "a")
+            sum += val.get!long;
+    sum.should == 3;
+
+    // annotations
     auto annotated = Annotated!IonAlgebraic(["birthday"], Timestamp("2001-01-01"));
     value = annotated;
     assert(value == annotated);
diff --git a/source/mir/algebraic_alias/json.d b/source/mir/algebraic_alias/json.d
index fb8e97ed..64daf39f 100644
--- a/source/mir/algebraic_alias/json.d
+++ b/source/mir/algebraic_alias/json.d
@@ -49,6 +49,7 @@ version(mir_test)
 @safe pure
 unittest
 {
+    import mir.test: should;
     import mir.ndslice.topology: map;
     import mir.array.allocation: array;
 
@@ -141,4 +142,11 @@ unittest
     JsonAlgebraic fromAA = ["a" : JsonAlgebraic(3), "b" : JsonAlgebraic("b")];
     assert(fromAA.object["a"] == 3);
     assert(fromAA.object["b"] == "b");
+
+    // object foreach iteration
+    long sum;
+    foreach (ref key, ref val; fromAA.object)
+        if (key == "a")
+            sum += val.get!long;
+    sum.should == 3;
 }
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index bff5b62c..ddd3b58c 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -258,6 +258,14 @@ struct StringMap(T)
         return implementation ? implementation.length : 0;
     }
 
+    /++
+    Returns: number of elements in the table.
+    +/
+    bool empty()() @safe pure nothrow @nogc const @property
+    {
+        return !implementation || implementation.length == 0;
+    }
+
     version(mir_test) static if (is(T == int))
     ///
     @safe pure unittest
@@ -345,8 +353,8 @@ struct StringMap(T)
     */
     auto byKeyValue(this This)() @trusted pure nothrow @nogc
     {
-        import mir.ndslice.topology: zip, map;
-        return keys.zip(values).map!KeyValue;
+        import mir.ndslice.topology: map;
+        return this.opIndex.map!KeyValue;
     }
 
     version(mir_test) static if (is(T == int))
@@ -369,6 +377,19 @@ struct StringMap(T)
         assert(map.values == [4.0]);
     }
 
+    ///
+    auto opIndex(this This)() @trusted pure nothrow @nogc
+    {
+        import mir.ndslice.topology: zip;
+        return keys.zip(values);
+    }
+
+    ///
+    auto dup(this This)() @trusted
+    {
+        return StringMap(keys.dup, values.dup);
+    }
+
     /++
     (Property) Gets the current capacity of an associative array.
     The capacity is the size that the underlaynig slices can grow to before the underlying arrays may be reallocated or extended.
@@ -688,7 +709,7 @@ struct StringMap(T)
 
     Complexity: `O(log(s))`, where `s` is the number of the keys with the same length as the input key.
     +/
-    inout(T) get()(scope const(char)[] key, lazy inout(T) defaultValue)
+    inout(T) get()(scope const(char)[] key, lazy inout(T) defaultValue) inout
     {
         size_t index;
         if (implementation && implementation.findIndex(key, index))
@@ -808,6 +829,7 @@ struct StringMap(T)
     }
 
     ///
+    version(mir_test) static if (is(T == int))
     @safe pure nothrow unittest
     {
         StringMap!int map = ["k": 1];
@@ -971,22 +993,22 @@ struct StringMap(T)
             return _length;
         }
 
-        inout(string)[] keys()() @trusted inout @property
+        inout(string)[] keys()() @trusted inout @property pure @nogc nothrow
         {
             return _keys[0 .. _length];
         }
 
-        inout(T)[] values()() @trusted inout @property
+        inout(T)[] values()() @trusted inout @property pure @nogc nothrow
         {
             return _values[0 .. _length];
         }
 
-        inout(U)[] indices()() @trusted inout @property
+        inout(U)[] indices()() @trusted inout @property pure @nogc nothrow
         {
             return _indices[0 .. _length];
         }
 
-        inout(U)[] lengthTable()() @trusted inout @property
+        inout(U)[] lengthTable()() @trusted inout @property pure @nogc nothrow
         {
             return _lengthTable;
         }
@@ -1217,3 +1239,62 @@ version(mir_test)
 
     assert((*tkType) == "Bearer"); // *tkType contains value 3599
 }
+
+///
+template intersectionMap(alias merger)
+{
+    ///
+    StringMap!V intersectionMap(V)(StringMap!V a, StringMap!V b)
+    {
+        import mir.functional : naryFun;
+        typeof(return) res;
+        foreach (key, ref value; a)
+            if (auto bValPtr = key in b)
+                res[key] = naryFun!merger(value, *bValPtr);
+        return res;
+    }
+}
+
+///
+version(mir_test)
+@safe pure
+unittest {
+    import mir.test: should;
+    import mir.string_map : StringMap;
+    auto m0 = StringMap!int(["foo", "bar"], [1, 2]);
+    auto m1 = StringMap!int(["foo"], [2]);
+    auto m2 = StringMap!int(["foo"], [3]);
+    intersectionMap!"a + b"(m0, m1).should == m2;
+}
+
+///
+template unionMap(alias merger)
+{
+    ///
+    StringMap!V unionMap(V)(StringMap!V a, StringMap!V b)
+    {
+        import mir.functional : naryFun;
+        typeof(return) res;
+        foreach (key, ref value; a)
+            if (auto bValPtr = key in b)
+                res[key] = naryFun!merger(value, *bValPtr);
+            else
+                res[key] = value;
+        foreach (key, ref value; b)
+            if (key !in res)
+                res[key] = value;
+        return res;
+    }
+}
+
+///
+version(mir_test)
+@safe pure
+unittest {
+    import mir.test: should;
+    import mir.string_map : StringMap;
+    auto m0 = StringMap!int(["foo", "bar"], [1, 2]);
+    auto m1 = StringMap!int(["foo"], [2]);
+    auto m2 = StringMap!int(["foo", "bar"], [3, 2]);
+    unionMap!"a + b"(m0, m1).should == m2;
+}

From 5309f513b6aaa196a1eacf6b5afc68e032284483 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 14 Oct 2022 16:57:55 +0700
Subject: [PATCH 529/573] try workaround compiler bug

---
 source/mir/string_map.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index ddd3b58c..b00088f1 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -1072,7 +1072,7 @@ struct StringMap(T)
     }
 
     import std.traits: isAssociativeArray, isAggregateType;
-    static if (!isAssociativeArray!(.basicElementType!T) && (!isAggregateType!(.basicElementType!T) || __traits(hasMember, .basicElementType!T, "opCmp")))
+    // static if (!isAssociativeArray!(.basicElementType!T) && (!isAggregateType!(.basicElementType!T) || __traits(hasMember, .basicElementType!T, "opCmp")))
     /// `opCmp` Implementation. Doesn't depend on order
     int opCmp()(ref scope const typeof(this) rhs) scope const @trusted // pure nothrow @nogc
     {

From 78c27907a6ce8b37f30efea2a4fd5f37d98e9e0b Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 14 Oct 2022 17:00:51 +0700
Subject: [PATCH 530/573] ditto

---
 source/mir/string_map.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index b00088f1..91668112 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -1074,7 +1074,7 @@ struct StringMap(T)
     import std.traits: isAssociativeArray, isAggregateType;
     // static if (!isAssociativeArray!(.basicElementType!T) && (!isAggregateType!(.basicElementType!T) || __traits(hasMember, .basicElementType!T, "opCmp")))
     /// `opCmp` Implementation. Doesn't depend on order
-    int opCmp()(ref scope const typeof(this) rhs) scope const @trusted // pure nothrow @nogc
+    int opCmp()(scope const typeof(this) rhs) scope const @trusted // pure nothrow @nogc
     {
         if (sizediff_t d = length - rhs.length)
             return d < 0 ? -1 : 1;

From 6f8d0f4920aa9ecefb66991d265b69262d23547f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 14 Oct 2022 17:24:29 +0700
Subject: [PATCH 531/573] fixup annotated

---
 source/mir/annotated.d | 24 ++++++------------------
 1 file changed, 6 insertions(+), 18 deletions(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index 2b19e050..ba47c2eb 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -69,12 +69,6 @@ struct Annotated(T) {
             return annotations == rhs.annotations && value == rhs.value;
         }
 
-        /// ditto
-        bool opEquals(ref scope const Annotated rhs) scope const
-        {
-            return annotations == rhs.annotations && value == rhs.value;
-        }
-
         size_t toHash() scope @trusted const pure nothrow @nogc
         {
             static if (__traits(compiles, hashOf(value)))
@@ -120,12 +114,12 @@ struct Annotated(T) {
             this.value = T(forward!args);
     }
 
-    private alias E = .basicElementType!T;
+    // private alias E = .basicElementType!T;
 
     import std.traits: isAssociativeArray, isAggregateType;
     ///
-    int opCmp()(ref scope const typeof(this) rhs) scope const pure nothrow @nogc
-        if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
+    int opCmp()(scope const typeof(this) rhs) scope const pure nothrow @nogc @system
+        // if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
     {
         if (auto d = __cmp(annotations, rhs.annotations))
             return d;
@@ -226,12 +220,6 @@ struct AnnotatedOnce(T) {
         {
             return annotation == rhs.annotation && value == rhs.value;
         }
-
-        ///
-        bool opEquals(ref scope const AnnotatedOnce rhs) scope const
-        {
-            return annotation == rhs.annotation && value == rhs.value;
-        }
     }
     else
     {
@@ -260,12 +248,12 @@ struct AnnotatedOnce(T) {
             this.value = T(forward!args);
     }
 
-    private alias E = .basicElementType!T;
+    // private alias E = .basicElementType!T;
 
     import std.traits: isAssociativeArray, isAggregateType;
-    static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
+    // static if (!isAssociativeArray!E && (!isAggregateType!E || __traits(hasMember, E, "opCmp")))
     ///
-    int opCmp()(ref scope const typeof(this) rhs) scope const @safe pure nothrow @nogc
+    int opCmp()(scope const typeof(this) rhs) scope const @system pure nothrow @nogc
     {
         if (auto d = __cmp(annotation, rhs.annotation))
             return d;

From 0e4c4ce7262b2491a5f2406ccaaf7641c2f36d48 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 21 Oct 2022 14:31:31 +0700
Subject: [PATCH 532/573] add const iteration for concatenation

---
 source/mir/ndslice/concatenation.d | 28 ++++++++++++++++++++++++++--
 source/mir/ndslice/iterator.d      |  4 ++--
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index 670e3aa5..4edb7249 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -133,17 +133,26 @@ version(mir_ndslice_test) unittest
     // 4 5 6
     auto matrix = iota([2, 3], 1);
 
-    assert(concatenation(vector, matrix).slice == [
+    auto c0 = concatenation(vector, matrix);
+
+    assert(c0.slice == [
         [0, 0, 0],
         [1, 2, 3],
         [4, 5, 6],
     ]);
 
     vector.popFront;
-    assert(concatenation!1(vector, matrix).slice == [
+    auto c1 = concatenation!1(vector, matrix);
+    assert(c1.slice == [
         [0, 1, 2, 3],
         [0, 4, 5, 6],
     ]);
+
+    auto opIndexCompiles0 = c0[];
+    auto opIndexCompiles1 = c1[];
+
+    auto opIndexCompilesForConst0 = (cast(const)c0)[];
+    auto opIndexCompilesForConst1 = (cast(const)c1)[];
 }
 
 /// Multidimensional
@@ -364,6 +373,21 @@ struct Concatenation(size_t dim, Slices...)
         assert(indices[dim] < _slices[$-1].length!dim);
         return _slices[$-1][indices];
     }
+
+    ref opIndex()() scope return
+    {
+        return this;
+    }
+
+    auto opIndex()() const return scope
+    {
+        import mir.ndslice.topology: iota;
+        import mir.qualifier: LightConstOf, lightConst;
+        import std.format: format;
+        alias Ret = .Concatenation!(dim, staticMap!(LightConstOf, Slices));
+        enum ret = "Ret(%(lightConst(_slices[%s]),%)]))".format(_slices.length.iota);
+        return mixin(ret);
+    }
 }
 
 
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index bbeeb6ed..8698b8d9 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -606,7 +606,7 @@ struct ZipIterator(Iterators...)
     ///
     auto lightConst()() const @property
     {
-        import std.format;
+        import std.format: format;
         import mir.ndslice.topology: iota;
         import std.meta: staticMap;
         alias Ret = ZipIterator!(staticMap!(LightConstOf, Iterators));
@@ -617,7 +617,7 @@ struct ZipIterator(Iterators...)
     ///
     auto lightImmutable()() immutable @property
     {
-        import std.format;
+        import std.format: format;
         import mir.ndslice.topology: iota;
         import std.meta: staticMap;
         alias Ret = ZipIterator!(staticMap!(LightImmutableOf, Iterators));

From 9c4a9e7ea0b7c737b0b8e55dff5af0e380d90716 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 22 Oct 2022 01:37:33 +0700
Subject: [PATCH 533/573] Fix BigInt.fromBigEndian

---
 source/mir/bignum/integer.d | 3 ++-
 source/mir/stdio.d          | 1 -
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 428b9d68..52ca0449 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -191,9 +191,10 @@ struct BigInt(uint size64)
     static BigInt fromBigEndian()(scope const(ubyte)[] data, bool sign = false)
         @trusted pure @nogc
     {
+        static immutable bigIntOverflowException = new Exception("BigInt!" ~ size64.stringof ~ ".fromBigEndian: data overflow");
         BigInt ret = void;
         if (!ret.copyFromBigEndian(data, sign))
-            static immutable bigIntOverflowException = new Exception("BigInt!" ~ size64.stringof ~ ".fromBigEndian: data overflow");
+            throw bigIntOverflowException;
         return ret;
     }
 
diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index 6c9af852..273ba1a2 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -211,7 +211,6 @@ mixin template FileMembers()
         {
             if (__ctfe)
                 return this;
-            import mir.format: print;
             this.put(endl);
             this.flush;
             return this;

From 9a61cd61bb6316c2e93f5e0a7a770cb55e13f63d Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 22 Oct 2022 02:31:33 +0700
Subject: [PATCH 534/573] fixup biginteger parsing

---
 source/mir/bignum/integer.d         | 35 ++++++++++++++---------------
 source/mir/bignum/internal/kernel.d | 12 +++++-----
 source/mir/bignum/low_level_view.d  | 29 ++++++++++++++++++------
 3 files changed, 45 insertions(+), 31 deletions(-)

diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 52ca0449..cdce4921 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -265,10 +265,10 @@ struct BigInt(uint size64)
     ///
     BigInt copy() @property
     {
-        BigInt ret;
+        BigInt ret = void;
         ret.sign = sign;
         ret.length = length;
-        ret.data = data;
+        ret.data[0 .. length] = data[0 .. length];
         return ret;
     }
 
@@ -762,7 +762,7 @@ struct BigInt(uint size64)
     static BigInt fromHexString(bool allowUnderscores = false)(scope const(char)[] str)
         @trusted pure
     {
-        BigInt ret;
+        BigInt ret = void;
         if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
             return ret;
         version(D_Exceptions)
@@ -779,11 +779,8 @@ struct BigInt(uint size64)
     {
         auto work = BigIntView!size_t(data);
         auto ret = work.fromHexStringImpl!(C, allowUnderscores)(str);
-        if (ret)
-        {
-            length = cast(uint)work.unsigned.coefficients.length;
-            sign = work.sign;
-        }
+        length = cast(uint)work.unsigned.coefficients.length;
+        sign = work.sign;
         return ret;
     }
 
@@ -792,7 +789,7 @@ struct BigInt(uint size64)
     static BigInt fromBinaryString(bool allowUnderscores = false)(scope const(char)[] str)
         @trusted pure
     {
-        BigInt ret;
+        BigInt ret = void;
         if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
             return ret;
         version(D_Exceptions)
@@ -817,11 +814,8 @@ struct BigInt(uint size64)
     {
         auto work = BigIntView!size_t(data);
         auto ret = work.fromBinaryStringImpl!(C, allowUnderscores)(str);
-        if (ret)
-        {
-            length = cast(uint)work.unsigned.coefficients.length;
-            sign = work.sign;
-        }
+        length = cast(uint)work.unsigned.coefficients.length;
+        sign = work.sign;
         return ret;
     }
 
@@ -1065,6 +1059,7 @@ version(mir_bignum_test) @safe pure @nogc unittest
 version(mir_bignum_test)
 unittest
 {
+    import mir.test;
     import mir.bignum.fixed;
     import mir.bignum.low_level_view;
 
@@ -1073,18 +1068,22 @@ unittest
         auto b = UInt!128.fromHexString("f79a222050aaeaaa417fa25a2ac93291");
 
         // ca3d7e25aebe687b 168dcef32d0bb2f0
-        import mir.format;
-        assert((a %= b) == BigInt!4.fromHexString("bf4c87424431d21563f23b1fc00d75ac"));
+        auto c = BigInt!4.fromHexString("bf4c87424431d21563f23b1fc00d75ac");
+        a %= b;
+        a.should == c;
         a = BigInt!4.fromHexString("c39b18a9f06fd8e962d99935cea0707f79a222050aaeaaaed17feb7aa76999d7");
         a /= b;
-        assert(a == BigInt!4.fromHexString("ca3d7e25aebe687b7cc1b250b44690fb"), a.data.text);
+        assert(a == BigInt!4.fromHexString("ca3d7e25aebe687b7cc1b250b44690fb"));
     }
 
     {
         auto a = BigInt!4.fromHexString("7fff000080000000000000000000");
         auto b = UInt!128.fromHexString("80000000000000000001");
 
-        assert((a /= b) == BigInt!4.fromHexString("fffe0000"));
+        auto c = BigInt!4.fromHexString("fffe0000");
+        a /= b;
+        a.should == c;
+
         a = BigInt!4.fromHexString("7fff000080000000000000000000");
         assert((a %= b) == BigInt!4.fromHexString("7fffffffffff00020000"));
     }
diff --git a/source/mir/bignum/internal/kernel.d b/source/mir/bignum/internal/kernel.d
index 34438d03..3f4cc547 100644
--- a/source/mir/bignum/internal/kernel.d
+++ b/source/mir/bignum/internal/kernel.d
@@ -183,7 +183,7 @@ unittest
         // Test division by a single-digit divisor here.
         auto dividend = BigUIntView!uint.fromHexString("5");
         auto divisor = BigUIntView!uint.fromHexString("5");
-        auto quotient = BigUIntView!uint.fromHexString("0");
+        auto quotient = BigUIntView!uint([0u]);
         // auto remainder = BigUIntView!uint.fromHexString("0");
 
         divMod(dividend, divisor, quotient);
@@ -194,7 +194,7 @@ unittest
         // Test division by a single-digit divisor here.
         auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf");
         auto divisor = BigUIntView!uint.fromHexString("5");
-        auto quotient = BigUIntView!uint.fromHexString("0000000000000000000000000000000000000000000000000000000000000000");
+        auto quotient = BigUIntView!uint.fromHexString("1000000000000000000000000000000000000000000000000000000000000000");
         divMod(dividend, divisor, quotient);
         assert(dividend.normalized == BigUIntView!uint.fromHexString("3"));
         assert(quotient == BigUIntView!uint.fromHexString("1120a1229e8a217d0691b02b819107174a4b677088ef0df874259b283c1d588c"));
@@ -204,8 +204,8 @@ unittest
     {
         auto dividend = BigUIntView!uint.fromHexString("55a325ad18b2a77120d870d987d5237473790532acab45da44bc07c92c92babf0b5e2e2c7771cd472ae5d7acdb159a56fbf74f851a058ae341f69d1eb750d7e3");
         auto divisor = BigUIntView!uint.fromHexString("55e5669576d31726f4a9b58a90159de5923adc6c762ebd3c4ba518d495229072");
-        auto quotient = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000");
-        // auto remainder = BigUIntView!uint.fromHexString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+        auto quotient = BigUIntView!uint.fromHexString("10000000000000000000000000000000000000000000000000000000000000000");
+        // auto remainder = BigUIntView!uint.fromHexString("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
 
         divMod(dividend, divisor, quotient);
         assert(quotient.normalized == BigUIntView!uint.fromHexString("ff3a8aa4da35237811a0ffbf007fe938630dee8a810f2f82ae01f80c033291f6"));
@@ -216,8 +216,8 @@ unittest
     {
         auto dividend = BigUIntView!uint.fromHexString("800000000000000000000003");
         auto divisor = BigUIntView!uint.fromHexString("200000000000000000000001");
-        auto quotient = BigUIntView!uint.fromHexString("0");
-        // auto remainder = BigUIntView!uint.fromHexString("000000000000000000000000");
+        auto quotient = BigUIntView!uint([0u]);
+        // auto remainder = BigUIntView!uint.fromHexString("100000000000000000000000");
         divMod(dividend, divisor, quotient);
         assert(quotient == BigUIntView!uint.fromHexString("3"));
         assert(dividend == BigUIntView!uint.fromHexString("200000000000000000000000"));
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 47acfd44..7d742449 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -220,6 +220,7 @@ struct BigUIntView(W)
     /++
     +/
     BigUIntView topMostSignificantPart(size_t length)
+        in (length <= coefficients.length)
     {
         return BigUIntView(coefficients[$ - length .. $]);
     }
@@ -227,6 +228,7 @@ struct BigUIntView(W)
     /++
     +/
     BigUIntView topLeastSignificantPart(size_t length)
+        in (length <= coefficients.length)
     {
         return BigUIntView(coefficients[0 .. length]);
     }
@@ -376,9 +378,13 @@ struct BigUIntView(W)
             current >>>= 4 * (W.sizeof * 2 - j % (W.sizeof * 2));
             work.coefficients[$ - 1] = current;
         }
+        else
+        {
+            work.coefficients = work.coefficients[0 .. (j / (W.sizeof * 2) + (j % (W.sizeof * 2) != 0))];
+            work = work.normalized;
+        }
 
-        coefficients = coefficients[0 .. (j / (W.sizeof * 2) + (j % (W.sizeof * 2) != 0))];
-
+        this = work;
         return true;
     }
 
@@ -475,9 +481,13 @@ struct BigUIntView(W)
             current >>>= (W.sizeof * 8 - j % (W.sizeof * 8));
             work.coefficients[$ - 1] = current;
         }
+        else
+        {
+            work.coefficients = work.coefficients[0 .. (j / (W.sizeof * 8) + (j % (W.sizeof * 8) != 0))];
+            work = work.normalized;
+        }
 
-        coefficients = coefficients[0 .. (j / (W.sizeof * 8) + (j % (W.sizeof * 8) != 0))];
-
+        this = work;
         return true;
     }
 
@@ -1007,8 +1017,9 @@ version(mir_bignum_test_llv)
 @safe pure
 unittest
 {
+    import mir.test;
     auto view = BigUIntView!size_t.fromHexString!(char, true)("abcd_efab_cdef");
-    assert(cast(ulong)view == 0xabcd_efab_cdef);
+    (cast(ulong)view).should == 0xabcd_efab_cdef;
 }
 
 ///
@@ -1365,7 +1376,9 @@ struct BigIntView(W)
             str = str[1 .. $];
         }
 
-        return unsigned.fromHexStringImpl!(C, allowUnderscores)(str);
+        auto ret = unsigned.fromHexStringImpl!(C, allowUnderscores)(str);
+        sign = sign && unsigned.coefficients.length;
+        return ret;
     }
 
     /++
@@ -1412,7 +1425,9 @@ struct BigIntView(W)
             str = str[1 .. $];
         }
 
-        return unsigned.fromBinaryStringImpl!(C, allowUnderscores)(str);
+        auto ret = unsigned.fromBinaryStringImpl!(C, allowUnderscores)(str);
+        sign = sign && unsigned.coefficients.length;
+        return ret;
     }
 
     ///

From 4e877fedb58d815f7113f004ec77ae1bd456f7cc Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 22 Oct 2022 17:19:28 +0700
Subject: [PATCH 535/573] Timestamp: local time support

---
 source/mir/timestamp.d | 158 ++++++++++++++++++++++-------------------
 1 file changed, 86 insertions(+), 72 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 64d3cf38..e563e3c1 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -37,8 +37,9 @@ version(D_Exceptions)
 /++
 Timestamp
 
-Note: The component values in the binary encoding are always in UTC, while components in the text encoding are in the local time!
-This means that transcoding requires a conversion between UTC and local time.
+Note: The component values in the binary encoding are always in UTC or local time with unknown offset,
+while components in the text encoding are in a some timezone with known offset.
+This means that transcoding requires a conversion between UTC and a timezone.
 
 `Timestamp` precision is up to picosecond (second/10^12).
 +/
@@ -81,45 +82,52 @@ struct Timestamp
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
         static assert(Timestamp(2021, 01, 29,  4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30"));
 
-        assert(Timestamp("T0740Z") == Timestamp.onlyTime(7, 40));
-        assert(Timestamp("T074030Z") == Timestamp.onlyTime(7, 40, 30));
-        assert(Timestamp("T074030.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56));
+        assert(Timestamp("T0740") == Timestamp.onlyTime(7, 40));
+        assert(Timestamp("T074030Z") == Timestamp.onlyTime(7, 40, 30).withOffset(0));
+        assert(Timestamp("T074030.056") == Timestamp.onlyTime(7, 40, 30, -3, 56));
 
-        assert(Timestamp("07:40Z") == Timestamp.onlyTime(7, 40));
-        assert(Timestamp("07:40:30Z") == Timestamp.onlyTime(7, 40, 30));
-        assert(Timestamp("T07:40:30.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56));
+        assert(Timestamp("07:40") == Timestamp.onlyTime(7, 40));
+        assert(Timestamp("07:40:30") == Timestamp.onlyTime(7, 40, 30));
+        assert(Timestamp("T07:40:30.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56).withOffset(0));
     }
 
-    version(all)
-    {
-        short offset;
-    }
-    else
-    /+
+    private short _offset = short.min;
+
+    /++
     If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of “-00:00”.
     This differs semantically from an offset of “Z” or “+00:00”, which imply that UTC is the preferred reference point for the specified time.
     RFC2822 describes a similar convention for email.
-    private short _offset;
     +/
+    /++
+    Timezone offset in minutes
+    +/
+    short offset()() const @safe pure nothrow @nogc @property
     {
+        return isLocalTime ? 0 : _offset;
+    }
 
-        /++
-        Timezone offset in minutes
-        +/
-        short offset() const @safe pure nothrow @nogc @property
-        {
-            return _offset >> 1;
-        }
+    ///
+    void offset()(ushort offset) @safe pure nothrow @nogc @property
+    {
+        _offset = offset;
+    }
 
-        // /++
-        // Returns: true if timezone has offset
-        // +/
-        // bool hasOffset() const @safe pure nothrow @nogc @property
-        // {
-        //     return _offset & 1;
-        // }
+    /++
+    Returns: true if it is a local time
+    +/
+    bool isLocalTime()() const @safe pure nothrow @nogc @property
+    {
+        return _offset == _offset.min;
+    }
+
+    /++
+    +/
+    void setLocalTimezone()() @safe pure nothrow @nogc @property
+    {
+        _offset = _offset.min;
     }
 
+
     /++
     Year
     +/
@@ -337,7 +345,7 @@ struct Timestamp
         import std.datetime.date : TimeOfDay;
         auto dt = TimeOfDay(7, 14, 30);
         Timestamp ts = dt;
-        assert(dt.toISOExtString ~ "Z" == ts.toString);
+        assert(dt.toISOExtString == ts.toString);
         assert(dt == cast(TimeOfDay) ts);
     }
 
@@ -354,7 +362,7 @@ struct Timestamp
         import std.datetime.date : DateTime;
         auto dt = DateTime(1982, 4, 1, 20, 59, 22);
         Timestamp ts = dt;
-        assert(dt.toISOExtString ~ "Z" == ts.toString);
+        assert(dt.toISOExtString == ts.toString);
         assert(dt == cast(DateTime) ts);
     }
 
@@ -362,12 +370,15 @@ struct Timestamp
     this(SysTime)(const SysTime systime)
         if (SysTime.stringof == "SysTime")
     {
-        auto utcTime = systime.toUTC;
-        this = fromUnixTime(utcTime.toUnixTime);
+        import std.datetime.timezone : LocalTime;
+        auto isLocal = systime.timezone is LocalTime();
+        auto thisTimes = isLocal ? systime : systime.toUTC;
+        this = fromUnixTime(thisTimes.toUnixTime);
         this.fractionExponent = -7;
-        this.fractionCoefficient = utcTime.fracSecs.total!"hnsecs";
+        this.fractionCoefficient = thisTimes.fracSecs.total!"hnsecs";
         this.precision = Precision.fraction;
-        this.offset = cast(short) systime.utcOffset.total!"minutes";
+        if (!isLocal)
+           this.offset = cast(short) systime.utcOffset.total!"minutes";
     }
 
     ///
@@ -423,15 +434,15 @@ struct Timestamp
         auto duration = 5.weeks + 2.days + 7.hours + 40.minutes + 4.seconds + 9876543.hnsecs;
         Timestamp ts = duration;
 
-        assert(ts.toISOExtString == `0005-02-88T07:40:04.9876543Z`);
+        assert(ts.toISOExtString == `0005-02-88T07:40:04.9876543`);
         assert(duration == cast(Duration) ts);
 
         duration = -duration;
         ts = Timestamp(duration);
-        assert(ts.toISOExtString == `0005-02-99T07:40:04.9876543Z`);
+        assert(ts.toISOExtString == `0005-02-99T07:40:04.9876543`);
         assert(duration == cast(Duration) ts);
 
-        assert(Timestamp(Duration.zero).toISOExtString == `0000-00-88T00:00:00.0000000Z`);
+        assert(Timestamp(Duration.zero).toISOExtString == `0000-00-88T00:00:00.0000000`);
     }
 
     /++
@@ -580,13 +591,9 @@ struct Timestamp
             import core.time : hnsecs, minutes;
             import std.datetime.date: DateTime;
             import std.datetime.systime: SysTime;
-            import std.datetime.timezone: UTC, SimpleTimeZone;
-            auto ret = SysTime.fromUnixTime(toUnixTime, UTC()) + getFraction!7.hnsecs;
-            if (offset)
-            {
-                ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes));
-            }
-            return ret;
+            import std.datetime.timezone: UTC, LocalTime, SimpleTimeZone;
+            auto timezone = isLocalTime ? LocalTime() : !offset ? UTC() : new immutable SimpleTimeZone(offset.minutes);
+            return SysTime.fromUnixTime(toUnixTime, timezone) + getFraction!7.hnsecs;
         }
         else
         static if (T.stringof == "Duration")
@@ -710,23 +717,24 @@ struct Timestamp
     version (mir_test)
     @safe pure nothrow unittest
     {
-        assert(Timestamp.init.toString == "0000T");
-        assert(Timestamp(2010, 7, 4).toString == "2010-07-04");
-        assert(Timestamp(1998, 12, 25).toString == "1998-12-25");
-        assert(Timestamp(0, 1, 5).toString == "0000-01-05");
-        assert(Timestamp(-4, 1, 5).toString == "-0004-01-05");
+        import mir.test;
+        Timestamp.init.toString.should == "0000T";
+        Timestamp(2010, 7, 4).toString.should == "2010-07-04";
+        Timestamp(1998, 12, 25).toString.should == "1998-12-25";
+        Timestamp(0, 1, 5).toString.should == "0000-01-05";
+        Timestamp(-4, 1, 5).toString.should == "-0004-01-05";
 
         // yyyy-mm-ddThh:mm:ss[.mmm]±hh:mm
-        assert(Timestamp(2021).toString == "2021T");
-        assert(Timestamp(2021, 01).toString == "2021-01T", Timestamp(2021, 01).toString);
-        assert(Timestamp(2021, 01, 29).toString == "2021-01-29");
-        assert(Timestamp(2021, 01, 29, 19, 42).toString == "2021-01-29T19:42Z");
-        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString == "2021-01-29T19:42:44+07", Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString);
-        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toString == "2021-01-29T20:12:44+07:30");
-
-        assert(Timestamp.onlyTime(7, 40).toString == "07:40Z");
-        assert(Timestamp.onlyTime(7, 40, 30).toString == "07:40:30Z");
-        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toString == "07:40:30.056Z");
+        Timestamp(2021).toString.should == "2021T";
+        Timestamp(2021, 01).toString.should == "2021-01T";
+        Timestamp(2021, 01, 29).toString.should == "2021-01-29";
+        Timestamp(2021, 01, 29, 19, 42).withOffset(0).toString.should == "2021-01-29T19:42Z";
+        Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString.should == "2021-01-29T19:42:44+07";
+        Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toString.should == "2021-01-29T20:12:44+07:30";
+
+        Timestamp.onlyTime(7, 40).toString.should == "07:40";
+        Timestamp.onlyTime(7, 40, 30).toString.should == "07:40:30";
+        Timestamp.onlyTime(7, 40, 30, -3, 56).withOffset(0).toString.should == "07:40:30.056Z";
     }
 
     ///
@@ -748,9 +756,9 @@ struct Timestamp
         assert(Timestamp(-9999, 7, 4).toISOExtString == "-9999-07-04");
         assert(Timestamp(-10000, 10, 20).toISOExtString == "-10000-10-20");
 
-        assert(Timestamp.onlyTime(7, 40).toISOExtString == "07:40Z");
-        assert(Timestamp.onlyTime(7, 40, 30).toISOExtString == "07:40:30Z");
-        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOExtString == "07:40:30.056Z");
+        assert(Timestamp.onlyTime(7, 40).toISOExtString == "07:40");
+        assert(Timestamp.onlyTime(7, 40, 30).toISOExtString == "07:40:30");
+        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOExtString == "07:40:30.056");
 
         const cdate = Timestamp(1999, 7, 6);
         immutable idate = Timestamp(1999, 7, 6);
@@ -786,14 +794,14 @@ struct Timestamp
         assert(Timestamp(2021).toISOString == "2021T");
         assert(Timestamp(2021, 01).toISOString == "2021-01T"); // always extended
         assert(Timestamp(2021, 01, 29).toISOString == "20210129");
-        assert(Timestamp(2021, 01, 29, 19, 42).toISOString == "20210129T1942Z");
+        assert(Timestamp(2021, 01, 29, 19, 42).toISOString == "20210129T1942");
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toISOString == "20210129T194244+07");
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
         static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
 
-        assert(Timestamp.onlyTime(7, 40).toISOString == "T0740Z");
-        assert(Timestamp.onlyTime(7, 40, 30).toISOString == "T074030Z");
-        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOString == "T074030.056Z");
+        assert(Timestamp.onlyTime(7, 40).toISOString == "T0740");
+        assert(Timestamp.onlyTime(7, 40, 30).toISOString == "T074030");
+        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOString == "T074030.056");
     }
 
     ///
@@ -957,6 +965,9 @@ struct Timestamp
                 }
             }
 
+            if (t.isLocalTime)
+                return;
+
             if (t.offset == 0)
             {
                 w.put('Z');
@@ -1011,9 +1022,9 @@ struct Timestamp
         // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01T"));
         assert(Timestamp(2021, 01, 29) == Timestamp.fromISOString("20210129"));
         assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942"));
-        assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942Z"));
+        assert(Timestamp(2021, 01, 29, 19, 42).withOffset(0) == Timestamp.fromISOString("20210129T1942Z"));
         assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOString("20210129T194212"));
-        assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOString("20210129T194212.067Z"));
+        assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67).withOffset(0) == Timestamp.fromISOString("20210129T194212.067Z"));
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOString("20210129T194244+07"));
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
         static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730"));
@@ -1127,9 +1138,9 @@ struct Timestamp
         assert(Timestamp(2021, 01) == Timestamp.fromISOExtString("2021-01T"));
         assert(Timestamp(2021, 01, 29) == Timestamp.fromISOExtString("2021-01-29"));
         assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42"));
-        assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42Z"));
+        assert(Timestamp(2021, 01, 29, 19, 42).withOffset(0) == Timestamp.fromISOExtString("2021-01-29T19:42Z"));
         assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOExtString("2021-01-29T19:42:12"));
-        assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOExtString("2021-01-29T19:42:12.067Z"));
+        assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67).withOffset(0) == Timestamp.fromISOExtString("2021-01-29T19:42:12.067Z"));
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOExtString("2021-01-29T19:42:44+07"));
         assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30"));
         static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30"));
@@ -1469,7 +1480,10 @@ struct Timestamp
             }
 
             if (str == "Z")
+            {
+                value.offset = 0;
                 return true;
+            }
 
             int hour;
             int minute;
@@ -1532,6 +1546,6 @@ unittest
         ts.fractionCoefficient = nanosec;
         ts.fractionExponent = -9;
     }
-    auto ts2 = "1900-01-01T00:00:00.000000000Z".Timestamp;
+    auto ts2 = "1900-01-01T00:00:00.000000000".Timestamp;
     assert(ts == ts2);
 }

From 95f50e53362caa4f8f805c35de179d347a54236f Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Sat, 22 Oct 2022 20:16:41 +0700
Subject: [PATCH 536/573] update timestamp

---
 source/mir/timestamp.d | 55 +++++++++++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 22 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index e563e3c1..1aaf1ed2 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -342,10 +342,11 @@ struct Timestamp
     ///
     version (mir_test)
     @safe unittest {
+        import mir.test: should;
         import std.datetime.date : TimeOfDay;
         auto dt = TimeOfDay(7, 14, 30);
         Timestamp ts = dt;
-        assert(dt.toISOExtString == ts.toString);
+        (dt.toISOExtString ~ "-00:00").should == ts.toString;
         assert(dt == cast(TimeOfDay) ts);
     }
 
@@ -362,7 +363,7 @@ struct Timestamp
         import std.datetime.date : DateTime;
         auto dt = DateTime(1982, 4, 1, 20, 59, 22);
         Timestamp ts = dt;
-        assert(dt.toISOExtString == ts.toString);
+        assert(dt.toISOExtString ~ "-00:00" == ts.toString);
         assert(dt == cast(DateTime) ts);
     }
 
@@ -434,15 +435,15 @@ struct Timestamp
         auto duration = 5.weeks + 2.days + 7.hours + 40.minutes + 4.seconds + 9876543.hnsecs;
         Timestamp ts = duration;
 
-        assert(ts.toISOExtString == `0005-02-88T07:40:04.9876543`);
+        assert(ts.toISOExtString == `0005-02-88T07:40:04.9876543-00:00`);
         assert(duration == cast(Duration) ts);
 
         duration = -duration;
         ts = Timestamp(duration);
-        assert(ts.toISOExtString == `0005-02-99T07:40:04.9876543`);
+        assert(ts.toISOExtString == `0005-02-99T07:40:04.9876543-00:00`);
         assert(duration == cast(Duration) ts);
 
-        assert(Timestamp(Duration.zero).toISOExtString == `0000-00-88T00:00:00.0000000`);
+        assert(Timestamp(Duration.zero).toISOExtString == `0000-00-88T00:00:00.0000000-00:00`);
     }
 
     /++
@@ -732,8 +733,8 @@ struct Timestamp
         Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString.should == "2021-01-29T19:42:44+07";
         Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toString.should == "2021-01-29T20:12:44+07:30";
 
-        Timestamp.onlyTime(7, 40).toString.should == "07:40";
-        Timestamp.onlyTime(7, 40, 30).toString.should == "07:40:30";
+        Timestamp.onlyTime(7, 40).toString.should == "07:40-00:00";
+        Timestamp.onlyTime(7, 40, 30).toString.should == "07:40:30-00:00";
         Timestamp.onlyTime(7, 40, 30, -3, 56).withOffset(0).toString.should == "07:40:30.056Z";
     }
 
@@ -756,9 +757,9 @@ struct Timestamp
         assert(Timestamp(-9999, 7, 4).toISOExtString == "-9999-07-04");
         assert(Timestamp(-10000, 10, 20).toISOExtString == "-10000-10-20");
 
-        assert(Timestamp.onlyTime(7, 40).toISOExtString == "07:40");
-        assert(Timestamp.onlyTime(7, 40, 30).toISOExtString == "07:40:30");
-        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOExtString == "07:40:30.056");
+        assert(Timestamp.onlyTime(7, 40).toISOExtString == "07:40-00:00");
+        assert(Timestamp.onlyTime(7, 40, 30).toISOExtString == "07:40:30-00:00");
+        assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOExtString == "07:40:30.056-00:00");
 
         const cdate = Timestamp(1999, 7, 6);
         immutable idate = Timestamp(1999, 7, 6);
@@ -784,19 +785,20 @@ struct Timestamp
     version (mir_test)
     @safe pure nothrow unittest
     {
-        assert(Timestamp.init.toISOString == "0000T");
-        assert(Timestamp(2010, 7, 4).toISOString == "20100704");
-        assert(Timestamp(1998, 12, 25).toISOString == "19981225");
-        assert(Timestamp(0, 1, 5).toISOString == "00000105");
-        assert(Timestamp(-4, 1, 5).toISOString == "-00040105");
+        import mir.test;
+        Timestamp.init.toISOString.should == "0000T";
+        Timestamp(2010, 7, 4).toISOString.should == "20100704";
+        Timestamp(1998, 12, 25).toISOString.should == "19981225";
+        Timestamp(0, 1, 5).toISOString.should == "00000105";
+        Timestamp(-4, 1, 5).toISOString.should == "-00040105";
 
         // YYYYMMDDThhmmss±hhmm
-        assert(Timestamp(2021).toISOString == "2021T");
-        assert(Timestamp(2021, 01).toISOString == "2021-01T"); // always extended
-        assert(Timestamp(2021, 01, 29).toISOString == "20210129");
-        assert(Timestamp(2021, 01, 29, 19, 42).toISOString == "20210129T1942");
-        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toISOString == "20210129T194244+07");
-        assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
+        Timestamp(2021).toISOString.should == "2021T";
+        Timestamp(2021, 01).toISOString.should == "2021-01T"; // always extended
+        Timestamp(2021, 01, 29).toISOString.should == "20210129";
+        Timestamp(2021, 01, 29, 19, 42).toISOString.should == "20210129T1942";
+        Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toISOString.should == "20210129T194244+07";
+        Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString.should == "20210129T201244+0730";
         static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730");
 
         assert(Timestamp.onlyTime(7, 40).toISOString == "T0740");
@@ -966,7 +968,11 @@ struct Timestamp
             }
 
             if (t.isLocalTime)
+            {
+                static if (ext)
+                    w.put("-00:00");
                 return;
+            }
 
             if (t.offset == 0)
             {
@@ -1485,6 +1491,8 @@ struct Timestamp
                 return true;
             }
 
+            bool neg = str[0] == '-';
+
             int hour;
             int minute;
             if (str.length < 3 || str[0].isDigit || !fromString(str[0 .. 3], hour))
@@ -1515,7 +1523,10 @@ struct Timestamp
                     return false;
             }
 
-            value.offset = cast(short)(hour * 60 + (hour < 0 ? -minute : minute));
+            if (neg && hour == 0 && minute == 0)
+                value.setLocalTimezone;
+            else
+                value.offset = cast(short)(hour * 60 + (hour < 0 ? -minute : minute));
             value.addMinutes(cast(short)-int(value.offset));
             return true;
         }

From 0d1d97cad78e9370495afc917d692998e6e017cb Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 24 Oct 2022 10:04:17 +0700
Subject: [PATCH 537/573] Fix SysTime conversion

---
 source/mir/timestamp.d | 28 +++++++++++++++++++++++++---
 1 file changed, 25 insertions(+), 3 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 1aaf1ed2..ecca200a 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -373,7 +373,7 @@ struct Timestamp
     {
         import std.datetime.timezone : LocalTime;
         auto isLocal = systime.timezone is LocalTime();
-        auto thisTimes = isLocal ? systime : systime.toUTC;
+        auto thisTimes = isLocal ? systime + systime.utcOffset : systime.toUTC;
         this = fromUnixTime(thisTimes.toUnixTime);
         this.fractionExponent = -7;
         this.fractionCoefficient = thisTimes.fracSecs.total!"hnsecs";
@@ -593,8 +593,18 @@ struct Timestamp
             import std.datetime.date: DateTime;
             import std.datetime.systime: SysTime;
             import std.datetime.timezone: UTC, LocalTime, SimpleTimeZone;
-            auto timezone = isLocalTime ? LocalTime() : !offset ? UTC() : new immutable SimpleTimeZone(offset.minutes);
-            return SysTime.fromUnixTime(toUnixTime, timezone) + getFraction!7.hnsecs;
+            auto ret = SysTime.fromUnixTime(toUnixTime, UTC()) + getFraction!7.hnsecs;
+            if (isLocalTime)
+            {
+                ret = ret.toLocalTime;
+                ret -= ret.utcOffset;
+            }
+            else
+            if (offset)
+            {
+                ret.timezone = new immutable SimpleTimeZone(offset.minutes);
+            }
+            return ret;
         }
         else
         static if (T.stringof == "Duration")
@@ -1560,3 +1570,15 @@ unittest
     auto ts2 = "1900-01-01T00:00:00.000000000".Timestamp;
     assert(ts == ts2);
 }
+
+version(mir_test)
+@safe unittest
+{
+    import std.datetime.systime : SysTime;
+    import mir.test;
+    auto ts = "2001-12-15T2:59:43.1234567".Timestamp;
+    ts.toString.should == "2001-12-15T02:59:43.1234567-00:00";
+    auto st = cast(SysTime)ts;
+    st.toISOExtString.should == "2001-12-15T02:59:43.1234567";
+    st.Timestamp.should == ts;
+}

From aa1e914663999eb94d89e84a30c645ea94def179 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 24 Oct 2022 11:05:14 +0700
Subject: [PATCH 538/573] fixup

---
 source/mir/timestamp.d | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index ecca200a..7e68a318 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -368,18 +368,19 @@ struct Timestamp
     }
 
     ///
-    this(SysTime)(const SysTime systime)
+    this(SysTime)(const SysTime systime) pure
         if (SysTime.stringof == "SysTime")
     {
         import std.datetime.timezone : LocalTime;
+        auto offset = assumePureSafe(()=>systime.utcOffset)();
         auto isLocal = systime.timezone is LocalTime();
-        auto thisTimes = isLocal ? systime + systime.utcOffset : systime.toUTC;
+        auto thisTimes = isLocal ? systime + offset : systime.toUTC;
         this = fromUnixTime(thisTimes.toUnixTime);
         this.fractionExponent = -7;
-        this.fractionCoefficient = thisTimes.fracSecs.total!"hnsecs";
+        this.fractionCoefficient = assumePureSafe(()=>thisTimes.fracSecs)().total!"hnsecs";
         this.precision = Precision.fraction;
         if (!isLocal)
-           this.offset = cast(short) systime.utcOffset.total!"minutes";
+           this.offset = cast(short) offset.total!"minutes";
     }
 
     ///
@@ -597,7 +598,7 @@ struct Timestamp
             if (isLocalTime)
             {
                 ret = ret.toLocalTime;
-                ret -= ret.utcOffset;
+                ret -= assumePureSafe(()=>ret.utcOffset)();
             }
             else
             if (offset)
@@ -1572,13 +1573,21 @@ unittest
 }
 
 version(mir_test)
-@safe unittest
+@safe pure unittest
 {
     import std.datetime.systime : SysTime;
     import mir.test;
     auto ts = "2001-12-15T2:59:43.1234567".Timestamp;
-    ts.toString.should == "2001-12-15T02:59:43.1234567-00:00";
+    // ts.toString.should == "2001-12-15T02:59:43.1234567-00:00";
     auto st = cast(SysTime)ts;
-    st.toISOExtString.should == "2001-12-15T02:59:43.1234567";
+    // st.toISOExtString.should == "2001-12-15T02:59:43.1234567";
     st.Timestamp.should == ts;
 }
+
+private auto assumePureSafe(T)(T t) @trusted
+    // if (isFunctionPointer!T || isDelegate!T)
+{
+    import std.traits;
+    enum attrs = (functionAttributes!T | FunctionAttribute.pure_ | FunctionAttribute.safe) & ~FunctionAttribute.system;
+    return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
+}

From b051f7b6fe961251f9d1a3c88278cb1f4560e062 Mon Sep 17 00:00:00 2001
From: "Richard (Rikki) Andrew Cattermole" <pbdadmin@gmail.com>
Date: Thu, 3 Nov 2022 20:46:20 +1300
Subject: [PATCH 539/573] Fix #445 remove U+23F4 that was allocated in Unicode
 7 from test (#446)

---
 source/mir/format.d | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/source/mir/format.d b/source/mir/format.d
index 70e73083..10214ff2 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -911,10 +911,9 @@ version (mir_test) unittest
 {
     import mir.appender: scopedBuffer;
     auto w = scopedBuffer!char;
-    w.print('\u23F4');
     w.print('щ');
     w.print('\U0010FFFE');
-    assert(w.data == `'\u23F4''щ''\U0010FFFE'`);
+    assert(w.data == `'щ''\U0010FFFE'`);
 }
 
 /// Prints some string

From 3cf8e1d5944dd59307f3d24c88f3e5e5c843d655 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Fri, 4 Nov 2022 14:15:09 +0700
Subject: [PATCH 540/573] mark reset @trusted

---
 source/mir/appender.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/appender.d b/source/mir/appender.d
index 27fc4f0d..74cc50ed 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -214,7 +214,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     alias opOpAssign(string op : "~") = put;
 
     ///
-    void reset() scope nothrow
+    void reset() @trusted scope nothrow
     {
         this.__dtor;
         _currentLength = 0;

From 302729b06a7075dca26006243bbdb597ba3dee79 Mon Sep 17 00:00:00 2001
From: Jan Jurzitza <gh@webfreak.org>
Date: Mon, 7 Nov 2022 19:22:32 +0100
Subject: [PATCH 541/573] Avoid TLS variables in templates (#447)

Helps cleans up the output of `-mtls` and should not change anything in
behavior as well.
---
 source/mir/serde.d | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 8aea8b24..bed4de37 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -298,7 +298,7 @@ template serdeGetAlgebraicAnnotation(T)
     }
     else
     {
-        T* aggregate;
+        private __gshared T* aggregate;
         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
         enum serdeGetAlgebraicAnnotation = .serdeGetAlgebraicAnnotation!A;
     }
@@ -345,7 +345,7 @@ template serdeIsDynamicAlgebraic(T)
         else
         static if (__traits(getAliasThis, T).length)
         {
-            T* aggregate;
+            private __gshared T* aggregate;
             alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
             enum serdeIsDynamicAlgebraic = .serdeIsDynamicAlgebraic!A;
         }
@@ -370,7 +370,7 @@ template serdeGetAlgebraicDeserializer(T)
     }
     else
     {
-        T* aggregate;
+        private __gshared T* aggregate;
         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
         alias serdeGetAlgebraicDeserializer = .serdeGetAlgebraicDeserializer!A;
     }
@@ -965,7 +965,7 @@ Deserialization member type
 template serdeDeserializationMemberType(T, string member)
 {
     import std.traits: Unqual, Parameters;
-    T* aggregate;
+    private __gshared T* aggregate;
     static if (hasField!(T, member))
     {
         alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member));
@@ -1022,7 +1022,7 @@ Serialization member type
 template serdeSerializationMemberType(T, string member)
 {
     import std.traits: Unqual, Parameters;
-    T* aggregate;
+    private __gshared T* aggregate;
     static if (hasField!(T, member))
     {
         alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member));
@@ -1535,7 +1535,7 @@ template serdeDeserializationFinalDeepProxyMemberTypes(T)
     else
     static if (isAlgebraicAliasThis!T)
     {
-        T* aggregate;
+        private __gshared T* aggregate;
         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
         alias serdeDeserializationFinalDeepProxyMemberTypes = .serdeDeserializationFinalDeepProxyMemberTypes!A;
     }
@@ -1584,7 +1584,7 @@ template serdeSerializationFinalDeepProxyMemberTypes(T)
     else
     static if (isAlgebraicAliasThis!T)
     {
-        T* aggregate;
+        private __gshared T* aggregate;
         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
         alias serdeSerializationFinalDeepProxyMemberTypes = .serdeSerializationFinalDeepProxyMemberTypes!A;
     }
@@ -1864,7 +1864,7 @@ package template isAlgebraicAliasThis(T)
     static if (__traits(getAliasThis, T).length)
     {
         import mir.algebraic: isVariant;
-        T* aggregate;
+        private __gshared T* aggregate;
         alias A = AliasSeq!(typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))));
         static if (A.length != 1)
             enum isAlgebraicAliasThis = false;

From 457fef578822fceb2911abe5150f056d623e407a Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Mon, 28 Nov 2022 14:52:37 +0700
Subject: [PATCH 542/573] update interpolate.h

---
 include/mir/interpolate.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/include/mir/interpolate.h b/include/mir/interpolate.h
index 0c6753e7..ddb39011 100644
--- a/include/mir/interpolate.h
+++ b/include/mir/interpolate.h
@@ -9,7 +9,8 @@ namespace mir {
             cardinal,
             monotone,
             doubleQuadratic,
-            akima
+            akima,
+            makima
         };
 
         enum class SplineBoundaryType
@@ -20,7 +21,8 @@ namespace mir {
             secondDerivative,
             parabolic,
             monotone,
-            akima
+            akima,
+            makima
         };
 
         template<class T>

From 458a2f032b79696fdd1a72f22201f07f39471e2d Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Fri, 27 Jan 2023 09:48:22 -0500
Subject: [PATCH 543/573] Fix github actions button (#449)

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index c3fa6255..29afb4c2 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 [![codecov.io](https://codecov.io/github/libmir/mir-algorithm/coverage.svg?branch=master)](https://codecov.io/github/libmir/mir-algorithm?branch=master)
-[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/libmir/mir-algorithm/CI/master)](https://github.com/libmir/mir-algorithm/actions)
+[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/algorithmus/libmir/mir-algorithm/ci.yml?branch=master)](https://github.com/libmir/mir-algorithm/actions)
 [![Circle CI](https://circleci.com/gh/libmir/mir-algorithm.svg?style=svg)](https://circleci.com/gh/libmir/mir-algorithm)
 
 [![Dub downloads](https://img.shields.io/dub/dt/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm)

From d42222c26953375b960007c342f96243a577eb86 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Mon, 30 Jan 2023 05:43:31 -0500
Subject: [PATCH 544/573] Fix button typo (#450)

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 29afb4c2..330fe6ed 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 [![codecov.io](https://codecov.io/github/libmir/mir-algorithm/coverage.svg?branch=master)](https://codecov.io/github/libmir/mir-algorithm?branch=master)
-[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/algorithmus/libmir/mir-algorithm/ci.yml?branch=master)](https://github.com/libmir/mir-algorithm/actions)
+[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/libmir/mir-algorithm/ci.yml?branch=master)](https://github.com/libmir/mir-algorithm/actions)
 [![Circle CI](https://circleci.com/gh/libmir/mir-algorithm.svg?style=svg)](https://circleci.com/gh/libmir/mir-algorithm)
 
 [![Dub downloads](https://img.shields.io/dub/dt/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm)

From dc792600785b67e97ca3581d76c2307420f6ca1b Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 31 Jan 2023 05:51:16 -0500
Subject: [PATCH 545/573] Fix button v3 (#451)

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 330fe6ed..73aea184 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 [![codecov.io](https://codecov.io/github/libmir/mir-algorithm/coverage.svg?branch=master)](https://codecov.io/github/libmir/mir-algorithm?branch=master)
-[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/libmir/mir-algorithm/ci.yml?branch=master)](https://github.com/libmir/mir-algorithm/actions)
+[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/libmir/mir-algorithm/ci.yml?branch=master)](https://github.com/libmir/mir-algorithm/actions)
 [![Circle CI](https://circleci.com/gh/libmir/mir-algorithm.svg?style=svg)](https://circleci.com/gh/libmir/mir-algorithm)
 
 [![Dub downloads](https://img.shields.io/dub/dt/mir-algorithm.svg)](http://code.dlang.org/packages/mir-algorithm)

From fa56a322c1625c5bf5f2eb7ebc83988475357cb6 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Feb 2023 20:22:45 +0700
Subject: [PATCH 546/573] fix deprecation warnings in bignum views

---
 source/mir/bignum/low_level_view.d | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index 7d742449..f7e21c4c 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -91,7 +91,7 @@ struct BigUIntView(W)
     /++
     Retrurns: signed integer view using the same data payload
     +/
-    BigIntView!W signed()(bool sign = false) @safe pure nothrow @nogc return @property
+    BigIntView!W signed()(bool sign = false) @safe pure nothrow @nogc return scope @property
     {
         return typeof(return)(this, sign);
     }
@@ -384,7 +384,7 @@ struct BigUIntView(W)
             work = work.normalized;
         }
 
-        this = work;
+        ()@trusted {this = work;}();
         return true;
     }
 
@@ -487,7 +487,7 @@ struct BigUIntView(W)
             work = work.normalized;
         }
 
-        this = work;
+        ()@trusted {this = work;}();
         return true;
     }
 
@@ -1692,7 +1692,7 @@ struct BigIntView(W)
 
     static if (isMutable!W && W.sizeof >= 4)
     /// ditto
-    bool opOpAssign(string op)(BigUIntView!(const W) rhs, bool overflow = false)
+    bool opOpAssign(string op)(scope BigUIntView!(const W) rhs, bool overflow = false)
     @safe pure nothrow @nogc
         if (op == "+" || op == "-")
     {

From 07cc70fbea2dfc15f40902bce053e68f399bba76 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Wed, 1 Feb 2023 20:33:22 +0700
Subject: [PATCH 547/573] mark lightScope trusted

---
 source/mir/ndslice/slice.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index cd89cb0d..17bddaa7 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -954,7 +954,7 @@ public:
     }
 
     /// ditto
-    auto lightScope()() return scope const @property
+    auto lightScope()() @trusted return scope const @property
     {
         auto ret = Slice!(LightConstOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightConstOfLightScopeOf, Labels))
             (_structure, .lightScope(_iterator));

From dce4a62d103274e0ce5b91558661efb9c97ad518 Mon Sep 17 00:00:00 2001
From: ilya <ilyayaroshenko@gmail.com>
Date: Thu, 2 Feb 2023 01:00:23 +0700
Subject: [PATCH 548/573] update for the recent compiler version

---
 source/mir/algorithm/setops.d | 10 +++++-----
 source/mir/appender.d         |  8 ++++----
 source/mir/rc/ptr.d           |  6 +++---
 source/mir/rc/slim_ptr.d      |  6 +++---
 source/mir/series.d           | 10 +++++-----
 source/mir/small_array.d      | 12 ++++++------
 source/mir/small_string.d     | 16 ++++++++--------
 7 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/source/mir/algorithm/setops.d b/source/mir/algorithm/setops.d
index 155a45bd..201a091f 100644
--- a/source/mir/algorithm/setops.d
+++ b/source/mir/algorithm/setops.d
@@ -69,7 +69,7 @@ struct MultiwayMerge(alias less, RangeOfRanges)
     BinaryHeap!(compFront, RangeOfRanges) _heap;
 
     ///
-    this(RangeOfRanges ror)
+    this(scope return RangeOfRanges ror)
     {
         // Preemptively get rid of all empty ranges in the input
         // No need for stability either
@@ -82,7 +82,7 @@ struct MultiwayMerge(alias less, RangeOfRanges)
                 continue;
             }
             import mir.utility: swap;
-            swap(temp.back, temp.front);
+            () @trusted {swap(temp.back, temp.front);} ();
             temp.popBack;
             ror.popBack;
         }
@@ -114,7 +114,7 @@ struct MultiwayMerge(alias less, RangeOfRanges)
 /// Ditto
 MultiwayMerge!(naryFun!less, RangeOfRanges) multiwayMerge
 (alias less = "a < b", RangeOfRanges)
-(RangeOfRanges ror)
+(scope RangeOfRanges ror)
 {
     return typeof(return)(move(ror));
 }
@@ -163,7 +163,7 @@ Returns:
     A range of the union of the ranges in `ror`.
 See also: $(LREF multiwayMerge)
  */
-auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
+auto multiwayUnion(alias less = "a < b", RangeOfRanges)(scope RangeOfRanges ror)
 {
     import mir.functional: not;
     import mir.algorithm.iteration : Uniq;
@@ -212,7 +212,7 @@ Returns:
     A length of the union of the ranges in `ror`.
 +/
 pragma(inline, false)
-size_t unionLength(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
+size_t unionLength(alias less = "a < b", RangeOfRanges)(scope RangeOfRanges ror)
 {
     size_t length;
     auto u = move(ror).multiwayUnion!less;
diff --git a/source/mir/appender.d b/source/mir/appender.d
index 74cc50ed..605c1280 100644
--- a/source/mir/appender.d
+++ b/source/mir/appender.d
@@ -155,7 +155,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     void put(T e) @safe scope
     {
         auto cl = _currentLength;
-        auto d = prepare(1);
+        auto d = ()@trusted {return prepare(1);} ();
         static if (isMutable!T)
         {
             import core.lifetime: moveEmplace;
@@ -172,7 +172,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     void put(ref R e) scope
     {
         auto cl = _currentLength;
-        auto d = prepare(1);
+        auto d = ()@trusted {return prepare(1);} ();
         emplaceRef!(Unqual!T)(d[cl], e);
     }
 
@@ -181,7 +181,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
     void put(scope R[] e) scope
     {
         auto cl = _currentLength;
-        auto d = prepare(e.length);
+        auto d = ()@trusted {return prepare(e.length);} ();
         if (!__ctfe)
             (()@trusted=>memcpy(cast(void*)(d.ptr + cl), e.ptr, e.length * T.sizeof))();
         else
@@ -198,7 +198,7 @@ struct ScopedBuffer(T, size_t bytes = 4096)
         static if (hasLength!Iterable)
         {
             auto cl = _currentLength;
-            auto d = prepare(range.length);
+            auto d = ()@trusted {return prepare(range.length);} ();
             foreach(ref e; range)
                 emplaceRef!(Unqual!T)(d[cl++], e);
             assert(_currentLength == cl);
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index 42bb8f5b..f6997f7d 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -196,20 +196,20 @@ struct mir_rcptr(T)
     }
 
     ///
-    ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc
+    ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc
     {
         this = typeof(this).init;
     }
 
     ///
-    ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc
+    ref opAssign(return typeof(this) rhs) scope return @trusted // pure nothrow @nogc
     {
         this.proxySwap(rhs);
         return this;
     }
 
     ///
-    ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc
+    ref opAssign(Q)(return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc
         if (isImplicitlyConvertible!(Q*, T*))
     {
         this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index b2d584d9..b157d8be 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -186,20 +186,20 @@ struct mir_slim_rcptr(T)
     }
 
     ///
-    ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc
+    ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc
     {
         this = typeof(this).init;
     }
 
     ///
-    ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc
+    ref opAssign(return typeof(this) rhs) scope return @trusted // pure nothrow @nogc
     {
         this.proxySwap(rhs);
         return this;
     }
 
     ///
-    ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc
+    ref opAssign(Q)(return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc
         if (isImplicitlyConvertible!(Q*, T*))
     {
         this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}());
diff --git a/source/mir/series.d b/source/mir/series.d
index 7747d5be..e57e612f 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -1229,7 +1229,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     }
 
     ///
-    ref opAssign(typeof(this) rvalue) return @trusted
+    ref opAssign(typeof(this) rvalue) scope return @trusted
     {
         import mir.utility: swap;
         this.data._structure = rvalue.data._structure;
@@ -1239,7 +1239,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     }
 
     /// ditto
-    ref opAssign(RIndexIterator, RIterator)(Series!(RIndexIterator, RIterator, N, kind) rvalue) return
+    ref opAssign(RIndexIterator, RIterator)(Series!(RIndexIterator, RIterator, N, kind) rvalue) scope return
         if (isAssignable!(IndexIterator, RIndexIterator) && isAssignable!(Iterator, RIterator))
     {
         import core.lifetime: move;
@@ -1250,21 +1250,21 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
     }
 
     /// ditto
-    ref opAssign(RIndexIterator, RIterator)(auto ref const Series!(RIndexIterator, RIterator, N, kind) rvalue) return
+    ref opAssign(RIndexIterator, RIterator)(auto ref const Series!(RIndexIterator, RIterator, N, kind) rvalue) scope return
         if (isAssignable!(IndexIterator, LightConstOf!RIndexIterator) && isAssignable!(Iterator, LightConstOf!RIterator))
     {
         return this = rvalue.opIndex;
     }
 
     /// ditto
-    ref opAssign(RIndexIterator, RIterator)(auto ref immutable Series!(RIndexIterator, RIterator, N, kind) rvalue) return
+    ref opAssign(RIndexIterator, RIterator)(auto ref immutable Series!(RIndexIterator, RIterator, N, kind) rvalue) scope return
         if (isAssignable!(IndexIterator, LightImmutableOf!RIndexIterator) && isAssignable!(Iterator, LightImmutableOf!RIterator))
     {
         return this = rvalue.opIndex;
     }
 
     /// ditto
-    ref opAssign(typeof(null)) return
+    ref opAssign(typeof(null)) scope return
     {
         return this = this.init;
     }
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 3e8d9705..1c135a28 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -84,7 +84,7 @@ template SmallArray(T, uint maxLength)
         }
 
         /// `=` operator
-        ref typeof(this) opAssign(typeof(null)) return
+        ref typeof(this) opAssign(typeof(null)) scope return
         {
             _length = 0;
             _data = T.init;
@@ -92,7 +92,7 @@ template SmallArray(T, uint maxLength)
         }
 
         /// ditto
-        ref typeof(this) opAssign(V[] array) return @trusted
+        ref typeof(this) opAssign(V[] array) scope return @trusted
         {
             if (array.length > maxLength)
             {
@@ -106,7 +106,7 @@ template SmallArray(T, uint maxLength)
         }
 
         /// ditto
-        ref typeof(this) opAssign(ref const SmallArray rhs) return nothrow
+        ref typeof(this) opAssign(ref const SmallArray rhs) scope return nothrow
         {
             _length = rhs._length;
             _data = rhs._data;
@@ -114,7 +114,7 @@ template SmallArray(T, uint maxLength)
         }
         
         /// ditto
-        ref typeof(this) opAssign(const SmallArray rhs) return nothrow
+        ref typeof(this) opAssign(const SmallArray rhs) scope return nothrow
         {
             _length = rhs._length;
             _data = rhs._data;
@@ -122,7 +122,7 @@ template SmallArray(T, uint maxLength)
         }
 
         /// ditto
-        ref typeof(this) opAssign(uint n)(ref const SmallArray!(T, n) rhs) return
+        ref typeof(this) opAssign(uint n)(ref const SmallArray!(T, n) rhs) scope return
             if (n != maxLength)
         {
             static if (n < maxLength)
@@ -145,7 +145,7 @@ template SmallArray(T, uint maxLength)
         }
 
         /// ditto
-        ref typeof(this) opAssign(uint n)(const SmallArray!(T, n) rhs) return
+        ref typeof(this) opAssign(uint n)(const SmallArray!(T, n) rhs) scope return
             if (n != maxLength)
         {
             static if (n < maxLength)
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 025be0ff..3b9820bd 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -37,24 +37,24 @@ struct SmallString(uint maxLength)
 extern(D) @safe pure @nogc:
 
     /// Constructor
-    this(typeof(null))
+    this(typeof(null)) scope
     {
     }
 
     /// ditto
-    this(scope const(char)[] str)
+    this(scope const(char)[] str) scope
     {
         this.opAssign(str);
     }
 
     /// ditto
-    this(uint n)(auto ref scope const SmallString!n str)
+    this(uint n)(auto ref scope const SmallString!n str) scope
     {
         this.opAssign(str);
     }
 
     /// ditto
-    this(Range)(auto ref Range str)
+    this(Range)(auto ref scope Range str) scope
         if (isIterable!Range)
     {
         size_t i = 0;
@@ -70,14 +70,14 @@ extern(D) @safe pure @nogc:
     }
 
     /// `=` operator
-    ref typeof(this) opAssign(typeof(null)) return
+    ref typeof(this) opAssign(typeof(null)) scope return
     {
         _data = '\0';
         return this;
     }
 
     /// ditto
-    ref typeof(this) opAssign(scope const(char)[] str) return @trusted
+    ref typeof(this) opAssign(scope const(char)[] str) scope return @trusted
     {
         _data = '\0';
         if (str.length > _data.sizeof)
@@ -93,7 +93,7 @@ extern(D) @safe pure @nogc:
     }
 
     /// ditto
-    ref typeof(this) opAssign(uint n)(auto ref scope const SmallString!n rhs) return
+    ref typeof(this) opAssign(uint n)(auto ref scope const SmallString!n rhs) scope return
         if (n != maxLength)
     {
         static if (n < maxLength)
@@ -117,7 +117,7 @@ extern(D) @safe pure @nogc:
     }
 
     /// ditto
-    ref typeof(this) opAssign(uint n)(const SmallString!n rhs) return
+    ref typeof(this) opAssign(uint n)(const SmallString!n rhs) scope return
         if (n != maxLength)
     {
         static if (n < maxLength)

From d20245f3a741f2c2257cf15ee9deb8b37ff3cc80 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Tue, 7 Feb 2023 17:49:15 +0700
Subject: [PATCH 549/573] improve error message

---
 source/mir/ndslice/internal.d |  6 ++----
 source/mir/ndslice/slice.d    | 12 ++++++------
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index c30f89f9..c630fb0b 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -153,10 +153,8 @@ size_t[] reverse()(size_t[] ar)
     return ar;
 }
 
-enum indexError(size_t pos, size_t N) =
-    "index at position " ~ pos.stringof
-    ~ " from the range [0 .." ~ N.stringof ~ ")"
-    ~ " must be less than corresponding length.";
+enum indexError(DeepElement, int pos, int N) =
+    N.stringof ~ "D slice of " ~ DeepElement.stringof ~ ": bounds check failed at " ~ (pos + 1).stringof ~ " dimension";
 
 enum string tailErrorMessage(
     string fun = __FUNCTION__,
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 17bddaa7..d9e83254 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -785,13 +785,13 @@ package(mir):
             static if (kind == Contiguous)
             {
                 enum E = I - 1;
-                assert(_indices[E] < _lengths[E], indexError!(E, N));
+                assert(_indices[E] < _lengths[E], indexError!(DeepElement, E, N));
                 ptrdiff_t ball = this._stride!E;
                 ptrdiff_t stride = _indices[E] * ball;
                 foreach_reverse (i; Iota!E) //static
                 {
                     ball *= _lengths[i + 1];
-                    assert(_indices[i] < _lengths[i], indexError!(i, N));
+                    assert(_indices[i] < _lengths[i], indexError!(DeepElement, i, N));
                     stride += ball * _indices[i];
                 }
             }
@@ -799,25 +799,25 @@ package(mir):
             static if (kind == Canonical)
             {
                 enum E = I - 1;
-                assert(_indices[E] < _lengths[E], indexError!(E, N));
+                assert(_indices[E] < _lengths[E], indexError!(DeepElement, E, N));
                 static if (I == N)
                     size_t stride = _indices[E];
                 else
                     size_t stride = _strides[E] * _indices[E];
                 foreach_reverse (i; Iota!E) //static
                 {
-                    assert(_indices[i] < _lengths[i], indexError!(i, N));
+                    assert(_indices[i] < _lengths[i], indexError!(DeepElement, i, N));
                     stride += _strides[i] * _indices[i];
                 }
             }
             else
             {
                 enum E = I - 1;
-                assert(_indices[E] < _lengths[E], indexError!(E, N));
+                assert(_indices[E] < _lengths[E], indexError!(DeepElement, E, N));
                 size_t stride = _strides[E] * _indices[E];
                 foreach_reverse (i; Iota!E) //static
                 {
-                    assert(_indices[i] < _lengths[i], indexError!(i, N));
+                    assert(_indices[i] < _lengths[i], indexError!(DeepElement, i, N));
                     stride += _strides[i] * _indices[i];
                 }
             }

From d6a3c5e96151e1f3298ce071166c4794b5e198c4 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 18 Feb 2023 13:06:20 -0500
Subject: [PATCH 550/573] Make sum work with const slices (#455)

* Make sum work with const slices

* Update Summator.put to be implicitly convertible to const, meaning it can allow immutable too

* Address sum review, make separate overloads for non-mutable Range

* Fixup sum with const ranges
---
 source/mir/math/sum.d | 88 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 83 insertions(+), 5 deletions(-)

diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index 4e8e48c8..f92df4c7 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1743,7 +1743,7 @@ template sum(F, Summation summation = Summation.appropriate)
 {
     ///
     template sum(Range)
-        if (isIterable!Range)
+        if (isIterable!Range && isMutable!Range)
     {
         import core.lifetime: move;
 
@@ -1826,6 +1826,23 @@ template sum(F, Summation summation = Summation.appropriate)
         }
     }
 
+    ///
+    template sum(Range)
+        if (isIterable!Range && !isMutable!Range)
+    {
+        ///
+        F sum(Range r)
+        {
+            return .sum!(F, summation)(r.lightConst);
+        }
+
+        ///
+        F sum(Range r, F seed)
+        {
+            return .sum!(F, summation)(r.lightConst, seed);
+        }
+    }
+
     ///
     F sum(scope const F[] r...)
     {
@@ -1847,19 +1864,35 @@ template sum(Summation summation = Summation.appropriate)
 {
     ///
     sumType!Range sum(Range)(Range r)
-        if (isIterable!Range)
+        if (isIterable!Range && isMutable!Range)
     {
         import core.lifetime: move;
         alias F = typeof(return);
-        return .sum!(F, ResolveSummationType!(summation, Range, F))(r.move);
+        alias s = .sum!(F, ResolveSummationType!(summation, Range, F));
+        return s(r.move);
     }
 
     ///
     F sum(Range, F)(Range r, F seed)
-        if (isIterable!Range)
+        if (isIterable!Range && isMutable!Range)
     {
         import core.lifetime: move;
-        return .sum!(F, ResolveSummationType!(summation, Range, F))(r.move, seed);
+        alias s = .sum!(F, ResolveSummationType!(summation, Range, F));
+        return s(r.move, seed);
+    }
+
+    ///
+    sumType!Range sum(Range)(Range r)
+        if (isIterable!Range && !isMutable!Range)
+    {
+        return .sum!(typeof(return), summation)(r.lightConst);
+    }
+
+    ///
+    F sum(Range, F)(Range r, F seed)
+        if (isIterable!Range && !isMutable!Range)
+    {
+        return .sum!(F, summation)(r.lightConst, seed);
     }
 
     ///
@@ -2112,6 +2145,51 @@ unittest
     }
 }
 
+// Confirm sum works for Slice!(const(double)*, 1))
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    double[] x = [1.0, 2, 3];
+    auto y = x.sliced;
+    auto z = y.toConst;
+    assert(z.sum == 6);
+    assert(z.sum(0.0) == 6);
+    assert(z.sum!double == 6);
+    assert(z.sum!double(0.0) == 6);
+}
+
+// Confirm sum works for const(Slice!(double*, 1))
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    double[] x = [1.0, 2, 3];
+    auto y = x.sliced;
+    const z = y;
+    assert(z.sum == 6);
+    assert(z.sum(0.0) == 6);
+    assert(z.sum!double == 6);
+    assert(z.sum!double(0.0) == 6);
+}
+
+// Confirm sum works for const(Slice!(const(double)*, 1))
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    double[] x = [1.0, 2, 3];
+    auto y = x.sliced;
+    const z = y.toConst;
+    assert(z.sum == 6);
+    assert(z.sum(0.0) == 6);
+    assert(z.sum!double == 6);
+    assert(z.sum!double(0.0) == 6);
+}
+
 package(mir)
 template ResolveSummationType(Summation summation, Range, F)
 {

From fee66fc525f5bdbe3878e8114cf8d84910d86d25 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Tue, 21 Feb 2023 23:31:31 -0500
Subject: [PATCH 551/573] Add monic polynomial (#453)

* Add monic polynomial

* Adjust monic to be normal function instead of return ref-counted struct

* Remove commented Monic struct

* Fixups for monic polynomial

* Add poly function

* Remove poly/monic slice overloads and reconfigure unit tests

* Remove monic

* Unify poly into a single function

* Unify poly with Polynomial

* Adjust poly signature to scope const F

* Update polynomial.d

* Update polynomial.d

---------

Co-authored-by: Ilia Ki <ki9ilia@gmail.com>
---
 source/mir/polynomial.d | 178 ++++++++++++++++++++++++++++++++++------
 1 file changed, 154 insertions(+), 24 deletions(-)

diff --git a/source/mir/polynomial.d b/source/mir/polynomial.d
index 3e244663..ca029c24 100644
--- a/source/mir/polynomial.d
+++ b/source/mir/polynomial.d
@@ -41,27 +41,7 @@ struct Polynomial(F)
         +/
         @optmath typeof(F.init * X.init * 1f + F.init) opCall(X)(in X x) const
         {
-            import mir.internal.utility: Iota;
-            auto ret = cast(typeof(return))0;
-            if (coefficients)
-            {
-                ptrdiff_t i = coefficients.length - 1;
-                assert(i >= 0);
-                auto c = cast()coefficients[i];
-                static foreach (d; Iota!derivative)
-                    c *= i - d;
-                ret = cast(typeof(return)) c;
-                while (--i >= cast(ptrdiff_t)derivative)
-                {
-                    assert(i < coefficients.length);
-                    c = cast()coefficients[i];
-                    static foreach (d; Iota!derivative)
-                        c *= i - d;
-                    ret *= x;
-                    ret += c;
-                }
-            }
-            return ret;
+            return x.poly!derivative(this.coefficients[]);
         }
     }
 }
@@ -81,9 +61,9 @@ version (mir_test) @safe pure nothrow @nogc unittest
     auto a = rcarray!(const double)(3.0, 4.5, 1.9, 2);
     auto p = a.polynomial;
 
-    alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3;
-    alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2;
-    alias d2f = (x) => 2 * 1.9  + 6 * 2 * x^^1;
+    alias   f = (x) =>     3.0 +     4.5 * x^^1 +   1.9 * x^^2 + 2 * x^^3;
+    alias  df = (x) =>     4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2;
+    alias d2f = (x) => 2 * 1.9 + 6 *   2 * x^^1;
 
     assert(p(3.3).approxEqual(f(3.3)));
     assert(p(7.2).approxEqual(f(7.2)));
@@ -94,3 +74,153 @@ version (mir_test) @safe pure nothrow @nogc unittest
     assert(p.opCall!2(3.3).approxEqual(d2f(3.3)));
     assert(p.opCall!2(7.2).approxEqual(d2f(7.2)));
 }
+
+/++
+Evaluate polynomial.
+
+Coefficients assumed to be in the order a0 + a1 * x ^^ 1 + ... + aN * x ^^ N
+
+Params:
+    F = controls type of output
+    derivative = order of derivatives (default = 0)
+
+Returns:
+    Value of the polynomial, evaluated at `x`
+
+See_also:
+    $(WEB en.wikipedia.org/wiki/Polynomial, Polynomial).
++/
+template poly(uint derivative = 0)
+{
+    import std.traits: ForeachType;
+    /++
+    Params:
+        x = value to evaluate
+        coefficients = coefficients of polynomial
+    +/
+    @optmath typeof(F.init * X.init * 1f + F.init) poly(X, F)(in X x, scope const F[] coefficients...)
+    {
+        import mir.internal.utility: Iota;
+        auto ret = cast(typeof(return))0;
+        if (coefficients.length > 0)
+        {
+            ptrdiff_t i = coefficients.length - 1;
+            assert(i >= 0);
+            auto c = cast()coefficients[i];
+            static foreach (d; Iota!derivative)
+                c *= i - d;
+            ret = cast(typeof(return)) c;
+            while (--i >= cast(ptrdiff_t)derivative)
+            {
+                assert(i < coefficients.length);
+                c = cast()coefficients[i];
+                static foreach (d; Iota!derivative)
+                    c *= i - d;
+                ret *= x;
+                ret += c;
+            }
+        }
+        return ret;
+    }
+}
+
+///
+version (mir_test) @safe pure nothrow unittest
+{
+    import mir.math.common: approxEqual;
+
+    double[] x = [3.0, 4.5, 1.9, 2];
+
+    alias   f = (x) =>     3.0 +     4.5 * x^^1 +   1.9 * x^^2 + 2 * x^^3;
+    alias  df = (x) =>     4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2;
+    alias d2f = (x) => 2 * 1.9 + 6 *   2 * x^^1;
+
+    assert(poly(3.3, x).approxEqual(f(3.3)));
+    assert(poly(7.2, x).approxEqual(f(7.2)));
+
+    assert(poly!1(3.3, x).approxEqual(df(3.3)));
+    assert(poly!1(7.2, x).approxEqual(df(7.2)));
+
+    assert(poly!2(3.3, x).approxEqual(d2f(3.3)));
+    assert(poly!2(7.2, x).approxEqual(d2f(7.2)));
+}
+
+// static array test
+version (mir_test) @safe pure @nogc nothrow unittest
+{
+    import mir.math.common: approxEqual;
+
+    double[4] x = [3.0, 4.5, 1.9, 2];
+
+    alias   f = (x) =>     3.0 +     4.5 * x^^1 +   1.9 * x^^2 + 2 * x^^3;
+    alias  df = (x) =>     4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2;
+    alias d2f = (x) => 2 * 1.9 + 6 *   2 * x^^1;
+
+    assert(poly(3.3, x).approxEqual(f(3.3)));
+    assert(poly(7.2, x).approxEqual(f(7.2)));
+
+    assert(poly!1(3.3, x).approxEqual(df(3.3)));
+    assert(poly!1(7.2, x).approxEqual(df(7.2)));
+
+    assert(poly!2(3.3, x).approxEqual(d2f(3.3)));
+    assert(poly!2(7.2, x).approxEqual(d2f(7.2)));
+}
+
+// Check coefficient.length = 3
+version (mir_test) @safe pure nothrow unittest
+{
+    import mir.math.common: approxEqual;
+
+    double[] x = [3.0, 4.5, 1.9];
+
+    alias   f = (x) =>     3.0 +     4.5 * x^^1 + 1.9 * x^^2;
+    alias  df = (x) =>     4.5 + 2 * 1.9 * x^^1;
+    alias d2f = (x) => 2 * 1.9;
+
+    assert(poly(3.3, x).approxEqual(f(3.3)));
+    assert(poly(7.2, x).approxEqual(f(7.2)));
+
+    assert(poly!1(3.3, x).approxEqual(df(3.3)));
+    assert(poly!1(7.2, x).approxEqual(df(7.2)));
+
+    assert(poly!2(3.3, x).approxEqual(d2f(3.3)));
+    assert(poly!2(7.2, x).approxEqual(d2f(7.2)));
+}
+
+// Check coefficient.length = 2
+version (mir_test) @safe pure nothrow unittest
+{
+    import mir.math.common: approxEqual;
+
+    double[] x = [3.0, 4.5];
+
+    alias   f = (x) => 3.0 + 4.5 * x^^1;
+    alias  df = (x) => 4.5;
+    alias d2f = (x) => 0.0;
+
+    assert(poly(3.3, x).approxEqual(f(3.3)));
+    assert(poly(7.2, x).approxEqual(f(7.2)));
+
+    assert(poly!1(3.3, x).approxEqual(df(3.3)));
+    assert(poly!1(7.2, x).approxEqual(df(7.2)));
+
+    assert(poly!2(3.3, x).approxEqual(d2f(3.3)));
+    assert(poly!2(7.2, x).approxEqual(d2f(7.2)));
+}
+
+// Check coefficient.length = 1
+version (mir_test) @safe pure nothrow unittest
+{
+    import mir.math.common: approxEqual;
+
+    double[] x = [3.0];
+
+    alias  f = (x) => 3.0;
+    alias df = (x) => 0.0;
+
+    assert(poly(3.3, x).approxEqual(f(3.3)));
+    assert(poly(7.2, x).approxEqual(f(7.2)));
+
+    assert(poly!1(3.3, x).approxEqual(df(3.3)));
+    assert(poly!1(7.2, x).approxEqual(df(7.2)));
+}

From 98fa97f3d095a9e14a297d581ee0b6ae6598e1bd Mon Sep 17 00:00:00 2001
From: Daniel Zuncke <26112656+danielzuncke@users.noreply.github.com>
Date: Tue, 28 Feb 2023 02:12:37 +0100
Subject: [PATCH 552/573] Fix typo in documentation

---
 source/mir/serde.d | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index bed4de37..128f9e73 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -137,7 +137,7 @@ struct serdeKeyOut
 }
 
 /++
-The attribute should be used as a hind for scripting languages to register type deserializer in the type system.
+The attribute should be used as a hint for scripting languages to register type deserializer in the type system.
 
 The attribute should be applied to a type definition.
 +/

From 826afe2dada78bccd7c2e8a3d44f2f74868ccb03 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Sat, 4 Mar 2023 17:36:07 +0700
Subject: [PATCH 553/573] update for arm

---
 source/mir/algorithm/iteration.d    | 71 ++++++++++++++++++-----------
 source/mir/date.d                   |  4 +-
 source/mir/ediff.d                  | 35 +++++++-------
 source/mir/graph/package.d          |  6 +--
 source/mir/graph/tarjan.d           |  4 +-
 source/mir/interpolate/constant.d   |  6 +--
 source/mir/interpolate/generic.d    |  6 +--
 source/mir/interpolate/linear.d     |  8 ++--
 source/mir/interpolate/package.d    |  4 +-
 source/mir/interpolate/polynomial.d | 10 ++--
 source/mir/interpolate/utility.d    |  6 +--
 source/mir/ndslice/allocation.d     |  4 +-
 source/mir/ndslice/chunks.d         |  4 +-
 source/mir/ndslice/concatenation.d  | 20 ++++----
 source/mir/ndslice/dynamic.d        | 14 +++---
 source/mir/ndslice/field.d          | 28 ++++++------
 source/mir/ndslice/internal.d       | 24 +++++-----
 source/mir/ndslice/iterator.d       | 46 +++++++++----------
 source/mir/ndslice/slice.d          |  8 ++--
 source/mir/ndslice/sorting.d        | 12 ++---
 source/mir/ndslice/topology.d       | 28 ++++++------
 source/mir/numeric.d                |  2 +-
 source/mir/polynomial.d             | 28 ++++++------
 source/mir/series.d                 |  6 +--
 24 files changed, 202 insertions(+), 182 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index e0db8cb4..041093f6 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -54,7 +54,7 @@ module mir.algorithm.iteration;
 
 import mir.functional: naryFun;
 import mir.internal.utility;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.field: BitField;
 import mir.ndslice.internal;
 import mir.ndslice.iterator: FieldIterator, RetroIterator;
@@ -101,7 +101,7 @@ version(mir_test) unittest
 
 }
 
-@optmath:
+@fmamath:
 
 /+
 Bitslice representation for accelerated bitwise algorithm.
@@ -130,7 +130,7 @@ private struct BitSliceAccelerator(Field, I = typeof(Field.init[size_t.init]))
     /// tail length
     int tailLength;
 
-@optmath:
+@fmamath:
 
     this(Slice!(FieldIterator!(BitField!(Field, I))) slice)
     {
@@ -458,6 +458,7 @@ scope const:
 
     sizediff_t nBitsToCount(size_t count)
     {
+        pragma(inline, false);
         size_t ret;
         if (count == 0)
             return count;
@@ -557,6 +558,22 @@ sizediff_t nBitsToCount(Field, I)(Slice!(RetroIterator!(FieldIterator!(BitField!
     return BitSliceAccelerator!(Field, I)(bitSlice.retro).retroNBitsToCount(count);
 }
 
+/++
+Сount bits starting from the end until set bit count is reached. Works with ndslices created with $(REF bitwise, mir,ndslice,topology), $(REF bitSlice, mir,ndslice,allocation).
+Returns: bit count if set bit count is reached or `-1` otherwise.
++/
+sizediff_t retroNBitsToCount(Field, I)(Slice!(FieldIterator!(BitField!(Field, I))) bitSlice, size_t count)
+{
+    return BitSliceAccelerator!(Field, I)(bitSlice).retroNBitsToCount(count);
+}
+
+///ditto
+sizediff_t retroNBitsToCount(Field, I)(Slice!(RetroIterator!(FieldIterator!(BitField!(Field, I)))) bitSlice, size_t count)
+{
+    import mir.ndslice.topology: retro;
+    return BitSliceAccelerator!(Field, I)(bitSlice.retro).nBitsToCount(count);
+}
+
 ///
 version(mir_test)
 pure unittest
@@ -604,7 +621,7 @@ package(mir) template allFlattened(args...)
     static if (args.length)
     {
         alias arg = args[0];
-        @optmath @property allFlattenedMod()()
+        @fmamath @property allFlattenedMod()()
         {
             import mir.ndslice.topology: flattened;
             return flattened(arg);
@@ -661,11 +678,11 @@ version(Mir_disable_inlining_in_reduce)
 
     private template nonInlinedNaryFun(alias fun)
     {
-        import mir.math.common : optmath;
+        import mir.math.common : fmamath;
         static if (is(typeof(fun) : string))
         {
             /// Specialization for string lambdas
-            @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
+            @fmamath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
                 if (args.length <= 26)
             {
                 pragma(inline,false);
@@ -675,7 +692,7 @@ version(Mir_disable_inlining_in_reduce)
         }
         else static if (is(typeof(fun.opCall) == function))
         {
-            @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
+            @fmamath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
                 if (is(typeof(fun.opCall(args))))
             {
                 pragma(inline,false);
@@ -684,7 +701,7 @@ version(Mir_disable_inlining_in_reduce)
         }
         else
         {
-            @optmath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
+            @fmamath auto ref nonInlinedNaryFun(Args...)(auto ref Args args)
                 if (is(typeof(fun(args))))
             {
                 pragma(inline,false);
@@ -745,7 +762,7 @@ template reduce(alias fun)
     Returns:
         the accumulated `result`
     +/
-    @optmath auto reduce(S, Slices...)(S seed, Slices slices)
+    @fmamath auto reduce(S, Slices...)(S seed, Slices slices)
         if (Slices.length)
     {
         static if (Slices.length > 1)
@@ -768,7 +785,7 @@ template reduce(alias fun)
     }
     else version(Mir_disable_inlining_in_reduce)
     //As above, but with inlining disabled.
-    @optmath auto reduce(S, Slices...)(S seed, Slices slices)
+    @fmamath auto reduce(S, Slices...)(S seed, Slices slices)
         if (Slices.length)
     {
         static if (Slices.length > 1)
@@ -849,9 +866,9 @@ version(mir_test) unittest
     import std.numeric : dotProduct;
     import mir.ndslice.allocation : slice;
     import mir.ndslice.topology : as, iota, zip, universal;
-    import mir.math.common : optmath;
+    import mir.math.common : fmamath;
 
-    static @optmath T fmuladd(T, Z)(const T a, Z z)
+    static @fmamath T fmuladd(T, Z)(const T a, Z z)
     {
         return a + z.a * z.b;
     }
@@ -879,9 +896,9 @@ unittest
 {
     import mir.ndslice.allocation : slice;
     import mir.ndslice.topology : as, iota;
-    import mir.math.common : optmath;
+    import mir.math.common : fmamath;
 
-    static @optmath T fun(T)(const T a, ref T b)
+    static @fmamath T fun(T)(const T a, ref T b)
     {
         return a + b++;
     }
@@ -1018,7 +1035,7 @@ template eachOnBorder(alias fun)
     Params:
         slices = One or more slices.
     +/
-    @optmath void eachOnBorder(Slices...)(Slices slices)
+    @fmamath void eachOnBorder(Slices...)(Slices slices)
         if (allSatisfy!(isSlice, Slices))
     {
         import mir.ndslice.traits: isContiguousSlice;
@@ -1117,7 +1134,7 @@ template each(alias fun)
         Params:
             slices = One or more slices, ranges, and arrays.
         +/
-        @optmath auto each(Slices...)(Slices slices)
+        @fmamath auto each(Slices...)(Slices slices)
             if (Slices.length && !is(Slices[0] : Chequer))
         {
             static if (Slices.length > 1)
@@ -1141,7 +1158,7 @@ template each(alias fun)
             color = $(LREF Chequer).
             slices = One or more slices.
         +/
-        @optmath auto each(Slices...)(Chequer color, Slices slices)
+        @fmamath auto each(Slices...)(Chequer color, Slices slices)
             if (Slices.length && allSatisfy!(isSlice, Slices))
         {
             static if (Slices.length > 1)
@@ -1546,7 +1563,7 @@ template minmaxPos(alias pred = "a < b")
     Returns:
         2 subslices with minimal and maximal `first` elements.
     +/
-    @optmath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)[2]
+    @fmamath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)[2]
         minmaxPos(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
         typeof(return) pret;
@@ -1622,7 +1639,7 @@ template minmaxIndex(alias pred = "a < b")
     Returns:
         Subslice with minimal (maximal) `first` element.
     +/
-    @optmath size_t[N][2] minmaxIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+    @fmamath size_t[N][2] minmaxIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
         typeof(return) pret = size_t.max;
         if (!slice.anyEmpty)
@@ -1691,7 +1708,7 @@ template minPos(alias pred = "a < b")
         Multidimensional backward index such that element is minimal(maximal).
         Backward index equals zeros, if slice is empty.
     +/
-    @optmath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)
+    @fmamath Slice!(Iterator, N, kind == Contiguous && N > 1 ? Canonical : kind)
         minPos(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
         typeof(return) ret;
@@ -1766,7 +1783,7 @@ template minIndex(alias pred = "a < b")
         Multidimensional index such that element is minimal(maximal).
         Index elements equal to `size_t.max`, if slice is empty.
     +/
-    @optmath size_t[N] minIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+    @fmamath size_t[N] minIndex(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
         size_t[N] ret = size_t.max;
         if (!slice.anyEmpty)
@@ -1922,7 +1939,7 @@ template findIndex(alias pred)
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) findIndex(Slices...)(Slices slices)
+    @fmamath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) findIndex(Slices...)(Slices slices)
         if (Slices.length)
     {
         static if (Slices.length > 1)
@@ -2018,7 +2035,7 @@ template find(alias pred)
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) find(Slices...)(Slices slices)
+    @fmamath Select!(DimensionCount!(Slices[0]) > 1, size_t[DimensionCount!(Slices[0])], size_t) find(Slices...)(Slices slices)
         if (Slices.length && allSatisfy!(hasShape, Slices))
     {
         static if (Slices.length > 1)
@@ -2197,7 +2214,7 @@ template any(alias pred = "a")
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath bool any(Slices...)(Slices slices)
+    @fmamath bool any(Slices...)(Slices slices)
         if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
     {
         static if (Slices.length > 1)
@@ -2357,7 +2374,7 @@ template all(alias pred = "a")
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath bool all(Slices...)(Slices slices)
+    @fmamath bool all(Slices...)(Slices slices)
         if ((Slices.length == 1 || !__traits(isSame, pred, "a")) && Slices.length)
     {
         static if (Slices.length > 1)
@@ -2487,7 +2504,7 @@ template count(alias fun)
     Constraints:
         All slices must have the same shape.
     +/
-    @optmath size_t count(Slices...)(Slices slices)
+    @fmamath size_t count(Slices...)(Slices slices)
         if (Slices.length)
     {
         static if (Slices.length > 1)
@@ -4308,7 +4325,7 @@ template fold(alias fun)
     Returns:
         the accumulated result
     +/
-    @optmath auto fold(Slice, S)(scope Slice slice, S seed)
+    @fmamath auto fold(Slice, S)(scope Slice slice, S seed)
     {
         import core.lifetime: move;
         return reduce!fun(seed, slice.move);
diff --git a/source/mir/date.d b/source/mir/date.d
index 08f408d8..d58c825d 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -4209,12 +4209,12 @@ version(unittest)
     auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
 
     // I'd use a Tuple, but I get forward reference errors if I try.
-    struct MonthDay
+    static struct MonthDay
     {
         Month month;
         short day;
 
-        this(int m, short d)
+        this(int m, short d) @safe
         {
             month = cast(Month) m;
             day = d;
diff --git a/source/mir/ediff.d b/source/mir/ediff.d
index 934b3656..66813948 100644
--- a/source/mir/ediff.d
+++ b/source/mir/ediff.d
@@ -120,6 +120,7 @@ unittest
 version(mir_test)
 unittest
 {
+    import mir.test;
     import mir.math;
 
     // Test Const
@@ -162,16 +163,16 @@ unittest
     assert(f3.getFunctionValue == f2.getFunctionValue);
     assert(f3.getDerivative!(["vol"]) == f2.getDerivative!(["vol"]));
     assert(f3.getDerivative!(["strike"]) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike"]));
-    assert(f3.getDerivative!(["barrier"]) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]));
-    assert(getDerivative!(["barrier"])(f3 + barrier) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) + 1);
-    assert(f3.getDerivative!(["strike", "barrier"]) == 
+    f3.getDerivative!(["barrier"]).shouldApprox == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]);
+    getDerivative!(["barrier"])(f3 + barrier).shouldApprox == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) + 1;
+    f3.getDerivative!(["strike", "barrier"]).shouldApprox == 
         f2.getDerivative!(["strike", "strike"]) * fc.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) +
-        f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike", "barrier"]));
+        f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike", "barrier"]);
 
     /// normalDistribution
     import mir.math.func.normal: constantNormalCDF = normalCDF, normalPDF;
-    assert(barrier.powi!2.normalCDF.getFunctionValue == constantNormalCDF(13.0 ^^ 2));
-    assert(barrier.powi!2.normalCDF.getDerivative!(["barrier"]) == normalPDF(13.0 ^^ 2) * (2 * 13));
+    barrier.powi!2.normalCDF.getFunctionValue.shouldApprox == constantNormalCDF(13.0 ^^ 2);
+    barrier.powi!2.normalCDF.getDerivative!(["barrier"]).shouldApprox == normalPDF(13.0 ^^ 2) * (2 * 13);
 }
 
 import mir.algorithm.iteration: uniq;
@@ -180,7 +181,7 @@ import mir.math.common;
 import mir.ndslice.sorting: sort;
 import std.traits: Unqual, getUDAs, hasUDA, isPointer, PointerTarget;
 
-@optmath:
+@fmamath:
 
 /++
 +/
@@ -278,7 +279,7 @@ private template Sum(A, B, bool diff = false)
         A a;
         B b;
 
-    @optmath:
+    @fmamath:
 
         template getDerivative(string[] variables, bool strict = true)
         {
@@ -315,7 +316,7 @@ private template Product(A, B)
 
         template getDerivative(string[] variables, bool strict = true)
         {
-        @optmath:
+        @fmamath:
             static if (Dependencies!(typeof(this)).containsAll(variables))
                 auto getDerivative() const @property
                 {
@@ -459,7 +460,7 @@ template getDerivative(string[] variables, bool strict = true)
     import mir.ndslice.topology: pairwise;
     import mir.algorithm.iteration: all;
 
-@optmath:
+@fmamath:
 
     static if (variables.length == 0 || variables.pairwise!"a <= b".all)
     {
@@ -634,7 +635,7 @@ private template DerivativeOf(T, string variable)
         // Underlying expression
         T underlying;
 
-    @optmath:
+    @fmamath:
 
         ///
         auto ref getDerivative(string[] variables, bool strict = true)() const @property
@@ -653,7 +654,7 @@ Constructs a partial derivative of one order.
 +/
 template derivativeOf(string variable)
 {
-@optmath:
+@fmamath:
 
     /++
     Params:
@@ -723,7 +724,7 @@ private template Powi(int power, T)
 
         template getDerivative(string[] variables, bool strict = true)
         {
-        @optmath:
+        @fmamath:
             static if (Dependencies!(typeof(this)).containsAll(variables))
                 auto getDerivative() const @property
                 {
@@ -792,7 +793,7 @@ private template Exp(T)
 
         template getDerivative(string[] variables, bool strict = true)
         {
-        @optmath:
+        @fmamath:
             static if (Dependencies!(typeof(this)).containsAll(variables))
                 auto getDerivative() const @property
                 {
@@ -830,7 +831,7 @@ private template Log(T)
 
         template getDerivative(string[] variables, bool strict = true)
         {
-        @optmath:
+        @fmamath:
             static if (Dependencies!(typeof(this)).containsAll(variables))
                 auto getDerivative() const @property
                 {
@@ -868,7 +869,7 @@ private template Sqrt(T)
 
         template getDerivative(string[] variables, bool strict = true)
         {
-        @optmath:
+        @fmamath:
             static if (Dependencies!(typeof(this)).containsAll(variables))
                 auto getDerivative() const @property
                 {
@@ -907,7 +908,7 @@ private template NormalCDF(T)
 
         template getDerivative(string[] variables, bool strict = true)
         {
-        @optmath:
+        @fmamath:
             static if (Dependencies!(typeof(this)).containsAll(variables))
                 auto getDerivative() const @property
                 {
diff --git a/source/mir/graph/package.d b/source/mir/graph/package.d
index 44001f50..8a408fb7 100644
--- a/source/mir/graph/package.d
+++ b/source/mir/graph/package.d
@@ -12,7 +12,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 
 module mir.graph;
 
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 
 import mir.series;
 import mir.rc.array;
@@ -45,7 +45,7 @@ Returns:
     A graph series composed of keys (sorted `.index`) and arrays of indeces (`.data`)
 Complexity: `O(log(V) (V + E))`
 +/
-@optmath
+@fmamath
 GraphSeries!(T, I, J) graphSeries(I = uint, J = size_t, T, Range)(in Range[T] aaGraph)
 {
     import mir.array.allocation: array;
@@ -106,7 +106,7 @@ Returns:
     A graph as an arrays of indeces
 Complexity: `O(log(V) (V + E))`
 +/
-@optmath
+@fmamath
 RCGraph!(I, J) rcgraph(I = uint, J = size_t, KeyIterator, RangeIterator)(Series!(KeyIterator, RangeIterator) graph)
 {
     import mir.array.allocation: array;
diff --git a/source/mir/graph/tarjan.d b/source/mir/graph/tarjan.d
index b63b8262..f8821b31 100644
--- a/source/mir/graph/tarjan.d
+++ b/source/mir/graph/tarjan.d
@@ -16,9 +16,9 @@ module mir.graph.tarjan;
 
 import std.traits;
 
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 
-@optmath:
+@fmamath:
 
 
 /++
diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index 154c3171..c50b621f 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -13,7 +13,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 +/
 module mir.interpolate.constant;
 
-@optmath:
+@fmamath:
 
 ///
 version(mir_test)
@@ -42,7 +42,7 @@ import core.lifetime: move;
 import mir.internal.utility;
 import mir.functional;
 import mir.interpolate;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.slice;
 import mir.primitives;
 import mir.rc.array;
@@ -77,7 +77,7 @@ Multivariate constant interpolant with nodes on rectilinear grid.
 struct Constant(F, size_t N = 1, X = F)
     if (N && N <= 6)
 {
-@optmath:
+@fmamath:
 
     /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
     Slice!(RCI!(const F), N) _data;
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index 37a9deab..a56a5a3f 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -13,7 +13,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 +/
 module mir.interpolate.generic;
 
-@optmath:
+@fmamath:
 
 ///
 version(mir_test)
@@ -64,7 +64,7 @@ import core.lifetime: move;
 import mir.internal.utility;
 import mir.functional;
 import mir.interpolate;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.slice;
 import mir.primitives;
 import mir.rc.array;
@@ -98,7 +98,7 @@ Multivariate generic interpolant with nodes on rectilinear grid.
 +/
 struct Generic(X, F)
 {
-@optmath:
+@fmamath:
 
     /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.)
     Slice!(RCI!(const F)) _data;
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index 7fe1c165..f4249e7f 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -17,7 +17,7 @@ import core.lifetime: move;
 import mir.functional;
 import mir.internal.utility;
 import mir.interpolate;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.slice;
 import mir.primitives;
 import mir.rc.array;
@@ -36,7 +36,7 @@ version(D_Exceptions)
     static immutable exc_eq = new Exception(msg_eq);
 }
 
-@optmath:
+@fmamath:
 
 /++
 Constructs multivariate linear interpolant with nodes on rectilinear grid.
@@ -197,7 +197,7 @@ struct Linear(F, size_t N = 1, X = F)
     /// Grid iterators. $(RED For internal use.)
     Repeat!(N, RCI!(immutable X)) _grid;
 
-@optmath extern(D):
+@fmamath extern(D):
 
     bool opEquals()(auto ref scope const typeof(this) rhs) scope const @trusted pure nothrow @nogc
     {
@@ -362,7 +362,7 @@ struct LinearKernel(X)
     X w0 = 0;
     X w1 = 0;
 
-@optmath:
+@fmamath:
 
     ///
     auto lightConst()() const @property
diff --git a/source/mir/interpolate/package.d b/source/mir/interpolate/package.d
index 57475475..0311a606 100644
--- a/source/mir/interpolate/package.d
+++ b/source/mir/interpolate/package.d
@@ -21,13 +21,13 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 module mir.interpolate;
 
 import mir.functional: Tuple;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.slice: Slice, Contiguous;
 import mir.primitives;
 import mir.qualifier;
 import std.traits: isInstanceOf;
 
-@optmath:
+@fmamath:
 
 package ref iter(alias s)() { return s._iterator; };
 package alias GridVector(It) = Slice!It;
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index 3cd0fd4b..72d33eb8 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -28,12 +28,14 @@ import mir.rc.array;
 import mir.utility: min, swap;
 import std.traits: Unqual;
 
-@optmath:
+@fmamath:
 
 ///
 version(mir_test)
-@safe pure unittest
+// @safe pure
+unittest
 {
+    import mir.test;
     import mir.algorithm.iteration: all;
     import mir.math;
     import mir.ndslice;
@@ -61,7 +63,7 @@ version(mir_test)
         auto l_ldx = l.withDerivative(testX);
         auto l_ld_ld2x = l.withTwoDerivatives(testX);
 
-        assert(lx.approxEqual(f(testX)));
+        lx.shouldApprox == f(testX);
         assert(l_ldx[].all!approxEqual(fWithDerivative(testX)[]));
         assert(l_ld_ld2x[].all!approxEqual(fWithTwoDerivatives(testX)[]));
     }
@@ -100,7 +102,7 @@ struct Lagrange(T, uint maxAdditionalFunctions = 0, X = T)
     /// $(RED for internal use only.)
     T[maxAdditionalFunctions + 1] _asums;
 
-@optmath @safe pure @nogc extern(D):
+@fmamath @safe pure @nogc extern(D):
 
     ///
     enum uint derivativeOrder = maxAdditionalFunctions;
diff --git a/source/mir/interpolate/utility.d b/source/mir/interpolate/utility.d
index 036dbaf7..b520ce29 100644
--- a/source/mir/interpolate/utility.d
+++ b/source/mir/interpolate/utility.d
@@ -2,7 +2,7 @@ module mir.interpolate.utility;
 
 import mir.ndslice.slice;
 import std.traits;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 
 package template DeepType(T)
 {
@@ -24,7 +24,7 @@ struct ParabolaKernel(T)
     ///
     T c = 0;
 
-@optmath:
+@fmamath:
 
     ///
     this(T a, T b, T c)
@@ -160,7 +160,7 @@ struct CubicKernel(T)
     ///
     T d = 0;
 
-@optmath:
+@fmamath:
 
     ///
     this(T a, T b, T c, T d)
diff --git a/source/mir/ndslice/allocation.d b/source/mir/ndslice/allocation.d
index f791f979..b5253fd0 100644
--- a/source/mir/ndslice/allocation.d
+++ b/source/mir/ndslice/allocation.d
@@ -53,7 +53,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 +/
 module mir.ndslice.allocation;
 
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.concatenation;
 import mir.ndslice.field: BitField;
 import mir.ndslice.internal;
@@ -63,7 +63,7 @@ import mir.rc.array;
 import std.traits;
 import std.meta: staticMap;
 
-@optmath:
+@fmamath:
 
 /++
 Allocates an n-dimensional reference-counted (thread-safe) slice.
diff --git a/source/mir/ndslice/chunks.d b/source/mir/ndslice/chunks.d
index e5ff246e..09e865e4 100644
--- a/source/mir/ndslice/chunks.d
+++ b/source/mir/ndslice/chunks.d
@@ -17,7 +17,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 module mir.ndslice.chunks;
 
 import mir.internal.utility;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.internal;
 import mir.ndslice.iterator: IotaIterator;
 import mir.ndslice.slice;
@@ -229,7 +229,7 @@ version(mir_ndslice_test) unittest
 +/
 struct Chunks(size_t[] dimensions, Iterator, size_t N = 1, SliceKind kind = Contiguous)
 {
-@optmath:
+@fmamath:
 
     /++
     Chunk shape.
diff --git a/source/mir/ndslice/concatenation.d b/source/mir/ndslice/concatenation.d
index 4edb7249..396e8200 100644
--- a/source/mir/ndslice/concatenation.d
+++ b/source/mir/ndslice/concatenation.d
@@ -36,16 +36,16 @@ import std.traits;
 import std.meta;
 
 import mir.internal.utility;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.internal;
 import mir.ndslice.slice;
 import mir.primitives;
 
-@optmath:
+@fmamath:
 
 private template _expose(size_t maxN, size_t dim)
 {
-    static @optmath auto _expose(S)(S s)
+    static @fmamath auto _expose(S)(S s)
     {
         static if (s.N == maxN)
         {
@@ -239,7 +239,7 @@ enum size_t concatenationDimension(T : Concatenation!(dim, Slices), size_t dim,
 struct Concatenation(size_t dim, Slices...)
     if (Slices.length > 1)
 {
-    @optmath:
+    @fmamath:
 
 
     /// Slices and sub-concatenations
@@ -488,7 +488,7 @@ See_also: $(LREF ._concatenation) examples.
 template pad(size_t[] dimensions, string[] directions)
     if (dimensions.length && dimensions.length == directions.length)
 {
-    @optmath:
+    @fmamath:
 
     /++
     Params:
@@ -634,7 +634,7 @@ See_also: $(LREF ._concatenation) examples.
 template padWrap(size_t[] dimensions, string[] directions)
     if (dimensions.length && dimensions.length == directions.length)
 {
-    @optmath:
+    @fmamath:
 
     /++
     Params:
@@ -802,7 +802,7 @@ See_also: $(LREF ._concatenation) examples.
 template padSymmetric(size_t[] dimensions, string[] directions)
     if (dimensions.length && dimensions.length == directions.length)
 {
-    @optmath:
+    @fmamath:
 
     /++
     Params:
@@ -988,7 +988,7 @@ See_also: $(LREF ._concatenation) examples.
 template padEdge(size_t[] dimensions, string[] directions)
     if (dimensions.length && dimensions.length == directions.length)
 {
-    @optmath:
+    @fmamath:
 
     /++
     Params:
@@ -1109,7 +1109,7 @@ See_also: $(LREF ._concatenation) examples.
 +/
 template forEachFragment(alias pred)
 {
-    @optmath:
+    @fmamath:
 
     import mir.functional: naryFun;
     static if (__traits(isSame, naryFun!pred, pred))
@@ -1179,7 +1179,7 @@ See_also: $(LREF ._concatenation) examples.
 +/
 template until(alias pred)
 {
-    @optmath:
+    @fmamath:
 
     import mir.functional: naryFun;
     static if (__traits(isSame, naryFun!pred, pred))
diff --git a/source/mir/ndslice/dynamic.d b/source/mir/ndslice/dynamic.d
index bab8a638..c7f6e20c 100644
--- a/source/mir/ndslice/dynamic.d
+++ b/source/mir/ndslice/dynamic.d
@@ -81,13 +81,13 @@ module mir.ndslice.dynamic;
 import std.traits;
 import std.meta;
 
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.internal.utility: Iota;
 import mir.ndslice.internal;
 import mir.ndslice.slice;
 import mir.utility;
 
-@optmath:
+@fmamath:
 
 /++
 Reverses iteration order for dimensions with negative strides, they become not negative;
@@ -180,7 +180,7 @@ See_also: $(LREF everted), $(LREF transposed)
 template swapped(size_t dimensionA, size_t dimensionB)
 {
     ///
-    @optmath auto swapped(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice)
+    @fmamath auto swapped(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice)
     {
         static if (kind == Universal || kind == Canonical && dimensionA + 1 < N && dimensionB + 1 < N)
         {
@@ -312,7 +312,7 @@ Returns:
 template rotated(size_t dimensionA, size_t dimensionB)
 {
     ///
-    @optmath auto rotated(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice, sizediff_t k = 1)
+    @fmamath auto rotated(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice, sizediff_t k = 1)
     {
         static if (kind == Universal || kind == Canonical && dimensionA + 1 < N && dimensionB + 1 < N)
         {
@@ -491,7 +491,7 @@ template transposed(Dimensions...)
 {
     static if (allSatisfy!(isSize_t, Dimensions))
     ///
-    @optmath auto transposed(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice)
+    @fmamath auto transposed(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice)
     {
         import mir.algorithm.iteration: any;
         enum s = N;
@@ -664,7 +664,7 @@ template reversed(Dimensions...)
 {
     static if (allSatisfy!(isSize_t, Dimensions))
     ///
-    @optmath auto reversed(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice) @trusted
+    @fmamath auto reversed(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice) @trusted
     {
         import mir.algorithm.iteration: any;
         enum s = N;
@@ -837,7 +837,7 @@ template strided(Dimensions...)
         _slice = input slice
         factors = list of step extension factors
     +/
-    @optmath auto strided(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice, Repeat!(Dimensions.length, ptrdiff_t) factors)
+    @fmamath auto strided(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) _slice, Repeat!(Dimensions.length, ptrdiff_t) factors)
     {
         import mir.algorithm.iteration: any;
         enum s = N;
diff --git a/source/mir/ndslice/field.d b/source/mir/ndslice/field.d
index fd61c374..eb540e4f 100644
--- a/source/mir/ndslice/field.d
+++ b/source/mir/ndslice/field.d
@@ -32,11 +32,11 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 module mir.ndslice.field;
 
 import mir.internal.utility: Iota;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.internal;
 import mir.qualifier;
 
-@optmath:
+@fmamath:
 
 package template ZeroShiftField(T)
 {
@@ -73,7 +73,7 @@ auto MapField__map(Field, alias fun, alias fun1)(ref MapField!(Field, fun) f)
 +/
 struct MapField(Field, alias _fun)
 {
-@optmath:
+@fmamath:
     ///
     Field _field;
 
@@ -137,7 +137,7 @@ struct MapField(Field, alias _fun)
 +/
 struct VmapField(Field, Fun)
 {
-@optmath:
+@fmamath:
     ///
     Field _field;
     ///
@@ -238,7 +238,7 @@ Iterates multiple fields in lockstep.
 struct ZipField(Fields...)
     if (Fields.length > 1)
 {
-@optmath:
+@fmamath:
     import mir.functional: Tuple, Ref, _ref;
     import std.meta: anySatisfy;
 
@@ -297,7 +297,7 @@ struct RepeatField(T)
 {
     import std.traits: Unqual;
 
-@optmath:
+@fmamath:
     alias UT = Unqual!T;
 
     ///
@@ -325,7 +325,7 @@ struct RepeatField(T)
 struct BitField(Field, I = typeof(cast()Field.init[size_t.init]))
     if (__traits(isUnsigned, I))
 {
-@optmath:
+@fmamath:
     import mir.bitop: ctlz;
     package(mir) alias E = I;
     package(mir) enum shift = ctlz(I.sizeof) + 3;
@@ -425,7 +425,7 @@ struct BitpackField(Field, uint pack, I = typeof(cast()Field.init[size_t.init]))
     if (__traits(isUnsigned, I))
 {
     //static assert();
-@optmath:
+@fmamath:
     package(mir) alias E = I;
     package(mir) enum mask = (I(1) << pack) - 1;
     package(mir) enum bits = I.sizeof * 8;
@@ -539,7 +539,7 @@ struct OrthogonalReduceField(FieldsIterator, alias fun, T)
 {
     import mir.ndslice.slice: Slice;
 
-@optmath:
+@fmamath:
     /// non empty slice
 
     Slice!FieldsIterator _fields;
@@ -582,7 +582,7 @@ struct CycleField(Field)
 {
     import mir.ndslice.slice: Slice;
 
-@optmath:
+@fmamath:
     /// Cycle length
     size_t _length;
     ///
@@ -630,7 +630,7 @@ struct CycleField(Field, size_t length)
 {
     import mir.ndslice.slice: Slice;
 
-@optmath:
+@fmamath:
     /// Cycle length
     enum _length = length;
     ///
@@ -679,7 +679,7 @@ struct CycleField(Field, size_t length)
 struct ndIotaField(size_t N)
     if (N)
 {
-@optmath:
+@fmamath:
     ///
     size_t[N - 1] _lengths;
 
@@ -746,7 +746,7 @@ struct LinspaceField(T)
         return a + b;
     }
 
-@optmath:
+@fmamath:
 
     ///
     size_t length(size_t dimension = 0)() scope const @property
@@ -767,7 +767,7 @@ Magic square field.
 +/
 struct MagicField
 {
-@optmath:
+@fmamath:
 @safe pure nothrow @nogc:
 
     /++
diff --git a/source/mir/ndslice/internal.d b/source/mir/ndslice/internal.d
index c630fb0b..fb191c72 100644
--- a/source/mir/ndslice/internal.d
+++ b/source/mir/ndslice/internal.d
@@ -1,14 +1,14 @@
 module mir.ndslice.internal;
 
 import mir.internal.utility : isFloatingPoint, Iota;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.iterator: IotaIterator;
 import mir.ndslice.slice;
 import mir.primitives;
 import std.meta;
 import std.traits;
 
-@optmath:
+@fmamath:
 
 template ConstIfPointer(T)
 {
@@ -348,7 +348,7 @@ package(mir) template strideOf(args...)
         enum strideOf = args;
     else
     {
-        @optmath @property auto ref ls()()
+        @fmamath @property auto ref ls()()
         {
             import mir.ndslice.topology: stride;
             return stride(args[0]);
@@ -377,7 +377,7 @@ package(mir) template frontOf2(args...)
         enum frontOf2 = args;
     else
     {
-        @optmath @property auto frontOf2Mod()()
+        @fmamath @property auto frontOf2Mod()()
         {
             return args[0].front;
         }
@@ -391,7 +391,7 @@ package(mir) template backOf(args...)
         enum backOf = args;
     else
     {
-        @optmath @property auto ref backOfMod()()
+        @fmamath @property auto ref backOfMod()()
         {
             return args[0].back;
         }
@@ -405,7 +405,7 @@ package(mir) template frontOfD(size_t dimension, args...)
         enum frontOfD = args;
     else
     {
-        @optmath @property auto ref frontOfDMod()()
+        @fmamath @property auto ref frontOfDMod()()
         {
             return args[0].front!dimension;
         }
@@ -419,7 +419,7 @@ package(mir) template backOfD(size_t dimension, args...)
         enum backOfD = args;
     else
     {
-        @optmath @property auto ref backOfDMod()()
+        @fmamath @property auto ref backOfDMod()()
         {
             return args[0].back!dimension;
         }
@@ -434,7 +434,7 @@ package(mir) template frontOfDim(size_t dim, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref frontOfDimMod()
+        @fmamath @property auto ref frontOfDimMod()
         {
             return arg.front!dim;
         }
@@ -449,7 +449,7 @@ package(mir) template selectFrontOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref selectFrontOfMod()()
+        @fmamath @property auto ref selectFrontOfMod()()
         {
             return arg.lightScope.selectFront!0(input);
         }
@@ -464,7 +464,7 @@ package(mir) template selectBackOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref selectBackOfMod()()
+        @fmamath @property auto ref selectBackOfMod()()
         {
             return arg.selectBack!0(input);
         }
@@ -479,7 +479,7 @@ package(mir) template frontSelectFrontOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref frontSelectFrontOfMod()()
+        @fmamath @property auto ref frontSelectFrontOfMod()()
         {
             return arg.lightScope.front.selectFront!0(input);
         }
@@ -494,7 +494,7 @@ package(mir) template frontSelectBackOf(alias input, args...)
     else
     {
         alias arg = args[0];
-        @optmath @property auto ref frontSelectBackOfMod
+        @fmamath @property auto ref frontSelectBackOfMod
         ()()
         {
             return arg.lightScope.front.selectBack!0(input);
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 8698b8d9..4ea37a57 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -37,7 +37,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
 module mir.ndslice.iterator;
 
 import mir.internal.utility: Iota;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.field;
 import mir.ndslice.internal;
 import mir.ndslice.slice: SliceKind, Slice, Universal, Canonical, Contiguous, isSlice;
@@ -49,7 +49,7 @@ private static immutable assumeZeroShiftExceptionMsg = "*.assumeFieldsHaveZeroSh
 version(D_Exceptions)
     private static immutable assumeZeroShiftException = new Exception(assumeZeroShiftExceptionMsg);
 
-@optmath:
+@fmamath:
 
 enum std_ops = q{
     void opUnary(string op)() scope
@@ -91,7 +91,7 @@ Step counter.
 struct IotaIterator(I)
     if (isIntegral!I || isPointer!I)
 {
-@optmath:
+@fmamath:
 
     ///
     I _index;
@@ -225,7 +225,7 @@ Reverse directions for an iterator.
 +/
 struct RetroIterator(Iterator)
 {
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
 
@@ -342,7 +342,7 @@ Iterates an iterator with a fixed strides.
 +/
 struct StrideIterator(Iterator)
 {
-@optmath:
+@fmamath:
     ///
     ptrdiff_t _stride;
     ///
@@ -450,7 +450,7 @@ Iterates an iterator with a fixed strides.
 +/
 struct StrideIterator(Iterator, ptrdiff_t factor)
 {
-@optmath:
+@fmamath:
     ///
     enum _stride = factor;
 
@@ -596,7 +596,7 @@ Iterates multiple iterators in lockstep.
 struct ZipIterator(Iterators...)
     if (Iterators.length > 1)
 {
-@optmath:
+@fmamath:
     import std.traits: ConstOf, ImmutableOf;
     import std.meta: staticMap;
     import mir.functional: Tuple, Ref, _ref;
@@ -746,7 +746,7 @@ struct CachedIterator(Iterator, CacheIterator, FlagIterator)
     ///
     FlagIterator _flags;
 
-@optmath:
+@fmamath:
 
     ///
     auto lightScope()() return scope @property
@@ -926,7 +926,7 @@ private enum map_primitives = q{
 +/
 struct VmapIterator(Iterator, Fun)
 {
-@optmath:
+@fmamath:
 
     ///
     Iterator _iterator;
@@ -1025,7 +1025,7 @@ auto MapIterator__map(Iterator, alias fun0, alias fun)(ref MapIterator!(Iterator
 +/
 struct MapIterator(Iterator, alias _fun)
 {
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
 
@@ -1181,7 +1181,7 @@ auto _vmapIterator(Iterator, Fun)(Iterator iterator, Fun fun)
 struct NeighboursIterator(Iterator, size_t N, alias _fun, bool around)
 {
     import std.meta: AliasSeq;
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
     static if (N)
@@ -1291,7 +1291,7 @@ struct NeighboursIterator(Iterator, size_t N, alias _fun, bool around)
 +/
 struct MemberIterator(Iterator, string member)
 {
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
 
@@ -1344,7 +1344,7 @@ struct MemberIterator(Iterator, string member)
 struct BytegroupIterator(Iterator, size_t count, DestinationType)
     if (count)
 {
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
 
@@ -1448,7 +1448,7 @@ auto SlideIterator__map(Iterator, size_t params, alias fun0, alias fun)(SlideIte
 struct SlideIterator(Iterator, size_t params, alias fun)
     if (params > 1)
 {
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
 
@@ -1518,7 +1518,7 @@ struct IndexIterator(Iterator, Field)
 {
     import mir.functional: Tuple, autoExpandAndForward;
 
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
     ///
@@ -1611,7 +1611,7 @@ auto elem  = sliceable[index[0] .. index[1]];
 +/
 struct SubSliceIterator(Iterator, Sliceable)
 {
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
     ///
@@ -1654,7 +1654,7 @@ auto elem  = _sliceable[_iterator[index] .. _iterator[index + 1]];
 +/
 struct ChopIterator(Iterator, Sliceable)
 {
-@optmath:
+@fmamath:
     ///
     Iterator _iterator;
     ///
@@ -1693,7 +1693,7 @@ as a multidimensional window at the current position.
 +/
 struct SliceIterator(Iterator, size_t N = 1, SliceKind kind = Contiguous)
 {
-@optmath:
+@fmamath:
     ///
     alias Element = Slice!(Iterator, N, kind);
     ///
@@ -1749,7 +1749,7 @@ Creates an iterator on top of a field.
 +/
 struct FieldIterator(Field)
 {
-@optmath:
+@fmamath:
     ///
     ptrdiff_t _index;
     ///
@@ -1871,7 +1871,7 @@ Creates an iterator on top of all elements in a slice.
 struct FlattenedIterator(Iterator, size_t N, SliceKind kind)
     if (N > 1 && (kind == Universal || kind == Canonical))
 {
-@optmath:
+@fmamath:
     ///
     ptrdiff_t[N] _indices;
     ///
@@ -2116,7 +2116,7 @@ struct StairsIterator(Iterator, string direction)
         return StairsIterator!(LightImmutableOf!Iterator, direction)(_length, .lightImmutable(_iterator));
     }
 
-@optmath:
+@fmamath:
 
     ///
     Slice!Iterator opUnary(string op : "*")()
@@ -2263,7 +2263,7 @@ Element type of $(LREF TripletIterator).
 +/
 struct Triplet(Iterator, SliceKind kind = Contiguous)
 {
-@optmath:
+@fmamath:
     ///
     size_t _iterator;
     ///
@@ -2313,7 +2313,7 @@ Iterates triplets position in a slice.
 +/
 struct TripletIterator(Iterator, SliceKind kind = Contiguous)
 {
-@optmath:
+@fmamath:
 
     ///
     size_t _iterator;
diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index d9e83254..5d843eee 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -33,7 +33,7 @@ STD = $(TD $(SMALL $0))
 module mir.ndslice.slice;
 
 import mir.internal.utility : Iota;
-import mir.math.common : optmath;
+import mir.math.common : fmamath;
 import mir.ndslice.concatenation;
 import mir.ndslice.field;
 import mir.ndslice.internal;
@@ -139,7 +139,7 @@ template toSlices(args...)
         static if (isMutable!Arg && isSlice!Arg)
             alias slc = arg;
         else
-        @optmath @property auto ref slc()()
+        @fmamath @property auto ref slc()()
         {
             return toSlice(arg);
         }
@@ -476,7 +476,7 @@ package(mir) template allLightScope(args...)
         static if(!isDynamicArray!Arg)
         {
             static if(!is(LightScopeOf!Arg == Arg))
-            @optmath @property auto allLightScopeMod()()
+            @fmamath @property auto allLightScopeMod()()
             {
                 import mir.qualifier: lightScope;
                 return args[0].lightScope;
@@ -669,7 +669,7 @@ Slice!(Iterator, N, Contiguous)
 struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_...)
     if (0 < N_ && N_ < 255 && !(kind_ == Canonical && N_ == 1) && Labels_.length <= N_ && isIterator!Iterator_)
 {
-@optmath:
+@fmamath:
 
     /// $(LREF SliceKind)
     enum SliceKind kind = kind_;
diff --git a/source/mir/ndslice/sorting.d b/source/mir/ndslice/sorting.d
index e29b2a13..5ee53f24 100644
--- a/source/mir/ndslice/sorting.d
+++ b/source/mir/ndslice/sorting.d
@@ -83,9 +83,9 @@ version(mir_ndslice_test) unittest
 }
 
 import mir.ndslice.slice;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 
-@optmath:
+@fmamath:
 
 @safe pure version(mir_ndslice_test) unittest
 {
@@ -128,7 +128,7 @@ template sort(alias less = "a < b")
     import mir.series: Series;
     static if (__traits(isSame, naryFun!less, less))
     {
-@optmath:
+@fmamath:
         /++
         Sort n-dimensional slice.
         +/
@@ -514,7 +514,7 @@ template assumeSortedContains(alias test = "a < b")
     import mir.functional: naryFun;
     static if (__traits(isSame, naryFun!test, test))
     {
-@optmath:
+@fmamath:
         /++
         Params:
             slice = sorted one-dimensional slice or array.
@@ -555,7 +555,7 @@ template assumeSortedEqualIndex(alias test = "a < b")
     import mir.functional: naryFun;
     static if (__traits(isSame, naryFun!test, test))
     {
-@optmath:
+@fmamath:
         /++
         Params:
             slice = sorted one-dimensional slice or array.
@@ -606,7 +606,7 @@ template transitionIndex(alias test = "a < b")
     import mir.functional: naryFun;
     static if (__traits(isSame, naryFun!test, test))
     {
-@optmath:
+@fmamath:
         /++
         Params:
             slice = sorted one-dimensional slice or array.
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 91c94729..4b54781c 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -114,7 +114,7 @@ T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
 module mir.ndslice.topology;
 
 import mir.internal.utility;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.ndslice.field;
 import mir.ndslice.internal;
 import mir.ndslice.iterator;
@@ -128,7 +128,7 @@ import std.meta: AliasSeq, allSatisfy, staticMap, templateOr, Repeat;
 private immutable choppedExceptionMsg = "bounds passed to chopped are out of sliceable bounds.";
 version (D_Exceptions) private immutable choppedException = new Exception(choppedExceptionMsg);
 
-@optmath:
+@fmamath:
 
 /++
 Converts a slice to user provided kind.
@@ -2744,7 +2744,7 @@ template map(fun...)
         static if (__traits(isSame, naryFun!(fun[0]), fun[0]))
         {
             alias f = fun[0];
-        @optmath:
+        @fmamath:
             /++
             Params:
                 slice = An ndslice, array, or an input range.
@@ -3045,7 +3045,7 @@ See_Also:
     $(LREF pairwise), $(LREF subSlices), $(LREF slide), $(LREF zip),
     $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
 +/
-@optmath auto vmap(Iterator, size_t N, SliceKind kind, Callable)
+@fmamath auto vmap(Iterator, size_t N, SliceKind kind, Callable)
     (
         Slice!(Iterator, N, kind) slice,
         Callable callable,
@@ -3317,7 +3317,7 @@ template rcmap(fun...)
         static if (__traits(isSame, naryFun!(fun[0]), fun[0]))
         {
             alias f = fun[0];
-        @optmath:
+        @fmamath:
             /++
             Params:
                 slice = An ndslice, array, or an input range.
@@ -3663,7 +3663,7 @@ See_also: $(LREF map), $(LREF vmap)
 template as(T)
 {
     ///
-    @optmath auto as(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+    @fmamath auto as(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
     {
         static if (is(slice.DeepElement == T))
             return slice;
@@ -3932,7 +3932,7 @@ template zip(bool sameStrides = false)
         n-dimensional slice of elements tuple
     See_also: $(SUBREF slice, Slice.strides).
     +/
-    @optmath
+    @fmamath
     auto zip(Slices...)(Slices slices)
         if (Slices.length > 1 && allSatisfy!(isConvertibleToSlice, Slices))
     {
@@ -4082,7 +4082,7 @@ template slideAlong(size_t params, alias fun, SDimensions...)
 
     static if (allSatisfy!(isSizediff_t, SDimensions) && params > 1 && __traits(isSame, naryFun!fun, fun))
     {
-    @optmath:
+    @fmamath:
         /++
         Params: slice = ndslice or array
         Returns: lazy convolution result
@@ -4184,7 +4184,7 @@ template slide(size_t params, alias fun)
 
     static if (params > 1 && __traits(isSame, naryFun!fun, fun))
     {
-    @optmath:
+    @fmamath:
         /++
         Params: slice = ndslice or array
         Returns: lazy convolution result
@@ -4378,7 +4378,7 @@ template withNeighboursSum(alias fun = "a + b")
 
     static if (__traits(isSame, naryFun!fun, fun))
     {
-    @optmath:
+    @fmamath:
         /++
         Params:
             slice = ndslice or array
@@ -4559,7 +4559,7 @@ template kronecker(alias fun = product)
     Returns:
         $(SUBREF ndfield, Kronecker)`!(fun, NdFields)(fields).`$(SUBREF slice, slicedNdField)
     +/
-    @optmath auto kronecker(NdFields...)(NdFields fields)
+    @fmamath auto kronecker(NdFields...)(NdFields fields)
         if (allSatisfy!(hasShape, NdFields) || allSatisfy!(hasLength, NdFields))
     {
         return Kronecker!(fun, NdFields)(fields).slicedNdField;
@@ -4869,7 +4869,7 @@ template alongDim(SDimensions...)
         Returns:
             `(n-d)`-dimensional slice composed of d-dimensional slices
         +/
-        @optmath auto alongDim(Iterator, size_t N, SliceKind kind)
+        @fmamath auto alongDim(Iterator, size_t N, SliceKind kind)
             (Slice!(Iterator, N, kind) slice)
             if (N > SDimensions.length)
         {
@@ -5156,7 +5156,7 @@ template byDim(SDimensions...)
         Returns:
             d-dimensional slice composed of `(n-d)`-dimensional slices
         +/
-        @optmath auto byDim(Iterator, size_t N, SliceKind kind)
+        @fmamath auto byDim(Iterator, size_t N, SliceKind kind)
             (Slice!(Iterator, N, kind) slice)
             if (N >= SDimensions.length)
         {
@@ -5885,7 +5885,7 @@ template orthogonalReduceField(alias fun)
     import mir.functional: naryFun;
     static if (__traits(isSame, naryFun!fun, fun))
     {
-    @optmath:
+    @fmamath:
         /++
         Params:
             slice = Non empty input slice composed of fields or iterators.
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 10f1d9d4..1edf090b 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -1864,7 +1864,7 @@ T integrate(alias f, T)(const T a, const T b, const T tolerance = T.epsilon)
 }
 
 /// ditto
-@optmath
+@fmamath
 T integrateImpl(T)(
     scope const T delegate(T) @safe pure nothrow @nogc f,
     const T a,
diff --git a/source/mir/polynomial.d b/source/mir/polynomial.d
index ca029c24..db2eca86 100644
--- a/source/mir/polynomial.d
+++ b/source/mir/polynomial.d
@@ -6,10 +6,10 @@ Authors: Ilia Ki
 +/
 module mir.polynomial;
 
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 import mir.rc.array;
 
-@optmath:
+@fmamath:
 
 /++
 Polynomial callable ref-counted structure.
@@ -39,7 +39,7 @@ struct Polynomial(F)
         Params:
             x = `x` point
         +/
-        @optmath typeof(F.init * X.init * 1f + F.init) opCall(X)(in X x) const
+        @fmamath typeof(F.init * X.init * 1f + F.init) opCall(X)(in X x) const
         {
             return x.poly!derivative(this.coefficients[]);
         }
@@ -56,23 +56,23 @@ Polynomial!F polynomial(F)(RCArray!(const F) coefficients)
 ///
 version (mir_test) @safe pure nothrow @nogc unittest
 {
-    import mir.math.common: approxEqual;
+    import mir.test;
     import mir.rc.array;
     auto a = rcarray!(const double)(3.0, 4.5, 1.9, 2);
     auto p = a.polynomial;
 
-    alias   f = (x) =>     3.0 +     4.5 * x^^1 +   1.9 * x^^2 + 2 * x^^3;
-    alias  df = (x) =>     4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2;
-    alias d2f = (x) => 2 * 1.9 + 6 *   2 * x^^1;
+    alias f = (x) => 3.0 + 4.5 * x^^1 + 1.9 * x^^2 + 2 * x^^3;
+    alias df = (x) => 4.5 + 2 * 1.9 * x^^1 + 3 * 2 * x^^2;
+    alias d2f = (x) => 2 * 1.9  + 6 * 2 * x^^1;
 
-    assert(p(3.3).approxEqual(f(3.3)));
-    assert(p(7.2).approxEqual(f(7.2)));
+    p(3.3).shouldApprox == f(3.3);
+    p(7.2).shouldApprox == f(7.2);
 
-    assert(p.opCall!1(3.3).approxEqual(df(3.3)));
-    assert(p.opCall!1(7.2).approxEqual(df(7.2)));
+    p.opCall!1(3.3).shouldApprox == df(3.3);
+    p.opCall!1(7.2).shouldApprox == df(7.2);
 
-    assert(p.opCall!2(3.3).approxEqual(d2f(3.3)));
-    assert(p.opCall!2(7.2).approxEqual(d2f(7.2)));
+    p.opCall!2(3.3).shouldApprox == d2f(3.3);
+    p.opCall!2(7.2).shouldApprox == d2f(7.2);
 }
 
 /++
@@ -98,7 +98,7 @@ template poly(uint derivative = 0)
         x = value to evaluate
         coefficients = coefficients of polynomial
     +/
-    @optmath typeof(F.init * X.init * 1f + F.init) poly(X, F)(in X x, scope const F[] coefficients...)
+    @fmamath typeof(F.init * X.init * 1f + F.init) poly(X, F)(in X x, scope const F[] coefficients...)
     {
         import mir.internal.utility: Iota;
         auto ret = cast(typeof(return))0;
diff --git a/source/mir/series.d b/source/mir/series.d
index e57e612f..7f1084d8 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -193,11 +193,11 @@ unittest{
 
 import mir.ndslice.slice;
 import mir.ndslice.internal: is_Slice, isIndex;
-import mir.math.common: optmath;
+import mir.math.common: fmamath;
 
 import std.meta;
 
-@optmath:
+@fmamath:
 
 /++
 Plain index/time observation data structure.
@@ -349,7 +349,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
             sort(mutableOf);
     }
 
-@optmath:
+@fmamath:
 
     ///
     this()(Slice!IndexIterator index, Slice!(Iterator, N, kind) data)

From 5a28f57a489732f1f5117b8eb7c450813db9518b Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 9 Mar 2023 13:12:06 -0500
Subject: [PATCH 554/573] Update mir.core dependency (#456)

* Update mir.core dependency

* Add UT for filter and each
---
 dub.sdl                          |  2 +-
 source/mir/algorithm/iteration.d | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index 4758f61a..af3103bb 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilia Ki" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And
 copyright "2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.3.5"
+dependency "mir-core" version=">=1.5.2"
 
 // dflags "-version=MirNoSIMD"
 // dflags "-mtriple=aarch64-linux-gnu"
diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 041093f6..d75d0d1f 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -4206,6 +4206,21 @@ unittest {
     assert(z2.equal!equal(result2));
 }
 
+// Ensure filter works with each
+version(mir_test)
+@safe pure nothrow
+unittest {
+    import mir.ndslice.slice: sliced;
+
+    int[] result = [3, 4];
+
+    int[] x = [1, 2, 3];
+    auto y = x.filter!"a >= 2";
+    y.each!"a++";
+
+    assert(y.equal(result));
+}
+
 /++
 Implements the higher order filter and map function. The predicate and map functions are passed to
 `mir.functional.naryFun`, and can either accept a string, or any callable

From 4939ed30eb97987e3ff5e6c61a2dd743038a4c50 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Fri, 10 Mar 2023 14:32:13 +0700
Subject: [PATCH 555/573] Convert Date to gregorian calendar

---
 source/mir/date.d   | 95 ++++++++++++++++++++++++++-------------------
 source/mir/series.d | 21 +++++-----
 2 files changed, 65 insertions(+), 51 deletions(-)

diff --git a/source/mir/date.d b/source/mir/date.d
index d58c825d..55a093d8 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -1914,10 +1914,11 @@ struct YearQuarter
 
     Year 0 is a leap year.
  +/
-extern(C++, "boost", "gregorian")
-extern(C++, class)
+// extern(C++, "boost", "gregorian")
+// extern(C++, class)
+extern(C++, "mir")
 @serdeProxy!YearMonthDay
-struct date
+struct Date
 {
 extern(D):
 public:
@@ -1927,7 +1928,7 @@ public:
     ///
     uint toHash() @safe pure nothrow @nogc const scope
     {
-        return _julianDay;
+        return _dayNumber;
     }
 
     /++
@@ -1957,7 +1958,7 @@ public:
         {
             if (_year == 1)
             {
-                ret._julianDay = dayOfYear;
+                ret._dayNumber = dayOfYear;
                 goto R;
             }
 
@@ -1975,11 +1976,11 @@ public:
 
             days += dayOfYear;
 
-            ret._julianDay = days;
+            ret._dayNumber = days;
         }
         else if (_year == 0)
         {
-            ret._julianDay = dayOfYear - daysInLeapYear;
+            ret._dayNumber = dayOfYear - daysInLeapYear;
         }
         else
         {
@@ -2005,10 +2006,10 @@ public:
             else
                 days -= daysInLeapYear - dayOfYear;
 
-            ret._julianDay = days;
+            ret._dayNumber = days;
         }
     R:
-        ret._julianDay += _julianShift;
+        ret._dayNumber -= 1;
         return ret;
     }
 
@@ -2073,7 +2074,7 @@ public:
     version(mir_test)
     @safe unittest
     {
-        auto d = date(YearMonthDay(2020, Month.may, 31));
+        auto d = Date(YearMonthDay(2020, Month.may, 31));
     }
 
     version(D_Exceptions)
@@ -2094,8 +2095,8 @@ public:
     version(mir_test)
     @safe unittest
     {
-        auto d1 = date(YearQuarter(2020, Quarter.q2));
-        auto d2 = date(YearQuarter(2020, Quarter.q2), AssumePeriod.end);
+        auto d1 = Date(YearQuarter(2020, Quarter.q2));
+        auto d2 = Date(YearQuarter(2020, Quarter.q2), AssumePeriod.end);
     }
 
     version(D_Exceptions)
@@ -2109,8 +2110,8 @@ public:
     version(mir_test)
     @safe unittest
     {
-        auto d1 = date(YearMonth(2020, Month.may));
-        auto d2 = date(YearMonth(2020, Month.may), AssumePeriod.end);
+        auto d1 = Date(YearMonth(2020, Month.may));
+        auto d2 = Date(YearMonth(2020, Month.may), AssumePeriod.end);
     }
 
     version(D_Exceptions)
@@ -2190,9 +2191,10 @@ public:
         Params:
             day = Julian day.
      +/
+    deprecated("Use `fromDayNumber` adjusted by -1_721_426")
     this(int day) @safe pure nothrow @nogc
     {
-        _julianDay = day;
+        _dayNumber = day - (1 + _julianShift);
     }
 
     version (mir_test)
@@ -2218,7 +2220,7 @@ public:
      +/
     int opCmp(Date rhs) const @safe pure nothrow @nogc
     {
-        return this._julianDay - rhs._julianDay;
+        return this._dayNumber - rhs._dayNumber;
     }
 
     version (mir_test)
@@ -2310,7 +2312,7 @@ public:
       +/
     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
     {
-        return getDayOfWeek(_julianDay);
+        return getDayOfWeek(_dayNumber);
     }
 
     version (mir_test)
@@ -2324,12 +2326,23 @@ public:
         static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
     }
 
+    /++
+    Params:
+        dayNumber = Day Of Gregorian Calendar Minus One
+    +/
+    static Date fromDayNumber(int dayNumber) @safe pure nothrow @nogc
+    {
+        Date date;
+        date._dayNumber = dayNumber;
+        return date;
+    }
+
     /++
         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
      +/
     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
     {
-        return _julianDay - _julianShift;
+        return _dayNumber + 1;
     }
 
     ///
@@ -2369,18 +2382,21 @@ public:
 
         Params:
             day = The day of the Gregorian Calendar to set this $(LREF Date) to.
+        
+        Note:
+            Zero value corresponds to 
      +/
     @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
     {
-        this = Date(day + _julianShift);
+        _dayNumber = day - 1;
     }
 
     ///
     version (mir_test)
     @safe unittest
     {
+        import mir.test;
         auto date = Date.init;
-        date.dayOfGregorianCal = 1;
         assert(date == Date(1, 1, 1));
 
         date.dayOfGregorianCal = 365;
@@ -2417,20 +2433,20 @@ public:
         static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
     }
 
-    private enum uint _startDict = Date(1900, 1, 1)._julianDay; // [
-    private enum uint _endDict = Date(2040, 1, 1)._julianDay; // )
+    private enum uint _startDict = Date(1900, 1, 1)._dayNumber; // [
+    private enum uint _endDict = Date(2040, 1, 1)._dayNumber; // )
     static immutable _dictYMD = ()
     {
         YearMonthDay[Date._endDict - Date._startDict] dict;
         foreach (uint i; 0 .. dict.length)
-            dict[i] = Date(i + Date._startDict).yearMonthDayImpl;
+            dict[i] = Date.fromDayNumber(i + Date._startDict).yearMonthDayImpl;
         return dict;
     }();
 
     ///
     YearMonthDay yearMonthDay() const @safe pure nothrow @nogc @property
     {
-        uint day = _julianDay;
+        uint day = _dayNumber;
         if (day < _endDict)
         {
             import mir.checkedint: subu;
@@ -2445,7 +2461,7 @@ public:
     ///
     YearQuarter yearQuarter() const @safe pure nothrow @nogc @property
     {
-        uint day = _julianDay;
+        uint day = _dayNumber;
         if (day < _endDict)
         {
             return yearMonthDay().YearQuarter;
@@ -2748,7 +2764,7 @@ public:
     version(mir_test)
     @safe unittest
     {
-        auto d = date(2020, Month.may, 31);
+        auto d = Date(2020, Month.may, 31);
         auto yq = d.yearQuarterImpl;
     }
 
@@ -2759,7 +2775,7 @@ public:
     {
         with(yearMonthDay)
         {
-            int d = _julianDay - day;
+            int d = _dayNumber - day;
             final switch (month) with(Month)
             {
                 case jan: d += maxDay(year, jan); goto case;
@@ -2778,7 +2794,7 @@ public:
                 case nov: d += maxDay(year, nov); goto case;
                 case dec: d += maxDay(year, dec); break;
             }
-            return Date(d);
+            return Date.fromDayNumber(d);
         }
     }
 
@@ -2798,7 +2814,7 @@ public:
     @property Date endOfMonth() const @safe pure nothrow @nogc
     {
         with(yearMonthDay)
-            return Date(_julianDay + maxDay(year, month) - day);
+            return Date.fromDayNumber(_dayNumber + maxDay(year, month) - day);
     }
 
     ///
@@ -2853,25 +2869,25 @@ public:
     ///
     int opBinary(string op : "-")(Date rhs) const
     {
-        return _julianDay - rhs._julianDay;
+        return _dayNumber - rhs._dayNumber;
     }
 
     ///
     Date opBinary(string op : "+")(int rhs) const
     {
-        return Date(_julianDay + rhs);
+        return Date.fromDayNumber(_dayNumber + rhs);
     }
 
     ///
     Date opBinaryRight(string op : "+")(int rhs) const
     {
-        return Date(_julianDay + rhs);
+        return Date.fromDayNumber(_dayNumber + rhs);
     }
 
     ///
     Date opBinary(string op : "-")(int rhs) const
     {
-        return Date(_julianDay - rhs);
+        return Date.fromDayNumber(_dayNumber - rhs);
     }
 
     ///
@@ -2943,7 +2959,7 @@ public:
       +/
     @property int julianDay() const @safe pure nothrow @nogc
     {
-        return _julianDay;
+        return _dayNumber + (1 + _julianShift);
     }
 
     version (mir_test)
@@ -3716,7 +3732,7 @@ public:
       +/
     @property static Date min() @safe pure nothrow @nogc
     {
-        return Date(-(int.max / 2));
+        return Date.fromDayNumber(int.max);
     }
 
     /++
@@ -3725,7 +3741,7 @@ public:
       +/
     @property static Date max() @safe pure nothrow @nogc
     {
-        return Date(int.max / 2);
+        return Date.fromDayNumber(int.min);
     }
 
 private:
@@ -3766,7 +3782,7 @@ package:
       +/
     ref Date _addDays(long days) return @safe pure nothrow @nogc
     {
-        _julianDay = cast(int)(_julianDay + days);
+        _dayNumber = cast(int)(_dayNumber + days);
         return this;
     }
 
@@ -3929,11 +3945,12 @@ package:
         static assert(!__traits(compiles, idate._addDays(12)));
     }
 
-    int _julianDay;
+    int _dayNumber;
 }
 
 /// ditto
-alias Date = date;
+deprecated("use `Date` instead")
+alias date = Date;
 
 /++
     Returns the number of days from the current day of the week to the given
diff --git a/source/mir/series.d b/source/mir/series.d
index 7f1084d8..825cbfd3 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -30,6 +30,7 @@ See_also: $(LREF unionSeries), $(LREF troykaSeries), $(LREF troykaGalop).
 +/
 @safe version(mir_test) unittest
 {
+    import mir.test;
     import mir.ndslice;
     import mir.series;
 
@@ -86,21 +87,17 @@ See_also: $(LREF unionSeries), $(LREF troykaSeries), $(LREF troykaGalop).
     assert(series0.getVerbose(refDate) == 3);
     assert(series0.getExtraVerbose(refDate, "My exception msg") == 3);
 
-    assert(collectExceptionMsg!Exception(
-            series0.get(missingDate)
-        ) == "Series double[date]: Missing required key");
+    collectExceptionMsg!Exception(series0.get(missingDate)).should
+        == "Series double[Date]: Missing required key";
 
-    assert(collectExceptionMsg!Exception(
-            series0.get(missingDate, new Exception("My exception msg"))
-        ) == "My exception msg");
+    collectExceptionMsg!Exception(series0.get(missingDate, new Exception("My exception msg"))).should
+        == "My exception msg";
 
-    assert(collectExceptionMsg!Exception(
-            series0.getVerbose(missingDate)
-        ) == "Series double[date]: Missing 2016-03-01 key");
+    collectExceptionMsg!Exception(series0.getVerbose(missingDate)).should
+        == "Series double[Date]: Missing 2016-03-01 key";
 
-    assert(collectExceptionMsg!Exception(
-            series0.getExtraVerbose(missingDate, "My exception msg")
-        ) == "My exception msg. Series double[date]: Missing 2016-03-01 key");
+    collectExceptionMsg!Exception(series0.getExtraVerbose(missingDate, "My exception msg")).should
+        == "My exception msg. Series double[Date]: Missing 2016-03-01 key";
 
     // assign with get*
     series0.get(refDate) = 100;

From bb5e9494f19857f767e91b9a7775dc64e54ae0d1 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Fri, 10 Mar 2023 15:51:44 +0700
Subject: [PATCH 556/573] add day number property

---
 source/mir/date.d | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/source/mir/date.d b/source/mir/date.d
index 55a093d8..8fec334d 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -2337,6 +2337,15 @@ public:
         return date;
     }
 
+    /++
+    Returns;
+        Day Of Gregorian Calendar Minus One
+    +/
+    int dayNumber() @safe pure nothrow @nogc const @property
+    {
+        return _dayNumber;
+    }
+
     /++
         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
      +/

From 081418a5bb9f22861fc15cbf44045f7aef5c848f Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 16 Mar 2023 14:31:38 -0400
Subject: [PATCH 557/573] Add documented UTs for lightConst/lightImmutable
 (#457)

* Add documented UTs for lightConst/lightImmutable

* UT fixups
---
 source/mir/ndslice/slice.d | 42 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/source/mir/ndslice/slice.d b/source/mir/ndslice/slice.d
index 5d843eee..1221105f 100644
--- a/source/mir/ndslice/slice.d
+++ b/source/mir/ndslice/slice.d
@@ -982,6 +982,28 @@ public:
         return ret;
     }
 
+    static if (doUnittest)
+    ///
+    @safe pure nothrow
+    version(mir_ndslice_test) unittest {
+        import mir.algorithm.iteration: equal;
+
+        immutable Slice!(int*, 1) x = [1, 2].sliced;
+        auto y = x.lightImmutable;
+        // this._iterator is copied to the new slice (i.e. both point to the same underlying data)
+        assert(x._iterator == y._iterator);
+        assert(x[0] == 1);
+        assert(x[1] == 2);
+        assert(y[0] == 1);
+        assert(y[1] == 2);
+        // Outer immutable is moved to iteration type
+        static assert(is(typeof(y) == Slice!(immutable(int)*, 1)));
+        // meaning that y can be modified, even if its elements can't
+        y.popFront;
+        // even if x can't be modified
+        //x.popFront; //error
+    }
+
     /// Returns: Mutable slice over const data.
     Slice!(LightConstOf!Iterator, N, kind, staticMap!(LightConstOf, Labels)) lightConst()() return scope const @property @trusted
     {
@@ -997,6 +1019,26 @@ public:
         return this.lightImmutable;
     }
 
+    static if (doUnittest)
+    ///
+    @safe pure nothrow
+    version(mir_ndslice_test) unittest {
+        import mir.algorithm.iteration: equal;
+
+        const Slice!(int*, 1) x = [1, 2].sliced;
+        auto y = x.lightConst;
+        // this._iterator is copied to the new slice (i.e. both point to the same underlying data)
+        assert(x._iterator == y._iterator);
+        assert(x.equal([1, 2]));
+        assert(y.equal([1, 2]));
+        // Outer const is moved to iteration type
+        static assert(is(typeof(y) == Slice!(const(int)*, 1)));
+        // meaning that y can be modified, even if its elements can't
+        y.popFront;
+        // even if x can't be modified
+        //x.popFront; //error
+    }
+
     /// Label for the dimensions 'd'. By default returns the row label.
     Slice!(Labels[d])
         label(size_t d = 0)() @property

From 713f77f9fcd5b74f686e3c78a0280d22e3e4b98e Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Tue, 2 May 2023 15:58:50 +0700
Subject: [PATCH 558/573] extend shouldApprox API

---
 source/mir/test.d | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/source/mir/test.d b/source/mir/test.d
index 850cc2ed..282936f6 100644
--- a/source/mir/test.d
+++ b/source/mir/test.d
@@ -14,15 +14,15 @@ private noreturn assumeAllAttrAndCall(scope const void delegate() t)
 }
 
 ///
-struct ShouldApprox(T)
-    if (__traits(isFloating, T))
+struct ShouldApprox(T, F = T)
+    if ((__traits(isFloating, T) || __traits(hasMember, T, "approxEqual")) && __traits(isFloating, F))
 {
     ///
     T value;
     ///
-    T maxRelDiff = 0x1p-20f;
+    F maxRelDiff = 0x1p-20f;
     ///
-    T maxAbsDiff = 0x1p-20f;
+    F maxAbsDiff = 0x1p-20f;
 
     ///
     void opEquals(T expected, string file = __FILE__, int line = __LINE__) @safe pure nothrow @nogc
@@ -59,6 +59,28 @@ unittest
     shouldApprox(1 + 9e-7, 1e-6, 1e-6) == 1;
 }
 
+/// ditto
+ShouldApprox!(T, F) shouldApprox(T, F)(const T value, const F maxRelDiff = double(0x1p-20f), const F maxAbsDiff = double(0x1p-20f))
+    if (__traits(hasMember, T, "approxEqual") && __traits(isFloating, F))
+{
+    return typeof(return)(value, maxRelDiff, maxAbsDiff);
+}
+
+///
+version(mir_test)
+unittest
+{
+    static struct C {
+        double re, im;
+        auto approxEqual(C rhs, double maxRelDiff, double maxAbsDiff)
+        {
+            import mir.math.common: approxEqual;
+            return approxEqual(re, rhs.re, maxRelDiff, maxAbsDiff) && approxEqual(im, rhs.im, maxRelDiff, maxAbsDiff);
+        }
+    }
+    C(1.0, 1.0).shouldApprox == C(1 + 9e-7, 1 - 9e-7);
+}
+
 ///
 struct Should(T)
 {

From bbf0c99027783a6e792c44c88e6ddeb45abf77fc Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Thu, 11 May 2023 17:50:29 +0700
Subject: [PATCH 559/573] add SmallString.toHash

---
 source/mir/small_string.d | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 3b9820bd..3ba4955d 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -289,6 +289,12 @@ scope const:
     {
         return __cmp(opIndex, str);
     }
+
+    ///
+    extern (D) auto toHash() const nothrow @safe
+    {
+        return hashOf(this[]);
+    }
 }
 
 ///

From 8697ca5fe577a10e5c07db7ca8724fcaa9c09444 Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Sat, 20 May 2023 07:43:01 -0400
Subject: [PATCH 560/573] Adjust center for isConvertibleToSlice and add
 another UT example (#466)

---
 source/mir/math/stat.d | 41 +++++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/source/mir/math/stat.d b/source/mir/math/stat.d
index a2ce2936..72db9a0e 100644
--- a/source/mir/math/stat.d
+++ b/source/mir/math/stat.d
@@ -1716,7 +1716,7 @@ Returns:
 +/
 template center(alias centralTendency = mean!(Summation.appropriate))
 {
-    import mir.ndslice.slice: Slice, SliceKind, sliced, hasAsSlice;
+    import mir.ndslice.slice: isConvertibleToSlice, isSlice, Slice, SliceKind;
     /++
     Params:
         slice = slice
@@ -1734,16 +1734,11 @@ template center(alias centralTendency = mean!(Summation.appropriate))
     }
     
     /// ditto
-    auto center(T)(T[] array)
+    auto center(T)(T x)
+        if (isConvertibleToSlice!T && !isSlice!T)
     {
-        return center(array.sliced);
-    }
-
-    /// ditto
-    auto center(T)(T withAsSlice)
-        if (hasAsSlice!T)
-    {
-        return center(withAsSlice.asSlice);
+        import mir.ndslice.slice: toSlice;
+        return center(x.toSlice);
     }
 }
 
@@ -1763,6 +1758,32 @@ unittest
     assert(x.center!hmean.all!approxEqual([-1.44898, -0.44898, 0.55102, 1.55102, 2.55102, 3.55102]));
     assert(x.center!gmean.all!approxEqual([-1.99379516, -0.99379516, 0.00620483, 1.00620483, 2.00620483, 3.00620483]));
     assert(x.center!median.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
+
+    // center operates lazily, if original slice is changed, then 
+    auto y = x.center;
+    assert(y.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
+    x[0]++;
+    assert(y.all!approxEqual([-1.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
+}
+
+/// Example of lazy behavior of center
+version(mir_test)
+@safe pure nothrow
+unittest
+{
+    import mir.algorithm.iteration: all;
+    import mir.math.common: approxEqual;
+    import mir.ndslice.allocation: slice;
+    import mir.ndslice.slice: sliced;
+
+    auto x = [1.0, 2, 3, 4, 5, 6].sliced;
+    auto y = x.center;
+    auto z = x.center.slice;
+    assert(y.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
+    x[0]++;
+    // y changes, while z does not
+    assert(y.all!approxEqual([-1.5, -1.5, -0.5, 0.5, 1.5, 2.5]));
+    assert(z.all!approxEqual([-2.5, -1.5, -0.5, 0.5, 1.5, 2.5]));   
 }
 
 /// Center dynamic array

From f2f0f94877707ede9882b44cee16275aca70513a Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Mon, 22 May 2023 21:24:42 +0700
Subject: [PATCH 561/573] move toHash

---
 source/mir/small_string.d | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 3ba4955d..345e47bf 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -196,6 +196,11 @@ extern(D) @safe pure @nogc:
     /// ditto
     alias opBinary(string op : "~") = concat;
 
+    ///
+    extern (D) size_t toHash() const nothrow @safe
+    {
+        return hashOf(this[]);
+    }
 
 scope nothrow:
 
@@ -289,12 +294,6 @@ scope const:
     {
         return __cmp(opIndex, str);
     }
-
-    ///
-    extern (D) auto toHash() const nothrow @safe
-    {
-        return hashOf(this[]);
-    }
 }
 
 ///

From 04dd7ddd6c48943f7862e5f25f7349c7512c9c9d Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Sat, 29 Jul 2023 16:37:20 +0700
Subject: [PATCH 562/573] adopt for newer compilers

---
 dub.sdl            |  2 +-
 source/mir/serde.d | 13 +++++--------
 2 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index af3103bb..7748504d 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilia Ki" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And
 copyright "2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.5.2"
+dependency "mir-core" version=">=1.5.6"
 
 // dflags "-version=MirNoSIMD"
 // dflags "-mtriple=aarch64-linux-gnu"
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 128f9e73..191be1a4 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -14,7 +14,9 @@ module mir.serde;
 import mir.functional: naryFun;
 import mir.reflection;
 import std.meta: AliasSeq;
-import std.traits: TemplateArgsOf, EnumMembers, hasUDA, isAggregateType;
+import std.traits: TemplateArgsOf, EnumMembers, isAggregateType;
+import mir.internal.meta: hasUDA;
+import mir.reflection: getUDA;
 
 version (D_Exceptions)
 {
@@ -612,7 +614,6 @@ version(mir_test) unittest
         double d = 0; // skips field if 0 during deserialization
     }
 
-    import std.traits: hasUDA;
 
     static assert(hasUDA!(S.d, serdeIgnoreDefault));
 }
@@ -636,7 +637,6 @@ version(mir_test) unittest
         double d;
     }
 
-    import std.traits: hasUDA;
 
     static assert(hasUDA!(S.d, serdeProxy));
     static assert(hasUDA!(S.d, serdeProxy!(SmallString!32)));
@@ -1074,7 +1074,7 @@ Final proxy type
 template serdeGetFinalProxy(T)
 {
     import mir.timestamp: Timestamp;
-    import std.traits: hasUDA, isAggregateType;
+    import std.traits: isAggregateType;
     static if (isAggregateType!T || is(T == enum))
     {
         static if (hasUDA!(T, serdeProxy))
@@ -1120,7 +1120,7 @@ Final deep proxy type
 template serdeGetFinalDeepProxy(T)
 {
     import mir.timestamp: Timestamp;
-    import std.traits: Unqual, hasUDA, isAggregateType, isArray, ForeachType;
+    import std.traits: Unqual, isAggregateType, isArray, ForeachType;
     static if (isAggregateType!T || is(T == enum))
     {
         static if (hasUDA!(T, serdeProxy))
@@ -1348,7 +1348,6 @@ Deserialization member final proxy type
 +/
 template serdeFinalDeserializationMemberType(T, string member)
 {
-    import std.traits: hasUDA;
     static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
     {
         alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
@@ -1438,7 +1437,6 @@ Serialization member final proxy type
 +/
 template serdeFinalSerializationMemberType(T, string member)
 {
-    import std.traits: hasUDA;
     static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
     {
         alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
@@ -2018,7 +2016,6 @@ A dummy structure usefull $(LREF serdeOrderedIn) support.
 struct SerdeOrderedDummy(T, bool __optionalByDefault = false)
     if (is(serdeGetFinalProxy!T == T) && isAggregateType!T)
 {
-    import std.traits: hasUDA;
 
     @serdeIgnore
     SerdeFlags!(typeof(this)) __serdeFlags;

From bac330898d90498b4798e056d8d1edf4345cddd3 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Wed, 2 Aug 2023 21:55:32 +0700
Subject: [PATCH 563/573] update for new compilers

---
 source/mir/annotated.d              |  2 +-
 source/mir/base64.d                 | 14 +++--
 source/mir/bignum/decimal.d         |  2 +-
 source/mir/bignum/fixed.d           |  6 +-
 source/mir/bignum/integer.d         |  8 +--
 source/mir/bignum/low_level_view.d  |  8 +--
 source/mir/date.d                   | 28 ++++-----
 source/mir/ediff.d                  | 23 +++----
 source/mir/graph/package.d          |  4 +-
 source/mir/interpolate/constant.d   |  4 +-
 source/mir/interpolate/generic.d    |  4 +-
 source/mir/interpolate/linear.d     |  8 +--
 source/mir/interpolate/polynomial.d |  2 +-
 source/mir/interpolate/spline.d     |  6 +-
 source/mir/math/sum.d               |  2 +-
 source/mir/ndslice/fuse.d           |  2 +-
 source/mir/ndslice/iterator.d       |  2 +-
 source/mir/ndslice/topology.d       |  2 +-
 source/mir/numeric.d                | 19 +++---
 source/mir/parse.d                  |  6 +-
 source/mir/rc/array.d               |  2 +-
 source/mir/rc/ptr.d                 |  2 +-
 source/mir/rc/slim_ptr.d            |  2 +-
 source/mir/serde.d                  | 95 +++++++++++++++++++++--------
 source/mir/series.d                 |  5 +-
 source/mir/small_array.d            | 12 ++--
 source/mir/small_string.d           | 12 ++--
 source/mir/stdio.d                  | 10 +--
 source/mir/timestamp.d              | 12 ++--
 29 files changed, 177 insertions(+), 127 deletions(-)

diff --git a/source/mir/annotated.d b/source/mir/annotated.d
index ba47c2eb..7b2d7c81 100644
--- a/source/mir/annotated.d
+++ b/source/mir/annotated.d
@@ -96,7 +96,7 @@ struct Annotated(T) {
         if (annotations.length == 0)
         {
             version (D_Exceptions)
-                throw exc;
+                { import mir.exception : toMutable; throw exc.toMutable; }
             else
                 assert(0, excMsg);
         }
diff --git a/source/mir/base64.d b/source/mir/base64.d
index 8170e2ee..7743d22d 100644
--- a/source/mir/base64.d
+++ b/source/mir/base64.d
@@ -7,6 +7,8 @@ Copyright: 2021 Harrison Ford, Symmetry Investments
 +/
 module mir.base64;
 
+import mir.exception: toMutable;
+
 package static immutable base64DecodeInvalidCharMsg = "base64: Invalid character encountered.";
 package static immutable base64DecodeInvalidLenMsg = "Cannot decode a buffer with given length (not a multiple of 4, missing padding?)";
 version(D_Exceptions) {
@@ -78,7 +80,7 @@ private ubyte lookup_decoding(char i, char plusChar = '+', char slashChar = '/')
     }
     else {
         version(D_Exceptions) {
-            throw base64DecodeInvalidCharException;
+            throw base64DecodeInvalidCharException.toMutable;
         } else {
             assert(0, base64DecodeInvalidCharMsg);
         }
@@ -110,7 +112,7 @@ void decodeBase64(Appender)(scope const(char)[] data,
     if (data.length % 4 != 0)
     {
         version(D_Exceptions) {
-            throw base64DecodeInvalidLenException;
+            throw base64DecodeInvalidLenException.toMutable;
         } else {
             assert(0, base64DecodeInvalidLenMsg);
         }
@@ -137,7 +139,7 @@ void decodeBase64(Appender)(scope const(char)[] data,
         if (group[0] == '=' || group[1] == '=')
         {
             version(D_Exceptions)
-                throw base64DecodeInvalidCharException;
+                throw base64DecodeInvalidCharException.toMutable;
             else
                 assert(0, base64DecodeInvalidCharMsg);
         }
@@ -151,7 +153,7 @@ void decodeBase64(Appender)(scope const(char)[] data,
             if ((i / 4) != ((data.length / 4) - 1))
             {
                 version(D_Exceptions)
-                    throw base64DecodeInvalidCharException;
+                    throw base64DecodeInvalidCharException.toMutable;
                 else
                     assert(0, base64DecodeInvalidCharMsg);
             }
@@ -166,7 +168,7 @@ void decodeBase64(Appender)(scope const(char)[] data,
             else
             {
                 version(D_Exceptions)
-                    throw base64DecodeInvalidCharException;
+                    throw base64DecodeInvalidCharException.toMutable;
                 else
                     assert(0, base64DecodeInvalidCharMsg);
             }
@@ -180,7 +182,7 @@ void decodeBase64(Appender)(scope const(char)[] data,
             if ((i / 4) != ((data.length / 4) - 1))
             {
                 version(D_Exceptions)
-                    throw base64DecodeInvalidCharException;
+                    throw base64DecodeInvalidCharException.toMutable;
                 else
                     assert(0, base64DecodeInvalidCharMsg);
             }
diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index e5c5bdd6..94fc07bb 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -243,7 +243,7 @@ struct Decimal(uint size64)
         else
         {
             static immutable exception = new Exception("Can't parse Decimal!" ~ size64.stringof ~ ".");
-            throw exception;
+            { import mir.exception : toMutable; throw exception.toMutable; }
         }
     }
 
diff --git a/source/mir/bignum/fixed.d b/source/mir/bignum/fixed.d
index e23a3355..54920761 100644
--- a/source/mir/bignum/fixed.d
+++ b/source/mir/bignum/fixed.d
@@ -105,7 +105,7 @@ struct UInt(size_t size)
         else
         {
             static immutable exception = new Exception("Can't parse UInt!" ~ size.stringof ~ ".");
-            throw exception;
+            { import mir.exception : toMutable; throw exception.toMutable; }
         }
     }
 
@@ -205,7 +205,7 @@ struct UInt(size_t size)
         version(D_Exceptions)
         {
             import mir.bignum.low_level_view: hexStringException;
-            throw hexStringException;
+            { import mir.exception : toMutable; throw hexStringException.toMutable; }
         }
         else
         {
@@ -232,7 +232,7 @@ struct UInt(size_t size)
         version(D_Exceptions)
         {
             import mir.bignum.low_level_view: binaryStringException;
-            throw binaryStringException;
+            { import mir.exception : toMutable; throw binaryStringException.toMutable; }
         }
         else
         {
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index cdce4921..6957579f 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -115,7 +115,7 @@ struct BigInt(uint size64)
         else
         {
             static immutable exception = new Exception("Can't parse BigInt!" ~ size64.stringof ~ ".");
-            throw exception;
+            { import mir.exception : toMutable; throw exception.toMutable; }
         }
     }
 
@@ -194,7 +194,7 @@ struct BigInt(uint size64)
         static immutable bigIntOverflowException = new Exception("BigInt!" ~ size64.stringof ~ ".fromBigEndian: data overflow");
         BigInt ret = void;
         if (!ret.copyFromBigEndian(data, sign))
-            throw bigIntOverflowException;
+            { import mir.exception : toMutable; throw bigIntOverflowException.toMutable; }
         return ret;
     }
 
@@ -766,7 +766,7 @@ struct BigInt(uint size64)
         if (ret.fromHexStringImpl!(char, allowUnderscores)(str))
             return ret;
         version(D_Exceptions)
-            throw hexStringException;
+            { import mir.exception : toMutable; throw hexStringException.toMutable; }
         else
             assert(0, hexStringErrorMsg);
     }
@@ -793,7 +793,7 @@ struct BigInt(uint size64)
         if (ret.fromBinaryStringImpl!(char, allowUnderscores)(str))
             return ret;
         version(D_Exceptions)
-            throw binaryStringException;
+            { import mir.exception : toMutable; throw binaryStringException.toMutable; }
         else
             assert(0, binaryStringErrorMsg);
     }
diff --git a/source/mir/bignum/low_level_view.d b/source/mir/bignum/low_level_view.d
index f7e21c4c..4a7fd635 100644
--- a/source/mir/bignum/low_level_view.d
+++ b/source/mir/bignum/low_level_view.d
@@ -277,7 +277,7 @@ struct BigUIntView(W)
         if (view.fromHexStringImpl!(C, allowUnderscores)(str))
             return BigUIntView(cast(W[])view.coefficients);
         version(D_Exceptions)
-            throw hexStringException;
+            { import mir.exception : toMutable; throw hexStringException.toMutable; }
         else
             assert(0, hexStringErrorMsg);
     }
@@ -400,7 +400,7 @@ struct BigUIntView(W)
         if (view.fromBinaryStringImpl!(C, allowUnderscores)(str))
             return BigUIntView(cast(W[])view.coefficients);
         version(D_Exceptions)
-            throw binaryStringException;
+            { import mir.exception : toMutable; throw binaryStringException.toMutable; }
         else
             assert(0, binaryStringErrorMsg);
     }
@@ -1343,7 +1343,7 @@ struct BigIntView(W)
         if (ret.fromHexStringImpl!(C, allowUnderscores)(str))
             return  cast(BigIntView) ret;
         version(D_Exceptions)
-            throw hexStringException;
+            { import mir.exception : toMutable; throw hexStringException.toMutable; }
         else
             assert(0, hexStringErrorMsg);
     }
@@ -1392,7 +1392,7 @@ struct BigIntView(W)
         if (ret.fromBinaryStringImpl!(C, allowUnderscores)(str))
             return cast(BigIntView) ret;
         version(D_Exceptions)
-            throw binaryStringException;
+            { import mir.exception : toMutable; throw binaryStringException.toMutable; }
         else
             assert(0, binaryStringErrorMsg);
     }
diff --git a/source/mir/date.d b/source/mir/date.d
index 8fec334d..ea6f8950 100644
--- a/source/mir/date.d
+++ b/source/mir/date.d
@@ -343,7 +343,7 @@ struct YearMonthDay
         if (timestamp.precision != Timestamp.Precision.day)
         {
             static immutable exc = new Exception("YearMonthDay: invalid timestamp precision");
-            throw exc;
+            { import mir.exception : toMutable; throw exc.toMutable; }
         }
         with(timestamp) this(year, cast(Month)month, day);
     }
@@ -459,7 +459,7 @@ struct YearMonthDay
 
         static if (useExceptions)
         {
-            if (dayOutOfRange) throw InvalidDay;
+            if (dayOutOfRange) { import mir.exception : toMutable; throw InvalidDay.toMutable; }
         }
         else
         {
@@ -632,7 +632,7 @@ struct YearMonth
     {
         static immutable exc = new Exception("Month out of bounds [1, 12]");
         if (1 > month || month > 12)
-            throw exc;
+            { import mir.exception : toMutable; throw exc.toMutable; }
         this.year = year;
         this.month = cast(Month)month;
     }
@@ -711,7 +711,7 @@ struct YearMonth
         if (timestamp.precision != Timestamp.Precision.month)
         {
             static immutable exc = new Exception("YearMonth: invalid timestamp precision");
-            throw exc;
+            { import mir.exception : toMutable; throw exc.toMutable; }
         }
         with(timestamp) this(year, cast(Month)month);
     }
@@ -851,7 +851,7 @@ struct YearMonth
         if (fromISOExtString(str, ret))
             return ret;
         static immutable exc = new Exception("Invalid YearMonth string");
-        throw exc;
+        { import mir.exception : toMutable; throw exc.toMutable; }
     }
 
 nothrow:
@@ -1087,7 +1087,7 @@ nothrow:
 
         static if (useExceptions)
         {
-            if (dayOutOfRange) throw InvalidDay;
+            if (dayOutOfRange) { import mir.exception : toMutable; throw InvalidDay.toMutable; }
         }
         else
         {
@@ -1482,7 +1482,7 @@ struct YearQuarter
         if (timestamp.precision != Timestamp.Precision.month)
         {
             static immutable exc = new Exception("YearMonth: invalid timestamp precision");
-            throw exc;
+            { import mir.exception : toMutable; throw exc.toMutable; }
         }
         with(timestamp) this(year, cast(Month)month);
     }
@@ -1662,7 +1662,7 @@ struct YearQuarter
 
         static if (useExceptions)
         {
-            if (dayOutOfRange) throw InvalidDay;
+            if (dayOutOfRange) { import mir.exception : toMutable; throw InvalidDay.toMutable; }
         }
         else
         {
@@ -2035,7 +2035,7 @@ public:
         if (timestamp.precision != Timestamp.Precision.day)
         {
             static immutable exc = new Exception("Date: invalid timestamp precision");
-            throw exc;
+            { import mir.exception : toMutable; throw exc.toMutable; }
         }
         with(timestamp) this(year, cast(Month)month, day);
     }
@@ -2119,9 +2119,9 @@ public:
     this(int _year, int _month, int _day) @safe pure @nogc
     {
         if (!valid!"months"(_month))
-            throw InvalidMonth;
+            { import mir.exception : toMutable; throw InvalidMonth.toMutable; }
         if (!valid!"days"(_year, cast(Month) _month, _day))
-            throw InvalidDay;
+            { import mir.exception : toMutable; throw InvalidDay.toMutable; }
         this = trustedCreate(_year, _month, _day);
     }
 
@@ -3330,7 +3330,7 @@ public:
         Date ret;
         if (fromISOString(str, ret))
             return ret;
-        throw InvalidISOString;
+        { import mir.exception : toMutable; throw InvalidISOString.toMutable; }
     }
 
     ///
@@ -3461,7 +3461,7 @@ public:
         Date ret;
         if (fromISOExtString(str, ret))
             return ret;
-        throw InvalidISOExtendedString;
+        { import mir.exception : toMutable; throw InvalidISOExtendedString.toMutable; }
     }
 
     ///
@@ -3732,7 +3732,7 @@ public:
         Date ret;
         if (fromString(str, ret))
             return ret;
-        throw InvalidString;
+        { import mir.exception : toMutable; throw InvalidString.toMutable; }
     }
 
     /++
diff --git a/source/mir/ediff.d b/source/mir/ediff.d
index 66813948..48238691 100644
--- a/source/mir/ediff.d
+++ b/source/mir/ediff.d
@@ -179,7 +179,8 @@ import mir.algorithm.iteration: uniq;
 import mir.array.allocation;
 import mir.math.common;
 import mir.ndslice.sorting: sort;
-import std.traits: Unqual, getUDAs, hasUDA, isPointer, PointerTarget;
+import std.traits: Unqual, isPointer, PointerTarget;
+import mir.internal.meta: getUDAs, hasUDA;
 
 @fmamath:
 
@@ -405,9 +406,9 @@ template Dependencies(T)
             string[] variables;
             static foreach (member; __traits(allMembers, T))
             {
-                static if (hasUDA!(__traits(getMember, T, member), Derivative))
+                static if (hasUDA!(T, member, Derivative))
                 {
-                    variables ~= getUDAs!(__traits(getMember, T, member), Derivative)[0].variables;
+                    variables ~= getUDAs!(T, member, Derivative)[0].variables;
                 }
             }
             return variables.sort.uniq.array.DependsOn;
@@ -478,8 +479,8 @@ template getDerivative(string[] variables, bool strict = true)
                     alias V = T;
                 template hasDerivative(string member)
                 {
-                    static if (hasUDA!(__traits(getMember, V, member), Derivative))
-                        enum hasDerivative = variables == getUDAs!(__traits(getMember, V, member), Derivative)[0].variables;
+                    static if (hasUDA!(V, member, Derivative))
+                        enum hasDerivative = variables == getUDAs!(V, member, Derivative)[0].variables;
                     else
                         enum hasDerivative = false;
                 }
@@ -489,7 +490,7 @@ template getDerivative(string[] variables, bool strict = true)
                     {
                         static if (hasDerivative!member)
                         {
-                            static if (hasUDA!(__traits(getMember, V, member), Minus))
+                            static if (hasUDA!(V, member, Minus))
                                 return -__traits(getMember, value, member);
                             else
                                 return __traits(getMember, value, member);
@@ -530,16 +531,16 @@ void setDerivatives(bool strict = true, D, E)(scope ref D derivatives, E express
         return derivatives.setDerivatives!strict(expression);
     else
     {
-        import std.traits: getUDAs, hasUDA, isPointer, PointerTarget;
+        import std.traits: isPointer, PointerTarget;
 
         static foreach (member; __traits(allMembers, D))
         {
-            static if (hasUDA!(__traits(getMember, D, member), Derivative))
+            static if (hasUDA!(D, member, Derivative))
             {
-                static if (hasUDA!(__traits(getMember, derivatives, member), Minus))
-                    __traits(getMember, derivatives, member) = -expression.getDerivative!(getUDAs!(__traits(getMember, derivatives, member), Derivative)[0].variables, strict);
+                static if (hasUDA!(D, member, Minus))
+                    __traits(getMember, derivatives, member) = -expression.getDerivative!(getUDAs!(D, member, Derivative)[0].variables, strict);
                 else
-                    __traits(getMember, derivatives, member) = expression.getDerivative!(getUDAs!(__traits(getMember, derivatives, member), Derivative)[0].variables, strict);
+                    __traits(getMember, derivatives, member) = expression.getDerivative!(getUDAs!(D, member, Derivative)[0].variables, strict);
             }
         }
     }
diff --git a/source/mir/graph/package.d b/source/mir/graph/package.d
index 8a408fb7..979ae297 100644
--- a/source/mir/graph/package.d
+++ b/source/mir/graph/package.d
@@ -70,7 +70,7 @@ GraphSeries!(T, I, J) graphSeries(I = uint, J = size_t, T, Range)(in Range[T] aa
             if (index >= keys.length)
             {
                 version(D_Exceptions)
-                    throw exception;
+                    { import mir.exception : toMutable; throw exception.toMutable; }
                 else
                     assert(0, exc_msg);
             }
@@ -132,7 +132,7 @@ RCGraph!(I, J) rcgraph(I = uint, J = size_t, KeyIterator, RangeIterator)(Series!
             if (index >= keys.length)
             {
                 version(D_Exceptions)
-                    throw exception;
+                    { import mir.exception : toMutable; throw exception.toMutable; }
                 else
                     assert(0, exc_msg);
             }
diff --git a/source/mir/interpolate/constant.d b/source/mir/interpolate/constant.d
index c50b621f..a41d85a6 100644
--- a/source/mir/interpolate/constant.d
+++ b/source/mir/interpolate/constant.d
@@ -111,12 +111,12 @@ extern(D):
         {
             if (x.length < 1)
             {
-                version(D_Exceptions) throw exc_min;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; }
                 else assert(0, msg_min);
             }
             if (x.length != data._lengths[i])
             {
-                version(D_Exceptions) throw exc_eq;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; }
                 else assert(0, msg_eq);
             }
             _grid[i] = x._iterator;
diff --git a/source/mir/interpolate/generic.d b/source/mir/interpolate/generic.d
index a56a5a3f..70125359 100644
--- a/source/mir/interpolate/generic.d
+++ b/source/mir/interpolate/generic.d
@@ -131,12 +131,12 @@ extern(D):
         }
         if (grid.length < 2)
         {
-            version(D_Exceptions) throw exc_min;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; }
             else assert(0, msg_min);
         }
         if (grid.length != data._lengths[0] + 1)
         {
-            version(D_Exceptions) throw exc_eq;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; }
             else assert(0, msg_eq);
         }
         _grid = move(grid._iterator);
diff --git a/source/mir/interpolate/linear.d b/source/mir/interpolate/linear.d
index f4249e7f..f7c68a93 100644
--- a/source/mir/interpolate/linear.d
+++ b/source/mir/interpolate/linear.d
@@ -217,12 +217,12 @@ struct Linear(F, size_t N = 1, X = F)
         {
             if (x.length < 2)
             {
-                version(D_Exceptions) throw exc_min;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; }
                 else assert(0, msg_min);
             }
             if (x.length != data._lengths[i])
             {
-                version(D_Exceptions) throw exc_eq;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; }
                 else assert(0, msg_eq);
             }
             _grid[i] = x._iterator.move;
@@ -461,12 +461,12 @@ struct MetaLinear(T, X)
 
         if (grid.length < 2)
         {
-            version(D_Exceptions) throw exc_min;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; }
             else assert(0, msg_min);
         }
         if (grid.length != data.length)
         {
-            version(D_Exceptions) throw exc_eq;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; }
             else assert(0, msg_eq);
         }
 
diff --git a/source/mir/interpolate/polynomial.d b/source/mir/interpolate/polynomial.d
index 72d33eb8..1f4c2d96 100644
--- a/source/mir/interpolate/polynomial.d
+++ b/source/mir/interpolate/polynomial.d
@@ -125,7 +125,7 @@ struct Lagrange(T, uint maxAdditionalFunctions = 0, X = T)
             enum T md = T.min_normal / T.epsilon;
             static immutable exc = new Exception(msg);
             if (!grid.lightScope.diff.all!(a => md <= a && a <= T.max))
-                throw exc;
+                { import mir.exception : toMutable; throw exc.toMutable; }
         }
         swap(_grid, grid);
         swap(_inversedBarycentricWeights, inversedBarycentricWeights);
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index b586e1be..8bdd8eee 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -833,7 +833,7 @@ struct Spline(F, size_t N = 1, X = F)
         {
             if (x.length < 2)
             {
-                version(D_Exceptions) throw exc_min;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; }
                 else assert(0, msg_min);
             }
             length *= shape[i] = x.length;
@@ -1967,13 +1967,13 @@ struct MetaSpline(T, X)
 
         if (grid.length < 2)
         {
-            version(D_Exceptions) throw exc_min;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exc_min.toMutable; }
             else assert(0, msg_min);
         }
 
         if (grid.length != data.length)
         {
-            version(D_Exceptions) throw exc_eq;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exc_eq.toMutable; }
             else assert(0, msg_eq);
         }
 
diff --git a/source/mir/math/sum.d b/source/mir/math/sum.d
index f92df4c7..c74f106e 100644
--- a/source/mir/math/sum.d
+++ b/source/mir/math/sum.d
@@ -1944,7 +1944,7 @@ template sumSlices()
             if (sl.length != ret.length)
             {
                 version (D_Exceptions)
-                    throw jaggedException;
+                    { import mir.exception : toMutable; throw jaggedException.toMutable; }
                 else
                     assert(0);
             }
diff --git a/source/mir/ndslice/fuse.d b/source/mir/ndslice/fuse.d
index aa232000..52fbb4c5 100644
--- a/source/mir/ndslice/fuse.d
+++ b/source/mir/ndslice/fuse.d
@@ -363,7 +363,7 @@ size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r)
                         if (elemShape != ret[N .. $])
                         {
                             version (D_Exceptions)
-                                throw shapeException;
+                                { import mir.exception : toMutable; throw shapeException.toMutable; }
                             else
                                 assert(0, shapeExceptionMsg);
                         }
diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index 4ea37a57..a2fb780a 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -1836,7 +1836,7 @@ struct FieldIterator(Field)
         if (_expect(_index != 0, false))
         {
             version (D_Exceptions)
-                throw assumeZeroShiftException;
+                { import mir.exception : toMutable; throw assumeZeroShiftException.toMutable; }
             else
                 assert(0, assumeZeroShiftExceptionMsg);
         }
diff --git a/source/mir/ndslice/topology.d b/source/mir/ndslice/topology.d
index 4b54781c..d8b76ffa 100644
--- a/source/mir/ndslice/topology.d
+++ b/source/mir/ndslice/topology.d
@@ -3876,7 +3876,7 @@ do {
         if (length && bounds[length - 1] > sliceable.length)
         {
             version (D_Exceptions)
-                throw choppedException;
+                { import mir.exception : toMutable; throw choppedException.toMutable; }
             else
                assert(0, choppedExceptionMsg);
         }
diff --git a/source/mir/numeric.d b/source/mir/numeric.d
index 1edf090b..436b14f7 100644
--- a/source/mir/numeric.d
+++ b/source/mir/numeric.d
@@ -11,6 +11,7 @@ module mir.numeric;
 import mir.internal.utility: isFloatingPoint;
 import mir.math.common;
 import mir.math.ieee;
+import mir.exception: toMutable;
 
 version(D_Exceptions)
 {
@@ -64,9 +65,9 @@ struct mir_find_root_result(T)
         with(FindRootStatus) final switch(status)
         {
             case success: return this;
-            case badBounds: throw findRoot_badBounds;
-            case nanX: throw findRoot_nanX;
-            case nanY: throw findRoot_nanY;
+            case badBounds: throw findRoot_badBounds.toMutable;
+            case nanX: throw findRoot_nanX.toMutable;
+            case nanY: throw findRoot_nanY.toMutable;
         }
     }
 
@@ -1128,9 +1129,9 @@ struct FindLocalMinResult(T)
         with(FindRootStatus) final switch(status)
         {
             case success: return this;
-            case badBounds: throw findRoot_badBounds;
-            case nanX: throw findRoot_nanX;
-            case nanY: throw findRoot_nanY;
+            case badBounds: throw findRoot_badBounds.toMutable;
+            case nanX: throw findRoot_nanX.toMutable;
+            case nanY: throw findRoot_nanY.toMutable;
         }
     }
 
@@ -1564,9 +1565,9 @@ struct FindSmileRootsResult(T)
         with(FindRootStatus) final switch(status)
         {
             case success: return this;
-            case badBounds: throw findRoot_badBounds;
-            case nanX: throw findRoot_nanX;
-            case nanY: throw findRoot_nanY;
+            case badBounds: throw findRoot_badBounds.toMutable;
+            case nanX: throw findRoot_nanX.toMutable;
+            case nanY: throw findRoot_nanY.toMutable;
         }
     }
 
diff --git a/source/mir/parse.d b/source/mir/parse.d
index 9425c33f..a76891fe 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -115,7 +115,7 @@ template fromString(T)
             if (_expect(.fromString(str, value), true))
                 return value;
             version (D_Exceptions)
-                throw excfp;
+                { import mir.exception : toMutable; throw excfp.toMutable; }
             else
                 assert(0);
         }
@@ -129,14 +129,14 @@ template fromString(T)
                 if (_expect(str.length == 0, true))
                     return value;
                 version (D_Exceptions)
-                    throw excne;
+                    { import mir.exception : toMutable; throw excne.toMutable; }
                 else
                     assert(0);
             }
             else
             {
                 version (D_Exceptions)
-                    throw excfp;
+                    { import mir.exception : toMutable; throw excfp.toMutable; }
                 else
                     assert(0);
             }
diff --git a/source/mir/rc/array.d b/source/mir/rc/array.d
index 55c1f5d6..697a2495 100644
--- a/source/mir/rc/array.d
+++ b/source/mir/rc/array.d
@@ -191,7 +191,7 @@ struct mir_rcarray(T)
             if (!ctx)
             {
                 version(D_Exceptions)
-                    throw allocationError;
+                    { import mir.exception : toMutable; throw allocationError.toMutable; }
                 else
                     assert(0, allocationExcMsg);
             }
diff --git a/source/mir/rc/ptr.d b/source/mir/rc/ptr.d
index f6997f7d..75f39aa5 100644
--- a/source/mir/rc/ptr.d
+++ b/source/mir/rc/ptr.d
@@ -308,7 +308,7 @@ template createRC(T)
             if (!_context)
             {
                 version(D_Exceptions)
-                    throw allocationError;
+                    { import mir.exception : toMutable; throw allocationError.toMutable; }
                 else
                     assert(0, allocationExcMsg);
             }
diff --git a/source/mir/rc/slim_ptr.d b/source/mir/rc/slim_ptr.d
index b157d8be..665c3471 100644
--- a/source/mir/rc/slim_ptr.d
+++ b/source/mir/rc/slim_ptr.d
@@ -223,7 +223,7 @@ template createSlimRC(T)
             if (!context)
             {
                 version(D_Exceptions)
-                    throw allocationError;
+                    { import mir.exception : toMutable; throw allocationError.toMutable; }
                 else
                     assert(0, allocationExcMsg);
             }
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 191be1a4..488bec3f 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -172,9 +172,9 @@ template isAnnotated(T)
 private template serdeIsAnnotationMemberIn(T)
 {
     enum bool serdeIsAnnotationMemberIn(string member)
-          = hasUDA!(__traits(getMember, T, member), serdeAnnotation) 
-        && !hasUDA!(__traits(getMember, T, member), serdeIgnore) 
-        && !hasUDA!(__traits(getMember, T, member), serdeIgnoreIn);
+          = hasUDA!(T, member, serdeAnnotation) 
+        && !hasUDA!(T, member, serdeIgnore) 
+        && !hasUDA!(T, member, serdeIgnoreIn);
 }
 
 /++
@@ -213,9 +213,9 @@ version(mir_test) unittest
 private template serdeIsAnnotationMemberOut(T)
 {
     enum bool serdeIsAnnotationMemberOut(string member)
-          = hasUDA!(__traits(getMember, T, member), serdeAnnotation) 
-        && !hasUDA!(__traits(getMember, T, member), serdeIgnore) 
-        && !hasUDA!(__traits(getMember, T, member), serdeIgnoreOut);
+          = hasUDA!(T, member, serdeAnnotation) 
+        && !hasUDA!(T, member, serdeIgnore) 
+        && !hasUDA!(T, member, serdeIgnoreOut);
 }
 
 /++
@@ -223,6 +223,7 @@ private template serdeIsAnnotationMemberOut(T)
 template serdeGetAnnotationMembersOut(T)
 {
     import std.meta: aliasSeqOf, Filter;
+    import mir.ndslice.topology;
     static if (isAggregateType!T)
         enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(SerializableMembers!T))];
     else
@@ -393,6 +394,18 @@ template serdeGetKeysIn(alias symbol)
         enum immutable(string)[] serdeGetKeysIn = [__traits(identifier, symbol)];
 }
 
+// ditto
+template serdeGetKeysIn(T, string member)
+{
+    static if (hasUDA!(T, member, serdeAnnotation) || hasUDA!(T, member, serdeIgnore) || hasUDA!(T, member, serdeIgnoreIn))
+        enum immutable(string)[] serdeGetKeysIn = null;
+    else
+    static if (hasUDA!(T, member, serdeKeys))
+        enum immutable(string)[] serdeGetKeysIn = getUDA!(T, member, serdeKeys).keys;
+    else
+        enum immutable(string)[] serdeGetKeysIn = [member];
+}
+
 /// ditto
 immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc
     if (is(T == enum))
@@ -489,6 +502,21 @@ template serdeGetKeyOut(alias symbol)
         enum string serdeGetKeyOut = __traits(identifier, symbol);
 }
 
+/// ditto
+template serdeGetKeyOut(T, string member)
+{
+    static if (hasUDA!(T, member, serdeAnnotation) || hasUDA!(T, member, serdeIgnore) || hasUDA!(T, member, serdeIgnoreOut))
+        enum string serdeGetKeyOut = null;
+    else
+    static if (hasUDA!(T, member, serdeKeyOut))
+        enum string serdeGetKeyOut = getUDA!(T, member, serdeKeyOut).key;
+    else
+    static if (hasUDA!(T, member, serdeKeys))
+        enum string serdeGetKeyOut = getUDA!(T, member, serdeKeys).keys[0];
+    else
+        enum string serdeGetKeyOut = member;
+}
+
 ///ditto
 @safe pure nothrow @nogc
 string serdeGetKeyOut(T)(const T value)
@@ -646,6 +674,8 @@ version(mir_test) unittest
 /++
 +/
 alias serdeGetProxy(alias symbol) = TemplateArgsOf!(getUDA!(symbol, serdeProxy))[0];
+/// ditto
+alias serdeGetProxy(T, string member) = TemplateArgsOf!(getUDA!(T, member, serdeProxy))[0];
 
 /// Can be applied to @serdeProxy types to make (de)serialization use
 /// underlying type through casting. Useful for enums.
@@ -666,6 +696,8 @@ struct serdeIgnoreOutIf(alias pred);
 /++
 +/
 alias serdeGetIgnoreOutIf(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIf))[0]);
+/// ditto
+alias serdeGetIgnoreOutIf(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreOutIf))[0]);
 
 /++
 Attributes to conditional ignore field during serialization.
@@ -679,6 +711,8 @@ struct serdeIgnoreOutIfAggregate(alias pred);
 /++
 +/
 alias serdeGetIgnoreOutIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIfAggregate))[0]);
+/// ditto
+alias serdeGetIgnoreOutIfAggregate(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreOutIfAggregate))[0]);
 
 /++
 Attributes to conditional ignore field during deserialization.
@@ -692,6 +726,8 @@ struct serdeIgnoreInIfAggregate(alias pred);
 /++
 +/
 alias serdeGetIgnoreInIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreInIfAggregate))[0]);
+/// ditto
+alias serdeGetIgnoreInIfAggregate(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreInIfAggregate))[0]);
 
 /++
 Attributes to conditional ignore field during serialization and deserialization.
@@ -707,6 +743,8 @@ struct serdeIgnoreIfAggregate(alias pred);
 /++
 +/
 alias serdeGetIgnoreIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreIfAggregate))[0]);
+/// ditto
+alias serdeGetIgnoreIfAggregate(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeIgnoreIfAggregate))[0]);
 
 /++
 Allows to use flexible deserialization rules such as conversion from input string to numeric types.
@@ -862,6 +900,8 @@ struct serdeTransformIn(alias fun) {}
 Returns: unary function of underlaying alias of $(LREF serdeTransformIn)
 +/
 alias serdeGetTransformIn(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformIn))[0]);
+/// ditto
+alias serdeGetTransformIn(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeTransformIn))[0]);
 
 /++
 Attributes for out transformation.
@@ -874,6 +914,8 @@ struct serdeTransformOut(alias fun) {}
 Returns: unary function of underlaying alias of $(LREF serdeTransformOut)
 +/
 alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]);
+/// ditto
+alias serdeGetTransformOut(T, string member) = naryFun!(TemplateArgsOf!(getUDA!(T, member, serdeTransformOut))[0]);
 
 /++
 +/
@@ -995,7 +1037,7 @@ Is deserializable member
 template serdeIsDeserializable(T)
 {
     ///
-    enum bool serdeIsDeserializable(string member) = serdeGetKeysIn!(__traits(getMember, T, member)).length > 0;
+    enum bool serdeIsDeserializable(string member) = serdeGetKeysIn!(T, member).length > 0;
 }
 
 ///
@@ -1047,7 +1089,7 @@ Is deserializable member
 template serdeIsSerializable(T)
 {
     ///
-    enum bool serdeIsSerializable(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) !is null;
+    enum bool serdeIsSerializable(string member) = serdeGetKeyOut!(T, member) !is null;
 }
 
 ///
@@ -1348,9 +1390,9 @@ Deserialization member final proxy type
 +/
 template serdeFinalDeserializationMemberType(T, string member)
 {
-    static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
+    static if (hasUDA!(T, member, serdeProxy))
     {
-        alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
+        alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(T, member));
     }
     else
     {
@@ -1437,9 +1479,9 @@ Serialization member final proxy type
 +/
 template serdeFinalSerializationMemberType(T, string member)
 {
-    static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
+    static if (hasUDA!(T, member, serdeProxy))
     {
-        alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member)));
+        alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(T, member));
     }
     else
     {
@@ -1775,12 +1817,12 @@ package string[] sortUniqKeys()(string[] keys)
 private template serdeGetKeysIn2(T)
 {
     // T* value;
-    enum string[] serdeGetKeysIn2(string member) = serdeGetKeysIn!(__traits(getMember, T, member));
+    enum string[] serdeGetKeysIn2(string member) = serdeGetKeysIn!(T, member);
 }
 
 private template serdeGetKeyOut2(T)
 {
-    enum string[] serdeGetKeyOut2(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) is null ? null : [serdeGetKeyOut!(__traits(getMember, T, member))];
+    enum string[] serdeGetKeyOut2(string member) = serdeGetKeyOut!(T, member) is null ? null : [serdeGetKeyOut!(T, member)];
 }
 
 private template serdeFinalDeepProxyDeserializableMemberKeys(T)
@@ -1998,6 +2040,7 @@ See_also: $(LREF serdeOrderedIn), $(LREF serdeIgnoreInIfAggregate)
 +/
 enum serdeRealOrderedIn;
 
+
 /++
 UDA used to force deserializer to skip the member final deserialization.
 A user should finalize the member deserialize using the dummy object provided in `serdeFinalizeWithDummy(ref SerdeOrderedDummy!(typeof(this)) dummy)` struct method
@@ -2043,7 +2086,7 @@ public:
     {
         static if (hasField!(T, member))
         {
-            static if (hasUDA!(__traits(getMember, T, member), serdeProxy))
+            static if (hasUDA!(T, member, serdeProxy))
             {
                 mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";");
             }
@@ -2090,9 +2133,9 @@ public:
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
             __traits(getMember, flags, member) = __traits(getMember, __serdeFlags, member);
         static foreach (member; serdeFinalProxyDeserializableMembers!T)
-            static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser))
+            static if (!hasUDA!(T, member, serdeFromDummyByUser))
         {{
-            if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(T, member, __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -2129,7 +2172,7 @@ public:
     /// Initialize target member
     void serdeFinalizeTargetMember(string member)(ref T value)
     {
-            if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
+            if (hasUDA!(T, member, __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member))
             {
                 static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I))
                 {
@@ -2215,10 +2258,10 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
         import core.lifetime: move;
         import mir.conv: to;
 
-        enum likeList = hasUDA!(__traits(getMember, value, member), serdeLikeList);
-        enum likeStruct  = hasUDA!(__traits(getMember, value, member), serdeLikeStruct);
-        enum hasProxy = hasUDA!(__traits(getMember, value, member), serdeProxy);
-        enum hasScoped = hasUDA!(__traits(getMember, value, member), serdeScoped);
+        enum likeList = hasUDA!(T, member, serdeLikeList);
+        enum likeStruct  = hasUDA!(T, member, serdeLikeStruct);
+        enum hasProxy = hasUDA!(T, member, serdeProxy);
+        enum hasScoped = hasUDA!(T, member, serdeScoped);
 
         static assert (likeList + likeStruct <= 1, T.stringof ~ "." ~ member ~ " can't have both @serdeLikeStruct and @serdeLikeList attributes");
         static assert (hasProxy >= likeStruct, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization");
@@ -2227,7 +2270,7 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
         alias Member = serdeDeserializationMemberType!(T, member);
 
         static if (hasProxy)
-            alias Temporal = serdeGetProxy!(__traits(getMember, value, member));
+            alias Temporal = serdeGetProxy!(T, member);
         else
             alias Temporal = Member;
 
@@ -2241,7 +2284,7 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
 
         static immutable excm(string member) = new SerdeException("ASDF deserialisation: multiple keys for member '" ~ member ~ "' in " ~ T.stringof ~ " are not allowed.");
 
-        static if (!hasUDA!(__traits(getMember, value, member), serdeAllowMultiple))
+        static if (!hasUDA!(T, member, serdeAllowMultiple))
             if (__traits(getMember, requiredFlags, member))
                 return excm!member;
 
@@ -2290,9 +2333,9 @@ template deserializeValueMemberImpl(alias deserializeValue, alias deserializeSco
             __traits(getMember, value, member) = move(temporal);
         }
 
-        static if (hasUDA!(__traits(getMember, value, member), serdeTransformIn))
+        static if (hasUDA!(T, member, serdeTransformIn))
         {
-            alias transform = serdeGetTransformIn!(__traits(getMember, value, member));
+            alias transform = serdeGetTransformIn!(T, member, member);
             static if (hasField!(T, member))
             {
                 transform(__traits(getMember, value, member));
diff --git a/source/mir/series.d b/source/mir/series.d
index 825cbfd3..1d2ad74d 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -663,7 +663,8 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         {
             return this.data[idx];
         }
-        throw defaultExc!();
+        import mir.exception : toMutable;
+        throw defaultExc!().toMutable;
     }
 
     /// ditto
@@ -674,7 +675,7 @@ struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Co
         {
             return this.data[idx];
         }
-        throw exc;
+        { import mir.exception : toMutable; throw exc.toMutable; }
     }
 
     /// ditto
diff --git a/source/mir/small_array.d b/source/mir/small_array.d
index 1c135a28..a070ac45 100644
--- a/source/mir/small_array.d
+++ b/source/mir/small_array.d
@@ -76,7 +76,7 @@ template SmallArray(T, uint maxLength)
             {
                 if (_length > _data.length)
                 {
-                    version(D_Exceptions) throw exception;
+                    version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                     else assert(0, errorMsg);
                 }
                 _data[_length++] = c;
@@ -96,7 +96,7 @@ template SmallArray(T, uint maxLength)
         {
             if (array.length > maxLength)
             {
-                version(D_Exceptions) throw exception;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                 else assert(0, errorMsg);
             }
             _data[0 .. _length] = T.init;
@@ -135,7 +135,7 @@ template SmallArray(T, uint maxLength)
             {
                 if (rhs._length > maxLength)
                 {
-                    version(D_Exceptions) throw exception;
+                    version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                     else assert(0, errorMsg);
                 }
                 _data = rhs._data[0 .. maxLength];
@@ -158,7 +158,7 @@ template SmallArray(T, uint maxLength)
             {
                 if (rhs._length > maxLength)
                 {
-                    version(D_Exceptions) throw exception;
+                    version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                     else assert(0, errorMsg);
                 }
                 _data = rhs._data[0 .. maxLength];
@@ -181,7 +181,7 @@ template SmallArray(T, uint maxLength)
             import core.lifetime: move;
             if (_length == maxLength)
             {
-                version(D_Exceptions) throw exception;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                 else assert(0, errorMsg);
             }
             _data[_length++] = move(elem);
@@ -200,7 +200,7 @@ template SmallArray(T, uint maxLength)
         {
             if (_length + array.length > maxLength)
             {
-                version(D_Exceptions) throw exception;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                 else assert(0, errorMsg);
             }
             _data[_length .. _length + array.length] = array;
diff --git a/source/mir/small_string.d b/source/mir/small_string.d
index 345e47bf..6d342abb 100644
--- a/source/mir/small_string.d
+++ b/source/mir/small_string.d
@@ -62,7 +62,7 @@ extern(D) @safe pure @nogc:
         {
             if (i > _data.length)
             {
-                version(D_Exceptions) throw exception;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                 else assert(0, errorMsg);
             }
             _data[i++] = c;
@@ -82,7 +82,7 @@ extern(D) @safe pure @nogc:
         _data = '\0';
         if (str.length > _data.sizeof)
         {
-            version(D_Exceptions) throw exception;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
             else assert(0, errorMsg);
         }
         if (__ctfe)
@@ -108,7 +108,7 @@ extern(D) @safe pure @nogc:
         {
             if (rhs._data[maxLength])
             {
-                version(D_Exceptions) throw exception;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                 else assert(0, errorMsg);
             }
             _data = cast(char[maxLength])(rhs._data[0 .. maxLength]);
@@ -132,7 +132,7 @@ extern(D) @safe pure @nogc:
         {
             if (rhs._data[maxLength])
             {
-                version(D_Exceptions) throw exception;
+                version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
                 else assert(0, errorMsg);
             }
             _data = cast(char[maxLength])(rhs._data[0 .. maxLength]);
@@ -156,7 +156,7 @@ extern(D) @safe pure @nogc:
         auto length = opIndex.length;
         if (length == maxLength)
         {
-            version(D_Exceptions) throw exception;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
             else assert(0, errorMsg);
         }
         _data[length] = c;
@@ -169,7 +169,7 @@ extern(D) @safe pure @nogc:
         auto length = opIndex.length;
         if (length + str.length > maxLength)
         {
-            version(D_Exceptions) throw exception;
+            version(D_Exceptions) { import mir.exception : toMutable; throw exception.toMutable; }
             else assert(0, errorMsg);
         }
         if (__ctfe)
diff --git a/source/mir/stdio.d b/source/mir/stdio.d
index 273ba1a2..f4b4b455 100644
--- a/source/mir/stdio.d
+++ b/source/mir/stdio.d
@@ -7,6 +7,8 @@ module mir.stdio;
 
 static import core.stdc.stdio;
 
+import mir.exception: toMutable;
+
 /// Writes values in a text form
 void writeln(string separator = "", Args...)(auto ref const Args args)
     if (Args.length > 0)
@@ -118,7 +120,7 @@ struct File
             return;
         core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp);
         if (core.stdc.stdio.ferror(fp))
-            throw writeException;
+            throw writeException.toMutable;
     }
 
     /++
@@ -131,7 +133,7 @@ struct File
             return;
         core.stdc.stdio.fflush(fp);
         if (core.stdc.stdio.ferror(fp))
-            throw writeException;
+            throw writeException.toMutable;
     }
 }
 
@@ -158,7 +160,7 @@ struct AssumeNothrowFile
             return;
         core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp);
         if (core.stdc.stdio.ferror(fp))
-            throw writeError;
+            throw writeError.toMutable;
     }
 
     /++
@@ -171,7 +173,7 @@ struct AssumeNothrowFile
             return;
         core.stdc.stdio.fflush(fp);
         if (core.stdc.stdio.ferror(fp))
-            throw writeError;
+            throw writeError.toMutable;
     }
 }
 
diff --git a/source/mir/timestamp.d b/source/mir/timestamp.d
index 7e68a318..b4dc0df2 100644
--- a/source/mir/timestamp.d
+++ b/source/mir/timestamp.d
@@ -510,7 +510,7 @@ struct Timestamp
         static if (staticIndexOf!(Timestamp, T.AllowedTypes) < 0)
         {
             static immutable exc = new Exception("Cannot cast Timestamp to " ~ T.stringof);
-            throw exc;
+            { import mir.exception : toMutable; throw exc.toMutable; }
         }
         else
         {
@@ -611,7 +611,7 @@ struct Timestamp
         static if (T.stringof == "Duration")
         {
             if (!isDuration)
-                throw ExpectedDuration;
+                { import mir.exception : toMutable; throw ExpectedDuration.toMutable; }
             auto coeff = ((((long(year) * 7 + month) * 24 + hour) * 60 + minute) * 60 + second) * 10_000_000 + getFraction!7;
             if (isNegativeDuration)
                 coeff = -coeff;
@@ -1304,7 +1304,7 @@ struct Timestamp
         Timestamp ret;
         if (fromString(str, ret))
             return ret;
-        throw InvalidString;
+        { import mir.exception : toMutable; throw InvalidString.toMutable; }
     }
 
     template fromISOStringImpl(bool ext, bool yaml = false)
@@ -1316,12 +1316,12 @@ struct Timestamp
             if (fromISOStringImpl(str, ret))
                 return ret;
             static if (yaml)
-                throw InvalidYamlString;
+                { import mir.exception : toMutable; throw InvalidYamlString.toMutable; }
             else
             static if (ext)
-                throw InvalidISOExtendedString;
+                { import mir.exception : toMutable; throw InvalidISOExtendedString.toMutable; }
             else
-                throw InvalidISOString;
+                { import mir.exception : toMutable; throw InvalidISOString.toMutable; }
         }
 
         static bool fromISOStringImpl(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc

From 41ee85bdac5ad1172dbb4199bcdddbc55c3e22cf Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Wed, 2 Aug 2023 21:56:48 +0700
Subject: [PATCH 564/573] update dub

---
 dub.sdl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dub.sdl b/dub.sdl
index 7748504d..e8085ba7 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@ authors "Ilia Ki" "John Michael Hall" "Shigeki Karita" "Sebastian Wilzbach" "And
 copyright "2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments"
 license "Apache-2.0"
 
-dependency "mir-core" version=">=1.5.6"
+dependency "mir-core" version=">=1.6.0"
 
 // dflags "-version=MirNoSIMD"
 // dflags "-mtriple=aarch64-linux-gnu"

From e888555fbbb5a494f14a6639900842d6210cc9da Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Wed, 9 Aug 2023 13:32:04 +0700
Subject: [PATCH 565/573] update compilers

---
 .github/workflows/compilers.json           |  4 +-
 source/mir/bignum/internal/phobos_kernel.d | 15 ++--
 source/mir/serde.d                         | 30 -------
 source/mir/series.d                        | 93 ----------------------
 source/mir/string_map.d                    |  5 +-
 5 files changed, 8 insertions(+), 139 deletions(-)

diff --git a/.github/workflows/compilers.json b/.github/workflows/compilers.json
index a737cdb0..b2ecda9c 100644
--- a/.github/workflows/compilers.json
+++ b/.github/workflows/compilers.json
@@ -2,9 +2,7 @@
     "dmd-master",
     "dmd-latest",
     "dmd-beta",
-    "dmd-2.100.2",
     "ldc-master",
     "ldc-latest",
-    "ldc-beta",
-    "ldc-1.30.1"
+    "ldc-beta"
 ]
\ No newline at end of file
diff --git a/source/mir/bignum/internal/phobos_kernel.d b/source/mir/bignum/internal/phobos_kernel.d
index 3457fc4f..4a77b30e 100644
--- a/source/mir/bignum/internal/phobos_kernel.d
+++ b/source/mir/bignum/internal/phobos_kernel.d
@@ -39,22 +39,19 @@ Idioms:
 */
 module mir.bignum.internal.phobos_kernel;
 
-static if (__VERSION__ > 2100)
-    version (D_InlineAsm_X86)
-        static import std.internal.math.biguintx86;
+version (D_InlineAsm_X86)
+    static import std.internal.math.biguintx86;
 
 version (DMD)
 {
-    static if (__VERSION__ > 2100)
-        version (D_InlineAsm_X86)
-            version = HaveAsmVersion;
+    version (D_InlineAsm_X86)
+        version = HaveAsmVersion;
 }
 
 version (LDC)
 {
-    static if (__VERSION__ > 2100)
-        version (D_InlineAsm_X86)
-            version = HaveAsmVersion;
+    version (D_InlineAsm_X86)
+        version = HaveAsmVersion;
 
     version (ARM)
     {
diff --git a/source/mir/serde.d b/source/mir/serde.d
index 488bec3f..326242dd 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -417,25 +417,11 @@ immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc
 
     import std.meta: staticMap;
     static immutable ret = [staticMap!(.serdeGetKeysIn, EnumMembers!T)];
-    static if (__VERSION__ < 2093)
-    {
-        final switch (value)
-        {
-            foreach (i, member; EnumMembers!T)
-            {
-                case member:
-                    return ret[i];
-            }
-        }
-    }
-    else
-    {
     import mir.enums: getEnumIndex;
     uint index = void;
     if (getEnumIndex(value, index))
         return ret[index];
     assert(0);
-    }
 }
 
 ///
@@ -527,21 +513,6 @@ string serdeGetKeyOut(T)(const T value)
         alias all = __traits(getAttributes, EnumMembers!T[i]);
     }}
 
-    static if (__VERSION__ < 2093)
-    {
-        import std.meta: staticMap;
-        static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
-        final switch (value)
-        {
-            foreach (i, member; EnumMembers!T)
-            {
-                case member:
-                    return ret[i];
-            }
-        }
-    }
-    else
-    {
     import std.meta: staticMap;
     import mir.enums: getEnumIndex;
     static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)];
@@ -549,7 +520,6 @@ string serdeGetKeyOut(T)(const T value)
     if (getEnumIndex(value, index))
         return ret[index];
     assert(0);
-    }
 }
 
 ///
diff --git a/source/mir/series.d b/source/mir/series.d
index 1d2ad74d..87ca3bce 100644
--- a/source/mir/series.d
+++ b/source/mir/series.d
@@ -2598,96 +2598,3 @@ ref V[K] insert(V, K, IndexIterator, Iterator, size_t N, SliceKind kind)(return
     a.insert = s;
     assert(a.series == series([1, 2, 3, 4], [3.0, 20, 30, 2]));
 }
-
-
-static if (__VERSION__ < 2078)
-//////////////////// OBJECT.d
-{
-
-private:
-
-extern (C)
-{
-    // from druntime/src/rt/aaA.d
-
-    // size_t _aaLen(in void* p) pure nothrow @nogc;
-    private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow;
-    // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey);
-    inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow;
-    inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow;
-    void* _aaRehash(void** pp, in TypeInfo keyti) pure nothrow;
-    void _aaClear(void* p) pure nothrow;
-
-    // alias _dg_t = extern(D) int delegate(void*);
-    // int _aaApply(void* aa, size_t keysize, _dg_t dg);
-
-    // alias _dg2_t = extern(D) int delegate(void*, void*);
-    // int _aaApply2(void* aa, size_t keysize, _dg2_t dg);
-
-    // private struct AARange { void* impl; size_t idx; }
-    alias AARange = ReturnType!(object._aaRange);
-    AARange _aaRange(void* aa) pure nothrow @nogc @safe;
-    bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe;
-    void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe;
-    void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe;
-    void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe;
-
-}
-
-auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
-{
-    import core.internal.traits : substInout;
-
-    static struct Result
-    {
-        AARange r;
-
-    pure nothrow @nogc:
-        @property bool empty() @safe { return _aaRangeEmpty(r); }
-        @property auto front()
-        {
-            static struct Pair
-            {
-                // We save the pointers here so that the Pair we return
-                // won't mutate when Result.popFront is called afterwards.
-                private void* keyp;
-                private void* valp;
-
-                @property ref key() inout
-                {
-                    auto p = (() @trusted => cast(substInout!K*) keyp) ();
-                    return *p;
-                };
-                @property ref value() inout
-                {
-                    auto p = (() @trusted => cast(substInout!V*) valp) ();
-                    return *p;
-                };
-            }
-            return Pair(_aaRangeFrontKey(r),
-                        _aaRangeFrontValue(r));
-        }
-        void popFront() @safe { return _aaRangePopFront(r); }
-        @property Result save() { return this; }
-    }
-
-    return Result(_aaToRange(aa));
-}
-
-auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
-{
-    return (*aa).byKeyValue();
-}
-
-// this should never be made public.
-private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
-{
-    // ensure we are dealing with a genuine AA.
-    static if (is(const(V[K]) == const(T)))
-        alias realAA = aa;
-    else
-        const(V[K]) realAA = aa;
-    return _aaRange(() @trusted { return cast(void*)realAA; } ());
-}
-
-}
diff --git a/source/mir/string_map.d b/source/mir/string_map.d
index 91668112..c05cb11b 100644
--- a/source/mir/string_map.d
+++ b/source/mir/string_map.d
@@ -1142,10 +1142,7 @@ version(mir_test)
     table["0"] = v0;
     table["1"] = v1;
     assert(table.keys == ["0", "1"]);
-    static if (__VERSION__ > 2098) // See https://github.com/libmir/mir-algorithm/runs/6809888795?check_suite_focus=true#step:5:17
-    {
-        assert(table.values == [v0, v1]); // TODO: qualify unittest as `pure` when this is inferred `pure`
-    }
+    assert(table.values == [v0, v1]); // TODO: qualify unittest as `pure` when this is inferred `pure`
     static assert(is(typeof(table.values) == const(C)[]));
 }
 

From f3d0b7fe4cb0e4a52c8929a1ac85213f5e123bd3 Mon Sep 17 00:00:00 2001
From: aferust <aferust@gmail.com>
Date: Mon, 8 Apr 2024 19:34:06 +0300
Subject: [PATCH 566/573] Update package.d doc for median filter example (#468)

---
 source/mir/ndslice/package.d | 32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/source/mir/ndslice/package.d b/source/mir/ndslice/package.d
index 76d21c0b..0ef1e315 100644
--- a/source/mir/ndslice/package.d
+++ b/source/mir/ndslice/package.d
@@ -360,8 +360,8 @@ Returns:
         where с is the number of channels in the image.
         Dense data layout is guaranteed.
 +/
-Slice!(ubyte*, 3) movingWindowByChannel
-(Slice!(Universal, [3], ubyte*) image, size_t nr, size_t nc, ubyte delegate(Slice!(Universal, [2], ubyte*)) filter)
+Slice!(C*, 3) movingWindowByChannel(alias filter, C)
+(Slice!(C*, 3, Universal) image, size_t nr, size_t nc)
 {
         // 0. 3D
         // The last dimension represents the color channel.
@@ -374,10 +374,12 @@ Slice!(ubyte*, 3) movingWindowByChannel
         .windows(nr, nc)
         // 3. 5D
         // Unpacks the windows.
-        .unpack
-        .transposed!(0, 1, 4)
+        .unpack.unpack
         // 4. 5D
         // Brings the color channel dimension to the third position.
+        .transposed!(0, 1, 4)
+        // 5. 3D Composed of 2D
+        // Packs the last two dimensions.
         .pack!2
         // 2D to pixel lazy conversion.
         .map!filter
@@ -397,12 +399,15 @@ Params:
 Returns:
     median value over the range `r`
 +/
-T median(Range, T)(Slice!(Universal, [2], Range) sl, T[] buf)
+T median(Iterator, SliceKind kind, T)(Slice!(Iterator, 2, kind) sl, T[] buf)
 {
     import std.algorithm.sorting : topN;
     // copy sl to the buffer
     auto retPtr = reduce!(
-        (ptr, elem) { *ptr = elem; return ptr + 1;} )(buf.ptr, sl);
+        (ptr, elem) {
+            *ptr = elem;
+            return ptr + 1;
+        } )(buf.ptr, sl);
     auto n = retPtr - buf.ptr;
     buf[0 .. n].topN(n / 2);
     return buf[n / 2];
@@ -414,9 +419,9 @@ The `main` function:
 -------
 void main(string[] args)
 {
-    import std.conv : to;
-    import std.getopt : getopt, defaultGetoptPrinter;
-    import std.path : stripExtension;
+    import std.conv: to;
+    import std.getopt: getopt, defaultGetoptPrinter;
+    import std.path: stripExtension;
 
     uint nr, nc, def = 3;
     auto helpInformation = args.getopt(
@@ -434,6 +439,12 @@ void main(string[] args)
 
     auto buf = new ubyte[nr * nc];
 
+    if (args.length == 1)
+    {
+        import std.stdio: writeln;
+        writeln("No input file given");
+    }
+
     foreach (name; args[1 .. $])
     {
         import imageformats; // can be found at code.dlang.org
@@ -442,6 +453,7 @@ void main(string[] args)
 
         auto ret = image.pixels
             .sliced(cast(size_t)image.h, cast(size_t)image.w, cast(size_t)image.c)
+            .universal
             .movingWindowByChannel
                 !(window => median(window, buf))
                  (nr, nc);
@@ -450,7 +462,7 @@ void main(string[] args)
             name.stripExtension ~ "_filtered.png",
             ret.length!1,
             ret.length!0,
-            (&ret[0, 0, 0])[0 .. ret.elementCount]);
+            ret.field);
     }
 }
 -------

From 277dd3723c5144106e7982a95ea8af4bf21b36cf Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Tue, 23 Apr 2024 14:22:59 +0700
Subject: [PATCH 567/573] add serdeMembersExactly

---
 source/mir/serde.d | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/source/mir/serde.d b/source/mir/serde.d
index 326242dd..9b964ee1 100644
--- a/source/mir/serde.d
+++ b/source/mir/serde.d
@@ -2193,6 +2193,16 @@ version(mir_test) unittest
     static assert(hasUDA!(SerdeOrderedDummy!S.d, serdeProxy));
 }
 
+/++ 
+Exact amount of total serializable members in a structure/class.
+The UDA is used to help CBOR and MsgPack serializers to construct finite length maps.
++/
+struct serdeMembersExactly
+{
+    ///
+    int n;
+}
+
 /++
 A dummy structure passed to `.serdeFinalizeWithFlags` finalizer method.
 +/

From 77b74a1ac1628543adec8cd1feca690cbfd6bb0e Mon Sep 17 00:00:00 2001
From: Mathias LANG <mlang@symmetryinvestments.com>
Date: Tue, 10 Dec 2024 10:23:11 +0100
Subject: [PATCH 568/573] Remove Symmetry Investments logo (#474)

sure thing
---
 README.md           |   2 --
 images/symmetry.png | Bin 8870 -> 0 bytes
 2 files changed, 2 deletions(-)
 delete mode 100644 images/symmetry.png

diff --git a/README.md b/README.md
index 73aea184..6007370c 100644
--- a/README.md
+++ b/README.md
@@ -88,6 +88,4 @@ void main()
 
 ### Our sponsors
 
-[<img src="https://raw.githubusercontent.com/libmir/mir-algorithm/master/images/symmetry.png" height="80" />](http://symmetryinvestments.com/) 	&nbsp; 	&nbsp;	&nbsp;	&nbsp;
 [<img src="https://raw.githubusercontent.com/libmir/mir-algorithm/master/images/kaleidic.jpeg" height="80" />](https://github.com/kaleidicassociates)
-
diff --git a/images/symmetry.png b/images/symmetry.png
deleted file mode 100644
index 4dcca0f3d289478339423521645bec77d1aa335d..0000000000000000000000000000000000000000
GIT binary patch

From 8b2c779a398dce7d0f334d7ff311cdaa171e0b65 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Tue, 10 Dec 2024 16:26:15 +0700
Subject: [PATCH 569/573] Update README.md

---
 README.md | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/README.md b/README.md
index 6007370c..c8de29ef 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,3 @@ void main()
 ```
 
 [![Open on run.dlang.io](https://img.shields.io/badge/run.dlang.io-open-blue.svg)](https://run.dlang.io/is/67Gi6X)
-
-### Our sponsors
-
-[<img src="https://raw.githubusercontent.com/libmir/mir-algorithm/master/images/kaleidic.jpeg" height="80" />](https://github.com/kaleidicassociates)

From 406d116a85bf213e11e132f53833f6f544eb13d8 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Wed, 18 Dec 2024 01:31:59 +0700
Subject: [PATCH 570/573] update for recent compiler

---
 source/mir/bignum/decimal.d            |  8 ++++--
 source/mir/bignum/fp.d                 |  2 +-
 source/mir/bignum/integer.d            |  6 ++---
 source/mir/bignum/internal/dec2float.d |  2 +-
 source/mir/parse.d                     | 34 +++++++++++++-------------
 5 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/source/mir/bignum/decimal.d b/source/mir/bignum/decimal.d
index 94fc07bb..b98fb63c 100644
--- a/source/mir/bignum/decimal.d
+++ b/source/mir/bignum/decimal.d
@@ -38,6 +38,9 @@ struct Decimal(uint size64)
     void toString(C = char, W)(ref scope W w, NumericSpec spec = NumericSpec.init) const scope
         if(isSomeChar!C && isMutable!C)
     {
+        scope C[] _buffer;
+        if (false) w.put(_buffer);
+        () @trusted {
         assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human);
         import mir.utility: _expect;
         // handle special values
@@ -212,6 +215,7 @@ struct Decimal(uint size64)
         auto expLength = printSignedToTail(exponent, buffer0[$ - N - 16 .. $ - 16], '+');
         buffer[$ - ++expLength] = spec.exponentChar;
         w.put(buffer[$ - expLength .. $]);
+    } ();
     }
 
 @safe:
@@ -422,7 +426,7 @@ struct Decimal(uint size64)
     }
 
     ///
-    ref opOpAssign(string op, size_t rhsMaxSize64)(ref const Decimal!rhsMaxSize64 rhs) @safe pure return
+    ref opOpAssign(string op, size_t rhsMaxSize64)(ref const Decimal!rhsMaxSize64 rhs) @trusted pure return
         if (op == "+" || op == "-")
     {
         import mir.utility: max;
@@ -450,7 +454,7 @@ struct Decimal(uint size64)
 
 ///
 version(mir_bignum_test)
-@safe pure nothrow @nogc
+@trusted pure nothrow @nogc
 unittest
 {
     import mir.test: should;
diff --git a/source/mir/bignum/fp.d b/source/mir/bignum/fp.d
index cbd77061..d56109de 100644
--- a/source/mir/bignum/fp.d
+++ b/source/mir/bignum/fp.d
@@ -637,7 +637,7 @@ struct Fp(uint size)
 
 ///
 Fp!(coefficientizeA + coefficientizeB) extendedMul(bool noSpecial = false, uint coefficientizeA, uint coefficientizeB)(Fp!coefficientizeA a, Fp!coefficientizeB b)
-    @safe pure nothrow @nogc
+    @trusted pure nothrow @nogc
 {
     import mir.bignum.fixed: extendedMul;
     import mir.checkedint: adds;
diff --git a/source/mir/bignum/integer.d b/source/mir/bignum/integer.d
index 6957579f..0991c151 100644
--- a/source/mir/bignum/integer.d
+++ b/source/mir/bignum/integer.d
@@ -263,7 +263,7 @@ struct BigInt(uint size64)
     }
 
     ///
-    BigInt copy() @property
+    BigInt copy() @property @trusted
     {
         BigInt ret = void;
         ret.sign = sign;
@@ -479,7 +479,7 @@ struct BigInt(uint size64)
 
     ///ditto
     ref powMod()(scope BigUIntView!(const size_t) exponent, scope ref const BigInt modulus)
-        @safe pure nothrow @nogc return scope
+        @trusted pure nothrow @nogc return scope
     {
         pragma(inline, false);
 
@@ -654,7 +654,7 @@ struct BigInt(uint size64)
         remainder or quotient from the truncated division
     +/
     ref opOpAssign(string op : "*", size_t rhsSize64)(scope const ref BigInt!rhsSize64 rhs)
-        @safe pure nothrow @nogc return
+        @trusted pure nothrow @nogc return
     {
         BigInt!(size64 + rhsSize64) c = void;
         c.multiply(this, rhs);
diff --git a/source/mir/bignum/internal/dec2float.d b/source/mir/bignum/internal/dec2float.d
index 01b0be74..9772e77e 100644
--- a/source/mir/bignum/internal/dec2float.d
+++ b/source/mir/bignum/internal/dec2float.d
@@ -288,7 +288,7 @@ private template bigSize(T)
     }
 }
 
-@optStrategy("minsize")
+@optStrategy("minsize") @trusted
 private T algorithmM(T)(scope const size_t[] coefficients, long exponent)
     if ((is(T == float) || is(T == double) || is(T == real)))
     in (coefficients.length)
diff --git a/source/mir/parse.d b/source/mir/parse.d
index a76891fe..b870c213 100644
--- a/source/mir/parse.d
+++ b/source/mir/parse.d
@@ -295,27 +295,27 @@ Performs `nothrow` and `@nogc` string to native type conversion.
 
 Rseturns: true if success and false otherwise.
 +/
-bool fromString(T, C)(scope const(C)[] str, ref T value)
-    if (isSomeChar!C)
+bool fromString(T, C)(scope const(C)[] str, ref T value) @trusted
+    if (isSomeChar!C && isFloatingPoint!T)
 {
-    static if (isFloatingPoint!T)
-    {
-        import mir.bignum.decimal: Decimal, DecimalExponentKey;
-        import mir.utility: _expect;
+    import mir.bignum.decimal: Decimal, DecimalExponentKey;
+    import mir.utility: _expect;
 
-        Decimal!128 decimal = void;
-        DecimalExponentKey key;
-        auto ret = decimal.fromStringImpl(str, key);
-        if (_expect(ret, true))
-        {
-            value = cast(T) decimal;
-        }
-        return ret;
-    }
-    else
+    Decimal!128 decimal = void;
+    DecimalExponentKey key;
+    auto ret = decimal.fromStringImpl(str, key);
+    if (_expect(ret, true))
     {
-        return parse!T(str, value) && str.length == 0;
+        value = cast(T) decimal;
     }
+    return ret;
+}
+
+/// ditto 
+bool fromString(T, C)(scope const(C)[] str, ref T value)
+    if (isSomeChar!C && !isFloatingPoint!T)
+{
+    return parse!T(str, value) && str.length == 0;
 }
 
 ///

From b0d1e500f74ac30f169357d7144d8d1c7298281f Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Wed, 18 Dec 2024 02:05:32 +0700
Subject: [PATCH 571/573] ditto

---
 source/mir/ndslice/iterator.d | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/source/mir/ndslice/iterator.d b/source/mir/ndslice/iterator.d
index a2fb780a..38c4ee07 100644
--- a/source/mir/ndslice/iterator.d
+++ b/source/mir/ndslice/iterator.d
@@ -1058,15 +1058,16 @@ struct MapIterator(Iterator, alias _fun)
             return _fun(*_iterator);
     }
 
+    static if (is(typeof(_iterator[0]) : Tuple!T, T...))
+    auto ref opIndex(ptrdiff_t index) 
+    {
+        auto t = _iterator[index];
+        return _fun(autoExpandAndForward!t);
+    }
+    else
     auto ref opIndex(ptrdiff_t index) scope
     {
-        static if (is(typeof(_iterator[0]) : Tuple!T, T...))
-        {
-            auto t = _iterator[index];
-            return _fun(autoExpandAndForward!t);
-        }
-        else
-            return _fun(_iterator[index]);
+        return _fun(_iterator[index]);
     }
 
     static if (!__traits(compiles, &opIndex(ptrdiff_t.init)))

From 9e86dd490dea867a347f4fdd629b1291bfc10d6b Mon Sep 17 00:00:00 2001
From: jmh530 <jmh530@users.noreply.github.com>
Date: Thu, 2 Jan 2025 13:41:47 -0500
Subject: [PATCH 572/573] Add `minElement` / `maxElement` (#473)

* Add minElement / maxElement

* Fixup typo
---
 source/mir/algorithm/iteration.d | 85 ++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index d75d0d1f..851b1291 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -20,8 +20,10 @@ $(T2 find, Finds backward index.)
 $(T2 findIndex, Finds index.)
 $(T2 fold, Accumulates all elements (different parameter order than `reduce`).)
 $(T2 isSymmetric, Checks if the matrix is symmetric.)
+$(T2 maxElement, Returns the maximum.)
 $(T2 maxIndex, Finds index of the maximum.)
 $(T2 maxPos, Finds backward index of the maximum.)
+$(T2 minElement, Returns the minimum.)
 $(T2 minIndex, Finds index of the minimum.)
 $(T2 minmaxIndex, Finds indices of the minimum and the maximum.)
 $(T2 minmaxPos, Finds backward indices of the minimum and the maximum.)
@@ -4432,3 +4434,86 @@ unittest
     auto z4 = y.minIndex;
     auto z5 = y.maxIndex;
 }
+
+/++
+Returns the minimal(maximal) element of a multidimensional slice.
+
+Params:
+    pred = A predicate.
+
+See_also:
+    $(LREF minIndex),
+    $(LREF maxElement),
+    $(LREF maxIndex),
+    $(LREF maxPos).
++/
+template minElement(alias pred = "a < b")
+{
+    import mir.functional: naryFun;
+    static if (__traits(isSame, naryFun!pred, pred))
+    /++
+    Params:
+        slice = ndslice.
+    Returns:
+        Minimal(maximal) element of a multidimensional slice
+    +/
+    @fmamath DeepElementType!(Slice!(Iterator, N, kind)) minElement(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
+    {
+        return slice[slice.minIndex!pred];
+    }
+    else
+        alias minElement = .minElement!(naryFun!pred);
+}
+
+/// ditto
+template maxElement(alias pred = "a < b")
+{
+    import mir.functional: naryFun, reverseArgs;
+    alias maxElement = minElement!(reverseArgs!(naryFun!pred));
+}
+
+///
+@safe pure nothrow
+version(mir_test)
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    auto s = [
+        2, 6, 4, -3,
+        0, -4, -3, 3,
+        -3, -2, 7, 8,
+        ].sliced(3, 4);
+
+    assert(s.minElement == -4);
+    assert(s.maxElement == 8);
+}
+
+///
+@safe pure nothrow
+version(mir_test)
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    auto s = [
+        -8, 6, 4, -3,
+        0, -4, -3, 3,
+        -3, -2, 7, 8,
+        ].sliced(3, 4);
+
+    assert(s.minElement == -8);
+}
+
+@safe pure nothrow
+version(mir_test)
+unittest
+{
+    import mir.ndslice.slice: sliced;
+    auto s = [
+            0, 1, 2, 3,
+            4, 5, 6, 7,
+            8, 9, 10, 11
+            ].sliced(3, 4);
+
+    assert(s.minElement == 0);
+    assert(s.maxElement == 11);
+}

From ecde2eb4ff41dc9d9dc6f71234d0838dccce22b4 Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Fri, 13 Jun 2025 01:48:09 +0700
Subject: [PATCH 573/573] fix deprecations

---
 source/mir/algorithm/iteration.d | 2 +-
 source/mir/format.d              | 4 ++--
 source/mir/interpolate/spline.d  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/mir/algorithm/iteration.d b/source/mir/algorithm/iteration.d
index 851b1291..a0f5b125 100644
--- a/source/mir/algorithm/iteration.d
+++ b/source/mir/algorithm/iteration.d
@@ -121,7 +121,7 @@ private struct BitSliceAccelerator(Field, I = typeof(Field.init[size_t.init]))
     import mir.ndslice.field: BitField;
 
     ///
-    alias U = typeof(I + 1u);
+    alias U = typeof(I.init + 1u);
     /// body bits chunks
     static if (isIterator!Field)
         Slice!Field bodyChunks;
diff --git a/source/mir/format.d b/source/mir/format.d
index 10214ff2..8be3e4b5 100644
--- a/source/mir/format.d
+++ b/source/mir/format.d
@@ -88,7 +88,7 @@ unittest
 }
 
 /// Concatenated string results
-string text(string separator = "", Args...)(auto scope ref const Args args)
+string text(string separator = "", Args...)(auto ref scope const Args args)
     if (Args.length > 0)
 {
     import mir.utility: _expect;
@@ -607,7 +607,7 @@ void printElement(C = char, EscapeFormat escapeFormat = EscapeFormat.ion, W, T)(
 /++
 Multiargument overload.
 +/
-void print(C = char, W, Args...)(scope ref W w, auto scope ref const Args args)
+void print(C = char, W, Args...)(scope ref W w, auto ref scope const Args args)
     if (isSomeChar!C && Args.length > 1)
 {
     foreach(i, ref c; args)
diff --git a/source/mir/interpolate/spline.d b/source/mir/interpolate/spline.d
index 8bdd8eee..9a1d0963 100644
--- a/source/mir/interpolate/spline.d
+++ b/source/mir/interpolate/spline.d
@@ -843,7 +843,7 @@ struct Spline(F, size_t N = 1, X = F)
         this._data = shape.rcslice!(F[2 ^^ N]);
     }
 
-    package static auto pickDataSubslice(D)(auto scope ref D data, size_t index) @trusted
+    package static auto pickDataSubslice(D)(auto ref scope D data, size_t index) @trusted
     {
         auto strides = data.strides;
         foreach (i; Iota!(strides.length))
